@thangnqdev/ui 0.1.2 → 0.2.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 (231) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +54 -206
  3. package/dist/bento/index.cjs.map +1 -1
  4. package/dist/bento/index.js +2 -2
  5. package/dist/bento/styles.css +2 -46
  6. package/dist/buttons/index.cjs +111 -18
  7. package/dist/buttons/index.cjs.map +1 -1
  8. package/dist/buttons/index.d.cts +1 -1
  9. package/dist/buttons/index.d.ts +1 -1
  10. package/dist/buttons/index.js +3 -3
  11. package/dist/buttons/styles.css +2 -6
  12. package/dist/{chunk-4S6L5V7X.js → chunk-2YJBW4E2.js} +2 -2
  13. package/dist/chunk-2YJBW4E2.js.map +1 -0
  14. package/dist/{chunk-F5Q7BTMH.js → chunk-4CRFNALF.js} +1011 -556
  15. package/dist/chunk-4CRFNALF.js.map +1 -0
  16. package/dist/{chunk-LWFRUJRU.js → chunk-4MS6ZHFQ.js} +225 -41
  17. package/dist/chunk-4MS6ZHFQ.js.map +1 -0
  18. package/dist/{chunk-2OXZK6IP.js → chunk-77KTYOMQ.js} +3 -3
  19. package/dist/chunk-77KTYOMQ.js.map +1 -0
  20. package/dist/{chunk-IABPUCAQ.js → chunk-ADO3SVUA.js} +2 -2
  21. package/dist/chunk-ADO3SVUA.js.map +1 -0
  22. package/dist/{chunk-GXI2H5XA.js → chunk-D7FB3TCA.js} +3 -3
  23. package/dist/chunk-D7FB3TCA.js.map +1 -0
  24. package/dist/chunk-DSEL7ELZ.js +314 -0
  25. package/dist/chunk-DSEL7ELZ.js.map +1 -0
  26. package/dist/{chunk-KJRBRTQE.js → chunk-EXFR2BFJ.js} +37 -28
  27. package/dist/chunk-EXFR2BFJ.js.map +1 -0
  28. package/dist/{chunk-PF4ZHGVW.js → chunk-GXWB6BSY.js} +116 -23
  29. package/dist/chunk-GXWB6BSY.js.map +1 -0
  30. package/dist/{chunk-66VWUJEI.js → chunk-I2FQBMVW.js} +3 -3
  31. package/dist/chunk-I2FQBMVW.js.map +1 -0
  32. package/dist/{chunk-EU2LPUYP.js → chunk-I5O2WCGF.js} +3 -3
  33. package/dist/chunk-I5O2WCGF.js.map +1 -0
  34. package/dist/{chunk-ATYGDTMK.js → chunk-ME4XILG5.js} +5 -5
  35. package/dist/chunk-ME4XILG5.js.map +1 -0
  36. package/dist/{chunk-5FZ6ZFII.js → chunk-PC5SZFZC.js} +373 -150
  37. package/dist/chunk-PC5SZFZC.js.map +1 -0
  38. package/dist/{chunk-C4SUXMI3.js → chunk-S6XJMYME.js} +3 -3
  39. package/dist/chunk-S6XJMYME.js.map +1 -0
  40. package/dist/{chunk-A7P3O5Z5.js → chunk-V52JOSDF.js} +3 -3
  41. package/dist/chunk-V52JOSDF.js.map +1 -0
  42. package/dist/{chunk-NFVONJUK.js → chunk-W5WNWTQC.js} +455 -211
  43. package/dist/chunk-W5WNWTQC.js.map +1 -0
  44. package/dist/{chunk-WAFHC4DQ.js → chunk-YJ64RI7J.js} +3 -3
  45. package/dist/chunk-YJ64RI7J.js.map +1 -0
  46. package/dist/chunk-YOCGBORV.js +155 -0
  47. package/dist/chunk-YOCGBORV.js.map +1 -0
  48. package/dist/chunk-ZEDXARXW.js +96 -0
  49. package/dist/chunk-ZEDXARXW.js.map +1 -0
  50. package/dist/components.css +2 -0
  51. package/dist/core/index.cjs +33 -24
  52. package/dist/core/index.cjs.map +1 -1
  53. package/dist/core/index.js +2 -2
  54. package/dist/core/styles.css +2 -134
  55. package/dist/data-display/index.cjs +770 -247
  56. package/dist/data-display/index.cjs.map +1 -1
  57. package/dist/data-display/index.d.cts +1 -1
  58. package/dist/data-display/index.d.ts +1 -1
  59. package/dist/data-display/index.js +5 -4
  60. package/dist/data-display/styles.css +2 -4
  61. package/dist/feedback/index.cjs.map +1 -1
  62. package/dist/feedback/index.js +2 -2
  63. package/dist/feedback/styles.css +2 -4
  64. package/dist/forms/index.cjs +1211 -459
  65. package/dist/forms/index.cjs.map +1 -1
  66. package/dist/forms/index.d.cts +1 -1
  67. package/dist/forms/index.d.ts +1 -1
  68. package/dist/forms/index.js +8 -8
  69. package/dist/forms/styles.css +2 -6
  70. package/dist/glass/index.cjs.map +1 -1
  71. package/dist/glass/index.js +2 -2
  72. package/dist/glass/styles.css +2 -81
  73. package/dist/{index-D6fE7w-b.d.cts → index-BGt_hecO.d.cts} +1 -0
  74. package/dist/{index-D6fE7w-b.d.ts → index-BGt_hecO.d.ts} +1 -0
  75. package/dist/{index-DzcWh1Hs.d.cts → index-BtDvpZCf.d.cts} +7 -0
  76. package/dist/{index-DzcWh1Hs.d.ts → index-BtDvpZCf.d.ts} +7 -0
  77. package/dist/{index-Cle6QzQl.d.ts → index-COX1sBUN.d.ts} +27 -3
  78. package/dist/{index-BkRqGNG1.d.ts → index-Cg8MuP7u.d.cts} +40 -13
  79. package/dist/{index-BkRqGNG1.d.cts → index-Cg8MuP7u.d.ts} +40 -13
  80. package/dist/{index-CenuF_P2.d.ts → index-DHkPRTvE.d.cts} +39 -8
  81. package/dist/{index-CenuF_P2.d.cts → index-DHkPRTvE.d.ts} +39 -8
  82. package/dist/{index-TyK-Ldpy.d.cts → index-Du_oht9k.d.cts} +27 -3
  83. package/dist/index.cjs +2481 -917
  84. package/dist/index.cjs.map +1 -1
  85. package/dist/index.d.cts +5 -5
  86. package/dist/index.d.ts +5 -5
  87. package/dist/index.js +19 -19
  88. package/dist/layout/index.cjs.map +1 -1
  89. package/dist/layout/index.js +2 -2
  90. package/dist/layout/styles.css +2 -8
  91. package/dist/navigation/index.cjs +288 -48
  92. package/dist/navigation/index.cjs.map +1 -1
  93. package/dist/navigation/index.d.cts +1 -1
  94. package/dist/navigation/index.d.ts +1 -1
  95. package/dist/navigation/index.js +6 -6
  96. package/dist/navigation/styles.css +2 -4
  97. package/dist/overlays/index.cjs +566 -229
  98. package/dist/overlays/index.cjs.map +1 -1
  99. package/dist/overlays/index.d.cts +1 -1
  100. package/dist/overlays/index.d.ts +1 -1
  101. package/dist/overlays/index.js +7 -7
  102. package/dist/overlays/styles.css +2 -8
  103. package/dist/reset.css +45 -0
  104. package/dist/styles.css +1 -1
  105. package/dist/tokens.css +11 -0
  106. package/dist/typography/index.cjs.map +1 -1
  107. package/dist/typography/index.js +4 -4
  108. package/dist/typography/styles.css +2 -5
  109. package/llms.txt +211 -113
  110. package/package.json +160 -116
  111. package/src/bento/BentoCard.tsx +140 -140
  112. package/src/bento/BentoGrid.tsx +50 -50
  113. package/src/bento/index.ts +2 -2
  114. package/src/bento/tokens.css +46 -46
  115. package/src/buttons/Button.tsx +104 -104
  116. package/src/buttons/ButtonGroup.tsx +43 -43
  117. package/src/buttons/IconButton.tsx +79 -79
  118. package/src/buttons/SplitButton.tsx +186 -86
  119. package/src/buttons/ToggleButton.tsx +157 -157
  120. package/src/buttons/index.ts +5 -5
  121. package/src/buttons/tokens.css +6 -6
  122. package/src/core/ThemeProvider.tsx +135 -123
  123. package/src/core/index.ts +2 -2
  124. package/src/core/tokens.css +134 -134
  125. package/src/data-display/Accordion.tsx +147 -147
  126. package/src/data-display/Avatar.tsx +134 -134
  127. package/src/data-display/Card.tsx +112 -112
  128. package/src/data-display/Chip.tsx +74 -74
  129. package/src/data-display/DataTablePro.tsx +299 -121
  130. package/src/data-display/StatCard.tsx +60 -60
  131. package/src/data-display/Table.tsx +74 -74
  132. package/src/data-display/Timeline.tsx +62 -62
  133. package/src/data-display/TreeView.tsx +243 -104
  134. package/src/data-display/tokens.css +4 -4
  135. package/src/feedback/Alert.tsx +80 -80
  136. package/src/feedback/EmptyState.tsx +45 -45
  137. package/src/feedback/Skeleton.tsx +66 -66
  138. package/src/feedback/Spinner.tsx +65 -65
  139. package/src/feedback/index.ts +5 -5
  140. package/src/feedback/tokens.css +4 -4
  141. package/src/forms/Checkbox.tsx +149 -69
  142. package/src/forms/CheckboxGroup.tsx +76 -29
  143. package/src/forms/Combobox.tsx +172 -28
  144. package/src/forms/DatePicker.tsx +385 -81
  145. package/src/forms/FileUpload.tsx +51 -7
  146. package/src/forms/FormField.tsx +115 -115
  147. package/src/forms/Input.tsx +151 -151
  148. package/src/forms/NumberInput.tsx +34 -20
  149. package/src/forms/PasswordInput.tsx +35 -35
  150. package/src/forms/PinInput.tsx +139 -125
  151. package/src/forms/Radio.tsx +142 -142
  152. package/src/forms/RangeSlider.tsx +237 -111
  153. package/src/forms/SearchInput.tsx +34 -34
  154. package/src/forms/Select.tsx +103 -103
  155. package/src/forms/Slider.tsx +85 -85
  156. package/src/forms/Textarea.tsx +114 -114
  157. package/src/forms/tokens.css +6 -6
  158. package/src/glass/GlassButton.tsx +59 -59
  159. package/src/glass/GlassCard.tsx +71 -71
  160. package/src/glass/index.ts +3 -3
  161. package/src/glass/tokens.css +81 -81
  162. package/src/index.ts +26 -26
  163. package/src/layout/AspectRatio.tsx +39 -39
  164. package/src/layout/Container.tsx +53 -53
  165. package/src/layout/Divider.tsx +61 -61
  166. package/src/layout/Flex.tsx +79 -79
  167. package/src/layout/Grid.tsx +124 -124
  168. package/src/layout/PageLayout.tsx +59 -59
  169. package/src/layout/Section.tsx +47 -47
  170. package/src/layout/Spacer.tsx +30 -30
  171. package/src/layout/Stack.tsx +88 -88
  172. package/src/layout/tokens.css +8 -8
  173. package/src/navigation/Breadcrumb.tsx +73 -73
  174. package/src/navigation/CommandPalette.tsx +142 -142
  175. package/src/navigation/DropdownMenu.tsx +244 -160
  176. package/src/navigation/Pagination.tsx +114 -114
  177. package/src/navigation/Stepper.tsx +93 -93
  178. package/src/navigation/Tabs.tsx +265 -133
  179. package/src/navigation/index.ts +6 -6
  180. package/src/navigation/tokens.css +4 -4
  181. package/src/overlays/AlertDialog.tsx +63 -54
  182. package/src/overlays/BottomSheet.tsx +30 -10
  183. package/src/overlays/ContextMenu.tsx +51 -9
  184. package/src/overlays/Drawer.tsx +160 -145
  185. package/src/overlays/FocusTrap.tsx +122 -49
  186. package/src/overlays/Modal.tsx +296 -219
  187. package/src/overlays/Popover.tsx +55 -25
  188. package/src/overlays/Portal.tsx +23 -23
  189. package/src/overlays/Toast.tsx +175 -137
  190. package/src/overlays/Tooltip.tsx +125 -100
  191. package/src/overlays/tokens.css +8 -8
  192. package/src/registry/components.ts +1368 -0
  193. package/src/styles/components.css +39 -0
  194. package/src/styles/index.css +2 -93
  195. package/src/styles/reset.css +45 -0
  196. package/src/styles/tokens.css +11 -2
  197. package/src/typography/Blockquote.tsx +32 -32
  198. package/src/typography/Code.tsx +89 -89
  199. package/src/typography/Heading.tsx +56 -56
  200. package/src/typography/Kbd.tsx +25 -25
  201. package/src/typography/Label.tsx +47 -47
  202. package/src/typography/Link.tsx +56 -56
  203. package/src/typography/Text.tsx +62 -62
  204. package/src/typography/index.ts +7 -7
  205. package/src/typography/tokens.css +5 -5
  206. package/src/utils/cn.ts +6 -6
  207. package/src/utils/scroll-lock.ts +19 -0
  208. package/src/utils/useControllableState.ts +54 -0
  209. package/ui-contract.json +192 -35
  210. package/dist/chunk-2OXZK6IP.js.map +0 -1
  211. package/dist/chunk-4S6L5V7X.js.map +0 -1
  212. package/dist/chunk-5FZ6ZFII.js.map +0 -1
  213. package/dist/chunk-66VWUJEI.js.map +0 -1
  214. package/dist/chunk-A7P3O5Z5.js.map +0 -1
  215. package/dist/chunk-ATYGDTMK.js.map +0 -1
  216. package/dist/chunk-C4SUXMI3.js.map +0 -1
  217. package/dist/chunk-EU2LPUYP.js.map +0 -1
  218. package/dist/chunk-F5Q7BTMH.js.map +0 -1
  219. package/dist/chunk-GXI2H5XA.js.map +0 -1
  220. package/dist/chunk-H3LCNPMC.js +0 -101
  221. package/dist/chunk-H3LCNPMC.js.map +0 -1
  222. package/dist/chunk-IABPUCAQ.js.map +0 -1
  223. package/dist/chunk-KJRBRTQE.js.map +0 -1
  224. package/dist/chunk-LBUBXUYQ.js +0 -40
  225. package/dist/chunk-LBUBXUYQ.js.map +0 -1
  226. package/dist/chunk-LWFRUJRU.js.map +0 -1
  227. package/dist/chunk-NFVONJUK.js.map +0 -1
  228. package/dist/chunk-PF4ZHGVW.js.map +0 -1
  229. package/dist/chunk-WAFHC4DQ.js.map +0 -1
  230. package/dist/chunk-X76KBH4Y.js +0 -71
  231. package/dist/chunk-X76KBH4Y.js.map +0 -1
