@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,380 @@
1
+ import { useAnnouncer } from './chunk-47MFBHV6.js';
2
+ import { useKeyboard } from './chunk-U6DUSMEA.js';
3
+ import { useId } from './chunk-3WBTHTVK.js';
4
+ import { createContext, forwardRef, useRef, useState, useEffect, useLayoutEffect, useCallback, useContext, useMemo } from 'react';
5
+ import { createComponentWarnings } from '@a11y-core/core';
6
+ import { jsxs, jsx } from 'react/jsx-runtime';
7
+
8
+ var warnings = createComponentWarnings("Combobox");
9
+ var ComboboxContext = createContext(null);
10
+ function useComboboxContext() {
11
+ const context = useContext(ComboboxContext);
12
+ if (!context) {
13
+ throw new Error(
14
+ "Combobox compound components must be used within a Combobox component"
15
+ );
16
+ }
17
+ return context;
18
+ }
19
+ function Combobox({
20
+ options,
21
+ value: controlledValue,
22
+ onValueChange,
23
+ onInputChange,
24
+ defaultInputValue = "",
25
+ disabled = false,
26
+ filterFn,
27
+ "aria-label": ariaLabel,
28
+ "aria-labelledby": ariaLabelledBy,
29
+ children
30
+ }) {
31
+ const [inputValue, setInputValueState] = useState(defaultInputValue);
32
+ const [selectedValue, setSelectedValueState] = useState(
33
+ controlledValue ?? null
34
+ );
35
+ const [isOpen, setIsOpen] = useState(false);
36
+ const [highlightedIndex, setHighlightedIndex] = useState(-1);
37
+ const inputId = useId("combobox-input");
38
+ const listboxId = useId("combobox-listbox");
39
+ const baseOptionId = useId("combobox-option");
40
+ useEffect(() => {
41
+ if (controlledValue !== void 0) {
42
+ setSelectedValueState(controlledValue);
43
+ const option = options.find((o) => o.value === controlledValue);
44
+ if (option) {
45
+ setInputValueState(option.label);
46
+ }
47
+ }
48
+ }, [controlledValue, options]);
49
+ const setInputValue = useCallback(
50
+ (value) => {
51
+ setInputValueState(value);
52
+ onInputChange?.(value);
53
+ },
54
+ [onInputChange]
55
+ );
56
+ const setSelectedValue = useCallback(
57
+ (value) => {
58
+ if (controlledValue === void 0) {
59
+ setSelectedValueState(value);
60
+ }
61
+ onValueChange?.(value);
62
+ },
63
+ [controlledValue, onValueChange]
64
+ );
65
+ const filteredOptions = useMemo(() => {
66
+ if (!inputValue) return options;
67
+ const defaultFilter = (opt, input) => opt.label.toLowerCase().includes(input.toLowerCase());
68
+ const filter = filterFn ?? defaultFilter;
69
+ return options.filter((opt) => filter(opt, inputValue));
70
+ }, [options, inputValue, filterFn]);
71
+ const getOptionId = useCallback(
72
+ (index) => `${baseOptionId}-${index}`,
73
+ [baseOptionId]
74
+ );
75
+ const onSelect = useCallback(
76
+ (option) => {
77
+ setSelectedValue(option.value);
78
+ setInputValue(option.label);
79
+ setIsOpen(false);
80
+ setHighlightedIndex(-1);
81
+ },
82
+ [setSelectedValue, setInputValue]
83
+ );
84
+ useEffect(() => {
85
+ if (!ariaLabel && !ariaLabelledBy) {
86
+ warnings.warning(
87
+ "Combobox has no accessible label.",
88
+ "Add aria-label or aria-labelledby prop."
89
+ );
90
+ }
91
+ }, [ariaLabel, ariaLabelledBy]);
92
+ const contextValue = {
93
+ inputValue,
94
+ setInputValue,
95
+ selectedValue,
96
+ setSelectedValue,
97
+ isOpen,
98
+ setIsOpen,
99
+ highlightedIndex,
100
+ setHighlightedIndex,
101
+ options,
102
+ filteredOptions,
103
+ inputId,
104
+ listboxId,
105
+ getOptionId,
106
+ onSelect
107
+ };
108
+ return /* @__PURE__ */ jsx(ComboboxContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx("div", { "data-a11y-core-combobox": true, "data-disabled": disabled, children }) });
109
+ }
110
+ var ComboboxInput = forwardRef(
111
+ function ComboboxInput2({ clearable = false, onKeyDown, onFocus, onBlur, ...props }, ref) {
112
+ const {
113
+ inputValue,
114
+ setInputValue,
115
+ setSelectedValue,
116
+ isOpen,
117
+ setIsOpen,
118
+ highlightedIndex,
119
+ setHighlightedIndex,
120
+ filteredOptions,
121
+ inputId,
122
+ listboxId,
123
+ getOptionId,
124
+ onSelect
125
+ } = useComboboxContext();
126
+ const { announce } = useAnnouncer();
127
+ const keyboardProps = useKeyboard(
128
+ {
129
+ ArrowDown: () => {
130
+ if (!isOpen) {
131
+ setIsOpen(true);
132
+ setHighlightedIndex(0);
133
+ } else {
134
+ const nextIndex = (highlightedIndex + 1) % filteredOptions.length;
135
+ setHighlightedIndex(nextIndex);
136
+ }
137
+ },
138
+ ArrowUp: () => {
139
+ if (!isOpen) {
140
+ setIsOpen(true);
141
+ setHighlightedIndex(filteredOptions.length - 1);
142
+ } else {
143
+ const prevIndex = (highlightedIndex - 1 + filteredOptions.length) % filteredOptions.length;
144
+ setHighlightedIndex(prevIndex);
145
+ }
146
+ },
147
+ Enter: () => {
148
+ if (isOpen && highlightedIndex >= 0) {
149
+ const option = filteredOptions[highlightedIndex];
150
+ if (option && !option.disabled) {
151
+ onSelect(option);
152
+ announce(`${option.label} selected`);
153
+ }
154
+ }
155
+ },
156
+ Escape: () => {
157
+ if (isOpen) {
158
+ setIsOpen(false);
159
+ setHighlightedIndex(-1);
160
+ }
161
+ },
162
+ Tab: () => {
163
+ if (isOpen) {
164
+ setIsOpen(false);
165
+ setHighlightedIndex(-1);
166
+ }
167
+ return false;
168
+ },
169
+ Home: () => {
170
+ if (isOpen) {
171
+ setHighlightedIndex(0);
172
+ }
173
+ },
174
+ End: () => {
175
+ if (isOpen) {
176
+ setHighlightedIndex(filteredOptions.length - 1);
177
+ }
178
+ }
179
+ },
180
+ { preventDefault: true, stopPropagation: false }
181
+ );
182
+ const handleKeyDown = (event) => {
183
+ onKeyDown?.(event);
184
+ if (!event.defaultPrevented) {
185
+ keyboardProps.onKeyDown(event);
186
+ }
187
+ };
188
+ const handleChange = (event) => {
189
+ const value = event.target.value;
190
+ setInputValue(value);
191
+ setIsOpen(true);
192
+ setHighlightedIndex(0);
193
+ if (value === "") {
194
+ setSelectedValue(null);
195
+ }
196
+ };
197
+ const handleFocus = (event) => {
198
+ onFocus?.(event);
199
+ setIsOpen(true);
200
+ };
201
+ const handleBlur = (event) => {
202
+ onBlur?.(event);
203
+ setTimeout(() => {
204
+ setIsOpen(false);
205
+ }, 150);
206
+ };
207
+ const handleClear = () => {
208
+ setInputValue("");
209
+ setSelectedValue(null);
210
+ setIsOpen(false);
211
+ };
212
+ const activeDescendant = isOpen && highlightedIndex >= 0 ? getOptionId(highlightedIndex) : void 0;
213
+ return /* @__PURE__ */ jsxs("div", { "data-a11y-core-combobox-input-wrapper": true, children: [
214
+ /* @__PURE__ */ jsx(
215
+ "input",
216
+ {
217
+ ref,
218
+ id: inputId,
219
+ type: "text",
220
+ role: "combobox",
221
+ value: inputValue,
222
+ onChange: handleChange,
223
+ onKeyDown: handleKeyDown,
224
+ onFocus: handleFocus,
225
+ onBlur: handleBlur,
226
+ "aria-expanded": isOpen,
227
+ "aria-controls": listboxId,
228
+ "aria-activedescendant": activeDescendant,
229
+ "aria-autocomplete": "list",
230
+ "aria-haspopup": "listbox",
231
+ autoComplete: "off",
232
+ "data-a11y-core-combobox-input": true,
233
+ ...props
234
+ }
235
+ ),
236
+ clearable && inputValue && /* @__PURE__ */ jsx(
237
+ "button",
238
+ {
239
+ type: "button",
240
+ onClick: handleClear,
241
+ "aria-label": "Clear selection",
242
+ tabIndex: -1,
243
+ "data-a11y-core-combobox-clear": true,
244
+ children: "\xD7"
245
+ }
246
+ )
247
+ ] });
248
+ }
249
+ );
250
+ var ComboboxListbox = forwardRef(function ComboboxListbox2({ emptyMessage = "No results found", children, style, ...props }, forwardedRef) {
251
+ const { isOpen, filteredOptions, listboxId, inputId } = useComboboxContext();
252
+ const { announce } = useAnnouncer();
253
+ const internalRef = useRef(null);
254
+ const [position, setPosition] = useState("bottom");
255
+ useEffect(() => {
256
+ if (isOpen) {
257
+ const count = filteredOptions.length;
258
+ announce(
259
+ count === 0 ? "No results" : `${count} result${count === 1 ? "" : "s"} available`
260
+ );
261
+ }
262
+ }, [isOpen, filteredOptions.length, announce]);
263
+ useLayoutEffect(() => {
264
+ if (isOpen && internalRef.current) {
265
+ const listbox = internalRef.current;
266
+ const rect = listbox.getBoundingClientRect();
267
+ const viewportHeight = window.innerHeight;
268
+ const spaceBelow = viewportHeight - rect.top;
269
+ const spaceAbove = rect.top;
270
+ const listboxHeight = Math.min(rect.height, 200);
271
+ if (spaceBelow < listboxHeight + 50 && spaceAbove > spaceBelow) {
272
+ setPosition("top");
273
+ } else {
274
+ setPosition("bottom");
275
+ }
276
+ }
277
+ }, [isOpen]);
278
+ const setRefs = useCallback(
279
+ (node) => {
280
+ internalRef.current = node;
281
+ if (typeof forwardedRef === "function") {
282
+ forwardedRef(node);
283
+ } else if (forwardedRef) {
284
+ forwardedRef.current = node;
285
+ }
286
+ },
287
+ [forwardedRef]
288
+ );
289
+ if (!isOpen) {
290
+ return null;
291
+ }
292
+ const positionStyle = position === "top" ? { bottom: "100%", top: "auto", marginBottom: "4px", marginTop: 0 } : {};
293
+ return /* @__PURE__ */ jsx(
294
+ "ul",
295
+ {
296
+ ref: setRefs,
297
+ id: listboxId,
298
+ role: "listbox",
299
+ "aria-labelledby": inputId,
300
+ style: { ...style, ...positionStyle },
301
+ "data-a11y-core-combobox-listbox": true,
302
+ "data-position": position,
303
+ ...props,
304
+ children: filteredOptions.length === 0 ? /* @__PURE__ */ jsx("li", { role: "presentation", "data-a11y-core-combobox-empty": true, children: emptyMessage }) : children ?? filteredOptions.map((option, index) => /* @__PURE__ */ jsx(ComboboxOption, { option, index }, option.value))
305
+ }
306
+ );
307
+ });
308
+ var ComboboxOption = forwardRef(
309
+ function ComboboxOption2({ option, index, onClick, onMouseEnter, ...props }, forwardedRef) {
310
+ const {
311
+ selectedValue,
312
+ highlightedIndex,
313
+ setHighlightedIndex,
314
+ getOptionId,
315
+ onSelect
316
+ } = useComboboxContext();
317
+ const internalRef = useRef(null);
318
+ const isSelected = selectedValue === option.value;
319
+ const isHighlighted = highlightedIndex === index;
320
+ const optionId = getOptionId(index);
321
+ useEffect(() => {
322
+ if (isHighlighted && internalRef.current) {
323
+ internalRef.current.scrollIntoView({
324
+ block: "nearest",
325
+ behavior: "smooth"
326
+ });
327
+ }
328
+ }, [isHighlighted]);
329
+ const handleClick = (event) => {
330
+ onClick?.(event);
331
+ if (!event.defaultPrevented && !option.disabled) {
332
+ onSelect(option);
333
+ }
334
+ };
335
+ const handleMouseEnter = (event) => {
336
+ onMouseEnter?.(event);
337
+ if (!option.disabled) {
338
+ setHighlightedIndex(index);
339
+ }
340
+ };
341
+ const setRefs = useCallback(
342
+ (node) => {
343
+ internalRef.current = node;
344
+ if (typeof forwardedRef === "function") {
345
+ forwardedRef(node);
346
+ } else if (forwardedRef) {
347
+ forwardedRef.current = node;
348
+ }
349
+ },
350
+ [forwardedRef]
351
+ );
352
+ return /* @__PURE__ */ jsx(
353
+ "li",
354
+ {
355
+ ref: setRefs,
356
+ id: optionId,
357
+ role: "option",
358
+ "aria-selected": isSelected,
359
+ "aria-disabled": option.disabled,
360
+ "data-highlighted": isHighlighted,
361
+ "data-selected": isSelected,
362
+ "data-disabled": option.disabled,
363
+ onClick: handleClick,
364
+ onMouseEnter: handleMouseEnter,
365
+ "data-a11y-core-combobox-option": true,
366
+ ...props,
367
+ children: option.label
368
+ }
369
+ );
370
+ }
371
+ );
372
+ var ComboboxCompound = Object.assign(Combobox, {
373
+ Input: ComboboxInput,
374
+ Listbox: ComboboxListbox,
375
+ Option: ComboboxOption
376
+ });
377
+
378
+ export { Combobox, ComboboxCompound, ComboboxInput, ComboboxListbox, ComboboxOption };
379
+ //# sourceMappingURL=chunk-EY73HQNR.js.map
380
+ //# sourceMappingURL=chunk-EY73HQNR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/combobox/combobox.tsx"],"names":["ComboboxInput","ComboboxListbox","ComboboxOption"],"mappings":"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","file":"chunk-EY73HQNR.js","sourcesContent":["import React, {\n createContext,\n forwardRef,\n useCallback,\n useContext,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport { useId } from '../../hooks/use-id';\nimport { useKeyboard } from '../../hooks/use-keyboard';\nimport { useAnnouncer } from '../../hooks/use-announcer';\nimport { createComponentWarnings } from '@a11y-core/core';\n\nconst warnings = createComponentWarnings('Combobox');\n\nexport interface ComboboxOption {\n value: string;\n label: string;\n disabled?: boolean;\n}\n\ninterface ComboboxContextValue {\n inputValue: string;\n setInputValue: (value: string) => void;\n selectedValue: string | null;\n setSelectedValue: (value: string | null) => void;\n isOpen: boolean;\n setIsOpen: (open: boolean) => void;\n highlightedIndex: number;\n setHighlightedIndex: (index: number) => void;\n options: ComboboxOption[];\n filteredOptions: ComboboxOption[];\n inputId: string;\n listboxId: string;\n getOptionId: (index: number) => string;\n onSelect: (option: ComboboxOption) => void;\n}\n\nconst ComboboxContext = createContext<ComboboxContextValue | null>(null);\n\nfunction useComboboxContext(): ComboboxContextValue {\n const context = useContext(ComboboxContext);\n if (!context) {\n throw new Error(\n 'Combobox compound components must be used within a Combobox component'\n );\n }\n return context;\n}\n\nexport interface ComboboxProps {\n /** List of options */\n options: ComboboxOption[];\n /** Currently selected value */\n value?: string | null;\n /** Called when selection changes */\n onValueChange?: (value: string | null) => void;\n /** Called when input changes */\n onInputChange?: (value: string) => void;\n /** Default input value */\n defaultInputValue?: string;\n /** Whether the combobox is disabled */\n disabled?: boolean;\n /** Placeholder text */\n placeholder?: string;\n /** Custom filter function */\n filterFn?: (option: ComboboxOption, inputValue: string) => boolean;\n /** Accessible label */\n 'aria-label'?: string;\n /** ID of labelling element */\n 'aria-labelledby'?: string;\n children: React.ReactNode;\n}\n\nexport function Combobox({\n options,\n value: controlledValue,\n onValueChange,\n onInputChange,\n defaultInputValue = '',\n disabled = false,\n filterFn,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledBy,\n children,\n}: ComboboxProps) {\n const [inputValue, setInputValueState] = useState(defaultInputValue);\n const [selectedValue, setSelectedValueState] = useState<string | null>(\n controlledValue ?? null\n );\n const [isOpen, setIsOpen] = useState(false);\n const [highlightedIndex, setHighlightedIndex] = useState(-1);\n\n const inputId = useId('combobox-input');\n const listboxId = useId('combobox-listbox');\n const baseOptionId = useId('combobox-option');\n\n // Sync controlled value\n useEffect(() => {\n if (controlledValue !== undefined) {\n setSelectedValueState(controlledValue);\n // Update input value to match selected option\n const option = options.find((o) => o.value === controlledValue);\n if (option) {\n setInputValueState(option.label);\n }\n }\n }, [controlledValue, options]);\n\n const setInputValue = useCallback(\n (value: string) => {\n setInputValueState(value);\n onInputChange?.(value);\n },\n [onInputChange]\n );\n\n const setSelectedValue = useCallback(\n (value: string | null) => {\n if (controlledValue === undefined) {\n setSelectedValueState(value);\n }\n onValueChange?.(value);\n },\n [controlledValue, onValueChange]\n );\n\n const filteredOptions = useMemo(() => {\n if (!inputValue) return options;\n\n const defaultFilter = (opt: ComboboxOption, input: string) =>\n opt.label.toLowerCase().includes(input.toLowerCase());\n\n const filter = filterFn ?? defaultFilter;\n return options.filter((opt) => filter(opt, inputValue));\n }, [options, inputValue, filterFn]);\n\n const getOptionId = useCallback(\n (index: number) => `${baseOptionId}-${index}`,\n [baseOptionId]\n );\n\n const onSelect = useCallback(\n (option: ComboboxOption) => {\n setSelectedValue(option.value);\n setInputValue(option.label);\n setIsOpen(false);\n setHighlightedIndex(-1);\n },\n [setSelectedValue, setInputValue]\n );\n\n useEffect(() => {\n if (!ariaLabel && !ariaLabelledBy) {\n warnings.warning(\n 'Combobox has no accessible label.',\n 'Add aria-label or aria-labelledby prop.'\n );\n }\n }, [ariaLabel, ariaLabelledBy]);\n\n const contextValue: ComboboxContextValue = {\n inputValue,\n setInputValue,\n selectedValue,\n setSelectedValue,\n isOpen,\n setIsOpen,\n highlightedIndex,\n setHighlightedIndex,\n options,\n filteredOptions,\n inputId,\n listboxId,\n getOptionId,\n onSelect,\n };\n\n return (\n <ComboboxContext.Provider value={contextValue}>\n <div data-a11y-core-combobox data-disabled={disabled}>\n {children}\n </div>\n </ComboboxContext.Provider>\n );\n}\n\nexport interface ComboboxInputProps extends Omit<\n React.InputHTMLAttributes<HTMLInputElement>,\n 'value' | 'onChange'\n> {\n /** Show clear button */\n clearable?: boolean;\n}\n\nexport const ComboboxInput = forwardRef<HTMLInputElement, ComboboxInputProps>(\n function ComboboxInput(\n { clearable = false, onKeyDown, onFocus, onBlur, ...props },\n ref\n ) {\n const {\n inputValue,\n setInputValue,\n setSelectedValue,\n isOpen,\n setIsOpen,\n highlightedIndex,\n setHighlightedIndex,\n filteredOptions,\n inputId,\n listboxId,\n getOptionId,\n onSelect,\n } = useComboboxContext();\n const { announce } = useAnnouncer();\n\n const keyboardProps = useKeyboard(\n {\n ArrowDown: () => {\n if (!isOpen) {\n setIsOpen(true);\n setHighlightedIndex(0);\n } else {\n const nextIndex = (highlightedIndex + 1) % filteredOptions.length;\n setHighlightedIndex(nextIndex);\n }\n },\n ArrowUp: () => {\n if (!isOpen) {\n setIsOpen(true);\n setHighlightedIndex(filteredOptions.length - 1);\n } else {\n const prevIndex =\n (highlightedIndex - 1 + filteredOptions.length) %\n filteredOptions.length;\n setHighlightedIndex(prevIndex);\n }\n },\n Enter: () => {\n if (isOpen && highlightedIndex >= 0) {\n const option = filteredOptions[highlightedIndex];\n if (option && !option.disabled) {\n onSelect(option);\n announce(`${option.label} selected`);\n }\n }\n },\n Escape: () => {\n if (isOpen) {\n setIsOpen(false);\n setHighlightedIndex(-1);\n }\n },\n Tab: () => {\n // Tab should close the listbox and allow natural focus movement\n if (isOpen) {\n setIsOpen(false);\n setHighlightedIndex(-1);\n }\n // Return false to allow browser's default Tab behavior\n return false;\n },\n Home: () => {\n if (isOpen) {\n setHighlightedIndex(0);\n }\n },\n End: () => {\n if (isOpen) {\n setHighlightedIndex(filteredOptions.length - 1);\n }\n },\n },\n { preventDefault: true, stopPropagation: false }\n );\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {\n onKeyDown?.(event);\n if (!event.defaultPrevented) {\n keyboardProps.onKeyDown(event);\n }\n };\n\n const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {\n const value = event.target.value;\n setInputValue(value);\n setIsOpen(true);\n setHighlightedIndex(0);\n\n if (value === '') {\n setSelectedValue(null);\n }\n };\n\n const handleFocus = (event: React.FocusEvent<HTMLInputElement>) => {\n onFocus?.(event);\n setIsOpen(true);\n };\n\n const handleBlur = (event: React.FocusEvent<HTMLInputElement>) => {\n onBlur?.(event);\n // Use setTimeout to allow clicks on options to register before closing\n // This delay allows option click events to fire before the listbox closes\n setTimeout(() => {\n // Only close if focus didn't move to the listbox itself\n // This handles cases where user clicks on an option\n setIsOpen(false);\n }, 150);\n };\n\n const handleClear = () => {\n setInputValue('');\n setSelectedValue(null);\n setIsOpen(false);\n };\n\n const activeDescendant =\n isOpen && highlightedIndex >= 0\n ? getOptionId(highlightedIndex)\n : undefined;\n\n return (\n <div data-a11y-core-combobox-input-wrapper>\n <input\n ref={ref}\n id={inputId}\n type=\"text\"\n role=\"combobox\"\n value={inputValue}\n onChange={handleChange}\n onKeyDown={handleKeyDown}\n onFocus={handleFocus}\n onBlur={handleBlur}\n aria-expanded={isOpen}\n aria-controls={listboxId}\n aria-activedescendant={activeDescendant}\n aria-autocomplete=\"list\"\n aria-haspopup=\"listbox\"\n autoComplete=\"off\"\n data-a11y-core-combobox-input\n {...props}\n />\n {clearable && inputValue && (\n <button\n type=\"button\"\n onClick={handleClear}\n aria-label=\"Clear selection\"\n tabIndex={-1}\n data-a11y-core-combobox-clear\n >\n ×\n </button>\n )}\n </div>\n );\n }\n);\n\nexport interface ComboboxListboxProps extends React.HTMLAttributes<HTMLUListElement> {\n /** Render when no options match */\n emptyMessage?: React.ReactNode;\n children?: React.ReactNode;\n}\n\nexport const ComboboxListbox = forwardRef<\n HTMLUListElement,\n ComboboxListboxProps\n>(function ComboboxListbox(\n { emptyMessage = 'No results found', children, style, ...props },\n forwardedRef\n) {\n const { isOpen, filteredOptions, listboxId, inputId } = useComboboxContext();\n const { announce } = useAnnouncer();\n const internalRef = useRef<HTMLUListElement | null>(null);\n const [position, setPosition] = useState<'bottom' | 'top'>('bottom');\n\n useEffect(() => {\n if (isOpen) {\n const count = filteredOptions.length;\n announce(\n count === 0\n ? 'No results'\n : `${count} result${count === 1 ? '' : 's'} available`\n );\n }\n }, [isOpen, filteredOptions.length, announce]);\n\n useLayoutEffect(() => {\n if (isOpen && internalRef.current) {\n const listbox = internalRef.current;\n const rect = listbox.getBoundingClientRect();\n const viewportHeight = window.innerHeight;\n const spaceBelow = viewportHeight - rect.top;\n const spaceAbove = rect.top;\n const listboxHeight = Math.min(rect.height, 200); // max-height from CSS\n\n if (spaceBelow < listboxHeight + 50 && spaceAbove > spaceBelow) {\n setPosition('top');\n } else {\n setPosition('bottom');\n }\n }\n }, [isOpen]);\n\n const setRefs = useCallback(\n (node: HTMLUListElement | null) => {\n internalRef.current = node;\n if (typeof forwardedRef === 'function') {\n forwardedRef(node);\n } else if (forwardedRef) {\n (\n forwardedRef as React.MutableRefObject<HTMLUListElement | null>\n ).current = node;\n }\n },\n [forwardedRef]\n );\n\n if (!isOpen) {\n return null;\n }\n\n const positionStyle: React.CSSProperties =\n position === 'top'\n ? { bottom: '100%', top: 'auto', marginBottom: '4px', marginTop: 0 }\n : {};\n\n return (\n <ul\n ref={setRefs}\n id={listboxId}\n role=\"listbox\"\n aria-labelledby={inputId}\n style={{ ...style, ...positionStyle }}\n data-a11y-core-combobox-listbox\n data-position={position}\n {...props}\n >\n {filteredOptions.length === 0 ? (\n <li role=\"presentation\" data-a11y-core-combobox-empty>\n {emptyMessage}\n </li>\n ) : (\n (children ??\n filteredOptions.map((option, index) => (\n <ComboboxOption key={option.value} option={option} index={index} />\n )))\n )}\n </ul>\n );\n});\n\nexport interface ComboboxOptionProps extends React.LiHTMLAttributes<HTMLLIElement> {\n option: ComboboxOption;\n index: number;\n}\n\nexport const ComboboxOption = forwardRef<HTMLLIElement, ComboboxOptionProps>(\n function ComboboxOption(\n { option, index, onClick, onMouseEnter, ...props },\n forwardedRef\n ) {\n const {\n selectedValue,\n highlightedIndex,\n setHighlightedIndex,\n getOptionId,\n onSelect,\n } = useComboboxContext();\n const internalRef = useRef<HTMLLIElement | null>(null);\n\n const isSelected = selectedValue === option.value;\n const isHighlighted = highlightedIndex === index;\n const optionId = getOptionId(index);\n\n useEffect(() => {\n if (isHighlighted && internalRef.current) {\n internalRef.current.scrollIntoView({\n block: 'nearest',\n behavior: 'smooth',\n });\n }\n }, [isHighlighted]);\n\n const handleClick = (event: React.MouseEvent<HTMLLIElement>) => {\n onClick?.(event);\n if (!event.defaultPrevented && !option.disabled) {\n onSelect(option);\n }\n };\n\n const handleMouseEnter = (event: React.MouseEvent<HTMLLIElement>) => {\n onMouseEnter?.(event);\n if (!option.disabled) {\n setHighlightedIndex(index);\n }\n };\n\n const setRefs = useCallback(\n (node: HTMLLIElement | null) => {\n internalRef.current = node;\n if (typeof forwardedRef === 'function') {\n forwardedRef(node);\n } else if (forwardedRef) {\n (\n forwardedRef as React.MutableRefObject<HTMLLIElement | null>\n ).current = node;\n }\n },\n [forwardedRef]\n );\n\n return (\n <li\n ref={setRefs}\n id={optionId}\n role=\"option\"\n aria-selected={isSelected}\n aria-disabled={option.disabled}\n data-highlighted={isHighlighted}\n data-selected={isSelected}\n data-disabled={option.disabled}\n onClick={handleClick}\n onMouseEnter={handleMouseEnter}\n data-a11y-core-combobox-option\n {...props}\n >\n {option.label}\n </li>\n );\n }\n);\n\nexport const ComboboxCompound = Object.assign(Combobox, {\n Input: ComboboxInput,\n Listbox: ComboboxListbox,\n Option: ComboboxOption,\n});\n"]}
@@ -0,0 +1,215 @@
1
+ import { createContext, forwardRef, useState, useRef, useCallback, useEffect, useContext } from 'react';
2
+ import { createPortal } from 'react-dom';
3
+ import { announceAssertive, announce } from '@a11y-core/core';
4
+ import { jsxs, jsx } from 'react/jsx-runtime';
5
+
6
+ // src/components/toast/toast.tsx
7
+ var ToastContext = createContext(null);
8
+ function useToast() {
9
+ const context = useContext(ToastContext);
10
+ if (!context) {
11
+ throw new Error("useToast must be used within a ToastProvider");
12
+ }
13
+ return context;
14
+ }
15
+ function ToastProvider({
16
+ children,
17
+ duration = 5e3,
18
+ maxToasts = 5
19
+ }) {
20
+ const [toasts, setToasts] = useState([]);
21
+ const toastIdCounter = useRef(0);
22
+ const addToast = useCallback(
23
+ (toast) => {
24
+ const id = `toast-${++toastIdCounter.current}`;
25
+ const newToast = {
26
+ ...toast,
27
+ id,
28
+ duration: toast.duration ?? duration
29
+ };
30
+ setToasts((prev) => {
31
+ const updated = [...prev, newToast];
32
+ return updated.slice(-maxToasts);
33
+ });
34
+ const message = toast.title ? `${toast.title}. ${toast.description || ""}` : toast.description || "";
35
+ if (toast.type === "error") {
36
+ announceAssertive(message);
37
+ } else {
38
+ announce(message, { politeness: "polite" });
39
+ }
40
+ return id;
41
+ },
42
+ [duration, maxToasts]
43
+ );
44
+ const removeToast = useCallback((id) => {
45
+ setToasts((prev) => prev.filter((t) => t.id !== id));
46
+ }, []);
47
+ const updateToast = useCallback(
48
+ (id, updates) => {
49
+ setToasts(
50
+ (prev) => prev.map((t) => t.id === id ? { ...t, ...updates } : t)
51
+ );
52
+ },
53
+ []
54
+ );
55
+ return /* @__PURE__ */ jsx(
56
+ ToastContext.Provider,
57
+ {
58
+ value: { toasts, addToast, removeToast, updateToast },
59
+ children
60
+ }
61
+ );
62
+ }
63
+ var positionStyles = {
64
+ "top-left": { top: 0, left: 0 },
65
+ "top-center": { top: 0, left: "50%", transform: "translateX(-50%)" },
66
+ "top-right": { top: 0, right: 0 },
67
+ "bottom-left": { bottom: 0, left: 0 },
68
+ "bottom-center": { bottom: 0, left: "50%", transform: "translateX(-50%)" },
69
+ "bottom-right": { bottom: 0, right: 0 }
70
+ };
71
+ var ToastViewport = forwardRef(
72
+ function ToastViewport2({
73
+ position = "bottom-right",
74
+ label = "Notifications",
75
+ style,
76
+ children,
77
+ ...props
78
+ }, ref) {
79
+ const { toasts, removeToast } = useToast();
80
+ const viewport = /* @__PURE__ */ jsxs(
81
+ "div",
82
+ {
83
+ ref,
84
+ role: "region",
85
+ "aria-label": label,
86
+ "aria-live": "polite",
87
+ "aria-relevant": "additions removals",
88
+ tabIndex: -1,
89
+ style: {
90
+ position: "fixed",
91
+ zIndex: 9999,
92
+ padding: "1rem",
93
+ display: "flex",
94
+ flexDirection: "column",
95
+ gap: "0.5rem",
96
+ ...positionStyles[position],
97
+ ...style
98
+ },
99
+ "data-a11y-core-toast-viewport": true,
100
+ "data-position": position,
101
+ ...props,
102
+ children: [
103
+ toasts.map((toast) => /* @__PURE__ */ jsx(
104
+ ToastItem,
105
+ {
106
+ toast,
107
+ onClose: () => removeToast(toast.id)
108
+ },
109
+ toast.id
110
+ )),
111
+ children
112
+ ]
113
+ }
114
+ );
115
+ return createPortal(viewport, document.body);
116
+ }
117
+ );
118
+ function ToastItem({ toast, onClose }) {
119
+ const [isVisible, setIsVisible] = useState(true);
120
+ const [, setIsPaused] = useState(false);
121
+ const timerRef = useRef(null);
122
+ const remainingRef = useRef(toast.duration || 5e3);
123
+ const startTimeRef = useRef(Date.now());
124
+ const startTimer = useCallback(() => {
125
+ if (toast.duration === 0) return;
126
+ startTimeRef.current = Date.now();
127
+ timerRef.current = setTimeout(() => {
128
+ setIsVisible(false);
129
+ setTimeout(onClose, 200);
130
+ }, remainingRef.current);
131
+ }, [toast.duration, onClose]);
132
+ const pauseTimer = useCallback(() => {
133
+ if (timerRef.current) {
134
+ clearTimeout(timerRef.current);
135
+ remainingRef.current -= Date.now() - startTimeRef.current;
136
+ }
137
+ }, []);
138
+ useEffect(() => {
139
+ startTimer();
140
+ return () => {
141
+ if (timerRef.current) {
142
+ clearTimeout(timerRef.current);
143
+ }
144
+ };
145
+ }, [startTimer]);
146
+ const handleMouseEnter = () => {
147
+ setIsPaused(true);
148
+ pauseTimer();
149
+ };
150
+ const handleMouseLeave = () => {
151
+ setIsPaused(false);
152
+ startTimer();
153
+ };
154
+ const handleKeyDown = (event) => {
155
+ if (event.key === "Escape") {
156
+ onClose();
157
+ }
158
+ };
159
+ return /* @__PURE__ */ jsxs(
160
+ "div",
161
+ {
162
+ role: "alert",
163
+ "aria-atomic": "true",
164
+ tabIndex: 0,
165
+ onMouseEnter: handleMouseEnter,
166
+ onMouseLeave: handleMouseLeave,
167
+ onKeyDown: handleKeyDown,
168
+ "data-type": toast.type,
169
+ "data-visible": isVisible,
170
+ "data-a11y-core-toast": true,
171
+ children: [
172
+ toast.title && /* @__PURE__ */ jsx("div", { "data-a11y-core-toast-title": true, children: toast.title }),
173
+ toast.description && /* @__PURE__ */ jsx("div", { "data-a11y-core-toast-description": true, children: toast.description }),
174
+ toast.action && /* @__PURE__ */ jsx(
175
+ "button",
176
+ {
177
+ type: "button",
178
+ tabIndex: 0,
179
+ onClick: () => {
180
+ toast.action?.onClick();
181
+ onClose();
182
+ },
183
+ "data-a11y-core-toast-action": true,
184
+ children: toast.action.label
185
+ }
186
+ ),
187
+ /* @__PURE__ */ jsx(
188
+ "button",
189
+ {
190
+ type: "button",
191
+ tabIndex: 0,
192
+ "aria-label": "Dismiss",
193
+ onClick: onClose,
194
+ "data-a11y-core-toast-close": true,
195
+ children: "\xD7"
196
+ }
197
+ )
198
+ ]
199
+ }
200
+ );
201
+ }
202
+ function useToastHelpers() {
203
+ const { addToast } = useToast();
204
+ return {
205
+ toast: addToast,
206
+ success: (title, description) => addToast({ type: "success", title, description }),
207
+ error: (title, description) => addToast({ type: "error", title, description }),
208
+ warning: (title, description) => addToast({ type: "warning", title, description }),
209
+ info: (title, description) => addToast({ type: "info", title, description })
210
+ };
211
+ }
212
+
213
+ export { ToastProvider, ToastViewport, useToast, useToastHelpers };
214
+ //# sourceMappingURL=chunk-FJABCNFE.js.map
215
+ //# sourceMappingURL=chunk-FJABCNFE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/toast/toast.tsx"],"names":["ToastViewport"],"mappings":"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","file":"chunk-FJABCNFE.js","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 '@a11y-core/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-a11y-core-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-a11y-core-toast\n >\n {toast.title && <div data-a11y-core-toast-title>{toast.title}</div>}\n {toast.description && (\n <div data-a11y-core-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-a11y-core-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-a11y-core-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"]}