Skip to content

Commit 3faa05e

Browse files
fix: menu positioning when scrolling (#167)
* Made all menus/toolbars scroll with page * Revert "Made all menus/toolbars scroll with page" This reverts commit 6071095. * Made all menus/toolbars scroll with page
1 parent 23cbacc commit 3faa05e

File tree

4 files changed

+86
-37
lines changed

4 files changed

+86
-37
lines changed

packages/core/src/extensions/DraggableBlocks/DraggableBlocksPlugin.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,9 @@ export class BlockMenuView {
277277
// Shows or updates menu position whenever the cursor moves, if the menu isn't frozen.
278278
document.body.addEventListener("mousemove", this.onMouseMove, true);
279279

280+
// Makes menu scroll with the page.
281+
document.addEventListener("scroll", this.onScroll);
282+
280283
// Hides and unfreezes the menu whenever the user selects the editor with the mouse or presses a key.
281284
// TODO: Better integration with suggestions menu and only editor scope?
282285
document.body.addEventListener("mousedown", this.onMouseDown, true);
@@ -421,6 +424,12 @@ export class BlockMenuView {
421424
}
422425
};
423426

427+
onScroll = () => {
428+
if (this.menuOpen) {
429+
this.blockMenu.render(this.getDynamicParams(), false);
430+
}
431+
};
432+
424433
destroy() {
425434
if (this.menuOpen) {
426435
this.menuOpen = false;
@@ -430,6 +439,7 @@ export class BlockMenuView {
430439
document.body.removeEventListener("dragover", this.onDragOver);
431440
document.body.removeEventListener("drop", this.onDrop);
432441
document.body.removeEventListener("mousedown", this.onMouseDown);
442+
document.removeEventListener("scroll", this.onScroll);
433443
document.body.removeEventListener("keydown", this.onKeyDown);
434444
}
435445

packages/core/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ export class FormattingToolbarView {
8585
this.view.dom.addEventListener("mouseup", this.viewMouseupHandler);
8686
this.view.dom.addEventListener("dragstart", this.dragstartHandler);
8787

88+
document.addEventListener("scroll", this.scrollHandler);
89+
8890
this.editor.on("focus", this.focusHandler);
8991
this.editor.on("blur", this.blurHandler);
9092
}
@@ -130,6 +132,12 @@ export class FormattingToolbarView {
130132
}
131133
};
132134

135+
scrollHandler = () => {
136+
if (this.toolbarIsOpen) {
137+
this.formattingToolbar.render(this.getDynamicParams(), false);
138+
}
139+
};
140+
133141
update(view: EditorView, oldState?: EditorState) {
134142
const { state, composing } = view;
135143
const { doc, selection } = state;
@@ -213,6 +221,8 @@ export class FormattingToolbarView {
213221
this.view.dom.removeEventListener("mouseup", this.viewMouseupHandler);
214222
this.view.dom.removeEventListener("dragstart", this.dragstartHandler);
215223

224+
document.removeEventListener("scroll", this.scrollHandler);
225+
216226
this.editor.off("focus", this.focusHandler);
217227
this.editor.off("blur", this.blurHandler);
218228
}

packages/core/src/extensions/HyperlinkToolbar/HyperlinkToolbarPlugin.ts

Lines changed: 54 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -56,47 +56,56 @@ class HyperlinkToolbarView {
5656
return false;
5757
};
5858

59-
editor.view.dom.addEventListener("mouseover", (event) => {
60-
// Resets the hyperlink mark currently hovered by the mouse cursor.
61-
this.mouseHoveredHyperlinkMark = undefined;
62-
this.mouseHoveredHyperlinkMarkRange = undefined;
63-
64-
this.stopMenuUpdateTimer();
65-
66-
if (
67-
event.target instanceof HTMLAnchorElement &&
68-
event.target.nodeName === "A"
69-
) {
70-
// Finds link mark at the hovered element's position to update mouseHoveredHyperlinkMark and
71-
// mouseHoveredHyperlinkMarkRange.
72-
const hoveredHyperlinkElement = event.target;
73-
const posInHoveredHyperlinkMark =
74-
editor.view.posAtDOM(hoveredHyperlinkElement, 0) + 1;
75-
const resolvedPosInHoveredHyperlinkMark = editor.state.doc.resolve(
76-
posInHoveredHyperlinkMark
77-
);
78-
const marksAtPos = resolvedPosInHoveredHyperlinkMark.marks();
79-
80-
for (const mark of marksAtPos) {
81-
if (mark.type.name === editor.schema.mark("link").type.name) {
82-
this.mouseHoveredHyperlinkMark = mark;
83-
this.mouseHoveredHyperlinkMarkRange =
84-
getMarkRange(
85-
resolvedPosInHoveredHyperlinkMark,
86-
mark.type,
87-
mark.attrs
88-
) || undefined;
89-
90-
break;
91-
}
59+
this.editor.view.dom.addEventListener("mouseover", this.mouseOverHandler);
60+
document.addEventListener("scroll", this.scrollHandler);
61+
}
62+
63+
mouseOverHandler = (event: MouseEvent) => {
64+
// Resets the hyperlink mark currently hovered by the mouse cursor.
65+
this.mouseHoveredHyperlinkMark = undefined;
66+
this.mouseHoveredHyperlinkMarkRange = undefined;
67+
68+
this.stopMenuUpdateTimer();
69+
70+
if (
71+
event.target instanceof HTMLAnchorElement &&
72+
event.target.nodeName === "A"
73+
) {
74+
// Finds link mark at the hovered element's position to update mouseHoveredHyperlinkMark and
75+
// mouseHoveredHyperlinkMarkRange.
76+
const hoveredHyperlinkElement = event.target;
77+
const posInHoveredHyperlinkMark =
78+
this.editor.view.posAtDOM(hoveredHyperlinkElement, 0) + 1;
79+
const resolvedPosInHoveredHyperlinkMark = this.editor.state.doc.resolve(
80+
posInHoveredHyperlinkMark
81+
);
82+
const marksAtPos = resolvedPosInHoveredHyperlinkMark.marks();
83+
84+
for (const mark of marksAtPos) {
85+
if (mark.type.name === this.editor.schema.mark("link").type.name) {
86+
this.mouseHoveredHyperlinkMark = mark;
87+
this.mouseHoveredHyperlinkMarkRange =
88+
getMarkRange(
89+
resolvedPosInHoveredHyperlinkMark,
90+
mark.type,
91+
mark.attrs
92+
) || undefined;
93+
94+
break;
9295
}
9396
}
97+
}
9498

95-
this.startMenuUpdateTimer();
99+
this.startMenuUpdateTimer();
96100

97-
return false;
98-
});
99-
}
101+
return false;
102+
};
103+
104+
scrollHandler = () => {
105+
if (this.hyperlinkMark !== undefined) {
106+
this.hyperlinkToolbar.render(this.getDynamicParams(), false);
107+
}
108+
};
100109

101110
update() {
102111
if (!this.editor.view.hasFocus()) {
@@ -187,6 +196,14 @@ class HyperlinkToolbarView {
187196
}
188197
}
189198

199+
destroy() {
200+
this.editor.view.dom.removeEventListener(
201+
"mouseover",
202+
this.mouseOverHandler
203+
);
204+
document.removeEventListener("scroll", this.scrollHandler);
205+
}
206+
190207
getStaticParams(): HyperlinkToolbarStaticParams {
191208
return {
192209
editHyperlink: (url: string, text: string) => {

packages/core/src/shared/plugins/suggestion/SuggestionPlugin.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,16 @@ class SuggestionPluginView<T extends SuggestionItem> {
127127
};
128128

129129
this.suggestionsMenu = suggestionsMenuFactory(this.getStaticParams());
130+
131+
document.addEventListener("scroll", this.handleScroll);
130132
}
131133

134+
handleScroll = () => {
135+
if (this.pluginKey.getState(this.editor._tiptapEditor.state).active) {
136+
this.suggestionsMenu.render(this.getDynamicParams(), false);
137+
}
138+
};
139+
132140
update(view: EditorView, prevState: EditorState) {
133141
const prev = this.pluginKey.getState(prevState);
134142
const next = this.pluginKey.getState(view.state);
@@ -170,6 +178,10 @@ class SuggestionPluginView<T extends SuggestionItem> {
170178
}
171179
}
172180

181+
destroy() {
182+
document.removeEventListener("scroll", this.handleScroll);
183+
}
184+
173185
getStaticParams(): SuggestionsMenuStaticParams<T> {
174186
return {
175187
itemCallback: (item: T) => this.itemCallback(item),

0 commit comments

Comments
 (0)