Skip to content

Commit 51c9b62

Browse files
committed
Add support for eventing inside of a shadow dom
1 parent 78e69f3 commit 51c9b62

File tree

3 files changed

+127
-7
lines changed

3 files changed

+127
-7
lines changed

packages/core/src/common/utils.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { deepEqual } from "./support.js";
55
export function useEventListener<K extends keyof HTMLElementEventMap>(
66
eventName: K,
77
handler: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any,
8-
element: HTMLElement | Window | null,
8+
element: HTMLElement | Window | Document | null,
99
passive: boolean,
1010
capture = false
1111
) {
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import React from "react";
2+
3+
import { DataEditorAll as DataEditor } from "../../data-editor-all.js";
4+
import {
5+
BeautifulWrapper,
6+
Description,
7+
PropName,
8+
useMockDataGenerator,
9+
defaultProps,
10+
} from "../../data-editor/stories/utils.js";
11+
import { SimpleThemeWrapper } from "../../stories/story-utils.js";
12+
import ReactDOM from "react-dom";
13+
14+
export default {
15+
title: "Glide-Data-Grid/DataEditor Demos",
16+
17+
decorators: [
18+
(Story: React.ComponentType) => (
19+
<SimpleThemeWrapper>
20+
<BeautifulWrapper
21+
title="Shadow DOM"
22+
description={
23+
<Description>
24+
Columns in the data grid may be grouped by setting their <PropName>group</PropName>{" "}
25+
property.
26+
</Description>
27+
}>
28+
<Story />
29+
</BeautifulWrapper>
30+
</SimpleThemeWrapper>
31+
),
32+
],
33+
};
34+
35+
export const ShadowDOM: React.VFC = () => {
36+
const { cols, getCellContent } = useMockDataGenerator(20, false, false);
37+
38+
return (
39+
<ShadowDOMWrapper
40+
render={() => (
41+
<DataEditor
42+
{...defaultProps}
43+
getCellContent={getCellContent}
44+
columns={cols}
45+
rows={1000}
46+
height={"100%"}
47+
rowMarkers="both"
48+
/>
49+
)}
50+
/>
51+
);
52+
};
53+
54+
const copyStylesToShadowRoot = (shadowRoot: ShadowRoot) => {
55+
const styleElement = document.createElement("style");
56+
for (const sheet of document.styleSheets) {
57+
try {
58+
if (sheet.cssRules !== undefined) {
59+
// Check if cssRules are accessible
60+
const rules = [...sheet.cssRules].map(rule => rule.cssText).join("\n");
61+
styleElement.append(document.createTextNode(rules));
62+
}
63+
} catch (error: unknown) {
64+
// eslint-disable-next-line no-console
65+
console.warn("Cannot access stylesheet rules due to CORS policy", error);
66+
}
67+
}
68+
shadowRoot.append(styleElement);
69+
};
70+
71+
const ShadowDOMWrapper: React.FC<{
72+
className?: string;
73+
render: () => React.ReactElement;
74+
}> = ({ className, render }) => {
75+
const hostRef = React.useRef<HTMLDivElement | null>(null);
76+
const didMountRef = React.useRef(true);
77+
78+
React.useEffect(() => {
79+
if (hostRef.current === null || didMountRef.current) {
80+
didMountRef.current = false;
81+
return;
82+
}
83+
84+
const host = hostRef.current;
85+
const shadowRoot = host.attachShadow({ mode: "open" });
86+
87+
(window as any).glideShadowRoot = shadowRoot;
88+
89+
copyStylesToShadowRoot(shadowRoot);
90+
91+
// Create a div to serve as the react root container inside the shadow DOM
92+
const reactRootContainer = document.createElement("div");
93+
reactRootContainer.style.height = "100%";
94+
shadowRoot.append(reactRootContainer);
95+
shadowRoot.pare;
96+
97+
// Create a React root and render the children inside it
98+
ReactDOM.render(<>{render()}</>, reactRootContainer);
99+
100+
// Clean up when the component unmounts
101+
return () => {
102+
// reactRoot.unmount()
103+
reactRootContainer.remove();
104+
};
105+
}, [render]);
106+
107+
return <div ref={hostRef} className={className} style={{ height: "100%" }} />;
108+
};

packages/core/src/internal/data-grid/data-grid.tsx

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,9 @@ const DataGrid: React.ForwardRefRenderFunction<DataGridRef, DataGridProps> = (p,
383383
const cellXOffset = Math.max(freezeColumns, Math.min(columns.length - 1, cellXOffsetReal));
384384

385385
const ref = React.useRef<HTMLCanvasElement | null>(null);
386+
const windowEventTargetRef = React.useRef<Document | Window>(window);
387+
const windowEventTarget = windowEventTargetRef.current;
388+
386389
const imageLoader = imageWindowLoader;
387390
const damageRegion = React.useRef<CellSet | undefined>();
388391
const [scrolling, setScrolling] = React.useState<boolean>(false);
@@ -1076,8 +1079,8 @@ const DataGrid: React.ForwardRefRenderFunction<DataGridRef, DataGridProps> = (p,
10761079
onMouseDown,
10771080
]
10781081
);
1079-
useEventListener("touchstart", onMouseDownImpl, window, false);
1080-
useEventListener("mousedown", onMouseDownImpl, window, false);
1082+
useEventListener("touchstart", onMouseDownImpl, windowEventTarget, false);
1083+
useEventListener("mousedown", onMouseDownImpl, windowEventTarget, false);
10811084

10821085
const lastUpTime = React.useRef(0);
10831086

@@ -1153,8 +1156,8 @@ const DataGrid: React.ForwardRefRenderFunction<DataGridRef, DataGridProps> = (p,
11531156
},
11541157
[onMouseUp, eventTargetRef, getMouseArgsForPosition, isOverHeaderElement, groupHeaderActionForEvent]
11551158
);
1156-
useEventListener("mouseup", onMouseUpImpl, window, false);
1157-
useEventListener("touchend", onMouseUpImpl, window, false);
1159+
useEventListener("mouseup", onMouseUpImpl, windowEventTarget, false);
1160+
useEventListener("touchend", onMouseUpImpl, windowEventTarget, false);
11581161

