@umodeapporg/ui 0.12.0 → 0.13.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/dist/components/Form/ControlledButton.d.ts +12 -0
- package/dist/components/Form/ControlledButton.js +10 -5
- package/dist/components/Form/ControlledButton.js.map +1 -1
- package/dist/components/Form/ControlledDialog.d.ts +9 -2
- package/dist/components/Form/ControlledDialog.js +14 -8
- package/dist/components/Form/ControlledDialog.js.map +1 -1
- package/dist/components/Form/ControlledDialogPrimitives.d.ts +23 -0
- package/dist/components/Form/ControlledDialogPrimitives.js +114 -5
- package/dist/components/Form/ControlledDialogPrimitives.js.map +1 -1
- package/dist/components/Form/ControlledSelect.d.ts +11 -2
- package/dist/components/Form/ControlledSelect.js +273 -48
- package/dist/components/Form/ControlledSelect.js.map +1 -1
- package/dist/components/Form/ControlledSwitch.d.ts +9 -2
- package/dist/components/Form/ControlledSwitch.js +88 -69
- package/dist/components/Form/ControlledSwitch.js.map +1 -1
- package/dist/components/Navigation/LinkButton.d.ts +26 -0
- package/dist/components/Navigation/LinkButton.js +82 -0
- package/dist/components/Navigation/LinkButton.js.map +1 -0
- package/dist/components/UI/ControlledMenu.d.ts +6 -0
- package/dist/components/UI/ControlledMenu.js +3 -4
- package/dist/components/UI/ControlledMenu.js.map +1 -1
- package/dist/components/UI/DataTable/DataTable.js +1 -0
- package/dist/components/UI/DataTable/DataTable.js.map +1 -1
- package/dist/components/UI/DataTable/DataTableBulkEditRowsPicker.js +2 -1
- package/dist/components/UI/DataTable/DataTableBulkEditRowsPicker.js.map +1 -1
- package/dist/components/UI/DataTable/DataTableFilters.js +1 -0
- package/dist/components/UI/DataTable/DataTableFilters.js.map +1 -1
- package/dist/components/UI/DataTable/DataTableToolbar.js +2 -1
- package/dist/components/UI/DataTable/DataTableToolbar.js.map +1 -1
- package/dist/components/UI/DataTable/applyDataTableFilters.js +1 -0
- package/dist/components/UI/DataTable/applyDataTableFilters.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +50 -47
- package/dist/index.js.map +1 -1
- package/package.json +9 -2
|
@@ -8,29 +8,13 @@ import { ControlledMenuItem, ControlledMenuRoot, ControlledMenuHandler, Controll
|
|
|
8
8
|
import ControlledTooltip from "../UI/ControlledTooltip.js";
|
|
9
9
|
import { capitalizeAllLetters, formatFilter } from "../../helpers/stringHelpers.js";
|
|
10
10
|
import { uiLogger } from "../../helpers/uiLogger.js";
|
|
11
|
-
import "
|
|
11
|
+
import UnifiedSearchBar from "../Search/UnifiedSearchBar.js";
|
|
12
12
|
import OutlinedChip from "../UI/Chips/OutlinedChip.js";
|
|
13
|
+
import LoadingSpinner from "../UI/LoadingSpinner.js";
|
|
13
14
|
import { MaterialSymbol } from "../UI/MaterialSymbol/index.js";
|
|
14
|
-
import "date-fns";
|
|
15
|
-
import "date-fns/locale";
|
|
16
|
-
import "react-day-picker";
|
|
17
|
-
import "tailwind-merge";
|
|
18
|
-
import "../UI/ControlledPopover.js";
|
|
19
|
-
import ControlledInput from "./ControlledInput.js";
|
|
20
|
-
import "react-day-picker/style.css";
|
|
21
|
-
import "../DateTime/DateRangePicker.js";
|
|
22
|
-
import ControlledCheckbox from "./ControlledCheckbox.js";
|
|
23
15
|
import NoDataFound from "../UI/NoDataFound/index.js";
|
|
24
|
-
import "
|
|
25
|
-
import "
|
|
26
|
-
import "./ControlledDialog.js";
|
|
27
|
-
import "motion/react";
|
|
28
|
-
import "react-dom";
|
|
29
|
-
import "../../motion/motionPresets.js";
|
|
30
|
-
import "../../motion/motionTokens.js";
|
|
31
|
-
import "../Navigation/UiNavigationContext.js";
|
|
32
|
-
import UnifiedSearchBar from "../Search/UnifiedSearchBar.js";
|
|
33
|
-
import LoadingSpinner from "../UI/LoadingSpinner.js";
|
|
16
|
+
import ControlledCheckbox from "./ControlledCheckbox.js";
|
|
17
|
+
import ControlledInput from "./ControlledInput.js";
|
|
34
18
|
/* empty css */
|
|
35
19
|
const ITEM_HEIGHT = 40;
|
|
36
20
|
const MAX_LIST_HEIGHT = 300;
|
|
@@ -38,6 +22,9 @@ const OVERSCAN_COUNT = 10;
|
|
|
38
22
|
const SEARCH_DEBOUNCE_MS = 300;
|
|
39
23
|
const MAX_IN_FIELD_CHIPS = 2;
|
|
40
24
|
const SEARCH_INPUT_MARKER_CLASS = "controlled-select-search-input";
|
|
25
|
+
const TYPEAHEAD_RESET_MS = 500;
|
|
26
|
+
const FOCUS_RETRY_FRAMES = 6;
|
|
27
|
+
const isPrintableKey = (event) => event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey;
|
|
41
28
|
const SelectSearchSlot = ({ children }) => /* @__PURE__ */ jsx("div", { className: "px-1 py-0.5", onClick: (event) => event.stopPropagation(), children });
|
|
42
29
|
function ControlledSelect({
|
|
43
30
|
label,
|
|
@@ -67,7 +54,14 @@ function ControlledSelect({
|
|
|
67
54
|
ariaRequired,
|
|
68
55
|
inputClassName,
|
|
69
56
|
matchTriggerWidth = false,
|
|
70
|
-
compact = false
|
|
57
|
+
compact = false,
|
|
58
|
+
id,
|
|
59
|
+
name,
|
|
60
|
+
required,
|
|
61
|
+
onBlur,
|
|
62
|
+
onFocus,
|
|
63
|
+
autoFocus,
|
|
64
|
+
...triggerAttributes
|
|
71
65
|
}) {
|
|
72
66
|
const { t } = useTranslation("ui");
|
|
73
67
|
const options = useMemo(() => Array.isArray(optionsProp) ? optionsProp : [], [optionsProp]);
|
|
@@ -102,6 +96,12 @@ function ControlledSelect({
|
|
|
102
96
|
const [isOpen, setIsOpen] = useState(false);
|
|
103
97
|
const menuListRef = useRef(null);
|
|
104
98
|
const triggerRef = useRef(null);
|
|
99
|
+
const triggerInputRef = useRef(null);
|
|
100
|
+
const virtualListRef = useRef(null);
|
|
101
|
+
const [activeIndex, setActiveIndex] = useState(-1);
|
|
102
|
+
const [focusRequest, setFocusRequest] = useState(0);
|
|
103
|
+
const pendingFocusIndexRef = useRef(null);
|
|
104
|
+
const typeaheadRef = useRef({ buffer: "", timer: 0 });
|
|
105
105
|
const [currentSearchText, setCurrentSearchText] = useState("");
|
|
106
106
|
const scrollableContainerId = useId();
|
|
107
107
|
const scrollableContainerRef = useRef(null);
|
|
@@ -210,8 +210,216 @@ function ControlledSelect({
|
|
|
210
210
|
return formatLabel(foundOption?.label || selected || label);
|
|
211
211
|
}
|
|
212
212
|
}, [multiple, selected, options, label, formatLabel]);
|
|
213
|
+
const getOptionId = useCallback(
|
|
214
|
+
(index) => `${scrollableContainerId}-option-${index}`,
|
|
215
|
+
[scrollableContainerId]
|
|
216
|
+
);
|
|
217
|
+
const isOptionEnabled = useCallback(
|
|
218
|
+
(index) => {
|
|
219
|
+
const option = filteredOptions[index];
|
|
220
|
+
return Boolean(option) && !option.disabled;
|
|
221
|
+
},
|
|
222
|
+
[filteredOptions]
|
|
223
|
+
);
|
|
224
|
+
const findEnabledIndex = useCallback(
|
|
225
|
+
(start, step) => {
|
|
226
|
+
for (let index = start; index >= 0 && index < filteredOptions.length; index += step) {
|
|
227
|
+
if (isOptionEnabled(index)) return index;
|
|
228
|
+
}
|
|
229
|
+
return -1;
|
|
230
|
+
},
|
|
231
|
+
[filteredOptions.length, isOptionEnabled]
|
|
232
|
+
);
|
|
233
|
+
const getSelectedIndex = useCallback(() => {
|
|
234
|
+
const firstSelected = multiple ? selected[0] : selected;
|
|
235
|
+
const index = filteredOptions.findIndex((option) => option.value === firstSelected);
|
|
236
|
+
return index >= 0 && isOptionEnabled(index) ? index : -1;
|
|
237
|
+
}, [multiple, selected, filteredOptions, isOptionEnabled]);
|
|
238
|
+
const focusOptionAt = useCallback((index) => {
|
|
239
|
+
if (index < 0) return;
|
|
240
|
+
setActiveIndex(index);
|
|
241
|
+
pendingFocusIndexRef.current = index;
|
|
242
|
+
setFocusRequest((request) => request + 1);
|
|
243
|
+
}, []);
|
|
244
|
+
const focusSearchInput = useCallback(() => {
|
|
245
|
+
const searchInput = menuListRef.current?.querySelector(`.${SEARCH_INPUT_MARKER_CLASS}`);
|
|
246
|
+
if (!searchInput) return false;
|
|
247
|
+
searchInput.focus();
|
|
248
|
+
return true;
|
|
249
|
+
}, []);
|
|
250
|
+
const findTypeaheadMatch = useCallback(
|
|
251
|
+
(key, fromIndex) => {
|
|
252
|
+
const state = typeaheadRef.current;
|
|
253
|
+
window.clearTimeout(state.timer);
|
|
254
|
+
state.buffer += key.toLowerCase();
|
|
255
|
+
state.timer = window.setTimeout(() => {
|
|
256
|
+
state.buffer = "";
|
|
257
|
+
}, TYPEAHEAD_RESET_MS);
|
|
258
|
+
const isCycling = state.buffer.split("").every((char) => char === state.buffer[0]);
|
|
259
|
+
const term = isCycling ? state.buffer[0] : state.buffer;
|
|
260
|
+
const start = isCycling ? fromIndex + 1 : Math.max(fromIndex, 0);
|
|
261
|
+
const total = filteredOptions.length;
|
|
262
|
+
for (let offset = 0; offset < total; offset += 1) {
|
|
263
|
+
const index = (start + offset) % total;
|
|
264
|
+
const label2 = formatLabel(filteredOptions[index]?.label).toLowerCase();
|
|
265
|
+
if (isOptionEnabled(index) && label2.startsWith(term)) return index;
|
|
266
|
+
}
|
|
267
|
+
return -1;
|
|
268
|
+
},
|
|
269
|
+
[filteredOptions, formatLabel, isOptionEnabled]
|
|
270
|
+
);
|
|
271
|
+
const openAndFocus = useCallback(
|
|
272
|
+
(target) => {
|
|
273
|
+
if (disabled) return;
|
|
274
|
+
if (!isOpen) {
|
|
275
|
+
setIsOpen(true);
|
|
276
|
+
onOpen?.();
|
|
277
|
+
}
|
|
278
|
+
if (searchable && target === "selected") {
|
|
279
|
+
pendingFocusIndexRef.current = -1;
|
|
280
|
+
setFocusRequest((request) => request + 1);
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
let index;
|
|
284
|
+
if (typeof target === "number") index = target;
|
|
285
|
+
else if (target === "first") index = findEnabledIndex(0, 1);
|
|
286
|
+
else if (target === "last") index = findEnabledIndex(filteredOptions.length - 1, -1);
|
|
287
|
+
else index = getSelectedIndex() >= 0 ? getSelectedIndex() : findEnabledIndex(0, 1);
|
|
288
|
+
focusOptionAt(index);
|
|
289
|
+
},
|
|
290
|
+
[disabled, isOpen, onOpen, searchable, findEnabledIndex, filteredOptions.length, getSelectedIndex, focusOptionAt]
|
|
291
|
+
);
|
|
292
|
+
useEffect(() => {
|
|
293
|
+
const index = pendingFocusIndexRef.current;
|
|
294
|
+
if (!isOpen || index === null) return void 0;
|
|
295
|
+
if (index >= 0) virtualListRef.current?.scrollToItem(index);
|
|
296
|
+
let attempts = 0;
|
|
297
|
+
let frame = 0;
|
|
298
|
+
const tryFocus = () => {
|
|
299
|
+
if (index < 0) {
|
|
300
|
+
if (focusSearchInput()) {
|
|
301
|
+
pendingFocusIndexRef.current = null;
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
} else {
|
|
305
|
+
const node = document.getElementById(getOptionId(index));
|
|
306
|
+
if (node) {
|
|
307
|
+
pendingFocusIndexRef.current = null;
|
|
308
|
+
node.focus({ preventScroll: Boolean(virtualListRef.current) });
|
|
309
|
+
if (!virtualListRef.current) node.scrollIntoView?.({ block: "nearest" });
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
attempts += 1;
|
|
314
|
+
if (attempts < FOCUS_RETRY_FRAMES) frame = window.requestAnimationFrame(tryFocus);
|
|
315
|
+
};
|
|
316
|
+
tryFocus();
|
|
317
|
+
return () => window.cancelAnimationFrame(frame);
|
|
318
|
+
}, [isOpen, focusRequest]);
|
|
319
|
+
const wasOpenRef = useRef(false);
|
|
320
|
+
useEffect(() => {
|
|
321
|
+
if (isOpen) {
|
|
322
|
+
wasOpenRef.current = true;
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
if (!wasOpenRef.current) return;
|
|
326
|
+
wasOpenRef.current = false;
|
|
327
|
+
setActiveIndex(-1);
|
|
328
|
+
pendingFocusIndexRef.current = null;
|
|
329
|
+
const active = document.activeElement;
|
|
330
|
+
const focusWasInList = Boolean(active && menuListRef.current?.contains(active));
|
|
331
|
+
if (focusWasInList || active === document.body || active === null) {
|
|
332
|
+
triggerInputRef.current?.focus({ preventScroll: true });
|
|
333
|
+
}
|
|
334
|
+
}, [isOpen]);
|
|
335
|
+
useEffect(() => {
|
|
336
|
+
setActiveIndex(-1);
|
|
337
|
+
}, [filteredOptions]);
|
|
338
|
+
useEffect(() => {
|
|
339
|
+
if (!isOpen || activeIndex < 0) return;
|
|
340
|
+
const active = document.activeElement;
|
|
341
|
+
if (active === null || active === document.body) focusOptionAt(activeIndex);
|
|
342
|
+
}, [selected]);
|
|
343
|
+
useEffect(() => () => window.clearTimeout(typeaheadRef.current.timer), []);
|
|
344
|
+
const listboxLabel = triggerAttributes["aria-label"] ?? ariaLabel ?? label ?? (isOpen ? triggerInputRef.current?.labels?.[0]?.textContent?.trim() || void 0 : void 0);
|
|
345
|
+
const handleTriggerKeyDown = (event) => {
|
|
346
|
+
if (disabled) return;
|
|
347
|
+
switch (event.key) {
|
|
348
|
+
case "Enter":
|
|
349
|
+
case " ":
|
|
350
|
+
case "ArrowDown":
|
|
351
|
+
event.preventDefault();
|
|
352
|
+
openAndFocus(isOpen && event.key === "ArrowDown" && activeIndex >= 0 ? activeIndex : "selected");
|
|
353
|
+
return;
|
|
354
|
+
case "ArrowUp":
|
|
355
|
+
event.preventDefault();
|
|
356
|
+
openAndFocus(getSelectedIndex() >= 0 ? getSelectedIndex() : "last");
|
|
357
|
+
return;
|
|
358
|
+
case "Home":
|
|
359
|
+
event.preventDefault();
|
|
360
|
+
openAndFocus("first");
|
|
361
|
+
return;
|
|
362
|
+
case "End":
|
|
363
|
+
event.preventDefault();
|
|
364
|
+
openAndFocus("last");
|
|
365
|
+
return;
|
|
366
|
+
case "Escape":
|
|
367
|
+
if (!isOpen) return;
|
|
368
|
+
event.preventDefault();
|
|
369
|
+
setIsOpen(false);
|
|
370
|
+
return;
|
|
371
|
+
case "Tab":
|
|
372
|
+
if (isOpen) setIsOpen(false);
|
|
373
|
+
return;
|
|
374
|
+
default:
|
|
375
|
+
if (!isPrintableKey(event) || searchable) return;
|
|
376
|
+
event.preventDefault();
|
|
377
|
+
openAndFocus(findTypeaheadMatch(event.key, getSelectedIndex()));
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
const handleListKeyDown = (event) => {
|
|
381
|
+
const target = event.target;
|
|
382
|
+
const isOnSearch = target.classList.contains(SEARCH_INPUT_MARKER_CLASS);
|
|
383
|
+
const optionNode = target.closest('[role="option"]');
|
|
384
|
+
const currentIndex = optionNode ? Number(optionNode.dataset.optionIndex ?? -1) : activeIndex;
|
|
385
|
+
switch (event.key) {
|
|
386
|
+
case "ArrowDown": {
|
|
387
|
+
event.preventDefault();
|
|
388
|
+
const next = isOnSearch ? findEnabledIndex(0, 1) : findEnabledIndex(currentIndex + 1, 1);
|
|
389
|
+
focusOptionAt(next);
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
case "ArrowUp": {
|
|
393
|
+
if (isOnSearch) return;
|
|
394
|
+
event.preventDefault();
|
|
395
|
+
const previous = findEnabledIndex(currentIndex - 1, -1);
|
|
396
|
+
if (previous >= 0) focusOptionAt(previous);
|
|
397
|
+
else if (searchable) focusSearchInput();
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
case "Home":
|
|
401
|
+
case "End":
|
|
402
|
+
if (isOnSearch) return;
|
|
403
|
+
event.preventDefault();
|
|
404
|
+
focusOptionAt(event.key === "Home" ? findEnabledIndex(0, 1) : findEnabledIndex(filteredOptions.length - 1, -1));
|
|
405
|
+
return;
|
|
406
|
+
case "Escape":
|
|
407
|
+
event.preventDefault();
|
|
408
|
+
setIsOpen(false);
|
|
409
|
+
return;
|
|
410
|
+
case "Tab":
|
|
411
|
+
event.preventDefault();
|
|
412
|
+
setIsOpen(false);
|
|
413
|
+
triggerInputRef.current?.focus({ preventScroll: true });
|
|
414
|
+
return;
|
|
415
|
+
default:
|
|
416
|
+
if (isOnSearch || !isPrintableKey(event) || event.key === " ") return;
|
|
417
|
+
event.preventDefault();
|
|
418
|
+
focusOptionAt(findTypeaheadMatch(event.key, currentIndex));
|
|
419
|
+
}
|
|
420
|
+
};
|
|
213
421
|
const renderOptionItem = useCallback(
|
|
214
|
-
(option, style) => {
|
|
422
|
+
(option, index, style) => {
|
|
215
423
|
const isSelected = multiple ? Array.isArray(selected) && selected.includes(option.value) : option.value === selected;
|
|
216
424
|
const isDisabledWithReason = Boolean(option.disabled && option.tooltip);
|
|
217
425
|
const menuItem = /* @__PURE__ */ jsxs(
|
|
@@ -219,20 +427,32 @@ function ControlledSelect({
|
|
|
219
427
|
{
|
|
220
428
|
disabled: option.disabled,
|
|
221
429
|
onClick: (event) => handleSelect(option.value, event),
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
430
|
+
role: "option",
|
|
431
|
+
id: getOptionId(index),
|
|
432
|
+
tabIndex: -1,
|
|
433
|
+
"aria-selected": isSelected,
|
|
434
|
+
"aria-setsize": filteredOptions.length,
|
|
435
|
+
"aria-posinset": index + 1,
|
|
436
|
+
"data-option-index": index,
|
|
437
|
+
className: classNames(
|
|
438
|
+
"relative flex items-center p-2 pl-3 text-foreground",
|
|
439
|
+
"focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring",
|
|
440
|
+
{
|
|
441
|
+
// The hover variant has to live inside the selected branch: the menu item's own
|
|
442
|
+
// default hover would otherwise wipe the selected tint.
|
|
443
|
+
"bg-brand-soft font-medium text-brand-emphasis hover:!bg-brand-soft focus:!bg-brand-soft": isSelected,
|
|
444
|
+
"hover:!bg-muted": !isSelected
|
|
445
|
+
}
|
|
446
|
+
),
|
|
228
447
|
style: {
|
|
229
448
|
...isDisabledWithReason ? {} : style,
|
|
230
449
|
height: ITEM_HEIGHT - 1
|
|
231
450
|
},
|
|
232
451
|
children: [
|
|
233
452
|
isSelected && /* @__PURE__ */ jsx("span", { className: "absolute inset-y-1 left-0 w-0.5 rounded-full bg-brand" }),
|
|
234
|
-
multiple &&
|
|
235
|
-
|
|
453
|
+
multiple && // The option itself carries aria-selected; the checkbox is only its picture.
|
|
454
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "pointer-events-none", children: /* @__PURE__ */ jsx(ControlledCheckbox, { checked: isSelected, onChange: () => {
|
|
455
|
+
} }) }),
|
|
236
456
|
option.tooltip && !isDisabledWithReason && /* @__PURE__ */ jsx(ControlledTooltip, { content: option.tooltip, className: "w-full max-w-72", children: /* @__PURE__ */ jsx(MaterialSymbol, { icon: "info", size: 15, color: "rgb(var(--color-foreground-subtle))", className: "mr-1" }) }),
|
|
237
457
|
option.swatch && /* @__PURE__ */ jsx(
|
|
238
458
|
"span",
|
|
@@ -252,13 +472,13 @@ function ControlledSelect({
|
|
|
252
472
|
);
|
|
253
473
|
return isDisabledWithReason ? /* @__PURE__ */ jsx(ControlledTooltip, { content: option.tooltip, className: "z-[9999] max-w-72", children: /* @__PURE__ */ jsx("div", { style, children: menuItem }) }, option.value) : menuItem;
|
|
254
474
|
},
|
|
255
|
-
[multiple, selected, handleSelect, formatLabel]
|
|
475
|
+
[multiple, selected, handleSelect, formatLabel, getOptionId, filteredOptions.length]
|
|
256
476
|
);
|
|
257
477
|
const ItemRenderer = useCallback(
|
|
258
478
|
({ index, style }) => {
|
|
259
479
|
const option = filteredOptions[index];
|
|
260
480
|
if (!option) return null;
|
|
261
|
-
return renderOptionItem(option, style);
|
|
481
|
+
return renderOptionItem(option, index, style);
|
|
262
482
|
},
|
|
263
483
|
[filteredOptions, renderOptionItem]
|
|
264
484
|
);
|
|
@@ -376,7 +596,7 @@ function ControlledSelect({
|
|
|
376
596
|
},
|
|
377
597
|
dismiss: { itemPress: !multiple },
|
|
378
598
|
children: [
|
|
379
|
-
/* @__PURE__ */ jsx(ControlledMenuHandler, { children: /* @__PURE__ */ jsxs(
|
|
599
|
+
/* @__PURE__ */ jsx(ControlledMenuHandler, { skipTriggerAria: true, children: /* @__PURE__ */ jsxs(
|
|
380
600
|
"div",
|
|
381
601
|
{
|
|
382
602
|
className: classNames("flex", {
|
|
@@ -430,21 +650,18 @@ function ControlledSelect({
|
|
|
430
650
|
/* @__PURE__ */ jsx(
|
|
431
651
|
ControlledInput,
|
|
432
652
|
{
|
|
653
|
+
...triggerAttributes,
|
|
654
|
+
id,
|
|
655
|
+
inputRef: triggerInputRef,
|
|
656
|
+
onBlur,
|
|
657
|
+
onFocus,
|
|
658
|
+
autoFocus,
|
|
433
659
|
readOnly: true,
|
|
434
660
|
role: "combobox",
|
|
435
661
|
"aria-haspopup": "listbox",
|
|
436
662
|
"aria-expanded": isOpen,
|
|
437
|
-
"aria-controls": scrollableContainerId,
|
|
438
|
-
onKeyDown:
|
|
439
|
-
if (disabled) return;
|
|
440
|
-
if (event.key === "Enter" || event.key === " " || event.key === "ArrowDown") {
|
|
441
|
-
event.preventDefault();
|
|
442
|
-
setIsOpen(true);
|
|
443
|
-
onOpen?.();
|
|
444
|
-
} else if (event.key === "Escape" && isOpen) {
|
|
445
|
-
setIsOpen(false);
|
|
446
|
-
}
|
|
447
|
-
},
|
|
663
|
+
"aria-controls": isOpen ? scrollableContainerId : void 0,
|
|
664
|
+
onKeyDown: handleTriggerKeyDown,
|
|
448
665
|
className: classNames(
|
|
449
666
|
"controlled-select-input flex w-full min-w-0 justify-between truncate text-left",
|
|
450
667
|
!compact && "tablet:min-w-52",
|
|
@@ -458,8 +675,8 @@ function ControlledSelect({
|
|
|
458
675
|
autoComplete: "off",
|
|
459
676
|
rightSlot: /* @__PURE__ */ jsx(MaterialSymbol, { className: cursorClassName, icon: "keyboard_arrow_down", size: 15 }),
|
|
460
677
|
label: selectedHasValue && !persistentLabel ? void 0 : label,
|
|
461
|
-
"aria-label": ariaLabel ?? label,
|
|
462
|
-
"aria-required": ariaRequired ?
|
|
678
|
+
"aria-label": triggerAttributes["aria-label"] ?? ariaLabel ?? label,
|
|
679
|
+
"aria-required": ariaRequired || required ? true : triggerAttributes["aria-required"],
|
|
463
680
|
value: showInFieldChips ? " " : selectedHasValue || hasEmptyValueOption ? getDisplayLabel() : "",
|
|
464
681
|
disabled
|
|
465
682
|
}
|
|
@@ -473,6 +690,7 @@ function ControlledSelect({
|
|
|
473
690
|
}
|
|
474
691
|
) })
|
|
475
692
|
] }),
|
|
693
|
+
name && (multiple ? selected.map((value) => /* @__PURE__ */ jsx("input", { type: "hidden", name, value, disabled }, value)) : selected !== "" && /* @__PURE__ */ jsx("input", { type: "hidden", name, value: selected, disabled })),
|
|
476
694
|
(selectedItemsPosition === "bottom" || selectedItemsPosition === "right") && selectedItemsChips
|
|
477
695
|
]
|
|
478
696
|
}
|
|
@@ -481,6 +699,8 @@ function ControlledSelect({
|
|
|
481
699
|
ControlledMenuList,
|
|
482
700
|
{
|
|
483
701
|
ref: menuListRef,
|
|
702
|
+
role: "presentation",
|
|
703
|
+
onKeyDown: handleListKeyDown,
|
|
484
704
|
className: classNames("zIndexMax w-80 max-w-[calc(100vw-1.5rem)] overflow-hidden", listClassName),
|
|
485
705
|
children: [
|
|
486
706
|
searchable && /* @__PURE__ */ jsx(SelectSearchSlot, { children: /* @__PURE__ */ jsx(
|
|
@@ -507,6 +727,10 @@ function ControlledSelect({
|
|
|
507
727
|
"overflow-hidden": !infiniteScroll
|
|
508
728
|
}),
|
|
509
729
|
id: scrollableContainerId,
|
|
730
|
+
role: "listbox",
|
|
731
|
+
"aria-multiselectable": multiple || void 0,
|
|
732
|
+
"aria-labelledby": triggerAttributes["aria-labelledby"],
|
|
733
|
+
"aria-label": triggerAttributes["aria-labelledby"] ? void 0 : listboxLabel,
|
|
510
734
|
children: infiniteScroll ? /* @__PURE__ */ jsx(
|
|
511
735
|
InfiniteScroll,
|
|
512
736
|
{
|
|
@@ -516,11 +740,12 @@ function ControlledSelect({
|
|
|
516
740
|
loader: /* @__PURE__ */ jsx("div", { className: "flex size-full items-center justify-center py-1", children: /* @__PURE__ */ jsx(LoadingSpinner, { size: 4, label: t("controlledSelect.loadingOptions") }) }),
|
|
517
741
|
scrollableTarget: scrollableContainerId,
|
|
518
742
|
className: "!overflow-hidden",
|
|
519
|
-
children: filteredOptions.length > 0 ? filteredOptions.map((option) => renderOptionItem(option)) : loading ? /* @__PURE__ */ jsx("div", { className: "flex w-full items-center justify-center py-4", children: /* @__PURE__ */ jsx(LoadingSpinner, { size: 4, label: t("controlledSelect.loadingOptions") }) }) : /* @__PURE__ */ jsx(ControlledMenuItem, { disabled: true, children: /* @__PURE__ */ jsx(NoDataFound, { compact: true, message: t("controlledSelect.noDataToShow") }) })
|
|
743
|
+
children: filteredOptions.length > 0 ? filteredOptions.map((option, index) => renderOptionItem(option, index)) : loading ? /* @__PURE__ */ jsx("div", { className: "flex w-full items-center justify-center py-4", children: /* @__PURE__ */ jsx(LoadingSpinner, { size: 4, label: t("controlledSelect.loadingOptions") }) }) : /* @__PURE__ */ jsx(ControlledMenuItem, { disabled: true, role: "presentation", children: /* @__PURE__ */ jsx(NoDataFound, { compact: true, message: t("controlledSelect.noDataToShow") }) })
|
|
520
744
|
}
|
|
521
745
|
) : filteredOptions.length > 0 ? /* @__PURE__ */ jsx(
|
|
522
746
|
FixedSizeList,
|
|
523
747
|
{
|
|
748
|
+
ref: virtualListRef,
|
|
524
749
|
className: "virtualized-select-list",
|
|
525
750
|
height: listHeight,
|
|
526
751
|
width: "100%",
|
|
@@ -529,7 +754,7 @@ function ControlledSelect({
|
|
|
529
754
|
overscanCount: OVERSCAN_COUNT,
|
|
530
755
|
children: ItemRenderer
|
|
531
756
|
}
|
|
532
|
-
) : loading ? /* @__PURE__ */ jsx("div", { className: "flex w-full items-center justify-center py-4", children: /* @__PURE__ */ jsx(LoadingSpinner, { size: 4, label: t("controlledSelect.loadingOptions") }) }) : /* @__PURE__ */ jsx(ControlledMenuItem, { disabled: true, children: /* @__PURE__ */ jsx(NoDataFound, { compact: true, message: t("controlledSelect.noDataToShow") }) })
|
|
757
|
+
) : loading ? /* @__PURE__ */ jsx("div", { className: "flex w-full items-center justify-center py-4", children: /* @__PURE__ */ jsx(LoadingSpinner, { size: 4, label: t("controlledSelect.loadingOptions") }) }) : /* @__PURE__ */ jsx(ControlledMenuItem, { disabled: true, role: "presentation", children: /* @__PURE__ */ jsx(NoDataFound, { compact: true, message: t("controlledSelect.noDataToShow") }) })
|
|
533
758
|
}
|
|
534
759
|
)
|
|
535
760
|
]
|