@navikt/ds-react 8.5.2 → 8.7.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/cjs/data/drag-and-drop/item/DataDragAndDropItem.d.ts +27 -0
- package/cjs/data/drag-and-drop/item/DataDragAndDropItem.js +91 -0
- package/cjs/data/drag-and-drop/item/DataDragAndDropItem.js.map +1 -0
- package/cjs/data/drag-and-drop/root/DataDragAndDrop.context.d.ts +5 -0
- package/cjs/data/drag-and-drop/root/DataDragAndDrop.context.js +6 -0
- package/cjs/data/drag-and-drop/root/DataDragAndDrop.context.js.map +1 -0
- package/cjs/data/drag-and-drop/root/DataDragAndDropRoot.d.ts +24 -0
- package/cjs/data/drag-and-drop/root/DataDragAndDropRoot.js +111 -0
- package/cjs/data/drag-and-drop/root/DataDragAndDropRoot.js.map +1 -0
- package/cjs/data/table/helpers/table-keyboard.d.ts +1 -0
- package/cjs/data/table/helpers/table-keyboard.js +5 -3
- package/cjs/data/table/helpers/table-keyboard.js.map +1 -1
- package/cjs/data/table/root/DataTableRoot.context.d.ts +8 -0
- package/cjs/data/table/root/DataTableRoot.context.js +11 -0
- package/cjs/data/table/root/DataTableRoot.context.js.map +1 -0
- package/cjs/data/table/root/DataTableRoot.js +5 -3
- package/cjs/data/table/root/DataTableRoot.js.map +1 -1
- package/cjs/data/table/th/DataTableTh.d.ts +18 -2
- package/cjs/data/table/th/DataTableTh.js +45 -20
- package/cjs/data/table/th/DataTableTh.js.map +1 -1
- package/cjs/data/table/tr/DataTableTr.js +9 -2
- package/cjs/data/table/tr/DataTableTr.js.map +1 -1
- package/cjs/data/token-filter/AutoSuggest.d.ts +6 -2
- package/cjs/data/token-filter/AutoSuggest.js +46 -14
- package/cjs/data/token-filter/AutoSuggest.js.map +1 -1
- package/cjs/data/token-filter/AutoSuggest.types.d.ts +0 -1
- package/cjs/data/token-filter/TokenFilter.d.ts +10 -5
- package/cjs/data/token-filter/TokenFilter.js +110 -48
- package/cjs/data/token-filter/TokenFilter.js.map +1 -1
- package/cjs/data/token-filter/TokenFilter.types.d.ts +51 -35
- package/cjs/data/token-filter/helpers/generate-autocomplete-options.d.ts +3 -6
- package/cjs/data/token-filter/helpers/generate-autocomplete-options.js +24 -37
- package/cjs/data/token-filter/helpers/generate-autocomplete-options.js.map +1 -1
- package/cjs/data/token-filter/helpers/operators.d.ts +6 -6
- package/cjs/data/token-filter/helpers/operators.js +3 -4
- package/cjs/data/token-filter/helpers/operators.js.map +1 -1
- package/cjs/data/token-filter/helpers/parse-query-text.d.ts +2 -20
- package/cjs/data/token-filter/helpers/parse-query-text.js +1 -1
- package/cjs/data/token-filter/helpers/parse-query-text.js.map +1 -1
- package/cjs/data/token-filter/helpers/query-builder.d.ts +2 -2
- package/cjs/data/token-filter/helpers/query-builder.js.map +1 -1
- package/cjs/date/Date.Dialog.d.ts +5 -1
- package/cjs/date/Date.Dialog.js +6 -2
- package/cjs/date/Date.Dialog.js.map +1 -1
- package/cjs/date/datepicker/DatePicker.js +3 -2
- package/cjs/date/datepicker/DatePicker.js.map +1 -1
- package/cjs/date/datepicker/hooks/useDatepicker.js +5 -2
- package/cjs/date/datepicker/hooks/useDatepicker.js.map +1 -1
- package/cjs/date/datepicker/hooks/useRangeDatepicker.js +3 -1
- package/cjs/date/datepicker/hooks/useRangeDatepicker.js.map +1 -1
- package/cjs/date/datepicker/parts/DatePicker.Months.d.ts +2 -1
- package/cjs/date/datepicker/parts/DatePicker.Months.js +3 -3
- package/cjs/date/datepicker/parts/DatePicker.Months.js.map +1 -1
- package/cjs/date/datepicker/parts/DatePicker.RDP.d.ts +5 -1
- package/cjs/date/datepicker/parts/DatePicker.RDP.js +2 -2
- package/cjs/date/datepicker/parts/DatePicker.RDP.js.map +1 -1
- package/cjs/date/monthpicker/MonthPicker.js +3 -2
- package/cjs/date/monthpicker/MonthPicker.js.map +1 -1
- package/cjs/date/monthpicker/hooks/useMonthPicker.js +3 -1
- package/cjs/date/monthpicker/hooks/useMonthPicker.js.map +1 -1
- package/cjs/date/monthpicker/parts/MonthPicker.Caption.d.ts +4 -1
- package/cjs/date/monthpicker/parts/MonthPicker.Caption.js +3 -2
- package/cjs/date/monthpicker/parts/MonthPicker.Caption.js.map +1 -1
- package/cjs/dropdown/Toggle.js +5 -12
- package/cjs/dropdown/Toggle.js.map +1 -1
- package/cjs/form/combobox/Input/Input.js +1 -1
- package/cjs/form/combobox/Input/Input.js.map +1 -1
- package/cjs/inline-message/root/InlineMessage.js +2 -2
- package/cjs/inline-message/root/InlineMessage.js.map +1 -1
- package/cjs/provider/Provider.d.ts +2 -2
- package/cjs/toggle-group/useToggleGroup.js +5 -3
- package/cjs/toggle-group/useToggleGroup.js.map +1 -1
- package/cjs/tooltip/Tooltip.js +1 -3
- package/cjs/tooltip/Tooltip.js.map +1 -1
- package/cjs/utils/components/HighlightText/HighlightText.d.ts +8 -0
- package/cjs/utils/components/HighlightText/HighlightText.js +27 -0
- package/cjs/utils/components/HighlightText/HighlightText.js.map +1 -0
- package/cjs/utils/components/Listbox/group/ListboxGroup.d.ts +7 -0
- package/cjs/utils/components/Listbox/group/ListboxGroup.js +15 -0
- package/cjs/utils/components/Listbox/group/ListboxGroup.js.map +1 -0
- package/cjs/utils/components/Listbox/input-slot/ListboxInputSlot.d.ts +7 -0
- package/cjs/utils/components/Listbox/input-slot/ListboxInputSlot.js +15 -0
- package/cjs/utils/components/Listbox/input-slot/ListboxInputSlot.js.map +1 -0
- package/cjs/utils/components/Listbox/item/ListboxItem.d.ts +24 -0
- package/cjs/utils/components/Listbox/item/ListboxItem.js +33 -0
- package/cjs/utils/components/Listbox/item/ListboxItem.js.map +1 -0
- package/cjs/utils/components/Listbox/list/ListboxList.d.ts +8 -0
- package/cjs/utils/components/Listbox/list/ListboxList.js +32 -0
- package/cjs/utils/components/Listbox/list/ListboxList.js.map +1 -0
- package/cjs/utils/components/Listbox/root/ListboxRoot.d.ts +20 -0
- package/cjs/utils/components/Listbox/root/ListboxRoot.js +84 -0
- package/cjs/utils/components/Listbox/root/ListboxRoot.js.map +1 -0
- package/cjs/utils/components/Listbox/root/domHelpers.d.ts +3 -0
- package/cjs/utils/components/Listbox/root/domHelpers.js +53 -0
- package/cjs/utils/components/Listbox/root/domHelpers.js.map +1 -0
- package/cjs/utils/components/focus-boundary/FocusBoundary.js +9 -64
- package/cjs/utils/components/focus-boundary/FocusBoundary.js.map +1 -1
- package/cjs/utils/helpers/focus.d.ts +14 -0
- package/cjs/utils/helpers/focus.js +63 -0
- package/cjs/utils/helpers/focus.js.map +1 -0
- package/cjs/utils/hooks/useDeferredValue.d.ts +1 -0
- package/cjs/utils/hooks/useDeferredValue.js +14 -0
- package/cjs/utils/hooks/useDeferredValue.js.map +1 -0
- package/esm/data/drag-and-drop/item/DataDragAndDropItem.d.ts +27 -0
- package/esm/data/drag-and-drop/item/DataDragAndDropItem.js +55 -0
- package/esm/data/drag-and-drop/item/DataDragAndDropItem.js.map +1 -0
- package/esm/data/drag-and-drop/root/DataDragAndDrop.context.d.ts +5 -0
- package/esm/data/drag-and-drop/root/DataDragAndDrop.context.js +3 -0
- package/esm/data/drag-and-drop/root/DataDragAndDrop.context.js.map +1 -0
- package/esm/data/drag-and-drop/root/DataDragAndDropRoot.d.ts +24 -0
- package/esm/data/drag-and-drop/root/DataDragAndDropRoot.js +71 -0
- package/esm/data/drag-and-drop/root/DataDragAndDropRoot.js.map +1 -0
- package/esm/data/table/helpers/table-keyboard.d.ts +1 -0
- package/esm/data/table/helpers/table-keyboard.js +5 -3
- package/esm/data/table/helpers/table-keyboard.js.map +1 -1
- package/esm/data/table/root/DataTableRoot.context.d.ts +8 -0
- package/esm/data/table/root/DataTableRoot.context.js +7 -0
- package/esm/data/table/root/DataTableRoot.context.js.map +1 -0
- package/esm/data/table/root/DataTableRoot.js +5 -3
- package/esm/data/table/root/DataTableRoot.js.map +1 -1
- package/esm/data/table/th/DataTableTh.d.ts +18 -2
- package/esm/data/table/th/DataTableTh.js +46 -21
- package/esm/data/table/th/DataTableTh.js.map +1 -1
- package/esm/data/table/tr/DataTableTr.js +9 -2
- package/esm/data/table/tr/DataTableTr.js.map +1 -1
- package/esm/data/token-filter/AutoSuggest.d.ts +6 -2
- package/esm/data/token-filter/AutoSuggest.js +45 -16
- package/esm/data/token-filter/AutoSuggest.js.map +1 -1
- package/esm/data/token-filter/AutoSuggest.types.d.ts +0 -1
- package/esm/data/token-filter/TokenFilter.d.ts +10 -5
- package/esm/data/token-filter/TokenFilter.js +110 -48
- package/esm/data/token-filter/TokenFilter.js.map +1 -1
- package/esm/data/token-filter/TokenFilter.types.d.ts +51 -35
- package/esm/data/token-filter/helpers/generate-autocomplete-options.d.ts +3 -6
- package/esm/data/token-filter/helpers/generate-autocomplete-options.js +24 -37
- package/esm/data/token-filter/helpers/generate-autocomplete-options.js.map +1 -1
- package/esm/data/token-filter/helpers/operators.d.ts +6 -6
- package/esm/data/token-filter/helpers/operators.js +3 -4
- package/esm/data/token-filter/helpers/operators.js.map +1 -1
- package/esm/data/token-filter/helpers/parse-query-text.d.ts +2 -20
- package/esm/data/token-filter/helpers/parse-query-text.js +1 -1
- package/esm/data/token-filter/helpers/parse-query-text.js.map +1 -1
- package/esm/data/token-filter/helpers/query-builder.d.ts +2 -2
- package/esm/data/token-filter/helpers/query-builder.js.map +1 -1
- package/esm/date/Date.Dialog.d.ts +5 -1
- package/esm/date/Date.Dialog.js +6 -2
- package/esm/date/Date.Dialog.js.map +1 -1
- package/esm/date/datepicker/DatePicker.js +3 -2
- package/esm/date/datepicker/DatePicker.js.map +1 -1
- package/esm/date/datepicker/hooks/useDatepicker.js +5 -2
- package/esm/date/datepicker/hooks/useDatepicker.js.map +1 -1
- package/esm/date/datepicker/hooks/useRangeDatepicker.js +3 -1
- package/esm/date/datepicker/hooks/useRangeDatepicker.js.map +1 -1
- package/esm/date/datepicker/parts/DatePicker.Months.d.ts +2 -1
- package/esm/date/datepicker/parts/DatePicker.Months.js +3 -3
- package/esm/date/datepicker/parts/DatePicker.Months.js.map +1 -1
- package/esm/date/datepicker/parts/DatePicker.RDP.d.ts +5 -1
- package/esm/date/datepicker/parts/DatePicker.RDP.js +2 -2
- package/esm/date/datepicker/parts/DatePicker.RDP.js.map +1 -1
- package/esm/date/monthpicker/MonthPicker.js +3 -2
- package/esm/date/monthpicker/MonthPicker.js.map +1 -1
- package/esm/date/monthpicker/hooks/useMonthPicker.js +3 -1
- package/esm/date/monthpicker/hooks/useMonthPicker.js.map +1 -1
- package/esm/date/monthpicker/parts/MonthPicker.Caption.d.ts +4 -1
- package/esm/date/monthpicker/parts/MonthPicker.Caption.js +3 -2
- package/esm/date/monthpicker/parts/MonthPicker.Caption.js.map +1 -1
- package/esm/dropdown/Toggle.js +5 -12
- package/esm/dropdown/Toggle.js.map +1 -1
- package/esm/form/combobox/Input/Input.js +1 -1
- package/esm/form/combobox/Input/Input.js.map +1 -1
- package/esm/inline-message/root/InlineMessage.js +3 -3
- package/esm/inline-message/root/InlineMessage.js.map +1 -1
- package/esm/provider/Provider.d.ts +2 -2
- package/esm/toggle-group/useToggleGroup.js +6 -4
- package/esm/toggle-group/useToggleGroup.js.map +1 -1
- package/esm/tooltip/Tooltip.js +1 -3
- package/esm/tooltip/Tooltip.js.map +1 -1
- package/esm/utils/components/HighlightText/HighlightText.d.ts +8 -0
- package/esm/utils/components/HighlightText/HighlightText.js +21 -0
- package/esm/utils/components/HighlightText/HighlightText.js.map +1 -0
- package/esm/utils/components/Listbox/group/ListboxGroup.d.ts +7 -0
- package/esm/utils/components/Listbox/group/ListboxGroup.js +10 -0
- package/esm/utils/components/Listbox/group/ListboxGroup.js.map +1 -0
- package/esm/utils/components/Listbox/input-slot/ListboxInputSlot.d.ts +7 -0
- package/esm/utils/components/Listbox/input-slot/ListboxInputSlot.js +9 -0
- package/esm/utils/components/Listbox/input-slot/ListboxInputSlot.js.map +1 -0
- package/esm/utils/components/Listbox/item/ListboxItem.d.ts +24 -0
- package/esm/utils/components/Listbox/item/ListboxItem.js +27 -0
- package/esm/utils/components/Listbox/item/ListboxItem.js.map +1 -0
- package/esm/utils/components/Listbox/list/ListboxList.d.ts +8 -0
- package/esm/utils/components/Listbox/list/ListboxList.js +27 -0
- package/esm/utils/components/Listbox/list/ListboxList.js.map +1 -0
- package/esm/utils/components/Listbox/root/ListboxRoot.d.ts +20 -0
- package/esm/utils/components/Listbox/root/ListboxRoot.js +79 -0
- package/esm/utils/components/Listbox/root/ListboxRoot.js.map +1 -0
- package/esm/utils/components/Listbox/root/domHelpers.d.ts +3 -0
- package/esm/utils/components/Listbox/root/domHelpers.js +50 -0
- package/esm/utils/components/Listbox/root/domHelpers.js.map +1 -0
- package/esm/utils/components/focus-boundary/FocusBoundary.js +8 -63
- package/esm/utils/components/focus-boundary/FocusBoundary.js.map +1 -1
- package/esm/utils/helpers/focus.d.ts +14 -0
- package/esm/utils/helpers/focus.js +60 -0
- package/esm/utils/helpers/focus.js.map +1 -0
- package/esm/utils/hooks/useDeferredValue.d.ts +1 -0
- package/esm/utils/hooks/useDeferredValue.js +7 -0
- package/esm/utils/hooks/useDeferredValue.js.map +1 -0
- package/package.json +7 -7
- package/src/data/drag-and-drop/item/DataDragAndDropItem.tsx +101 -0
- package/src/data/drag-and-drop/root/DataDragAndDrop.context.tsx +9 -0
- package/src/data/drag-and-drop/root/DataDragAndDropRoot.tsx +98 -0
- package/src/data/table/helpers/table-keyboard.ts +7 -3
- package/src/data/table/root/DataTableRoot.context.ts +13 -0
- package/src/data/table/root/DataTableRoot.tsx +16 -13
- package/src/data/table/th/DataTableTh.tsx +110 -54
- package/src/data/table/tr/DataTableTr.tsx +13 -2
- package/src/data/token-filter/AutoSuggest.tsx +142 -35
- package/src/data/token-filter/AutoSuggest.types.ts +0 -1
- package/src/data/token-filter/TokenFilter.tsx +179 -81
- package/src/data/token-filter/TokenFilter.types.ts +70 -44
- package/src/data/token-filter/helpers/generate-autocomplete-options.test.ts +97 -157
- package/src/data/token-filter/helpers/generate-autocomplete-options.ts +56 -53
- package/src/data/token-filter/helpers/operators.test.ts +29 -29
- package/src/data/token-filter/helpers/operators.ts +16 -16
- package/src/data/token-filter/helpers/parse-query-text.test.ts +37 -35
- package/src/data/token-filter/helpers/parse-query-text.ts +7 -26
- package/src/data/token-filter/helpers/query-builder.ts +2 -2
- package/src/date/Date.Dialog.tsx +15 -0
- package/src/date/datepicker/DatePicker.tsx +3 -0
- package/src/date/datepicker/hooks/useDatepicker.tsx +7 -2
- package/src/date/datepicker/hooks/useRangeDatepicker.tsx +5 -1
- package/src/date/datepicker/parts/DatePicker.Months.tsx +9 -1
- package/src/date/datepicker/parts/DatePicker.RDP.tsx +7 -1
- package/src/date/monthpicker/MonthPicker.tsx +3 -1
- package/src/date/monthpicker/hooks/useMonthPicker.tsx +5 -1
- package/src/date/monthpicker/parts/MonthPicker.Caption.tsx +20 -2
- package/src/dropdown/Toggle.tsx +6 -12
- package/src/form/combobox/Input/Input.tsx +2 -2
- package/src/inline-message/root/InlineMessage.tsx +5 -5
- package/src/provider/Provider.tsx +2 -2
- package/src/toggle-group/useToggleGroup.ts +6 -5
- package/src/tooltip/Tooltip.tsx +1 -3
- package/src/utils/components/HighlightText/HighlightText.tsx +34 -0
- package/src/utils/components/Listbox/group/ListboxGroup.tsx +26 -0
- package/src/utils/components/Listbox/input-slot/ListboxInputSlot.tsx +22 -0
- package/src/utils/components/Listbox/item/ListboxItem.tsx +57 -0
- package/src/utils/components/Listbox/list/ListboxList.tsx +38 -0
- package/src/utils/components/Listbox/root/ListboxRoot.tsx +104 -0
- package/src/utils/components/Listbox/root/domHelpers.ts +59 -0
- package/src/utils/components/focus-boundary/FocusBoundary.tsx +8 -78
- package/src/utils/helpers/focus.ts +75 -0
- package/src/utils/hooks/useDeferredValue.ts +12 -0
- package/cjs/data/table/th/DataTableThSortHandle.d.ts +0 -6
- package/cjs/data/table/th/DataTableThSortHandle.js +0 -82
- package/cjs/data/table/th/DataTableThSortHandle.js.map +0 -1
- package/esm/data/table/th/DataTableThSortHandle.d.ts +0 -6
- package/esm/data/table/th/DataTableThSortHandle.js +0 -47
- package/esm/data/table/th/DataTableThSortHandle.js.map +0 -1
- package/src/data/table/th/DataTableThSortHandle.tsx +0 -67
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { OperatorT } from "../TokenFilter.types";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Human-readable labels for query filter operators.
|
|
5
5
|
* Used for displaying operator descriptions in autocomplete suggestions.
|
|
6
6
|
* TODO: Support i18n
|
|
7
7
|
*/
|
|
8
|
-
const OPERATOR_LABELS: Record<
|
|
8
|
+
const OPERATOR_LABELS: Record<OperatorT, string> = {
|
|
9
9
|
":": "contains",
|
|
10
10
|
"!:": "does not contain",
|
|
11
11
|
"=": "is",
|
package/src/date/Date.Dialog.tsx
CHANGED
|
@@ -5,6 +5,7 @@ import { Modal } from "../modal";
|
|
|
5
5
|
import { useModalContext } from "../modal/Modal.context";
|
|
6
6
|
import { Popover } from "../popover";
|
|
7
7
|
import { cl } from "../utils/helpers";
|
|
8
|
+
import { focusElement } from "../utils/helpers/focus";
|
|
8
9
|
import { useMedia } from "../utils/hooks";
|
|
9
10
|
import { useI18n } from "../utils/i18n/i18n.hooks";
|
|
10
11
|
import type { TFunction } from "../utils/i18n/i18n.types";
|
|
@@ -30,6 +31,10 @@ type DateWrapperProps = {
|
|
|
30
31
|
id?: string;
|
|
31
32
|
strategy?: "absolute" | "fixed";
|
|
32
33
|
};
|
|
34
|
+
/**
|
|
35
|
+
* Id for the label of the popup, used for aria-labelledby
|
|
36
|
+
*/
|
|
37
|
+
popupLabelId?: string;
|
|
33
38
|
};
|
|
34
39
|
|
|
35
40
|
const DateDialog = ({
|
|
@@ -41,6 +46,7 @@ const DateDialog = ({
|
|
|
41
46
|
translate,
|
|
42
47
|
variant,
|
|
43
48
|
popoverProps,
|
|
49
|
+
popupLabelId,
|
|
44
50
|
}: DateWrapperProps) => {
|
|
45
51
|
const translateGlobal = useI18n("global", getGlobalTranslations(locale));
|
|
46
52
|
|
|
@@ -68,6 +74,10 @@ const DateDialog = ({
|
|
|
68
74
|
"aksel-date": variant === "month",
|
|
69
75
|
})}
|
|
70
76
|
{...popoverProps}
|
|
77
|
+
ref={focusPopoverOnOpen}
|
|
78
|
+
tabIndex={-1}
|
|
79
|
+
role="dialog"
|
|
80
|
+
aria-labelledby={popupLabelId}
|
|
71
81
|
>
|
|
72
82
|
{children}
|
|
73
83
|
</Popover>
|
|
@@ -104,4 +114,9 @@ const DateDialog = ({
|
|
|
104
114
|
</Modal>
|
|
105
115
|
);
|
|
106
116
|
};
|
|
117
|
+
|
|
118
|
+
function focusPopoverOnOpen(element: HTMLDivElement | null) {
|
|
119
|
+
focusElement(element, { preventScroll: true, sync: false });
|
|
120
|
+
}
|
|
121
|
+
|
|
107
122
|
export { DateDialog };
|
|
@@ -95,6 +95,7 @@ export const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>(
|
|
|
95
95
|
);
|
|
96
96
|
|
|
97
97
|
const ariaId = useId(id);
|
|
98
|
+
const popupLabelId = useId();
|
|
98
99
|
|
|
99
100
|
const [open, setOpen] = useControllableState({
|
|
100
101
|
defaultValue: false,
|
|
@@ -161,6 +162,7 @@ export const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>(
|
|
|
161
162
|
id: ariaId,
|
|
162
163
|
strategy,
|
|
163
164
|
}}
|
|
165
|
+
popupLabelId={popupLabelId}
|
|
164
166
|
>
|
|
165
167
|
<ReactDayPicker
|
|
166
168
|
{...rest}
|
|
@@ -168,6 +170,7 @@ export const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>(
|
|
|
168
170
|
handleSelect={setValue}
|
|
169
171
|
selected={value as any}
|
|
170
172
|
mode={mode as any}
|
|
173
|
+
popupLabelId={popupLabelId}
|
|
171
174
|
/>
|
|
172
175
|
</DateDialog>
|
|
173
176
|
</div>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { differenceInCalendarDays, isWeekend } from "date-fns";
|
|
2
2
|
import React, { useCallback, useState } from "react";
|
|
3
3
|
import { DayEventHandler, dateMatchModifiers } from "react-day-picker";
|
|
4
|
+
import { focusElement } from "../../../utils/helpers/focus";
|
|
4
5
|
import { useDateLocale } from "../../../utils/i18n/i18n.hooks";
|
|
5
6
|
import { DateInputProps } from "../../Date.Input";
|
|
6
7
|
import { getLocaleFromString } from "../../Date.locale";
|
|
@@ -160,8 +161,9 @@ export const useDatepicker = (
|
|
|
160
161
|
const handleOpen = useCallback(
|
|
161
162
|
(newOpen: boolean) => {
|
|
162
163
|
setOpen(newOpen);
|
|
163
|
-
newOpen
|
|
164
|
+
if (newOpen) {
|
|
164
165
|
setMonth(selectedDay ?? defaultSelected ?? defaultMonth ?? today);
|
|
166
|
+
}
|
|
165
167
|
},
|
|
166
168
|
[defaultMonth, defaultSelected, selectedDay, today],
|
|
167
169
|
);
|
|
@@ -313,7 +315,10 @@ export const useDatepicker = (
|
|
|
313
315
|
open,
|
|
314
316
|
onClose: () => {
|
|
315
317
|
handleOpen(false);
|
|
316
|
-
|
|
318
|
+
/* Delay focus to allow "open"-button to update title before focus */
|
|
319
|
+
queueMicrotask(() =>
|
|
320
|
+
focusElement(anchorRef, { sync: false, preventScroll: true }),
|
|
321
|
+
);
|
|
317
322
|
},
|
|
318
323
|
onOpenToggle: () => handleOpen(!open),
|
|
319
324
|
disabled,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { differenceInCalendarDays, isSameDay, isWeekend } from "date-fns";
|
|
2
2
|
import React, { useState } from "react";
|
|
3
3
|
import { dateMatchModifiers } from "react-day-picker";
|
|
4
|
+
import { focusElement } from "../../../utils/helpers/focus";
|
|
4
5
|
import { useDateLocale } from "../../../utils/i18n/i18n.hooks";
|
|
5
6
|
import { DateInputProps } from "../../Date.Input";
|
|
6
7
|
import { getLocaleFromString } from "../../Date.locale";
|
|
@@ -539,7 +540,10 @@ export const useRangeDatepicker = (
|
|
|
539
540
|
onOpenToggle: () => setOpen((x) => !x),
|
|
540
541
|
onClose: () => {
|
|
541
542
|
setOpen(false);
|
|
542
|
-
|
|
543
|
+
/* Delay focus to allow "open"-button to update title before focus */
|
|
544
|
+
queueMicrotask(() =>
|
|
545
|
+
focusElement(anchorRef, { sync: false, preventScroll: true }),
|
|
546
|
+
);
|
|
543
547
|
},
|
|
544
548
|
disabled,
|
|
545
549
|
disableWeekends,
|
|
@@ -28,12 +28,14 @@ const DatePickerMonths = ({
|
|
|
28
28
|
calendarMonth,
|
|
29
29
|
locale,
|
|
30
30
|
onWeekNumberClick,
|
|
31
|
+
popupLabelId,
|
|
31
32
|
...rest
|
|
32
33
|
}: {
|
|
33
34
|
calendarMonth: CalendarMonth;
|
|
34
35
|
displayIndex: number;
|
|
35
36
|
locale: Locale;
|
|
36
37
|
onWeekNumberClick: MultipleMode["onWeekNumberClick"];
|
|
38
|
+
popupLabelId?: string;
|
|
37
39
|
} & React.HTMLAttributes<HTMLDivElement>) => {
|
|
38
40
|
const { dayPickerProps, goToMonth, previousMonth, nextMonth } =
|
|
39
41
|
useDayPicker();
|
|
@@ -80,7 +82,12 @@ const DatePickerMonths = ({
|
|
|
80
82
|
<div {...omit(rest, ["displayIndex"])}>
|
|
81
83
|
<div className="aksel-date__caption">
|
|
82
84
|
{captionLayout?.startsWith("dropdown") && (
|
|
83
|
-
<span
|
|
85
|
+
<span
|
|
86
|
+
aria-live="polite"
|
|
87
|
+
aria-atomic="true"
|
|
88
|
+
className="aksel-sr-only"
|
|
89
|
+
id={popupLabelId}
|
|
90
|
+
>
|
|
84
91
|
{format(calendarMonth.date, "LLLL y", { locale })}
|
|
85
92
|
</span>
|
|
86
93
|
)}
|
|
@@ -129,6 +136,7 @@ const DatePickerMonths = ({
|
|
|
129
136
|
aria-live="polite"
|
|
130
137
|
role="status"
|
|
131
138
|
className="aksel-date__caption-label"
|
|
139
|
+
id={popupLabelId}
|
|
132
140
|
>
|
|
133
141
|
{format(calendarMonth.date, "LLLL y", { locale })}
|
|
134
142
|
</BodyShort>
|
|
@@ -44,6 +44,10 @@ type ReactDayPickerProps = DatePickerDefaultProps &
|
|
|
44
44
|
* Update selected date
|
|
45
45
|
*/
|
|
46
46
|
handleSelect: (newSelected: any) => void;
|
|
47
|
+
/**
|
|
48
|
+
* Id for the label of the popup, used for aria-labelledby
|
|
49
|
+
*/
|
|
50
|
+
popupLabelId?: string;
|
|
47
51
|
};
|
|
48
52
|
|
|
49
53
|
const ReactDayPicker = ({
|
|
@@ -61,6 +65,7 @@ const ReactDayPicker = ({
|
|
|
61
65
|
mode: _mode,
|
|
62
66
|
handleSelect,
|
|
63
67
|
locale: _locale,
|
|
68
|
+
popupLabelId,
|
|
64
69
|
...rest
|
|
65
70
|
}: ReactDayPickerProps) => {
|
|
66
71
|
const langProviderLocale = useDateLocale();
|
|
@@ -130,9 +135,10 @@ const ReactDayPicker = ({
|
|
|
130
135
|
onWeekNumberClick={
|
|
131
136
|
mode === "multiple" ? onWeekNumberClick : undefined
|
|
132
137
|
}
|
|
138
|
+
popupLabelId={popupLabelId}
|
|
133
139
|
/>
|
|
134
140
|
),
|
|
135
|
-
[locale, mode, onWeekNumberClick],
|
|
141
|
+
[locale, mode, onWeekNumberClick, popupLabelId],
|
|
136
142
|
),
|
|
137
143
|
Day: useCallback(
|
|
138
144
|
(props) => (
|
|
@@ -97,6 +97,7 @@ export const MonthPicker = forwardRef<HTMLDivElement, MonthPickerProps>(
|
|
|
97
97
|
);
|
|
98
98
|
const langProviderLocale = useDateLocale();
|
|
99
99
|
const ariaId = useId(id);
|
|
100
|
+
const popupLabelId = useId();
|
|
100
101
|
|
|
101
102
|
const [open, setOpen] = useControllableState({
|
|
102
103
|
defaultValue: false,
|
|
@@ -163,9 +164,10 @@ export const MonthPicker = forwardRef<HTMLDivElement, MonthPickerProps>(
|
|
|
163
164
|
id: ariaId,
|
|
164
165
|
strategy,
|
|
165
166
|
}}
|
|
167
|
+
popupLabelId={popupLabelId}
|
|
166
168
|
>
|
|
167
169
|
<div className={cl("rdp-month", className)}>
|
|
168
|
-
<MonthPickerCaption />
|
|
170
|
+
<MonthPickerCaption popupLabelId={popupLabelId} />
|
|
169
171
|
<MonthPickerTable />
|
|
170
172
|
</div>
|
|
171
173
|
</DateDialog>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { useCallback, useMemo, useState } from "react";
|
|
2
2
|
import { dateMatchModifiers } from "react-day-picker";
|
|
3
|
+
import { focusElement } from "../../../utils/helpers/focus";
|
|
3
4
|
import { useDateLocale } from "../../../utils/i18n/i18n.hooks";
|
|
4
5
|
import { DateInputProps } from "../../Date.Input";
|
|
5
6
|
import { getLocaleFromString } from "../../Date.locale";
|
|
@@ -310,7 +311,10 @@ export const useMonthpicker = (
|
|
|
310
311
|
onOpenToggle: () => handleOpen(!open),
|
|
311
312
|
onClose: () => {
|
|
312
313
|
handleOpen(false);
|
|
313
|
-
|
|
314
|
+
/* Delay focus to allow "open"-button to update title before focus */
|
|
315
|
+
queueMicrotask(() =>
|
|
316
|
+
focusElement(anchorRef, { sync: false, preventScroll: true }),
|
|
317
|
+
);
|
|
314
318
|
},
|
|
315
319
|
disabled,
|
|
316
320
|
};
|
|
@@ -13,7 +13,11 @@ import { Select } from "../../../form/select";
|
|
|
13
13
|
import { useDateTranslationContext } from "../../Date.locale";
|
|
14
14
|
import { useMonthPickerContext } from "../MonthPicker.context";
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
type MonthPickerCaptionProps = {
|
|
17
|
+
popupLabelId?: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const MonthPickerCaption = ({ popupLabelId }: MonthPickerCaptionProps) => {
|
|
17
21
|
const { fromDate, toDate, locale, year, onYearChange, caption } =
|
|
18
22
|
useMonthPickerContext();
|
|
19
23
|
|
|
@@ -58,6 +62,16 @@ const MonthPickerCaption = () => {
|
|
|
58
62
|
|
|
59
63
|
return (
|
|
60
64
|
<div className="aksel-date__caption">
|
|
65
|
+
{caption === "dropdown" && (
|
|
66
|
+
<span
|
|
67
|
+
aria-live="polite"
|
|
68
|
+
aria-atomic="true"
|
|
69
|
+
className="aksel-sr-only"
|
|
70
|
+
id={popupLabelId}
|
|
71
|
+
>
|
|
72
|
+
{year.getFullYear()}
|
|
73
|
+
</span>
|
|
74
|
+
)}
|
|
61
75
|
<Button
|
|
62
76
|
className="aksel-date__caption-button"
|
|
63
77
|
disabled={disablePreviousYear()}
|
|
@@ -82,7 +96,11 @@ const MonthPickerCaption = () => {
|
|
|
82
96
|
))}
|
|
83
97
|
</Select>
|
|
84
98
|
) : (
|
|
85
|
-
<span
|
|
99
|
+
<span
|
|
100
|
+
className="aksel-date__year-label"
|
|
101
|
+
aria-live="polite"
|
|
102
|
+
id={popupLabelId}
|
|
103
|
+
>
|
|
86
104
|
{year.getFullYear()}
|
|
87
105
|
</span>
|
|
88
106
|
)}
|
package/src/dropdown/Toggle.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { forwardRef, useContext } from "react";
|
|
2
2
|
import { cl, composeEventHandlers } from "../utils/helpers";
|
|
3
|
+
import { useMergeRefs } from "../utils/hooks";
|
|
3
4
|
import { DropdownContext } from "./context";
|
|
4
5
|
|
|
5
6
|
export interface DropdownToggleProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
@@ -15,30 +16,23 @@ export const DropdownToggle = forwardRef<
|
|
|
15
16
|
>(({ className, onClick, ...rest }, ref) => {
|
|
16
17
|
const context = useContext(DropdownContext);
|
|
17
18
|
|
|
19
|
+
const mergedRef = useMergeRefs(context?.setAnchorEl, ref);
|
|
20
|
+
|
|
18
21
|
if (!context) {
|
|
19
22
|
console.warn("Dropdown.Toggle has to be wrapped in <Dropdown />");
|
|
20
23
|
return null;
|
|
21
24
|
}
|
|
22
25
|
|
|
23
|
-
const {
|
|
26
|
+
const { handleToggle, isOpen } = context;
|
|
24
27
|
|
|
25
|
-
const handleClick = (
|
|
26
|
-
setAnchorEl(e.currentTarget);
|
|
28
|
+
const handleClick = () => {
|
|
27
29
|
handleToggle(!isOpen);
|
|
28
30
|
};
|
|
29
31
|
|
|
30
32
|
return (
|
|
31
33
|
<button
|
|
32
34
|
{...rest}
|
|
33
|
-
ref={
|
|
34
|
-
setAnchorEl(el);
|
|
35
|
-
|
|
36
|
-
if (typeof ref === "function") {
|
|
37
|
-
ref(el);
|
|
38
|
-
} else if (ref != null) {
|
|
39
|
-
ref.current = el;
|
|
40
|
-
}
|
|
41
|
-
}}
|
|
35
|
+
ref={mergedRef}
|
|
42
36
|
onClick={composeEventHandlers(onClick, handleClick)}
|
|
43
37
|
aria-expanded={isOpen}
|
|
44
38
|
className={cl("aksel-dropdown__toggle", className)}
|
|
@@ -255,8 +255,8 @@ const Input = forwardRef<HTMLInputElement, InputProps>(
|
|
|
255
255
|
);
|
|
256
256
|
|
|
257
257
|
const onChangeHandler = useCallback(
|
|
258
|
-
(event: React.
|
|
259
|
-
const newValue = event.
|
|
258
|
+
(event: React.InputEvent<HTMLInputElement>) => {
|
|
259
|
+
const newValue = event.currentTarget.value;
|
|
260
260
|
if (newValue && newValue !== "") {
|
|
261
261
|
toggleIsListOpen(true);
|
|
262
262
|
} else if (filteredOptions.length === 0) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { forwardRef } from "react";
|
|
2
2
|
import { useThemeInternal } from "../../theme/Theme";
|
|
3
|
-
import {
|
|
3
|
+
import { BodyLong } from "../../typography";
|
|
4
4
|
import { type OverridableComponent, useId } from "../../utils-external";
|
|
5
5
|
import { cl } from "../../utils/helpers";
|
|
6
6
|
import { useI18n } from "../../utils/i18n/i18n.hooks";
|
|
@@ -64,7 +64,7 @@ export const InlineMessage: OverridableComponent<
|
|
|
64
64
|
const contentId = useId();
|
|
65
65
|
|
|
66
66
|
return (
|
|
67
|
-
<
|
|
67
|
+
<BodyLong
|
|
68
68
|
ref={forwardedRef}
|
|
69
69
|
className={cl("aksel-inline-message", className)}
|
|
70
70
|
data-color={status === "error" ? "danger" : status}
|
|
@@ -75,9 +75,9 @@ export const InlineMessage: OverridableComponent<
|
|
|
75
75
|
>
|
|
76
76
|
<InlineMessageIcon status={status} />
|
|
77
77
|
{status && (
|
|
78
|
-
<
|
|
78
|
+
<BodyLong id={statusId} aria-hidden visuallyHidden>
|
|
79
79
|
{`${translate(status)}: `}
|
|
80
|
-
</
|
|
80
|
+
</BodyLong>
|
|
81
81
|
)}
|
|
82
82
|
{/** biome-ignore lint/a11y/useAriaPropsSupportedByRole: Testing shows that this works. */}
|
|
83
83
|
<span
|
|
@@ -87,7 +87,7 @@ export const InlineMessage: OverridableComponent<
|
|
|
87
87
|
>
|
|
88
88
|
{children}
|
|
89
89
|
</span>
|
|
90
|
-
</
|
|
90
|
+
</BodyLong>
|
|
91
91
|
);
|
|
92
92
|
},
|
|
93
93
|
);
|
|
@@ -3,7 +3,7 @@ import { PartialTranslations, Translations } from "../utils/i18n/i18n.types";
|
|
|
3
3
|
import nb from "../utils/i18n/locales/nb";
|
|
4
4
|
|
|
5
5
|
type ProviderContextType = {
|
|
6
|
-
rootElement?: HTMLElement;
|
|
6
|
+
rootElement?: HTMLElement | null;
|
|
7
7
|
locale: Translations;
|
|
8
8
|
translations?: PartialTranslations | PartialTranslations[];
|
|
9
9
|
};
|
|
@@ -17,7 +17,7 @@ export type ProviderProps = {
|
|
|
17
17
|
/**
|
|
18
18
|
* Global root-element to attach portals to. Used by Tooltip, Modal (optionally) and ActionMenu.
|
|
19
19
|
*/
|
|
20
|
-
rootElement?: HTMLElement;
|
|
20
|
+
rootElement?: HTMLElement | null;
|
|
21
21
|
} & (
|
|
22
22
|
| {
|
|
23
23
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState } from "react";
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
2
|
import { useControllableState } from "../utils/hooks";
|
|
3
3
|
import { ToggleGroupProps } from "./ToggleGroup.types";
|
|
4
4
|
|
|
@@ -8,7 +8,6 @@ export function useToggleGroup({
|
|
|
8
8
|
defaultValue = "",
|
|
9
9
|
}: Pick<ToggleGroupProps, "onChange" | "value" | "defaultValue">) {
|
|
10
10
|
const [focusedValue, setFocusedValue] = useState(defaultValue);
|
|
11
|
-
|
|
12
11
|
const [selectedValue, setSelectedValue] = useControllableState({
|
|
13
12
|
defaultValue,
|
|
14
13
|
value,
|
|
@@ -16,9 +15,11 @@ export function useToggleGroup({
|
|
|
16
15
|
});
|
|
17
16
|
|
|
18
17
|
/* Sync focused `value` with controlled `selectedValue` */
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
if (value != null) {
|
|
20
|
+
setFocusedValue(value);
|
|
21
|
+
}
|
|
22
|
+
}, [value]);
|
|
22
23
|
|
|
23
24
|
return {
|
|
24
25
|
selectedValue,
|
package/src/tooltip/Tooltip.tsx
CHANGED
|
@@ -237,9 +237,7 @@ export const Tooltip = forwardRef<HTMLDivElement, TooltipProps>(
|
|
|
237
237
|
<TooltipShortcuts shortcuts={keys} />
|
|
238
238
|
{_arrow && (
|
|
239
239
|
<div
|
|
240
|
-
ref={
|
|
241
|
-
arrowRef.current = node;
|
|
242
|
-
}}
|
|
240
|
+
ref={arrowRef}
|
|
243
241
|
className="aksel-tooltip__arrow"
|
|
244
242
|
style={{
|
|
245
243
|
left: arrowX != null ? `${arrowX}px` : "",
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
// Før vi ev. eksponerer denne, vurder følgende:
|
|
4
|
+
// - Bør man kunne slå av aria-label? (Ev. kan vi kun sette på aria-label hvis process.env.NODE_ENV === "test")
|
|
5
|
+
// - Navngivning. Bør ikke bruke kun Highlight fordi det er allerede noe som heter det i JS.
|
|
6
|
+
|
|
7
|
+
interface HighlightTextProps {
|
|
8
|
+
/** Text to be highlighted */
|
|
9
|
+
text: string;
|
|
10
|
+
children: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const HighlightText = ({ text, children }: HighlightTextProps) => {
|
|
14
|
+
const textIndex = children
|
|
15
|
+
.toLocaleLowerCase()
|
|
16
|
+
.indexOf(text.toLocaleLowerCase());
|
|
17
|
+
if (textIndex === -1) {
|
|
18
|
+
return children;
|
|
19
|
+
}
|
|
20
|
+
const start = children.substring(0, textIndex);
|
|
21
|
+
const match = children.substring(textIndex, textIndex + text.length);
|
|
22
|
+
const end = children.substring(textIndex + text.length);
|
|
23
|
+
return (
|
|
24
|
+
// aria-label is used to fix testing-library wrongly evaluating the accessible name of the option when highlighting text
|
|
25
|
+
// biome-ignore lint/a11y/useAriaPropsSupportedByRole: Doesn't matter if it doesn't work in the browser
|
|
26
|
+
<span aria-label={children}>
|
|
27
|
+
{start}
|
|
28
|
+
{match && <mark>{match}</mark>}
|
|
29
|
+
{end}
|
|
30
|
+
</span>
|
|
31
|
+
);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export { HighlightText };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { useId } from "../../../../utils-external";
|
|
3
|
+
|
|
4
|
+
interface ListboxGroupProps {
|
|
5
|
+
label: React.ReactNode;
|
|
6
|
+
children: React.ReactNode;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function ListboxGroup({ label, children }: ListboxGroupProps) {
|
|
10
|
+
const labelId = useId();
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<div
|
|
14
|
+
role="group"
|
|
15
|
+
className="aksel-listbox__group"
|
|
16
|
+
aria-labelledby={labelId}
|
|
17
|
+
>
|
|
18
|
+
<div id={labelId} aria-hidden>
|
|
19
|
+
{label}
|
|
20
|
+
</div>
|
|
21
|
+
{children}
|
|
22
|
+
</div>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export { ListboxGroup };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Slot } from "../../slot/Slot";
|
|
3
|
+
|
|
4
|
+
interface ListboxInputSlotProps {
|
|
5
|
+
children: React.ReactElement;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const ListboxInputSlot = ({ children }: ListboxInputSlotProps) => {
|
|
9
|
+
return (
|
|
10
|
+
<Slot
|
|
11
|
+
aria-activedescendant="aksel-listbox__item-active"
|
|
12
|
+
// @ts-expect-error - You are meant to use an <input>, but Slot doesn't know that.
|
|
13
|
+
autoComplete="off"
|
|
14
|
+
role="combobox"
|
|
15
|
+
>
|
|
16
|
+
{children}
|
|
17
|
+
</Slot>
|
|
18
|
+
);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export { ListboxInputSlot };
|
|
22
|
+
export type { ListboxInputSlotProps };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { cl } from "../../../helpers";
|
|
3
|
+
|
|
4
|
+
export interface ListboxItemProps extends Omit<
|
|
5
|
+
React.HTMLAttributes<HTMLDivElement>,
|
|
6
|
+
"role" | "tabIndex"
|
|
7
|
+
> {
|
|
8
|
+
/**
|
|
9
|
+
* Unique ID used for tracking which item has virtual focus.
|
|
10
|
+
*/
|
|
11
|
+
id: string;
|
|
12
|
+
hasVirtualFocus: boolean;
|
|
13
|
+
children: React.ReactNode;
|
|
14
|
+
/**
|
|
15
|
+
* Callback when item is selected.
|
|
16
|
+
* To improve performance when you have many items,
|
|
17
|
+
* memoize the prop with e.g. useEventCallback.
|
|
18
|
+
*/
|
|
19
|
+
onClick: React.MouseEventHandler<HTMLDivElement>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function ListboxItemComponent({
|
|
23
|
+
id,
|
|
24
|
+
hasVirtualFocus,
|
|
25
|
+
children,
|
|
26
|
+
className,
|
|
27
|
+
...rest
|
|
28
|
+
}: ListboxItemProps) {
|
|
29
|
+
//console.log("Rendering item", id);
|
|
30
|
+
|
|
31
|
+
// TODO: Slot?
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<div
|
|
35
|
+
aria-selected={false}
|
|
36
|
+
{...rest}
|
|
37
|
+
className={cl("aksel-listbox__item", className)}
|
|
38
|
+
role="option"
|
|
39
|
+
tabIndex={-1}
|
|
40
|
+
data-virtual-focus={hasVirtualFocus}
|
|
41
|
+
data-id={id}
|
|
42
|
+
id={hasVirtualFocus ? "aksel-listbox__item-active" : undefined}
|
|
43
|
+
>
|
|
44
|
+
{children}
|
|
45
|
+
</div>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* This component is memoized. To improve performance when you have many items,
|
|
51
|
+
* make sure all object props have stable references (i.e. memoize the event handlers with e.g. useEventCallback).
|
|
52
|
+
*
|
|
53
|
+
* NB: Remember to set `aria-selected` on selected items!
|
|
54
|
+
*/
|
|
55
|
+
export const ListboxItem = React.memo(
|
|
56
|
+
ListboxItemComponent,
|
|
57
|
+
) as typeof ListboxItemComponent;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/* eslint-disable jsx-a11y/mouse-events-have-key-events */
|
|
2
|
+
/** biome-ignore-all lint/a11y/useKeyWithMouseEvents: We know what we are doing */
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { cl } from "../../../helpers";
|
|
5
|
+
|
|
6
|
+
export interface ListboxListProps extends Omit<
|
|
7
|
+
React.HTMLAttributes<HTMLDivElement>,
|
|
8
|
+
"role" | "tabIndex" | "onMouseOver"
|
|
9
|
+
> {
|
|
10
|
+
children: React.ReactNode;
|
|
11
|
+
setVirtuallyFocusedItemId: (value: string) => void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function ListboxList({
|
|
15
|
+
children,
|
|
16
|
+
setVirtuallyFocusedItemId,
|
|
17
|
+
...rest
|
|
18
|
+
}: ListboxListProps) {
|
|
19
|
+
return (
|
|
20
|
+
<div
|
|
21
|
+
{...rest}
|
|
22
|
+
className={cl(rest.className, "aksel-listbox__list")}
|
|
23
|
+
role="listbox"
|
|
24
|
+
tabIndex={-1}
|
|
25
|
+
onMouseOver={(event) => {
|
|
26
|
+
const target = event.target as HTMLElement;
|
|
27
|
+
const itemEl: HTMLElement | null = target.closest('[role="option"]');
|
|
28
|
+
if (itemEl) {
|
|
29
|
+
setVirtuallyFocusedItemId(itemEl?.dataset.id || "");
|
|
30
|
+
}
|
|
31
|
+
}}
|
|
32
|
+
>
|
|
33
|
+
{children}
|
|
34
|
+
</div>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export { ListboxList };
|