11591162
const onClickImpl = React.useCallback(
11601163
(ev: MouseEvent | TouchEvent) => {
@@ -1217,7 +1220,7 @@ const DataGrid: React.ForwardRefRenderFunction<DataGridRef, DataGridProps> = (p,
12171220
groupHeaderActionForEvent,
12181221
]
12191222
);
1220-
useEventListener("click", onClickImpl, window, false);
1223+
useEventListener("click", onClickImpl, windowEventTarget, false);
12211224

12221225
const onContextMenuImpl = React.useCallback(
12231226
(ev: MouseEvent) => {
@@ -1342,7 +1345,7 @@ const DataGrid: React.ForwardRefRenderFunction<DataGridRef, DataGridProps> = (p,
13421345
damageInternal,
13431346
]
13441347
);
1345-
useEventListener("mousemove", onMouseMoveImpl, window, true);
1348+
useEventListener("mousemove", onMouseMoveImpl, windowEventTarget, true);
13461349

13471350
const onKeyDownImpl = React.useCallback(
13481351
(event: React.KeyboardEvent<HTMLCanvasElement>) => {
@@ -1410,6 +1413,15 @@ const DataGrid: React.ForwardRefRenderFunction<DataGridRef, DataGridProps> = (p,
14101413
if (canvasRef !== undefined) {
14111414
canvasRef.current = instance;
14121415
}
1416+
1417+
if (instance === null) {
1418+
windowEventTargetRef.current = window;
1419+
} else {
1420+
const docRoot = instance.getRootNode();
1421+
1422+
if (docRoot === document) windowEventTargetRef.current = window;
1423+
windowEventTargetRef.current = docRoot as any;
1424+
}
14131425
},
14141426
[canvasRef]
14151427
);

0 commit comments

Comments
 (0)