Skip to content

Commit f85a0d9

Browse files
Update: ShadowRoot support (#849)
* Updated core files to make BlockNote work correctly with Shadow roots & added arabic translation & updated past extension to support pasting images * removed updates to pasteExtension * removed arabic updates * removed comments * Implemented PR feedback * Fixed drop error * Small test fix --------- Co-authored-by: Matthew Lipski <[email protected]>
1 parent 31dd97f commit f85a0d9

File tree

8 files changed

+143
-60
lines changed

8 files changed

+143
-60
lines changed

packages/core/src/extensions/FilePanel/FilePanelPlugin.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export class FilePanelView<I extends InlineContentSchema, S extends StyleSchema>
4949
// Setting capture=true ensures that any parent container of the editor that
5050
// gets scrolled will trigger the scroll event. Scroll events do not bubble
5151
// and so won't propagate to the document by default.
52-
document.addEventListener("scroll", this.scrollHandler, true);
52+
pmView.root.addEventListener("scroll", this.scrollHandler, true);
5353
}
5454

5555
mouseDownHandler = () => {
@@ -69,7 +69,7 @@ export class FilePanelView<I extends InlineContentSchema, S extends StyleSchema>
6969

7070
scrollHandler = () => {
7171
if (this.state?.show) {
72-
const blockElement = document.querySelector(
72+
const blockElement = this.pmView.root.querySelector(
7373
`[data-node-type="blockContainer"][data-id="${this.state.block.id}"]`
7474
)!;
7575

@@ -84,7 +84,7 @@ export class FilePanelView<I extends InlineContentSchema, S extends StyleSchema>
8484
} = this.pluginKey.getState(view.state);
8585

8686
if (!this.state?.show && pluginState.block && this.editor.isEditable) {
87-
const blockElement = document.querySelector(
87+
const blockElement = this.pmView.root.querySelector(
8888
`[data-node-type="blockContainer"][data-id="${pluginState.block.id}"]`
8989
)!;
9090

@@ -124,7 +124,7 @@ export class FilePanelView<I extends InlineContentSchema, S extends StyleSchema>
124124

125125
this.pmView.dom.removeEventListener("dragstart", this.dragstartHandler);
126126

127-
document.removeEventListener("scroll", this.scrollHandler, true);
127+
this.pmView.root.removeEventListener("scroll", this.scrollHandler, true);
128128
}
129129
}
130130

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export class FormattingToolbarView implements PluginView {
6262
// Setting capture=true ensures that any parent container of the editor that
6363
// gets scrolled will trigger the scroll event. Scroll events do not bubble
6464
// and so won't propagate to the document by default.
65-
document.addEventListener("scroll", this.scrollHandler, true);
65+
pmView.root.addEventListener("scroll", this.scrollHandler, true);
6666
}
6767

6868
viewMousedownHandler = () => {
@@ -147,7 +147,7 @@ export class FormattingToolbarView implements PluginView {
147147
this.pmView.dom.removeEventListener("dragstart", this.dragHandler);
148148
this.pmView.dom.removeEventListener("dragover", this.dragHandler);
149149

150-
document.removeEventListener("scroll", this.scrollHandler, true);
150+
this.pmView.root.removeEventListener("scroll", this.scrollHandler, true);
151151
}
152152

153153
closeMenu = () => {

packages/core/src/extensions/LinkToolbar/LinkToolbarPlugin.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,16 @@ class LinkToolbarView implements PluginView {
6161
};
6262

6363
this.pmView.dom.addEventListener("mouseover", this.mouseOverHandler);
64-
document.addEventListener("click", this.clickHandler, true);
64+
this.pmView.root.addEventListener(
65+
"click",
66+
this.clickHandler as EventListener,
67+
true
68+
);
6569

6670
// Setting capture=true ensures that any parent container of the editor that
6771
// gets scrolled will trigger the scroll event. Scroll events do not bubble
6872
// and so won't propagate to the document by default.
69-
document.addEventListener("scroll", this.scrollHandler, true);
73+
this.pmView.root.addEventListener("scroll", this.scrollHandler, true);
7074
}
7175

7276
mouseOverHandler = (event: MouseEvent) => {
@@ -271,8 +275,12 @@ class LinkToolbarView implements PluginView {
271275

272276
destroy() {
273277
this.pmView.dom.removeEventListener("mouseover", this.mouseOverHandler);
274-
document.removeEventListener("scroll", this.scrollHandler, true);
275-
document.removeEventListener("click", this.clickHandler, true);
278+
this.pmView.root.removeEventListener("scroll", this.scrollHandler, true);
279+
this.pmView.root.removeEventListener(
280+
"click",
281+
this.clickHandler as EventListener,
282+
true
283+
);
276284
}
277285
}
278286

packages/core/src/extensions/Placeholder/PlaceholderPlugin.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@ export const PlaceholderPlugin = (
1616
if (nonce) {
1717
styleEl.setAttribute("nonce", nonce);
1818
}
19-
document.head.appendChild(styleEl);
19+
if (editor._tiptapEditor.view.root instanceof ShadowRoot) {
20+
editor._tiptapEditor.view.root.append(styleEl);
21+
} else {
22+
editor._tiptapEditor.view.root.head.appendChild(styleEl);
23+
}
24+
2025
const styleSheet = styleEl.sheet!;
2126

2227
const getBaseSelector = (additionalSelectors = "") =>
@@ -62,7 +67,11 @@ export const PlaceholderPlugin = (
6267

6368
return {
6469
destroy: () => {
65-
document.head.removeChild(styleEl);
70+
if (editor._tiptapEditor.view.root instanceof ShadowRoot) {
71+
editor._tiptapEditor.view.root.removeChild(styleEl);
72+
} else {
73+
editor._tiptapEditor.view.root.head.removeChild(styleEl);
74+
}
6675
},
6776
};
6877
},

packages/core/src/extensions/SideMenu/SideMenuPlugin.ts

Lines changed: 59 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ function setDragImage(view: EditorView, from: number, to = from) {
128128
}
129129

130130
// dataTransfer.setDragImage(element) only works if element is attached to the DOM.
131-
unsetDragImage();
131+
unsetDragImage(view.root);
132132
dragImageElement = parentClone;
133133

134134
// TODO: This is hacky, need a better way of assigning classes to the editor so that they can also be applied to the
@@ -146,12 +146,21 @@ function setDragImage(view: EditorView, from: number, to = from) {
146146
dragImageElement.className =
147147
dragImageElement.className + " bn-drag-preview " + inheritedClasses;
148148

149-
document.body.appendChild(dragImageElement);
149+
if (view.root instanceof ShadowRoot) {
150+
view.root.appendChild(dragImageElement);
151+
} else {
152+
view.root.body.appendChild(dragImageElement);
153+
}
150154
}
151155

152-
function unsetDragImage() {
156+
function unsetDragImage(rootEl: Document | ShadowRoot) {
153157
if (dragImageElement !== undefined) {
154-
document.body.removeChild(dragImageElement);
158+
if (rootEl instanceof ShadowRoot) {
159+
rootEl.removeChild(dragImageElement);
160+
} else {
161+
rootEl.body.appendChild(dragImageElement);
162+
}
163+
155164
dragImageElement = undefined;
156165
}
157166
}
@@ -177,7 +186,7 @@ function dragStart<
177186
top: e.clientY,
178187
};
179188

180-
const elements = document.elementsFromPoint(coords.left, coords.top);
189+
const elements = view.root.elementsFromPoint(coords.left, coords.top);
181190
let blockEl = undefined;
182191

183192
for (const element of elements) {
@@ -283,22 +292,37 @@ export class SideMenuView<
283292
this.pmView.dom.firstChild! as HTMLElement
284293
).getBoundingClientRect().x;
285294

286-
document.body.addEventListener("drop", this.onDrop, true);
287-
document.body.addEventListener("dragover", this.onDragOver);
295+
this.pmView.root.addEventListener(
296+
"drop",
297+
this.onDrop as EventListener,
298+
true
299+
);
300+
this.pmView.root.addEventListener(
301+
"dragover",
302+
this.onDragOver as EventListener
303+
);
288304
this.pmView.dom.addEventListener("dragstart", this.onDragStart);
289305

290306
// Shows or updates menu position whenever the cursor moves, if the menu isn't frozen.
291-
document.body.addEventListener("mousemove", this.onMouseMove, true);
307+
this.pmView.root.addEventListener(
308+
"mousemove",
309+
this.onMouseMove as EventListener,
310+
true
311+
);
292312

293313
// Unfreezes the menu whenever the user clicks.
294314
this.pmView.dom.addEventListener("mousedown", this.onMouseDown);
295315
// Hides and unfreezes the menu whenever the user presses a key.
296-
document.body.addEventListener("keydown", this.onKeyDown, true);
316+
this.pmView.root.addEventListener(
317+
"keydown",
318+
this.onKeyDown as EventListener,
319+
true
320+
);
297321

298322
// Setting capture=true ensures that any parent container of the editor that
299323
// gets scrolled will trigger the scroll event. Scroll events do not bubble
300324
// and so won't propagate to the document by default.
301-
document.addEventListener("scroll", this.onScroll, true);
325+
this.pmView.root.addEventListener("scroll", this.onScroll, true);
302326
}
303327

304328
updateState = () => {
@@ -322,7 +346,10 @@ export class SideMenuView<
322346
top: this.mousePos.y,
323347
};
324348

325-
const elements = document.elementsFromPoint(coords.left, coords.top);
349+
const elements = this.pmView.root.elementsFromPoint(
350+
coords.left,
351+
coords.top
352+
);
326353
let block = undefined;
327354

328355
for (const element of elements) {
@@ -553,13 +580,28 @@ export class SideMenuView<
553580
this.state.show = false;
554581
this.emitUpdate(this.state);
555582
}
556-
document.body.removeEventListener("mousemove", this.onMouseMove, true);
557-
document.body.removeEventListener("dragover", this.onDragOver);
583+
this.pmView.root.removeEventListener(
584+
"mousemove",
585+
this.onMouseMove as EventListener,
586+
true
587+
);
588+
this.pmView.root.removeEventListener(
589+
"dragover",
590+
this.onDragOver as EventListener
591+
);
558592
this.pmView.dom.removeEventListener("dragstart", this.onDragStart);
559-
document.body.removeEventListener("drop", this.onDrop, true);
560-
document.removeEventListener("scroll", this.onScroll, true);
593+
this.pmView.root.removeEventListener(
594+
"drop",
595+
this.onDrop as EventListener,
596+
true
597+
);
598+
this.pmView.root.removeEventListener("scroll", this.onScroll, true);
561599
this.pmView.dom.removeEventListener("mousedown", this.onMouseDown);
562-
document.body.removeEventListener("keydown", this.onKeyDown, true);
600+
this.pmView.root.removeEventListener(
601+
"keydown",
602+
this.onKeyDown as EventListener,
603+
true
604+
);
563605
}
564606

565607
addBlock() {
@@ -665,7 +707,7 @@ export class SideMenuProsemirrorPlugin<
665707
/**
666708
* Handles drag & drop events for blocks.
667709
*/
668-
blockDragEnd = () => unsetDragImage();
710+
blockDragEnd = () => unsetDragImage(this.editor.prosemirrorView.root);
669711
/**
670712
* Freezes the side menu. When frozen, the side menu will stay
671713
* attached to the same block regardless of which block is hovered by the

packages/core/src/extensions/SuggestionMenu/SuggestionPlugin.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class SuggestionMenuView<
2020
> {
2121
public state?: SuggestionMenuState;
2222
public emitUpdate: (triggerCharacter: string) => void;
23-
23+
private rootEl?: Document | ShadowRoot;
2424
pluginState: SuggestionPluginState;
2525

2626
constructor(
@@ -37,15 +37,17 @@ class SuggestionMenuView<
3737
emitUpdate(menuName, this.state);
3838
};
3939

40+
this.rootEl = this.editor._tiptapEditor.view.root;
41+
4042
// Setting capture=true ensures that any parent container of the editor that
4143
// gets scrolled will trigger the scroll event. Scroll events do not bubble
4244
// and so won't propagate to the document by default.
43-
document.addEventListener("scroll", this.handleScroll, true);
45+
this.rootEl.addEventListener("scroll", this.handleScroll, true);
4446
}
4547

4648
handleScroll = () => {
4749
if (this.state?.show) {
48-
const decorationNode = document.querySelector(
50+
const decorationNode = this.rootEl?.querySelector(
4951
`[data-decoration-id="${this.pluginState!.decorationId}"]`
5052
);
5153
this.state.referencePos = decorationNode!.getBoundingClientRect();
@@ -79,7 +81,7 @@ class SuggestionMenuView<
7981
return;
8082
}
8183

82-
const decorationNode = document.querySelector(
84+
const decorationNode = this.rootEl?.querySelector(
8385
`[data-decoration-id="${this.pluginState!.decorationId}"]`
8486
);
8587

@@ -95,7 +97,7 @@ class SuggestionMenuView<
9597
}
9698

9799
destroy() {
98-
document.removeEventListener("scroll", this.handleScroll, true);
100+
this.rootEl?.removeEventListener("scroll", this.handleScroll, true);
99101
}
100102

101103
closeMenu = () => {

0 commit comments

Comments
 (0)