@compa11y/react 0.1.0 → 0.1.3

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 (76) hide show
  1. package/README.md +487 -5
  2. package/dist/chunk-36S2JYVF.cjs +1 -0
  3. package/dist/chunk-AJ7JSWUT.cjs +1 -0
  4. package/dist/chunk-DDFEION3.cjs +1 -0
  5. package/dist/chunk-DWU3PTJO.cjs +1 -0
  6. package/dist/chunk-FD4F6ONU.cjs +1 -0
  7. package/dist/chunk-FOVHQAY5.cjs +1 -0
  8. package/dist/chunk-GITBIGD4.js +1 -0
  9. package/dist/chunk-HEA4NAOM.js +1 -0
  10. package/dist/chunk-IZ7LLPPV.js +1 -0
  11. package/dist/chunk-JS3UD7KS.cjs +1 -0
  12. package/dist/chunk-MAR6RBHF.cjs +1 -0
  13. package/dist/chunk-MD4AVTLT.js +1 -0
  14. package/dist/chunk-SB6ASQ36.js +1 -0
  15. package/dist/chunk-VMM4K2K4.js +1 -0
  16. package/dist/chunk-XEJXACWE.js +1 -0
  17. package/dist/chunk-ZB3SYGHE.js +1 -0
  18. package/dist/components/combobox/index.cjs +1 -31
  19. package/dist/components/combobox/index.js +1 -6
  20. package/dist/components/dialog/index.cjs +1 -46
  21. package/dist/components/dialog/index.js +1 -5
  22. package/dist/components/menu/index.cjs +1 -46
  23. package/dist/components/menu/index.js +1 -5
  24. package/dist/components/tabs/index.cjs +1 -35
  25. package/dist/components/tabs/index.js +1 -6
  26. package/dist/components/toast/index.cjs +1 -24
  27. package/dist/components/toast/index.js +1 -3
  28. package/dist/index.cjs +1 -702
  29. package/dist/index.d.cts +760 -3
  30. package/dist/index.d.ts +760 -3
  31. package/dist/index.js +1 -430
  32. package/package.json +44 -3
  33. package/dist/chunk-2S4C6FGA.js +0 -380
  34. package/dist/chunk-2S4C6FGA.js.map +0 -1
  35. package/dist/chunk-52J4Z3QD.cjs +0 -45
  36. package/dist/chunk-52J4Z3QD.cjs.map +0 -1
  37. package/dist/chunk-C7QK2I7H.js +0 -373
  38. package/dist/chunk-C7QK2I7H.js.map +0 -1
  39. package/dist/chunk-D2UMS62N.cjs +0 -245
  40. package/dist/chunk-D2UMS62N.cjs.map +0 -1
  41. package/dist/chunk-E265U2RK.js +0 -234
  42. package/dist/chunk-E265U2RK.js.map +0 -1
  43. package/dist/chunk-E4XJRXWM.js +0 -215
  44. package/dist/chunk-E4XJRXWM.js.map +0 -1
  45. package/dist/chunk-GDLOJH6K.cjs +0 -110
  46. package/dist/chunk-GDLOJH6K.cjs.map +0 -1
  47. package/dist/chunk-IR46CNNY.cjs +0 -329
  48. package/dist/chunk-IR46CNNY.cjs.map +0 -1
  49. package/dist/chunk-JXYOE7SH.js +0 -103
  50. package/dist/chunk-JXYOE7SH.js.map +0 -1
  51. package/dist/chunk-O3YYQZ5O.js +0 -317
  52. package/dist/chunk-O3YYQZ5O.js.map +0 -1
  53. package/dist/chunk-OIVTOU4Z.cjs +0 -386
  54. package/dist/chunk-OIVTOU4Z.cjs.map +0 -1
  55. package/dist/chunk-OND5B7UG.js +0 -85
  56. package/dist/chunk-OND5B7UG.js.map +0 -1
  57. package/dist/chunk-R4FR6M6I.cjs +0 -383
  58. package/dist/chunk-R4FR6M6I.cjs.map +0 -1
  59. package/dist/chunk-RBDQCIS7.cjs +0 -89
  60. package/dist/chunk-RBDQCIS7.cjs.map +0 -1
  61. package/dist/chunk-SOBS7MIH.cjs +0 -220
  62. package/dist/chunk-SOBS7MIH.cjs.map +0 -1
  63. package/dist/chunk-WURPAE3R.js +0 -41
  64. package/dist/chunk-WURPAE3R.js.map +0 -1
  65. package/dist/components/combobox/index.cjs.map +0 -1
  66. package/dist/components/combobox/index.js.map +0 -1
  67. package/dist/components/dialog/index.cjs.map +0 -1
  68. package/dist/components/dialog/index.js.map +0 -1
  69. package/dist/components/menu/index.cjs.map +0 -1
  70. package/dist/components/menu/index.js.map +0 -1
  71. package/dist/components/tabs/index.cjs.map +0 -1
  72. package/dist/components/tabs/index.js.map +0 -1
  73. package/dist/components/toast/index.cjs.map +0 -1
  74. package/dist/components/toast/index.js.map +0 -1
  75. package/dist/index.cjs.map +0 -1
  76. package/dist/index.js.map +0 -1
