nuvox 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +42 -0
- package/dist/react/index.cjs +28 -1
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.js +28 -1
- package/dist/react/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,48 @@
|
|
|
3
3
|
All notable changes to this project are documented here.
|
|
4
4
|
Format loosely follows [Keep a Changelog](https://keepachangelog.com/).
|
|
5
5
|
|
|
6
|
+
## [0.1.4]
|
|
7
|
+
|
|
8
|
+
### Fixed
|
|
9
|
+
- The `[data-nuvox-portal]` exemption added in 0.1.3 fixed a nested
|
|
10
|
+
overlay closing its own container, but was too broad: it exempted
|
|
11
|
+
a click inside ANY portal, including one belonging to a completely
|
|
12
|
+
unrelated SIBLING overlay that merely shares a common enclosing
|
|
13
|
+
overlay. Two `DropdownMenu`s both living inside the same `Popover`
|
|
14
|
+
are siblings of each other, not nested in each other — opening one
|
|
15
|
+
while the other was open left both on screen at once, only closing
|
|
16
|
+
on a re-click or a selection. `useClickOutside` now walks the real
|
|
17
|
+
relationship instead of doing a blanket portal check: it follows
|
|
18
|
+
"which portal contains this click" → "whose trigger opened that
|
|
19
|
+
portal" → "is that trigger contained in my own refs, or itself
|
|
20
|
+
inside another portal" (repeating for arbitrarily deep nesting) —
|
|
21
|
+
which correctly tells "genuinely nested inside me" apart from
|
|
22
|
+
"elsewhere inside a shared ancestor," however many levels deep.
|
|
23
|
+
`ThemedPortal` now tracks each portal's own anchor element for this
|
|
24
|
+
to walk. See the "Sibling overlays" and first two "Nested overlay"
|
|
25
|
+
cases in `tests/nested-overlays.test.tsx`.
|
|
26
|
+
- A `DropdownMenu` (or `Select`/`Combobox`/`ColorPicker`) nested
|
|
27
|
+
inside an open `Popover` rendered BEHIND it: each component's CSS
|
|
28
|
+
sets a fixed z-index tier (`Popover` 1060, `DropdownMenu`/`Select`/
|
|
29
|
+
`Combobox` 1050, `Tooltip` 1100, `Toast` 1200, ...), correct for
|
|
30
|
+
independent, non-nested overlays but backwards for this one
|
|
31
|
+
relationship — the nested overlay needs to paint above its own
|
|
32
|
+
container regardless of which tier either belongs to. `ThemedPortal`
|
|
33
|
+
now detects when an overlay's own trigger sits inside another,
|
|
34
|
+
already-open overlay's portaled content, and in that case only,
|
|
35
|
+
assigns it an inline z-index above the shared 1150 base (below
|
|
36
|
+
`Toast`'s 1200, which stays on top of everything as a global
|
|
37
|
+
notification regardless of nesting) via `position: relative` on the
|
|
38
|
+
portal wrapper itself — not the component's own CSS class, so
|
|
39
|
+
ordinary non-nested overlays are completely untouched. Verified safe
|
|
40
|
+
for `Popover`/`DropdownMenu` specifically: both position their
|
|
41
|
+
content with inline `position: fixed` (`useFloatingPosition`), which
|
|
42
|
+
isn't affected by an ancestor's `position: relative` — only
|
|
43
|
+
`transform`/`filter`/etc. would change a fixed descendant's
|
|
44
|
+
containing block. Not verified against every possible pairing (e.g.
|
|
45
|
+
a `position: absolute` `Tooltip` ending up as the nested one) — this
|
|
46
|
+
covers the reported and tested composition, not every combination.
|
|
47
|
+
|
|
6
48
|
## [0.1.3]
|
|
7
49
|
|
|
8
50
|
### Fixed
|
package/dist/react/index.cjs
CHANGED
|
@@ -1448,6 +1448,12 @@ function withAccentOverride(color, children) {
|
|
|
1448
1448
|
var import_react = require("react");
|
|
1449
1449
|
var import_react_dom = require("react-dom");
|
|
1450
1450
|
var TRACKED_ATTRIBUTES = ["data-theme", "data-accent"];
|
|
1451
|
+
var portalAnchors = /* @__PURE__ */ new WeakMap();
|
|
1452
|
+
function getPortalAnchor(portalNode) {
|
|
1453
|
+
return portalAnchors.get(portalNode) ?? null;
|
|
1454
|
+
}
|
|
1455
|
+
var nextNestedStackOffset = 0;
|
|
1456
|
+
var NESTED_OVERRIDE_BASE_Z_INDEX = 1150;
|
|
1451
1457
|
function ThemedPortal({ anchorRef, children, container }) {
|
|
1452
1458
|
const portalElRef = (0, import_react.useRef)(null);
|
|
1453
1459
|
if (!portalElRef.current && typeof document !== "undefined") {
|
|
@@ -1459,6 +1465,14 @@ function ThemedPortal({ anchorRef, children, container }) {
|
|
|
1459
1465
|
const portalEl = portalElRef.current;
|
|
1460
1466
|
if (!portalEl) return;
|
|
1461
1467
|
target.appendChild(portalEl);
|
|
1468
|
+
if (anchorRef.current) {
|
|
1469
|
+
portalAnchors.set(portalEl, anchorRef.current);
|
|
1470
|
+
const enclosingPortal = anchorRef.current.closest("[data-nuvox-portal]");
|
|
1471
|
+
if (enclosingPortal) {
|
|
1472
|
+
portalEl.style.position = "relative";
|
|
1473
|
+
portalEl.style.zIndex = String(NESTED_OVERRIDE_BASE_Z_INDEX + nextNestedStackOffset++);
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1462
1476
|
function syncAttribute(attr) {
|
|
1463
1477
|
const anchor2 = anchorRef.current;
|
|
1464
1478
|
if (!anchor2 || !portalEl) return;
|
|
@@ -16529,6 +16543,19 @@ function getOpenIntent(key) {
|
|
|
16529
16543
|
|
|
16530
16544
|
// src/theme-engine/useClickOutside.ts
|
|
16531
16545
|
var import_react25 = require("react");
|
|
16546
|
+
function isNestedWithinRefs(target, refs) {
|
|
16547
|
+
if (!(target instanceof Element)) return false;
|
|
16548
|
+
let current = target;
|
|
16549
|
+
for (let depth = 0; depth < 20; depth++) {
|
|
16550
|
+
const portalNode = current.closest("[data-nuvox-portal]");
|
|
16551
|
+
if (!portalNode) return false;
|
|
16552
|
+
const anchor = getPortalAnchor(portalNode);
|
|
16553
|
+
if (!anchor) return false;
|
|
16554
|
+
if (refs.some((ref) => ref.current?.contains(anchor))) return true;
|
|
16555
|
+
current = anchor;
|
|
16556
|
+
}
|
|
16557
|
+
return false;
|
|
16558
|
+
}
|
|
16532
16559
|
function useClickOutside(isOpen, refs, onOutsideClick) {
|
|
16533
16560
|
(0, import_react25.useEffect)(() => {
|
|
16534
16561
|
if (!isOpen) return;
|
|
@@ -16536,7 +16563,7 @@ function useClickOutside(isOpen, refs, onOutsideClick) {
|
|
|
16536
16563
|
const target = e.target;
|
|
16537
16564
|
const isInside = refs.some((ref) => ref.current?.contains(target));
|
|
16538
16565
|
if (isInside) return;
|
|
16539
|
-
if (target
|
|
16566
|
+
if (isNestedWithinRefs(target, refs)) return;
|
|
16540
16567
|
onOutsideClick();
|
|
16541
16568
|
}
|
|
16542
16569
|
document.addEventListener("pointerdown", onPointerDown, true);
|