From 98299ff1d78c4c426ca2fc79856e6d160202a0eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arnaud=20Barr=C3=A9?= Date: Sun, 9 Feb 2025 02:09:16 +0100 Subject: [PATCH] Menu UI fixes [publish] --- CHANGELOG.md | 6 ++++++ package.json | 2 +- playground/src/App.tsx | 1 + src/client.ts | 32 +++++++++++++++++++++++++++----- 4 files changed, 35 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3fdf652..245fea3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 3.1.1 + +- Correctly display component using forwardRef +- Fix menu position when div is taller than viewport +- Use dynamic z-index to always be on top of the targeted element (like MUI dialog) (fixes #9) + ## 3.1.0 - Experimental support for React 19. `_debugSource` [was removed](https://github.com/facebook/react/pull/28265) in React 19. The PR says that tools should lazily generate component stack strace, but I couldn't find a way to use the React devtools globals to generate one for a given Fiber node. I've aksed some React team members about insights on how to make this works, but for now I decided to patch the jsx dev runtime (when serving it) to reinject into Fiber node the source prop added by JSX transform. diff --git a/package.json b/package.json index 87b1472..f40f637 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "vite-plugin-react-click-to-component", "type": "module", - "version": "3.1.0", + "version": "3.1.1", "license": "MIT", "scripts": { "postinstall": "cd playground && bun i", diff --git a/playground/src/App.tsx b/playground/src/App.tsx index 57df1af..ffaed92 100644 --- a/playground/src/App.tsx +++ b/playground/src/App.tsx @@ -24,5 +24,6 @@ export const App = () => (

Click on the Vite and React logos to learn more

+
Tall div
); diff --git a/src/client.ts b/src/client.ts index 981c8d8..9a3b7ca 100644 --- a/src/client.ts +++ b/src/client.ts @@ -9,7 +9,7 @@ style.innerHTML = `[data-click-to-component-target] { } #click-to-component-menu { position: fixed !important; - z-index: 1000 !important; + z-index: 1000; margin-top: 8px !important; margin-bottom: 8px !important; background: #222 !important; @@ -62,6 +62,16 @@ window.addEventListener("mousemove", (event) => { event.target.dataset["clickToComponentTarget"] = "true"; }); +const getMaxZIndex = (target: HTMLElement, current: number) => { + const parent = target.parentElement; + if (!parent || parent === document.body) return current; + const zIndex = parseInt(window.getComputedStyle(parent).zIndex); + return getMaxZIndex( + parent, + isNaN(zIndex) ? current : Math.max(zIndex, current), + ); +}; + window.addEventListener("contextmenu", (event) => { if (!event.altKey) return; const target = event.target; @@ -69,6 +79,8 @@ window.addEventListener("contextmenu", (event) => { event.preventDefault(); const layers = getLayersForElement(target); if (layers.length === 0) return; + const zIndex = getMaxZIndex(target, 999); + if (zIndex > 999) menuElement.style.zIndex = `${zIndex + 1}`; const rect = target.getBoundingClientRect(); if (rect.bottom < window.innerHeight / 2) { menuElement.style.top = `${rect.bottom}px`; @@ -79,9 +91,14 @@ window.addEventListener("contextmenu", (event) => { menuElement.style.top = ""; menuElement.style.maxHeight = `${rect.top - 16}px`; } else { - menuElement.style.bottom = `${window.innerHeight - rect.bottom}px`; + const bottomVisible = rect.bottom < window.innerHeight; + menuElement.style.bottom = `${ + bottomVisible ? window.innerHeight - rect.bottom : 0 + }px`; menuElement.style.top = ""; - menuElement.style.maxHeight = `${rect.bottom - 16}px`; + menuElement.style.maxHeight = `${ + (bottomVisible ? rect.bottom : window.innerHeight) - 16 + }px`; } if (rect.left < window.innerWidth / 2) { menuElement.style.left = `${rect.left}px`; @@ -147,7 +164,10 @@ const getLayersForElement = (element: Element) => { const name = typeof instance.type === "string" ? instance.type - : instance.type.displayName ?? instance.type.name; + : instance.type.displayName ?? + instance.type.name ?? + instance.type.render?.name ?? + "undefined"; layers.push({ name, path }); } instance = instance._debugOwner; @@ -175,7 +195,9 @@ type Fiber = { _debugSource?: Source; _debugInfo?: Source; // Injected by React jsxDev patch for React 19 _debugOwner?: Fiber; - type: string | { displayName?: string; name: string }; + type: + | string + | { displayName?: string; name?: string; render?: () => unknown }; }; const getReactInstanceForElement = (element: Element): Fiber | undefined => {