@@ -1,10 +1,10 @@
1
- import { Popover } from './chunk-H3LCNPMC.js';
2
- import { FocusTrap } from './chunk-LBUBXUYQ.js';
3
- import { Portal } from './chunk-IABPUCAQ.js';
4
- import { Kbd } from './chunk-EU2LPUYP.js';
5
- import { Button } from './chunk-66VWUJEI.js';
6
- import { __export, cn } from './chunk-4S6L5V7X.js';
7
- import { createContext, useContext, useEffect, forwardRef, useState, useRef, useCallback } from 'react';
1
+ import { Popover } from './chunk-YOCGBORV.js';
2
+ import { FocusTrap } from './chunk-ZEDXARXW.js';
3
+ import { Portal } from './chunk-ADO3SVUA.js';
4
+ import { Kbd } from './chunk-I5O2WCGF.js';
5
+ import { Button } from './chunk-I2FQBMVW.js';
6
+ import { __export, cn } from './chunk-2YJBW4E2.js';
7
+ import React4, { createContext, useContext, useId, useState, useCallback, useEffect, forwardRef, useRef } from 'react';
8
8
  import { cva } from 'class-variance-authority';
9
9
  import { X, AlertCircle, AlertTriangle, CheckCircle2, Info } from 'lucide-react';
