@stackone/malachite 2.27.2 → 2.29.0
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/COMPONENTS.md +2 -0
- package/dist/components/CommandPalette.js +150 -0
- package/dist/components/CommandPalette.js.map +1 -0
- package/dist/components/Keycap.js +38 -0
- package/dist/components/Keycap.js.map +1 -0
- package/dist/components/SidePanelNavFooter.js +21 -37
- package/dist/components/SidePanelNavFooter.js.map +1 -1
- package/dist/components/Surface.js +130 -133
- package/dist/components/Surface.js.map +1 -1
- package/dist/components/asChild.js +13 -0
- package/dist/components/asChild.js.map +1 -0
- package/dist/components/overlayBehaviour.js +82 -73
- package/dist/components/overlayBehaviour.js.map +1 -1
- package/dist/components/portalOverlay.js +12 -11
- package/dist/components/portalOverlay.js.map +1 -1
- package/dist/index.d.ts +132 -18
- package/dist/index.js +333 -329
- package/dist/index.js.map +1 -1
- package/dist/tailwind.css.js +1 -1
- package/package.json +1 -1
|
@@ -1,118 +1,127 @@
|
|
|
1
|
-
import { useSyncExternalStore as
|
|
2
|
-
import { getDeepActiveElement as
|
|
3
|
-
import { pushEscapeLayer as
|
|
4
|
-
import { FOCUS_RING as
|
|
5
|
-
import { OVERLAY_Z_BASE as
|
|
6
|
-
import { lockBodyScroll as
|
|
7
|
-
import { leaveTopLayer as
|
|
8
|
-
const
|
|
1
|
+
import { useSyncExternalStore as m, useRef as l, useEffect as L, useLayoutEffect as v, useCallback as k } from "react";
|
|
2
|
+
import { getDeepActiveElement as E } from "./activeElement.js";
|
|
3
|
+
import { pushEscapeLayer as N } from "./escapeLayers.js";
|
|
4
|
+
import { FOCUS_RING as z } from "./focusRing.js";
|
|
5
|
+
import { OVERLAY_Z_BASE as I, ensureElevatedOverlayStyles as U } from "./portalOverlay.js";
|
|
6
|
+
import { lockBodyScroll as q } from "./scrollLock.js";
|
|
7
|
+
import { leaveTopLayer as A, raiseToTopLayer as K } from "./topLayer.js";
|
|
8
|
+
const X = `flex shrink-0 cursor-pointer items-center justify-center rounded-md border-0 bg-transparent p-[4px] text-[var(--malachite-text-secondary)] hover:text-[var(--malachite-text-primary)] transition-colors ${z}`, C = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])', o = [], f = /* @__PURE__ */ new Set(), x = /* @__PURE__ */ new Map(), b = /* @__PURE__ */ new Set(), M = () => {
|
|
9
9
|
};
|
|
10
10
|
let p = [];
|
|
11
|
-
const
|
|
12
|
-
const e = f.size ?
|
|
13
|
-
var
|
|
14
|
-
return (
|
|
11
|
+
const V = () => {
|
|
12
|
+
const e = f.size ? o.map((t) => {
|
|
13
|
+
var n;
|
|
14
|
+
return (n = x.get(t)) == null ? void 0 : n();
|
|
15
15
|
}).filter((t) => !!t) : [];
|
|
16
|
-
if (e.length === p.length && e.every((t,
|
|
16
|
+
if (e.length === p.length && e.every((t, n) => t === p[n]))
|
|
17
17
|
return;
|
|
18
18
|
const r = new Set(e);
|
|
19
19
|
for (const t of p)
|
|
20
|
-
r.has(t) ||
|
|
20
|
+
r.has(t) || A(t);
|
|
21
21
|
for (const t of e)
|
|
22
|
-
|
|
22
|
+
K(t);
|
|
23
23
|
p = e;
|
|
24
|
-
},
|
|
24
|
+
}, h = () => {
|
|
25
25
|
for (const e of b)
|
|
26
26
|
e();
|
|
27
|
-
},
|
|
27
|
+
}, _ = (e) => (b.add(e), () => {
|
|
28
28
|
b.delete(e);
|
|
29
|
-
}),
|
|
29
|
+
}), g = (e) => {
|
|
30
30
|
if (e.key !== "Tab" || e.defaultPrevented)
|
|
31
31
|
return;
|
|
32
32
|
let r = null;
|
|
33
|
-
for (let a =
|
|
34
|
-
r =
|
|
33
|
+
for (let a = o.length - 1; a >= 0 && !r; a -= 1)
|
|
34
|
+
r = o[a]();
|
|
35
35
|
if (!r)
|
|
36
36
|
return;
|
|
37
|
-
const t = Array.from(r.querySelectorAll(
|
|
37
|
+
const t = Array.from(r.querySelectorAll(C));
|
|
38
38
|
if (t.length === 0) {
|
|
39
39
|
e.preventDefault(), r.focus();
|
|
40
40
|
return;
|
|
41
41
|
}
|
|
42
|
-
const
|
|
43
|
-
e.shiftKey ? (!u ||
|
|
44
|
-
},
|
|
45
|
-
const t =
|
|
46
|
-
t !== -1 &&
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
}),
|
|
50
|
-
|
|
51
|
-
() =>
|
|
42
|
+
const n = t[0], d = t[t.length - 1], c = E(), u = c !== null && r.contains(c);
|
|
43
|
+
e.shiftKey ? (!u || c === n || c === r) && (e.preventDefault(), d.focus()) : (!u || c === d) && (e.preventDefault(), n.focus());
|
|
44
|
+
}, Y = (e, r) => (o.length === 0 && document.addEventListener("keydown", g), o.push(e), x.set(e, r), h(), () => {
|
|
45
|
+
const t = o.lastIndexOf(e);
|
|
46
|
+
t !== -1 && o.splice(t, 1), f.delete(e), x.delete(e);
|
|
47
|
+
const n = r();
|
|
48
|
+
n && A(n), o.length === 0 && document.removeEventListener("keydown", g), h();
|
|
49
|
+
}), P = () => m(
|
|
50
|
+
_,
|
|
51
|
+
() => o.length > 0,
|
|
52
52
|
() => !1
|
|
53
|
-
),
|
|
53
|
+
), w = (e, r) => {
|
|
54
|
+
if (r === "content") {
|
|
55
|
+
const t = E();
|
|
56
|
+
if (t && e.contains(t))
|
|
57
|
+
return;
|
|
58
|
+
(e.querySelector(C) ?? e).focus();
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
e.focus();
|
|
62
|
+
}, ee = ({
|
|
54
63
|
open: e,
|
|
55
64
|
onClose: r,
|
|
56
65
|
elevated: t = !1,
|
|
57
|
-
hostRef:
|
|
66
|
+
hostRef: n,
|
|
67
|
+
initialFocus: d = "panel"
|
|
58
68
|
}) => {
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}, [r]),
|
|
69
|
+
const c = l(null), u = l(() => c.current).current, a = l(null), i = l(null), O = l(d), T = l(r);
|
|
70
|
+
L(() => {
|
|
71
|
+
T.current = r;
|
|
72
|
+
}, [r]), L(() => {
|
|
63
73
|
if (e)
|
|
64
|
-
return
|
|
65
|
-
const
|
|
66
|
-
|
|
74
|
+
return a.current = E(), () => {
|
|
75
|
+
const s = a.current;
|
|
76
|
+
a.current = null, s != null && s.isConnected && s.focus();
|
|
67
77
|
};
|
|
68
|
-
}, [e]),
|
|
69
|
-
var E;
|
|
78
|
+
}, [e]), L(() => {
|
|
70
79
|
if (!e)
|
|
71
80
|
return;
|
|
72
|
-
|
|
73
|
-
const
|
|
74
|
-
return
|
|
75
|
-
|
|
81
|
+
i.current = u;
|
|
82
|
+
const s = N(() => T.current()), R = q(), D = Y(u, () => (n == null ? void 0 : n.current) ?? null);
|
|
83
|
+
return c.current && w(c.current, O.current), () => {
|
|
84
|
+
i.current = null, D(), s(), R();
|
|
76
85
|
};
|
|
77
|
-
}, [e,
|
|
86
|
+
}, [e, n, u]), v(() => {
|
|
78
87
|
if (!(!e || !t))
|
|
79
|
-
return f.add(
|
|
80
|
-
f.delete(
|
|
88
|
+
return f.add(u), h(), () => {
|
|
89
|
+
f.delete(u), h();
|
|
81
90
|
};
|
|
82
|
-
}, [e, t,
|
|
83
|
-
const
|
|
84
|
-
(
|
|
91
|
+
}, [e, t, u]);
|
|
92
|
+
const S = k(
|
|
93
|
+
(s) => e ? _(s) : M,
|
|
85
94
|
[e]
|
|
86
|
-
),
|
|
87
|
-
|
|
95
|
+
), B = m(
|
|
96
|
+
S,
|
|
88
97
|
// Gated on `open` so an overlay fading out stops claiming modal. Before it registers
|
|
89
98
|
// it is the one about to go on top, or aria-modal would drop for a frame on open.
|
|
90
|
-
() => e && (
|
|
99
|
+
() => e && (i.current === null || o[o.length - 1] === i.current),
|
|
91
100
|
() => !0
|
|
92
|
-
),
|
|
93
|
-
|
|
101
|
+
), y = m(
|
|
102
|
+
S,
|
|
94
103
|
() => e && (t || f.size > 0),
|
|
95
104
|
() => !1
|
|
96
|
-
),
|
|
97
|
-
|
|
105
|
+
), F = m(
|
|
106
|
+
S,
|
|
98
107
|
() => {
|
|
99
|
-
const
|
|
100
|
-
return
|
|
108
|
+
const s = i.current;
|
|
109
|
+
return I + (e && s ? Math.max(0, o.indexOf(s)) : 0);
|
|
101
110
|
},
|
|
102
|
-
() =>
|
|
111
|
+
() => I
|
|
103
112
|
);
|
|
104
|
-
return
|
|
105
|
-
|
|
106
|
-
}, [
|
|
107
|
-
|
|
108
|
-
}), { ref:
|
|
109
|
-
|
|
110
|
-
}, []), isTopmost:
|
|
113
|
+
return v(() => {
|
|
114
|
+
y && U();
|
|
115
|
+
}, [y]), v(() => {
|
|
116
|
+
V();
|
|
117
|
+
}), { ref: k((s) => {
|
|
118
|
+
c.current = s, s && w(s, O.current);
|
|
119
|
+
}, []), isTopmost: B, zIndex: y ? void 0 : F, elevated: y };
|
|
111
120
|
};
|
|
112
121
|
export {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
122
|
+
C as FOCUSABLE_SELECTOR,
|
|
123
|
+
X as OVERLAY_ICON_BUTTON_CLASSES,
|
|
124
|
+
P as useHasOpenOverlay,
|
|
125
|
+
ee as useOverlayBehaviour
|
|
117
126
|
};
|
|
118
127
|
//# sourceMappingURL=overlayBehaviour.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"overlayBehaviour.js","sources":["../../src/components/overlayBehaviour.ts"],"sourcesContent":["import { useCallback, useEffect, useLayoutEffect, useRef, useSyncExternalStore } from 'react';\nimport { getDeepActiveElement } from './activeElement';\nimport { pushEscapeLayer } from './escapeLayers';\nimport { FOCUS_RING } from './focusRing';\nimport { OVERLAY_Z_BASE, ensureElevatedOverlayStyles } from './portalOverlay';\nimport { lockBodyScroll } from './scrollLock';\nimport { leaveTopLayer, raiseToTopLayer } from './topLayer';\n\n/** Back/close button chrome, shared so the two overlays cannot drift apart. */\nexport const OVERLAY_ICON_BUTTON_CLASSES = `flex shrink-0 cursor-pointer items-center justify-center rounded-md border-0 bg-transparent p-[4px] text-[var(--malachite-text-secondary)] hover:text-[var(--malachite-text-primary)] transition-colors ${FOCUS_RING}`;\n\nexport const FOCUSABLE_SELECTOR =\n 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n\ntype PanelSource = () => HTMLElement | null;\n\n// LIFO like escapeLayers: with a listener per overlay the first-registered one wins, which\n// sent Tab from a dialog into the panel behind it.\nconst trapLayers: PanelSource[] = [];\n// Elevation belongs to the stack: a Modal left in the base band opens behind the panel that\n// elevated it.\nconst elevatedLayers = new Set<PanelSource>();\n// The shadow host per layer, so promotion can replay in stack order.\nconst hostSources = new Map<PanelSource, () => HTMLElement | null>();\nconst stackListeners = new Set<() => void>();\nconst NO_SUBSCRIPTION = () => {\n // Nothing to tear down: a closed overlay never subscribed.\n};\n\n// Top-layer paint order is entry order, so replay the whole stack bottom-up rather than letting\n// each overlay promote itself: those effects run in React tree order, which need not match.\nlet promoted: HTMLElement[] = [];\n\nconst syncTopLayer = (): void => {\n const wanted = elevatedLayers.size\n ? trapLayers\n .map((layer) => hostSources.get(layer)?.())\n .filter((host): host is HTMLElement => Boolean(host))\n : [];\n // Compared, not reapplied: this runs on every commit, because a host can appear a commit\n // after the layer that owns it, and re-entering the top layer unprompted costs a recalc.\n if (\n wanted.length === promoted.length &&\n wanted.every((host, index) => host === promoted[index])\n ) {\n return;\n }\n const stillWanted = new Set(wanted);\n for (const host of promoted) {\n if (!stillWanted.has(host)) {\n leaveTopLayer(host);\n }\n }\n for (const host of wanted) {\n raiseToTopLayer(host);\n }\n promoted = wanted;\n};\n\nconst notifyStackChanged = (): void => {\n for (const listener of stackListeners) {\n listener();\n }\n};\n\nconst subscribeToStack = (listener: () => void): (() => void) => {\n stackListeners.add(listener);\n return () => {\n stackListeners.delete(listener);\n };\n};\n\nconst handleTabKeyDown = (event: KeyboardEvent): void => {\n if (event.key !== 'Tab' || event.defaultPrevented) {\n return;\n }\n // Walk down rather than bail: React detaches refs before flushing the cleanup that pops\n // the layer, so the topmost can be registered-but-null and Tab would escape entirely.\n let panel: HTMLElement | null = null;\n for (let index = trapLayers.length - 1; index >= 0 && !panel; index -= 1) {\n panel = trapLayers[index]();\n }\n if (!panel) {\n return;\n }\n const focusables = Array.from(panel.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR));\n if (focusables.length === 0) {\n event.preventDefault();\n panel.focus();\n return;\n }\n const first = focusables[0];\n const last = focusables[focusables.length - 1];\n const active = getDeepActiveElement();\n const activeInPanel = active !== null && panel.contains(active);\n if (event.shiftKey) {\n if (!activeInPanel || active === first || active === panel) {\n event.preventDefault();\n last.focus();\n }\n } else if (!activeInPanel || active === last) {\n event.preventDefault();\n first.focus();\n }\n};\n\n// On `document` so it also catches focus that escaped behind the overlay. The trap reaches\n// focusables in the overlay's own shadow root only, so a popup portaled elsewhere (Dropdown,\n// ActionMenu, Tooltip) is outside it and Tab will pull focus back out of the popup.\nconst pushFocusTrapLayer = (getPanel: PanelSource, getHost: () => HTMLElement | null) => {\n if (trapLayers.length === 0) {\n document.addEventListener('keydown', handleTabKeyDown);\n }\n trapLayers.push(getPanel);\n hostSources.set(getPanel, getHost);\n notifyStackChanged();\n return () => {\n const index = trapLayers.lastIndexOf(getPanel);\n if (index !== -1) {\n trapLayers.splice(index, 1);\n }\n elevatedLayers.delete(getPanel);\n hostSources.delete(getPanel);\n const host = getHost();\n if (host) {\n leaveTopLayer(host);\n }\n if (trapLayers.length === 0) {\n document.removeEventListener('keydown', handleTabKeyDown);\n }\n notifyStackChanged();\n };\n};\n\n/**\n * Whether a SidePanel or Modal is open anywhere, for host chrome that has to move out of the way.\n * Overlays portal, so the only other signal is this module's markers, which a host cannot read\n * without coupling to an internal. Dropdowns and tooltips do not count: they trap no focus.\n */\nexport const useHasOpenOverlay = (): boolean =>\n useSyncExternalStore(\n subscribeToStack,\n () => trapLayers.length > 0,\n () => false,\n );\n\n/**\n * The modal-overlay behaviour shared by SidePanel and Modal: ESC and the Tab focus trap\n * (topmost overlay only, so these may be stacked), background scroll lock, and focus moved\n * into the overlay on open then restored to the trigger on close.\n *\n * Returns the ref callback the dialog element must take, whether this overlay is the topmost\n * one — only that one may claim `aria-modal`, or a screen reader can still browse the overlay\n * underneath — the z-index its host must carry, which keeps paint order with the focus stack,\n * and whether it is elevated. Presentation otherwise stays with the calling component.\n */\nexport const useOverlayBehaviour = ({\n open,\n onClose,\n elevated = false,\n hostRef,\n}: {\n open: boolean;\n onClose: () => void;\n elevated?: boolean;\n /** The shadow host, which the stack promotes into the top layer while elevated. */\n hostRef?: { current: HTMLElement | null };\n}): {\n ref: (node: HTMLElement | null) => void;\n isTopmost: boolean;\n zIndex: number | undefined;\n elevated: boolean;\n} => {\n const panelRef = useRef<HTMLElement | null>(null);\n // One identity for the life of the instance: elevation registers it in the layout phase,\n // before the effect below pushes it onto the stack.\n const source = useRef<PanelSource>(() => panelRef.current).current;\n const previousFocusRef = useRef<HTMLElement | null>(null);\n const layerRef = useRef<PanelSource | null>(null);\n\n // Ref so the ESC layer registers once per open, not on every inline-onClose change.\n const onCloseRef = useRef(onClose);\n useEffect(() => {\n onCloseRef.current = onClose;\n }, [onClose]);\n\n // Before the trap effect, so the trigger is recorded before focus moves in — and relies on\n // ShadowRootWrapper portaling a commit late, or this would capture the panel itself.\n useEffect(() => {\n if (!open) {\n return;\n }\n previousFocusRef.current = getDeepActiveElement();\n return () => {\n const previous = previousFocusRef.current;\n previousFocusRef.current = null;\n if (previous?.isConnected) {\n previous.focus();\n }\n };\n }, [open]);\n\n useEffect(() => {\n if (!open) {\n return;\n }\n layerRef.current = source;\n const popEscapeLayer = pushEscapeLayer(() => onCloseRef.current());\n const releaseScrollLock = lockBodyScroll();\n const popFocusTrapLayer = pushFocusTrapLayer(source, () => hostRef?.current ?? null);\n // Reopening inside the exit transition reuses the same node, so the ref callback\n // never fires again and nothing would take focus.\n panelRef.current?.focus();\n return () => {\n layerRef.current = null;\n popFocusTrapLayer();\n popEscapeLayer();\n releaseScrollLock();\n };\n }, [open, hostRef, source]);\n\n // Its own effect: re-pushing the trap layer to change elevation would move this overlay to\n // the top of the stack, handing it the Escape and Tab that belong to a dialog above it.\n // Layout, and declared above the sync below, so that sync sees it in the same commit.\n useLayoutEffect(() => {\n if (!open || !elevated) {\n return;\n }\n elevatedLayers.add(source);\n notifyStackChanged();\n return () => {\n elevatedLayers.delete(source);\n notifyStackChanged();\n };\n }, [open, elevated, source]);\n\n // Only an open overlay subscribes: a mounted-but-idle one must carry no at-rest cost,\n // and its snapshot is a constant `false` regardless of what the stack does.\n const subscribe = useCallback(\n (onStackChange: () => void) => (open ? subscribeToStack(onStackChange) : NO_SUBSCRIPTION),\n [open],\n );\n\n const isTopmost = useSyncExternalStore(\n subscribe,\n // Gated on `open` so an overlay fading out stops claiming modal. Before it registers\n // it is the one about to go on top, or aria-modal would drop for a frame on open.\n () =>\n open &&\n (layerRef.current === null || trapLayers[trapLayers.length - 1] === layerRef.current),\n () => true,\n );\n\n // Its own request counts before the stack has it: the layer is not pushed until effects run.\n const stackElevated = useSyncExternalStore(\n subscribe,\n () => open && (elevated || elevatedLayers.size > 0),\n () => false,\n );\n\n // Overlay hosts share one z-index band, so paint order would otherwise fall out of body\n // order: an overlay reopened over a newer one takes the keyboard while painting under it.\n const zIndex = useSyncExternalStore(\n subscribe,\n () => {\n const layer = layerRef.current;\n return OVERLAY_Z_BASE + (open && layer ? Math.max(0, trapLayers.indexOf(layer)) : 0);\n },\n () => OVERLAY_Z_BASE,\n );\n\n // Layout, not passive: the rule has to exist before the first paint the attribute applies to.\n useLayoutEffect(() => {\n if (stackElevated) {\n ensureElevatedOverlayStyles();\n }\n }, [stackElevated]);\n\n // No deps: the host mounts a commit after the layer that owns it, so there is no stack\n // change left to key this on. `syncTopLayer` compares before it touches anything.\n useLayoutEffect(() => {\n syncTopLayer();\n });\n\n const ref = useCallback((node: HTMLElement | null) => {\n panelRef.current = node;\n // The panel mounts fresh on every open, so first attach = time to take focus.\n node?.focus();\n }, []);\n\n return { ref, isTopmost, zIndex: stackElevated ? undefined : zIndex, elevated: stackElevated };\n};\n\nexport default useOverlayBehaviour;\n"],"names":["OVERLAY_ICON_BUTTON_CLASSES","FOCUS_RING","FOCUSABLE_SELECTOR","trapLayers","elevatedLayers","hostSources","stackListeners","NO_SUBSCRIPTION","promoted","syncTopLayer","wanted","layer","_a","host","index","stillWanted","leaveTopLayer","raiseToTopLayer","notifyStackChanged","listener","subscribeToStack","handleTabKeyDown","event","panel","focusables","first","last","active","getDeepActiveElement","activeInPanel","pushFocusTrapLayer","getPanel","getHost","useHasOpenOverlay","useSyncExternalStore","useOverlayBehaviour","open","onClose","elevated","hostRef","panelRef","useRef","source","previousFocusRef","layerRef","onCloseRef","useEffect","previous","popEscapeLayer","pushEscapeLayer","releaseScrollLock","lockBodyScroll","popFocusTrapLayer","useLayoutEffect","subscribe","useCallback","onStackChange","isTopmost","stackElevated","zIndex","OVERLAY_Z_BASE","ensureElevatedOverlayStyles","node"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"overlayBehaviour.js","sources":["../../src/components/overlayBehaviour.ts"],"sourcesContent":["import { useCallback, useEffect, useLayoutEffect, useRef, useSyncExternalStore } from 'react';\nimport { getDeepActiveElement } from './activeElement';\nimport { pushEscapeLayer } from './escapeLayers';\nimport { FOCUS_RING } from './focusRing';\nimport { OVERLAY_Z_BASE, ensureElevatedOverlayStyles } from './portalOverlay';\nimport { lockBodyScroll } from './scrollLock';\nimport { leaveTopLayer, raiseToTopLayer } from './topLayer';\n\n/** Back/close button chrome, shared so the two overlays cannot drift apart. */\nexport const OVERLAY_ICON_BUTTON_CLASSES = `flex shrink-0 cursor-pointer items-center justify-center rounded-md border-0 bg-transparent p-[4px] text-[var(--malachite-text-secondary)] hover:text-[var(--malachite-text-primary)] transition-colors ${FOCUS_RING}`;\n\nexport const FOCUSABLE_SELECTOR =\n 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n\ntype PanelSource = () => HTMLElement | null;\n\n// LIFO like escapeLayers: with a listener per overlay the first-registered one wins, which\n// sent Tab from a dialog into the panel behind it.\nconst trapLayers: PanelSource[] = [];\n// Elevation belongs to the stack: a Modal left in the base band opens behind the panel that\n// elevated it.\nconst elevatedLayers = new Set<PanelSource>();\n// The shadow host per layer, so promotion can replay in stack order.\nconst hostSources = new Map<PanelSource, () => HTMLElement | null>();\nconst stackListeners = new Set<() => void>();\nconst NO_SUBSCRIPTION = () => {\n // Nothing to tear down: a closed overlay never subscribed.\n};\n\n// Top-layer paint order is entry order, so replay the whole stack bottom-up rather than letting\n// each overlay promote itself: those effects run in React tree order, which need not match.\nlet promoted: HTMLElement[] = [];\n\nconst syncTopLayer = (): void => {\n const wanted = elevatedLayers.size\n ? trapLayers\n .map((layer) => hostSources.get(layer)?.())\n .filter((host): host is HTMLElement => Boolean(host))\n : [];\n // Compared, not reapplied: this runs on every commit, because a host can appear a commit\n // after the layer that owns it, and re-entering the top layer unprompted costs a recalc.\n if (\n wanted.length === promoted.length &&\n wanted.every((host, index) => host === promoted[index])\n ) {\n return;\n }\n const stillWanted = new Set(wanted);\n for (const host of promoted) {\n if (!stillWanted.has(host)) {\n leaveTopLayer(host);\n }\n }\n for (const host of wanted) {\n raiseToTopLayer(host);\n }\n promoted = wanted;\n};\n\nconst notifyStackChanged = (): void => {\n for (const listener of stackListeners) {\n listener();\n }\n};\n\nconst subscribeToStack = (listener: () => void): (() => void) => {\n stackListeners.add(listener);\n return () => {\n stackListeners.delete(listener);\n };\n};\n\nconst handleTabKeyDown = (event: KeyboardEvent): void => {\n if (event.key !== 'Tab' || event.defaultPrevented) {\n return;\n }\n // Walk down rather than bail: React detaches refs before flushing the cleanup that pops\n // the layer, so the topmost can be registered-but-null and Tab would escape entirely.\n let panel: HTMLElement | null = null;\n for (let index = trapLayers.length - 1; index >= 0 && !panel; index -= 1) {\n panel = trapLayers[index]();\n }\n if (!panel) {\n return;\n }\n const focusables = Array.from(panel.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR));\n if (focusables.length === 0) {\n event.preventDefault();\n panel.focus();\n return;\n }\n const first = focusables[0];\n const last = focusables[focusables.length - 1];\n const active = getDeepActiveElement();\n const activeInPanel = active !== null && panel.contains(active);\n if (event.shiftKey) {\n if (!activeInPanel || active === first || active === panel) {\n event.preventDefault();\n last.focus();\n }\n } else if (!activeInPanel || active === last) {\n event.preventDefault();\n first.focus();\n }\n};\n\n// On `document` so it also catches focus that escaped behind the overlay. The trap reaches\n// focusables in the overlay's own shadow root only, so a popup portaled elsewhere (Dropdown,\n// ActionMenu, Tooltip) is outside it and Tab will pull focus back out of the popup.\nconst pushFocusTrapLayer = (getPanel: PanelSource, getHost: () => HTMLElement | null) => {\n if (trapLayers.length === 0) {\n document.addEventListener('keydown', handleTabKeyDown);\n }\n trapLayers.push(getPanel);\n hostSources.set(getPanel, getHost);\n notifyStackChanged();\n return () => {\n const index = trapLayers.lastIndexOf(getPanel);\n if (index !== -1) {\n trapLayers.splice(index, 1);\n }\n elevatedLayers.delete(getPanel);\n hostSources.delete(getPanel);\n const host = getHost();\n if (host) {\n leaveTopLayer(host);\n }\n if (trapLayers.length === 0) {\n document.removeEventListener('keydown', handleTabKeyDown);\n }\n notifyStackChanged();\n };\n};\n\n/**\n * Whether a SidePanel, Modal or CommandPalette is open anywhere, for host chrome that has to move out of the way.\n * Overlays portal, so the only other signal is this module's markers, which a host cannot read\n * without coupling to an internal. Dropdowns and tooltips do not count: they trap no focus.\n */\nexport const useHasOpenOverlay = (): boolean =>\n useSyncExternalStore(\n subscribeToStack,\n () => trapLayers.length > 0,\n () => false,\n );\n\nconst takeInitialFocus = (panel: HTMLElement, initialFocus: 'panel' | 'content'): void => {\n if (initialFocus === 'content') {\n const active = getDeepActiveElement();\n if (active && panel.contains(active)) {\n return;\n }\n (panel.querySelector<HTMLElement>(FOCUSABLE_SELECTOR) ?? panel).focus();\n return;\n }\n panel.focus();\n};\n\n/**\n * The modal-overlay behaviour shared by SidePanel, Modal and CommandPalette: ESC and the Tab focus trap\n * (topmost overlay only, so these may be stacked), background scroll lock, and focus moved\n * into the overlay on open then restored to the trigger on close.\n *\n * Returns the ref callback the dialog element must take, whether this overlay is the topmost\n * one — only that one may claim `aria-modal`, or a screen reader can still browse the overlay\n * underneath — the z-index its host must carry, which keeps paint order with the focus stack,\n * and whether it is elevated. Presentation otherwise stays with the calling component.\n */\nexport const useOverlayBehaviour = ({\n open,\n onClose,\n elevated = false,\n hostRef,\n initialFocus = 'panel',\n}: {\n open: boolean;\n onClose: () => void;\n elevated?: boolean;\n /** The shadow host, which the stack promotes into the top layer while elevated. */\n hostRef?: { current: HTMLElement | null };\n /** Where focus lands on open: the panel itself, or the content's own first control. */\n initialFocus?: 'panel' | 'content';\n}): {\n ref: (node: HTMLElement | null) => void;\n isTopmost: boolean;\n zIndex: number | undefined;\n elevated: boolean;\n} => {\n const panelRef = useRef<HTMLElement | null>(null);\n // One identity for the life of the instance: elevation registers it in the layout phase,\n // before the effect below pushes it onto the stack.\n const source = useRef<PanelSource>(() => panelRef.current).current;\n const previousFocusRef = useRef<HTMLElement | null>(null);\n const layerRef = useRef<PanelSource | null>(null);\n const initialFocusRef = useRef(initialFocus);\n\n // Ref so the ESC layer registers once per open, not on every inline-onClose change.\n const onCloseRef = useRef(onClose);\n useEffect(() => {\n onCloseRef.current = onClose;\n }, [onClose]);\n\n // Before the trap effect, so the trigger is recorded before focus moves in — and relies on\n // ShadowRootWrapper portaling a commit late, or this would capture the panel itself.\n useEffect(() => {\n if (!open) {\n return;\n }\n previousFocusRef.current = getDeepActiveElement();\n return () => {\n const previous = previousFocusRef.current;\n previousFocusRef.current = null;\n if (previous?.isConnected) {\n previous.focus();\n }\n };\n }, [open]);\n\n useEffect(() => {\n if (!open) {\n return;\n }\n layerRef.current = source;\n const popEscapeLayer = pushEscapeLayer(() => onCloseRef.current());\n const releaseScrollLock = lockBodyScroll();\n const popFocusTrapLayer = pushFocusTrapLayer(source, () => hostRef?.current ?? null);\n // Reopening inside the exit transition reuses the same node, so the ref callback\n // never fires again and nothing would take focus.\n if (panelRef.current) {\n takeInitialFocus(panelRef.current, initialFocusRef.current);\n }\n return () => {\n layerRef.current = null;\n popFocusTrapLayer();\n popEscapeLayer();\n releaseScrollLock();\n };\n }, [open, hostRef, source]);\n\n // Its own effect: re-pushing the trap layer to change elevation would move this overlay to\n // the top of the stack, handing it the Escape and Tab that belong to a dialog above it.\n // Layout, and declared above the sync below, so that sync sees it in the same commit.\n useLayoutEffect(() => {\n if (!open || !elevated) {\n return;\n }\n elevatedLayers.add(source);\n notifyStackChanged();\n return () => {\n elevatedLayers.delete(source);\n notifyStackChanged();\n };\n }, [open, elevated, source]);\n\n // Only an open overlay subscribes: a mounted-but-idle one must carry no at-rest cost,\n // and its snapshot is a constant `false` regardless of what the stack does.\n const subscribe = useCallback(\n (onStackChange: () => void) => (open ? subscribeToStack(onStackChange) : NO_SUBSCRIPTION),\n [open],\n );\n\n const isTopmost = useSyncExternalStore(\n subscribe,\n // Gated on `open` so an overlay fading out stops claiming modal. Before it registers\n // it is the one about to go on top, or aria-modal would drop for a frame on open.\n () =>\n open &&\n (layerRef.current === null || trapLayers[trapLayers.length - 1] === layerRef.current),\n () => true,\n );\n\n // Its own request counts before the stack has it: the layer is not pushed until effects run.\n const stackElevated = useSyncExternalStore(\n subscribe,\n () => open && (elevated || elevatedLayers.size > 0),\n () => false,\n );\n\n // Overlay hosts share one z-index band, so paint order would otherwise fall out of body\n // order: an overlay reopened over a newer one takes the keyboard while painting under it.\n const zIndex = useSyncExternalStore(\n subscribe,\n () => {\n const layer = layerRef.current;\n return OVERLAY_Z_BASE + (open && layer ? Math.max(0, trapLayers.indexOf(layer)) : 0);\n },\n () => OVERLAY_Z_BASE,\n );\n\n // Layout, not passive: the rule has to exist before the first paint the attribute applies to.\n useLayoutEffect(() => {\n if (stackElevated) {\n ensureElevatedOverlayStyles();\n }\n }, [stackElevated]);\n\n // No deps: the host mounts a commit after the layer that owns it, so there is no stack\n // change left to key this on. `syncTopLayer` compares before it touches anything.\n useLayoutEffect(() => {\n syncTopLayer();\n });\n\n const ref = useCallback((node: HTMLElement | null) => {\n panelRef.current = node;\n // The panel mounts fresh on every open, so first attach = time to take focus.\n if (node) {\n takeInitialFocus(node, initialFocusRef.current);\n }\n }, []);\n\n return { ref, isTopmost, zIndex: stackElevated ? undefined : zIndex, elevated: stackElevated };\n};\n\nexport default useOverlayBehaviour;\n"],"names":["OVERLAY_ICON_BUTTON_CLASSES","FOCUS_RING","FOCUSABLE_SELECTOR","trapLayers","elevatedLayers","hostSources","stackListeners","NO_SUBSCRIPTION","promoted","syncTopLayer","wanted","layer","_a","host","index","stillWanted","leaveTopLayer","raiseToTopLayer","notifyStackChanged","listener","subscribeToStack","handleTabKeyDown","event","panel","focusables","first","last","active","getDeepActiveElement","activeInPanel","pushFocusTrapLayer","getPanel","getHost","useHasOpenOverlay","useSyncExternalStore","takeInitialFocus","initialFocus","useOverlayBehaviour","open","onClose","elevated","hostRef","panelRef","useRef","source","previousFocusRef","layerRef","initialFocusRef","onCloseRef","useEffect","previous","popEscapeLayer","pushEscapeLayer","releaseScrollLock","lockBodyScroll","popFocusTrapLayer","useLayoutEffect","subscribe","useCallback","onStackChange","isTopmost","stackElevated","zIndex","OVERLAY_Z_BASE","ensureElevatedOverlayStyles","node"],"mappings":"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"}
|
|
@@ -1,30 +1,31 @@
|
|
|
1
1
|
import { injectStyleSheet as o } from "./injectStyleSheet.js";
|
|
2
|
-
const
|
|
2
|
+
const l = "data-malachite-overlay", e = {
|
|
3
3
|
dropdown: "malachite-dropdown-surface",
|
|
4
4
|
multiselect: "malachite-multiselect-surface",
|
|
5
5
|
actionmenu: "malachite-actionmenu-surface",
|
|
6
6
|
tooltip: "malachite-tip-surface",
|
|
7
7
|
sidepanel: "malachite-drawer-surface",
|
|
8
|
-
modal: "malachite-modal-surface"
|
|
9
|
-
|
|
8
|
+
modal: "malachite-modal-surface",
|
|
9
|
+
commandpalette: "malachite-cmdpalette-surface"
|
|
10
|
+
}, $ = (t) => ({
|
|
10
11
|
className: e[t],
|
|
11
|
-
[
|
|
12
|
-
}), s = "malachite-overlay-styles", R = 1e3, i = 2147483646, a = "data-malachite-elevated", n = (t) => Object.values(e).map((
|
|
12
|
+
[l]: t
|
|
13
|
+
}), s = "malachite-overlay-styles", R = 1e3, i = 2147483646, a = "data-malachite-elevated", n = (t) => Object.values(e).map((c) => `.${c}:popover-open${t}`).join(", "), r = "inset: auto; margin: 0; border: 0; padding: 0; width: auto; height: auto; overflow: visible; background: none; color: inherit;", d = [
|
|
13
14
|
`.${e.dropdown}, .${e.multiselect}, .${e.actionmenu} { z-index: 2147483646; }`,
|
|
14
15
|
`.${e.tooltip} { z-index: 2147483647; }`
|
|
15
|
-
],
|
|
16
|
+
], E = "malachite-overlay-menu-box", m = [
|
|
16
17
|
`.${e.dropdown}, .${e.dropdown} *, .${e.multiselect}, .${e.multiselect} *, .${e.actionmenu}, .${e.actionmenu} * { box-sizing: border-box; }`
|
|
17
|
-
], S = () => (o(
|
|
18
|
+
], S = () => (o(E, m), o(s, d)), u = "malachite-overlay-elevated", _ = [
|
|
18
19
|
`.${e.sidepanel}[${a}], .${e.modal}[${a}] { z-index: ${i}; }`
|
|
19
|
-
], y = () => o(u, _),
|
|
20
|
+
], y = () => o(u, _), p = "malachite-overlay-top-layer", L = [
|
|
20
21
|
`${n("")} { ${r} }`,
|
|
21
22
|
`${n("::backdrop")} { display: none; }`
|
|
22
23
|
], A = () => {
|
|
23
|
-
const t = o(
|
|
24
|
+
const t = o(p, L);
|
|
24
25
|
return t && t.cssRules.length > 0 ? t : null;
|
|
25
26
|
};
|
|
26
27
|
export {
|
|
27
|
-
|
|
28
|
+
l as OVERLAY_ATTR,
|
|
28
29
|
e as OVERLAY_CLASS,
|
|
29
30
|
a as OVERLAY_ELEVATED_ATTR,
|
|
30
31
|
R as OVERLAY_Z_BASE,
|
|
@@ -32,6 +33,6 @@ export {
|
|
|
32
33
|
y as ensureElevatedOverlayStyles,
|
|
33
34
|
S as ensureOverlayStyles,
|
|
34
35
|
A as ensureTopLayerStyles,
|
|
35
|
-
|
|
36
|
+
$ as overlayIdentity
|
|
36
37
|
};
|
|
37
38
|
//# sourceMappingURL=portalOverlay.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"portalOverlay.js","sources":["../../src/components/portalOverlay.ts"],"sourcesContent":["// Host \"kill the ad overlay\" CSS erases anything portaled into document.body: `body >\n// div:not([class])` matches an unclassed body child, `[style*=\"2147483\"]` a max-int z-index.\n// The second matches the style ATTRIBUTE, so declaring z-index here keeps max-int stacking.\n\nimport { injectStyleSheet } from './injectStyleSheet';\n\n/** One selector for every malachite overlay, so a host allow-lists them in one rule. */\nexport const OVERLAY_ATTR = 'data-malachite-overlay';\n\n// Values avoid `panel`/`popup`/`overlay`/`tooltip`: a class defeats `:not([class])`, but one\n// matching `[class*=\"panel\"]` trades that hole for another these elements did not have.\nexport const OVERLAY_CLASS = {\n dropdown: 'malachite-dropdown-surface',\n multiselect: 'malachite-multiselect-surface',\n actionmenu: 'malachite-actionmenu-surface',\n tooltip: 'malachite-tip-surface',\n sidepanel: 'malachite-drawer-surface',\n modal: 'malachite-modal-surface',\n} as const;\n\nexport type OverlayName = keyof typeof OVERLAY_CLASS;\n\nexport const overlayIdentity = (name: OverlayName) => ({\n className: OVERLAY_CLASS[name],\n [OVERLAY_ATTR]: name,\n});\n\nconst OVERLAY_STYLE_ID = 'malachite-overlay-styles';\n\n/**\n * Where SidePanel and
|
|
1
|
+
{"version":3,"file":"portalOverlay.js","sources":["../../src/components/portalOverlay.ts"],"sourcesContent":["// Host \"kill the ad overlay\" CSS erases anything portaled into document.body: `body >\n// div:not([class])` matches an unclassed body child, `[style*=\"2147483\"]` a max-int z-index.\n// The second matches the style ATTRIBUTE, so declaring z-index here keeps max-int stacking.\n\nimport { injectStyleSheet } from './injectStyleSheet';\n\n/** One selector for every malachite overlay, so a host allow-lists them in one rule. */\nexport const OVERLAY_ATTR = 'data-malachite-overlay';\n\n// Values avoid `panel`/`popup`/`overlay`/`tooltip`: a class defeats `:not([class])`, but one\n// matching `[class*=\"panel\"]` trades that hole for another these elements did not have.\nexport const OVERLAY_CLASS = {\n dropdown: 'malachite-dropdown-surface',\n multiselect: 'malachite-multiselect-surface',\n actionmenu: 'malachite-actionmenu-surface',\n tooltip: 'malachite-tip-surface',\n sidepanel: 'malachite-drawer-surface',\n modal: 'malachite-modal-surface',\n commandpalette: 'malachite-cmdpalette-surface',\n} as const;\n\nexport type OverlayName = keyof typeof OVERLAY_CLASS;\n\nexport const overlayIdentity = (name: OverlayName) => ({\n className: OVERLAY_CLASS[name],\n [OVERLAY_ATTR]: name,\n});\n\nconst OVERLAY_STYLE_ID = 'malachite-overlay-styles';\n\n/**\n * Where SidePanel, Modal and CommandPalette start. `useOverlayBehaviour` steps one per stacked overlay, so\n * paint order follows the focus stack rather than body order — a handful above this at most.\n */\nexport const OVERLAY_Z_BASE = 1000;\n\n/**\n * The dropdown band, which clears app chrome pinned just under max-int. One integer wide: a host\n * pins its chrome at max-int-1 and the tooltip band sits at max-int, so the stack cannot step\n * per layer the way the base band does — the top layer orders it, and where popover is\n * unsupported, document order does.\n */\nexport const OVERLAY_Z_ELEVATED = 2147483646;\n\n/** Marks a host in that band — an attribute, not an inline z-index; see above. */\nexport const OVERLAY_ELEVATED_ATTR = 'data-malachite-elevated';\n\n// Every overlay class, not the four promoted by default: one the reset misses wears the UA box.\nconst promotedSelector = (suffix: string) =>\n Object.values(OVERLAY_CLASS)\n .map((name) => `.${name}:popover-open${suffix}`)\n .join(', ');\n\n// Promotion drags in the UA `[popover]` box: `inset: 0` (which survives as right/bottom and\n// stretches a panel to the viewport edge), auto margins, a border, padding and fit-content\n// sizing. Reset to what these computed before promotion; their own inline styles still win.\nconst POPOVER_RESET =\n 'inset: auto; margin: 0; border: 0; padding: 0; width: auto; height: auto; ' +\n 'overflow: visible; background: none; color: inherit;';\n\nconst OVERLAY_RULES = [\n `.${OVERLAY_CLASS.dropdown}, .${OVERLAY_CLASS.multiselect}, .${OVERLAY_CLASS.actionmenu} { z-index: 2147483646; }`,\n `.${OVERLAY_CLASS.tooltip} { z-index: 2147483647; }`,\n];\n\n// Its own id, never folded into OVERLAY_STYLE_ID: injectStyleSheet keeps one sheet per id across\n// malachite copies sharing a realm, so a copy predating this rule would win that id and leave a\n// portaled menu on the host page's box model — a 280px Options menu painting 298. No copy has\n// ever registered this one.\nconst MENU_BOX_STYLE_ID = 'malachite-overlay-menu-box';\n\n// The scoped stylesheet makes everything border-box; a portal escapes it onto a host page that\n// may have no reset at all, while the geometry is computed as a CSS width.\nconst MENU_BOX_RULES = [\n `.${OVERLAY_CLASS.dropdown}, .${OVERLAY_CLASS.dropdown} *,` +\n ` .${OVERLAY_CLASS.multiselect}, .${OVERLAY_CLASS.multiselect} *,` +\n ` .${OVERLAY_CLASS.actionmenu}, .${OVERLAY_CLASS.actionmenu} *` +\n ' { box-sizing: border-box; }',\n];\n\nexport const ensureOverlayStyles = (): CSSStyleSheet | null => {\n injectStyleSheet(MENU_BOX_STYLE_ID, MENU_BOX_RULES);\n return injectStyleSheet(OVERLAY_STYLE_ID, OVERLAY_RULES);\n};\n\n// Its own id like TOP_LAYER_STYLE_ID: a copy predating elevation would own OVERLAY_STYLE_ID.\nconst ELEVATED_STYLE_ID = 'malachite-overlay-elevated';\n\nconst ELEVATED_RULES = [\n `.${OVERLAY_CLASS.sidepanel}[${OVERLAY_ELEVATED_ATTR}], .${OVERLAY_CLASS.modal}[${OVERLAY_ELEVATED_ATTR}] { z-index: ${OVERLAY_Z_ELEVATED}; }`,\n];\n\nexport const ensureElevatedOverlayStyles = (): CSSStyleSheet | null =>\n injectStyleSheet(ELEVATED_STYLE_ID, ELEVATED_RULES);\n\n// Its own id, never folded into OVERLAY_STYLE_ID: injectStyleSheet keeps one sheet per id across\n// malachite copies sharing a realm, so a copy predating the reset would win that id and leave a\n// promoted overlay wearing the raw UA box. No copy has ever registered this one.\nconst TOP_LAYER_STYLE_ID = 'malachite-overlay-top-layer';\n\nconst TOP_LAYER_RULES = [\n `${promotedSelector('')} { ${POPOVER_RESET} }`,\n `${promotedSelector('::backdrop')} { display: none; }`,\n];\n\nexport const ensureTopLayerStyles = (): CSSStyleSheet | null => {\n // A sheet that installed does not mean its rules parsed: replaceSync (and the <style> path)\n // drop an unparseable rule silently rather than throwing, so an engine that chokes on\n // `:popover-open` or `::backdrop` hands back a valid, empty sheet.\n const sheet = injectStyleSheet(TOP_LAYER_STYLE_ID, TOP_LAYER_RULES);\n return sheet && sheet.cssRules.length > 0 ? sheet : null;\n};\n"],"names":["OVERLAY_ATTR","OVERLAY_CLASS","overlayIdentity","name","OVERLAY_STYLE_ID","OVERLAY_Z_BASE","OVERLAY_Z_ELEVATED","OVERLAY_ELEVATED_ATTR","promotedSelector","suffix","POPOVER_RESET","OVERLAY_RULES","MENU_BOX_STYLE_ID","MENU_BOX_RULES","ensureOverlayStyles","injectStyleSheet","ELEVATED_STYLE_ID","ELEVATED_RULES","ensureElevatedOverlayStyles","TOP_LAYER_STYLE_ID","TOP_LAYER_RULES","ensureTopLayerStyles","sheet"],"mappings":";AAOO,MAAMA,IAAe,0BAIfC,IAAgB;AAAA,EACzB,UAAU;AAAA,EACV,aAAa;AAAA,EACb,YAAY;AAAA,EACZ,SAAS;AAAA,EACT,WAAW;AAAA,EACX,OAAO;AAAA,EACP,gBAAgB;AACpB,GAIaC,IAAkB,CAACC,OAAuB;AAAA,EACnD,WAAWF,EAAcE,CAAI;AAAA,EAC7B,CAACH,CAAY,GAAGG;AACpB,IAEMC,IAAmB,4BAMZC,IAAiB,KAQjBC,IAAqB,YAGrBC,IAAwB,2BAG/BC,IAAmB,CAACC,MACtB,OAAO,OAAOR,CAAa,EACtB,IAAI,CAACE,MAAS,IAAIA,CAAI,gBAAgBM,CAAM,EAAE,EAC9C,KAAK,IAAI,GAKZC,IACF,kIAGEC,IAAgB;AAAA,EAClB,IAAIV,EAAc,QAAQ,MAAMA,EAAc,WAAW,MAAMA,EAAc,UAAU;AAAA,EACvF,IAAIA,EAAc,OAAO;AAC7B,GAMMW,IAAoB,8BAIpBC,IAAiB;AAAA,EACnB,IAAIZ,EAAc,QAAQ,MAAMA,EAAc,QAAQ,QAC7CA,EAAc,WAAW,MAAMA,EAAc,WAAW,QACxDA,EAAc,UAAU,MAAMA,EAAc,UAAU;AAEnE,GAEaa,IAAsB,OAC/BC,EAAiBH,GAAmBC,CAAc,GAC3CE,EAAiBX,GAAkBO,CAAa,IAIrDK,IAAoB,8BAEpBC,IAAiB;AAAA,EACnB,IAAIhB,EAAc,SAAS,IAAIM,CAAqB,OAAON,EAAc,KAAK,IAAIM,CAAqB,gBAAgBD,CAAkB;AAC7I,GAEaY,IAA8B,MACvCH,EAAiBC,GAAmBC,CAAc,GAKhDE,IAAqB,+BAErBC,IAAkB;AAAA,EACpB,GAAGZ,EAAiB,EAAE,CAAC,MAAME,CAAa;AAAA,EAC1C,GAAGF,EAAiB,YAAY,CAAC;AACrC,GAEaa,IAAuB,MAA4B;AAI5D,QAAMC,IAAQP,EAAiBI,GAAoBC,CAAe;AAClE,SAAOE,KAASA,EAAM,SAAS,SAAS,IAAIA,IAAQ;AACxD;"}
|
package/dist/index.d.ts
CHANGED
|
@@ -147,6 +147,8 @@ declare interface AlertProps {
|
|
|
147
147
|
|
|
148
148
|
export declare type AlertType = 'info' | 'success' | 'error' | 'warning' | 'assistant';
|
|
149
149
|
|
|
150
|
+
export declare type AnySurfaceRowProps = SurfaceRowProps | SurfaceRowAsChildProps;
|
|
151
|
+
|
|
150
152
|
export declare const ApiKeysIcon: React.FC<IconProps_5>;
|
|
151
153
|
|
|
152
154
|
export declare const AppLogo: default_2.FC<AppLogoProps>;
|
|
@@ -203,6 +205,12 @@ export declare const ArrowUpIcon: React.FC<IconProps_8>;
|
|
|
203
205
|
|
|
204
206
|
export declare const ArrowUpRightIcon: React.FC<IconProps_9>;
|
|
205
207
|
|
|
208
|
+
declare interface AsChildProps {
|
|
209
|
+
children: default_2.ReactElement;
|
|
210
|
+
className?: string;
|
|
211
|
+
style?: default_2.CSSProperties;
|
|
212
|
+
}
|
|
213
|
+
|
|
206
214
|
export declare const AuthenticationIcon: React.FC<IconProps_10>;
|
|
207
215
|
|
|
208
216
|
/**
|
|
@@ -951,6 +959,87 @@ export declare interface CollapsibleSectionProps {
|
|
|
951
959
|
|
|
952
960
|
export declare const CommandKeyIcon: React.FC<IconProps_26>;
|
|
953
961
|
|
|
962
|
+
export declare const CommandPalette: CommandPaletteComponent;
|
|
963
|
+
|
|
964
|
+
declare interface CommandPaletteComponent extends default_2.FC<CommandPaletteProps> {
|
|
965
|
+
FieldRow: default_2.FC<CommandPaletteFieldRowProps>;
|
|
966
|
+
Field: default_2.FC<AsChildProps>;
|
|
967
|
+
ScopeChip: default_2.FC<{
|
|
968
|
+
children?: default_2.ReactNode;
|
|
969
|
+
}>;
|
|
970
|
+
List: default_2.FC<CommandPaletteListProps>;
|
|
971
|
+
Group: default_2.FC<CommandPaletteGroupProps>;
|
|
972
|
+
Heading: default_2.FC<{
|
|
973
|
+
children?: default_2.ReactNode;
|
|
974
|
+
trailing?: default_2.ReactNode;
|
|
975
|
+
}>;
|
|
976
|
+
Row: default_2.FC<CommandPaletteRowProps>;
|
|
977
|
+
Grid: default_2.FC<CommandPaletteGridProps>;
|
|
978
|
+
Message: default_2.FC<CommandPaletteMessageProps>;
|
|
979
|
+
Legend: default_2.FC<CommandPaletteLegendProps>;
|
|
980
|
+
Footer: default_2.FC<{
|
|
981
|
+
children?: default_2.ReactNode;
|
|
982
|
+
trailing?: default_2.ReactNode;
|
|
983
|
+
}>;
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
export declare interface CommandPaletteFieldRowProps {
|
|
987
|
+
children?: default_2.ReactNode;
|
|
988
|
+
scope?: default_2.ReactNode;
|
|
989
|
+
className?: string;
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
export declare interface CommandPaletteGridProps {
|
|
993
|
+
children?: default_2.ReactNode;
|
|
994
|
+
columns?: 1 | 2;
|
|
995
|
+
className?: string;
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
export declare interface CommandPaletteGroupProps extends AsChildProps {
|
|
999
|
+
divider?: boolean;
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
export declare interface CommandPaletteLegendProps {
|
|
1003
|
+
items: {
|
|
1004
|
+
key: string;
|
|
1005
|
+
label: string;
|
|
1006
|
+
}[];
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
export declare interface CommandPaletteListProps extends AsChildProps {
|
|
1010
|
+
hasMoreBelow?: boolean;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
export declare interface CommandPaletteMessageProps {
|
|
1014
|
+
children: default_2.ReactNode;
|
|
1015
|
+
className?: string;
|
|
1016
|
+
style?: default_2.CSSProperties;
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
export declare interface CommandPaletteProps {
|
|
1020
|
+
children?: default_2.ReactNode;
|
|
1021
|
+
/** Scrim click, and Escape unless a consumer handler already called `preventDefault`. */
|
|
1022
|
+
onDismiss?: () => void;
|
|
1023
|
+
'aria-label'?: string;
|
|
1024
|
+
className?: string;
|
|
1025
|
+
style?: default_2.CSSProperties;
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
export declare interface CommandPaletteRowProps {
|
|
1029
|
+
/** Its children are replaced, so give the item an explicit `value`/`keywords`. */
|
|
1030
|
+
children: default_2.ReactElement;
|
|
1031
|
+
selected?: boolean;
|
|
1032
|
+
leading?: default_2.ReactNode;
|
|
1033
|
+
label?: default_2.ReactNode;
|
|
1034
|
+
/** Tags beside the label, kept visible while a long label truncates. */
|
|
1035
|
+
tags?: default_2.ReactNode;
|
|
1036
|
+
crumb?: default_2.ReactNode;
|
|
1037
|
+
meta?: default_2.ReactNode;
|
|
1038
|
+
status?: default_2.ReactNode;
|
|
1039
|
+
hint?: default_2.ReactNode;
|
|
1040
|
+
className?: string;
|
|
1041
|
+
}
|
|
1042
|
+
|
|
954
1043
|
export declare const Confetti: default_2.FC<ConfettiProps>;
|
|
955
1044
|
|
|
956
1045
|
export declare interface ConfettiProps {
|
|
@@ -2398,6 +2487,21 @@ declare type InputVariant = 'default' | 'ghost';
|
|
|
2398
2487
|
|
|
2399
2488
|
export declare const IntegrationsIcon: React.FC<IconProps_56>;
|
|
2400
2489
|
|
|
2490
|
+
export declare const Keycap: default_2.FC<KeycapProps>;
|
|
2491
|
+
|
|
2492
|
+
export declare interface KeycapProps {
|
|
2493
|
+
children: default_2.ReactNode;
|
|
2494
|
+
size?: KeycapSize;
|
|
2495
|
+
onClick?: () => void;
|
|
2496
|
+
/** Renders a `<button>` even when `false`. */
|
|
2497
|
+
disabled?: boolean;
|
|
2498
|
+
'aria-label'?: string;
|
|
2499
|
+
'aria-labelledby'?: string;
|
|
2500
|
+
className?: string;
|
|
2501
|
+
}
|
|
2502
|
+
|
|
2503
|
+
export declare type KeycapSize = 'small' | 'medium';
|
|
2504
|
+
|
|
2401
2505
|
export declare const KeyIcon: React.FC<IconProps_57>;
|
|
2402
2506
|
|
|
2403
2507
|
export declare const KeyValueList: default_2.FC<KeyValueListProps> & {
|
|
@@ -3996,7 +4100,7 @@ export declare type SubtitleLayout = 'inline' | 'stacked';
|
|
|
3996
4100
|
export declare const Surface: SurfaceComponent;
|
|
3997
4101
|
|
|
3998
4102
|
declare interface SurfaceComponent extends default_2.FC<SurfaceProps> {
|
|
3999
|
-
Row: default_2.FC<
|
|
4103
|
+
Row: default_2.FC<AnySurfaceRowProps>;
|
|
4000
4104
|
ExpandableRow: typeof SurfaceExpandableRow;
|
|
4001
4105
|
}
|
|
4002
4106
|
|
|
@@ -4051,24 +4155,19 @@ export declare interface SurfaceProps {
|
|
|
4051
4155
|
* Pass `onClick`/`href` to make the entire row clickable (rendered as a real
|
|
4052
4156
|
* button/anchor, so keyboard navigation works natively).
|
|
4053
4157
|
*/
|
|
4054
|
-
export declare const SurfaceRow: default_2.FC<
|
|
4158
|
+
export declare const SurfaceRow: default_2.FC<AnySurfaceRowProps>;
|
|
4055
4159
|
|
|
4056
|
-
export declare interface
|
|
4160
|
+
export declare interface SurfaceRowAsChildProps extends SurfaceRowChromeProps {
|
|
4161
|
+
asChild: true;
|
|
4162
|
+
children: default_2.ReactElement;
|
|
4163
|
+
}
|
|
4164
|
+
|
|
4165
|
+
declare interface SurfaceRowChromeProps {
|
|
4057
4166
|
children?: default_2.ReactNode;
|
|
4058
4167
|
/** Renders a bottom divider — omit on the last row in a Surface. */
|
|
4059
4168
|
divider?: boolean;
|
|
4060
4169
|
/** Removes the hover affordance / disables the control (e.g. while loading/busy). */
|
|
4061
4170
|
disabled?: boolean;
|
|
4062
|
-
/**
|
|
4063
|
-
* When provided, the whole row becomes clickable: it renders as a `<button>`
|
|
4064
|
-
* (or `<a>` when `href` is set), so the entire row is the click target with
|
|
4065
|
-
* native focus + Enter/Space — no manual tabIndex/key handlers. For a
|
|
4066
|
-
* selectable row, put a presentational `<Radio>`/`<Checkbox>` inside as the
|
|
4067
|
-
* visual indicator; do NOT nest an interactive control (invalid + double tab stop).
|
|
4068
|
-
*/
|
|
4069
|
-
onClick?: (event: default_2.MouseEvent) => void;
|
|
4070
|
-
/** Renders the clickable row as an anchor instead of a button. */
|
|
4071
|
-
href?: string;
|
|
4072
4171
|
/** Announces the row as selected — `aria-checked` under a `radio`/`checkbox` role,
|
|
4073
4172
|
* `aria-current` otherwise — and paints `Table`'s selected treatment unless `selection`
|
|
4074
4173
|
* says not to. */
|
|
@@ -4077,20 +4176,35 @@ export declare interface SurfaceRowProps {
|
|
|
4077
4176
|
* visual to a control inside the row and only sets the ARIA state. Defaults to `announce`
|
|
4078
4177
|
* under `role="checkbox"` and `highlight` otherwise. */
|
|
4079
4178
|
selection?: 'highlight' | 'announce';
|
|
4080
|
-
/** Content pinned right, outside the row button, so it may hold its own control. */
|
|
4081
|
-
trailing?: default_2.ReactNode;
|
|
4082
4179
|
/**
|
|
4083
4180
|
* Tints the whole row by what it reports. `muted` is a row whose subject is inactive —
|
|
4084
4181
|
* not disabled: it stays interactive. `success` and `error` are for a row that carries
|
|
4085
4182
|
* an outcome, where the result belongs to the whole line rather than to one cell in it.
|
|
4086
4183
|
*/
|
|
4087
4184
|
tone?: 'default' | 'muted' | 'success' | 'error';
|
|
4185
|
+
className?: string;
|
|
4186
|
+
style?: default_2.CSSProperties;
|
|
4187
|
+
}
|
|
4188
|
+
|
|
4189
|
+
/** Stays an interface under its original name: a consumer may `extends` it. */
|
|
4190
|
+
export declare interface SurfaceRowProps extends SurfaceRowChromeProps {
|
|
4191
|
+
asChild?: false;
|
|
4192
|
+
/**
|
|
4193
|
+
* When provided, the whole row becomes clickable: it renders as a `<button>`
|
|
4194
|
+
* (or `<a>` when `href` is set), so the entire row is the click target with
|
|
4195
|
+
* native focus + Enter/Space — no manual tabIndex/key handlers. For a
|
|
4196
|
+
* selectable row, put a presentational `<Radio>`/`<Checkbox>` inside as the
|
|
4197
|
+
* visual indicator; do NOT nest an interactive control (invalid + double tab stop).
|
|
4198
|
+
*/
|
|
4199
|
+
onClick?: (event: default_2.MouseEvent) => void;
|
|
4200
|
+
/** Renders the clickable row as an anchor instead of a button. */
|
|
4201
|
+
href?: string;
|
|
4202
|
+
/** Content pinned right, outside the row button, so it may hold its own control. */
|
|
4203
|
+
trailing?: default_2.ReactNode;
|
|
4088
4204
|
/** ARIA role for a clickable row, e.g. `radio`/`checkbox` (wrap the list in a `radiogroup`). */
|
|
4089
4205
|
role?: default_2.AriaRole;
|
|
4090
4206
|
'aria-label'?: string;
|
|
4091
4207
|
'aria-labelledby'?: string;
|
|
4092
|
-
className?: string;
|
|
4093
|
-
style?: default_2.CSSProperties;
|
|
4094
4208
|
}
|
|
4095
4209
|
|
|
4096
4210
|
export declare const Swatch: default_2.FC<SwatchProps>;
|
|
@@ -4569,7 +4683,7 @@ export declare const UnarchiveIcon: React.FC<IconProps_101>;
|
|
|
4569
4683
|
export declare const UploadIcon: React.FC<IconProps_102>;
|
|
4570
4684
|
|
|
4571
4685
|
/**
|
|
4572
|
-
* Whether a SidePanel or
|
|
4686
|
+
* Whether a SidePanel, Modal or CommandPalette is open anywhere, for host chrome that has to move out of the way.
|
|
4573
4687
|
* Overlays portal, so the only other signal is this module's markers, which a host cannot read
|
|
4574
4688
|
* without coupling to an internal. Dropdowns and tooltips do not count: they trap no focus.
|
|
4575
4689
|
*/
|