@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.
- package/README.md +252 -0
- package/dist/chunk-3WBTHTVK.js +41 -0
- package/dist/chunk-3WBTHTVK.js.map +1 -0
- package/dist/chunk-47MFBHV6.js +85 -0
- package/dist/chunk-47MFBHV6.js.map +1 -0
- package/dist/chunk-7A3IDIUB.cjs +89 -0
- package/dist/chunk-7A3IDIUB.cjs.map +1 -0
- package/dist/chunk-AZFZWGI6.cjs +383 -0
- package/dist/chunk-AZFZWGI6.cjs.map +1 -0
- package/dist/chunk-CTW5D77X.cjs +220 -0
- package/dist/chunk-CTW5D77X.cjs.map +1 -0
- package/dist/chunk-EY73HQNR.js +380 -0
- package/dist/chunk-EY73HQNR.js.map +1 -0
- package/dist/chunk-FJABCNFE.js +215 -0
- package/dist/chunk-FJABCNFE.js.map +1 -0
- package/dist/chunk-FTZ5KCOO.js +326 -0
- package/dist/chunk-FTZ5KCOO.js.map +1 -0
- package/dist/chunk-GS3H4T2O.cjs +106 -0
- package/dist/chunk-GS3H4T2O.cjs.map +1 -0
- package/dist/chunk-N6L4GLFC.cjs +45 -0
- package/dist/chunk-N6L4GLFC.cjs.map +1 -0
- package/dist/chunk-N774QCHE.js +234 -0
- package/dist/chunk-N774QCHE.js.map +1 -0
- package/dist/chunk-OSHIYZCZ.cjs +386 -0
- package/dist/chunk-OSHIYZCZ.cjs.map +1 -0
- package/dist/chunk-SRJSGDIA.js +373 -0
- package/dist/chunk-SRJSGDIA.js.map +1 -0
- package/dist/chunk-THB5U7YC.cjs +338 -0
- package/dist/chunk-THB5U7YC.cjs.map +1 -0
- package/dist/chunk-U6DUSMEA.js +99 -0
- package/dist/chunk-U6DUSMEA.js.map +1 -0
- package/dist/chunk-WDCYEMBO.cjs +245 -0
- package/dist/chunk-WDCYEMBO.cjs.map +1 -0
- package/dist/components/combobox/index.cjs +31 -0
- package/dist/components/combobox/index.cjs.map +1 -0
- package/dist/components/combobox/index.d.cts +55 -0
- package/dist/components/combobox/index.d.ts +55 -0
- package/dist/components/combobox/index.js +6 -0
- package/dist/components/combobox/index.js.map +1 -0
- package/dist/components/dialog/index.cjs +46 -0
- package/dist/components/dialog/index.cjs.map +1 -0
- package/dist/components/dialog/index.d.cts +84 -0
- package/dist/components/dialog/index.d.ts +84 -0
- package/dist/components/dialog/index.js +5 -0
- package/dist/components/dialog/index.js.map +1 -0
- package/dist/components/menu/index.cjs +46 -0
- package/dist/components/menu/index.cjs.map +1 -0
- package/dist/components/menu/index.d.cts +80 -0
- package/dist/components/menu/index.d.ts +80 -0
- package/dist/components/menu/index.js +5 -0
- package/dist/components/menu/index.js.map +1 -0
- package/dist/components/tabs/index.cjs +35 -0
- package/dist/components/tabs/index.cjs.map +1 -0
- package/dist/components/tabs/index.d.cts +65 -0
- package/dist/components/tabs/index.d.ts +65 -0
- package/dist/components/tabs/index.js +6 -0
- package/dist/components/tabs/index.js.map +1 -0
- package/dist/components/toast/index.cjs +24 -0
- package/dist/components/toast/index.cjs.map +1 -0
- package/dist/components/toast/index.d.cts +49 -0
- package/dist/components/toast/index.d.ts +49 -0
- package/dist/components/toast/index.js +3 -0
- package/dist/components/toast/index.js.map +1 -0
- package/dist/index.cjs +698 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +402 -0
- package/dist/index.d.ts +402 -0
- package/dist/index.js +426 -0
- package/dist/index.js.map +1 -0
- 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"]}
|