10
10
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
@@ -37,6 +37,7 @@ __export(overlays_exports, {
37
37
  ModalTitle: () => ModalTitle,
38
38
  Popover: () => Popover,
39
39
  Portal: () => Portal,
40
+ Toast: () => Toast,
40
41
  ToasterProvider: () => ToasterProvider,
41
42
  Tooltip: () => Tooltip,
42
43
  drawerVariants: () => drawerVariants,
@@ -47,6 +48,25 @@ __export(overlays_exports, {
47
48
  useModal: () => useModal,
48
49
  useToast: () => useToast
49
50
  });
51
+
52
+ // src/utils/scroll-lock.ts
53
+ var lockCount = 0;
54
+ var previousOverflow = "";
55
+ function lockBodyScroll() {
56
+ if (typeof document === "undefined") return;
57
+ if (lockCount === 0) {
58
+ previousOverflow = document.body.style.overflow;
59
+ document.body.style.overflow = "hidden";
60
+ }
61
+ lockCount++;
62
+ }
63
+ function unlockBodyScroll() {
64
+ if (typeof document === "undefined") return;
65
+ lockCount = Math.max(0, lockCount - 1);
66
+ if (lockCount === 0) {
67
+ document.body.style.overflow = previousOverflow;
68
+ }
69
+ }
50
70
  var ModalContext = createContext(null);
51
71
  function useModal() {
52
72
  const context = useContext(ModalContext);
@@ -60,33 +80,76 @@ function Modal({
60
80
  onClose,
61
81
  children,
62
82
  closeOnEsc = true,
63
- closeOnOverlayClick = true
83
+ closeOnOverlayClick = true,
84
+ role = "dialog",
85
+ "aria-label": ariaLabel,
86
+ "aria-labelledby": customLabelledBy,
87
+ "aria-describedby": customDescribedBy
64
88
  }) {
89
+ const generatedId = useId();
90
+ const titleId = customLabelledBy || `modal-title-${generatedId}`;
91
+ const descriptionId = customDescribedBy || `modal-desc-${generatedId}`;
92
+ const [mountedTitleId, setMountedTitleId] = useState(null);
93
+ const [mountedDescId, setMountedDescId] = useState(null);
94
+ const registerTitle = useCallback(
95
+ (id) => {
96
+ const activeId = id || titleId;
97
+ setMountedTitleId(activeId);
98
+ return () => setMountedTitleId(null);
99
+ },
100
+ [titleId]
101
+ );
102
+ const registerDescription = useCallback(
103
+ (id) => {
104
+ const activeId = id || descriptionId;
105
+ setMountedDescId(activeId);
106
+ return () => setMountedDescId(null);
107
+ },
108
+ [descriptionId]
109
+ );
65
110
  useEffect(() => {
66
111
  if (!isOpen) return;
112
+ lockBodyScroll();
67
113
  const handleKeyDown = (e) => {
68
114
  if (closeOnEsc && e.key === "Escape") {
69
115
  onClose();
70
116
  }
71
117
  };
72
- const originalOverflow = document.body.style.overflow;
73
- document.body.style.overflow = "hidden";
74
118
  window.addEventListener("keydown", handleKeyDown);
75
119
  return () => {
76
120
  window.removeEventListener("keydown", handleKeyDown);
77
- document.body.style.overflow = originalOverflow;
121
+ unlockBodyScroll();
78
122
  };
79
123
  }, [isOpen, closeOnEsc, onClose]);
80
124
  if (!isOpen) return null;
81
- return /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(ModalContext.Provider, { value: { isOpen, onClose, closeOnOverlayClick }, children: /* @__PURE__ */ jsx(FocusTrap, { active: isOpen, children: /* @__PURE__ */ jsx(
82
- "div",
125
+ const resolvedLabelledBy = customLabelledBy || (ariaLabel ? void 0 : mountedTitleId || void 0);
126
+ const resolvedDescribedBy = customDescribedBy || (mountedDescId || void 0);
127
+ return /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(
128
+ ModalContext.Provider,
83
129
  {
84
- className: "fixed inset-0 z-50 flex items-center justify-center p-4 sm:p-6",
85
- role: "dialog",
86
- "aria-modal": "true",
87
- children
130
+ value: {
131
+ isOpen,
132
+ onClose,
133
+ closeOnOverlayClick,
134
+ titleId,
135
+ descriptionId,
136
+ registerTitle,
137
+ registerDescription
138
+ },
139
+ children: /* @__PURE__ */ jsx(FocusTrap, { active: isOpen, restoreFocus: true, children: /* @__PURE__ */ jsx(
140
+ "div",
141
+ {
142
+ className: "fixed inset-0 z-50 flex items-center justify-center p-4 sm:p-6",
143
+ role,
144
+ "aria-modal": "true",
145
+ "aria-label": ariaLabel,
146
+ "aria-labelledby": resolvedLabelledBy,
147
+ "aria-describedby": resolvedDescribedBy,
148
+ children
149
+ }
150
+ ) })
88
151
  }
89
- ) }) }) });
152
+ ) });
90
153
  }
