Skip to content

Commit a7612c3

Browse files
authored
feat(YfmCut): auto-opening yfm-cut when dragging over it (#152)
1 parent 31fe9fb commit a7612c3

File tree

1 file changed

+66
-3
lines changed

1 file changed

+66
-3
lines changed

src/extensions/yfm/YfmCut/plugins/auto-open.ts

Lines changed: 66 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,26 @@
1-
import {Plugin, PluginKey} from 'prosemirror-state';
1+
import throttle from 'lodash/throttle';
2+
import {Plugin, PluginKey, PluginView} from 'prosemirror-state';
23
import type {EditorView} from 'prosemirror-view';
34
import type {ResolvedPos} from 'prosemirror-model';
5+
import {findDomRefAtPos} from 'prosemirror-utils';
46
import {isTextSelection} from '../../../../utils/selection';
57
import {cutContentType, cutType} from '../const';
68

79
const key = new PluginKey('yfm-cut-auto-open');
810

9-
export const cutAutoOpenPlugin = () =>
10-
new Plugin({
11+
export const cutAutoOpenPlugin = () => {
12+
return new Plugin({
1113
key,
1214
view(view) {
1315
update(view);
16+
const dragHandler = new CutAutoOpenOnDragOver(view);
1417
return {
1518
update: (view) => update(view),
19+
destroy: () => dragHandler.destroy(),
1620
};
1721
},
1822
});
23+
};
1924

2025
function update(view: EditorView) {
2126
const sel = view.state.selection;
@@ -44,3 +49,61 @@ function openParentYfmCuts($pos: ResolvedPos, domAtPos: EditorView['domAtPos']):
4449
depth--;
4550
}
4651
}
52+
53+
class CutAutoOpenOnDragOver implements PluginView {
54+
private static readonly YFM_CUT_SELECTOR = '.yfm-cut:not(.open)';
55+
private static readonly OPEN_TIMEOUT = 500; //ms
56+
private static readonly THROTTLE_WAIT = 50; //ms
57+
58+
private _cutElem: HTMLElement | null = null;
59+
private _editorView: EditorView;
60+
private _timeout: ReturnType<typeof setTimeout> | null = null;
61+
private readonly _docListener;
62+
63+
constructor(view: EditorView) {
64+
this._editorView = view;
65+
this._docListener = throttle(
66+
this._onDocEvent.bind(this),
67+
CutAutoOpenOnDragOver.THROTTLE_WAIT,
68+
);
69+
document.addEventListener('mousemove', this._docListener);
70+
document.addEventListener('dragover', this._docListener);
71+
}
72+
73+
destroy(): void {
74+
this._clear();
75+
this._docListener.cancel();
76+
document.removeEventListener('mousemove', this._docListener);
77+
document.removeEventListener('dragover', this._docListener);
78+
}
79+
80+
private _onDocEvent(event: MouseEvent) {
81+
const view = this._editorView;
82+
if (!view.dragging) return;
83+
const pos = view.posAtCoords({left: event.clientX, top: event.clientY});
84+
if (!pos) return;
85+
const elem = findDomRefAtPos(pos.pos, view.domAtPos.bind(view)) as HTMLElement;
86+
const cutElem = elem.closest(CutAutoOpenOnDragOver.YFM_CUT_SELECTOR);
87+
if (cutElem === this._cutElem) return;
88+
this._clear();
89+
if (cutElem) this._setCutElem(cutElem as HTMLElement);
90+
}
91+
92+
private _clear() {
93+
if (this._timeout !== null) clearTimeout(this._timeout);
94+
this._timeout = null;
95+
this._cutElem = null;
96+
}
97+
98+
private _setCutElem(elem: HTMLElement) {
99+
this._cutElem = elem;
100+
this._timeout = setTimeout(this._openCut.bind(this), CutAutoOpenOnDragOver.OPEN_TIMEOUT);
101+
}
102+
103+
private _openCut() {
104+
if (this._editorView.dragging) {
105+
this._cutElem?.classList.add('open');
106+
}
107+
this._clear();
108+
}
109+
}

0 commit comments

Comments
 (0)