@@ -1,220 +0,0 @@
1
- 'use strict';
2
-
3
- var react = require('react');
4
- var reactDom = require('react-dom');
5
- var core = require('@compa11y/core');
6
- var jsxRuntime = require('react/jsx-runtime');
7
-
8
- // src/components/toast/toast.tsx
9
- var ToastContext = react.createContext(null);
10
- function useToast() {
11
- const context = react.useContext(ToastContext);
12
- if (!context) {
13
- throw new Error("useToast must be used within a ToastProvider");
14
- }
15
- return context;
16
- }
17
- function ToastProvider({
18
- children,
19
- duration = 5e3,
20
- maxToasts = 5
21
- }) {
22
- const [toasts, setToasts] = react.useState([]);
23
- const toastIdCounter = react.useRef(0);
24
- const addToast = react.useCallback(
25
- (toast) => {
26
- const id = `toast-${++toastIdCounter.current}`;
27
- const newToast = {
28
- ...toast,
29
- id,
30
- duration: toast.duration ?? duration
31
- };
32
- setToasts((prev) => {
33
- const updated = [...prev, newToast];
34
- return updated.slice(-maxToasts);
35
- });
36
- const message = toast.title ? `${toast.title}. ${toast.description || ""}` : toast.description || "";
37
- if (toast.type === "error") {
38
- core.announceAssertive(message);
39
- } else {
40
- core.announce(message, { politeness: "polite" });
41
- }
42
- return id;
43
- },
44
- [duration, maxToasts]
45
- );
46
- const removeToast = react.useCallback((id) => {
47
- setToasts((prev) => prev.filter((t) => t.id !== id));
48
- }, []);
49
- const updateToast = react.useCallback(
50
- (id, updates) => {
51
- setToasts(
52
- (prev) => prev.map((t) => t.id === id ? { ...t, ...updates } : t)
53
- );
54
- },
55
- []
56
- );
57
- return /* @__PURE__ */ jsxRuntime.jsx(
58
- ToastContext.Provider,
59
- {
60
- value: { toasts, addToast, removeToast, updateToast },
61
- children
62
- }
63
- );
64
- }
65
- var positionStyles = {
66
- "top-left": { top: 0, left: 0 },
67
- "top-center": { top: 0, left: "50%", transform: "translateX(-50%)" },
68
- "top-right": { top: 0, right: 0 },
69
- "bottom-left": { bottom: 0, left: 0 },
70
- "bottom-center": { bottom: 0, left: "50%", transform: "translateX(-50%)" },
71
- "bottom-right": { bottom: 0, right: 0 }
72
- };
73
- var ToastViewport = react.forwardRef(
74
- function ToastViewport2({
75
- position = "bottom-right",
76
- label = "Notifications",
77
- style,
78
- children,
79
- ...props
80
- }, ref) {
81
- const { toasts, removeToast } = useToast();
82
- const viewport = /* @__PURE__ */ jsxRuntime.jsxs(
83
- "div",
84
- {
85
- ref,
86
- role: "region",
87
- "aria-label": label,
88
- "aria-live": "polite",
89
- "aria-relevant": "additions removals",
90
- tabIndex: -1,
91
- style: {
92
- position: "fixed",
93
- zIndex: 9999,
94
- padding: "1rem",
95
- display: "flex",
96
- flexDirection: "column",
97
- gap: "0.5rem",
98
- ...positionStyles[position],
99
- ...style
100
- },
101
- "data-compa11y-toast-viewport": true,
102
- "data-position": position,
103
- ...props,
104
- children: [
105
- toasts.map((toast) => /* @__PURE__ */ jsxRuntime.jsx(
106
- ToastItem,
107
- {
108
- toast,
109
- onClose: () => removeToast(toast.id)
110
- },
111
- toast.id
112
- )),
113
- children
114
- ]
115
- }
116
- );
117
- return reactDom.createPortal(viewport, document.body);
118
- }
119
- );
120
- function ToastItem({ toast, onClose }) {
121
- const [isVisible, setIsVisible] = react.useState(true);
122
- const [, setIsPaused] = react.useState(false);
123
- const timerRef = react.useRef(null);
124
- const remainingRef = react.useRef(toast.duration || 5e3);
125
- const startTimeRef = react.useRef(Date.now());
126
- const startTimer = react.useCallback(() => {
127
- if (toast.duration === 0) return;
128
- startTimeRef.current = Date.now();
129
- timerRef.current = setTimeout(() => {
130
- setIsVisible(false);
131
- setTimeout(onClose, 200);
132
- }, remainingRef.current);
133
- }, [toast.duration, onClose]);
134
- const pauseTimer = react.useCallback(() => {
135
- if (timerRef.current) {
136
- clearTimeout(timerRef.current);
137
- remainingRef.current -= Date.now() - startTimeRef.current;
138
- }
139
- }, []);
140
- react.useEffect(() => {
141
- startTimer();
142
- return () => {
143
- if (timerRef.current) {
144
- clearTimeout(timerRef.current);
145
- }
146
- };
147
- }, [startTimer]);
148
- const handleMouseEnter = () => {
149
- setIsPaused(true);
150
- pauseTimer();
151
- };
152
- const handleMouseLeave = () => {
153
- setIsPaused(false);
154
- startTimer();
155
- };
156
- const handleKeyDown = (event) => {
157
- if (event.key === "Escape") {
158
- onClose();
159
- }
160
- };
161
- return /* @__PURE__ */ jsxRuntime.jsxs(
162
- "div",
163
- {
164
- role: "alert",
165
- "aria-atomic": "true",
166
- tabIndex: 0,
167
- onMouseEnter: handleMouseEnter,
168
- onMouseLeave: handleMouseLeave,
169
- onKeyDown: handleKeyDown,
170
- "data-type": toast.type,
171
- "data-visible": isVisible,
172
- "data-compa11y-toast": true,
173
- children: [
174
- toast.title && /* @__PURE__ */ jsxRuntime.jsx("div", { "data-compa11y-toast-title": true, children: toast.title }),
175
- toast.description && /* @__PURE__ */ jsxRuntime.jsx("div", { "data-compa11y-toast-description": true, children: toast.description }),
176
- toast.action && /* @__PURE__ */ jsxRuntime.jsx(
177
- "button",
178
- {
179
- type: "button",
180
- tabIndex: 0,
181
- onClick: () => {
182
- toast.action?.onClick();
183
- onClose();
184
- },
185
- "data-compa11y-toast-action": true,
186
- children: toast.action.label
187
- }
188
- ),
189
- /* @__PURE__ */ jsxRuntime.jsx(
190
- "button",
191
- {
192
- type: "button",
193
- tabIndex: 0,
194
- "aria-label": "Dismiss",
195
- onClick: onClose,
196
- "data-compa11y-toast-close": true,
197
- children: "\xD7"
198
- }
199
- )
200
- ]
201
- }
202
- );
203
- }
204
- function useToastHelpers() {
205
- const { addToast } = useToast();
206
- return {
207
- toast: addToast,
208
- success: (title, description) => addToast({ type: "success", title, description }),
209
- error: (title, description) => addToast({ type: "error", title, description }),
210
- warning: (title, description) => addToast({ type: "warning", title, description }),
211
- info: (title, description) => addToast({ type: "info", title, description })
212
- };
213
- }
214
-
215
- exports.ToastProvider = ToastProvider;
216
- exports.ToastViewport = ToastViewport;
217
- exports.useToast = useToast;
218
- exports.useToastHelpers = useToastHelpers;
219
- //# sourceMappingURL=chunk-SOBS7MIH.cjs.map
220
- //# sourceMappingURL=chunk-SOBS7MIH.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/toast/toast.tsx"],"names":["createContext","useContext","useState","useRef","useCallback","announceAssertive","announce","jsx","forwardRef","ToastViewport","jsxs","createPortal","useEffect"],"mappings":"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","file":"chunk-SOBS7MIH.cjs","sourcesContent":["import React, {\n createContext,\n forwardRef,\n useCallback,\n useContext,\n useEffect,\n useRef,\n useState,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport { announce, announceAssertive } from '@compa11y/core';\nexport type ToastType = 'info' | 'success' | 'warning' | 'error';\n\nexport interface Toast {\n id: string;\n title?: string;\n description?: string;\n type: ToastType;\n duration?: number;\n action?: {\n label: string;\n onClick: () => void;\n };\n}\n\ninterface ToastContextValue {\n toasts: Toast[];\n addToast: (toast: Omit<Toast, 'id'>) => string;\n removeToast: (id: string) => void;\n updateToast: (id: string, toast: Partial<Omit<Toast, 'id'>>) => void;\n}\n\nconst ToastContext = createContext<ToastContextValue | null>(null);\n\nexport function useToast() {\n const context = useContext(ToastContext);\n if (!context) {\n throw new Error('useToast must be used within a ToastProvider');\n }\n return context;\n}\n\nexport interface ToastProviderProps {\n children: React.ReactNode;\n /** Default duration for toasts in ms */\n duration?: number;\n /** Maximum number of visible toasts */\n maxToasts?: number;\n}\n\nexport function ToastProvider({\n children,\n duration = 5000,\n maxToasts = 5,\n}: ToastProviderProps) {\n const [toasts, setToasts] = useState<Toast[]>([]);\n const toastIdCounter = useRef(0);\n\n const addToast = useCallback(\n (toast: Omit<Toast, 'id'>): string => {\n const id = `toast-${++toastIdCounter.current}`;\n const newToast: Toast = {\n ...toast,\n id,\n duration: toast.duration ?? duration,\n };\n\n setToasts((prev) => {\n const updated = [...prev, newToast];\n // Limit visible toasts\n return updated.slice(-maxToasts);\n });\n\n // Announce to screen readers\n const message = toast.title\n ? `${toast.title}. ${toast.description || ''}`\n : toast.description || '';\n\n if (toast.type === 'error') {\n announceAssertive(message);\n } else {\n announce(message, { politeness: 'polite' });\n }\n\n return id;\n },\n [duration, maxToasts]\n );\n\n const removeToast = useCallback((id: string) => {\n setToasts((prev) => prev.filter((t) => t.id !== id));\n }, []);\n\n const updateToast = useCallback(\n (id: string, updates: Partial<Omit<Toast, 'id'>>) => {\n setToasts((prev) =>\n prev.map((t) => (t.id === id ? { ...t, ...updates } : t))\n );\n },\n []\n );\n\n return (\n <ToastContext.Provider\n value={{ toasts, addToast, removeToast, updateToast }}\n >\n {children}\n </ToastContext.Provider>\n );\n}\n\nexport interface ToastViewportProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Position of the toast container */\n position?:\n | 'top-left'\n | 'top-center'\n | 'top-right'\n | 'bottom-left'\n | 'bottom-center'\n | 'bottom-right';\n /** Label for screen readers */\n label?: string;\n}\n\nconst positionStyles: Record<string, React.CSSProperties> = {\n 'top-left': { top: 0, left: 0 },\n 'top-center': { top: 0, left: '50%', transform: 'translateX(-50%)' },\n 'top-right': { top: 0, right: 0 },\n 'bottom-left': { bottom: 0, left: 0 },\n 'bottom-center': { bottom: 0, left: '50%', transform: 'translateX(-50%)' },\n 'bottom-right': { bottom: 0, right: 0 },\n};\n\nexport const ToastViewport = forwardRef<HTMLDivElement, ToastViewportProps>(\n function ToastViewport(\n {\n position = 'bottom-right',\n label = 'Notifications',\n style,\n children,\n ...props\n },\n ref\n ) {\n const { toasts, removeToast } = useToast();\n\n const viewport = (\n <div\n ref={ref}\n role=\"region\"\n aria-label={label}\n aria-live=\"polite\"\n aria-relevant=\"additions removals\"\n tabIndex={-1}\n style={{\n position: 'fixed',\n zIndex: 9999,\n padding: '1rem',\n display: 'flex',\n flexDirection: 'column',\n gap: '0.5rem',\n ...positionStyles[position],\n ...style,\n }}\n data-compa11y-toast-viewport\n data-position={position}\n {...props}\n >\n {toasts.map((toast) => (\n <ToastItem\n key={toast.id}\n toast={toast}\n onClose={() => removeToast(toast.id)}\n />\n ))}\n {children}\n </div>\n );\n\n return createPortal(viewport, document.body);\n }\n);\n\ninterface ToastItemProps {\n toast: Toast;\n onClose: () => void;\n}\n\nfunction ToastItem({ toast, onClose }: ToastItemProps) {\n const [isVisible, setIsVisible] = useState(true);\n const [, setIsPaused] = useState(false);\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const remainingRef = useRef(toast.duration || 5000);\n const startTimeRef = useRef(Date.now());\n\n const startTimer = useCallback(() => {\n if (toast.duration === 0) return; // Infinite duration\n\n startTimeRef.current = Date.now();\n timerRef.current = setTimeout(() => {\n setIsVisible(false);\n setTimeout(onClose, 200); // Allow exit animation\n }, remainingRef.current);\n }, [toast.duration, onClose]);\n\n const pauseTimer = useCallback(() => {\n if (timerRef.current) {\n clearTimeout(timerRef.current);\n remainingRef.current -= Date.now() - startTimeRef.current;\n }\n }, []);\n\n useEffect(() => {\n startTimer();\n return () => {\n if (timerRef.current) {\n clearTimeout(timerRef.current);\n }\n };\n }, [startTimer]);\n\n const handleMouseEnter = () => {\n setIsPaused(true);\n pauseTimer();\n };\n\n const handleMouseLeave = () => {\n setIsPaused(false);\n startTimer();\n };\n\n const handleKeyDown = (event: React.KeyboardEvent) => {\n if (event.key === 'Escape') {\n onClose();\n }\n };\n\n return (\n <div\n role=\"alert\"\n aria-atomic=\"true\"\n tabIndex={0}\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n onKeyDown={handleKeyDown}\n data-type={toast.type}\n data-visible={isVisible}\n data-compa11y-toast\n >\n {toast.title && <div data-compa11y-toast-title>{toast.title}</div>}\n {toast.description && (\n <div data-compa11y-toast-description>{toast.description}</div>\n )}\n {toast.action && (\n <button\n type=\"button\"\n // Safari fix: Ensure button is in tab order\n tabIndex={0}\n onClick={() => {\n toast.action?.onClick();\n onClose();\n }}\n data-compa11y-toast-action\n >\n {toast.action.label}\n </button>\n )}\n <button\n type=\"button\"\n // Safari fix: Ensure button is in tab order\n tabIndex={0}\n aria-label=\"Dismiss\"\n onClick={onClose}\n data-compa11y-toast-close\n >\n ×\n </button>\n </div>\n );\n}\n\n/**\n * Hook for common toast patterns\n */\nexport function useToastHelpers() {\n const { addToast } = useToast();\n\n return {\n toast: addToast,\n success: (title: string, description?: string) =>\n addToast({ type: 'success', title, description }),\n error: (title: string, description?: string) =>\n addToast({ type: 'error', title, description }),\n warning: (title: string, description?: string) =>\n addToast({ type: 'warning', title, description }),\n info: (title: string, description?: string) =>\n addToast({ type: 'info', title, description }),\n };\n}\n"]}
@@ -1,41 +0,0 @@
1
- import { useId as useId$1, useMemo } from 'react';
2
-
3
- // src/hooks/use-id.ts
4
- function useId(prefix) {
5
- const reactId = useId$1();
6
- return useMemo(() => {
7
- const cleanId = reactId.replace(/:/g, "");
8
- return prefix ? `compa11y-${prefix}-${cleanId}` : `compa11y-${cleanId}`;
9
- }, [reactId, prefix]);
10
- }
11
- function useIds(parts, prefix) {
12
- const baseId = useId(prefix);
13
- return useMemo(() => {
14
- const ids = {};
15
- for (const part of parts) {
16
- ids[part] = `${baseId}-${part}`;
17
- }
18
- return ids;
19
- }, [baseId, parts]);
20
- }
21
- function useIdScope(componentName) {
22
- const scopeId = useId(componentName);
23
- return useMemo(
24
- () => ({
25
- id: scopeId,
26
- generate: (suffix) => `${scopeId}-${suffix}`,
27
- generateMultiple: (parts) => {
28
- const ids = {};
29
- for (const part of parts) {
30
- ids[part] = `${scopeId}-${part}`;
31
- }
32
- return ids;
33
- }
34
- }),
35
- [scopeId]
36
- );
37
- }
38
-
39
- export { useId, useIdScope, useIds };
40
- //# sourceMappingURL=chunk-WURPAE3R.js.map
41
- //# sourceMappingURL=chunk-WURPAE3R.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/hooks/use-id.ts"],"names":["useReactId"],"mappings":";;;AAOO,SAAS,MAAM,MAAA,EAAyB;AAE7C,EAAA,MAAM,UAAUA,OAAA,EAAW;AAE3B,EAAA,OAAO,QAAQ,MAAM;AAEnB,IAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,OAAA,CAAQ,IAAA,EAAM,EAAE,CAAA;AACxC,IAAA,OAAO,SAAS,CAAA,SAAA,EAAY,MAAM,IAAI,OAAO,CAAA,CAAA,GAAK,YAAY,OAAO,CAAA,CAAA;AAAA,EACvE,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,OAAO,QAAQ,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,OAAO,OAAA;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-WURPAE3R.js","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 ? `compa11y-${prefix}-${cleanId}` : `compa11y-${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"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"index.cjs"}
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"index.cjs"}
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"index.cjs"}
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"index.cjs"}
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"index.cjs"}
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/hooks/use-focus-visible.ts","../src/hooks/use-roving-tabindex.ts","../src/components/switch/switch.tsx"],"names":["useState","useEffect","initFocusVisible","useCallback","checkFocusVisible","focusWithVisibleRing","useRef","createComponentWarnings","forwardRef","Switch","useId","announce","useAnnouncer","useKeyboard","jsxs","jsx"],"mappings":"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","file":"index.cjs","sourcesContent":["import { useEffect, useState, useCallback } from 'react';\nimport {\n initFocusVisible,\n isFocusVisible as checkFocusVisible,\n focusWithVisibleRing,\n} from '@compa11y/core';\n\n/**\n * Hook to detect if focus should be visible (keyboard navigation)\n *\n * @example\n * ```tsx\n * function Button({ children }) {\n * const { isFocusVisible, focusProps } = useFocusVisible();\n *\n * return (\n * <button\n * {...focusProps}\n * className={isFocusVisible ? 'focus-ring' : ''}\n * >\n * {children}\n * </button>\n * );\n * }\n * ```\n */\nexport function useFocusVisible() {\n const [isFocusVisible, setIsFocusVisible] = useState(false);\n\n // Initialize focus-visible tracking\n useEffect(() => {\n const cleanup = initFocusVisible();\n return cleanup;\n }, []);\n\n const handleFocus = useCallback(() => {\n setIsFocusVisible(checkFocusVisible());\n }, []);\n\n const handleBlur = useCallback(() => {\n setIsFocusVisible(false);\n }, []);\n\n return {\n isFocusVisible,\n focusProps: {\n onFocus: handleFocus,\n onBlur: handleBlur,\n },\n };\n}\n\n/**\n * Hook for managing focus on a specific element\n *\n * @example\n * ```tsx\n * function Combobox() {\n * const inputRef = useFocusManager<HTMLInputElement>({\n * autoFocus: true,\n * });\n *\n * return <input ref={inputRef} />;\n * }\n * ```\n */\nexport function useFocusManager<T extends HTMLElement = HTMLElement>(\n options: {\n autoFocus?: boolean;\n restoreFocus?: boolean;\n focusVisible?: boolean;\n } = {}\n) {\n const {\n autoFocus = false,\n restoreFocus = false,\n focusVisible = true,\n } = options;\n\n const elementRef = useCallback(\n (node: T | null) => {\n if (node && autoFocus) {\n // Delay focus to ensure DOM is ready\n requestAnimationFrame(() => {\n if (focusVisible) {\n focusWithVisibleRing(node);\n } else {\n node.focus();\n }\n });\n }\n },\n [autoFocus, focusVisible]\n );\n\n // Store ref for restore focus\n useEffect(() => {\n if (!restoreFocus) return;\n\n const previousElement = document.activeElement as HTMLElement;\n\n return () => {\n if (previousElement && previousElement.focus) {\n previousElement.focus();\n }\n };\n }, [restoreFocus]);\n\n return elementRef;\n}\n\n/**\n * Focus an element programmatically with proper focus-visible handling\n */\nexport function useFocusControl<T extends HTMLElement = HTMLElement>() {\n const elementRef = useCallback((_node: T | null) => {\n // Just store the ref\n }, []);\n\n const focus = useCallback((options?: { visible?: boolean }) => {\n const element = elementRef as unknown as { current: T | null };\n if (element.current) {\n if (options?.visible) {\n focusWithVisibleRing(element.current);\n } else {\n element.current.focus();\n }\n }\n }, []);\n\n return { ref: elementRef, focus };\n}\n\n/**\n * Track if an element has focus\n */\nexport function useFocusWithin<T extends HTMLElement = HTMLElement>() {\n const [hasFocus, setHasFocus] = useState(false);\n const containerRef = useCallback<React.RefCallback<T>>((_node) => {\n // Element ref callback\n }, []);\n\n const handleFocusIn = useCallback(() => {\n setHasFocus(true);\n }, []);\n\n const handleFocusOut = useCallback((event: React.FocusEvent) => {\n const container = event.currentTarget;\n const relatedTarget = event.relatedTarget as Node | null;\n\n // Check if focus moved outside the container\n if (!relatedTarget || !container.contains(relatedTarget)) {\n setHasFocus(false);\n }\n }, []);\n\n return {\n ref: containerRef,\n hasFocus,\n focusWithinProps: {\n onFocus: handleFocusIn,\n onBlur: handleFocusOut,\n },\n };\n}\n","import { useCallback, useEffect, useRef, useState } from 'react';\n\nexport interface UseRovingTabindexOptions {\n /** Initial active index */\n initialIndex?: number;\n /** Whether navigation wraps around */\n wrap?: boolean;\n /** Orientation for arrow keys */\n orientation?: 'horizontal' | 'vertical' | 'both';\n /** Called when active index changes */\n onChange?: (index: number) => void;\n}\n\nexport interface RovingTabindexItem {\n /** Props to spread on the item element */\n props: {\n tabIndex: number;\n onKeyDown: (event: React.KeyboardEvent) => void;\n onFocus: () => void;\n };\n /** Whether this item is the active/focusable one */\n isActive: boolean;\n}\n\n/**\n * Hook for roving tabindex pattern\n *\n * @example\n * ```tsx\n * function Toolbar() {\n * const { getItemProps, activeIndex } = useRovingTabindex({\n * itemCount: 3,\n * orientation: 'horizontal',\n * });\n *\n * return (\n * <div role=\"toolbar\">\n * <button {...getItemProps(0)}>Cut</button>\n * <button {...getItemProps(1)}>Copy</button>\n * <button {...getItemProps(2)}>Paste</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useRovingTabindex(options: UseRovingTabindexOptions & { itemCount: number }) {\n const {\n itemCount,\n initialIndex = 0,\n wrap = true,\n orientation = 'both',\n onChange,\n } = options;\n\n const [activeIndex, setActiveIndex] = useState(initialIndex);\n const itemRefs = useRef<(HTMLElement | null)[]>([]);\n\n // Reset if item count changes\n useEffect(() => {\n if (activeIndex >= itemCount) {\n setActiveIndex(Math.max(0, itemCount - 1));\n }\n }, [itemCount, activeIndex]);\n\n const moveTo = useCallback(\n (index: number) => {\n let newIndex = index;\n if (newIndex < 0) {\n newIndex = wrap ? itemCount - 1 : 0;\n } else if (newIndex >= itemCount) {\n newIndex = wrap ? 0 : itemCount - 1;\n }\n\n setActiveIndex(newIndex);\n onChange?.(newIndex);\n\n // Focus the element\n const element = itemRefs.current[newIndex];\n element?.focus();\n },\n [itemCount, wrap, onChange]\n );\n\n const handleKeyDown = useCallback(\n (event: React.KeyboardEvent, index: number) => {\n let handled = false;\n\n switch (event.key) {\n case 'ArrowRight':\n if (orientation === 'horizontal' || orientation === 'both') {\n moveTo(index + 1);\n handled = true;\n }\n break;\n case 'ArrowLeft':\n if (orientation === 'horizontal' || orientation === 'both') {\n moveTo(index - 1);\n handled = true;\n }\n break;\n case 'ArrowDown':\n if (orientation === 'vertical' || orientation === 'both') {\n moveTo(index + 1);\n handled = true;\n }\n break;\n case 'ArrowUp':\n if (orientation === 'vertical' || orientation === 'both') {\n moveTo(index - 1);\n handled = true;\n }\n break;\n case 'Home':\n moveTo(0);\n handled = true;\n break;\n case 'End':\n moveTo(itemCount - 1);\n handled = true;\n break;\n }\n\n if (handled) {\n event.preventDefault();\n event.stopPropagation();\n }\n },\n [orientation, moveTo, itemCount]\n );\n\n const getItemProps = useCallback(\n (index: number) => ({\n ref: (el: HTMLElement | null) => {\n itemRefs.current[index] = el;\n },\n tabIndex: index === activeIndex ? 0 : -1,\n onKeyDown: (event: React.KeyboardEvent) => handleKeyDown(event, index),\n onFocus: () => {\n if (index !== activeIndex) {\n setActiveIndex(index);\n onChange?.(index);\n }\n },\n }),\n [activeIndex, handleKeyDown, onChange]\n );\n\n return {\n activeIndex,\n setActiveIndex: moveTo,\n getItemProps,\n first: () => moveTo(0),\n last: () => moveTo(itemCount - 1),\n next: () => moveTo(activeIndex + 1),\n previous: () => moveTo(activeIndex - 1),\n };\n}\n\n/**\n * Simplified roving tabindex for items with known IDs\n */\nexport function useRovingTabindexMap<T extends string>(\n ids: T[],\n options: Omit<UseRovingTabindexOptions, 'itemCount'> = {}\n) {\n const roving = useRovingTabindex({\n ...options,\n itemCount: ids.length,\n });\n\n const getItemProps = useCallback(\n (id: T) => {\n const index = ids.indexOf(id);\n if (index === -1) {\n throw new Error(`Unknown item ID: ${id}`);\n }\n return roving.getItemProps(index);\n },\n [ids, roving]\n );\n\n const activeId = ids[roving.activeIndex];\n\n return {\n ...roving,\n activeId,\n getItemProps,\n setActiveId: (id: T) => {\n const index = ids.indexOf(id);\n if (index !== -1) {\n roving.setActiveIndex(index);\n }\n },\n };\n}\n","import React, { forwardRef, useCallback, useEffect, useState } from 'react';\nimport { createComponentWarnings } from '@compa11y/core';\nimport { useId } from '../../hooks/use-id';\nimport { useKeyboard } from '../../hooks/use-keyboard';\nimport { useAnnouncer } from '../../hooks/use-announcer';\n\nconst warnings = createComponentWarnings('Switch');\n\nexport interface SwitchProps extends Omit<\n React.ButtonHTMLAttributes<HTMLButtonElement>,\n 'onChange' | 'role'\n> {\n /** Controlled checked state */\n checked?: boolean;\n /** Default checked state (uncontrolled) */\n defaultChecked?: boolean;\n /** Called when the switch state changes */\n onCheckedChange?: (checked: boolean) => void;\n /** Accessible label for the switch (renders visible label next to switch) */\n label?: string;\n /** Whether the switch is disabled */\n disabled?: boolean;\n /** Remove default styles to allow full customization via className */\n unstyled?: boolean;\n /** Custom class name */\n className?: string;\n /** Size variant */\n size?: 'sm' | 'md' | 'lg';\n}\n\n/**\n * Switch component - An accessible toggle switch (on/off control)\n *\n * Follows WAI-ARIA Switch pattern with proper keyboard support and screen reader announcements.\n *\n * @example\n * ```tsx\n * // Uncontrolled\n * <Switch defaultChecked={true} label=\"Enable notifications\" />\n *\n * // Controlled\n * <Switch\n * checked={enabled}\n * onCheckedChange={setEnabled}\n * aria-label=\"Dark mode\"\n * />\n *\n * // With visible label\n * <Switch checked={enabled} onCheckedChange={setEnabled} label=\"Dark mode\" />\n * ```\n */\nexport const Switch = forwardRef<HTMLButtonElement, SwitchProps>(\n function Switch(\n {\n checked: controlledChecked,\n defaultChecked = false,\n onCheckedChange,\n label,\n disabled = false,\n unstyled = false,\n className,\n size = 'md',\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledby,\n onClick,\n ...props\n },\n ref\n ) {\n const id = useId('switch');\n const labelId = `${id}-label`;\n const { announce } = useAnnouncer();\n\n // Support both controlled and uncontrolled modes\n const [uncontrolledChecked, setUncontrolledChecked] =\n useState(defaultChecked);\n const isControlled = controlledChecked !== undefined;\n const checked = isControlled ? controlledChecked : uncontrolledChecked;\n\n // Warn if no accessible label is provided\n useEffect(() => {\n if (!label && !ariaLabel && !ariaLabelledby) {\n warnings.warning(\n 'Switch has no accessible label. Screen readers need this to identify the switch.',\n 'Add label=\"Description\", aria-label=\"...\", or aria-labelledby=\"...\"'\n );\n }\n }, [label, ariaLabel, ariaLabelledby]);\n\n const toggleSwitch = useCallback(() => {\n if (disabled) return;\n\n const newChecked = !checked;\n\n if (!isControlled) {\n setUncontrolledChecked(newChecked);\n }\n\n onCheckedChange?.(newChecked);\n\n // Announce state change to screen readers\n const labelText = label || ariaLabel || 'Switch';\n announce(`${labelText} ${newChecked ? 'on' : 'off'}`);\n }, [\n checked,\n disabled,\n isControlled,\n onCheckedChange,\n label,\n ariaLabel,\n announce,\n ]);\n\n const handleClick = useCallback(\n (event: React.MouseEvent<HTMLButtonElement>) => {\n onClick?.(event);\n if (!event.defaultPrevented) {\n toggleSwitch();\n }\n },\n [onClick, toggleSwitch]\n );\n\n // Keyboard handling: Space and Enter should toggle\n const keyboardProps = useKeyboard(\n {\n ' ': () => {\n // Space toggles the switch\n toggleSwitch();\n },\n Enter: () => {\n // Enter also toggles (some users prefer Enter over Space)\n toggleSwitch();\n },\n },\n { preventDefault: true }\n );\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>) => {\n props.onKeyDown?.(event);\n if (!event.defaultPrevented) {\n keyboardProps.onKeyDown(event);\n }\n };\n\n // Compute ARIA label\n const computedAriaLabel = ariaLabel;\n const computedAriaLabelledby =\n ariaLabelledby || (label ? labelId : undefined);\n\n // Structural styles (always applied)\n const wrapperStructuralStyles: React.CSSProperties = {\n display: 'inline-flex',\n alignItems: 'center',\n };\n\n // Size dimensions for structural styles\n const sizes = {\n sm: { width: 32, height: 18, thumb: 14, translate: 14 },\n md: { width: 44, height: 24, thumb: 20, translate: 20 },\n lg: { width: 56, height: 30, thumb: 26, translate: 26 },\n };\n\n const sizeConfig = sizes[size];\n\n const trackStructuralStyles: React.CSSProperties = {\n position: 'relative',\n display: 'inline-flex',\n alignItems: 'center',\n flexShrink: 0,\n width: sizeConfig.width,\n height: sizeConfig.height,\n border: 'none',\n padding: 2,\n cursor: disabled ? 'not-allowed' : 'pointer',\n };\n\n const trackVisualStyles: React.CSSProperties = unstyled\n ? {}\n : {\n backgroundColor: checked ? '#0066cc' : '#d1d5db',\n borderRadius: sizeConfig.height / 2,\n transition: 'background-color 0.2s ease',\n opacity: disabled ? 0.5 : 1,\n };\n\n const thumbStructuralStyles: React.CSSProperties = {\n position: 'absolute',\n left: 2,\n width: sizeConfig.thumb,\n height: sizeConfig.thumb,\n pointerEvents: 'none',\n transform: checked\n ? `translateX(${sizeConfig.translate}px)`\n : 'translateX(0)',\n };\n\n const thumbVisualStyles: React.CSSProperties = unstyled\n ? {}\n : {\n backgroundColor: 'white',\n borderRadius: '50%',\n boxShadow: '0 1px 3px rgba(0, 0, 0, 0.2)',\n transition: 'transform 0.2s ease',\n };\n\n const labelStyles: React.CSSProperties = unstyled\n ? {\n marginLeft: 8,\n userSelect: 'none',\n cursor: disabled ? 'not-allowed' : 'pointer',\n }\n : {\n marginLeft: 8,\n userSelect: 'none',\n cursor: disabled ? 'not-allowed' : 'pointer',\n opacity: disabled ? 0.5 : 1,\n };\n\n return (\n <div\n style={wrapperStructuralStyles}\n data-compa11y-switch-wrapper\n data-size={size}\n >\n <button\n ref={ref}\n type=\"button\"\n role=\"switch\"\n aria-checked={checked}\n aria-label={computedAriaLabel}\n aria-labelledby={computedAriaLabelledby}\n disabled={disabled}\n onClick={handleClick}\n onKeyDown={handleKeyDown}\n className={className}\n style={{ ...trackStructuralStyles, ...trackVisualStyles }}\n tabIndex={disabled ? -1 : 0}\n data-compa11y-switch\n data-checked={checked}\n data-disabled={disabled || undefined}\n data-size={size}\n {...props}\n // CSS-in-JS focus-visible styles\n onFocus={(e) => {\n // Add focus-visible indicator\n if (!unstyled) {\n e.currentTarget.style.outline = '2px solid #0066cc';\n e.currentTarget.style.outlineOffset = '2px';\n }\n props.onFocus?.(e);\n }}\n onBlur={(e) => {\n // Remove focus indicator on blur\n if (!unstyled) {\n e.currentTarget.style.outline = 'none';\n }\n props.onBlur?.(e);\n }}\n >\n <span\n style={{ ...thumbStructuralStyles, ...thumbVisualStyles }}\n data-compa11y-switch-thumb\n aria-hidden=\"true\"\n />\n </button>\n {label && (\n <label\n id={labelId}\n onClick={disabled ? undefined : () => toggleSwitch()}\n style={labelStyles}\n data-compa11y-switch-label\n >\n {label}\n </label>\n )}\n </div>\n );\n }\n);\n\nSwitch.displayName = 'Switch';\n"]}
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/hooks/use-focus-visible.ts","../src/hooks/use-roving-tabindex.ts","../src/components/switch/switch.tsx"],"names":["isFocusVisible","checkFocusVisible","useState","useEffect","useCallback","Switch","announce"],"mappings":"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","file":"index.js","sourcesContent":["import { useEffect, useState, useCallback } from 'react';\nimport {\n initFocusVisible,\n isFocusVisible as checkFocusVisible,\n focusWithVisibleRing,\n} from '@compa11y/core';\n\n/**\n * Hook to detect if focus should be visible (keyboard navigation)\n *\n * @example\n * ```tsx\n * function Button({ children }) {\n * const { isFocusVisible, focusProps } = useFocusVisible();\n *\n * return (\n * <button\n * {...focusProps}\n * className={isFocusVisible ? 'focus-ring' : ''}\n * >\n * {children}\n * </button>\n * );\n * }\n * ```\n */\nexport function useFocusVisible() {\n const [isFocusVisible, setIsFocusVisible] = useState(false);\n\n // Initialize focus-visible tracking\n useEffect(() => {\n const cleanup = initFocusVisible();\n return cleanup;\n }, []);\n\n const handleFocus = useCallback(() => {\n setIsFocusVisible(checkFocusVisible());\n }, []);\n\n const handleBlur = useCallback(() => {\n setIsFocusVisible(false);\n }, []);\n\n return {\n isFocusVisible,\n focusProps: {\n onFocus: handleFocus,\n onBlur: handleBlur,\n },\n };\n}\n\n/**\n * Hook for managing focus on a specific element\n *\n * @example\n * ```tsx\n * function Combobox() {\n * const inputRef = useFocusManager<HTMLInputElement>({\n * autoFocus: true,\n * });\n *\n * return <input ref={inputRef} />;\n * }\n * ```\n */\nexport function useFocusManager<T extends HTMLElement = HTMLElement>(\n options: {\n autoFocus?: boolean;\n restoreFocus?: boolean;\n focusVisible?: boolean;\n } = {}\n) {\n const {\n autoFocus = false,\n restoreFocus = false,\n focusVisible = true,\n } = options;\n\n const elementRef = useCallback(\n (node: T | null) => {\n if (node && autoFocus) {\n // Delay focus to ensure DOM is ready\n requestAnimationFrame(() => {\n if (focusVisible) {\n focusWithVisibleRing(node);\n } else {\n node.focus();\n }\n });\n }\n },\n [autoFocus, focusVisible]\n );\n\n // Store ref for restore focus\n useEffect(() => {\n if (!restoreFocus) return;\n\n const previousElement = document.activeElement as HTMLElement;\n\n return () => {\n if (previousElement && previousElement.focus) {\n previousElement.focus();\n }\n };\n }, [restoreFocus]);\n\n return elementRef;\n}\n\n/**\n * Focus an element programmatically with proper focus-visible handling\n */\nexport function useFocusControl<T extends HTMLElement = HTMLElement>() {\n const elementRef = useCallback((_node: T | null) => {\n // Just store the ref\n }, []);\n\n const focus = useCallback((options?: { visible?: boolean }) => {\n const element = elementRef as unknown as { current: T | null };\n if (element.current) {\n if (options?.visible) {\n focusWithVisibleRing(element.current);\n } else {\n element.current.focus();\n }\n }\n }, []);\n\n return { ref: elementRef, focus };\n}\n\n/**\n * Track if an element has focus\n */\nexport function useFocusWithin<T extends HTMLElement = HTMLElement>() {\n const [hasFocus, setHasFocus] = useState(false);\n const containerRef = useCallback<React.RefCallback<T>>((_node) => {\n // Element ref callback\n }, []);\n\n const handleFocusIn = useCallback(() => {\n setHasFocus(true);\n }, []);\n\n const handleFocusOut = useCallback((event: React.FocusEvent) => {\n const container = event.currentTarget;\n const relatedTarget = event.relatedTarget as Node | null;\n\n // Check if focus moved outside the container\n if (!relatedTarget || !container.contains(relatedTarget)) {\n setHasFocus(false);\n }\n }, []);\n\n return {\n ref: containerRef,\n hasFocus,\n focusWithinProps: {\n onFocus: handleFocusIn,\n onBlur: handleFocusOut,\n },\n };\n}\n","import { useCallback, useEffect, useRef, useState } from 'react';\n\nexport interface UseRovingTabindexOptions {\n /** Initial active index */\n initialIndex?: number;\n /** Whether navigation wraps around */\n wrap?: boolean;\n /** Orientation for arrow keys */\n orientation?: 'horizontal' | 'vertical' | 'both';\n /** Called when active index changes */\n onChange?: (index: number) => void;\n}\n\nexport interface RovingTabindexItem {\n /** Props to spread on the item element */\n props: {\n tabIndex: number;\n onKeyDown: (event: React.KeyboardEvent) => void;\n onFocus: () => void;\n };\n /** Whether this item is the active/focusable one */\n isActive: boolean;\n}\n\n/**\n * Hook for roving tabindex pattern\n *\n * @example\n * ```tsx\n * function Toolbar() {\n * const { getItemProps, activeIndex } = useRovingTabindex({\n * itemCount: 3,\n * orientation: 'horizontal',\n * });\n *\n * return (\n * <div role=\"toolbar\">\n * <button {...getItemProps(0)}>Cut</button>\n * <button {...getItemProps(1)}>Copy</button>\n * <button {...getItemProps(2)}>Paste</button>\n * </div>\n * );\n * }\n * ```\n */\nexport function useRovingTabindex(options: UseRovingTabindexOptions & { itemCount: number }) {\n const {\n itemCount,\n initialIndex = 0,\n wrap = true,\n orientation = 'both',\n onChange,\n } = options;\n\n const [activeIndex, setActiveIndex] = useState(initialIndex);\n const itemRefs = useRef<(HTMLElement | null)[]>([]);\n\n // Reset if item count changes\n useEffect(() => {\n if (activeIndex >= itemCount) {\n setActiveIndex(Math.max(0, itemCount - 1));\n }\n }, [itemCount, activeIndex]);\n\n const moveTo = useCallback(\n (index: number) => {\n let newIndex = index;\n if (newIndex < 0) {\n newIndex = wrap ? itemCount - 1 : 0;\n } else if (newIndex >= itemCount) {\n newIndex = wrap ? 0 : itemCount - 1;\n }\n\n setActiveIndex(newIndex);\n onChange?.(newIndex);\n\n // Focus the element\n const element = itemRefs.current[newIndex];\n element?.focus();\n },\n [itemCount, wrap, onChange]\n );\n\n const handleKeyDown = useCallback(\n (event: React.KeyboardEvent, index: number) => {\n let handled = false;\n\n switch (event.key) {\n case 'ArrowRight':\n if (orientation === 'horizontal' || orientation === 'both') {\n moveTo(index + 1);\n handled = true;\n }\n break;\n case 'ArrowLeft':\n if (orientation === 'horizontal' || orientation === 'both') {\n moveTo(index - 1);\n handled = true;\n }\n break;\n case 'ArrowDown':\n if (orientation === 'vertical' || orientation === 'both') {\n moveTo(index + 1);\n handled = true;\n }\n break;\n case 'ArrowUp':\n if (orientation === 'vertical' || orientation === 'both') {\n moveTo(index - 1);\n handled = true;\n }\n break;\n case 'Home':\n moveTo(0);\n handled = true;\n break;\n case 'End':\n moveTo(itemCount - 1);\n handled = true;\n break;\n }\n\n if (handled) {\n event.preventDefault();\n event.stopPropagation();\n }\n },\n [orientation, moveTo, itemCount]\n );\n\n const getItemProps = useCallback(\n (index: number) => ({\n ref: (el: HTMLElement | null) => {\n itemRefs.current[index] = el;\n },\n tabIndex: index === activeIndex ? 0 : -1,\n onKeyDown: (event: React.KeyboardEvent) => handleKeyDown(event, index),\n onFocus: () => {\n if (index !== activeIndex) {\n setActiveIndex(index);\n onChange?.(index);\n }\n },\n }),\n [activeIndex, handleKeyDown, onChange]\n );\n\n return {\n activeIndex,\n setActiveIndex: moveTo,\n getItemProps,\n first: () => moveTo(0),\n last: () => moveTo(itemCount - 1),\n next: () => moveTo(activeIndex + 1),\n previous: () => moveTo(activeIndex - 1),\n };\n}\n\n/**\n * Simplified roving tabindex for items with known IDs\n */\nexport function useRovingTabindexMap<T extends string>(\n ids: T[],\n options: Omit<UseRovingTabindexOptions, 'itemCount'> = {}\n) {\n const roving = useRovingTabindex({\n ...options,\n itemCount: ids.length,\n });\n\n const getItemProps = useCallback(\n (id: T) => {\n const index = ids.indexOf(id);\n if (index === -1) {\n throw new Error(`Unknown item ID: ${id}`);\n }\n return roving.getItemProps(index);\n },\n [ids, roving]\n );\n\n const activeId = ids[roving.activeIndex];\n\n return {\n ...roving,\n activeId,\n getItemProps,\n setActiveId: (id: T) => {\n const index = ids.indexOf(id);\n if (index !== -1) {\n roving.setActiveIndex(index);\n }\n },\n };\n}\n","import React, { forwardRef, useCallback, useEffect, useState } from 'react';\nimport { createComponentWarnings } from '@compa11y/core';\nimport { useId } from '../../hooks/use-id';\nimport { useKeyboard } from '../../hooks/use-keyboard';\nimport { useAnnouncer } from '../../hooks/use-announcer';\n\nconst warnings = createComponentWarnings('Switch');\n\nexport interface SwitchProps extends Omit<\n React.ButtonHTMLAttributes<HTMLButtonElement>,\n 'onChange' | 'role'\n> {\n /** Controlled checked state */\n checked?: boolean;\n /** Default checked state (uncontrolled) */\n defaultChecked?: boolean;\n /** Called when the switch state changes */\n onCheckedChange?: (checked: boolean) => void;\n /** Accessible label for the switch (renders visible label next to switch) */\n label?: string;\n /** Whether the switch is disabled */\n disabled?: boolean;\n /** Remove default styles to allow full customization via className */\n unstyled?: boolean;\n /** Custom class name */\n className?: string;\n /** Size variant */\n size?: 'sm' | 'md' | 'lg';\n}\n\n/**\n * Switch component - An accessible toggle switch (on/off control)\n *\n * Follows WAI-ARIA Switch pattern with proper keyboard support and screen reader announcements.\n *\n * @example\n * ```tsx\n * // Uncontrolled\n * <Switch defaultChecked={true} label=\"Enable notifications\" />\n *\n * // Controlled\n * <Switch\n * checked={enabled}\n * onCheckedChange={setEnabled}\n * aria-label=\"Dark mode\"\n * />\n *\n * // With visible label\n * <Switch checked={enabled} onCheckedChange={setEnabled} label=\"Dark mode\" />\n * ```\n */\nexport const Switch = forwardRef<HTMLButtonElement, SwitchProps>(\n function Switch(\n {\n checked: controlledChecked,\n defaultChecked = false,\n onCheckedChange,\n label,\n disabled = false,\n unstyled = false,\n className,\n size = 'md',\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledby,\n onClick,\n ...props\n },\n ref\n ) {\n const id = useId('switch');\n const labelId = `${id}-label`;\n const { announce } = useAnnouncer();\n\n // Support both controlled and uncontrolled modes\n const [uncontrolledChecked, setUncontrolledChecked] =\n useState(defaultChecked);\n const isControlled = controlledChecked !== undefined;\n const checked = isControlled ? controlledChecked : uncontrolledChecked;\n\n // Warn if no accessible label is provided\n useEffect(() => {\n if (!label && !ariaLabel && !ariaLabelledby) {\n warnings.warning(\n 'Switch has no accessible label. Screen readers need this to identify the switch.',\n 'Add label=\"Description\", aria-label=\"...\", or aria-labelledby=\"...\"'\n );\n }\n }, [label, ariaLabel, ariaLabelledby]);\n\n const toggleSwitch = useCallback(() => {\n if (disabled) return;\n\n const newChecked = !checked;\n\n if (!isControlled) {\n setUncontrolledChecked(newChecked);\n }\n\n onCheckedChange?.(newChecked);\n\n // Announce state change to screen readers\n const labelText = label || ariaLabel || 'Switch';\n announce(`${labelText} ${newChecked ? 'on' : 'off'}`);\n }, [\n checked,\n disabled,\n isControlled,\n onCheckedChange,\n label,\n ariaLabel,\n announce,\n ]);\n\n const handleClick = useCallback(\n (event: React.MouseEvent<HTMLButtonElement>) => {\n onClick?.(event);\n if (!event.defaultPrevented) {\n toggleSwitch();\n }\n },\n [onClick, toggleSwitch]\n );\n\n // Keyboard handling: Space and Enter should toggle\n const keyboardProps = useKeyboard(\n {\n ' ': () => {\n // Space toggles the switch\n toggleSwitch();\n },\n Enter: () => {\n // Enter also toggles (some users prefer Enter over Space)\n toggleSwitch();\n },\n },\n { preventDefault: true }\n );\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>) => {\n props.onKeyDown?.(event);\n if (!event.defaultPrevented) {\n keyboardProps.onKeyDown(event);\n }\n };\n\n // Compute ARIA label\n const computedAriaLabel = ariaLabel;\n const computedAriaLabelledby =\n ariaLabelledby || (label ? labelId : undefined);\n\n // Structural styles (always applied)\n const wrapperStructuralStyles: React.CSSProperties = {\n display: 'inline-flex',\n alignItems: 'center',\n };\n\n // Size dimensions for structural styles\n const sizes = {\n sm: { width: 32, height: 18, thumb: 14, translate: 14 },\n md: { width: 44, height: 24, thumb: 20, translate: 20 },\n lg: { width: 56, height: 30, thumb: 26, translate: 26 },\n };\n\n const sizeConfig = sizes[size];\n\n const trackStructuralStyles: React.CSSProperties = {\n position: 'relative',\n display: 'inline-flex',\n alignItems: 'center',\n flexShrink: 0,\n width: sizeConfig.width,\n height: sizeConfig.height,\n border: 'none',\n padding: 2,\n cursor: disabled ? 'not-allowed' : 'pointer',\n };\n\n const trackVisualStyles: React.CSSProperties = unstyled\n ? {}\n : {\n backgroundColor: checked ? '#0066cc' : '#d1d5db',\n borderRadius: sizeConfig.height / 2,\n transition: 'background-color 0.2s ease',\n opacity: disabled ? 0.5 : 1,\n };\n\n const thumbStructuralStyles: React.CSSProperties = {\n position: 'absolute',\n left: 2,\n width: sizeConfig.thumb,\n height: sizeConfig.thumb,\n pointerEvents: 'none',\n transform: checked\n ? `translateX(${sizeConfig.translate}px)`\n : 'translateX(0)',\n };\n\n const thumbVisualStyles: React.CSSProperties = unstyled\n ? {}\n : {\n backgroundColor: 'white',\n borderRadius: '50%',\n boxShadow: '0 1px 3px rgba(0, 0, 0, 0.2)',\n transition: 'transform 0.2s ease',\n };\n\n const labelStyles: React.CSSProperties = unstyled\n ? {\n marginLeft: 8,\n userSelect: 'none',\n cursor: disabled ? 'not-allowed' : 'pointer',\n }\n : {\n marginLeft: 8,\n userSelect: 'none',\n cursor: disabled ? 'not-allowed' : 'pointer',\n opacity: disabled ? 0.5 : 1,\n };\n\n return (\n <div\n style={wrapperStructuralStyles}\n data-compa11y-switch-wrapper\n data-size={size}\n >\n <button\n ref={ref}\n type=\"button\"\n role=\"switch\"\n aria-checked={checked}\n aria-label={computedAriaLabel}\n aria-labelledby={computedAriaLabelledby}\n disabled={disabled}\n onClick={handleClick}\n onKeyDown={handleKeyDown}\n className={className}\n style={{ ...trackStructuralStyles, ...trackVisualStyles }}\n tabIndex={disabled ? -1 : 0}\n data-compa11y-switch\n data-checked={checked}\n data-disabled={disabled || undefined}\n data-size={size}\n {...props}\n // CSS-in-JS focus-visible styles\n onFocus={(e) => {\n // Add focus-visible indicator\n if (!unstyled) {\n e.currentTarget.style.outline = '2px solid #0066cc';\n e.currentTarget.style.outlineOffset = '2px';\n }\n props.onFocus?.(e);\n }}\n onBlur={(e) => {\n // Remove focus indicator on blur\n if (!unstyled) {\n e.currentTarget.style.outline = 'none';\n }\n props.onBlur?.(e);\n }}\n >\n <span\n style={{ ...thumbStructuralStyles, ...thumbVisualStyles }}\n data-compa11y-switch-thumb\n aria-hidden=\"true\"\n />\n </button>\n {label && (\n <label\n id={labelId}\n onClick={disabled ? undefined : () => toggleSwitch()}\n style={labelStyles}\n data-compa11y-switch-label\n >\n {label}\n </label>\n )}\n </div>\n );\n }\n);\n\nSwitch.displayName = 'Switch';\n"]}