91
154
  var ModalOverlay = forwardRef(
92
155
  ({ className, onClick, ...props }, ref) => {
@@ -149,14 +212,42 @@ var ModalHeader = forwardRef(
149
212
  );
150
213
  ModalHeader.displayName = "ModalHeader";
151
214
  var ModalTitle = forwardRef(
152
- ({ className, children, ...props }, ref) => {
153
- return /* @__PURE__ */ jsx("h2", { ref, className: cn("text-lg sm:text-xl font-bold tracking-tight text-[var(--ui-text-primary)]", className), ...props, children });
215
+ ({ className, children, id, ...props }, ref) => {
216
+ const { titleId, registerTitle } = useModal();
217
+ const activeId = id || titleId;
218
+ useEffect(() => {
219
+ return registerTitle(activeId);
220
+ }, [registerTitle, activeId]);
221
+ return /* @__PURE__ */ jsx(
222
+ "h2",
223
+ {
224
+ ref,
225
+ id: activeId,
226
+ className: cn("text-lg sm:text-xl font-bold tracking-tight text-[var(--ui-text-primary)]", className),
227
+ ...props,
228
+ children
229
+ }
230
+ );
154
231
  }
155
232
  );
156
233
  ModalTitle.displayName = "ModalTitle";
157
234
  var ModalDescription = forwardRef(
158
- ({ className, children, ...props }, ref) => {
159
- return /* @__PURE__ */ jsx("p", { ref, className: cn("text-sm text-[var(--ui-text-muted)]", className), ...props, children });
235
+ ({ className, children, id, ...props }, ref) => {
236
+ const { descriptionId, registerDescription } = useModal();
237
+ const activeId = id || descriptionId;
238
+ useEffect(() => {
239
+ return registerDescription(activeId);
240
+ }, [registerDescription, activeId]);
241
+ return /* @__PURE__ */ jsx(
242
+ "p",
243
+ {
244
+ ref,
245
+ id: activeId,
246
+ className: cn("text-sm text-[var(--ui-text-muted)]", className),
247
+ ...props,
248
+ children
249
+ }
250
+ );
160
251
  }
161
252
  );
162
253
  ModalDescription.displayName = "ModalDescription";
@@ -186,7 +277,7 @@ var ModalCloseButton = forwardRef(
186
277
  },
187
278
  "aria-label": "Close dialog",
188
279
  className: cn(
189
- "absolute top-4 right-4 p-1.5 rounded-full text-[var(--ui-text-muted)] hover:text-[var(--ui-text-primary)] hover:bg-[var(--ui-bg-tertiary)] transition-colors",
280
+ "absolute top-4 right-4 p-1.5 rounded-full text-[var(--ui-text-muted)] hover:text-[var(--ui-text-primary)] hover:bg-[var(--ui-bg-tertiary)] transition-colors cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ui-primary)]",
190
281
  className
191
282
  ),
192
283
  ...props,
@@ -205,32 +296,43 @@ function AlertDialog({
205
296
  confirmText = "Confirm",
206
297
  cancelText = "Cancel",
207
298
  variant = "danger",
208
- isLoading = false
299
+ isLoading = false,
300
+ closeOnOverlayClick = false
209
301
  }) {
210
- return /* @__PURE__ */ jsxs(Modal, { isOpen, onClose, children: [
211
- /* @__PURE__ */ jsx(ModalOverlay, {}),
212
- /* @__PURE__ */ jsxs(ModalContent, { size: "sm", children: [
213
- /* @__PURE__ */ jsx(ModalCloseButton, {}),
214
- /* @__PURE__ */ jsxs(ModalHeader, { children: [
215
- /* @__PURE__ */ jsx(ModalTitle, { children: title }),
216
- /* @__PURE__ */ jsx(ModalDescription, { children: description })
217
- ] }),
218
- /* @__PURE__ */ jsxs(ModalFooter, { children: [
219
- /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: onClose, disabled: isLoading, children: cancelText }),
220
- /* @__PURE__ */ jsx(
221
- Button,
222
- {
223
- variant: variant === "danger" ? "danger" : "primary",
224
- size: "sm",
225
- loading: isLoading,
226
- onClick: onConfirm,
227
- children: confirmText
228
- }
229
- )
230
- ] })
231
- ] })
232
- ] });
302
+ return /* @__PURE__ */ jsxs(
303
+ Modal,
304
+ {
305
+ isOpen,
306
+ onClose,
307
+ role: "alertdialog",
308
+ closeOnOverlayClick,
309
+ children: [
310
+ /* @__PURE__ */ jsx(ModalOverlay, {}),
311
+ /* @__PURE__ */ jsxs(ModalContent, { size: "sm", children: [
312
+ /* @__PURE__ */ jsx(ModalCloseButton, {}),
313
+ /* @__PURE__ */ jsxs(ModalHeader, { children: [
314
+ /* @__PURE__ */ jsx(ModalTitle, { children: title }),
315
+ /* @__PURE__ */ jsx(ModalDescription, { children: description })
316
+ ] }),
317
+ /* @__PURE__ */ jsxs(ModalFooter, { children: [
318
+ /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: onClose, disabled: isLoading, autoFocus: true, children: cancelText }),
319
+ /* @__PURE__ */ jsx(
320
+ Button,
321
+ {
322
+ variant: variant === "danger" ? "danger" : "primary",
323
+ size: "sm",
324
+ loading: isLoading,
325
+ onClick: onConfirm,
326
+ children: confirmText
327
+ }
328
+ )
329
+ ] })
330
+ ] })
331
+ ]
332
+ }
333
+ );
233
334
  }
