@particle-academy/react-fancy 6.10.0 → 6.11.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.
Files changed (52) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/{chunk-GEQKNV7E.js → chunk-26GRLVCQ.js} +17 -13
  3. package/dist/chunk-26GRLVCQ.js.map +1 -0
  4. package/dist/{chunk-HANQYZW2.cjs → chunk-GB2EGMVQ.cjs} +16 -12
  5. package/dist/chunk-GB2EGMVQ.cjs.map +1 -0
  6. package/dist/chunk-HHCVBIMM.js +21 -0
  7. package/dist/chunk-HHCVBIMM.js.map +1 -0
  8. package/dist/{chunk-ZBXJDQXN.js → chunk-JJYMRV62.js} +17 -13
  9. package/dist/chunk-JJYMRV62.js.map +1 -0
  10. package/dist/{chunk-R6O6SLXK.js → chunk-LG2O26CG.js} +13 -8
  11. package/dist/chunk-LG2O26CG.js.map +1 -0
  12. package/dist/{chunk-5U2DZ7AV.js → chunk-MUV7OK3W.js} +8 -6
  13. package/dist/chunk-MUV7OK3W.js.map +1 -0
  14. package/dist/{chunk-EC4ZYZNK.cjs → chunk-NFUKNVB7.cjs} +13 -8
  15. package/dist/chunk-NFUKNVB7.cjs.map +1 -0
  16. package/dist/chunk-RE3PMG47.cjs +23 -0
  17. package/dist/chunk-RE3PMG47.cjs.map +1 -0
  18. package/dist/{chunk-7NRG77U5.cjs → chunk-TXQT6S4D.cjs} +16 -12
  19. package/dist/chunk-TXQT6S4D.cjs.map +1 -0
  20. package/dist/{chunk-TXTRTTG7.cjs → chunk-UOG3O3KP.cjs} +8 -6
  21. package/dist/chunk-UOG3O3KP.cjs.map +1 -0
  22. package/dist/command.cjs +5 -4
  23. package/dist/command.js +3 -2
  24. package/dist/drawer.cjs +5 -4
  25. package/dist/drawer.d.cts +8 -1
  26. package/dist/drawer.d.ts +8 -1
  27. package/dist/drawer.js +3 -2
  28. package/dist/editor.cjs +1 -1
  29. package/dist/editor.js +1 -1
  30. package/dist/index.cjs +12 -11
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.d.cts +12 -1
  33. package/dist/index.d.ts +12 -1
  34. package/dist/index.js +5 -4
  35. package/dist/index.js.map +1 -1
  36. package/dist/mobile-menu.cjs +1 -1
  37. package/dist/mobile-menu.js +1 -1
  38. package/dist/modal.cjs +5 -4
  39. package/dist/modal.d.cts +11 -1
  40. package/dist/modal.d.ts +11 -1
  41. package/dist/modal.js +3 -2
  42. package/dist/sidebar.cjs +1 -1
  43. package/dist/sidebar.js +1 -1
  44. package/package.json +1 -1
  45. package/dist/chunk-5U2DZ7AV.js.map +0 -1
  46. package/dist/chunk-7NRG77U5.cjs.map +0 -1
  47. package/dist/chunk-EC4ZYZNK.cjs.map +0 -1
  48. package/dist/chunk-GEQKNV7E.js.map +0 -1
  49. package/dist/chunk-HANQYZW2.cjs.map +0 -1
  50. package/dist/chunk-R6O6SLXK.js.map +0 -1
  51. package/dist/chunk-TXTRTTG7.cjs.map +0 -1
  52. package/dist/chunk-ZBXJDQXN.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -11,6 +11,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
11
11
 
12
12
  ## [Unreleased]
13
13
 
14
+ ## [6.11.0] - 2026-11-04
15
+
16
+ ### Added
17
+
18
+ - **`Modal.Header` and `Drawer.Header` take `closeLabel`**, the close button's accessible name, so it can be translated. Default `"Close"`, as before. **Consumer action:** none; pass your locale's word if you localise.
19
+ - **`useFocusTrap` takes an optional third argument, `restoreTo`**, a ref to the element focus returns to on disable. Without it the hook behaves as before. **Consumer action:** none.
20
+
21
+ ### Fixed
22
+
23
+ - **`Modal`, `Drawer` and `Command` now trap focus.** The trap ran in the commit where `open` flipped, but the panel exists two renders later (`useAnimation` mounts it, then `Portal` renders it). It found no element, returned, and never ran again. So focus never moved into the dialog, Tab and Shift+Tab walked out of it, and closing never returned focus to the trigger. This affected every one of them, opened from a trigger or rendered open, with or without motion. The trap now runs once the panel is mounted. The element to return to is recorded when `open` flips, so a dialog whose content autofocuses (Command's input) still returns focus to the trigger. A container-attached `Drawer` still does not trap. Verified in headless Chromium, with and without `prefers-reduced-motion`. **Consumer action:** none. If you worked around it by focusing inside the dialog yourself, that still works.
24
+ - **The focus trap ignores elements with `tabindex="-1"`.** Its selector matched Command's options, which are `tabindex="-1"`, and took one for the last tab stop. Focus was never on it, so Tab never wrapped and left the palette. **Consumer action:** none.
25
+ - **The `Modal.Header` and `Drawer.Header` close buttons are a 44px target** (WCAG 2.5.5, AAA). They were 28px and 26px. A `::before` extends the hit area. The button, the icon, the hover fill and the header's height are unchanged. **Consumer action:** none.
26
+
14
27
  ## [6.10.0] - 2026-11-04
15
28
 
16
29
  ### Added
@@ -1,9 +1,9 @@
1
- import { useFocusTrap } from './chunk-5U2DZ7AV.js';
1
+ import { useDialogFocusTrap } from './chunk-HHCVBIMM.js';
2
2
  import { useAnimation } from './chunk-3K2CEAI7.js';
3
3
  import { Portal } from './chunk-RBCMKPBX.js';
4
4
  import { useEscapeKey } from './chunk-YX6FTJ6N.js';
5
5
  import { cn } from './chunk-MMW2JGKZ.js';
6
- import { createContext, useRef, useCallback, useEffect, useMemo, useContext } from 'react';
6
+ import { createContext, useCallback, useEffect, useMemo, useContext } from 'react';
7
7
  import { X } from 'lucide-react';
8
8
  import { jsx, jsxs } from 'react/jsx-runtime';
9
9
 
@@ -15,7 +15,7 @@ function useModal() {
15
15
  }
16
16
  return ctx;
17
17
  }
