65 lines
1.9 KiB
TypeScript
65 lines
1.9 KiB
TypeScript
import { Plugin } from "prosemirror-state";
|
|
import { EditorView } from "prosemirror-view";
|
|
import Extension from "../lib/Extension";
|
|
|
|
interface HoverPreviewsOptions {
|
|
/** Callback when a hover target is found or lost. */
|
|
onHoverLink?: (target: Element | null) => void;
|
|
|
|
/** Delay before the target is considered "hovered" and callback is triggered. */
|
|
delay: number;
|
|
}
|
|
|
|
export default class HoverPreviews extends Extension {
|
|
get defaultOptions(): HoverPreviewsOptions {
|
|
return {
|
|
delay: 500,
|
|
};
|
|
}
|
|
|
|
get name() {
|
|
return "hover-previews";
|
|
}
|
|
|
|
get plugins() {
|
|
const isHoverTarget = (target: Element | null, view: EditorView) =>
|
|
target instanceof HTMLElement &&
|
|
this.editor.elementRef.current?.contains(target) &&
|
|
(!view.editable || (view.editable && !view.hasFocus()));
|
|
|
|
let hoveringTimeout: ReturnType<typeof setTimeout>;
|
|
|
|
return [
|
|
new Plugin({
|
|
props: {
|
|
handleDOMEvents: {
|
|
mouseover: (view: EditorView, event: MouseEvent) => {
|
|
const target = (event.target as HTMLElement)?.closest(
|
|
".use-hover-preview"
|
|
);
|
|
if (isHoverTarget(target, view)) {
|
|
if (this.options.onHoverLink) {
|
|
hoveringTimeout = setTimeout(() => {
|
|
this.options.onHoverLink?.(target);
|
|
}, this.options.delay);
|
|
}
|
|
}
|
|
return false;
|
|
},
|
|
mouseout: (view: EditorView, event: MouseEvent) => {
|
|
const target = (event.target as HTMLElement)?.closest(
|
|
".use-hover-preview"
|
|
);
|
|
if (isHoverTarget(target, view)) {
|
|
clearTimeout(hoveringTimeout);
|
|
this.options.onHoverLink?.(null);
|
|
}
|
|
return false;
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
];
|
|
}
|
|
}
|