335
+ AlertDialog.displayName = "AlertDialog";
234
336
  var DrawerContext = createContext(null);
235
337
  function useDrawer() {
236
338
  const context = useContext(DrawerContext);
@@ -260,54 +362,68 @@ function Drawer({
260
362
  onClose,
261
363
  placement = "right",
262
364
  children,
263
- closeOnEsc = true
365
+ closeOnEsc = true,
366
+ "aria-label": ariaLabel,
367
+ "aria-labelledby": customLabelledBy
264
368
  }) {
369
+ const generatedId = useId();
370
+ const titleId = customLabelledBy || `drawer-title-${generatedId}`;
265
371
  useEffect(() => {
266
372
  if (!isOpen) return;
373
+ lockBodyScroll();
267
374
  const handleKeyDown = (e) => {
268
375
  if (closeOnEsc && e.key === "Escape") {
269
376
  onClose();
270
377
  }
271
378
  };
272
- const originalOverflow = document.body.style.overflow;
273
- document.body.style.overflow = "hidden";
274
379
  window.addEventListener("keydown", handleKeyDown);
275
380
  return () => {
276
381
  window.removeEventListener("keydown", handleKeyDown);
277
- document.body.style.overflow = originalOverflow;
382
+ unlockBodyScroll();
278
383
  };
279
384
  }, [isOpen, closeOnEsc, onClose]);
280
385
  if (!isOpen) return null;
281
- return /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(DrawerContext.Provider, { value: { isOpen, onClose }, children: /* @__PURE__ */ jsx(FocusTrap, { active: isOpen, children: /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-50 overflow-hidden", children: [
282
- /* @__PURE__ */ jsx(
283
- "div",
284
- {
285
- "aria-hidden": "true",
286
- onClick: onClose,
287
- className: "fixed inset-0 bg-[var(--ui-bg-overlay)] backdrop-blur-sm transition-opacity animate-in fade-in duration-200"
288
- }
289
- ),
290
- /* @__PURE__ */ jsx("div", { className: cn(drawerVariants({ placement })), children })
291
- ] }) }) }) });
386
+ return /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(DrawerContext.Provider, { value: { isOpen, onClose, titleId }, children: /* @__PURE__ */ jsx(FocusTrap, { active: isOpen, restoreFocus: true, children: /* @__PURE__ */ jsxs(
387
+ "div",
388
+ {
389
+ className: "fixed inset-0 z-50 overflow-hidden",
390
+ role: "dialog",
391
+ "aria-modal": "true",
392
+ "aria-label": ariaLabel,
393
+ "aria-labelledby": ariaLabel ? void 0 : titleId,
394
+ children: [
395
+ /* @__PURE__ */ jsx(
396
+ "div",
397
+ {
398
+ "aria-hidden": "true",
399
+ onClick: onClose,
400
+ className: "fixed inset-0 bg-[var(--ui-bg-overlay)] backdrop-blur-sm transition-opacity animate-in fade-in duration-200"
401
+ }
402
+ ),
403
+ /* @__PURE__ */ jsx("div", { className: cn(drawerVariants({ placement })), children })
404
+ ]
405
+ }
406
+ ) }) }) });
292
407
  }
