@dbcdk/react-components 0.0.211 → 0.0.213
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 +6 -1
- package/dist/components/button/Button.cjs +2 -1
- package/dist/components/button/Button.d.ts +6 -1
- package/dist/components/button/Button.js +2 -1
- package/dist/components/button/Button.module.css +14 -0
- package/dist/components/color-scheme-button/ColorSchemeButton.cjs +2 -2
- package/dist/components/color-scheme-button/ColorSchemeButton.js +2 -2
- package/dist/components/forms/file-upload/FileUpload.cjs +4 -0
- package/dist/components/forms/file-upload/FileUpload.js +4 -0
- package/dist/components/forms/form-select/FormSelect.cjs +4 -0
- package/dist/components/forms/form-select/FormSelect.d.ts +1 -1
- package/dist/components/forms/form-select/FormSelect.js +4 -0
- package/dist/components/forms/input/Input.cjs +4 -0
- package/dist/components/forms/input/Input.js +4 -0
- package/dist/components/forms/input-container/InputContainer.cjs +1 -1
- package/dist/components/forms/input-container/InputContainer.js +1 -1
- package/dist/components/forms/input-container/InputContainer.module.css +12 -1
- package/dist/components/forms/radio-buttons/RadioButtonGroup.cjs +6 -5
- package/dist/components/forms/radio-buttons/RadioButtonGroup.js +2 -5
- package/dist/components/forms/radio-buttons/RadioButtons.module.css +15 -0
- package/dist/components/forms/select/Select.cjs +4 -0
- package/dist/components/forms/select/Select.d.ts +1 -1
- package/dist/components/forms/select/Select.js +4 -0
- package/dist/components/forms/text-area/Textarea.cjs +4 -0
- package/dist/components/forms/text-area/Textarea.js +4 -0
- package/dist/components/forms/typeahead/Typeahead.cjs +109 -42
- package/dist/components/forms/typeahead/Typeahead.d.ts +6 -0
- package/dist/components/forms/typeahead/Typeahead.js +109 -42
- package/dist/components/forms/typeahead/Typeahead.module.css +16 -0
- package/dist/components/headline/CollapsibleHeadline.cjs +15 -18
- package/dist/components/headline/CollapsibleHeadline.d.ts +2 -2
- package/dist/components/headline/CollapsibleHeadline.js +15 -18
- package/dist/components/headline/Headline.module.css +22 -0
- package/dist/components/sidebar/components/SidebarItem.cjs +2 -0
- package/dist/components/sidebar/components/SidebarItem.d.ts +2 -1
- package/dist/components/sidebar/components/SidebarItem.js +2 -0
- package/dist/components/sidebar/components/expandable-sidebar-item/ExpandableSidebarItem.cjs +2 -0
- package/dist/components/sidebar/components/expandable-sidebar-item/ExpandableSidebarItem.d.ts +2 -1
- package/dist/components/sidebar/components/expandable-sidebar-item/ExpandableSidebarItem.js +2 -0
- package/dist/components/sidebar/components/expandable-sidebar-item/ExpandableSidebarItem.module.css +1 -1
- package/dist/components/sidebar/components/sidebar-container/SidebarContainer.module.css +40 -0
- package/dist/components/sidebar/components/sidebar-item-content/SidebarItemContent.cjs +2 -0
- package/dist/components/sidebar/components/sidebar-item-content/SidebarItemContent.d.ts +2 -1
- package/dist/components/sidebar/components/sidebar-item-content/SidebarItemContent.js +2 -0
- package/dist/components/sidebar/components/sidebar-item-content/SidebarItemContent.module.css +9 -0
- package/dist/components/sidebar/components/sidebar-items/SidebarItems.cjs +6 -4
- package/dist/components/sidebar/components/sidebar-items/SidebarItems.js +6 -4
- package/dist/components/theme-script/ThemeScript.cjs +1 -1
- package/dist/components/theme-script/ThemeScript.d.ts +1 -1
- package/dist/components/theme-script/ThemeScript.js +1 -1
- package/dist/hooks/useColorScheme.cjs +1 -1
- package/dist/hooks/useColorScheme.js +1 -1
- package/dist/hooks/useGetActiveTheme.cjs +1 -1
- package/dist/hooks/useGetActiveTheme.js +1 -1
- package/dist/styles/themes/dbc/colors.css +4 -4
- package/package.json +1 -1
|
@@ -32,6 +32,23 @@ function _interopNamespace(e) {
|
|
|
32
32
|
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
33
33
|
var styles__default = /*#__PURE__*/_interopDefault(styles);
|
|
34
34
|
|
|
35
|
+
function orderByGroup(options) {
|
|
36
|
+
if (!options.some((option) => option.group)) return options;
|
|
37
|
+
const groups = /* @__PURE__ */ new Map();
|
|
38
|
+
for (const option of options) {
|
|
39
|
+
const group = option.group || void 0;
|
|
40
|
+
const entries = groups.get(group);
|
|
41
|
+
if (entries) entries.push(option);
|
|
42
|
+
else groups.set(group, [option]);
|
|
43
|
+
}
|
|
44
|
+
return Array.from(groups.values()).flat();
|
|
45
|
+
}
|
|
46
|
+
function findEnabledIndex(items, start, step) {
|
|
47
|
+
for (let index = start; index >= 0 && index < items.length; index += step) {
|
|
48
|
+
if (!items[index].disabled) return index;
|
|
49
|
+
}
|
|
50
|
+
return -1;
|
|
51
|
+
}
|
|
35
52
|
function parseLengthToPx(value, referenceFontSize = 16) {
|
|
36
53
|
if (!value) return 0;
|
|
37
54
|
const trimmed = value.trim();
|
|
@@ -197,16 +214,25 @@ function Typeahead({
|
|
|
197
214
|
filteredOptions = options;
|
|
198
215
|
} else {
|
|
199
216
|
filteredOptions = options.filter(
|
|
200
|
-
(option) =>
|
|
217
|
+
(option) => {
|
|
218
|
+
var _a2, _b, _c, _d;
|
|
219
|
+
return option.label.toLocaleLowerCase().includes(normalizedQuery) || ((_b = (_a2 = option.description) == null ? void 0 : _a2.toLocaleLowerCase().includes(normalizedQuery)) != null ? _b : false) || ((_d = (_c = option.group) == null ? void 0 : _c.toLocaleLowerCase().includes(normalizedQuery)) != null ? _d : false);
|
|
220
|
+
}
|
|
201
221
|
);
|
|
202
222
|
}
|
|
203
223
|
}
|
|
224
|
+
filteredOptions = orderByGroup(filteredOptions);
|
|
204
225
|
if (typeof maxVisibleOptions === "number" && Number.isFinite(maxVisibleOptions)) {
|
|
205
226
|
filteredOptions = filteredOptions.slice(0, Math.max(0, maxVisibleOptions));
|
|
206
227
|
}
|
|
228
|
+
const groupStarts = /* @__PURE__ */ new Map();
|
|
229
|
+
filteredOptions.forEach((option, index) => {
|
|
230
|
+
if (option.group && !groupStarts.has(option.group)) groupStarts.set(option.group, index);
|
|
231
|
+
});
|
|
207
232
|
const commitSelection = React__namespace.useCallback(
|
|
208
233
|
(option) => {
|
|
209
234
|
var _a2, _b;
|
|
235
|
+
if (option == null ? void 0 : option.disabled) return;
|
|
210
236
|
locallyClearedRef.current = false;
|
|
211
237
|
setHideSelectedHighlight(false);
|
|
212
238
|
if (mode === "multi") {
|
|
@@ -334,24 +360,21 @@ function Typeahead({
|
|
|
334
360
|
}, [selectedOption, selectedSingleText, mode]);
|
|
335
361
|
const getSelectedIndex = React__namespace.useCallback(
|
|
336
362
|
(items) => {
|
|
337
|
-
|
|
338
|
-
if (locallyClearedRef.current) return
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
}
|
|
344
|
-
const singleSelectedIndex = items.findIndex((option) => option.value === selectedValue);
|
|
345
|
-
return singleSelectedIndex >= 0 ? singleSelectedIndex : 0;
|
|
363
|
+
const firstEnabled = findEnabledIndex(items, 0, 1);
|
|
364
|
+
if (firstEnabled < 0 || locallyClearedRef.current) return firstEnabled;
|
|
365
|
+
const selectedIndex = items.findIndex(
|
|
366
|
+
(option) => !option.disabled && (mode === "multi" ? Array.isArray(selectedValue) && selectedValue.includes(option.value) : option.value === selectedValue)
|
|
367
|
+
);
|
|
368
|
+
return selectedIndex >= 0 ? selectedIndex : firstEnabled;
|
|
346
369
|
},
|
|
347
370
|
[mode, selectedValue]
|
|
348
371
|
);
|
|
349
372
|
React.useEffect(() => {
|
|
350
373
|
setActiveIndex((current) => {
|
|
351
|
-
if (
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
return
|
|
374
|
+
if (locallyClearedRef.current || current < 0 || !filteredOptions[current] || filteredOptions[current].disabled) {
|
|
375
|
+
return getSelectedIndex(filteredOptions);
|
|
376
|
+
}
|
|
377
|
+
return current;
|
|
355
378
|
});
|
|
356
379
|
}, [filteredOptions, getSelectedIndex]);
|
|
357
380
|
React.useEffect(() => {
|
|
@@ -452,7 +475,14 @@ function Typeahead({
|
|
|
452
475
|
}
|
|
453
476
|
const exactLabelMatch = options.find((option) => option.label === inputValue);
|
|
454
477
|
if (exactLabelMatch) {
|
|
455
|
-
|
|
478
|
+
if (exactLabelMatch.disabled) {
|
|
479
|
+
setInputValue(selectedSingleText);
|
|
480
|
+
setQuery("");
|
|
481
|
+
setOpen(false);
|
|
482
|
+
setActiveIndex(-1);
|
|
483
|
+
} else {
|
|
484
|
+
commitSelection(exactLabelMatch);
|
|
485
|
+
}
|
|
456
486
|
return;
|
|
457
487
|
}
|
|
458
488
|
if (allowCustomValue) {
|
|
@@ -484,7 +514,7 @@ function Typeahead({
|
|
|
484
514
|
if (justClearedRef.current) return;
|
|
485
515
|
setQuery("");
|
|
486
516
|
setOpen(true);
|
|
487
|
-
setActiveIndex(getSelectedIndex(options));
|
|
517
|
+
setActiveIndex(getSelectedIndex(orderByGroup(options)));
|
|
488
518
|
}, [getSelectedIndex, options]);
|
|
489
519
|
const openWithCurrentFilter = React__namespace.useCallback(() => {
|
|
490
520
|
if (justClearedRef.current) return;
|
|
@@ -526,7 +556,7 @@ function Typeahead({
|
|
|
526
556
|
if (isAlreadyFocused && mode === "single" && selectedOption && !locallyClearedRef.current) {
|
|
527
557
|
prepareSingleSearchInput();
|
|
528
558
|
setOpen(true);
|
|
529
|
-
setActiveIndex(getSelectedIndex(options));
|
|
559
|
+
setActiveIndex(getSelectedIndex(orderByGroup(options)));
|
|
530
560
|
} else if (isAlreadyFocused && !open) {
|
|
531
561
|
setOpen(true);
|
|
532
562
|
setActiveIndex(getSelectedIndex(filteredOptions));
|
|
@@ -573,7 +603,8 @@ function Typeahead({
|
|
|
573
603
|
setActiveIndex((index) => {
|
|
574
604
|
if (filteredOptions.length === 0) return -1;
|
|
575
605
|
if (index < 0) return getSelectedIndex(filteredOptions);
|
|
576
|
-
|
|
606
|
+
const next = findEnabledIndex(filteredOptions, index + 1, 1);
|
|
607
|
+
return next >= 0 ? next : index;
|
|
577
608
|
});
|
|
578
609
|
return;
|
|
579
610
|
case "ArrowUp":
|
|
@@ -583,20 +614,23 @@ function Typeahead({
|
|
|
583
614
|
return;
|
|
584
615
|
}
|
|
585
616
|
setActiveIndex((index) => {
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
617
|
+
const next = findEnabledIndex(
|
|
618
|
+
filteredOptions,
|
|
619
|
+
index < 0 ? filteredOptions.length - 1 : index - 1,
|
|
620
|
+
-1
|
|
621
|
+
);
|
|
622
|
+
return next >= 0 ? next : index;
|
|
589
623
|
});
|
|
590
624
|
return;
|
|
591
625
|
case "Home":
|
|
592
626
|
if (!open || filteredOptions.length === 0) return;
|
|
593
627
|
e.preventDefault();
|
|
594
|
-
setActiveIndex(0);
|
|
628
|
+
setActiveIndex(findEnabledIndex(filteredOptions, 0, 1));
|
|
595
629
|
return;
|
|
596
630
|
case "End":
|
|
597
631
|
if (!open || filteredOptions.length === 0) return;
|
|
598
632
|
e.preventDefault();
|
|
599
|
-
setActiveIndex(filteredOptions.length - 1);
|
|
633
|
+
setActiveIndex(findEnabledIndex(filteredOptions, filteredOptions.length - 1, -1));
|
|
600
634
|
return;
|
|
601
635
|
case "Enter":
|
|
602
636
|
if (!open) return;
|
|
@@ -650,7 +684,7 @@ function Typeahead({
|
|
|
650
684
|
if (nextOpen) {
|
|
651
685
|
if (mode === "single" && selectedOption && !locallyClearedRef.current) {
|
|
652
686
|
setQuery("");
|
|
653
|
-
setActiveIndex(getSelectedIndex(options));
|
|
687
|
+
setActiveIndex(getSelectedIndex(orderByGroup(options)));
|
|
654
688
|
} else {
|
|
655
689
|
setActiveIndex(getSelectedIndex(filteredOptions));
|
|
656
690
|
}
|
|
@@ -768,41 +802,74 @@ function Typeahead({
|
|
|
768
802
|
const isActive = index === activeIndex;
|
|
769
803
|
const isSelected = mode === "multi" ? Array.isArray(selectedValue) && selectedValue.includes(option.value) : option.value === selectedValue && !hideSelectedHighlight;
|
|
770
804
|
const optionId = `${listboxId}-option-${index}`;
|
|
771
|
-
|
|
805
|
+
const groupId = option.group ? `${listboxId}-group-${groupStarts.get(option.group)}` : void 0;
|
|
806
|
+
const startsGroup = !!option.group && groupStarts.get(option.group) === index;
|
|
807
|
+
const descriptionId = option.description ? `${optionId}-description` : void 0;
|
|
808
|
+
const describedBy = [groupId, descriptionId].filter(Boolean).join(" ") || void 0;
|
|
809
|
+
const labelContent = /* @__PURE__ */ jsxRuntime.jsxs("span", { className: styles__default.default.optionText, children: [
|
|
810
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: option.label }),
|
|
811
|
+
option.description && /* @__PURE__ */ jsxRuntime.jsx("span", { id: descriptionId, className: styles__default.default.description, children: option.description })
|
|
812
|
+
] });
|
|
813
|
+
const item = mode === "multi" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
772
814
|
Menu.Menu.CheckItem,
|
|
773
815
|
{
|
|
774
816
|
checked: isSelected,
|
|
817
|
+
disabled: option.disabled,
|
|
775
818
|
active: isActive,
|
|
776
819
|
interactiveProps: {
|
|
777
820
|
id: optionId,
|
|
778
821
|
role: "option",
|
|
779
|
-
|
|
822
|
+
"aria-describedby": describedBy,
|
|
823
|
+
"aria-label": option.label,
|
|
824
|
+
"aria-disabled": option.disabled || void 0,
|
|
825
|
+
onMouseEnter: () => {
|
|
826
|
+
if (!option.disabled) setActiveIndex(index);
|
|
827
|
+
},
|
|
780
828
|
onMouseDown: (e) => {
|
|
781
829
|
interactingWithOptionsRef.current = true;
|
|
782
830
|
e.preventDefault();
|
|
783
831
|
},
|
|
784
832
|
onKeyDown: (e) => handleTabKeyDown(e)
|
|
785
833
|
},
|
|
786
|
-
label:
|
|
834
|
+
label: labelContent,
|
|
787
835
|
onCheckedChange: () => commitSelection(option)
|
|
788
836
|
},
|
|
789
837
|
option.value
|
|
790
|
-
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
791
|
-
|
|
838
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
839
|
+
Menu.Menu.Item,
|
|
792
840
|
{
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
841
|
+
active: isActive,
|
|
842
|
+
selected: isSelected,
|
|
843
|
+
disabled: option.disabled,
|
|
844
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
845
|
+
"button",
|
|
846
|
+
{
|
|
847
|
+
id: optionId,
|
|
848
|
+
type: "button",
|
|
849
|
+
role: "option",
|
|
850
|
+
"aria-selected": isSelected,
|
|
851
|
+
"aria-describedby": describedBy,
|
|
852
|
+
"aria-label": option.label,
|
|
853
|
+
"aria-disabled": option.disabled || void 0,
|
|
854
|
+
disabled: option.disabled,
|
|
855
|
+
onMouseEnter: () => {
|
|
856
|
+
if (!option.disabled) setActiveIndex(index);
|
|
857
|
+
},
|
|
858
|
+
onMouseDown: (e) => {
|
|
859
|
+
e.preventDefault();
|
|
860
|
+
},
|
|
861
|
+
onKeyDown: (e) => handleTabKeyDown(e),
|
|
862
|
+
onClick: () => commitSelection(option),
|
|
863
|
+
children: labelContent
|
|
864
|
+
}
|
|
865
|
+
)
|
|
866
|
+
},
|
|
867
|
+
option.value
|
|
868
|
+
);
|
|
869
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(React__namespace.Fragment, { children: [
|
|
870
|
+
startsGroup && /* @__PURE__ */ jsxRuntime.jsx(Menu.Menu.Header, { id: groupId, children: option.group }),
|
|
871
|
+
item
|
|
872
|
+
] }, option.value);
|
|
806
873
|
}) : /* @__PURE__ */ jsxRuntime.jsx(Menu.Menu.Item, { disabled: true, children: emptyMessage }) })
|
|
807
874
|
}
|
|
808
875
|
),
|
|
@@ -3,6 +3,12 @@ import { InputProps, InputVariant } from '../../../components/forms/input/Input'
|
|
|
3
3
|
type TypeaheadOption<T> = {
|
|
4
4
|
label: string;
|
|
5
5
|
value: T;
|
|
6
|
+
/** Optional section header; groups follow their first occurrence in the list. */
|
|
7
|
+
group?: string;
|
|
8
|
+
/** Secondary option text, included in default search but not the selected input value. */
|
|
9
|
+
description?: string;
|
|
10
|
+
/** Visible but unavailable for selection by pointer, keyboard, or exact-label commit. */
|
|
11
|
+
disabled?: boolean;
|
|
6
12
|
};
|
|
7
13
|
type MultiValueDisplayMode = 'chips' | 'count';
|
|
8
14
|
type MultiSelectedValuesDisplayMode = 'hidden' | 'below-input';
|
|
@@ -8,6 +8,23 @@ import { Menu } from '../../../components/menu/Menu';
|
|
|
8
8
|
import { Popover } from '../../../components/popover/Popover';
|
|
9
9
|
import styles from './Typeahead.module.css';
|
|
10
10
|
|
|
11
|
+
function orderByGroup(options) {
|
|
12
|
+
if (!options.some((option) => option.group)) return options;
|
|
13
|
+
const groups = /* @__PURE__ */ new Map();
|
|
14
|
+
for (const option of options) {
|
|
15
|
+
const group = option.group || void 0;
|
|
16
|
+
const entries = groups.get(group);
|
|
17
|
+
if (entries) entries.push(option);
|
|
18
|
+
else groups.set(group, [option]);
|
|
19
|
+
}
|
|
20
|
+
return Array.from(groups.values()).flat();
|
|
21
|
+
}
|
|
22
|
+
function findEnabledIndex(items, start, step) {
|
|
23
|
+
for (let index = start; index >= 0 && index < items.length; index += step) {
|
|
24
|
+
if (!items[index].disabled) return index;
|
|
25
|
+
}
|
|
26
|
+
return -1;
|
|
27
|
+
}
|
|
11
28
|
function parseLengthToPx(value, referenceFontSize = 16) {
|
|
12
29
|
if (!value) return 0;
|
|
13
30
|
const trimmed = value.trim();
|
|
@@ -173,16 +190,25 @@ function Typeahead({
|
|
|
173
190
|
filteredOptions = options;
|
|
174
191
|
} else {
|
|
175
192
|
filteredOptions = options.filter(
|
|
176
|
-
(option) =>
|
|
193
|
+
(option) => {
|
|
194
|
+
var _a2, _b, _c, _d;
|
|
195
|
+
return option.label.toLocaleLowerCase().includes(normalizedQuery) || ((_b = (_a2 = option.description) == null ? void 0 : _a2.toLocaleLowerCase().includes(normalizedQuery)) != null ? _b : false) || ((_d = (_c = option.group) == null ? void 0 : _c.toLocaleLowerCase().includes(normalizedQuery)) != null ? _d : false);
|
|
196
|
+
}
|
|
177
197
|
);
|
|
178
198
|
}
|
|
179
199
|
}
|
|
200
|
+
filteredOptions = orderByGroup(filteredOptions);
|
|
180
201
|
if (typeof maxVisibleOptions === "number" && Number.isFinite(maxVisibleOptions)) {
|
|
181
202
|
filteredOptions = filteredOptions.slice(0, Math.max(0, maxVisibleOptions));
|
|
182
203
|
}
|
|
204
|
+
const groupStarts = /* @__PURE__ */ new Map();
|
|
205
|
+
filteredOptions.forEach((option, index) => {
|
|
206
|
+
if (option.group && !groupStarts.has(option.group)) groupStarts.set(option.group, index);
|
|
207
|
+
});
|
|
183
208
|
const commitSelection = React.useCallback(
|
|
184
209
|
(option) => {
|
|
185
210
|
var _a2, _b;
|
|
211
|
+
if (option == null ? void 0 : option.disabled) return;
|
|
186
212
|
locallyClearedRef.current = false;
|
|
187
213
|
setHideSelectedHighlight(false);
|
|
188
214
|
if (mode === "multi") {
|
|
@@ -310,24 +336,21 @@ function Typeahead({
|
|
|
310
336
|
}, [selectedOption, selectedSingleText, mode]);
|
|
311
337
|
const getSelectedIndex = React.useCallback(
|
|
312
338
|
(items) => {
|
|
313
|
-
|
|
314
|
-
if (locallyClearedRef.current) return
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
}
|
|
320
|
-
const singleSelectedIndex = items.findIndex((option) => option.value === selectedValue);
|
|
321
|
-
return singleSelectedIndex >= 0 ? singleSelectedIndex : 0;
|
|
339
|
+
const firstEnabled = findEnabledIndex(items, 0, 1);
|
|
340
|
+
if (firstEnabled < 0 || locallyClearedRef.current) return firstEnabled;
|
|
341
|
+
const selectedIndex = items.findIndex(
|
|
342
|
+
(option) => !option.disabled && (mode === "multi" ? Array.isArray(selectedValue) && selectedValue.includes(option.value) : option.value === selectedValue)
|
|
343
|
+
);
|
|
344
|
+
return selectedIndex >= 0 ? selectedIndex : firstEnabled;
|
|
322
345
|
},
|
|
323
346
|
[mode, selectedValue]
|
|
324
347
|
);
|
|
325
348
|
useEffect(() => {
|
|
326
349
|
setActiveIndex((current) => {
|
|
327
|
-
if (
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
return
|
|
350
|
+
if (locallyClearedRef.current || current < 0 || !filteredOptions[current] || filteredOptions[current].disabled) {
|
|
351
|
+
return getSelectedIndex(filteredOptions);
|
|
352
|
+
}
|
|
353
|
+
return current;
|
|
331
354
|
});
|
|
332
355
|
}, [filteredOptions, getSelectedIndex]);
|
|
333
356
|
useEffect(() => {
|
|
@@ -428,7 +451,14 @@ function Typeahead({
|
|
|
428
451
|
}
|
|
429
452
|
const exactLabelMatch = options.find((option) => option.label === inputValue);
|
|
430
453
|
if (exactLabelMatch) {
|
|
431
|
-
|
|
454
|
+
if (exactLabelMatch.disabled) {
|
|
455
|
+
setInputValue(selectedSingleText);
|
|
456
|
+
setQuery("");
|
|
457
|
+
setOpen(false);
|
|
458
|
+
setActiveIndex(-1);
|
|
459
|
+
} else {
|
|
460
|
+
commitSelection(exactLabelMatch);
|
|
461
|
+
}
|
|
432
462
|
return;
|
|
433
463
|
}
|
|
434
464
|
if (allowCustomValue) {
|
|
@@ -460,7 +490,7 @@ function Typeahead({
|
|
|
460
490
|
if (justClearedRef.current) return;
|
|
461
491
|
setQuery("");
|
|
462
492
|
setOpen(true);
|
|
463
|
-
setActiveIndex(getSelectedIndex(options));
|
|
493
|
+
setActiveIndex(getSelectedIndex(orderByGroup(options)));
|
|
464
494
|
}, [getSelectedIndex, options]);
|
|
465
495
|
const openWithCurrentFilter = React.useCallback(() => {
|
|
466
496
|
if (justClearedRef.current) return;
|
|
@@ -502,7 +532,7 @@ function Typeahead({
|
|
|
502
532
|
if (isAlreadyFocused && mode === "single" && selectedOption && !locallyClearedRef.current) {
|
|
503
533
|
prepareSingleSearchInput();
|
|
504
534
|
setOpen(true);
|
|
505
|
-
setActiveIndex(getSelectedIndex(options));
|
|
535
|
+
setActiveIndex(getSelectedIndex(orderByGroup(options)));
|
|
506
536
|
} else if (isAlreadyFocused && !open) {
|
|
507
537
|
setOpen(true);
|
|
508
538
|
setActiveIndex(getSelectedIndex(filteredOptions));
|
|
@@ -549,7 +579,8 @@ function Typeahead({
|
|
|
549
579
|
setActiveIndex((index) => {
|
|
550
580
|
if (filteredOptions.length === 0) return -1;
|
|
551
581
|
if (index < 0) return getSelectedIndex(filteredOptions);
|
|
552
|
-
|
|
582
|
+
const next = findEnabledIndex(filteredOptions, index + 1, 1);
|
|
583
|
+
return next >= 0 ? next : index;
|
|
553
584
|
});
|
|
554
585
|
return;
|
|
555
586
|
case "ArrowUp":
|
|
@@ -559,20 +590,23 @@ function Typeahead({
|
|
|
559
590
|
return;
|
|
560
591
|
}
|
|
561
592
|
setActiveIndex((index) => {
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
593
|
+
const next = findEnabledIndex(
|
|
594
|
+
filteredOptions,
|
|
595
|
+
index < 0 ? filteredOptions.length - 1 : index - 1,
|
|
596
|
+
-1
|
|
597
|
+
);
|
|
598
|
+
return next >= 0 ? next : index;
|
|
565
599
|
});
|
|
566
600
|
return;
|
|
567
601
|
case "Home":
|
|
568
602
|
if (!open || filteredOptions.length === 0) return;
|
|
569
603
|
e.preventDefault();
|
|
570
|
-
setActiveIndex(0);
|
|
604
|
+
setActiveIndex(findEnabledIndex(filteredOptions, 0, 1));
|
|
571
605
|
return;
|
|
572
606
|
case "End":
|
|
573
607
|
if (!open || filteredOptions.length === 0) return;
|
|
574
608
|
e.preventDefault();
|
|
575
|
-
setActiveIndex(filteredOptions.length - 1);
|
|
609
|
+
setActiveIndex(findEnabledIndex(filteredOptions, filteredOptions.length - 1, -1));
|
|
576
610
|
return;
|
|
577
611
|
case "Enter":
|
|
578
612
|
if (!open) return;
|
|
@@ -626,7 +660,7 @@ function Typeahead({
|
|
|
626
660
|
if (nextOpen) {
|
|
627
661
|
if (mode === "single" && selectedOption && !locallyClearedRef.current) {
|
|
628
662
|
setQuery("");
|
|
629
|
-
setActiveIndex(getSelectedIndex(options));
|
|
663
|
+
setActiveIndex(getSelectedIndex(orderByGroup(options)));
|
|
630
664
|
} else {
|
|
631
665
|
setActiveIndex(getSelectedIndex(filteredOptions));
|
|
632
666
|
}
|
|
@@ -744,41 +778,74 @@ function Typeahead({
|
|
|
744
778
|
const isActive = index === activeIndex;
|
|
745
779
|
const isSelected = mode === "multi" ? Array.isArray(selectedValue) && selectedValue.includes(option.value) : option.value === selectedValue && !hideSelectedHighlight;
|
|
746
780
|
const optionId = `${listboxId}-option-${index}`;
|
|
747
|
-
|
|
781
|
+
const groupId = option.group ? `${listboxId}-group-${groupStarts.get(option.group)}` : void 0;
|
|
782
|
+
const startsGroup = !!option.group && groupStarts.get(option.group) === index;
|
|
783
|
+
const descriptionId = option.description ? `${optionId}-description` : void 0;
|
|
784
|
+
const describedBy = [groupId, descriptionId].filter(Boolean).join(" ") || void 0;
|
|
785
|
+
const labelContent = /* @__PURE__ */ jsxs("span", { className: styles.optionText, children: [
|
|
786
|
+
/* @__PURE__ */ jsx("span", { children: option.label }),
|
|
787
|
+
option.description && /* @__PURE__ */ jsx("span", { id: descriptionId, className: styles.description, children: option.description })
|
|
788
|
+
] });
|
|
789
|
+
const item = mode === "multi" ? /* @__PURE__ */ jsx(
|
|
748
790
|
Menu.CheckItem,
|
|
749
791
|
{
|
|
750
792
|
checked: isSelected,
|
|
793
|
+
disabled: option.disabled,
|
|
751
794
|
active: isActive,
|
|
752
795
|
interactiveProps: {
|
|
753
796
|
id: optionId,
|
|
754
797
|
role: "option",
|
|
755
|
-
|
|
798
|
+
"aria-describedby": describedBy,
|
|
799
|
+
"aria-label": option.label,
|
|
800
|
+
"aria-disabled": option.disabled || void 0,
|
|
801
|
+
onMouseEnter: () => {
|
|
802
|
+
if (!option.disabled) setActiveIndex(index);
|
|
803
|
+
},
|
|
756
804
|
onMouseDown: (e) => {
|
|
757
805
|
interactingWithOptionsRef.current = true;
|
|
758
806
|
e.preventDefault();
|
|
759
807
|
},
|
|
760
808
|
onKeyDown: (e) => handleTabKeyDown(e)
|
|
761
809
|
},
|
|
762
|
-
label:
|
|
810
|
+
label: labelContent,
|
|
763
811
|
onCheckedChange: () => commitSelection(option)
|
|
764
812
|
},
|
|
765
813
|
option.value
|
|
766
|
-
) : /* @__PURE__ */ jsx(
|
|
767
|
-
|
|
814
|
+
) : /* @__PURE__ */ jsx(
|
|
815
|
+
Menu.Item,
|
|
768
816
|
{
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
817
|
+
active: isActive,
|
|
818
|
+
selected: isSelected,
|
|
819
|
+
disabled: option.disabled,
|
|
820
|
+
children: /* @__PURE__ */ jsx(
|
|
821
|
+
"button",
|
|
822
|
+
{
|
|
823
|
+
id: optionId,
|
|
824
|
+
type: "button",
|
|
825
|
+
role: "option",
|
|
826
|
+
"aria-selected": isSelected,
|
|
827
|
+
"aria-describedby": describedBy,
|
|
828
|
+
"aria-label": option.label,
|
|
829
|
+
"aria-disabled": option.disabled || void 0,
|
|
830
|
+
disabled: option.disabled,
|
|
831
|
+
onMouseEnter: () => {
|
|
832
|
+
if (!option.disabled) setActiveIndex(index);
|
|
833
|
+
},
|
|
834
|
+
onMouseDown: (e) => {
|
|
835
|
+
e.preventDefault();
|
|
836
|
+
},
|
|
837
|
+
onKeyDown: (e) => handleTabKeyDown(e),
|
|
838
|
+
onClick: () => commitSelection(option),
|
|
839
|
+
children: labelContent
|
|
840
|
+
}
|
|
841
|
+
)
|
|
842
|
+
},
|
|
843
|
+
option.value
|
|
844
|
+
);
|
|
845
|
+
return /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
846
|
+
startsGroup && /* @__PURE__ */ jsx(Menu.Header, { id: groupId, children: option.group }),
|
|
847
|
+
item
|
|
848
|
+
] }, option.value);
|
|
782
849
|
}) : /* @__PURE__ */ jsx(Menu.Item, { disabled: true, children: emptyMessage }) })
|
|
783
850
|
}
|
|
784
851
|
),
|
|
@@ -36,3 +36,19 @@
|
|
|
36
36
|
.typeaheadInput {
|
|
37
37
|
cursor: pointer;
|
|
38
38
|
}
|
|
39
|
+
|
|
40
|
+
/* Label and description share one row; the description wraps below only when space runs out. */
|
|
41
|
+
.optionText {
|
|
42
|
+
display: flex;
|
|
43
|
+
flex-wrap: wrap;
|
|
44
|
+
align-items: baseline;
|
|
45
|
+
column-gap: var(--spacing-md);
|
|
46
|
+
min-width: 0;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.description {
|
|
50
|
+
margin-inline-start: auto;
|
|
51
|
+
font-size: var(--font-size-xs);
|
|
52
|
+
color: var(--color-fg-muted);
|
|
53
|
+
text-align: end;
|
|
54
|
+
}
|
|
@@ -7,7 +7,6 @@ var react = require('react');
|
|
|
7
7
|
var usePersistentState = require('../../hooks/usePersistentState');
|
|
8
8
|
var Headline = require('./Headline');
|
|
9
9
|
var styles = require('./Headline.module.css');
|
|
10
|
-
var Button = require('../button/Button');
|
|
11
10
|
|
|
12
11
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
13
12
|
|
|
@@ -45,7 +44,7 @@ function CollapsibleHeadline({
|
|
|
45
44
|
onToggle == null ? void 0 : onToggle();
|
|
46
45
|
};
|
|
47
46
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles__default.default.collapsibleRoot, children: [
|
|
48
|
-
/* @__PURE__ */ jsxRuntime.
|
|
47
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
49
48
|
Headline.Headline,
|
|
50
49
|
{
|
|
51
50
|
...headlineProps,
|
|
@@ -53,28 +52,26 @@ function CollapsibleHeadline({
|
|
|
53
52
|
weight,
|
|
54
53
|
size,
|
|
55
54
|
addition,
|
|
56
|
-
children:
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
"
|
|
66
|
-
|
|
67
|
-
onClick: handleToggle,
|
|
68
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
55
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
56
|
+
"button",
|
|
57
|
+
{
|
|
58
|
+
type: "button",
|
|
59
|
+
className: styles__default.default.toggleButton,
|
|
60
|
+
"aria-expanded": isExpanded,
|
|
61
|
+
"aria-controls": panelId,
|
|
62
|
+
onClick: handleToggle,
|
|
63
|
+
children: [
|
|
64
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: header }),
|
|
65
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
69
66
|
lucideReact.ChevronDown,
|
|
70
67
|
{
|
|
71
68
|
"aria-hidden": true,
|
|
72
69
|
className: [styles__default.default.toggleChevron, isExpanded ? styles__default.default.toggleChevronExpanded : ""].filter(Boolean).join(" ")
|
|
73
70
|
}
|
|
74
71
|
)
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
)
|
|
78
75
|
}
|
|
79
76
|
),
|
|
80
77
|
isExpanded && /* @__PURE__ */ jsxRuntime.jsx("div", { id: panelId, className: styles__default.default.collapsiblePanel, children })
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { PropsWithChildren } from 'react';
|
|
2
2
|
import type { HeadlineProps } from './Headline';
|
|
3
3
|
export interface CollapsibleHeadlineProps extends Omit<HeadlineProps, 'addition' | 'children'> {
|
|
4
|
-
/** The headline text — always visible. */
|
|
4
|
+
/** The headline text — always visible. Use addition for separate interactive content. */
|
|
5
5
|
header: React.ReactNode;
|
|
6
6
|
/** Controlled expanded state. When omitted, the component manages its own state. */
|
|
7
7
|
expanded?: boolean;
|
|
@@ -9,7 +9,7 @@ export interface CollapsibleHeadlineProps extends Omit<HeadlineProps, 'addition'
|
|
|
9
9
|
onToggle?: () => void;
|
|
10
10
|
/** id of the panel element, used for aria-controls. Generated automatically if omitted. */
|
|
11
11
|
controls?: string;
|
|
12
|
-
/** Extra content rendered
|
|
12
|
+
/** Extra content rendered beside the heading, outside the toggle button. */
|
|
13
13
|
addition?: React.ReactNode;
|
|
14
14
|
/**
|
|
15
15
|
* When set in uncontrolled mode, the component persists its expanded
|