@a11y-core/react 0.1.0-alpha.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 (70) hide show
  1. package/README.md +252 -0
  2. package/dist/chunk-3WBTHTVK.js +41 -0
  3. package/dist/chunk-3WBTHTVK.js.map +1 -0
  4. package/dist/chunk-47MFBHV6.js +85 -0
  5. package/dist/chunk-47MFBHV6.js.map +1 -0
  6. package/dist/chunk-7A3IDIUB.cjs +89 -0
  7. package/dist/chunk-7A3IDIUB.cjs.map +1 -0
  8. package/dist/chunk-AZFZWGI6.cjs +383 -0
  9. package/dist/chunk-AZFZWGI6.cjs.map +1 -0
  10. package/dist/chunk-CTW5D77X.cjs +220 -0
  11. package/dist/chunk-CTW5D77X.cjs.map +1 -0
  12. package/dist/chunk-EY73HQNR.js +380 -0
  13. package/dist/chunk-EY73HQNR.js.map +1 -0
  14. package/dist/chunk-FJABCNFE.js +215 -0
  15. package/dist/chunk-FJABCNFE.js.map +1 -0
  16. package/dist/chunk-FTZ5KCOO.js +326 -0
  17. package/dist/chunk-FTZ5KCOO.js.map +1 -0
  18. package/dist/chunk-GS3H4T2O.cjs +106 -0
  19. package/dist/chunk-GS3H4T2O.cjs.map +1 -0
  20. package/dist/chunk-N6L4GLFC.cjs +45 -0
  21. package/dist/chunk-N6L4GLFC.cjs.map +1 -0
  22. package/dist/chunk-N774QCHE.js +234 -0
  23. package/dist/chunk-N774QCHE.js.map +1 -0
  24. package/dist/chunk-OSHIYZCZ.cjs +386 -0
  25. package/dist/chunk-OSHIYZCZ.cjs.map +1 -0
  26. package/dist/chunk-SRJSGDIA.js +373 -0
  27. package/dist/chunk-SRJSGDIA.js.map +1 -0
  28. package/dist/chunk-THB5U7YC.cjs +338 -0
  29. package/dist/chunk-THB5U7YC.cjs.map +1 -0
  30. package/dist/chunk-U6DUSMEA.js +99 -0
  31. package/dist/chunk-U6DUSMEA.js.map +1 -0
  32. package/dist/chunk-WDCYEMBO.cjs +245 -0
  33. package/dist/chunk-WDCYEMBO.cjs.map +1 -0
  34. package/dist/components/combobox/index.cjs +31 -0
  35. package/dist/components/combobox/index.cjs.map +1 -0
  36. package/dist/components/combobox/index.d.cts +55 -0
  37. package/dist/components/combobox/index.d.ts +55 -0
  38. package/dist/components/combobox/index.js +6 -0
  39. package/dist/components/combobox/index.js.map +1 -0
  40. package/dist/components/dialog/index.cjs +46 -0
  41. package/dist/components/dialog/index.cjs.map +1 -0
  42. package/dist/components/dialog/index.d.cts +84 -0
  43. package/dist/components/dialog/index.d.ts +84 -0
  44. package/dist/components/dialog/index.js +5 -0
  45. package/dist/components/dialog/index.js.map +1 -0
  46. package/dist/components/menu/index.cjs +46 -0
  47. package/dist/components/menu/index.cjs.map +1 -0
  48. package/dist/components/menu/index.d.cts +80 -0
  49. package/dist/components/menu/index.d.ts +80 -0
  50. package/dist/components/menu/index.js +5 -0
  51. package/dist/components/menu/index.js.map +1 -0
  52. package/dist/components/tabs/index.cjs +35 -0
  53. package/dist/components/tabs/index.cjs.map +1 -0
  54. package/dist/components/tabs/index.d.cts +65 -0
  55. package/dist/components/tabs/index.d.ts +65 -0
  56. package/dist/components/tabs/index.js +6 -0
  57. package/dist/components/tabs/index.js.map +1 -0
  58. package/dist/components/toast/index.cjs +24 -0
  59. package/dist/components/toast/index.cjs.map +1 -0
  60. package/dist/components/toast/index.d.cts +49 -0
  61. package/dist/components/toast/index.d.ts +49 -0
  62. package/dist/components/toast/index.js +3 -0
  63. package/dist/components/toast/index.js.map +1 -0
  64. package/dist/index.cjs +698 -0
  65. package/dist/index.cjs.map +1 -0
  66. package/dist/index.d.cts +402 -0
  67. package/dist/index.d.ts +402 -0
  68. package/dist/index.js +426 -0
  69. package/dist/index.js.map +1 -0
  70. package/package.json +89 -0