293
408
  var DrawerHeader = forwardRef(
294
- ({ className, children, ...props }, ref) => {
295
- const { onClose } = useDrawer();
409
+ ({ className, children, id, ...props }, ref) => {
410
+ const { onClose, titleId } = useDrawer();
296
411
  return /* @__PURE__ */ jsxs(
297
412
  "div",
298
413
  {
299
414
  ref,
415
+ id: id || titleId,
300
416
  className: cn("flex items-center justify-between p-5 border-b border-[var(--ui-border-subtle)]", className),
301
417
  ...props,
302
418
  children: [
303
- /* @__PURE__ */ jsx("div", { className: "flex-1", children }),
419
+ /* @__PURE__ */ jsx("div", { className: "flex-1 font-semibold text-base text-[var(--ui-text-primary)]", children }),
304
420
  /* @__PURE__ */ jsx(
305
421
  "button",
306
422
  {
307
423
  type: "button",
308
424
  onClick: onClose,
309
425
  "aria-label": "Close drawer",
310
- className: "p-1.5 rounded-full text-[var(--ui-text-muted)] hover:text-[var(--ui-text-primary)] hover:bg-[var(--ui-bg-tertiary)] transition-colors ml-4",
426
+ className: "p-1.5 rounded-full text-[var(--ui-text-muted)] hover:text-[var(--ui-text-primary)] hover:bg-[var(--ui-bg-tertiary)] transition-colors ml-4 cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ui-primary)]",
311
427
  children: /* @__PURE__ */ jsx(X, { className: "w-4 h-4" })
312
428
  }
313
429
  )
@@ -358,40 +474,57 @@ function Tooltip({
358
474
  children,
359
475
  placement = "top",
360
476
  delayDuration = 200,
361
- className
477
+ className,
478
+ id: customId
362
479
  }) {
480
+ const generatedId = useId();
481
+ const tooltipId = customId || `tooltip-${generatedId}`;
363
482
  const [isVisible, setIsVisible] = useState(false);
364
483
  const [coords, setCoords] = useState({ top: 0, left: 0 });
365
484
  const timeoutRef = useRef(null);
366
485
  const triggerRef = useRef(null);
486
+ const calculatePosition = () => {
487
+ if (triggerRef.current) {
488
+ const rect = triggerRef.current.getBoundingClientRect();
489
+ let top = 0;
490
+ let left = 0;
491
+ if (placement === "top") {
492
+ top = rect.top + window.scrollY;
493
+ left = rect.left + rect.width / 2 + window.scrollX;
494
+ } else if (placement === "bottom") {
495
+ top = rect.bottom + window.scrollY;
496
+ left = rect.left + rect.width / 2 + window.scrollX;
497
+ } else if (placement === "left") {
498
+ top = rect.top + rect.height / 2 + window.scrollY;
499
+ left = rect.left + window.scrollX;
500
+ } else if (placement === "right") {
501
+ top = rect.top + rect.height / 2 + window.scrollY;
502
+ left = rect.right + window.scrollX;
503
+ }
504
+ setCoords({ top, left });
505
+ }
506
+ };
367
507
  const handleMouseEnter = () => {
508
+ if (timeoutRef.current) clearTimeout(timeoutRef.current);
368
509
  timeoutRef.current = setTimeout(() => {
369
- if (triggerRef.current) {
370
- const rect = triggerRef.current.getBoundingClientRect();
371
- let top = 0;
372
- let left = 0;
373
- if (placement === "top") {
374
- top = rect.top + window.scrollY;
375
- left = rect.left + rect.width / 2 + window.scrollX;
376
- } else if (placement === "bottom") {
377
- top = rect.bottom + window.scrollY;
378
- left = rect.left + rect.width / 2 + window.scrollX;
379
- } else if (placement === "left") {
380
- top = rect.top + rect.height / 2 + window.scrollY;
381
- left = rect.left + window.scrollX;
382
- } else if (placement === "right") {
383
- top = rect.top + rect.height / 2 + window.scrollY;
384
- left = rect.right + window.scrollX;
385
- }
386
- setCoords({ top, left });
387
- setIsVisible(true);
388
- }
510
+ calculatePosition();
511
+ setIsVisible(true);
389
512
  }, delayDuration);
390
513
  };
391
514
  const handleMouseLeave = () => {
392
515
  if (timeoutRef.current) clearTimeout(timeoutRef.current);
393
516
  setIsVisible(false);
394
517
  };
518
+ useEffect(() => {
519
+ if (!isVisible) return;
520
+ const handleKeyDown = (e) => {
521
+ if (e.key === "Escape") {
522
+ setIsVisible(false);
523
+ }
524
+ };
525
+ window.addEventListener("keydown", handleKeyDown);
526
+ return () => window.removeEventListener("keydown", handleKeyDown);
527
+ }, [isVisible]);
395
528
  return /* @__PURE__ */ jsxs(Fragment, { children: [
396
529
  /* @__PURE__ */ jsx(
397
530
  "div",
@@ -401,6 +534,7 @@ function Tooltip({
401
534
  onMouseLeave: handleMouseLeave,
402
535
  onFocus: handleMouseEnter,
403
536
  onBlur: handleMouseLeave,
537
+ "aria-describedby": isVisible ? tooltipId : void 0,
404
538
  className: "inline-flex cursor-pointer",
405
539
  children
406
540
  }
@@ -408,8 +542,9 @@ function Tooltip({
408
542
  isVisible && /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(
409
543
  "div",
410
544
  {
411
- style: { top: `${coords.top}px`, left: `${coords.left}px`, position: "absolute" },
545
+ id: tooltipId,
412
546
  role: "tooltip",
547
+ style: { top: `${coords.top}px`, left: `${coords.left}px`, position: "absolute" },
413
548
  className: cn(tooltipVariants({ placement }), className),
414
549
  children: content
415
550
  }
@@ -446,6 +581,38 @@ var toastIcons = {
446
581
  warning: /* @__PURE__ */ jsx(AlertTriangle, { className: "w-5 h-5 text-[var(--ui-warning)] flex-shrink-0" }),
447
582
  error: /* @__PURE__ */ jsx(AlertCircle, { className: "w-5 h-5 text-[var(--ui-error)] flex-shrink-0" })
448
583
  };
584
+ var Toast = React4.forwardRef(
585
+ ({ className, title, description, type = "info", onDismiss, ...props }, ref) => {
586
+ return /* @__PURE__ */ jsxs(
587
+ "div",
588
+ {
589
+ ref,
590
+ role: "status",
591
+ "aria-live": "polite",
592
+ className: cn(toastVariants({ type }), className),
593
+ ...props,
594
+ children: [
595
+ toastIcons[type || "info"],
596
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 space-y-1", children: [
597
+ /* @__PURE__ */ jsx("h4", { className: "text-sm font-semibold text-[var(--ui-text-primary)]", children: title }),
598
+ description && /* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--ui-text-muted)]", children: description })
599
+ ] }),
600
+ onDismiss && /* @__PURE__ */ jsx(
601
+ "button",
602
+ {
603
+ type: "button",
604
+ onClick: onDismiss,
605
+ "aria-label": "Dismiss toast",
606
+ className: "p-1 rounded text-[var(--ui-text-muted)] hover:text-[var(--ui-text-primary)] hover:bg-[var(--ui-bg-tertiary)] transition-colors cursor-pointer",
607
+ children: /* @__PURE__ */ jsx(X, { className: "w-3.5 h-3.5" })
608
+ }
609
+ )
610
+ ]
611
+ }
612
+ );
613
+ }
614
+ );
615
+ Toast.displayName = "Toast";
449
616
  function ToasterProvider({ children, position = "bottom-right" }) {
450
617
  const [toasts, setToasts] = useState([]);
451
618
  const dismiss = useCallback((id) => {
@@ -518,66 +685,86 @@ var BottomSheet = forwardRef(
518
685
  description,
519
686
  className,
520
687
  showHandle = true,
521
- maxHeight = "max-h-[85vh]"
688
+ maxHeight = "max-h-[85vh]",
689
+ "aria-label": ariaLabel
522
690
  }, ref) => {
523
- useRef(null);
691
+ const generatedId = useId();
692
+ const titleId = `bottomsheet-title-${generatedId}`;
693
+ const descId = `bottomsheet-desc-${generatedId}`;
524
694
  useEffect(() => {
695
+ if (!isOpen) return;
696
+ lockBodyScroll();
525
697
  const handleKeyDown = (e) => {
526
- if (e.key === "Escape" && isOpen) {
698
+ if (e.key === "Escape") {
527
699
  onClose();
528
700
  }
529
701
  };
530
702
  window.addEventListener("keydown", handleKeyDown);
531
- return () => window.removeEventListener("keydown", handleKeyDown);
703
+ return () => {
704
+ window.removeEventListener("keydown", handleKeyDown);
705
+ unlockBodyScroll();
706
+ };
532
707
  }, [isOpen, onClose]);
533
708
  if (!isOpen) return null;
534
- return /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(FocusTrap, { active: isOpen, children: /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-50 flex flex-col justify-end", children: [
535
- /* @__PURE__ */ jsx(
536
- "div",
537
- {
538
- onClick: onClose,
539
- className: "fixed inset-0 bg-black/60 backdrop-blur-md transition-opacity animate-in fade-in duration-300",
540
- "aria-hidden": "true"
541
- }
542
- ),
543
- /* @__PURE__ */ jsxs(
544
- "div",
545
- {
546
- ref,
547
- className: cn(
548
- "relative z-50 w-full max-w-2xl mx-auto rounded-t-[2rem] border-t border-x border-[var(--ui-border-default)] bg-[var(--ui-bg-surface)] backdrop-blur-3xl shadow-2xl overflow-hidden flex flex-col transition-transform animate-in slide-in-from-bottom duration-300",
549
- maxHeight,
550
- className
709
+ return /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(FocusTrap, { active: isOpen, restoreFocus: true, children: /* @__PURE__ */ jsxs(
710
+ "div",
711
+ {
712
+ className: "fixed inset-0 z-50 flex flex-col justify-end",
713
+ role: "dialog",
714
+ "aria-modal": "true",
715
+ "aria-label": ariaLabel,
716
+ "aria-labelledby": title ? titleId : void 0,
717
+ "aria-describedby": description ? descId : void 0,
718
+ children: [
719
+ /* @__PURE__ */ jsx(
720
+ "div",
721
+ {
722
+ onClick: onClose,
723
+ className: "fixed inset-0 bg-black/60 backdrop-blur-md transition-opacity animate-in fade-in duration-300",
724
+ "aria-hidden": "true"
725
+ }
551
726
  ),
552
- children: [
553
- showHandle && /* @__PURE__ */ jsx(
554
- "div",
555
- {
556
- onClick: onClose,
557
- className: "w-full py-3 flex items-center justify-center cursor-grab active:cursor-grabbing",
558
- children: /* @__PURE__ */ jsx("div", { className: "w-12 h-1.5 rounded-full bg-[var(--ui-text-muted)]/40 hover:bg-[var(--ui-text-muted)] transition-colors" })
559
- }
560
- ),
561
- (title || description) && /* @__PURE__ */ jsxs("div", { className: "px-6 pb-3 flex items-center justify-between border-b border-[var(--ui-border-subtle)]", children: [
562
- /* @__PURE__ */ jsxs("div", { children: [
563
- title && /* @__PURE__ */ jsx("h3", { className: "text-base font-bold tracking-tight text-[var(--ui-text-primary)]", children: title }),
564
- description && /* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--ui-text-muted)]", children: description })
565
- ] }),
566
- /* @__PURE__ */ jsx(
567
- "button",
568
- {
569
- type: "button",
570
- onClick: onClose,
571
- className: "p-1.5 rounded-full hover:bg-[var(--ui-bg-tertiary)] text-[var(--ui-text-muted)] hover:text-[var(--ui-text-primary)] transition-colors",
572
- children: /* @__PURE__ */ jsx(X, { className: "w-4 h-4" })
573
- }
574
- )
575
- ] }),
576
- /* @__PURE__ */ jsx("div", { className: "p-6 overflow-y-auto flex-1 space-y-4", children })
577
- ]
578
- }
579
- )
580
- ] }) }) });
727
+ /* @__PURE__ */ jsxs(
728
+ "div",
729
+ {
730
+ ref,
731
+ className: cn(
732
+ "relative z-50 w-full max-w-2xl mx-auto rounded-t-[2rem] border-t border-x border-[var(--ui-border-default)] bg-[var(--ui-bg-surface)] backdrop-blur-3xl shadow-2xl overflow-hidden flex flex-col transition-transform animate-in slide-in-from-bottom duration-300",
733
+ maxHeight,
734
+ className
735
+ ),
736
+ children: [
737
+ showHandle && /* @__PURE__ */ jsx(
738
+ "div",
739
+ {
740
+ onClick: onClose,
741
+ className: "w-full py-3 flex items-center justify-center cursor-grab active:cursor-grabbing",
742
+ children: /* @__PURE__ */ jsx("div", { className: "w-12 h-1.5 rounded-full bg-[var(--ui-text-muted)]/40 hover:bg-[var(--ui-text-muted)] transition-colors" })
743
+ }
744
+ ),
745
+ (title || description) && /* @__PURE__ */ jsxs("div", { className: "px-6 pb-3 flex items-center justify-between border-b border-[var(--ui-border-subtle)]", children: [
746
+ /* @__PURE__ */ jsxs("div", { children: [
747
+ title && /* @__PURE__ */ jsx("h3", { id: titleId, className: "text-base font-bold tracking-tight text-[var(--ui-text-primary)]", children: title }),
748
+ description && /* @__PURE__ */ jsx("p", { id: descId, className: "text-xs text-[var(--ui-text-muted)]", children: description })
749
+ ] }),
750
+ /* @__PURE__ */ jsx(
751
+ "button",
752
+ {
753
+ type: "button",
754
+ onClick: onClose,
755
+ "aria-label": "Close sheet",
756
+ className: "p-1.5 rounded-full hover:bg-[var(--ui-bg-tertiary)] text-[var(--ui-text-muted)] hover:text-[var(--ui-text-primary)] transition-colors cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ui-primary)]",
757
+ children: /* @__PURE__ */ jsx(X, { className: "w-4 h-4" })
758
+ }
759
+ )
760
+ ] }),
761
+ /* @__PURE__ */ jsx("div", { className: "p-6 overflow-y-auto flex-1 space-y-4", children })
762
+ ]
763
+ }
764
+ )
765
+ ]
766
+ }
767
+ ) }) });
581
768
  }
582
769
  );
583
770
  BottomSheet.displayName = "BottomSheet";
@@ -598,14 +785,21 @@ var ContextMenuItem = ({
598
785
  "div",
599
786
  {
600
787
  role: "menuitem",
788
+ tabIndex: disabled ? -1 : 0,
601
789
  "aria-disabled": disabled,
602
790
  onClick: () => {
603
791
  if (!disabled) onSelect?.();
604
792
  },
793
+ onKeyDown: (e) => {
794
+ if (!disabled && (e.key === "Enter" || e.key === " ")) {
795
+ e.preventDefault();
796
+ onSelect?.();
797
+ }
798
+ },
605
799
  className: cn(
606
- "flex items-center justify-between px-3 py-1.5 rounded-xl text-xs font-medium cursor-pointer transition-colors select-none",
800
+ "flex items-center justify-between px-3 py-1.5 rounded-xl text-xs font-medium cursor-pointer transition-colors select-none focus:outline-none focus:bg-[var(--ui-bg-tertiary)]",
607
801
  disabled && "opacity-40 cursor-not-allowed pointer-events-none",
608
- destructive ? "text-rose-500 hover:bg-rose-500/10" : "text-[var(--ui-text-primary)] hover:bg-[var(--ui-bg-tertiary)]",
802
+ destructive ? "text-rose-500 hover:bg-rose-500/10 focus:bg-rose-500/10" : "text-[var(--ui-text-primary)] hover:bg-[var(--ui-bg-tertiary)]",
609
803
  className
610
804
  ),
611
805
  ...props,
@@ -633,20 +827,47 @@ var ContextMenu = forwardRef(
633
827
  setIsOpen(true);
634
828
  };
635
829
  useEffect(() => {
830
+ if (!isOpen) return;
831
+ const timer = setTimeout(() => {
832
+ const firstItem = menuRef.current?.querySelector('[role="menuitem"]:not([aria-disabled="true"])');
833
+ firstItem?.focus();
834
+ }, 10);
636
835
  const handleClickOutside = (e) => {
637
836
  if (menuRef.current && !menuRef.current.contains(e.target)) {
638
837
  setIsOpen(false);
639
838
  }
640
839
  };
641
840
  const handleKeyDown = (e) => {
642
- if (e.key === "Escape") setIsOpen(false);
841
+ if (!menuRef.current) return;
842
+ const items = Array.from(
843
+ menuRef.current.querySelectorAll('[role="menuitem"]:not([aria-disabled="true"])')
844
+ );
845
+ if (items.length === 0) return;
846
+ const currentIndex = items.indexOf(document.activeElement);
847
+ if (e.key === "ArrowDown") {
848
+ e.preventDefault();
849
+ const nextIndex = currentIndex === -1 ? 0 : (currentIndex + 1) % items.length;
850
+ items[nextIndex]?.focus();
851
+ } else if (e.key === "ArrowUp") {
852
+ e.preventDefault();
853
+ const prevIndex = currentIndex === -1 ? items.length - 1 : (currentIndex - 1 + items.length) % items.length;
854
+ items[prevIndex]?.focus();
855
+ } else if (e.key === "Home") {
856
+ e.preventDefault();
857
+ items[0]?.focus();
858
+ } else if (e.key === "End") {
859
+ e.preventDefault();
860
+ items[items.length - 1]?.focus();
861
+ } else if (e.key === "Escape") {
862
+ e.preventDefault();
863
+ setIsOpen(false);
864
+ }
643
865
  };
644
- if (isOpen) {
645
- window.addEventListener("click", handleClickOutside);
646
- window.addEventListener("contextmenu", handleClickOutside);
647
- window.addEventListener("keydown", handleKeyDown);
648
- }
866
+ window.addEventListener("click", handleClickOutside);
867
+ window.addEventListener("contextmenu", handleClickOutside);
868
+ window.addEventListener("keydown", handleKeyDown);
649
869
  return () => {
870
+ clearTimeout(timer);
650
871
  window.removeEventListener("click", handleClickOutside);
651
872
  window.removeEventListener("contextmenu", handleClickOutside);
652
873
  window.removeEventListener("keydown", handleKeyDown);
@@ -658,9 +879,11 @@ var ContextMenu = forwardRef(
658
879
  "div",
659
880
  {
660
881
  ref: menuRef,
882
+ role: "menu",
883
+ tabIndex: -1,
661
884
  style: { top: `${position.y}px`, left: `${position.x}px` },
662
885
  onClick: () => setIsOpen(false),
663
- className: "fixed z-50 min-w-48 p-1.5 rounded-2xl bg-[var(--ui-bg-surface)]/90 backdrop-blur-3xl border border-[var(--ui-border-default)] shadow-2xl space-y-0.5 animate-in fade-in zoom-in-95 duration-150",
886
+ className: "fixed z-50 min-w-48 p-1.5 rounded-2xl bg-[var(--ui-bg-surface)]/90 backdrop-blur-3xl border border-[var(--ui-border-default)] shadow-2xl space-y-0.5 animate-in fade-in zoom-in-95 duration-150 focus:outline-none",
664
887
  children: menuContent
665
888
  }
666
889
  ) })
@@ -723,6 +946,6 @@ var HoverCard = forwardRef(
723
946
  );
724
947
  HoverCard.displayName = "HoverCard";
725
948
 
726
- export { AlertDialog, BottomSheet, BottomSheetBody, BottomSheetFooter, BottomSheetHeader, ContextMenu, ContextMenuItem, ContextMenuSeparator, Drawer, DrawerBody, DrawerFooter, DrawerHeader, HoverCard, Modal, ModalBody, ModalCloseButton, ModalContent, ModalDescription, ModalFooter, ModalHeader, ModalOverlay, ModalTitle, ToasterProvider, Tooltip, drawerVariants, modalContentVariants, overlays_exports, toastVariants, tooltipVariants, useDrawer, useModal, useToast };
727
- //# sourceMappingURL=chunk-5FZ6ZFII.js.map
728
- //# sourceMappingURL=chunk-5FZ6ZFII.js.map
949
+ export { AlertDialog, BottomSheet, BottomSheetBody, BottomSheetFooter, BottomSheetHeader, ContextMenu, ContextMenuItem, ContextMenuSeparator, Drawer, DrawerBody, DrawerFooter, DrawerHeader, HoverCard, Modal, ModalBody, ModalCloseButton, ModalContent, ModalDescription, ModalFooter, ModalHeader, ModalOverlay, ModalTitle, Toast, ToasterProvider, Tooltip, drawerVariants, modalContentVariants, overlays_exports, toastVariants, tooltipVariants, useDrawer, useModal, useToast };
950
+ //# sourceMappingURL=chunk-PC5SZFZC.js.map
951
+ //# sourceMappingURL=chunk-PC5SZFZC.js.map