18
- function ModalHeader({ children, className }) {
18
+ function ModalHeader({ children, className, closeLabel = "Close" }) {
19
19
  const { close } = useModal();
20
20
  return /* @__PURE__ */ jsxs(
21
21
  "div",
@@ -32,8 +32,9 @@ function ModalHeader({ children, className }) {
32
32
  {
33
33
  type: "button",
34
34
  onClick: close,
35
- className: "rounded-lg p-1 text-zinc-400 transition-colors hover:bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] hover:text-zinc-600 dark:hover:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))] dark:hover:text-zinc-300",
36
- "aria-label": "Close",
35
+ "data-react-fancy-modal-close": "",
36
+ className: "relative rounded-lg p-1 text-zinc-400 before:absolute before:-inset-2 before:content-[''] transition-colors hover:bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] hover:text-zinc-600 dark:hover:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))] dark:hover:text-zinc-300",
37
+ "aria-label": closeLabel,
37
38
  children: /* @__PURE__ */ jsx(X, { size: 20 })
38
39
  }
39
40
  )
@@ -83,10 +84,8 @@ function ModalRoot({
83
84
  className,
84
85
  ...rest
85
86
  }) {
86
- const panelRef = useRef(null);
87
87
  const close = useCallback(() => onClose(), [onClose]);
88
88
  useEscapeKey(close, open);
89
- useFocusTrap(panelRef, open);
90
89
  useEffect(() => {
91
90
  if (open) {
92
91
  const original = document.body.style.overflow;
@@ -101,6 +100,14 @@ function ModalRoot({
101
100
  enterClass: "fancy-slide-up",
102
101
  exitClass: "fancy-fade-out"
103
102
  });
103
+ const attachTrap = useDialogFocusTrap(open);
104
+ const attachPanel = useCallback(
105
+ (node) => {
106
+ animRef.current = node;
107
+ attachTrap(node);
108
+ },
109
+ [animRef, attachTrap]
110
+ );
104
111
  const ctx = useMemo(() => ({ open, close }), [open, close]);
105
112
  if (!mounted) return null;
106
113
  return /* @__PURE__ */ jsx(ModalContext.Provider, { value: ctx, children: /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center p-4", children: [
@@ -115,10 +122,7 @@ function ModalRoot({
115
122
  /* @__PURE__ */ jsx(
116
123
  "div",
117
124
  {
118
- ref: (node) => {
119
- panelRef.current = node;
120
- animRef.current = node;
121
- },
125
+ ref: attachPanel,
122
126
  "data-react-fancy-modal": "",
123
127
  role: "dialog",
124
128
  "aria-modal": "true",
@@ -141,5 +145,5 @@ var Modal = Object.assign(ModalRoot, {
141
145
  });
142
146
 
143
147
  export { Modal, useModal };
144
- //# sourceMappingURL=chunk-GEQKNV7E.js.map
145
- //# sourceMappingURL=chunk-GEQKNV7E.js.map
148
+ //# sourceMappingURL=chunk-26GRLVCQ.js.map
149
+ //# sourceMappingURL=chunk-26GRLVCQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/Modal/Modal.context.ts","../src/components/Modal/ModalHeader.tsx","../src/components/Modal/ModalBody.tsx","../src/components/Modal/ModalFooter.tsx","../src/components/Modal/Modal.tsx"],"names":["jsx","jsxs"],"mappings":"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","file":"chunk-26GRLVCQ.js","sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { ModalContextValue } from \"./Modal.types\";\n\nexport const ModalContext = createContext<ModalContextValue | null>(null);\n\nexport function useModal(): ModalContextValue {\n const ctx = useContext(ModalContext);\n if (!ctx) {\n throw new Error(\"Modal compound components must be used within <Modal>\");\n }\n return ctx;\n}\n","import { X } from \"lucide-react\";\nimport { cn } from \"../../utils/cn\";\nimport { useModal } from \"./Modal.context\";\nimport type { ModalHeaderProps } from \"./Modal.types\";\n\n/**\n * The close button draws at 28px (20px icon + `p-1`) and HITS at 44px, the\n * WCAG 2.5.5 (AAA) target: a `::before` overhangs it by 8px on every side. A\n * pseudo-element is part of its button for hit-testing, so the look, the hover\n * fill and the header's height are all unchanged — the header's `py-4` already\n * has room for the overhang. A size option was the alternative; it would have\n * left every existing Modal at 28px unless someone opted in.\n */\nexport function ModalHeader({ children, className, closeLabel = \"Close\" }: ModalHeaderProps) {\n const { close } = useModal();\n\n return (\n <div\n data-react-fancy-modal-header=\"\"\n className={cn(\n \"flex items-center justify-between border-b border-[color:var(--fancy-border,var(--color-zinc-200))] px-6 py-4 dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))]\",\n className,\n )}\n >\n <div className=\"text-lg font-semibold text-[color:var(--fancy-text,var(--color-zinc-900))] dark:text-[color:var(--fancy-text-dark,var(--color-zinc-100))]\">{children}</div>\n <button\n type=\"button\"\n onClick={close}\n data-react-fancy-modal-close=\"\"\n className=\"relative rounded-lg p-1 text-zinc-400 before:absolute before:-inset-2 before:content-[''] transition-colors hover:bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] hover:text-zinc-600 dark:hover:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))] dark:hover:text-zinc-300\"\n aria-label={closeLabel}\n >\n <X size={20} />\n </button>\n </div>\n );\n}\n\nModalHeader.displayName = \"ModalHeader\";\n","import { cn } from \"../../utils/cn\";\nimport type { ModalBodyProps } from \"./Modal.types\";\n\nexport function ModalBody({ children, className }: ModalBodyProps) {\n return (\n // `min-h-0` is load-bearing, not decoration. A flex child defaults to\n // `min-height: auto`, which refuses to shrink below its content — so\n // `flex-1` alone would push the panel through its own max-height and the\n // scrollbar would never appear. This is what permits the overflow that\n // `overflow-y-auto` then handles.\n <div data-react-fancy-modal-body=\"\" className={cn(\"min-h-0 flex-1 overflow-y-auto px-6 py-4 text-[color:var(--fancy-text-soft,var(--color-zinc-700))] dark:text-[color:var(--fancy-text-soft-dark,var(--color-zinc-300))]\", className)}>\n {children}\n </div>\n );\n}\n\nModalBody.displayName = \"ModalBody\";\n","import { cn } from \"../../utils/cn\";\nimport type { ModalFooterProps } from \"./Modal.types\";\n\nexport function ModalFooter({ children, className }: ModalFooterProps) {\n return (\n <div\n data-react-fancy-modal-footer=\"\"\n className={cn(\n \"flex items-center justify-end gap-3 border-t border-[color:var(--fancy-border,var(--color-zinc-200))] px-6 py-4 dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))]\",\n className,\n )}\n >\n {children}\n </div>\n );\n}\n\nModalFooter.displayName = \"ModalFooter\";\n","import { useCallback, useEffect, useMemo } from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Portal } from \"../Portal\";\nimport { ModalContext } from \"./Modal.context\";\nimport { ModalHeader } from \"./ModalHeader\";\nimport { ModalBody } from \"./ModalBody\";\nimport { ModalFooter } from \"./ModalFooter\";\nimport { useDialogFocusTrap } from \"../../hooks/use-dialog-focus-trap\";\nimport { useEscapeKey } from \"../../hooks/use-escape-key\";\nimport { useAnimation } from \"../../hooks/use-animation\";\nimport type { ModalProps } from \"./Modal.types\";\n\n/**\n * Every size is capped against the VIEWPORT, not just the width.\n *\n * Only `full` used to carry a max-height. The other four were width-constrained\n * and vertically unbounded, so a modal with long content simply grew past the\n * bottom of the screen and the rest became unreachable — no scrollbar, because\n * there was no overflow to scroll: the box got taller instead.\n *\n * `ModalBody` already had `flex-1 overflow-y-auto` and the panel already had\n * `overflow-hidden`. The scrolling machinery was complete and inert, waiting on\n * the one constraint that makes it engage.\n *\n * `dvh` over `vh` deliberately: on mobile browsers `100vh` is the tallest the\n * viewport ever gets, ignoring the address bar, so a `vh`-capped modal still\n * runs under the chrome on exactly the devices with least room. `dvh` tracks\n * the visible height. The `vh` fallback is listed first for any engine that\n * does not know `dvh` yet — an unknown unit drops the declaration, so ordering\n * is what keeps the old behaviour rather than none.\n */\nconst VIEWPORT_CAP = \"max-h-[calc(100vh-2rem)] max-h-[calc(100dvh-2rem)]\";\n\nconst SIZE_MAP = {\n sm: `max-w-sm ${VIEWPORT_CAP}`,\n md: `max-w-lg ${VIEWPORT_CAP}`,\n lg: `max-w-2xl ${VIEWPORT_CAP}`,\n xl: `max-w-4xl ${VIEWPORT_CAP}`,\n full: `max-w-[calc(100vw-2rem)] ${VIEWPORT_CAP}`,\n} as const;\n\nfunction ModalRoot({\n children,\n open,\n onClose,\n size = \"md\",\n className,\n ...rest\n}: ModalProps) {\n const close = useCallback(() => onClose(), [onClose]);\n useEscapeKey(close, open);\n\n // Body scroll lock\n useEffect(() => {\n if (open) {\n const original = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n return () => {\n document.body.style.overflow = original;\n };\n }\n }, [open]);\n\n const { mounted, className: animClass, ref: animRef } = useAnimation({\n open,\n enterClass: \"fancy-slide-up\",\n exitClass: \"fancy-fade-out\",\n });\n\n // The panel mounts renders after `open` flips; see useDialogFocusTrap.\n const attachTrap = useDialogFocusTrap(open);\n const attachPanel = useCallback(\n (node: HTMLDivElement | null) => {\n (animRef as React.MutableRefObject<HTMLElement | null>).current = node;\n attachTrap(node);\n },\n [animRef, attachTrap],\n );\n\n const ctx = useMemo(() => ({ open, close }), [open, close]);\n\n if (!mounted) return null;\n\n return (\n <ModalContext.Provider value={ctx}>\n <Portal>\n <div className=\"fixed inset-0 z-50 flex items-center justify-center p-4\">\n {/* Backdrop */}\n <div\n className=\"absolute inset-0 bg-black/50 backdrop-blur-sm\"\n onClick={close}\n aria-hidden=\"true\"\n />\n {/* Panel */}\n <div\n ref={attachPanel}\n data-react-fancy-modal=\"\"\n role=\"dialog\"\n aria-modal=\"true\"\n {...rest}\n className={cn(\n \"relative flex w-full flex-col overflow-hidden rounded-2xl border border-[color:var(--fancy-border,var(--color-zinc-200))] bg-[color:var(--fancy-surface,var(--color-white))] shadow-xl dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))] dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))]\",\n SIZE_MAP[size],\n animClass,\n className,\n )}\n >\n {children}\n </div>\n </div>\n </Portal>\n </ModalContext.Provider>\n );\n}\n\nexport const Modal = Object.assign(ModalRoot, {\n Header: ModalHeader,\n Body: ModalBody,\n Footer: ModalFooter,\n});\n"]}
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkTXTRTTG7_cjs = require('./chunk-TXTRTTG7.cjs');
3
+ var chunkRE3PMG47_cjs = require('./chunk-RE3PMG47.cjs');
4
4
  var chunkFQZE5N2N_cjs = require('./chunk-FQZE5N2N.cjs');
5
5
  var chunkTI7D4QDM_cjs = require('./chunk-TI7D4QDM.cjs');
6
6
  var chunkOUYC6EIL_cjs = require('./chunk-OUYC6EIL.cjs');
@@ -17,7 +17,7 @@ function useModal() {
17
17
  }
18
18
  return ctx;
19
19
  }
20
- function ModalHeader({ children, className }) {
20
+ function ModalHeader({ children, className, closeLabel = "Close" }) {
21
21
  const { close } = useModal();
22
22
  return /* @__PURE__ */ jsxRuntime.jsxs(
23
23
  "div",
@@ -34,8 +34,9 @@ function ModalHeader({ children, className }) {
34
34
  {
35
35
  type: "button",
36
36
  onClick: close,
37
- className: "rounded-lg p-1 text-zinc-400 transition-colors hover:bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] hover:text-zinc-600 dark:hover:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))] dark:hover:text-zinc-300",
38
- "aria-label": "Close",
37
+ "data-react-fancy-modal-close": "",
38
+ className: "relative rounded-lg p-1 text-zinc-400 before:absolute before:-inset-2 before:content-[''] transition-colors hover:bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] hover:text-zinc-600 dark:hover:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))] dark:hover:text-zinc-300",
39
+ "aria-label": closeLabel,
39
40
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 20 })
40
41
  }
41
42
  )
@@ -85,10 +86,8 @@ function ModalRoot({
85
86
  className,
86
87
  ...rest
87
88
  }) {
88
- const panelRef = react.useRef(null);
89
89
  const close = react.useCallback(() => onClose(), [onClose]);
90
90
  chunkOUYC6EIL_cjs.useEscapeKey(close, open);
91
- chunkTXTRTTG7_cjs.useFocusTrap(panelRef, open);
92
91
  react.useEffect(() => {
93
92
  if (open) {
94
93
  const original = document.body.style.overflow;
@@ -103,6 +102,14 @@ function ModalRoot({
103
102
  enterClass: "fancy-slide-up",
104
103
  exitClass: "fancy-fade-out"
105
104
  });
105
+ const attachTrap = chunkRE3PMG47_cjs.useDialogFocusTrap(open);
106
+ const attachPanel = react.useCallback(
107
+ (node) => {
108
+ animRef.current = node;
109
+ attachTrap(node);
110
+ },
111
+ [animRef, attachTrap]
112
+ );
106
113
  const ctx = react.useMemo(() => ({ open, close }), [open, close]);
107
114
  if (!mounted) return null;
108
115
  return /* @__PURE__ */ jsxRuntime.jsx(ModalContext.Provider, { value: ctx, children: /* @__PURE__ */ jsxRuntime.jsx(chunkTI7D4QDM_cjs.Portal, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center p-4", children: [
@@ -117,10 +124,7 @@ function ModalRoot({
117
124
  /* @__PURE__ */ jsxRuntime.jsx(
118
125
  "div",
119
126
  {
120
- ref: (node) => {
121
- panelRef.current = node;
122
- animRef.current = node;
123
- },
127
+ ref: attachPanel,
124
128
  "data-react-fancy-modal": "",
125
129
  role: "dialog",
126
130
  "aria-modal": "true",
@@ -144,5 +148,5 @@ var Modal = Object.assign(ModalRoot, {
144
148
 
145
149
  exports.Modal = Modal;
146
150
  exports.useModal = useModal;
147
- //# sourceMappingURL=chunk-HANQYZW2.cjs.map
148
- //# sourceMappingURL=chunk-HANQYZW2.cjs.map
151
+ //# sourceMappingURL=chunk-GB2EGMVQ.cjs.map
152
+ //# sourceMappingURL=chunk-GB2EGMVQ.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/Modal/Modal.context.ts","../src/components/Modal/ModalHeader.tsx","../src/components/Modal/ModalBody.tsx","../src/components/Modal/ModalFooter.tsx","../src/components/Modal/Modal.tsx"],"names":["createContext","useContext","jsxs","cn","jsx","X","useCallback","useEscapeKey","useEffect","useAnimation","useDialogFocusTrap","useMemo","Portal"],"mappings":";;;;;;;;;;;AAGO,IAAM,YAAA,GAAeA,oBAAwC,IAAI,CAAA;AAEjE,SAAS,QAAA,GAA8B;AAC5C,EAAA,MAAM,GAAA,GAAMC,iBAAW,YAAY,CAAA;AACnC,EAAA,IAAI,CAAC,GAAA,EAAK;AACR,IAAA,MAAM,IAAI,MAAM,uDAAuD,CAAA;AAAA,EACzE;AACA,EAAA,OAAO,GAAA;AACT;ACEO,SAAS,YAAY,EAAE,QAAA,EAAU,SAAA,EAAW,UAAA,GAAa,SAAQ,EAAqB;AAC3F,EAAA,MAAM,EAAE,KAAA,EAAM,GAAI,QAAA,EAAS;AAE3B,EAAA,uBACEC,eAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,+BAAA,EAA8B,EAAA;AAAA,MAC9B,SAAA,EAAWC,oBAAA;AAAA,QACT,kLAAA;AAAA,QACA;AAAA,OACF;AAAA,MAEA,QAAA,EAAA;AAAA,wBAAAC,cAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,2IAAA,EAA6I,QAAA,EAAS,CAAA;AAAA,wBACrKA,cAAA;AAAA,UAAC,QAAA;AAAA,UAAA;AAAA,YACC,IAAA,EAAK,QAAA;AAAA,YACL,OAAA,EAAS,KAAA;AAAA,YACT,8BAAA,EAA6B,EAAA;AAAA,YAC7B,SAAA,EAAU,wSAAA;AAAA,YACV,YAAA,EAAY,UAAA;AAAA,YAEZ,QAAA,kBAAAA,cAAA,CAACC,aAAA,EAAA,EAAE,IAAA,EAAM,EAAA,EAAI;AAAA;AAAA;AACf;AAAA;AAAA,GACF;AAEJ;AAEA,WAAA,CAAY,WAAA,GAAc,aAAA;ACnCnB,SAAS,SAAA,CAAU,EAAE,QAAA,EAAU,SAAA,EAAU,EAAmB;AACjE,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAMED,cAAAA,CAAC,KAAA,EAAA,EAAI,6BAAA,EAA4B,EAAA,EAAG,WAAWD,oBAAA,CAAG,wKAAA,EAA0K,SAAS,CAAA,EAClO,QAAA,EACH;AAAA;AAEJ;AAEA,SAAA,CAAU,WAAA,GAAc,WAAA;ACbjB,SAAS,WAAA,CAAY,EAAE,QAAA,EAAU,SAAA,EAAU,EAAqB;AACrE,EAAA,uBACEC,cAAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,+BAAA,EAA8B,EAAA;AAAA,MAC9B,SAAA,EAAWD,oBAAA;AAAA,QACT,oLAAA;AAAA,QACA;AAAA,OACF;AAAA,MAEC;AAAA;AAAA,GACH;AAEJ;AAEA,WAAA,CAAY,WAAA,GAAc,aAAA;ACc1B,IAAM,YAAA,GAAe,oDAAA;AAErB,IAAM,QAAA,GAAW;AAAA,EACf,EAAA,EAAI,YAAY,YAAY,CAAA,CAAA;AAAA,EAC5B,EAAA,EAAI,YAAY,YAAY,CAAA,CAAA;AAAA,EAC5B,EAAA,EAAI,aAAa,YAAY,CAAA,CAAA;AAAA,EAC7B,EAAA,EAAI,aAAa,YAAY,CAAA,CAAA;AAAA,EAC7B,IAAA,EAAM,4BAA4B,YAAY,CAAA;AAChD,CAAA;AAEA,SAAS,SAAA,CAAU;AAAA,EACjB,QAAA;AAAA,EACA,IAAA;AAAA,EACA,OAAA;AAAA,EACA,IAAA,GAAO,IAAA;AAAA,EACP,SAAA;AAAA,EACA,GAAG;AACL,CAAA,EAAe;AACb,EAAA,MAAM,QAAQG,iBAAA,CAAY,MAAM,SAAQ,EAAG,CAAC,OAAO,CAAC,CAAA;AACpD,EAAAC,8BAAA,CAAa,OAAO,IAAI,CAAA;AAGxB,EAAAC,eAAA,CAAU,MAAM;AACd,IAAA,IAAI,IAAA,EAAM;AACR,MAAA,MAAM,QAAA,GAAW,QAAA,CAAS,IAAA,CAAK,KAAA,CAAM,QAAA;AACrC,MAAA,QAAA,CAAS,IAAA,CAAK,MAAM,QAAA,GAAW,QAAA;AAC/B,MAAA,OAAO,MAAM;AACX,QAAA,QAAA,CAAS,IAAA,CAAK,MAAM,QAAA,GAAW,QAAA;AAAA,MACjC,CAAA;AAAA,IACF;AAAA,EACF,CAAA,EAAG,CAAC,IAAI,CAAC,CAAA;AAET,EAAA,MAAM,EAAE,OAAA,EAAS,SAAA,EAAW,WAAW,GAAA,EAAK,OAAA,KAAYC,8BAAA,CAAa;AAAA,IACnE,IAAA;AAAA,IACA,UAAA,EAAY,gBAAA;AAAA,IACZ,SAAA,EAAW;AAAA,GACZ,CAAA;AAGD,EAAA,MAAM,UAAA,GAAaC,qCAAmB,IAAI,CAAA;AAC1C,EAAA,MAAM,WAAA,GAAcJ,iBAAA;AAAA,IAClB,CAAC,IAAA,KAAgC;AAC/B,MAAC,QAAuD,OAAA,GAAU,IAAA;AAClE,MAAA,UAAA,CAAW,IAAI,CAAA;AAAA,IACjB,CAAA;AAAA,IACA,CAAC,SAAS,UAAU;AAAA,GACtB;AAEA,EAAA,MAAM,GAAA,GAAMK,aAAA,CAAQ,OAAO,EAAE,IAAA,EAAM,OAAM,CAAA,EAAI,CAAC,IAAA,EAAM,KAAK,CAAC,CAAA;AAE1D,EAAA,IAAI,CAAC,SAAS,OAAO,IAAA;AAErB,EAAA,uBACEP,cAAAA,CAAC,YAAA,CAAa,QAAA,EAAb,EAAsB,KAAA,EAAO,GAAA,EAC5B,QAAA,kBAAAA,cAAAA,CAACQ,wBAAA,EAAA,EACC,QAAA,kBAAAV,eAAAA,CAAC,KAAA,EAAA,EAAI,WAAU,yDAAA,EAEb,QAAA,EAAA;AAAA,oBAAAE,cAAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,SAAA,EAAU,+CAAA;AAAA,QACV,OAAA,EAAS,KAAA;AAAA,QACT,aAAA,EAAY;AAAA;AAAA,KACd;AAAA,oBAEAA,cAAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,GAAA,EAAK,WAAA;AAAA,QACL,wBAAA,EAAuB,EAAA;AAAA,QACvB,IAAA,EAAK,QAAA;AAAA,QACL,YAAA,EAAW,MAAA;AAAA,QACV,GAAG,IAAA;AAAA,QACJ,SAAA,EAAWD,oBAAA;AAAA,UACT,2TAAA;AAAA,UACA,SAAS,IAAI,CAAA;AAAA,UACb,SAAA;AAAA,UACA;AAAA,SACF;AAAA,QAEC;AAAA;AAAA;AACH,GAAA,EACF,GACF,CAAA,EACF,CAAA;AAEJ;AAEO,IAAM,KAAA,GAAQ,MAAA,CAAO,MAAA,CAAO,SAAA,EAAW;AAAA,EAC5C,MAAA,EAAQ,WAAA;AAAA,EACR,IAAA,EAAM,SAAA;AAAA,EACN,MAAA,EAAQ;AACV,CAAC","file":"chunk-GB2EGMVQ.cjs","sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { ModalContextValue } from \"./Modal.types\";\n\nexport const ModalContext = createContext<ModalContextValue | null>(null);\n\nexport function useModal(): ModalContextValue {\n const ctx = useContext(ModalContext);\n if (!ctx) {\n throw new Error(\"Modal compound components must be used within <Modal>\");\n }\n return ctx;\n}\n","import { X } from \"lucide-react\";\nimport { cn } from \"../../utils/cn\";\nimport { useModal } from \"./Modal.context\";\nimport type { ModalHeaderProps } from \"./Modal.types\";\n\n/**\n * The close button draws at 28px (20px icon + `p-1`) and HITS at 44px, the\n * WCAG 2.5.5 (AAA) target: a `::before` overhangs it by 8px on every side. A\n * pseudo-element is part of its button for hit-testing, so the look, the hover\n * fill and the header's height are all unchanged — the header's `py-4` already\n * has room for the overhang. A size option was the alternative; it would have\n * left every existing Modal at 28px unless someone opted in.\n */\nexport function ModalHeader({ children, className, closeLabel = \"Close\" }: ModalHeaderProps) {\n const { close } = useModal();\n\n return (\n <div\n data-react-fancy-modal-header=\"\"\n className={cn(\n \"flex items-center justify-between border-b border-[color:var(--fancy-border,var(--color-zinc-200))] px-6 py-4 dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))]\",\n className,\n )}\n >\n <div className=\"text-lg font-semibold text-[color:var(--fancy-text,var(--color-zinc-900))] dark:text-[color:var(--fancy-text-dark,var(--color-zinc-100))]\">{children}</div>\n <button\n type=\"button\"\n onClick={close}\n data-react-fancy-modal-close=\"\"\n className=\"relative rounded-lg p-1 text-zinc-400 before:absolute before:-inset-2 before:content-[''] transition-colors hover:bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] hover:text-zinc-600 dark:hover:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))] dark:hover:text-zinc-300\"\n aria-label={closeLabel}\n >\n <X size={20} />\n </button>\n </div>\n );\n}\n\nModalHeader.displayName = \"ModalHeader\";\n","import { cn } from \"../../utils/cn\";\nimport type { ModalBodyProps } from \"./Modal.types\";\n\nexport function ModalBody({ children, className }: ModalBodyProps) {\n return (\n // `min-h-0` is load-bearing, not decoration. A flex child defaults to\n // `min-height: auto`, which refuses to shrink below its content — so\n // `flex-1` alone would push the panel through its own max-height and the\n // scrollbar would never appear. This is what permits the overflow that\n // `overflow-y-auto` then handles.\n <div data-react-fancy-modal-body=\"\" className={cn(\"min-h-0 flex-1 overflow-y-auto px-6 py-4 text-[color:var(--fancy-text-soft,var(--color-zinc-700))] dark:text-[color:var(--fancy-text-soft-dark,var(--color-zinc-300))]\", className)}>\n {children}\n </div>\n );\n}\n\nModalBody.displayName = \"ModalBody\";\n","import { cn } from \"../../utils/cn\";\nimport type { ModalFooterProps } from \"./Modal.types\";\n\nexport function ModalFooter({ children, className }: ModalFooterProps) {\n return (\n <div\n data-react-fancy-modal-footer=\"\"\n className={cn(\n \"flex items-center justify-end gap-3 border-t border-[color:var(--fancy-border,var(--color-zinc-200))] px-6 py-4 dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))]\",\n className,\n )}\n >\n {children}\n </div>\n );\n}\n\nModalFooter.displayName = \"ModalFooter\";\n","import { useCallback, useEffect, useMemo } from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Portal } from \"../Portal\";\nimport { ModalContext } from \"./Modal.context\";\nimport { ModalHeader } from \"./ModalHeader\";\nimport { ModalBody } from \"./ModalBody\";\nimport { ModalFooter } from \"./ModalFooter\";\nimport { useDialogFocusTrap } from \"../../hooks/use-dialog-focus-trap\";\nimport { useEscapeKey } from \"../../hooks/use-escape-key\";\nimport { useAnimation } from \"../../hooks/use-animation\";\nimport type { ModalProps } from \"./Modal.types\";\n\n/**\n * Every size is capped against the VIEWPORT, not just the width.\n *\n * Only `full` used to carry a max-height. The other four were width-constrained\n * and vertically unbounded, so a modal with long content simply grew past the\n * bottom of the screen and the rest became unreachable — no scrollbar, because\n * there was no overflow to scroll: the box got taller instead.\n *\n * `ModalBody` already had `flex-1 overflow-y-auto` and the panel already had\n * `overflow-hidden`. The scrolling machinery was complete and inert, waiting on\n * the one constraint that makes it engage.\n *\n * `dvh` over `vh` deliberately: on mobile browsers `100vh` is the tallest the\n * viewport ever gets, ignoring the address bar, so a `vh`-capped modal still\n * runs under the chrome on exactly the devices with least room. `dvh` tracks\n * the visible height. The `vh` fallback is listed first for any engine that\n * does not know `dvh` yet — an unknown unit drops the declaration, so ordering\n * is what keeps the old behaviour rather than none.\n */\nconst VIEWPORT_CAP = \"max-h-[calc(100vh-2rem)] max-h-[calc(100dvh-2rem)]\";\n\nconst SIZE_MAP = {\n sm: `max-w-sm ${VIEWPORT_CAP}`,\n md: `max-w-lg ${VIEWPORT_CAP}`,\n lg: `max-w-2xl ${VIEWPORT_CAP}`,\n xl: `max-w-4xl ${VIEWPORT_CAP}`,\n full: `max-w-[calc(100vw-2rem)] ${VIEWPORT_CAP}`,\n} as const;\n\nfunction ModalRoot({\n children,\n open,\n onClose,\n size = \"md\",\n className,\n ...rest\n}: ModalProps) {\n const close = useCallback(() => onClose(), [onClose]);\n useEscapeKey(close, open);\n\n // Body scroll lock\n useEffect(() => {\n if (open) {\n const original = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n return () => {\n document.body.style.overflow = original;\n };\n }\n }, [open]);\n\n const { mounted, className: animClass, ref: animRef } = useAnimation({\n open,\n enterClass: \"fancy-slide-up\",\n exitClass: \"fancy-fade-out\",\n });\n\n // The panel mounts renders after `open` flips; see useDialogFocusTrap.\n const attachTrap = useDialogFocusTrap(open);\n const attachPanel = useCallback(\n (node: HTMLDivElement | null) => {\n (animRef as React.MutableRefObject<HTMLElement | null>).current = node;\n attachTrap(node);\n },\n [animRef, attachTrap],\n );\n\n const ctx = useMemo(() => ({ open, close }), [open, close]);\n\n if (!mounted) return null;\n\n return (\n <ModalContext.Provider value={ctx}>\n <Portal>\n <div className=\"fixed inset-0 z-50 flex items-center justify-center p-4\">\n {/* Backdrop */}\n <div\n className=\"absolute inset-0 bg-black/50 backdrop-blur-sm\"\n onClick={close}\n aria-hidden=\"true\"\n />\n {/* Panel */}\n <div\n ref={attachPanel}\n data-react-fancy-modal=\"\"\n role=\"dialog\"\n aria-modal=\"true\"\n {...rest}\n className={cn(\n \"relative flex w-full flex-col overflow-hidden rounded-2xl border border-[color:var(--fancy-border,var(--color-zinc-200))] bg-[color:var(--fancy-surface,var(--color-white))] shadow-xl dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))] dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))]\",\n SIZE_MAP[size],\n animClass,\n className,\n )}\n >\n {children}\n </div>\n </div>\n </Portal>\n </ModalContext.Provider>\n );\n}\n\nexport const Modal = Object.assign(ModalRoot, {\n Header: ModalHeader,\n Body: ModalBody,\n Footer: ModalFooter,\n});\n"]}
@@ -0,0 +1,21 @@
1
+ import { useFocusTrap } from './chunk-MUV7OK3W.js';
2
+ import { useRef, useState, useLayoutEffect, useCallback } from 'react';
3
+
4
+ function useDialogFocusTrap(open, enabled = true) {
5
+ const ref = useRef(null);
6
+ const opener = useRef(null);
7
+ const [node, setNode] = useState(null);
8
+ const active = open && enabled;
9
+ useLayoutEffect(() => {
10
+ if (active) opener.current = document.activeElement;
11
+ }, [active]);
12
+ useFocusTrap(ref, active && node !== null, opener);
13
+ return useCallback((el) => {
14
+ ref.current = el;
15
+ setNode(el);
16
+ }, []);
17
+ }
18
+
19
+ export { useDialogFocusTrap };
20
+ //# sourceMappingURL=chunk-HHCVBIMM.js.map
21
+ //# sourceMappingURL=chunk-HHCVBIMM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/hooks/use-dialog-focus-trap.ts"],"names":[],"mappings":";;;AAoBO,SAAS,kBAAA,CACd,IAAA,EACA,OAAA,GAAmB,IAAA,EACiB;AACpC,EAAA,MAAM,GAAA,GAAM,OAA2B,IAAI,CAAA;AAC3C,EAAA,MAAM,MAAA,GAAS,OAA2B,IAAI,CAAA;AAC9C,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAI,SAA6B,IAAI,CAAA;AACzD,EAAA,MAAM,SAAS,IAAA,IAAQ,OAAA;AAEvB,EAAA,eAAA,CAAgB,MAAM;AACpB,IAAA,IAAI,MAAA,EAAQ,MAAA,CAAO,OAAA,GAAU,QAAA,CAAS,aAAA;AAAA,EACxC,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAEX,EAAA,YAAA,CAAa,GAAA,EAAK,MAAA,IAAU,IAAA,KAAS,IAAA,EAAM,MAAM,CAAA;AAEjD,EAAA,OAAO,WAAA,CAAY,CAAC,EAAA,KAA2B;AAC7C,IAAA,GAAA,CAAI,OAAA,GAAU,EAAA;AACd,IAAA,OAAA,CAAQ,EAAE,CAAA;AAAA,EACZ,CAAA,EAAG,EAAE,CAAA;AACP","file":"chunk-HHCVBIMM.js","sourcesContent":["import { useCallback, useLayoutEffect, useRef, useState } from \"react\";\nimport { useFocusTrap } from \"./use-focus-trap\";\n\n/**\n * The focus trap for a dialog whose panel mounts LATER than `open` flips —\n * Modal, Drawer and Command, all of which render through `useAnimation` (the\n * panel mounts a render after `open`) and `Portal` (its children a render after\n * that). Returns a callback ref for the panel.\n *\n * Up to 6.10.0 these called `useFocusTrap(ref, open)` directly. Its effect ran in\n * the commit where `open` flipped, found `ref.current === null`, returned, and\n * never ran again because its deps had not changed — so focus never moved in,\n * Tab walked out of the dialog, and close restored nothing. Keying the trap on\n * the panel NODE makes it run in the commit the panel actually appears, with or\n * without an animation.\n *\n * The opener is captured when `open` flips, before the panel exists. Captured at\n * trap time instead it would be whatever the panel autofocused (Command's\n * input), and close would \"restore\" focus into the dialog that is going away.\n */\nexport function useDialogFocusTrap(\n open: boolean,\n enabled: boolean = true,\n): (node: HTMLElement | null) => void {\n const ref = useRef<HTMLElement | null>(null);\n const opener = useRef<HTMLElement | null>(null);\n const [node, setNode] = useState<HTMLElement | null>(null);\n const active = open && enabled;\n\n useLayoutEffect(() => {\n if (active) opener.current = document.activeElement as HTMLElement | null;\n }, [active]);\n\n useFocusTrap(ref, active && node !== null, opener);\n\n return useCallback((el: HTMLElement | null) => {\n ref.current = el;\n setNode(el);\n }, []);\n}\n"]}
@@ -1,9 +1,9 @@
1
- import { useFocusTrap } from './chunk-5U2DZ7AV.js';
1
+ import { useDialogFocusTrap } from './chunk-HHCVBIMM.js';
2
2
  import { useAnimation } from './chunk-3K2CEAI7.js';
3
3
  import { Portal } from './chunk-RBCMKPBX.js';
4
4
  import { useEscapeKey } from './chunk-YX6FTJ6N.js';
5
5
  import { cn } from './chunk-MMW2JGKZ.js';
6
- import { createContext, useRef, useCallback, useEffect, useMemo, useContext } from 'react';
6
+ import { createContext, useCallback, useEffect, useMemo, useContext } from 'react';
7
7
  import { X } from 'lucide-react';
8
8
  import { jsxs, jsx } from 'react/jsx-runtime';
9
9
 
@@ -15,7 +15,7 @@ function useDrawer() {
15
15
  }
16
16
  return ctx;
17
17
  }
18
- function DrawerHeader({ children, closable = true, className }) {
18
+ function DrawerHeader({ children, closable = true, className, closeLabel = "Close" }) {
19
19
  const { close } = useDrawer();
20
20
  return /* @__PURE__ */ jsxs(
21
21
  "div",
@@ -32,8 +32,9 @@ function DrawerHeader({ children, closable = true, className }) {
32
32
  {
33
33
  type: "button",
34
34
  onClick: close,
35
- className: "rounded-lg p-1 text-zinc-400 transition-colors hover:bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] hover:text-zinc-600 dark:hover:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))] dark:hover:text-zinc-300",
36
- "aria-label": "Close",
35
+ "data-react-fancy-drawer-close": "",
36
+ className: "relative rounded-lg p-1 text-zinc-400 before:absolute before:-inset-[9px] before:content-[''] transition-colors hover:bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] hover:text-zinc-600 dark:hover:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))] dark:hover:text-zinc-300",
37
+ "aria-label": closeLabel,
37
38
  children: /* @__PURE__ */ jsx(X, { size: 18 })
38
39
  }
39
40
  ) : null
@@ -138,12 +139,10 @@ function DrawerRoot({
138
139
  className,
139
140
  ...rest
140
141
  }) {
141
- const panelRef = useRef(null);
142
142
  const horizontal = side !== "top" && side !== "bottom";
143
143
  const isViewport = attach === "viewport";
144
144
  const close = useCallback(() => onClose(), [onClose]);
145
145
  useEscapeKey(close, open && dismissOnEscape);
146
- useFocusTrap(panelRef, open && isViewport);
147
146
  useEffect(() => {
148
147
  if (!open || !isViewport) return;
149
148
  const original = document.body.style.overflow;
@@ -157,6 +156,14 @@ function DrawerRoot({
157
156
  enterClass: ENTER[side],
158
157
  exitClass: EXIT[side]
159
158
  });
159
+ const attachTrap = useDialogFocusTrap(open, isViewport);
160
+ const attachPanel = useCallback(
161
+ (node) => {
162
+ animRef.current = node;
163
+ attachTrap(node);
164
+ },
165
+ [animRef, attachTrap]
166
+ );
160
167
  const ctx = useMemo(() => ({ open, close, side }), [open, close, side]);
161
168
  if (!mounted) return null;
162
169
  const frame = /* @__PURE__ */ jsxs(
@@ -181,10 +188,7 @@ function DrawerRoot({
181
188
  /* @__PURE__ */ jsx(
182
189
  "div",
183
190
  {
184
- ref: (node) => {
185
- panelRef.current = node;
186
- animRef.current = node;
187
- },
191
+ ref: attachPanel,
188
192
  "data-react-fancy-drawer": "",
189
193
  "data-side": side,
190
194
  role: "dialog",
@@ -219,5 +223,5 @@ var Drawer = Object.assign(DrawerRoot, {
219
223
  });
220
224
 
221
225
  export { Drawer, useDrawer };
222
- //# sourceMappingURL=chunk-ZBXJDQXN.js.map
223
- //# sourceMappingURL=chunk-ZBXJDQXN.js.map
226
+ //# sourceMappingURL=chunk-JJYMRV62.js.map
227
+ //# sourceMappingURL=chunk-JJYMRV62.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/Drawer/Drawer.context.ts","../src/components/Drawer/DrawerHeader.tsx","../src/components/Drawer/DrawerBody.tsx","../src/components/Drawer/DrawerFooter.tsx","../src/components/Drawer/DrawerContainer.tsx","../src/components/Drawer/Drawer.tsx"],"names":["jsx","jsxs"],"mappings":"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","file":"chunk-JJYMRV62.js","sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { DrawerContextValue } from \"./Drawer.types\";\n\nexport const DrawerContext = createContext<DrawerContextValue | null>(null);\n\nexport function useDrawer(): DrawerContextValue {\n const ctx = useContext(DrawerContext);\n if (!ctx) {\n throw new Error(\"Drawer compound components must be used within <Drawer>\");\n }\n return ctx;\n}\n","import { X } from \"lucide-react\";\nimport { cn } from \"../../utils/cn\";\nimport { useDrawer } from \"./Drawer.context\";\nimport type { DrawerHeaderProps } from \"./Drawer.types\";\n\n/**\n * The close button draws at 26px (18px icon + `p-1`) and HITS at 44px (WCAG\n * 2.5.5, AAA): a `::before` overhangs it by 9px, inside the header's `py-3`. The\n * look is unchanged; see ModalHeader for why this over a size option.\n */\nexport function DrawerHeader({ children, closable = true, className, closeLabel = \"Close\" }: DrawerHeaderProps) {\n const { close } = useDrawer();\n\n return (\n <div\n data-react-fancy-drawer-header=\"\"\n className={cn(\n \"flex shrink-0 items-center justify-between border-b border-[color:var(--fancy-border,var(--color-zinc-200))] px-4 py-3 dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))]\",\n className,\n )}\n >\n <div className=\"text-sm font-semibold text-[color:var(--fancy-text,var(--color-zinc-900))] dark:text-[color:var(--fancy-text-dark,var(--color-zinc-100))]\">{children}</div>\n {closable ? (\n <button\n type=\"button\"\n onClick={close}\n data-react-fancy-drawer-close=\"\"\n className=\"relative rounded-lg p-1 text-zinc-400 before:absolute before:-inset-[9px] before:content-[''] transition-colors hover:bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] hover:text-zinc-600 dark:hover:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))] dark:hover:text-zinc-300\"\n aria-label={closeLabel}\n >\n <X size={18} />\n </button>\n ) : null}\n </div>\n );\n}\n\nDrawerHeader.displayName = \"DrawerHeader\";\n","import { cn } from \"../../utils/cn\";\nimport type { DrawerBodyProps } from \"./Drawer.types\";\n\nexport function DrawerBody({ children, className }: DrawerBodyProps) {\n return (\n <div\n data-react-fancy-drawer-body=\"\"\n className={cn(\"min-h-0 flex-1 overflow-auto px-4 py-3 text-[color:var(--fancy-text-soft,var(--color-zinc-700))] dark:text-[color:var(--fancy-text-soft-dark,var(--color-zinc-300))]\", className)}\n >\n {children}\n </div>\n );\n}\n\nDrawerBody.displayName = \"DrawerBody\";\n","import { cn } from \"../../utils/cn\";\nimport type { DrawerFooterProps } from \"./Drawer.types\";\n\nexport function DrawerFooter({ children, className }: DrawerFooterProps) {\n return (\n <div\n data-react-fancy-drawer-footer=\"\"\n className={cn(\n \"flex shrink-0 items-center justify-end gap-2 border-t border-[color:var(--fancy-border,var(--color-zinc-200))] px-4 py-3 dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))]\",\n className,\n )}\n >\n {children}\n </div>\n );\n}\n\nDrawerFooter.displayName = \"DrawerFooter\";\n","import { cn } from \"../../utils/cn\";\nimport type { DrawerContainerProps } from \"./Drawer.types\";\n\n/**\n * The anchor for `<Drawer attach=\"container\">`.\n *\n * A container-attached drawer positions itself `absolute`, which resolves\n * against the nearest *positioned* ancestor — so without `relative` somewhere\n * above it, the drawer silently escapes to the viewport and looks like it\n * ignored `attach` entirely. This wrapper supplies `relative` + `overflow-hidden`\n * so the panel slides within the box rather than out of it.\n *\n * <Drawer.Container className=\"h-80 rounded-xl border\">\n * <YourContent />\n * <Drawer attach=\"container\" side=\"bottom\" open={open} onClose={close}>\n * <Drawer.Body>Filters</Drawer.Body>\n * </Drawer>\n * </Drawer.Container>\n *\n * Any element works as long as it is positioned — this is the shortcut, not a\n * requirement. Wrapping a Card, a layout pane, or a prompt-input shell in\n * `relative overflow-hidden` does the same job.\n */\nexport function DrawerContainer({ children, className, ...rest }: DrawerContainerProps) {\n return (\n <div\n data-react-fancy-drawer-container=\"\"\n {...rest}\n className={cn(\"relative overflow-hidden\", className)}\n >\n {children}\n </div>\n );\n}\n\nDrawerContainer.displayName = \"DrawerContainer\";\n","import { useCallback, useEffect, useMemo } from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Portal } from \"../Portal\";\nimport { DrawerContext } from \"./Drawer.context\";\nimport { DrawerHeader } from \"./DrawerHeader\";\nimport { DrawerBody } from \"./DrawerBody\";\nimport { DrawerFooter } from \"./DrawerFooter\";\nimport { DrawerContainer } from \"./DrawerContainer\";\nimport { useDialogFocusTrap } from \"../../hooks/use-dialog-focus-trap\";\nimport { useEscapeKey } from \"../../hooks/use-escape-key\";\nimport { useAnimation } from \"../../hooks/use-animation\";\nimport type { DrawerProps, DrawerSide } from \"./Drawer.types\";\n\n/**\n * `size` addresses the drawer's own axis: width on the horizontal edges,\n * height on the vertical ones. Keeping one scale across both means `size=\"lg\"`\n * does not have to be re-learned when a drawer moves from the side to the\n * bottom.\n */\nconst WIDTH = {\n sm: \"w-64\",\n md: \"w-80\",\n lg: \"w-96\",\n xl: \"w-[32rem]\",\n full: \"w-full\",\n} as const;\n\nconst HEIGHT = {\n sm: \"h-32\",\n md: \"h-64\",\n lg: \"h-96\",\n xl: \"h-[32rem]\",\n full: \"h-full\",\n} as const;\n\nconst ANCHOR: Record<DrawerSide, string> = {\n start: \"inset-y-0 start-0\",\n end: \"inset-y-0 end-0\",\n left: \"inset-y-0 left-0\",\n right: \"inset-y-0 right-0\",\n top: \"inset-x-0 top-0\",\n bottom: \"inset-x-0 bottom-0\",\n};\n\nconst ENTER: Record<DrawerSide, string> = {\n start: \"fancy-slide-in-start\",\n end: \"fancy-slide-in-end\",\n left: \"fancy-slide-in-left\",\n right: \"fancy-slide-in-right\",\n top: \"fancy-slide-in-top\",\n bottom: \"fancy-slide-in-bottom\",\n};\n\nconst EXIT: Record<DrawerSide, string> = {\n start: \"fancy-slide-out-start\",\n end: \"fancy-slide-out-end\",\n left: \"fancy-slide-out-left\",\n right: \"fancy-slide-out-right\",\n top: \"fancy-slide-out-top\",\n bottom: \"fancy-slide-out-bottom\",\n};\n\n/** The panel's edge shadow reads as depth away from the anchored edge. */\nconst EDGE: Record<DrawerSide, string> = {\n start: \"border-e\",\n end: \"border-s\",\n left: \"border-r\",\n right: \"border-l\",\n top: \"border-b\",\n bottom: \"border-t\",\n};\n\nfunction DrawerRoot({\n children,\n open,\n onClose,\n side = \"end\",\n size = \"md\",\n attach = \"viewport\",\n backdrop = true,\n dismissOnBackdrop = true,\n dismissOnEscape = true,\n className,\n ...rest\n}: DrawerProps) {\n const horizontal = side !== \"top\" && side !== \"bottom\";\n const isViewport = attach === \"viewport\";\n\n const close = useCallback(() => onClose(), [onClose]);\n useEscapeKey(close, open && dismissOnEscape);\n\n useEffect(() => {\n if (!open || !isViewport) return;\n const original = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n return () => {\n document.body.style.overflow = original;\n };\n }, [open, isViewport]);\n\n const { mounted, className: animClass, ref: animRef } = useAnimation({\n open,\n enterClass: ENTER[side],\n exitClass: EXIT[side],\n });\n\n // A container-attached drawer is a panel, not a dialog — trapping focus in it\n // would strand keyboard users inside a Card. Only the viewport form is modal.\n // The panel mounts renders after `open` flips; see useDialogFocusTrap.\n const attachTrap = useDialogFocusTrap(open, isViewport);\n const attachPanel = useCallback(\n (node: HTMLDivElement | null) => {\n (animRef as React.MutableRefObject<HTMLElement | null>).current = node;\n attachTrap(node);\n },\n [animRef, attachTrap],\n );\n\n const ctx = useMemo(() => ({ open, close, side }), [open, close, side]);\n\n if (!mounted) return null;\n\n const frame = (\n <div\n data-react-fancy-drawer-frame=\"\"\n className={cn(isViewport ? \"fixed inset-0 z-50\" : \"absolute inset-0 z-20\", !backdrop && \"pointer-events-none\")}\n >\n {backdrop ? (\n <div\n data-react-fancy-drawer-backdrop=\"\"\n className={cn(\n \"absolute inset-0 bg-black/40 transition-opacity\",\n isViewport && \"backdrop-blur-sm\",\n open ? \"opacity-100\" : \"opacity-0\",\n )}\n onClick={dismissOnBackdrop ? close : undefined}\n aria-hidden=\"true\"\n />\n ) : null}\n\n <div\n ref={attachPanel}\n data-react-fancy-drawer=\"\"\n data-side={side}\n role=\"dialog\"\n aria-modal={isViewport || undefined}\n {...rest}\n className={cn(\n \"pointer-events-auto absolute flex flex-col border-[color:var(--fancy-border,var(--color-zinc-200))] bg-[color:var(--fancy-surface,var(--color-white))] shadow-xl dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))] dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))]\",\n ANCHOR[side],\n EDGE[side],\n horizontal ? WIDTH[size] : HEIGHT[size],\n // Cross-axis fills the anchor; the main axis is capped so a viewport\n // drawer can never grow past the screen on a small device.\n horizontal ? \"h-full\" : \"w-full\",\n isViewport && (horizontal ? \"max-w-[90vw]\" : \"max-h-[90vh]\"),\n !isViewport && (horizontal ? \"max-w-full\" : \"max-h-full\"),\n animClass,\n className,\n )}\n >\n {children}\n </div>\n </div>\n );\n\n return (\n <DrawerContext.Provider value={ctx}>\n {isViewport ? <Portal>{frame}</Portal> : frame}\n </DrawerContext.Provider>\n );\n}\n\nexport const Drawer = Object.assign(DrawerRoot, {\n Header: DrawerHeader,\n Body: DrawerBody,\n Footer: DrawerFooter,\n Container: DrawerContainer,\n});\n"]}
@@ -1,4 +1,4 @@
1
- import { useFocusTrap } from './chunk-5U2DZ7AV.js';
1
+ import { useDialogFocusTrap } from './chunk-HHCVBIMM.js';
2
2
  import { useAnimation } from './chunk-3K2CEAI7.js';
3
3
  import { isImeComposing } from './chunk-S42OGG43.js';
4
4
  import { Portal } from './chunk-RBCMKPBX.js';
@@ -147,7 +147,6 @@ function CommandRoot({
147
147
  setActiveIndex(-1);
148
148
  }, [onClose]);
149
149
  useEscapeKey(close, open);
150
- useFocusTrap(panelRef, open);
151
150
  useEffect(() => {
152
151
  if (open) {
153
152
  const original = document.body.style.overflow;
@@ -209,6 +208,15 @@ function CommandRoot({
209
208
  enterClass: "fancy-slide-up",
210
209
  exitClass: "fancy-fade-out"
211
210
  });
211
+ const attachTrap = useDialogFocusTrap(open);
212
+ const attachPanel = useCallback(
213
+ (node) => {
214
+ panelRef.current = node;
215
+ animRef.current = node;
216
+ attachTrap(node);
217
+ },
218
+ [animRef, attachTrap]
219
+ );
212
220
  const ctx = useMemo(
213
221
  () => ({
214
222
  open,
@@ -235,10 +243,7 @@ function CommandRoot({
235
243
  /* @__PURE__ */ jsx(
236
244
  "div",
237
245
  {
238
- ref: (node) => {
239
- panelRef.current = node;
240
- animRef.current = node;
241
- },
246
+ ref: attachPanel,
242
247
  "data-react-fancy-command": "",
243
248
  className: cn(
244
249
  "relative w-full max-w-lg overflow-hidden rounded-2xl border border-[color:var(--fancy-border,var(--color-zinc-200))] bg-[color:var(--fancy-surface,var(--color-white))] shadow-2xl dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))] dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))]",
@@ -259,5 +264,5 @@ var Command = Object.assign(CommandRoot, {
259
264
  });
260
265
 
261
266
  export { Command, useCommand };
262
- //# sourceMappingURL=chunk-R6O6SLXK.js.map
263
- //# sourceMappingURL=chunk-R6O6SLXK.js.map
267
+ //# sourceMappingURL=chunk-LG2O26CG.js.map
268
+ //# sourceMappingURL=chunk-LG2O26CG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/Command/Command.context.ts","../src/components/Command/CommandInput.tsx","../src/components/Command/CommandList.tsx","../src/components/Command/CommandItem.tsx","../src/components/Command/CommandGroup.tsx","../src/components/Command/CommandEmpty.tsx","../src/components/Command/Command.tsx"],"names":["jsx","jsxs","useId"],"mappings":"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","file":"chunk-LG2O26CG.js","sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { CommandContextValue } from \"./Command.types\";\n\nexport const CommandContext = createContext<CommandContextValue | null>(null);\n\nexport function useCommand(): CommandContextValue {\n const ctx = useContext(CommandContext);\n if (!ctx) {\n throw new Error(\n \"Command compound components must be used within <Command>\",\n );\n }\n return ctx;\n}\n","import { Search } from \"lucide-react\";\nimport { cn } from \"../../utils/cn\";\nimport { useCommand } from \"./Command.context\";\nimport type { CommandInputProps } from \"./Command.types\";\n\nexport function CommandInput({\n placeholder = \"Type a command or search...\",\n className,\n}: CommandInputProps) {\n const { query, setQuery, listId } = useCommand();\n\n return (\n <div data-react-fancy-command-input=\"\" className=\"flex items-center border-b border-[color:var(--fancy-border,var(--color-zinc-200))] px-4 dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))]\">\n <Search size={16} className=\"shrink-0 text-zinc-400\" />\n <input\n type=\"text\"\n // A combobox: focus stays here while the arrows move the active option,\n // which Command announces through aria-activedescendant.\n role=\"combobox\"\n aria-expanded=\"true\"\n aria-controls={listId}\n aria-autocomplete=\"list\"\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder={placeholder}\n className={cn(\n \"flex-1 bg-transparent px-3 py-3 text-sm outline-none placeholder:text-zinc-400\",\n className,\n )}\n autoFocus\n />\n </div>\n );\n}\n\nCommandInput.displayName = \"CommandInput\";\n","import { cn } from \"../../utils/cn\";\nimport { useCommand } from \"./Command.context\";\nimport type { CommandListProps } from \"./Command.types\";\n\nexport function CommandList({ children, className }: CommandListProps) {\n const { listId } = useCommand();\n\n return (\n <div\n id={listId}\n data-react-fancy-command-list=\"\"\n role=\"listbox\"\n className={cn(\"max-h-72 overflow-y-auto p-2\", className)}\n >\n {children}\n </div>\n );\n}\n\nCommandList.displayName = \"CommandList\";\n","import { useId } from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { useCommand } from \"./Command.context\";\nimport type { CommandItemProps } from \"./Command.types\";\n\nexport function CommandItem({\n children,\n value,\n onSelect,\n className,\n}: CommandItemProps) {\n const { query, close, shouldFilter, setActiveIndex } = useCommand();\n // Called before the filter's early return, so the hook order never changes.\n const id = useId();\n\n // Filter by query — unless the caller filters for itself (`shouldFilter={false}`).\n const text = value ?? (typeof children === \"string\" ? children : \"\");\n if (shouldFilter && query && !text.toLowerCase().includes(query.toLowerCase())) {\n return null;\n }\n\n return (\n <button\n id={id}\n data-react-fancy-command-item=\"\"\n type=\"button\"\n role=\"option\"\n // Marked active by Command itself (aria-selected + data-active), since an\n // item cannot know its own position among the rendered options.\n aria-selected={false}\n // Keyboard focus stays in the input; the items are reached with the arrows.\n tabIndex={-1}\n onMouseEnter={(e) => {\n const options = e.currentTarget\n .closest(\"[data-react-fancy-command]\")\n ?.querySelectorAll('[role=\"option\"]');\n if (options) setActiveIndex(Array.from(options).indexOf(e.currentTarget));\n }}\n className={cn(\n \"flex w-full items-center rounded-lg px-3 py-2 text-start text-sm transition-colors\",\n \"text-[color:var(--fancy-text-soft,var(--color-zinc-700))] hover:bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] data-[active]:bg-[color:var(--fancy-surface-muted,var(--color-zinc-100))] dark:text-[color:var(--fancy-text-soft-dark,var(--color-zinc-300))] dark:hover:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))] dark:data-[active]:bg-[color:var(--fancy-surface-muted-dark,var(--color-zinc-800))]\",\n className,\n )}\n onClick={() => {\n onSelect?.();\n close();\n }}\n >\n {children}\n </button>\n );\n}\n\nCommandItem.displayName = \"CommandItem\";\n","import { cn } from \"../../utils/cn\";\nimport type { CommandGroupProps } from \"./Command.types\";\n\nexport function CommandGroup({\n children,\n heading,\n className,\n}: CommandGroupProps) {\n return (\n <div data-react-fancy-command-group=\"\" role=\"group\" className={cn(\"py-1\", className)}>\n {heading && (\n <div className=\"px-3 py-1.5 text-xs font-medium text-zinc-400\">\n {heading}\n </div>\n )}\n {children}\n </div>\n );\n}\n\nCommandGroup.displayName = \"CommandGroup\";\n","import { cn } from \"../../utils/cn\";\nimport type { CommandEmptyProps } from \"./Command.types\";\n\nexport function CommandEmpty({\n children = \"No results found.\",\n className,\n}: CommandEmptyProps) {\n return (\n <div\n data-react-fancy-command-empty=\"\"\n className={cn(\n \"py-6 text-center text-sm text-[color:var(--fancy-text-muted,var(--color-zinc-500))] dark:text-[color:var(--fancy-text-muted-dark,var(--color-zinc-400))]\",\n className,\n )}\n >\n {children}\n </div>\n );\n}\n\nCommandEmpty.displayName = \"CommandEmpty\";\n","import {\n useCallback,\n useEffect,\n useId,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { isImeComposing } from \"../../utils/ime\";\nimport { Portal } from \"../Portal\";\nimport { CommandContext } from \"./Command.context\";\nimport { CommandInput } from \"./CommandInput\";\nimport { CommandList } from \"./CommandList\";\nimport { CommandItem } from \"./CommandItem\";\nimport { CommandGroup } from \"./CommandGroup\";\nimport { CommandEmpty } from \"./CommandEmpty\";\nimport { useDialogFocusTrap } from \"../../hooks/use-dialog-focus-trap\";\nimport { useEscapeKey } from \"../../hooks/use-escape-key\";\nimport { useAnimation } from \"../../hooks/use-animation\";\nimport { useControllableState } from \"../../hooks/use-controllable-state\";\nimport type { CommandProps } from \"./Command.types\";\n\nfunction CommandRoot({\n children,\n open,\n onClose,\n className,\n shouldFilter = true,\n query: controlledQuery,\n onQueryChange,\n}: CommandProps) {\n const [query, setQuery] = useControllableState(controlledQuery, \"\", onQueryChange);\n const [activeIndex, setActiveIndex] = useState(-1);\n const panelRef = useRef<HTMLDivElement>(null);\n const listId = useId();\n\n const close = useCallback(() => {\n onClose();\n setQuery(\"\");\n setActiveIndex(-1);\n }, [onClose]);\n\n useEscapeKey(close, open);\n\n // Cmd+K global shortcut is handled by the consumer\n useEffect(() => {\n if (open) {\n const original = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n return () => {\n document.body.style.overflow = original;\n };\n }\n }, [open]);\n\n /*\n * Keyboard navigation, as a combobox: the arrows move the ACTIVE item while\n * focus stays in the input (announced through aria-activedescendant), and\n * Enter picks the active item — or the first visible one when none is active.\n *\n * Focus used to move onto the items, and Enter was handled nowhere, so typing\n * a query and pressing Enter did nothing: only a focused item took Enter, as a\n * native click. Found by Genie 2's real-window test.\n */\n useEffect(() => {\n if (!open) return;\n\n const handleKeyDown = (e: KeyboardEvent) => {\n const items = panelRef.current?.querySelectorAll<HTMLElement>(\n '[role=\"option\"]',\n );\n if (!items?.length) return;\n\n if (e.key === \"ArrowDown\") {\n e.preventDefault();\n setActiveIndex(activeIndex < items.length - 1 ? activeIndex + 1 : 0);\n } else if (e.key === \"ArrowUp\") {\n e.preventDefault();\n setActiveIndex(activeIndex > 0 ? activeIndex - 1 : items.length - 1);\n } else if (e.key === \"Enter\") {\n if (isImeComposing(e)) return;\n // A focused item takes Enter natively, as a click; picking here as well\n // would select it twice.\n if ((e.target as HTMLElement | null)?.getAttribute(\"role\") === \"option\") return;\n const pick = items[activeIndex] ?? items[0];\n e.preventDefault();\n pick.click();\n }\n };\n\n document.addEventListener(\"keydown\", handleKeyDown);\n return () => document.removeEventListener(\"keydown\", handleKeyDown);\n }, [open, activeIndex]);\n\n /*\n * Mark the active item and point the input at it. Done on the DOM after each\n * render because an item cannot know its own index — that is its position\n * among the options currently rendered, which filtering changes.\n */\n useLayoutEffect(() => {\n const panel = panelRef.current;\n if (!panel) return;\n\n const items = Array.from(panel.querySelectorAll<HTMLElement>('[role=\"option\"]'));\n const active = items[activeIndex];\n items.forEach((item) => {\n const on = item === active;\n item.setAttribute(\"aria-selected\", on ? \"true\" : \"false\");\n if (on) item.setAttribute(\"data-active\", \"\");\n else item.removeAttribute(\"data-active\");\n });\n\n const input = panel.querySelector<HTMLInputElement>(\"[data-react-fancy-command-input] input\");\n if (active?.id) input?.setAttribute(\"aria-activedescendant\", active.id);\n else input?.removeAttribute(\"aria-activedescendant\");\n active?.scrollIntoView?.({ block: \"nearest\" });\n });\n\n // A new query is a new list: nothing in it is active yet, so Enter picks the first match.\n const changeQuery = useCallback(\n (next: string) => {\n setQuery(next);\n setActiveIndex(-1);\n },\n [setQuery],\n );\n\n const { mounted, className: animClass, ref: animRef } = useAnimation({\n open,\n enterClass: \"fancy-slide-up\",\n exitClass: \"fancy-fade-out\",\n });\n\n // The panel mounts renders after `open` flips; see useDialogFocusTrap.\n const attachTrap = useDialogFocusTrap(open);\n const attachPanel = useCallback(\n (node: HTMLDivElement | null) => {\n panelRef.current = node;\n (animRef as React.MutableRefObject<HTMLElement | null>).current = node;\n attachTrap(node);\n },\n [animRef, attachTrap],\n );\n\n const ctx = useMemo(\n () => ({\n open,\n close,\n query,\n setQuery: changeQuery,\n activeIndex,\n setActiveIndex,\n shouldFilter,\n listId,\n }),\n [open, close, query, changeQuery, activeIndex, shouldFilter, listId],\n );\n\n if (!mounted) return null;\n\n return (\n <CommandContext.Provider value={ctx}>\n <Portal>\n <div className=\"fixed inset-0 z-50 flex items-start justify-center pt-[20vh]\">\n <div\n className=\"absolute inset-0 bg-black/50 backdrop-blur-sm\"\n onClick={close}\n aria-hidden=\"true\"\n />\n <div\n ref={attachPanel}\n data-react-fancy-command=\"\"\n className={cn(\n \"relative w-full max-w-lg overflow-hidden rounded-2xl border border-[color:var(--fancy-border,var(--color-zinc-200))] bg-[color:var(--fancy-surface,var(--color-white))] shadow-2xl dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))] dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))]\",\n animClass,\n className,\n )}\n >\n {children}\n </div>\n </div>\n </Portal>\n </CommandContext.Provider>\n );\n}\n\nexport const Command = Object.assign(CommandRoot, {\n Input: CommandInput,\n List: CommandList,\n Item: CommandItem,\n Group: CommandGroup,\n Empty: CommandEmpty,\n});\n"]}
@@ -9,13 +9,15 @@ var FOCUSABLE_SELECTOR = [
9
9
  "select:not([disabled])",
10
10
  "[tabindex]:not([tabindex='-1'])"
11
11
  ].join(", ");
12
- function useFocusTrap(ref, enabled = true) {
12
+ function useFocusTrap(ref, enabled = true, restoreTo) {
13
13
  useEffect(() => {
14
14
  if (!enabled) return;
15
15
  const el = ref.current;
16
16
  if (!el) return;
17
17
  const previouslyFocused = document.activeElement;
18
- const focusables = () => Array.from(el.querySelectorAll(FOCUSABLE_SELECTOR));
18
+ const focusables = () => Array.from(el.querySelectorAll(FOCUSABLE_SELECTOR)).filter(
19
+ (node) => node.getAttribute("tabindex") !== "-1"
20
+ );
19
21
  const first = focusables()[0];
20
22
  first?.focus();
21
23
  const listener = (event) => {
@@ -39,11 +41,11 @@ function useFocusTrap(ref, enabled = true) {
39
41
  el.addEventListener("keydown", listener);
40
42
  return () => {
41
43
  el.removeEventListener("keydown", listener);
42
- previouslyFocused?.focus();
44
+ (restoreTo?.current ?? previouslyFocused)?.focus();
43
45
  };
44
- }, [ref, enabled]);
46
+ }, [ref, enabled, restoreTo]);
45
47
  }
46
48
 
47
49
  export { useFocusTrap };
48
- //# sourceMappingURL=chunk-5U2DZ7AV.js.map
49
- //# sourceMappingURL=chunk-5U2DZ7AV.js.map
50
+ //# sourceMappingURL=chunk-MUV7OK3W.js.map
51
+ //# sourceMappingURL=chunk-MUV7OK3W.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/hooks/use-focus-trap.ts"],"names":[],"mappings":";;;AAEA,IAAM,kBAAA,GAAqB;AAAA,EACzB,SAAA;AAAA,EACA,wBAAA;AAAA,EACA,uBAAA;AAAA,EACA,0BAAA;AAAA,EACA,wBAAA;AAAA,EACA;AACF,CAAA,CAAE,KAAK,IAAI,CAAA;AAaJ,SAAS,YAAA,CACd,GAAA,EACA,OAAA,GAAmB,IAAA,EACnB,SAAA,EACM;AACN,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,OAAA,EAAS;AAEd,IAAA,MAAM,KAAK,GAAA,CAAI,OAAA;AACf,IAAA,IAAI,CAAC,EAAA,EAAI;AAET,IAAA,MAAM,oBAAoB,QAAA,CAAS,aAAA;AAKnC,IAAA,MAAM,UAAA,GAAa,MACjB,KAAA,CAAM,IAAA,CAAK,GAAG,gBAAA,CAA8B,kBAAkB,CAAC,CAAA,CAAE,MAAA;AAAA,MAC/D,CAAC,IAAA,KAAS,IAAA,CAAK,YAAA,CAAa,UAAU,CAAA,KAAM;AAAA,KAC9C;AAGF,IAAA,MAAM,KAAA,GAAQ,UAAA,EAAW,CAAE,CAAC,CAAA;AAC5B,IAAA,KAAA,EAAO,KAAA,EAAM;AAEb,IAAA,MAAM,QAAA,GAAW,CAAC,KAAA,KAAyB;AACzC,MAAA,IAAI,KAAA,CAAM,QAAQ,KAAA,EAAO;AAEzB,MAAA,MAAM,QAAQ,UAAA,EAAW;AACzB,MAAA,IAAI,KAAA,CAAM,WAAW,CAAA,EAAG;AAExB,MAAA,MAAM,SAAA,GAAY,MAAM,CAAC,CAAA;AACzB,MAAA,MAAM,QAAA,GAAW,KAAA,CAAM,KAAA,CAAM,MAAA,GAAS,CAAC,CAAA;AAEvC,MAAA,IAAI,MAAM,QAAA,EAAU;AAClB,QAAA,IAAI,QAAA,CAAS,kBAAkB,SAAA,EAAW;AACxC,UAAA,KAAA,CAAM,cAAA,EAAe;AACrB,UAAA,QAAA,CAAS,KAAA,EAAM;AAAA,QACjB;AAAA,MACF,CAAA,MAAO;AACL,QAAA,IAAI,QAAA,CAAS,kBAAkB,QAAA,EAAU;AACvC,UAAA,KAAA,CAAM,cAAA,EAAe;AACrB,UAAA,SAAA,CAAU,KAAA,EAAM;AAAA,QAClB;AAAA,MACF;AAAA,IACF,CAAA;AAEA,IAAA,EAAA,CAAG,gBAAA,CAAiB,WAAW,QAAQ,CAAA;AAEvC,IAAA,OAAO,MAAM;AACX,MAAA,EAAA,CAAG,mBAAA,CAAoB,WAAW,QAAQ,CAAA;AAC1C,MAAA,CAAC,SAAA,EAAW,OAAA,IAAW,iBAAA,GAAoB,KAAA,EAAM;AAAA,IACnD,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,GAAA,EAAK,OAAA,EAAS,SAAS,CAAC,CAAA;AAC9B","file":"chunk-MUV7OK3W.js","sourcesContent":["import { useEffect, type RefObject } from \"react\";\n\nconst FOCUSABLE_SELECTOR = [\n \"a[href]\",\n \"button:not([disabled])\",\n \"input:not([disabled])\",\n \"textarea:not([disabled])\",\n \"select:not([disabled])\",\n \"[tabindex]:not([tabindex='-1'])\",\n].join(\", \");\n\n/**\n * Moves focus into `ref`, keeps Tab inside it, and restores focus on disable.\n *\n * `ref.current` must already be set in the commit where `enabled` turns true:\n * the effect does not run again until `enabled` changes. A panel that mounts\n * later (an animation, a Portal) should key `enabled` on the node — see\n * `useDialogFocusTrap`.\n *\n * `restoreTo`, when it holds an element, is where focus returns on disable\n * instead of whatever was focused when the trap engaged.\n */\nexport function useFocusTrap(\n ref: RefObject<HTMLElement | null>,\n enabled: boolean = true,\n restoreTo?: RefObject<HTMLElement | null>,\n): void {\n useEffect(() => {\n if (!enabled) return;\n\n const el = ref.current;\n if (!el) return;\n\n const previouslyFocused = document.activeElement as HTMLElement | null;\n\n // Tab stops only. `button` / `input` match an element with `tabindex=\"-1\"`\n // too (Command's options), and treating one as the last stop meant Tab never\n // wrapped: focus was never ON it, so the browser walked out of the dialog.\n const focusables = () =>\n Array.from(el.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR)).filter(\n (node) => node.getAttribute(\"tabindex\") !== \"-1\",\n );\n\n // Focus first focusable element\n const first = focusables()[0];\n first?.focus();\n\n const listener = (event: KeyboardEvent) => {\n if (event.key !== \"Tab\") return;\n\n const items = focusables();\n if (items.length === 0) return;\n\n const firstItem = items[0];\n const lastItem = items[items.length - 1];\n\n if (event.shiftKey) {\n if (document.activeElement === firstItem) {\n event.preventDefault();\n lastItem.focus();\n }\n } else {\n if (document.activeElement === lastItem) {\n event.preventDefault();\n firstItem.focus();\n }\n }\n };\n\n el.addEventListener(\"keydown\", listener);\n\n return () => {\n el.removeEventListener(\"keydown\", listener);\n (restoreTo?.current ?? previouslyFocused)?.focus();\n };\n }, [ref, enabled, restoreTo]);\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkTXTRTTG7_cjs = require('./chunk-TXTRTTG7.cjs');
3
+ var chunkRE3PMG47_cjs = require('./chunk-RE3PMG47.cjs');
4
4
  var chunkFQZE5N2N_cjs = require('./chunk-FQZE5N2N.cjs');
5
5
  var chunk6V44RRSB_cjs = require('./chunk-6V44RRSB.cjs');
6
6
  var chunkTI7D4QDM_cjs = require('./chunk-TI7D4QDM.cjs');
@@ -149,7 +149,6 @@ function CommandRoot({
149
149
  setActiveIndex(-1);
150
150
  }, [onClose]);
151
151
  chunkOUYC6EIL_cjs.useEscapeKey(close, open);
152
- chunkTXTRTTG7_cjs.useFocusTrap(panelRef, open);
153
152
  react.useEffect(() => {
154
153
  if (open) {
155
154
  const original = document.body.style.overflow;
@@ -211,6 +210,15 @@ function CommandRoot({
211
210
  enterClass: "fancy-slide-up",
212
211
  exitClass: "fancy-fade-out"
213
212
  });
213
+ const attachTrap = chunkRE3PMG47_cjs.useDialogFocusTrap(open);
214
+ const attachPanel = react.useCallback(
215
+ (node) => {
216
+ panelRef.current = node;
217
+ animRef.current = node;
218
+ attachTrap(node);
219
+ },
220
+ [animRef, attachTrap]
221
+ );
214
222
  const ctx = react.useMemo(
215
223
  () => ({
216
224
  open,
@@ -237,10 +245,7 @@ function CommandRoot({
237
245
  /* @__PURE__ */ jsxRuntime.jsx(
238
246
  "div",
239
247
  {
240
- ref: (node) => {
241
- panelRef.current = node;
242
- animRef.current = node;
243
- },
248
+ ref: attachPanel,
244
249
  "data-react-fancy-command": "",
245
250
  className: chunkZU5Q77OT_cjs.cn(
246
251
  "relative w-full max-w-lg overflow-hidden rounded-2xl border border-[color:var(--fancy-border,var(--color-zinc-200))] bg-[color:var(--fancy-surface,var(--color-white))] shadow-2xl dark:border-[color:var(--fancy-border-dark,var(--color-zinc-700))] dark:bg-[color:var(--fancy-surface-dark,var(--color-zinc-900))]",
@@ -262,5 +267,5 @@ var Command = Object.assign(CommandRoot, {
262
267
 
263
268
  exports.Command = Command;
264
269
  exports.useCommand = useCommand;
265
- //# sourceMappingURL=chunk-EC4ZYZNK.cjs.map
266
- //# sourceMappingURL=chunk-EC4ZYZNK.cjs.map
270
+ //# sourceMappingURL=chunk-NFUKNVB7.cjs.map
271
+ //# sourceMappingURL=chunk-NFUKNVB7.cjs.map