@@ -0,0 +1,326 @@
1
+ import { useAnnouncer } from './chunk-47MFBHV6.js';
2
+ import { useId } from './chunk-3WBTHTVK.js';
3
+ import { createContext, forwardRef, useEffect, useContext, useState, useCallback, useRef } from 'react';
4
+ import { createPortal } from 'react-dom';
5
+ import { createComponentWarnings, createFocusTrap } from '@a11y-core/core';
6
+ import { jsx } from 'react/jsx-runtime';
7
+
8
+ function useFocusTrap(options = {}) {
9
+ const { active = true, ...trapOptions } = options;
10
+ const containerRef = useRef(null);
11
+ const trapRef = useRef(null);
12
+ useEffect(() => {
13
+ const container = containerRef.current;
14
+ if (!container) return;
15
+ trapRef.current = createFocusTrap(container, trapOptions);
16
+ if (active) {
17
+ trapRef.current.activate();
18
+ }
19
+ return () => {
20
+ trapRef.current?.destroy();
21
+ trapRef.current = null;
22
+ };
23
+ }, [
24
+ trapOptions.initialFocus,
25
+ trapOptions.returnFocus,
26
+ trapOptions.clickOutsideDeactivates,
27
+ trapOptions.escapeDeactivates,
28
+ trapOptions.onDeactivate
29
+ ]);
30
+ useEffect(() => {
31
+ if (!trapRef.current) return;
32
+ if (active) {
33
+ trapRef.current.activate();
34
+ } else {
35
+ trapRef.current.deactivate();
36
+ }
37
+ }, [active]);
38
+ return containerRef;
39
+ }
40
+ function useFocusTrapControls(options = {}) {
41
+ const containerRef = useRef(null);
42
+ const trapRef = useRef(null);
43
+ const activate = useCallback(() => {
44
+ const container = containerRef.current;
45
+ if (!container) return;
46
+ if (!trapRef.current) {
47
+ trapRef.current = createFocusTrap(container, options);
48
+ }
49
+ trapRef.current.activate();
50
+ }, [options]);
51
+ const deactivate = useCallback(() => {
52
+ trapRef.current?.deactivate();
53
+ }, []);
54
+ const pause = useCallback(() => {
55
+ trapRef.current?.pause();
56
+ }, []);
57
+ const unpause = useCallback(() => {
58
+ trapRef.current?.unpause();
59
+ }, []);
60
+ useEffect(() => {
61
+ return () => {
62
+ trapRef.current?.destroy();
63
+ trapRef.current = null;
64
+ };
65
+ }, []);
66
+ return {
67
+ ref: containerRef,
68
+ activate,
69
+ deactivate,
70
+ pause,
71
+ unpause,
72
+ isActive: () => trapRef.current?.isActive() ?? false,
73
+ isPaused: () => trapRef.current?.isPaused() ?? false
74
+ };
75
+ }
76
+ var DialogContext = createContext(null);
77
+ function useDialogContext() {
78
+ const context = useContext(DialogContext);
79
+ if (!context) {
80
+ throw new Error(
81
+ "Dialog compound components must be used within a Dialog component"
82
+ );
83
+ }
84
+ return context;
85
+ }
86
+ var DialogProvider = DialogContext.Provider;
87
+ var warnings = createComponentWarnings("Dialog");
88
+ function Dialog({
89
+ open,
90
+ onOpenChange,
91
+ children,
92
+ className,
93
+ initialFocus,
94
+ closeOnOutsideClick = true,
95
+ closeOnEscape = true,
96
+ container,
97
+ "aria-label": ariaLabel,
98
+ "aria-labelledby": ariaLabelledBy,
99
+ unstyled = false
100
+ }) {
101
+ const dialogId = useId("dialog");
102
+ const titleId = useId("dialog-title");
103
+ const descriptionId = useId("dialog-desc");
104
+ const [hasTitle, setHasTitle] = useState(false);
105
+ const [hasDescription, setHasDescription] = useState(false);
106
+ const close = useCallback(() => {
107
+ onOpenChange(false);
108
+ }, [onOpenChange]);
109
+ useEffect(() => {
110
+ if (open && !hasTitle && !ariaLabel && !ariaLabelledBy) {
111
+ warnings.warning(
112
+ "Dialog has no accessible title. Add a DialogTitle or aria-label prop.",
113
+ 'Use <Dialog.Title> or provide aria-label="..."'
114
+ );
115
+ }
116
+ }, [open, hasTitle, ariaLabel, ariaLabelledBy]);
117
+ const contextValue = {
118
+ isOpen: open,
119
+ close,
120
+ dialogId,
121
+ titleId,
122
+ descriptionId,
123
+ hasTitle,
124
+ hasDescription,
125
+ setHasTitle,
126
+ setHasDescription
127
+ };
128
+ if (!open) {
129
+ return null;
130
+ }
131
+ const dialogContent = /* @__PURE__ */ jsx(DialogProvider, { value: contextValue, children: /* @__PURE__ */ jsx(
132
+ DialogOverlay,
133
+ {
134
+ className,
135
+ closeOnOutsideClick,
136
+ closeOnEscape,
137
+ initialFocus,
138
+ ariaLabel,
139
+ ariaLabelledBy,
140
+ unstyled,
141
+ children
142
+ }
143
+ ) });
144
+ const portalContainer = container ?? document.body;
145
+ return createPortal(dialogContent, portalContainer);
146
+ }
147
+ function DialogOverlay({
148
+ children,
149
+ className,
150
+ closeOnOutsideClick,
151
+ closeOnEscape,
152
+ initialFocus,
153
+ ariaLabel,
154
+ ariaLabelledBy,
155
+ unstyled
156
+ }) {
157
+ const { close, dialogId, titleId, descriptionId, hasTitle, hasDescription } = useDialogContext();
158
+ const { announce } = useAnnouncer();
159
+ const trapRef = useFocusTrap({
160
+ active: true,
161
+ initialFocus: initialFocus?.current ?? void 0,
162
+ escapeDeactivates: closeOnEscape,
163
+ // Don't use clickOutsideDeactivates - we handle this in handleOverlayClick
164
+ clickOutsideDeactivates: false,
165
+ onDeactivate: close
166
+ });
167
+ useEffect(() => {
168
+ announce("Dialog opened", { politeness: "polite" });
169
+ return () => {
170
+ announce("Dialog closed", { politeness: "polite" });
171
+ };
172
+ }, [announce]);
173
+ useEffect(() => {
174
+ const originalOverflow = document.body.style.overflow;
175
+ document.body.style.overflow = "hidden";
176
+ return () => {
177
+ document.body.style.overflow = originalOverflow;
178
+ };
179
+ }, []);
180
+ const handleOverlayClick = (event) => {
181
+ if (closeOnOutsideClick && event.target === event.currentTarget) {
182
+ close();
183
+ }
184
+ };
185
+ const labelledBy = ariaLabelledBy ?? (hasTitle ? titleId : void 0);
186
+ const describedBy = hasDescription ? descriptionId : void 0;
187
+ const handleDialogClick = (event) => {
188
+ event.stopPropagation();
189
+ };
190
+ const overlayStructuralStyles = {
191
+ position: "fixed",
192
+ inset: 0,
193
+ display: "flex",
194
+ alignItems: "center",
195
+ justifyContent: "center",
196
+ zIndex: 9999
197
+ };
198
+ const overlayVisualStyles = unstyled ? {} : {
199
+ backgroundColor: "rgba(0, 0, 0, 0.5)"
200
+ };
201
+ const overlayStyles = {
202
+ ...overlayStructuralStyles,
203
+ ...overlayVisualStyles
204
+ };
205
+ const dialogStyles = unstyled ? {} : {
206
+ backgroundColor: "white",
207
+ borderRadius: "8px",
208
+ padding: "1.5rem",
209
+ minWidth: "300px",
210
+ maxWidth: "500px",
211
+ boxShadow: "0 10px 25px rgba(0, 0, 0, 0.2)"
212
+ };
213
+ return /* @__PURE__ */ jsx(
214
+ "div",
215
+ {
216
+ className,
217
+ style: overlayStyles,
218
+ onClick: handleOverlayClick,
219
+ "data-a11y-core-dialog-overlay": true,
220
+ children: /* @__PURE__ */ jsx(
221
+ "div",
222
+ {
223
+ ref: trapRef,
224
+ id: dialogId,
225
+ role: "dialog",
226
+ "aria-modal": "true",
227
+ "aria-label": ariaLabel,
228
+ "aria-labelledby": labelledBy,
229
+ "aria-describedby": describedBy,
230
+ onClick: handleDialogClick,
231
+ style: dialogStyles,
232
+ "data-a11y-core-dialog": true,
233
+ children
234
+ }
235
+ )
236
+ }
237
+ );
238
+ }
239
+ var DialogTrigger = forwardRef(
240
+ function DialogTrigger2({ children, ...props }, ref) {
241
+ return /* @__PURE__ */ jsx(
242
+ "button",
243
+ {
244
+ ref,
245
+ type: "button",
246
+ tabIndex: 0,
247
+ "data-a11y-core-dialog-trigger": true,
248
+ ...props,
249
+ children
250
+ }
251
+ );
252
+ }
253
+ );
254
+ var DialogTitle = forwardRef(
255
+ function DialogTitle2({ as: Component = "h2", children, ...props }, ref) {
256
+ const { titleId, setHasTitle } = useDialogContext();
257
+ useEffect(() => {
258
+ setHasTitle(true);
259
+ return () => setHasTitle(false);
260
+ }, [setHasTitle]);
261
+ return /* @__PURE__ */ jsx(Component, { ref, id: titleId, "data-a11y-core-dialog-title": true, ...props, children });
262
+ }
263
+ );
264
+ var DialogDescription = forwardRef(function DialogDescription2({ children, ...props }, ref) {
265
+ const { descriptionId, setHasDescription } = useDialogContext();
266
+ useEffect(() => {
267
+ setHasDescription(true);
268
+ return () => setHasDescription(false);
269
+ }, [setHasDescription]);
270
+ return /* @__PURE__ */ jsx(
271
+ "p",
272
+ {
273
+ ref,
274
+ id: descriptionId,
275
+ "data-a11y-core-dialog-description": true,
276
+ ...props,
277
+ children
278
+ }
279
+ );
280
+ });
281
+ var DialogClose = forwardRef(
282
+ function DialogClose2({ children, onClick, ...props }, ref) {
283
+ const { close } = useDialogContext();
284
+ const handleClick = (event) => {
285
+ onClick?.(event);
286
+ if (!event.defaultPrevented) {
287
+ close();
288
+ }
289
+ };
290
+ return /* @__PURE__ */ jsx(
291
+ "button",
292
+ {
293
+ ref,
294
+ type: "button",
295
+ tabIndex: 0,
296
+ onClick: handleClick,
297
+ "aria-label": children ? void 0 : "Close dialog",
298
+ "data-a11y-core-dialog-close": true,
299
+ ...props,
300
+ children: children ?? "\xD7"
301
+ }
302
+ );
303
+ }
304
+ );
305
+ var DialogContent = forwardRef(
306
+ function DialogContent2({ children, ...props }, ref) {
307
+ return /* @__PURE__ */ jsx("div", { ref, "data-a11y-core-dialog-content": true, ...props, children });
308
+ }
309
+ );
310
+ var DialogActions = forwardRef(
311
+ function DialogActions2({ children, ...props }, ref) {
312
+ return /* @__PURE__ */ jsx("div", { ref, "data-a11y-core-dialog-actions": true, ...props, children });
313
+ }
314
+ );
315
+ var DialogCompound = Object.assign(Dialog, {
316
+ Trigger: DialogTrigger,
317
+ Title: DialogTitle,
318
+ Description: DialogDescription,
319
+ Close: DialogClose,
320
+ Content: DialogContent,
321
+ Actions: DialogActions
322
+ });
323
+
324
+ export { Dialog, DialogActions, DialogClose, DialogCompound, DialogContent, DialogDescription, DialogTitle, DialogTrigger, useDialogContext, useFocusTrap, useFocusTrapControls };
325
+ //# sourceMappingURL=chunk-FTZ5KCOO.js.map
326
+ //# sourceMappingURL=chunk-FTZ5KCOO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/hooks/use-focus-trap.ts","../src/components/dialog/dialog-context.ts","../src/components/dialog/dialog.tsx"],"names":["useCallback","useEffect","DialogTrigger","DialogTitle","DialogDescription","DialogClose","DialogContent","DialogActions"],"mappings":"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","file":"chunk-FTZ5KCOO.js","sourcesContent":["import { useEffect, useRef, useCallback } from 'react';\nimport { createFocusTrap, type FocusTrapOptions } from '@a11y-core/core';\n\nexport interface UseFocusTrapOptions extends FocusTrapOptions {\n /** Whether the focus trap is active */\n active?: boolean;\n}\n\n/**\n * Hook to create a focus trap for modals, dialogs, etc.\n *\n * @example\n * ```tsx\n * function Modal({ isOpen, onClose }) {\n * const trapRef = useFocusTrap({\n * active: isOpen,\n * onDeactivate: onClose,\n * });\n *\n * return (\n * <div ref={trapRef} role=\"dialog\">\n * <button>Close</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useFocusTrap<T extends HTMLElement = HTMLDivElement>(\n options: UseFocusTrapOptions = {}\n): React.RefObject<T> {\n const { active = true, ...trapOptions } = options;\n const containerRef = useRef<T>(null);\n const trapRef = useRef<ReturnType<typeof createFocusTrap> | null>(null);\n\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n\n trapRef.current = createFocusTrap(container, trapOptions);\n\n if (active) {\n trapRef.current.activate();\n }\n\n return () => {\n // Use destroy() instead of deactivate() to avoid calling onDeactivate\n // during cleanup (which would cause issues with React Strict Mode)\n trapRef.current?.destroy();\n trapRef.current = null;\n };\n }, [\n trapOptions.initialFocus,\n trapOptions.returnFocus,\n trapOptions.clickOutsideDeactivates,\n trapOptions.escapeDeactivates,\n trapOptions.onDeactivate,\n ]);\n\n useEffect(() => {\n if (!trapRef.current) return;\n\n if (active) {\n trapRef.current.activate();\n } else {\n trapRef.current.deactivate();\n }\n }, [active]);\n\n return containerRef;\n}\n\n/**\n * Imperative focus trap controls\n */\nexport function useFocusTrapControls<T extends HTMLElement = HTMLDivElement>(\n options: Omit<FocusTrapOptions, 'onDeactivate'> = {}\n) {\n const containerRef = useRef<T | null>(null);\n const trapRef = useRef<ReturnType<typeof createFocusTrap> | null>(null);\n\n const activate = useCallback(() => {\n const container = containerRef.current;\n if (!container) return;\n\n if (!trapRef.current) {\n trapRef.current = createFocusTrap(container, options);\n }\n trapRef.current.activate();\n }, [options]);\n\n const deactivate = useCallback(() => {\n trapRef.current?.deactivate();\n }, []);\n\n const pause = useCallback(() => {\n trapRef.current?.pause();\n }, []);\n\n const unpause = useCallback(() => {\n trapRef.current?.unpause();\n }, []);\n\n useEffect(() => {\n return () => {\n // Use destroy() for cleanup to avoid calling onDeactivate during unmount\n trapRef.current?.destroy();\n trapRef.current = null;\n };\n }, []);\n\n return {\n ref: containerRef,\n activate,\n deactivate,\n pause,\n unpause,\n isActive: () => trapRef.current?.isActive() ?? false,\n isPaused: () => trapRef.current?.isPaused() ?? false,\n };\n}\n","import { createContext, useContext } from 'react';\n\nexport interface DialogContextValue {\n /** Whether the dialog is open */\n isOpen: boolean;\n /** Close the dialog */\n close: () => void;\n /** ID for the dialog element */\n dialogId: string;\n /** ID for the title element */\n titleId: string;\n /** ID for the description element */\n descriptionId: string;\n /** Whether the dialog has a visible title */\n hasTitle: boolean;\n /** Whether the dialog has a visible description */\n hasDescription: boolean;\n /** Set whether title is rendered */\n setHasTitle: (value: boolean) => void;\n /** Set whether description is rendered */\n setHasDescription: (value: boolean) => void;\n}\n\nconst DialogContext = createContext<DialogContextValue | null>(null);\n\nexport function useDialogContext(): DialogContextValue {\n const context = useContext(DialogContext);\n if (!context) {\n throw new Error(\n 'Dialog compound components must be used within a Dialog component'\n );\n }\n return context;\n}\n\nexport const DialogProvider = DialogContext.Provider;\n","import React, { forwardRef, useCallback, useEffect, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport { useId } from '../../hooks/use-id';\nimport { useFocusTrap } from '../../hooks/use-focus-trap';\nimport { useAnnouncer } from '../../hooks/use-announcer';\nimport { DialogProvider, useDialogContext } from './dialog-context';\nimport { createComponentWarnings } from '@a11y-core/core';\n\nconst warnings = createComponentWarnings('Dialog');\n\nexport interface DialogProps {\n /** Whether the dialog is open */\n open: boolean;\n /** Called when the dialog should close */\n onOpenChange: (open: boolean) => void;\n /** The dialog content */\n children: React.ReactNode;\n /** Custom class name */\n className?: string;\n /** Element to focus when dialog opens */\n initialFocus?: React.RefObject<HTMLElement>;\n /** Whether clicking outside closes the dialog */\n closeOnOutsideClick?: boolean;\n /** Whether pressing Escape closes the dialog */\n closeOnEscape?: boolean;\n /** Portal container (defaults to document.body) */\n container?: HTMLElement;\n /** Accessible label (required if no DialogTitle) */\n 'aria-label'?: string;\n /** ID of element that labels the dialog */\n 'aria-labelledby'?: string;\n /** Remove default styles to allow full customization via className */\n unstyled?: boolean;\n}\n\nexport function Dialog({\n open,\n onOpenChange,\n children,\n className,\n initialFocus,\n closeOnOutsideClick = true,\n closeOnEscape = true,\n container,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n unstyled = false,\n}: DialogProps) {\n const dialogId = useId('dialog');\n const titleId = useId('dialog-title');\n const descriptionId = useId('dialog-desc');\n\n const [hasTitle, setHasTitle] = useState(false);\n const [hasDescription, setHasDescription] = useState(false);\n\n const close = useCallback(() => {\n onOpenChange(false);\n }, [onOpenChange]);\n\n // Warn if no accessible label\n useEffect(() => {\n if (open && !hasTitle && !ariaLabel && !ariaLabelledBy) {\n warnings.warning(\n 'Dialog has no accessible title. Add a DialogTitle or aria-label prop.',\n 'Use <Dialog.Title> or provide aria-label=\"...\"'\n );\n }\n }, [open, hasTitle, ariaLabel, ariaLabelledBy]);\n\n const contextValue = {\n isOpen: open,\n close,\n dialogId,\n titleId,\n descriptionId,\n hasTitle,\n hasDescription,\n setHasTitle,\n setHasDescription,\n };\n\n if (!open) {\n return null;\n }\n\n const dialogContent = (\n <DialogProvider value={contextValue}>\n <DialogOverlay\n className={className}\n closeOnOutsideClick={closeOnOutsideClick}\n closeOnEscape={closeOnEscape}\n initialFocus={initialFocus}\n ariaLabel={ariaLabel}\n ariaLabelledBy={ariaLabelledBy}\n unstyled={unstyled}\n >\n {children}\n </DialogOverlay>\n </DialogProvider>\n );\n\n const portalContainer = container ?? document.body;\n return createPortal(dialogContent, portalContainer);\n}\n\ninterface DialogOverlayProps {\n children: React.ReactNode;\n className?: string;\n closeOnOutsideClick: boolean;\n closeOnEscape: boolean;\n initialFocus?: React.RefObject<HTMLElement>;\n ariaLabel?: string;\n ariaLabelledBy?: string;\n unstyled: boolean;\n}\n\nfunction DialogOverlay({\n children,\n className,\n closeOnOutsideClick,\n closeOnEscape,\n initialFocus,\n ariaLabel,\n ariaLabelledBy,\n unstyled,\n}: DialogOverlayProps) {\n const { close, dialogId, titleId, descriptionId, hasTitle, hasDescription } =\n useDialogContext();\n const { announce } = useAnnouncer();\n\n const trapRef = useFocusTrap<HTMLDivElement>({\n active: true,\n initialFocus: initialFocus?.current ?? undefined,\n escapeDeactivates: closeOnEscape,\n // Don't use clickOutsideDeactivates - we handle this in handleOverlayClick\n clickOutsideDeactivates: false,\n onDeactivate: close,\n });\n\n useEffect(() => {\n announce('Dialog opened', { politeness: 'polite' });\n return () => {\n announce('Dialog closed', { politeness: 'polite' });\n };\n }, [announce]);\n\n useEffect(() => {\n const originalOverflow = document.body.style.overflow;\n document.body.style.overflow = 'hidden';\n return () => {\n document.body.style.overflow = originalOverflow;\n };\n }, []);\n\n const handleOverlayClick = (event: React.MouseEvent) => {\n if (closeOnOutsideClick && event.target === event.currentTarget) {\n close();\n }\n };\n\n const labelledBy = ariaLabelledBy ?? (hasTitle ? titleId : undefined);\n const describedBy = hasDescription ? descriptionId : undefined;\n\n const handleDialogClick = (event: React.MouseEvent) => {\n // Prevent clicks inside dialog from bubbling to overlay\n event.stopPropagation();\n };\n\n const overlayStructuralStyles: React.CSSProperties = {\n position: 'fixed',\n inset: 0,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n zIndex: 9999,\n };\n\n // Visual styles - only applied when not unstyled\n const overlayVisualStyles: React.CSSProperties = unstyled\n ? {}\n : {\n backgroundColor: 'rgba(0, 0, 0, 0.5)',\n };\n\n const overlayStyles: React.CSSProperties = {\n ...overlayStructuralStyles,\n ...overlayVisualStyles,\n };\n\n // Visual styles for dialog panel - only applied when not unstyled\n const dialogStyles: React.CSSProperties = unstyled\n ? {}\n : {\n backgroundColor: 'white',\n borderRadius: '8px',\n padding: '1.5rem',\n minWidth: '300px',\n maxWidth: '500px',\n boxShadow: '0 10px 25px rgba(0, 0, 0, 0.2)',\n };\n\n return (\n <div\n className={className}\n style={overlayStyles}\n onClick={handleOverlayClick}\n data-a11y-core-dialog-overlay\n >\n <div\n ref={trapRef}\n id={dialogId}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={ariaLabel}\n aria-labelledby={labelledBy}\n aria-describedby={describedBy}\n onClick={handleDialogClick}\n style={dialogStyles}\n data-a11y-core-dialog\n >\n {children}\n </div>\n </div>\n );\n}\n\nexport interface DialogTriggerProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n children: React.ReactNode;\n}\n\nexport const DialogTrigger = forwardRef<HTMLButtonElement, DialogTriggerProps>(\n function DialogTrigger({ children, ...props }, ref) {\n return (\n <button\n ref={ref}\n type=\"button\"\n // Safari fix: Ensure button is in tab order (Safari skips buttons by default)\n tabIndex={0}\n data-a11y-core-dialog-trigger\n {...props}\n >\n {children}\n </button>\n );\n }\n);\n\nexport interface DialogTitleProps extends React.HTMLAttributes<HTMLHeadingElement> {\n as?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';\n children: React.ReactNode;\n}\n\nexport const DialogTitle = forwardRef<HTMLHeadingElement, DialogTitleProps>(\n function DialogTitle({ as: Component = 'h2', children, ...props }, ref) {\n const { titleId, setHasTitle } = useDialogContext();\n\n useEffect(() => {\n setHasTitle(true);\n return () => setHasTitle(false);\n }, [setHasTitle]);\n\n return (\n <Component ref={ref} id={titleId} data-a11y-core-dialog-title {...props}>\n {children}\n </Component>\n );\n }\n);\n\nexport interface DialogDescriptionProps extends React.HTMLAttributes<HTMLParagraphElement> {\n children: React.ReactNode;\n}\n\nexport const DialogDescription = forwardRef<\n HTMLParagraphElement,\n DialogDescriptionProps\n>(function DialogDescription({ children, ...props }, ref) {\n const { descriptionId, setHasDescription } = useDialogContext();\n\n useEffect(() => {\n setHasDescription(true);\n return () => setHasDescription(false);\n }, [setHasDescription]);\n\n return (\n <p\n ref={ref}\n id={descriptionId}\n data-a11y-core-dialog-description\n {...props}\n >\n {children}\n </p>\n );\n});\n\nexport interface DialogCloseProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n children?: React.ReactNode;\n}\n\nexport const DialogClose = forwardRef<HTMLButtonElement, DialogCloseProps>(\n function DialogClose({ children, onClick, ...props }, ref) {\n const { close } = useDialogContext();\n\n const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {\n onClick?.(event);\n if (!event.defaultPrevented) {\n close();\n }\n };\n\n return (\n <button\n ref={ref}\n type=\"button\"\n // Safari fix: Ensure button is in tab order (Safari skips buttons by default)\n tabIndex={0}\n onClick={handleClick}\n aria-label={children ? undefined : 'Close dialog'}\n data-a11y-core-dialog-close\n {...props}\n >\n {children ?? '×'}\n </button>\n );\n }\n);\n\nexport interface DialogContentProps extends React.HTMLAttributes<HTMLDivElement> {\n children: React.ReactNode;\n}\n\nexport const DialogContent = forwardRef<HTMLDivElement, DialogContentProps>(\n function DialogContent({ children, ...props }, ref) {\n return (\n <div ref={ref} data-a11y-core-dialog-content {...props}>\n {children}\n </div>\n );\n }\n);\n\nexport interface DialogActionsProps extends React.HTMLAttributes<HTMLDivElement> {\n children: React.ReactNode;\n}\n\nexport const DialogActions = forwardRef<HTMLDivElement, DialogActionsProps>(\n function DialogActions({ children, ...props }, ref) {\n return (\n <div ref={ref} data-a11y-core-dialog-actions {...props}>\n {children}\n </div>\n );\n }\n);\n\nexport const DialogCompound = Object.assign(Dialog, {\n Trigger: DialogTrigger,\n Title: DialogTitle,\n Description: DialogDescription,\n Close: DialogClose,\n Content: DialogContent,\n Actions: DialogActions,\n});\n"]}
@@ -0,0 +1,106 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var core = require('@a11y-core/core');
5
+
6
+ // src/hooks/use-keyboard.ts
7
+ function useKeyboard(handlers, options = {}) {
8
+ const { preventDefault = true, stopPropagation = true, disabled = false } = options;
9
+ const handlersRef = react.useRef(handlers);
10
+ handlersRef.current = handlers;
11
+ const handleKeyDown = react.useCallback(
12
+ (event) => {
13
+ if (disabled) return;
14
+ const combo = core.getKeyCombo(event.nativeEvent);
15
+ let handler = handlersRef.current[combo];
16
+ if (!handler) {
17
+ const key = core.normalizeKey(event.nativeEvent);
18
+ handler = handlersRef.current[key];
19
+ }
20
+ if (handler) {
21
+ const result = handler(event.nativeEvent);
22
+ if (result !== false) {
23
+ if (preventDefault) {
24
+ event.preventDefault();
25
+ }
26
+ if (stopPropagation) {
27
+ event.stopPropagation();
28
+ }
29
+ }
30
+ }
31
+ },
32
+ [disabled, preventDefault, stopPropagation]
33
+ );
34
+ return {
35
+ onKeyDown: handleKeyDown
36
+ };
37
+ }
38
+ function useMenuKeyboard(options) {
39
+ const { disabled, ...handlers } = options;
40
+ return useKeyboard(core.KeyboardPatterns.menu(handlers), { disabled });
41
+ }
42
+ function useTabsKeyboard(options) {
43
+ const { disabled, ...handlers } = options;
44
+ return useKeyboard(core.KeyboardPatterns.tabs(handlers), { disabled });
45
+ }
46
+ function useGridKeyboard(options) {
47
+ const { disabled, ...handlers } = options;
48
+ return useKeyboard(core.KeyboardPatterns.grid(handlers), { disabled });
49
+ }
50
+ function useTypeAhead(items, onMatch, options = {}) {
51
+ const { timeout = 500, disabled = false } = options;
52
+ const typeAhead = react.useMemo(
53
+ () => core.createTypeAhead(items, { timeout }),
54
+ [items, timeout]
55
+ );
56
+ const onMatchRef = react.useRef(onMatch);
57
+ onMatchRef.current = onMatch;
58
+ const handleKeyDown = react.useCallback(
59
+ (event) => {
60
+ if (disabled) return;
61
+ if (event.key.length !== 1) return;
62
+ if (event.ctrlKey || event.altKey || event.metaKey) return;
63
+ const match = typeAhead.type(event.key);
64
+ if (match) {
65
+ onMatchRef.current(match);
66
+ }
67
+ },
68
+ [disabled, typeAhead]
69
+ );
70
+ return {
71
+ onKeyDown: handleKeyDown,
72
+ reset: typeAhead.reset,
73
+ getSearch: typeAhead.getSearch
74
+ };
75
+ }
76
+ function useKeyPressed(targetKey) {
77
+ const [pressed, setPressed] = react.useState(false);
78
+ react.useEffect(() => {
79
+ const handleKeyDown = (event) => {
80
+ if (core.normalizeKey(event) === targetKey) {
81
+ setPressed(true);
82
+ }
83
+ };
84
+ const handleKeyUp = (event) => {
85
+ if (core.normalizeKey(event) === targetKey) {
86
+ setPressed(false);
87
+ }
88
+ };
89
+ document.addEventListener("keydown", handleKeyDown);
90
+ document.addEventListener("keyup", handleKeyUp);
91
+ return () => {
92
+ document.removeEventListener("keydown", handleKeyDown);
93
+ document.removeEventListener("keyup", handleKeyUp);
94
+ };
95
+ }, [targetKey]);
96
+ return pressed;
97
+ }
98
+
99
+ exports.useGridKeyboard = useGridKeyboard;
100
+ exports.useKeyPressed = useKeyPressed;
101
+ exports.useKeyboard = useKeyboard;
102
+ exports.useMenuKeyboard = useMenuKeyboard;
103
+ exports.useTabsKeyboard = useTabsKeyboard;
104
+ exports.useTypeAhead = useTypeAhead;
105
+ //# sourceMappingURL=chunk-GS3H4T2O.cjs.map
106
+ //# sourceMappingURL=chunk-GS3H4T2O.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/hooks/use-keyboard.ts"],"names":["useRef","useCallback","getKeyCombo","normalizeKey","KeyboardPatterns","useMemo","createTypeAhead","useState","useEffect"],"mappings":"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","file":"chunk-GS3H4T2O.cjs","sourcesContent":["import { useCallback, useEffect, useMemo, useRef } from 'react';\nimport {\n KeyboardPatterns,\n createTypeAhead,\n normalizeKey,\n getKeyCombo,\n type KeyboardHandlers,\n} from '@a11y-core/core';\n\n/**\n * Hook for keyboard event handling\n *\n * @example\n * ```tsx\n * function Menu({ items, onSelect }) {\n * const keyboardProps = useKeyboard({\n * ArrowDown: () => focusNext(),\n * ArrowUp: () => focusPrevious(),\n * Enter: () => onSelect(focused),\n * Escape: () => close(),\n * });\n *\n * return <ul {...keyboardProps}>...</ul>;\n * }\n * ```\n */\nexport function useKeyboard(\n handlers: KeyboardHandlers,\n options: {\n preventDefault?: boolean;\n stopPropagation?: boolean;\n disabled?: boolean;\n } = {}\n) {\n const { preventDefault = true, stopPropagation = true, disabled = false } = options;\n\n // Memoize handlers to prevent unnecessary re-renders\n const handlersRef = useRef(handlers);\n handlersRef.current = handlers;\n\n const handleKeyDown = useCallback(\n (event: React.KeyboardEvent) => {\n if (disabled) return;\n\n // Try key combo first\n const combo = getKeyCombo(event.nativeEvent);\n let handler = handlersRef.current[combo];\n\n // Fall back to simple key\n if (!handler) {\n const key = normalizeKey(event.nativeEvent);\n handler = handlersRef.current[key];\n }\n\n if (handler) {\n const result = handler(event.nativeEvent);\n if (result !== false) {\n if (preventDefault) {\n event.preventDefault();\n }\n if (stopPropagation) {\n event.stopPropagation();\n }\n }\n }\n },\n [disabled, preventDefault, stopPropagation]\n );\n\n return {\n onKeyDown: handleKeyDown,\n };\n}\n\n/**\n * Pre-built keyboard patterns for common widgets\n */\nexport function useMenuKeyboard(options: {\n onUp?: () => void;\n onDown?: () => void;\n onEnter?: () => void;\n onEscape?: () => void;\n onHome?: () => void;\n onEnd?: () => void;\n disabled?: boolean;\n}) {\n const { disabled, ...handlers } = options;\n return useKeyboard(KeyboardPatterns.menu(handlers), { disabled });\n}\n\nexport function useTabsKeyboard(options: {\n onLeft?: () => void;\n onRight?: () => void;\n onHome?: () => void;\n onEnd?: () => void;\n disabled?: boolean;\n}) {\n const { disabled, ...handlers } = options;\n return useKeyboard(KeyboardPatterns.tabs(handlers), { disabled });\n}\n\nexport function useGridKeyboard(options: {\n onUp?: () => void;\n onDown?: () => void;\n onLeft?: () => void;\n onRight?: () => void;\n onHome?: () => void;\n onEnd?: () => void;\n onCtrlHome?: () => void;\n onCtrlEnd?: () => void;\n disabled?: boolean;\n}) {\n const { disabled, ...handlers } = options;\n return useKeyboard(KeyboardPatterns.grid(handlers), { disabled });\n}\n\n/**\n * Hook for type-ahead search in menus/listboxes\n *\n * @example\n * ```tsx\n * function Listbox({ items }) {\n * const { onKeyDown, reset } = useTypeAhead(\n * items.map(i => i.label),\n * (match) => focusItem(match)\n * );\n *\n * return <ul onKeyDown={onKeyDown}>...</ul>;\n * }\n * ```\n */\nexport function useTypeAhead(\n items: string[],\n onMatch: (match: string) => void,\n options: { timeout?: number; disabled?: boolean } = {}\n) {\n const { timeout = 500, disabled = false } = options;\n\n const typeAhead = useMemo(\n () => createTypeAhead(items, { timeout }),\n [items, timeout]\n );\n\n const onMatchRef = useRef(onMatch);\n onMatchRef.current = onMatch;\n\n const handleKeyDown = useCallback(\n (event: React.KeyboardEvent) => {\n if (disabled) return;\n\n // Only handle single character keys\n if (event.key.length !== 1) return;\n\n // Ignore if modifier keys are pressed\n if (event.ctrlKey || event.altKey || event.metaKey) return;\n\n const match = typeAhead.type(event.key);\n if (match) {\n onMatchRef.current(match);\n }\n },\n [disabled, typeAhead]\n );\n\n return {\n onKeyDown: handleKeyDown,\n reset: typeAhead.reset,\n getSearch: typeAhead.getSearch,\n };\n}\n\n/**\n * Hook for tracking which key is currently pressed\n * Useful for showing keyboard shortcuts or modifier states\n */\nexport function useKeyPressed(\n targetKey: string\n): boolean {\n const [pressed, setPressed] = useState(false);\n\n useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n if (normalizeKey(event) === targetKey) {\n setPressed(true);\n }\n };\n\n const handleKeyUp = (event: KeyboardEvent) => {\n if (normalizeKey(event) === targetKey) {\n setPressed(false);\n }\n };\n\n document.addEventListener('keydown', handleKeyDown);\n document.addEventListener('keyup', handleKeyUp);\n\n return () => {\n document.removeEventListener('keydown', handleKeyDown);\n document.removeEventListener('keyup', handleKeyUp);\n };\n }, [targetKey]);\n\n return pressed;\n}\n\n// Need to import useState\nimport { useState } from 'react';\n"]}
@@ -0,0 +1,45 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+
5
+ // src/hooks/use-id.ts
6
+ function useId(prefix) {
7
+ const reactId = react.useId();
8
+ return react.useMemo(() => {
9
+ const cleanId = reactId.replace(/:/g, "");
10
+ return prefix ? `a11y-core-${prefix}-${cleanId}` : `a11y-core-${cleanId}`;
11
+ }, [reactId, prefix]);
12
+ }
13
+ function useIds(parts, prefix) {
14
+ const baseId = useId(prefix);
15
+ return react.useMemo(() => {
16
+ const ids = {};
17
+ for (const part of parts) {
18
+ ids[part] = `${baseId}-${part}`;
19
+ }
20
+ return ids;
21
+ }, [baseId, parts]);
22
+ }
23
+ function useIdScope(componentName) {
24
+ const scopeId = useId(componentName);
25
+ return react.useMemo(
26
+ () => ({
27
+ id: scopeId,
28
+ generate: (suffix) => `${scopeId}-${suffix}`,
29
+ generateMultiple: (parts) => {
30
+ const ids = {};
31
+ for (const part of parts) {
32
+ ids[part] = `${scopeId}-${part}`;
33
+ }
34
+ return ids;
35
+ }
36
+ }),
37
+ [scopeId]
38
+ );
39
+ }
40
+
41
+ exports.useId = useId;
42
+ exports.useIdScope = useIdScope;
43
+ exports.useIds = useIds;
44
+ //# sourceMappingURL=chunk-N6L4GLFC.cjs.map
45
+ //# sourceMappingURL=chunk-N6L4GLFC.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/hooks/use-id.ts"],"names":["useReactId","useMemo"],"mappings":";;;;;AAOO,SAAS,MAAM,MAAA,EAAyB;AAE7C,EAAA,MAAM,UAAUA,WAAA,EAAW;AAE3B,EAAA,OAAOC,cAAQ,MAAM;AAEnB,IAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,OAAA,CAAQ,IAAA,EAAM,EAAE,CAAA;AACxC,IAAA,OAAO,SAAS,CAAA,UAAA,EAAa,MAAM,IAAI,OAAO,CAAA,CAAA,GAAK,aAAa,OAAO,CAAA,CAAA;AAAA,EACzE,CAAA,EAAG,CAAC,OAAA,EAAS,MAAM,CAAC,CAAA;AACtB;AAKO,SAAS,MAAA,CACd,OACA,MAAA,EAC2B;AAC3B,EAAA,MAAM,MAAA,GAAS,MAAM,MAAM,CAAA;AAE3B,EAAA,OAAOA,cAAQ,MAAM;AACnB,IAAA,MAAM,MAAM,EAAC;AACb,IAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,MAAA,GAAA,CAAI,IAAI,CAAA,GAAI,CAAA,EAAG,MAAM,IAAI,IAAI,CAAA,CAAA;AAAA,IAC/B;AACA,IAAA,OAAO,GAAA;AAAA,EACT,CAAA,EAAG,CAAC,MAAA,EAAQ,KAAK,CAAC,CAAA;AACpB;AAKO,SAAS,WAAW,aAAA,EAAuB;AAChD,EAAA,MAAM,OAAA,GAAU,MAAM,aAAa,CAAA;AAEnC,EAAA,OAAOA,aAAA;AAAA,IACL,OAAO;AAAA,MACL,EAAA,EAAI,OAAA;AAAA,MACJ,UAAU,CAAC,MAAA,KAAmB,CAAA,EAAG,OAAO,IAAI,MAAM,CAAA,CAAA;AAAA,MAClD,gBAAA,EAAkB,CAA8B,KAAA,KAAa;AAC3D,QAAA,MAAM,MAAM,EAAC;AACb,QAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,UAAA,GAAA,CAAI,IAAI,CAAA,GAAI,CAAA,EAAG,OAAO,IAAI,IAAI,CAAA,CAAA;AAAA,QAChC;AACA,QAAA,OAAO,GAAA;AAAA,MACT;AAAA,KACF,CAAA;AAAA,IACA,CAAC,OAAO;AAAA,GACV;AACF","file":"chunk-N6L4GLFC.cjs","sourcesContent":["import { useId as useReactId, useMemo } from 'react';\n\n/**\n * Generate a unique ID for accessibility purposes\n *\n * Uses React's useId when available, with a fallback for SSR\n */\nexport function useId(prefix?: string): string {\n // Use React 18's useId as the base\n const reactId = useReactId();\n\n return useMemo(() => {\n // Clean up React's ID format (removes colons)\n const cleanId = reactId.replace(/:/g, '');\n return prefix ? `a11y-core-${prefix}-${cleanId}` : `a11y-core-${cleanId}`;\n }, [reactId, prefix]);\n}\n\n/**\n * Generate multiple related IDs for a component\n */\nexport function useIds<T extends readonly string[]>(\n parts: T,\n prefix?: string\n): Record<T[number], string> {\n const baseId = useId(prefix);\n\n return useMemo(() => {\n const ids = {} as Record<string, string>;\n for (const part of parts) {\n ids[part] = `${baseId}-${part}`;\n }\n return ids as Record<T[number], string>;\n }, [baseId, parts]);\n}\n\n/**\n * Create a scoped ID generator for complex components\n */\nexport function useIdScope(componentName: string) {\n const scopeId = useId(componentName);\n\n return useMemo(\n () => ({\n id: scopeId,\n generate: (suffix: string) => `${scopeId}-${suffix}`,\n generateMultiple: <T extends readonly string[]>(parts: T) => {\n const ids = {} as Record<string, string>;\n for (const part of parts) {\n ids[part] = `${scopeId}-${part}`;\n }\n return ids as Record<T[number], string>;\n },\n }),\n [scopeId]\n );\n}\n"]}