Merge pull request #791 from mathuo/774-drag-and-drop-not-working-for-iframe-panels-within-a-shadow-dom

bug: disable iframes within shadowdom during dnd
This commit is contained in:
mathuo 2025-03-16 20:33:07 +00:00 committed by GitHub
commit b231367e44
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 78 additions and 7 deletions

View File

@ -112,8 +112,11 @@ export function isAncestor(
return false;
}
export function getElementsByTagName(tag: string): HTMLElement[] {
return Array.prototype.slice.call(document.getElementsByTagName(tag), 0);
export function getElementsByTagName(
tag: string,
document: ParentNode
): HTMLElement[] {
return Array.prototype.slice.call(document.querySelectorAll(tag), 0);
}
export interface IFocusTracker extends IDisposable {
@ -288,11 +291,36 @@ export function addTestId(element: HTMLElement, id: string): void {
element.setAttribute('data-testid', id);
}
export function disableIframePointEvents() {
const iframes: HTMLElement[] = [
...getElementsByTagName('iframe'),
...getElementsByTagName('webview'),
];
/**
* Should be more efficient than element.querySelectorAll("*") since there
* is no need to store every element in-memory using this approach
*/
function allTagsNamesInclusiveOfShadowDoms(tagNames: string[]) {
const iframes: HTMLElement[] = [];
function findIframesInNode(node: Element) {
if (node.nodeType === Node.ELEMENT_NODE) {
if (tagNames.includes(node.tagName)) {
iframes.push(node as HTMLElement);
}
if (node.shadowRoot) {
findIframesInNode(<any>node.shadowRoot);
}
for (const child of node.children) {
findIframesInNode(child);
}
}
}
findIframesInNode(document.documentElement);
return iframes;
}
export function disableIframePointEvents(rootNode: ParentNode = document) {
const iframes = allTagsNamesInclusiveOfShadowDoms(['IFRAME', 'WEBVIEW']);
const original = new WeakMap<HTMLElement, string>(); // don't hold onto HTMLElement references longer than required

View File

@ -8,6 +8,7 @@ import {
DockviewTheme,
} from 'dockview';
import * as React from 'react';
import * as ReactDOM from 'react-dom/client';
import './app.scss';
import { defaultConfig } from './defaultLayout';
import { GridActions } from './gridActions';
@ -31,6 +32,20 @@ const Option = (props: {
);
};
const ShadowIframe = (props: IDockviewPanelProps) => {
return (
<iframe
onMouseDown={() => {
if (!props.api.isActive) {
props.api.setActive();
}
}}
style={{ border: 'none', width: '100%', height: '100%' }}
src="https://dockview.dev"
/>
);
};
const components = {
default: (props: IDockviewPanelProps) => {
const isDebug = React.useContext(DebugContext);
@ -110,6 +125,7 @@ const components = {
}
}}
style={{
border: 'none',
width: '100%',
height: '100%',
}}
@ -117,6 +133,33 @@ const components = {
/>
);
},
shadowDom: (props: IDockviewPanelProps) => {
const ref = React.useRef<HTMLDivElement>(null);
React.useEffect(() => {
if (!ref.current) {
return;
}
const shadow = ref.current.attachShadow({
mode: 'open',
});
const shadowRoot = document.createElement('div');
shadowRoot.style.height = '100%';
shadow.appendChild(shadowRoot);
const root = ReactDOM.createRoot(shadowRoot);
root.render(<ShadowIframe {...props} />);
return () => {
root.unmount();
};
}, []);
return <div style={{ height: '100%' }} ref={ref}></div>;
},
};
const headerComponents = {