@charcuterie/ui 1.0.1 → 2.0.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/dist/ColorSchemeSwitcher/ColorSchemeSwitcher.d.ts +35 -0
- package/dist/ColorSchemeSwitcher/ColorSchemeSwitcher.d.ts.map +1 -0
- package/dist/ColorSchemeSwitcher/ColorSchemeSwitcher.js +33 -0
- package/dist/ColorSchemeSwitcher/ColorSchemeSwitcher.js.map +1 -0
- package/dist/ColorSchemeToggle/ColorSchemeToggle.d.ts +54 -0
- package/dist/ColorSchemeToggle/ColorSchemeToggle.d.ts.map +1 -0
- package/dist/ColorSchemeToggle/ColorSchemeToggle.js +30 -0
- package/dist/ColorSchemeToggle/ColorSchemeToggle.js.map +1 -0
- package/dist/Combobox/Combobox.d.ts +54 -0
- package/dist/Combobox/Combobox.d.ts.map +1 -0
- package/dist/Combobox/Combobox.js +216 -0
- package/dist/Combobox/Combobox.js.map +1 -0
- package/dist/Combobox/ComboboxOption.d.ts +36 -0
- package/dist/Combobox/ComboboxOption.d.ts.map +1 -0
- package/dist/Combobox/ComboboxOption.js +32 -0
- package/dist/Combobox/ComboboxOption.js.map +1 -0
- package/dist/Dialog/Dialog.d.ts +47 -0
- package/dist/Dialog/Dialog.d.ts.map +1 -0
- package/dist/Dialog/Dialog.js +52 -0
- package/dist/Dialog/Dialog.js.map +1 -0
- package/dist/Lightbox/Lightbox.d.ts +57 -0
- package/dist/Lightbox/Lightbox.d.ts.map +1 -0
- package/dist/Lightbox/Lightbox.js +50 -0
- package/dist/Lightbox/Lightbox.js.map +1 -0
- package/dist/Listbox/Listbox.d.ts +56 -0
- package/dist/Listbox/Listbox.d.ts.map +1 -0
- package/dist/Listbox/Listbox.js +151 -0
- package/dist/Listbox/Listbox.js.map +1 -0
- package/dist/Listbox/ListboxOption.d.ts +36 -0
- package/dist/Listbox/ListboxOption.d.ts.map +1 -0
- package/dist/Listbox/ListboxOption.js +42 -0
- package/dist/Listbox/ListboxOption.js.map +1 -0
- package/dist/Menu/Menu.d.ts.map +1 -1
- package/dist/Menu/Menu.js +47 -97
- package/dist/Menu/Menu.js.map +1 -1
- package/dist/Modal/Modal.d.ts +11 -59
- package/dist/Modal/Modal.d.ts.map +1 -1
- package/dist/Modal/Modal.js +28 -101
- package/dist/Modal/Modal.js.map +1 -1
- package/dist/Overlay/OverlayPanel.d.ts +50 -0
- package/dist/Overlay/OverlayPanel.d.ts.map +1 -0
- package/dist/Overlay/OverlayPanel.js +72 -0
- package/dist/Overlay/OverlayPanel.js.map +1 -0
- package/dist/Overlay/OverlayStack.d.ts +47 -0
- package/dist/Overlay/OverlayStack.d.ts.map +1 -0
- package/dist/Overlay/OverlayStack.js +46 -0
- package/dist/Overlay/OverlayStack.js.map +1 -0
- package/dist/Overlay/SharedBackdrop.d.ts +24 -0
- package/dist/Overlay/SharedBackdrop.d.ts.map +1 -0
- package/dist/Overlay/SharedBackdrop.js +28 -0
- package/dist/Overlay/SharedBackdrop.js.map +1 -0
- package/dist/Overlay/lockScrollBehind.d.ts.map +1 -0
- package/dist/Overlay/lockScrollBehind.js.map +1 -0
- package/dist/Overlay/overlayPanelClass.d.ts +17 -0
- package/dist/Overlay/overlayPanelClass.d.ts.map +1 -0
- package/dist/Overlay/overlayPanelClass.js +17 -0
- package/dist/Overlay/overlayPanelClass.js.map +1 -0
- package/dist/Overlay/useAnchoredOverlay.d.ts +97 -0
- package/dist/Overlay/useAnchoredOverlay.d.ts.map +1 -0
- package/dist/Overlay/useAnchoredOverlay.js +81 -0
- package/dist/Overlay/useAnchoredOverlay.js.map +1 -0
- package/dist/Popover/Popover.d.ts +16 -24
- package/dist/Popover/Popover.d.ts.map +1 -1
- package/dist/Popover/Popover.js +32 -68
- package/dist/Popover/Popover.js.map +1 -1
- package/dist/Tooltip/Tooltip.d.ts.map +1 -1
- package/dist/Tooltip/Tooltip.js +25 -25
- package/dist/Tooltip/Tooltip.js.map +1 -1
- package/dist/index.d.ts +13 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/mountStory.testHelpers.d.ts +1 -0
- package/dist/mountStory.testHelpers.d.ts.map +1 -1
- package/dist/mountStory.testHelpers.js +21 -1
- package/dist/mountStory.testHelpers.js.map +1 -1
- package/package.json +5 -4
- package/src/ColorSchemeSwitcher/ColorSchemeSwitcher.mdx +70 -0
- package/src/ColorSchemeSwitcher/ColorSchemeSwitcher.stories.tsx +118 -0
- package/src/ColorSchemeSwitcher/ColorSchemeSwitcher.test.tsx +53 -0
- package/src/ColorSchemeSwitcher/ColorSchemeSwitcher.tsx +90 -0
- package/src/ColorSchemeToggle/ColorSchemeToggle.mdx +48 -0
- package/src/ColorSchemeToggle/ColorSchemeToggle.stories.tsx +161 -0
- package/src/ColorSchemeToggle/ColorSchemeToggle.test.tsx +80 -0
- package/src/ColorSchemeToggle/ColorSchemeToggle.tsx +107 -0
- package/src/Combobox/Combobox.mdx +71 -0
- package/src/Combobox/Combobox.stories.tsx +245 -0
- package/src/Combobox/Combobox.test.tsx +289 -0
- package/src/Combobox/Combobox.tsx +513 -0
- package/src/Combobox/ComboboxOption.tsx +88 -0
- package/src/Dialog/Dialog.mdx +87 -0
- package/src/Dialog/Dialog.stories.tsx +347 -0
- package/src/Dialog/Dialog.test.tsx +312 -0
- package/src/Dialog/Dialog.tsx +127 -0
- package/src/Field/Field.test.tsx +6 -4
- package/src/Lightbox/Lightbox.mdx +63 -0
- package/src/Lightbox/Lightbox.stories.tsx +133 -0
- package/src/Lightbox/Lightbox.test.tsx +124 -0
- package/src/Lightbox/Lightbox.tsx +143 -0
- package/src/Listbox/Listbox.mdx +68 -0
- package/src/Listbox/Listbox.stories.tsx +205 -0
- package/src/Listbox/Listbox.test.tsx +198 -0
- package/src/Listbox/Listbox.tsx +295 -0
- package/src/Listbox/ListboxOption.tsx +104 -0
- package/src/Menu/Menu.mdx +11 -13
- package/src/Menu/Menu.test.tsx +47 -38
- package/src/Menu/Menu.tsx +81 -142
- package/src/Modal/Modal.mdx +29 -82
- package/src/Modal/Modal.stories.tsx +55 -188
- package/src/Modal/Modal.test.tsx +44 -177
- package/src/Modal/Modal.tsx +54 -204
- package/src/Overlay/OverlayPanel.tsx +195 -0
- package/src/Overlay/OverlayStack.test.tsx +235 -0
- package/src/Overlay/OverlayStack.tsx +132 -0
- package/src/Overlay/SharedBackdrop.tsx +40 -0
- package/src/Overlay/overlayPanelClass.ts +17 -0
- package/src/Overlay/useAnchoredOverlay.ts +197 -0
- package/src/Popover/Popover.mdx +17 -20
- package/src/Popover/Popover.stories.tsx +33 -0
- package/src/Popover/Popover.test.tsx +68 -32
- package/src/Popover/Popover.tsx +56 -95
- package/src/Tooltip/Tooltip.mdx +12 -9
- package/src/Tooltip/Tooltip.test.tsx +17 -19
- package/src/Tooltip/Tooltip.tsx +35 -32
- package/src/icons.storyHelpers.tsx +22 -0
- package/src/index.ts +22 -4
- package/src/mountStory.testHelpers.ts +25 -1
- package/src/sourceRules.test.ts +26 -1
- package/src/storyControls.test.ts +25 -12
- package/dist/Modal/lockScrollBehind.d.ts.map +0 -1
- package/dist/Modal/lockScrollBehind.js.map +0 -1
- /package/dist/{Modal → Overlay}/lockScrollBehind.d.ts +0 -0
- /package/dist/{Modal → Overlay}/lockScrollBehind.js +0 -0
- /package/src/{Modal → Overlay}/lockScrollBehind.ts +0 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useLatestRef } from "@charcuterie/logic";
|
|
3
|
+
import { createContext, useCallback, useContext, useMemo, useState, } from "react";
|
|
4
|
+
import { SharedBackdrop } from "./SharedBackdrop.js";
|
|
5
|
+
const DEFAULT_STACK = {
|
|
6
|
+
depth: 0,
|
|
7
|
+
isProvided: false,
|
|
8
|
+
register: () => { },
|
|
9
|
+
requestCloseTop: () => { },
|
|
10
|
+
topId: null,
|
|
11
|
+
unregister: () => { },
|
|
12
|
+
};
|
|
13
|
+
const OverlayStackContext = createContext(DEFAULT_STACK);
|
|
14
|
+
export const useOverlayStack = () => useContext(OverlayStackContext);
|
|
15
|
+
export const OverlayStackProvider = ({ children, }) => {
|
|
16
|
+
const [entries, setEntries] = useState([]);
|
|
17
|
+
// Read through a ref so `requestCloseTop` stays stable while still
|
|
18
|
+
// seeing the live top.
|
|
19
|
+
const entriesRef = useLatestRef(entries);
|
|
20
|
+
const register = useCallback((entry) => {
|
|
21
|
+
setEntries((previous) =>
|
|
22
|
+
// In place when the id is already present — an `onClose`
|
|
23
|
+
// update must not reorder the stack, or a re-render would
|
|
24
|
+
// shuffle which modal is "top". New ids append, which is what
|
|
25
|
+
// makes the last-registered one the top.
|
|
26
|
+
previous.some((one) => one.id === entry.id)
|
|
27
|
+
? previous.map((one) => one.id === entry.id ? entry : one)
|
|
28
|
+
: [...previous, entry]);
|
|
29
|
+
}, []);
|
|
30
|
+
const unregister = useCallback((id) => {
|
|
31
|
+
setEntries((previous) => previous.filter((one) => one.id !== id));
|
|
32
|
+
}, []);
|
|
33
|
+
const requestCloseTop = useCallback(() => {
|
|
34
|
+
entriesRef.current.at(-1)?.onClose();
|
|
35
|
+
}, [entriesRef]);
|
|
36
|
+
const value = useMemo(() => ({
|
|
37
|
+
depth: entries.length,
|
|
38
|
+
isProvided: true,
|
|
39
|
+
register,
|
|
40
|
+
requestCloseTop,
|
|
41
|
+
topId: entries.at(-1)?.id ?? null,
|
|
42
|
+
unregister,
|
|
43
|
+
}), [entries, register, requestCloseTop, unregister]);
|
|
44
|
+
return (_jsxs(OverlayStackContext.Provider, { value: value, children: [_jsx(SharedBackdrop, { isVisible: entries.length > 0 }), children] }));
|
|
45
|
+
};
|
|
46
|
+
//# sourceMappingURL=OverlayStack.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OverlayStack.js","sourceRoot":"","sources":["../../src/Overlay/OverlayStack.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEjD,OAAO,EACL,aAAa,EACb,WAAW,EACX,UAAU,EACV,OAAO,EACP,QAAQ,GACT,MAAM,OAAO,CAAA;AAEd,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAsB,CAAA;AA6CrD,MAAM,aAAa,GAAsB;IACvC,KAAK,EAAE,CAAC;IACR,UAAU,EAAE,KAAK;IACjB,QAAQ,EAAE,GAAG,EAAE,GAAE,CAAC;IAClB,eAAe,EAAE,GAAG,EAAE,GAAE,CAAC;IACzB,KAAK,EAAE,IAAI;IACX,UAAU,EAAE,GAAG,EAAE,GAAE,CAAC;CACrB,CAAA;AAED,MAAM,mBAAmB,GACvB,aAAa,CAAoB,aAAa,CAAC,CAAA;AAEjD,MAAM,CAAC,MAAM,eAAe,GAAG,GAAsB,EAAE,CACrD,UAAU,CAAC,mBAAmB,CAAC,CAAA;AAEjC,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,EACnC,QAAQ,GAGT,EAAa,EAAE;IACd,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAEpC,EAAE,CAAC,CAAA;IAEL,mEAAmE;IACnE,uBAAuB;IACvB,MAAM,UAAU,GAAG,YAAY,CAAC,OAAO,CAAC,CAAA;IAExC,MAAM,QAAQ,GAAG,WAAW,CAC1B,CAAC,KAAwB,EAAE,EAAE;QAC3B,UAAU,CAAC,CAAC,QAAQ,EAAE,EAAE;QACtB,yDAAyD;QACzD,0DAA0D;QAC1D,8DAA8D;QAC9D,yCAAyC;QACzC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,KAAK,CAAC,EAAE,CAAC;YACzC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CACnB,GAAG,CAAC,EAAE,KAAK,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAClC;YACH,CAAC,CAAC,CAAC,GAAG,QAAQ,EAAE,KAAK,CAAC,CACzB,CAAA;IACH,CAAC,EACD,EAAE,CACH,CAAA;IAED,MAAM,UAAU,GAAG,WAAW,CAAC,CAAC,EAAU,EAAE,EAAE;QAC5C,UAAU,CAAC,CAAC,QAAQ,EAAE,EAAE,CACtB,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC,CACxC,CAAA;IACH,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,MAAM,eAAe,GAAG,WAAW,CAAC,GAAG,EAAE;QACvC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAA;IACtC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAA;IAEhB,MAAM,KAAK,GAAG,OAAO,CACnB,GAAG,EAAE,CAAC,CAAC;QACL,KAAK,EAAE,OAAO,CAAC,MAAM;QACrB,UAAU,EAAE,IAAI;QAChB,QAAQ;QACR,eAAe;QACf,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,IAAI;QACjC,UAAU;KACX,CAAC,EACF,CAAC,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,UAAU,CAAC,CACjD,CAAA;IAED,OAAO,CACL,MAAC,mBAAmB,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,aAGxC,KAAC,cAAc,IAAC,SAAS,EAAE,OAAO,CAAC,MAAM,GAAG,CAAC,GAAI,EAEhD,QAAQ,IACoB,CAChC,CAAA;AACH,CAAC,CAAA"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* The scrim, and there is exactly one of it however many backdrop
|
|
4
|
+
* modals are stacked.
|
|
5
|
+
*
|
|
6
|
+
* Purely visual: `aria-hidden`, no handlers. Dismissal — a press
|
|
7
|
+
* anywhere off the top panel — is the top panel's own
|
|
8
|
+
* `useDismiss({ outsidePress })`, not a click on this node, which is
|
|
9
|
+
* what lets the whole package drop the `useKeyWithClickEvents`
|
|
10
|
+
* suppression the `<dialog>` backdrop needed. A press lands on the
|
|
11
|
+
* top panel's full-viewport centring layer (which sits above this),
|
|
12
|
+
* so this element never has to be the click target.
|
|
13
|
+
*
|
|
14
|
+
* Portalled to `document.body` on `--layer-overlay`, one step **below**
|
|
15
|
+
* the panels' `--layer-modal`, so it always paints behind every open
|
|
16
|
+
* modal and never intercepts a click meant for the dialog on top of it
|
|
17
|
+
* — a real bug a same-z-plus-DOM-order scheme has, because a
|
|
18
|
+
* later-mounted scrim then covers the panel. Stacking *between* panels
|
|
19
|
+
* still falls out of append order at the one `--layer-modal`.
|
|
20
|
+
*/
|
|
21
|
+
export declare const SharedBackdrop: ({ isVisible, }: {
|
|
22
|
+
isVisible: boolean;
|
|
23
|
+
}) => ReactNode;
|
|
24
|
+
//# sourceMappingURL=SharedBackdrop.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SharedBackdrop.d.ts","sourceRoot":"","sources":["../../src/Overlay/SharedBackdrop.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEtC;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,cAAc,GAAI,gBAE5B;IACD,SAAS,EAAE,OAAO,CAAA;CACnB,KAAG,SAaH,CAAA"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { FloatingPortal } from "@floating-ui/react";
|
|
3
|
+
/**
|
|
4
|
+
* The scrim, and there is exactly one of it however many backdrop
|
|
5
|
+
* modals are stacked.
|
|
6
|
+
*
|
|
7
|
+
* Purely visual: `aria-hidden`, no handlers. Dismissal — a press
|
|
8
|
+
* anywhere off the top panel — is the top panel's own
|
|
9
|
+
* `useDismiss({ outsidePress })`, not a click on this node, which is
|
|
10
|
+
* what lets the whole package drop the `useKeyWithClickEvents`
|
|
11
|
+
* suppression the `<dialog>` backdrop needed. A press lands on the
|
|
12
|
+
* top panel's full-viewport centring layer (which sits above this),
|
|
13
|
+
* so this element never has to be the click target.
|
|
14
|
+
*
|
|
15
|
+
* Portalled to `document.body` on `--layer-overlay`, one step **below**
|
|
16
|
+
* the panels' `--layer-modal`, so it always paints behind every open
|
|
17
|
+
* modal and never intercepts a click meant for the dialog on top of it
|
|
18
|
+
* — a real bug a same-z-plus-DOM-order scheme has, because a
|
|
19
|
+
* later-mounted scrim then covers the panel. Stacking *between* panels
|
|
20
|
+
* still falls out of append order at the one `--layer-modal`.
|
|
21
|
+
*/
|
|
22
|
+
export const SharedBackdrop = ({ isVisible, }) => {
|
|
23
|
+
if (!isVisible) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
return (_jsx(FloatingPortal, { children: _jsx("div", { "aria-hidden": "true", className: "fixed inset-0 z-[var(--layer-overlay)] bg-scrim" }) }));
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=SharedBackdrop.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SharedBackdrop.js","sourceRoot":"","sources":["../../src/Overlay/SharedBackdrop.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAGnD;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,EAC7B,SAAS,GAGV,EAAa,EAAE;IACd,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO,IAAI,CAAA;IACb,CAAC;IAED,OAAO,CACL,KAAC,cAAc,cACb,6BACc,MAAM,EAClB,SAAS,EAAC,iDAAiD,GAC3D,GACa,CAClB,CAAA;AACH,CAAC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lockScrollBehind.d.ts","sourceRoot":"","sources":["../../src/Overlay/lockScrollBehind.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAMH,eAAO,MAAM,gBAAgB,QAAO,CAAC,MAAM,IAAI,CAiC9C,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lockScrollBehind.js","sourceRoot":"","sources":["../../src/Overlay/lockScrollBehind.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,IAAI,SAAS,GAAG,CAAC,CAAA;AAEjB,IAAI,gBAAgB,GAAG,EAAE,CAAA;AAEzB,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAiB,EAAE;IACjD,IAAI,OAAO,QAAQ,KAAK,WAAW,EAAE,CAAC;QACpC,OAAO,GAAG,EAAE,GAAE,CAAC,CAAA;IACjB,CAAC;IAED,MAAM,EAAE,eAAe,EAAE,GAAG,QAAQ,CAAA;IAEpC,IAAI,SAAS,KAAK,CAAC,EAAE,CAAC;QACpB,gBAAgB,GAAG,eAAe,CAAC,KAAK,CAAC,QAAQ,CAAA;QAEjD,eAAe,CAAC,KAAK,CAAC,QAAQ,GAAG,QAAQ,CAAA;IAC3C,CAAC;IAED,SAAS,IAAI,CAAC,CAAA;IAEd,IAAI,UAAU,GAAG,KAAK,CAAA;IAEtB,OAAO,GAAG,EAAE;QACV,6DAA6D;QAC7D,6DAA6D;QAC7D,qCAAqC;QACrC,IAAI,UAAU,EAAE,CAAC;YACf,OAAM;QACR,CAAC;QAED,UAAU,GAAG,IAAI,CAAA;QAEjB,SAAS,IAAI,CAAC,CAAA;QAEd,IAAI,SAAS,KAAK,CAAC,EAAE,CAAC;YACpB,eAAe,CAAC,KAAK,CAAC,QAAQ,GAAG,gBAAgB,CAAA;QACnD,CAAC;IACH,CAAC,CAAA;AACH,CAAC,CAAA"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The surface an anchored overlay's panel is drawn on — the four
|
|
3
|
+
* tokens `Popover` and `Menu` wrote out byte-identically, and that
|
|
4
|
+
* `Listbox` and `Combobox` are about to write a third and fourth
|
|
5
|
+
* time.
|
|
6
|
+
*
|
|
7
|
+
* Only the *surface* lives here: the raised background, the hairline
|
|
8
|
+
* border, the corner radius, and the elevation. Everything a panel
|
|
9
|
+
* does with that surface — a popover's `max-w-xs p-3`, a menu's
|
|
10
|
+
* `min-w-48 flex flex-col`, a listbox's scroll clamp — is the panel's
|
|
11
|
+
* own and stays at the call site, composed after this with
|
|
12
|
+
* `toClassName`. Pulling the shared half out is what keeps a change
|
|
13
|
+
* to the overlay surface (a new elevation token, say) from having to
|
|
14
|
+
* be made in four places and kept in agreement by hand.
|
|
15
|
+
*/
|
|
16
|
+
export declare const PANEL_SURFACE_CLASS = "rounded-md border border-border-default bg-surface-overlay shadow-medium";
|
|
17
|
+
//# sourceMappingURL=overlayPanelClass.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overlayPanelClass.d.ts","sourceRoot":"","sources":["../../src/Overlay/overlayPanelClass.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,mBAAmB,6EAC4C,CAAA"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The surface an anchored overlay's panel is drawn on — the four
|
|
3
|
+
* tokens `Popover` and `Menu` wrote out byte-identically, and that
|
|
4
|
+
* `Listbox` and `Combobox` are about to write a third and fourth
|
|
5
|
+
* time.
|
|
6
|
+
*
|
|
7
|
+
* Only the *surface* lives here: the raised background, the hairline
|
|
8
|
+
* border, the corner radius, and the elevation. Everything a panel
|
|
9
|
+
* does with that surface — a popover's `max-w-xs p-3`, a menu's
|
|
10
|
+
* `min-w-48 flex flex-col`, a listbox's scroll clamp — is the panel's
|
|
11
|
+
* own and stays at the call site, composed after this with
|
|
12
|
+
* `toClassName`. Pulling the shared half out is what keeps a change
|
|
13
|
+
* to the overlay surface (a new elevation token, say) from having to
|
|
14
|
+
* be made in four places and kept in agreement by hand.
|
|
15
|
+
*/
|
|
16
|
+
export const PANEL_SURFACE_CLASS = "rounded-md border border-border-default bg-surface-overlay shadow-medium";
|
|
17
|
+
//# sourceMappingURL=overlayPanelClass.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overlayPanelClass.js","sourceRoot":"","sources":["../../src/Overlay/overlayPanelClass.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAC9B,0EAA0E,CAAA"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { Placement } from "@floating-ui/react";
|
|
2
|
+
import type { ReactElement } from "react";
|
|
3
|
+
/**
|
|
4
|
+
* The floating-ui block every anchored overlay — `Popover`, `Menu`,
|
|
5
|
+
* `Listbox`, `Combobox` — wrote out for itself, consolidated once.
|
|
6
|
+
*
|
|
7
|
+
* `Popover` and `Menu` had the same twelve-line `useFloating` +
|
|
8
|
+
* `useInteractions` + `useClonedChild` preamble with two constants
|
|
9
|
+
* different (the offset and the role), and the fleet's four pickers
|
|
10
|
+
* hand-rolled a fifth, sixth, seventh and eighth `computePosition`
|
|
11
|
+
* that reimplemented `flip`/`shift` against `window.innerHeight`.
|
|
12
|
+
* This is the one implementation they all take.
|
|
13
|
+
*
|
|
14
|
+
* ### The owner's "configurable, but those are the basics"
|
|
15
|
+
*
|
|
16
|
+
* Escape and outside-press both close, **on by default**, for every
|
|
17
|
+
* anchored overlay — that is what an overlay is. A component switches
|
|
18
|
+
* one off through `dismiss` (a `Combobox`'s Escape clears the query
|
|
19
|
+
* before it closes; a menu opened from a context click wants no
|
|
20
|
+
* outside-press race with the thing that spawned it), but it opts out
|
|
21
|
+
* rather than opting in.
|
|
22
|
+
*
|
|
23
|
+
* ### `size`, for the pickers
|
|
24
|
+
*
|
|
25
|
+
* `matchTriggerWidth` and `maxHeightPx` are the two things
|
|
26
|
+
* `PortalDropdown` did with a hand-measured `TriggerRect` and a
|
|
27
|
+
* `window.innerHeight` clamp. The `size` middleware does both from the
|
|
28
|
+
* platform: the panel takes the trigger's width, and it never grows
|
|
29
|
+
* past the space actually left in the viewport.
|
|
30
|
+
*
|
|
31
|
+
* The computed width/height are written **straight onto the floating
|
|
32
|
+
* element's style** rather than lifted into React state, which is
|
|
33
|
+
* floating-ui's own documented shape and is deliberate twice over: a
|
|
34
|
+
* `setState` inside `apply` re-runs the position, which re-runs
|
|
35
|
+
* `apply`, which is a render loop; and because these properties never
|
|
36
|
+
* enter the `floatingStyles` object React manages, a component
|
|
37
|
+
* re-render (a keystroke in a combobox) does not wipe them — only an
|
|
38
|
+
* `autoUpdate` tick recomputes them.
|
|
39
|
+
*
|
|
40
|
+
* ### Portal-agnostic
|
|
41
|
+
*
|
|
42
|
+
* This returns the pieces — `clonedTrigger`, `getFloatingProps`,
|
|
43
|
+
* `setFloating`, `floatingStyles`, `context` — and renders nothing.
|
|
44
|
+
* Whether the panel goes in a `FloatingPortal`, the top layer, or in
|
|
45
|
+
* place is the component's call, which is what lets the same hook back
|
|
46
|
+
* a portalled `Listbox` and (for now) a top-layer `Popover`.
|
|
47
|
+
*/
|
|
48
|
+
export type AnchoredOverlayRole = "dialog" | "listbox" | "menu";
|
|
49
|
+
export type UseAnchoredOverlayOptions = {
|
|
50
|
+
/**
|
|
51
|
+
* Both default **on** — Escape and outside-press are what makes an
|
|
52
|
+
* overlay an overlay. Switch one off for the honest exception (a
|
|
53
|
+
* combobox that swallows the first Escape to clear its query), never
|
|
54
|
+
* to make a plain overlay sticky. Named for the flattened
|
|
55
|
+
* floating-ui `useDismiss` options they carry.
|
|
56
|
+
*/
|
|
57
|
+
isEscapeDismissable?: boolean;
|
|
58
|
+
isOutsidePressDismissable?: boolean;
|
|
59
|
+
/** Ports `PortalDropdown`'s anchor-width match, via `size`. */
|
|
60
|
+
isTriggerWidthMatched?: boolean;
|
|
61
|
+
isVisible: boolean;
|
|
62
|
+
/** Clamps the panel to `min(this, available viewport space)`. */
|
|
63
|
+
maxHeightPx?: number;
|
|
64
|
+
/** Popover 8, Menu/Listbox/Combobox 4. */
|
|
65
|
+
offsetValue?: number;
|
|
66
|
+
onDismiss: () => void;
|
|
67
|
+
placement?: Placement;
|
|
68
|
+
role: AnchoredOverlayRole;
|
|
69
|
+
/** The control the panel hangs off. **Cloned, not wrapped.** */
|
|
70
|
+
trigger: ReactElement;
|
|
71
|
+
};
|
|
72
|
+
export declare const useAnchoredOverlay: ({ isEscapeDismissable, isOutsidePressDismissable, isTriggerWidthMatched, isVisible, maxHeightPx, offsetValue, onDismiss, placement, role, trigger, }: UseAnchoredOverlayOptions) => {
|
|
73
|
+
clonedTrigger: ReactElement<Record<string, unknown>, string | import("react").JSXElementConstructor<any>>;
|
|
74
|
+
context: {
|
|
75
|
+
placement: Placement;
|
|
76
|
+
strategy: import("@floating-ui/utils").Strategy;
|
|
77
|
+
x: number;
|
|
78
|
+
y: number;
|
|
79
|
+
middlewareData: import("@floating-ui/core").MiddlewareData;
|
|
80
|
+
isPositioned: boolean;
|
|
81
|
+
update: () => void;
|
|
82
|
+
floatingStyles: React.CSSProperties;
|
|
83
|
+
open: boolean;
|
|
84
|
+
onOpenChange: (open: boolean, event?: Event, reason?: import("@floating-ui/react").OpenChangeReason) => void;
|
|
85
|
+
events: import("@floating-ui/react").FloatingEvents;
|
|
86
|
+
dataRef: React.MutableRefObject<import("@floating-ui/react").ContextData>;
|
|
87
|
+
nodeId: string | undefined;
|
|
88
|
+
floatingId: string | undefined;
|
|
89
|
+
refs: import("@floating-ui/react").ExtendedRefs<import("@floating-ui/react").ReferenceType>;
|
|
90
|
+
elements: import("@floating-ui/react").ExtendedElements<import("@floating-ui/react").ReferenceType>;
|
|
91
|
+
};
|
|
92
|
+
floatingStyles: import("react").CSSProperties;
|
|
93
|
+
getFloatingProps: (userProps?: React.HTMLProps<HTMLElement>) => Record<string, unknown>;
|
|
94
|
+
setFloating: ((node: HTMLElement | null) => void) & ((node: HTMLElement | null) => void);
|
|
95
|
+
triggerId: string;
|
|
96
|
+
};
|
|
97
|
+
//# sourceMappingURL=useAnchoredOverlay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useAnchoredOverlay.d.ts","sourceRoot":"","sources":["../../src/Overlay/useAnchoredOverlay.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAYnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAA;AAEzC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,MAAM,MAAM,mBAAmB,GAC3B,QAAQ,GACR,SAAS,GACT,MAAM,CAAA;AAEV,MAAM,MAAM,yBAAyB,GAAG;IACtC;;;;;;OAMG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B,yBAAyB,CAAC,EAAE,OAAO,CAAA;IACnC,+DAA+D;IAC/D,qBAAqB,CAAC,EAAE,OAAO,CAAA;IAC/B,SAAS,EAAE,OAAO,CAAA;IAClB,iEAAiE;IACjE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,0CAA0C;IAC1C,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,SAAS,CAAC,EAAE,SAAS,CAAA;IACrB,IAAI,EAAE,mBAAmB,CAAA;IACzB,gEAAgE;IAChE,OAAO,EAAE,YAAY,CAAA;CACtB,CAAA;AAED,eAAO,MAAM,kBAAkB,GAAI,sJAWhC,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;CA6F3B,CAAA"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { useClonedChild, useUniqueId, } from "@charcuterie/logic";
|
|
2
|
+
import { autoUpdate, flip, offset, shift, size, useDismiss, useFloating, useInteractions, useRole, } from "@floating-ui/react";
|
|
3
|
+
export const useAnchoredOverlay = ({ isEscapeDismissable = true, isOutsidePressDismissable = true, isTriggerWidthMatched = false, isVisible, maxHeightPx, offsetValue = 8, onDismiss, placement = "bottom-start", role, trigger, }) => {
|
|
4
|
+
const middleware = [
|
|
5
|
+
offset(offsetValue),
|
|
6
|
+
flip(),
|
|
7
|
+
shift({ padding: 8 }),
|
|
8
|
+
];
|
|
9
|
+
if (isTriggerWidthMatched || maxHeightPx !== undefined) {
|
|
10
|
+
middleware.push(size({
|
|
11
|
+
padding: 8,
|
|
12
|
+
apply({ availableHeight, elements, rects }) {
|
|
13
|
+
const cap = maxHeightPx === undefined
|
|
14
|
+
? availableHeight
|
|
15
|
+
: Math.min(maxHeightPx, availableHeight);
|
|
16
|
+
elements.floating.style.maxHeight = `${cap}px`;
|
|
17
|
+
if (isTriggerWidthMatched) {
|
|
18
|
+
elements.floating.style.width = `${rects.reference.width}px`;
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
}));
|
|
22
|
+
}
|
|
23
|
+
const { context, floatingStyles, refs } = useFloating({
|
|
24
|
+
// Read-only. Charcuterie remains the sole owner; floating-ui is
|
|
25
|
+
// told what is true and never decides it.
|
|
26
|
+
open: isVisible,
|
|
27
|
+
onOpenChange: (isNextVisible) => {
|
|
28
|
+
if (!isNextVisible) {
|
|
29
|
+
onDismiss();
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
placement,
|
|
33
|
+
middleware,
|
|
34
|
+
strategy: "fixed",
|
|
35
|
+
whileElementsMounted: autoUpdate,
|
|
36
|
+
});
|
|
37
|
+
// `useRole` writes `aria-expanded`/`aria-haspopup`/`aria-controls`
|
|
38
|
+
// on the trigger and `id` + `role` on the panel from one source —
|
|
39
|
+
// which is what keeps `aria-controls` pointing at the panel's real
|
|
40
|
+
// id across the portal boundary, the objection the top-layer
|
|
41
|
+
// decision raised and this answers.
|
|
42
|
+
const { getFloatingProps, getReferenceProps } = useInteractions([
|
|
43
|
+
useDismiss(context, {
|
|
44
|
+
escapeKey: isEscapeDismissable,
|
|
45
|
+
outsidePress: isOutsidePressDismissable,
|
|
46
|
+
}),
|
|
47
|
+
useRole(context, { role }),
|
|
48
|
+
]);
|
|
49
|
+
// `useRole` for a `listbox` overwrites the trigger's own role with
|
|
50
|
+
// `combobox` — the select-only-combobox pattern, which then demands
|
|
51
|
+
// its own `aria-label`. The fleet's pattern is a plain button that
|
|
52
|
+
// *opens* a listbox, so the injected `role` is dropped and the
|
|
53
|
+
// trigger keeps its native semantics while still gaining
|
|
54
|
+
// `aria-haspopup="listbox"`/`aria-expanded`/`aria-controls`. `useRole`
|
|
55
|
+
// injects a reference `role` only for listbox/combobox, so this is a
|
|
56
|
+
// no-op for `Popover` (dialog) and `Menu` (menu).
|
|
57
|
+
const { role: _discardedTriggerRole, ...referenceProps } = getReferenceProps();
|
|
58
|
+
// A stable id on the trigger, so a panel that needs a name can point
|
|
59
|
+
// `aria-labelledby` at it across the portal. `useRole` already gives
|
|
60
|
+
// a `menu`'s reference an id (and labels the panel with it); this
|
|
61
|
+
// reuses that when present and supplies one otherwise — which is what
|
|
62
|
+
// a `listbox` panel needs, since a bare `role="listbox"` is an ARIA
|
|
63
|
+
// input field and must be named.
|
|
64
|
+
const generatedTriggerId = useUniqueId();
|
|
65
|
+
const triggerId = referenceProps.id ??
|
|
66
|
+
generatedTriggerId;
|
|
67
|
+
const clonedTrigger = useClonedChild(trigger, {
|
|
68
|
+
...referenceProps,
|
|
69
|
+
id: triggerId,
|
|
70
|
+
ref: refs.setReference,
|
|
71
|
+
});
|
|
72
|
+
return {
|
|
73
|
+
clonedTrigger,
|
|
74
|
+
context,
|
|
75
|
+
floatingStyles,
|
|
76
|
+
getFloatingProps,
|
|
77
|
+
setFloating: refs.setFloating,
|
|
78
|
+
triggerId,
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
//# sourceMappingURL=useAnchoredOverlay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useAnchoredOverlay.js","sourceRoot":"","sources":["../../src/Overlay/useAnchoredOverlay.ts"],"names":[],"mappings":"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"}
|
|
@@ -35,32 +35,24 @@ export type PopoverProps = {
|
|
|
35
35
|
*
|
|
36
36
|
* What floating-ui is used for is what only it can do: collision
|
|
37
37
|
* handling (`flip`, `shift`), a real dismiss layer, and focus
|
|
38
|
-
* management
|
|
39
|
-
* wiring come from the platform and
|
|
38
|
+
* management — all of it consolidated in `useAnchoredOverlay`.
|
|
39
|
+
* Positioning strategy and the ARIA wiring come from the platform and
|
|
40
|
+
* from `useRole`.
|
|
40
41
|
*
|
|
41
|
-
* ###
|
|
42
|
+
* ### Portalled, not the top layer
|
|
42
43
|
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
* no `
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
* `
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
* ### Two UA defaults that have to be undone
|
|
57
|
-
*
|
|
58
|
-
* `[popover]` ships `position: fixed; inset: 0; margin: auto`,
|
|
59
|
-
* which centres the panel in the viewport and fights every
|
|
60
|
-
* coordinate floating-ui computes — hence `inset-auto m-0`, and
|
|
61
|
-
* `strategy: "fixed"` so the numbers are viewport-relative like the
|
|
62
|
-
* top layer itself. Getting either wrong reads as a positioning
|
|
63
|
-
* bug rather than as a UA default.
|
|
44
|
+
* The panel goes in a `FloatingPortal` to `document.body`. The top
|
|
45
|
+
* layer (`popover="manual"`) was reached for first — it needs no
|
|
46
|
+
* portal and no `z-index` — but it does not escape an
|
|
47
|
+
* `overflow: hidden` ancestor's *clipping* in every case the fleet
|
|
48
|
+
* hit, and a portal does. The objection a portal used to raise, that
|
|
49
|
+
* it moves the panel out of a scoped `canvas` query, is answered two
|
|
50
|
+
* ways: `useRole` writes `aria-controls` on the trigger pointing at
|
|
51
|
+
* the panel `id`, so trigger→panel stays followable across the
|
|
52
|
+
* boundary; and the story scopes panel queries to
|
|
53
|
+
* `within(document.body)` while the structural `expectAgentDrivable`
|
|
54
|
+
* needs no change. `strategy: "fixed"` keeps the coordinates
|
|
55
|
+
* viewport-relative.
|
|
64
56
|
*/
|
|
65
57
|
export declare const Popover: ({ children, className, heading, isVisible, onDismiss, placement, trigger, }: PopoverProps) => ReactNode;
|
|
66
58
|
//# sourceMappingURL=Popover.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Popover.d.ts","sourceRoot":"","sources":["../../src/Popover/Popover.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Popover.d.ts","sourceRoot":"","sources":["../../src/Popover/Popover.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAKnD,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAMpD,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,EAAE,SAAS,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;;;OAMG;IACH,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,OAAO,CAAA;IAClB;;;OAGG;IACH,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,SAAS,CAAC,EAAE,SAAS,CAAA;IACrB;;;;OAIG;IACH,OAAO,EAAE,YAAY,CAAA;CACtB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,OAAO,GAAI,6EAQrB,YAAY,KAAG,SAkDjB,CAAA"}
|
package/dist/Popover/Popover.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { FloatingFocusManager, FloatingPortal, } from "@floating-ui/react";
|
|
3
|
+
import { PANEL_SURFACE_CLASS } from "../Overlay/overlayPanelClass.js";
|
|
4
|
+
import { useAnchoredOverlay } from "../Overlay/useAnchoredOverlay.js";
|
|
4
5
|
import { toClassName } from "../toClassName.js";
|
|
5
6
|
/**
|
|
6
7
|
* Replaces mux-magic's `@radix-ui/react-popover` re-export, and it
|
|
@@ -12,77 +13,40 @@ import { toClassName } from "../toClassName.js";
|
|
|
12
13
|
*
|
|
13
14
|
* What floating-ui is used for is what only it can do: collision
|
|
14
15
|
* handling (`flip`, `shift`), a real dismiss layer, and focus
|
|
15
|
-
* management
|
|
16
|
-
* wiring come from the platform and
|
|
16
|
+
* management — all of it consolidated in `useAnchoredOverlay`.
|
|
17
|
+
* Positioning strategy and the ARIA wiring come from the platform and
|
|
18
|
+
* from `useRole`.
|
|
17
19
|
*
|
|
18
|
-
* ###
|
|
20
|
+
* ### Portalled, not the top layer
|
|
19
21
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* no `
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* `
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* ### Two UA defaults that have to be undone
|
|
34
|
-
*
|
|
35
|
-
* `[popover]` ships `position: fixed; inset: 0; margin: auto`,
|
|
36
|
-
* which centres the panel in the viewport and fights every
|
|
37
|
-
* coordinate floating-ui computes — hence `inset-auto m-0`, and
|
|
38
|
-
* `strategy: "fixed"` so the numbers are viewport-relative like the
|
|
39
|
-
* top layer itself. Getting either wrong reads as a positioning
|
|
40
|
-
* bug rather than as a UA default.
|
|
22
|
+
* The panel goes in a `FloatingPortal` to `document.body`. The top
|
|
23
|
+
* layer (`popover="manual"`) was reached for first — it needs no
|
|
24
|
+
* portal and no `z-index` — but it does not escape an
|
|
25
|
+
* `overflow: hidden` ancestor's *clipping* in every case the fleet
|
|
26
|
+
* hit, and a portal does. The objection a portal used to raise, that
|
|
27
|
+
* it moves the panel out of a scoped `canvas` query, is answered two
|
|
28
|
+
* ways: `useRole` writes `aria-controls` on the trigger pointing at
|
|
29
|
+
* the panel `id`, so trigger→panel stays followable across the
|
|
30
|
+
* boundary; and the story scopes panel queries to
|
|
31
|
+
* `within(document.body)` while the structural `expectAgentDrivable`
|
|
32
|
+
* needs no change. `strategy: "fixed"` keeps the coordinates
|
|
33
|
+
* viewport-relative.
|
|
41
34
|
*/
|
|
42
35
|
export const Popover = ({ children, className, heading, isVisible, onDismiss, placement = "bottom-start", trigger, }) => {
|
|
43
|
-
const { context, floatingStyles,
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
onOpenChange: (isNextVisible) => {
|
|
48
|
-
if (!isNextVisible) {
|
|
49
|
-
onDismiss();
|
|
50
|
-
}
|
|
51
|
-
},
|
|
36
|
+
const { clonedTrigger, context, floatingStyles, getFloatingProps, setFloating, } = useAnchoredOverlay({
|
|
37
|
+
isVisible,
|
|
38
|
+
offsetValue: 8,
|
|
39
|
+
onDismiss,
|
|
52
40
|
placement,
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
whileElementsMounted: autoUpdate,
|
|
56
|
-
});
|
|
57
|
-
// `useRole` writes `aria-expanded`, `aria-haspopup`, and
|
|
58
|
-
// `aria-controls` on the trigger and `id` + `role="dialog"` on
|
|
59
|
-
// the panel, from one source. Hand-writing them alongside is how
|
|
60
|
-
// an `aria-controls` ends up pointing at an id that moved.
|
|
61
|
-
const { getFloatingProps, getReferenceProps } = useInteractions([
|
|
62
|
-
useDismiss(context),
|
|
63
|
-
useRole(context, { role: "dialog" }),
|
|
64
|
-
]);
|
|
65
|
-
const clonedTrigger = useClonedChild(trigger, {
|
|
66
|
-
...getReferenceProps(),
|
|
67
|
-
ref: refs.setReference,
|
|
41
|
+
role: "dialog",
|
|
42
|
+
trigger,
|
|
68
43
|
});
|
|
69
|
-
return (_jsxs(_Fragment, { children: [clonedTrigger, isVisible ? (_jsx(FloatingFocusManager, { context: context, modal: false, children: _jsx("div", { ...getFloatingProps(), "aria-label": heading, className: toClassName("
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
refs.setFloating(node);
|
|
77
|
-
// In a ref callback rather than an effect, because a
|
|
78
|
-
// `[popover]` is `display: none` until it is shown —
|
|
79
|
-
// and `FloatingFocusManager` is a *child*, so its
|
|
80
|
-
// effect runs first and would be focusing into a
|
|
81
|
-
// hidden element. Refs are set during commit, before
|
|
82
|
-
// any of that.
|
|
83
|
-
if (node && !node.matches(":popover-open")) {
|
|
84
|
-
node.showPopover();
|
|
85
|
-
}
|
|
86
|
-
}, style: floatingStyles, children: children }) })) : null] }));
|
|
44
|
+
return (_jsxs(_Fragment, { children: [clonedTrigger, isVisible ? (_jsx(FloatingPortal, { children: _jsx(FloatingFocusManager, { context: context, modal: false, children: _jsx("div", { ...getFloatingProps(), "aria-label": heading, className: toClassName(PANEL_SURFACE_CLASS, "z-[var(--layer-modal)] max-w-xs p-3 text-content-primary text-sm", className), ref: setFloating,
|
|
45
|
+
// Duplicated from `getFloatingProps()` on purpose, and
|
|
46
|
+
// the two cannot differ — `useRole(context, { role:
|
|
47
|
+
// "dialog" })` inside the hook is where it comes from.
|
|
48
|
+
// Stated here because a linter cannot see a role through
|
|
49
|
+
// a spread, and neither can the next reader.
|
|
50
|
+
role: "dialog", style: floatingStyles, children: children }) }) })) : null] }));
|
|
87
51
|
};
|
|
88
52
|
//# sourceMappingURL=Popover.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Popover.js","sourceRoot":"","sources":["../../src/Popover/Popover.tsx"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"Popover.js","sourceRoot":"","sources":["../../src/Popover/Popover.tsx"],"names":[],"mappings":";AACA,OAAO,EACL,oBAAoB,EACpB,cAAc,GACf,MAAM,oBAAoB,CAAA;AAG3B,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAA;AACrE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAA;AACrE,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AA4B/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,EACtB,QAAQ,EACR,SAAS,EACT,OAAO,EACP,SAAS,EACT,SAAS,EACT,SAAS,GAAG,cAAc,EAC1B,OAAO,GACM,EAAa,EAAE;IAC5B,MAAM,EACJ,aAAa,EACb,OAAO,EACP,cAAc,EACd,gBAAgB,EAChB,WAAW,GACZ,GAAG,kBAAkB,CAAC;QACrB,SAAS;QACT,WAAW,EAAE,CAAC;QACd,SAAS;QACT,SAAS;QACT,IAAI,EAAE,QAAQ;QACd,OAAO;KACR,CAAC,CAAA;IAEF,OAAO,CACL,8BACG,aAAa,EAEb,SAAS,CAAC,CAAC,CAAC,CACX,KAAC,cAAc,cACb,KAAC,oBAAoB,IACnB,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,KAAK,YAEZ,iBACM,gBAAgB,EAAE,gBACV,OAAO,EACnB,SAAS,EAAE,WAAW,CACpB,mBAAmB,EACnB,kEAAkE,EAClE,SAAS,CACV,EACD,GAAG,EAAE,WAAW;wBAChB,uDAAuD;wBACvD,oDAAoD;wBACpD,uDAAuD;wBACvD,yDAAyD;wBACzD,6CAA6C;wBAC7C,IAAI,EAAC,QAAQ,EACb,KAAK,EAAE,cAAc,YAEpB,QAAQ,GACL,GACe,GACR,CAClB,CAAC,CAAC,CAAC,IAAI,IACP,CACJ,CAAA;AACH,CAAC,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Tooltip.d.ts","sourceRoot":"","sources":["../../src/Tooltip/Tooltip.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"Tooltip.d.ts","sourceRoot":"","sources":["../../src/Tooltip/Tooltip.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAcnD,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEpD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAIhD,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG;IACrC;;;;;;OAMG;IACH,QAAQ,EAAE,YAAY,CAAA;IACtB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,6CAA6C;IAC7C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;;OAKG;IACH,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,CAAC,EAAE,SAAS,CAAA;CACtB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AACH,eAAO,MAAM,OAAO,GAAI,wEAOrB,YAAY,KAAG,SAsFjB,CAAA"}
|
package/dist/Tooltip/Tooltip.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useClonedChild, useVisibility, } from "@charcuterie/logic";
|
|
3
|
-
import { autoUpdate, flip, offset, shift, useDismiss, useFloating, useFocus, useHover, useInteractions, useRole, } from "@floating-ui/react";
|
|
3
|
+
import { autoUpdate, FloatingPortal, flip, offset, shift, useDismiss, useFloating, useFocus, useHover, useInteractions, useRole, } from "@floating-ui/react";
|
|
4
4
|
import { mergeSlotProps } from "../slotProps.js";
|
|
5
5
|
import { toClassName } from "../toClassName.js";
|
|
6
6
|
/**
|
|
@@ -96,29 +96,29 @@ export const Tooltip = ({ children, className, delay = 200, label, placement = "
|
|
|
96
96
|
...getReferenceProps(),
|
|
97
97
|
ref: refs.setReference,
|
|
98
98
|
}));
|
|
99
|
-
return (_jsxs(_Fragment, { children: [clonedTrigger, isVisible ? (_jsx("div", { ...getFloatingProps(), className: toClassName(
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
99
|
+
return (_jsxs(_Fragment, { children: [clonedTrigger, isVisible ? (_jsx(FloatingPortal, { children: _jsx("div", { ...getFloatingProps(), className: toClassName(
|
|
100
|
+
// `intent-neutral-solid` + its own `on-solid`, rather
|
|
101
|
+
// than `surface-inverse` + `content-on-accent`. The
|
|
102
|
+
// second pair looks like the obvious one and is not a
|
|
103
|
+
// *pair* at all — nothing in the token set promises those
|
|
104
|
+
// two contrast, and in `daylight` dark they land at
|
|
105
|
+
// 2.9:1. The `solid`/`on-solid` roles exist precisely to
|
|
106
|
+
// be a guaranteed couple, and the contrast gate checks
|
|
107
|
+
// them as one.
|
|
108
|
+
"z-[var(--layer-tooltip)] max-w-xs rounded-md bg-intent-neutral-solid px-2 py-1 text-intent-neutral-on-solid text-xs shadow-medium", className),
|
|
109
|
+
// Portalled to `document.body`, the same reversal as the
|
|
110
|
+
// other overlays: `strategy: "fixed"` alone still let an
|
|
111
|
+
// `overflow: hidden` ancestor clip the tip, and a portal
|
|
112
|
+
// does not. No `FloatingFocusManager`, unlike `Popover` —
|
|
113
|
+
// focus never enters a tooltip, and moving it here is what
|
|
114
|
+
// would make one unescapable. `--layer-tooltip` keeps it
|
|
115
|
+
// above a modal.
|
|
116
|
+
ref: refs.setFloating,
|
|
117
|
+
// Duplicated from `getFloatingProps()` deliberately, and
|
|
118
|
+
// the two cannot differ — `useRole(context, { role:
|
|
119
|
+
// "tooltip" })` above is where it comes from. Stated here
|
|
120
|
+
// because a linter cannot see a role through a spread, and
|
|
121
|
+
// neither can the next reader.
|
|
122
|
+
role: "tooltip", style: floatingStyles, children: label }) })) : null] }));
|
|
123
123
|
};
|
|
124
124
|
//# sourceMappingURL=Tooltip.js.map
|