@navikt/ds-react 5.15.0 → 5.16.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/_docs.json +145 -1
- package/cjs/form/combobox/Combobox.js +1 -1
- package/cjs/form/combobox/ComboboxProvider.js +2 -1
- package/cjs/form/combobox/ComboboxWrapper.js +1 -1
- package/cjs/form/combobox/FilteredOptions/FilteredOptions.js +59 -41
- package/cjs/form/combobox/FilteredOptions/filtered-options-util.js +3 -1
- package/cjs/form/combobox/FilteredOptions/filteredOptionsContext.js +15 -3
- package/cjs/form/combobox/FilteredOptions/useVirtualFocus.js +52 -32
- package/cjs/form/combobox/Input/Input.js +3 -1
- package/cjs/form/combobox/SelectedOptions/selectedOptionsContext.js +3 -1
- package/cjs/help-text/HelpText.js +1 -1
- package/cjs/util/create-context.js +72 -0
- package/cjs/util/hooks/descendants/descendant.js +117 -0
- package/cjs/util/hooks/descendants/useDescendant.js +108 -0
- package/cjs/util/hooks/descendants/utils.js +53 -0
- package/esm/form/combobox/Combobox.js +1 -1
- package/esm/form/combobox/Combobox.js.map +1 -1
- package/esm/form/combobox/ComboboxProvider.js +2 -1
- package/esm/form/combobox/ComboboxProvider.js.map +1 -1
- package/esm/form/combobox/ComboboxWrapper.js +1 -1
- package/esm/form/combobox/ComboboxWrapper.js.map +1 -1
- package/esm/form/combobox/FilteredOptions/FilteredOptions.js +59 -41
- package/esm/form/combobox/FilteredOptions/FilteredOptions.js.map +1 -1
- package/esm/form/combobox/FilteredOptions/filtered-options-util.d.ts +2 -1
- package/esm/form/combobox/FilteredOptions/filtered-options-util.js +3 -1
- package/esm/form/combobox/FilteredOptions/filtered-options-util.js.map +1 -1
- package/esm/form/combobox/FilteredOptions/filteredOptionsContext.js +15 -3
- package/esm/form/combobox/FilteredOptions/filteredOptionsContext.js.map +1 -1
- package/esm/form/combobox/FilteredOptions/useVirtualFocus.d.ts +2 -4
- package/esm/form/combobox/FilteredOptions/useVirtualFocus.js +52 -32
- package/esm/form/combobox/FilteredOptions/useVirtualFocus.js.map +1 -1
- package/esm/form/combobox/Input/Input.js +3 -1
- package/esm/form/combobox/Input/Input.js.map +1 -1
- package/esm/form/combobox/SelectedOptions/selectedOptionsContext.d.ts +5 -2
- package/esm/form/combobox/SelectedOptions/selectedOptionsContext.js +3 -1
- package/esm/form/combobox/SelectedOptions/selectedOptionsContext.js.map +1 -1
- package/esm/form/combobox/types.d.ts +14 -0
- package/esm/help-text/HelpText.js +1 -1
- package/esm/help-text/HelpText.js.map +1 -1
- package/esm/util/create-context.d.ts +23 -0
- package/esm/util/create-context.js +46 -0
- package/esm/util/create-context.js.map +1 -0
- package/esm/util/hooks/descendants/descendant.d.ts +47 -0
- package/esm/util/hooks/descendants/descendant.js +114 -0
- package/esm/util/hooks/descendants/descendant.js.map +1 -0
- package/esm/util/hooks/descendants/useDescendant.d.ts +14 -0
- package/esm/util/hooks/descendants/useDescendant.js +82 -0
- package/esm/util/hooks/descendants/useDescendant.js.map +1 -0
- package/esm/util/hooks/descendants/utils.d.ts +12 -0
- package/esm/util/hooks/descendants/utils.js +46 -0
- package/esm/util/hooks/descendants/utils.js.map +1 -0
- package/package.json +3 -3
- package/src/form/combobox/Combobox.tsx +1 -1
- package/src/form/combobox/ComboboxProvider.tsx +2 -0
- package/src/form/combobox/ComboboxWrapper.tsx +0 -1
- package/src/form/combobox/FilteredOptions/FilteredOptions.tsx +131 -92
- package/src/form/combobox/FilteredOptions/filtered-options-util.ts +9 -2
- package/src/form/combobox/FilteredOptions/filteredOptionsContext.tsx +22 -3
- package/src/form/combobox/FilteredOptions/useVirtualFocus.ts +63 -45
- package/src/form/combobox/Input/Input.tsx +3 -1
- package/src/form/combobox/SelectedOptions/selectedOptionsContext.tsx +11 -1
- package/src/form/combobox/combobox.stories.tsx +36 -1
- package/src/form/combobox/combobox.test.tsx +1 -3
- package/src/form/combobox/types.ts +15 -0
- package/src/help-text/HelpText.tsx +1 -1
- package/src/util/create-context.tsx +67 -0
- package/src/util/hooks/descendants/descendant.stories.tsx +147 -0
- package/src/util/hooks/descendants/descendant.ts +161 -0
- package/src/util/hooks/descendants/useDescendant.tsx +111 -0
- package/src/util/hooks/descendants/utils.ts +56 -0
package/_docs.json
CHANGED
|
@@ -10641,6 +10641,107 @@
|
|
|
10641
10641
|
}
|
|
10642
10642
|
}
|
|
10643
10643
|
},
|
|
10644
|
+
{
|
|
10645
|
+
"filePath": "src/util/create-context.tsx",
|
|
10646
|
+
"displayName": "createContext",
|
|
10647
|
+
"props": {
|
|
10648
|
+
"hookName": {
|
|
10649
|
+
"defaultValue": null,
|
|
10650
|
+
"description": "",
|
|
10651
|
+
"name": "hookName",
|
|
10652
|
+
"parent": {
|
|
10653
|
+
"fileName": "src/util/create-context.tsx",
|
|
10654
|
+
"name": "CreateContextOptions"
|
|
10655
|
+
},
|
|
10656
|
+
"declarations": [
|
|
10657
|
+
{
|
|
10658
|
+
"fileName": "src/util/create-context.tsx",
|
|
10659
|
+
"name": "CreateContextOptions"
|
|
10660
|
+
}
|
|
10661
|
+
],
|
|
10662
|
+
"required": false,
|
|
10663
|
+
"type": {
|
|
10664
|
+
"name": "string"
|
|
10665
|
+
}
|
|
10666
|
+
},
|
|
10667
|
+
"providerName": {
|
|
10668
|
+
"defaultValue": null,
|
|
10669
|
+
"description": "",
|
|
10670
|
+
"name": "providerName",
|
|
10671
|
+
"parent": {
|
|
10672
|
+
"fileName": "src/util/create-context.tsx",
|
|
10673
|
+
"name": "CreateContextOptions"
|
|
10674
|
+
},
|
|
10675
|
+
"declarations": [
|
|
10676
|
+
{
|
|
10677
|
+
"fileName": "src/util/create-context.tsx",
|
|
10678
|
+
"name": "CreateContextOptions"
|
|
10679
|
+
}
|
|
10680
|
+
],
|
|
10681
|
+
"required": false,
|
|
10682
|
+
"type": {
|
|
10683
|
+
"name": "string"
|
|
10684
|
+
}
|
|
10685
|
+
},
|
|
10686
|
+
"errorMessage": {
|
|
10687
|
+
"defaultValue": null,
|
|
10688
|
+
"description": "",
|
|
10689
|
+
"name": "errorMessage",
|
|
10690
|
+
"parent": {
|
|
10691
|
+
"fileName": "src/util/create-context.tsx",
|
|
10692
|
+
"name": "CreateContextOptions"
|
|
10693
|
+
},
|
|
10694
|
+
"declarations": [
|
|
10695
|
+
{
|
|
10696
|
+
"fileName": "src/util/create-context.tsx",
|
|
10697
|
+
"name": "CreateContextOptions"
|
|
10698
|
+
}
|
|
10699
|
+
],
|
|
10700
|
+
"required": false,
|
|
10701
|
+
"type": {
|
|
10702
|
+
"name": "string"
|
|
10703
|
+
}
|
|
10704
|
+
},
|
|
10705
|
+
"name": {
|
|
10706
|
+
"defaultValue": null,
|
|
10707
|
+
"description": "",
|
|
10708
|
+
"name": "name",
|
|
10709
|
+
"parent": {
|
|
10710
|
+
"fileName": "src/util/create-context.tsx",
|
|
10711
|
+
"name": "CreateContextOptions"
|
|
10712
|
+
},
|
|
10713
|
+
"declarations": [
|
|
10714
|
+
{
|
|
10715
|
+
"fileName": "src/util/create-context.tsx",
|
|
10716
|
+
"name": "CreateContextOptions"
|
|
10717
|
+
}
|
|
10718
|
+
],
|
|
10719
|
+
"required": false,
|
|
10720
|
+
"type": {
|
|
10721
|
+
"name": "string"
|
|
10722
|
+
}
|
|
10723
|
+
},
|
|
10724
|
+
"defaultValue": {
|
|
10725
|
+
"defaultValue": null,
|
|
10726
|
+
"description": "",
|
|
10727
|
+
"name": "defaultValue",
|
|
10728
|
+
"parent": {
|
|
10729
|
+
"fileName": "src/util/create-context.tsx",
|
|
10730
|
+
"name": "CreateContextOptions"
|
|
10731
|
+
},
|
|
10732
|
+
"declarations": [
|
|
10733
|
+
{
|
|
10734
|
+
"fileName": "src/util/create-context.tsx",
|
|
10735
|
+
"name": "CreateContextOptions"
|
|
10736
|
+
}
|
|
10737
|
+
],
|
|
10738
|
+
"required": false,
|
|
10739
|
+
"type": {
|
|
10740
|
+
"name": "T"
|
|
10741
|
+
}
|
|
10742
|
+
}
|
|
10743
|
+
}
|
|
10744
|
+
},
|
|
10644
10745
|
{
|
|
10645
10746
|
"filePath": "src/date/context/useSharedMonthContext.tsx",
|
|
10646
10747
|
"displayName": "SharedMonthProvider",
|
|
@@ -14984,6 +15085,25 @@
|
|
|
14984
15085
|
"name": "string[]"
|
|
14985
15086
|
}
|
|
14986
15087
|
},
|
|
15088
|
+
"maxSelected": {
|
|
15089
|
+
"defaultValue": null,
|
|
15090
|
+
"description": "Options for the maximum number of selected options.",
|
|
15091
|
+
"name": "maxSelected",
|
|
15092
|
+
"parent": {
|
|
15093
|
+
"fileName": "react/src/form/combobox/types.ts",
|
|
15094
|
+
"name": "ComboboxProps"
|
|
15095
|
+
},
|
|
15096
|
+
"declarations": [
|
|
15097
|
+
{
|
|
15098
|
+
"fileName": "react/src/form/combobox/types.ts",
|
|
15099
|
+
"name": "ComboboxProps"
|
|
15100
|
+
}
|
|
15101
|
+
],
|
|
15102
|
+
"required": false,
|
|
15103
|
+
"type": {
|
|
15104
|
+
"name": "MaxSelected"
|
|
15105
|
+
}
|
|
15106
|
+
},
|
|
14987
15107
|
"shouldAutocomplete": {
|
|
14988
15108
|
"defaultValue": {
|
|
14989
15109
|
"value": "false"
|
|
@@ -15437,6 +15557,25 @@
|
|
|
15437
15557
|
"name": "string[]"
|
|
15438
15558
|
}
|
|
15439
15559
|
},
|
|
15560
|
+
"maxSelected": {
|
|
15561
|
+
"defaultValue": null,
|
|
15562
|
+
"description": "Options for the maximum number of selected options.",
|
|
15563
|
+
"name": "maxSelected",
|
|
15564
|
+
"parent": {
|
|
15565
|
+
"fileName": "react/src/form/combobox/types.ts",
|
|
15566
|
+
"name": "ComboboxProps"
|
|
15567
|
+
},
|
|
15568
|
+
"declarations": [
|
|
15569
|
+
{
|
|
15570
|
+
"fileName": "react/src/form/combobox/types.ts",
|
|
15571
|
+
"name": "ComboboxProps"
|
|
15572
|
+
}
|
|
15573
|
+
],
|
|
15574
|
+
"required": false,
|
|
15575
|
+
"type": {
|
|
15576
|
+
"name": "MaxSelected"
|
|
15577
|
+
}
|
|
15578
|
+
},
|
|
15440
15579
|
"shouldAutocomplete": {
|
|
15441
15580
|
"defaultValue": {
|
|
15442
15581
|
"value": "false"
|
|
@@ -19527,7 +19666,7 @@
|
|
|
19527
19666
|
],
|
|
19528
19667
|
"required": true,
|
|
19529
19668
|
"type": {
|
|
19530
|
-
"name": "Pick<ComboboxProps, \"allowNewValues\" | \"isMultiSelect\" | \"options\" | \"selectedOptions\" | \"onToggleSelected\">"
|
|
19669
|
+
"name": "Pick<ComboboxProps, \"allowNewValues\" | \"isMultiSelect\" | \"options\" | \"selectedOptions\" | \"onToggleSelected\" | \"maxSelected\">"
|
|
19531
19670
|
}
|
|
19532
19671
|
}
|
|
19533
19672
|
}
|
|
@@ -19682,5 +19821,10 @@
|
|
|
19682
19821
|
}
|
|
19683
19822
|
}
|
|
19684
19823
|
}
|
|
19824
|
+
},
|
|
19825
|
+
{
|
|
19826
|
+
"filePath": "src/util/hooks/descendants/useDescendant.tsx",
|
|
19827
|
+
"displayName": "createDescendantContext",
|
|
19828
|
+
"props": {}
|
|
19685
19829
|
}
|
|
19686
19830
|
]
|
|
@@ -67,7 +67,7 @@ exports.Combobox = (0, react_1.forwardRef)((props, ref) => {
|
|
|
67
67
|
}), id: inputDescriptionId, size: size }, description)),
|
|
68
68
|
react_1.default.createElement("div", { className: "navds-combobox__wrapper" },
|
|
69
69
|
react_1.default.createElement("div", { className: (0, clsx_1.default)("navds-combobox__wrapper-inner navds-text-field__input", {
|
|
70
|
-
"navds-combobox__wrapper-inner--virtually-unfocused": activeDecendantId !==
|
|
70
|
+
"navds-combobox__wrapper-inner--virtually-unfocused": activeDecendantId !== undefined,
|
|
71
71
|
}), onClick: focusInput },
|
|
72
72
|
!shouldShowSelectedOptions ? (react_1.default.createElement(Input_1.default, Object.assign({ id: inputProps.id, ref: mergedInputRef, inputClassName: inputClassName }, rest))) : (react_1.default.createElement(SelectedOptions_1.default, { selectedOptions: selectedOptions, size: size },
|
|
73
73
|
react_1.default.createElement(Input_1.default, Object.assign({ id: inputProps.id, ref: mergedInputRef, inputClassName: inputClassName }, rest)))),
|
|
@@ -66,7 +66,7 @@ const customOptionsContext_1 = require("./customOptionsContext");
|
|
|
66
66
|
* ```
|
|
67
67
|
*/
|
|
68
68
|
const ComboboxProvider = (0, react_1.forwardRef)((props, ref) => {
|
|
69
|
-
const { allowNewValues = false, children, defaultValue, error, errorId, filteredOptions, id, isListOpen, isLoading = false, isMultiSelect, onToggleSelected, selectedOptions, options, value, onChange, onClear, shouldAutocomplete, size } = props, rest = __rest(props, ["allowNewValues", "children", "defaultValue", "error", "errorId", "filteredOptions", "id", "isListOpen", "isLoading", "isMultiSelect", "onToggleSelected", "selectedOptions", "options", "value", "onChange", "onClear", "shouldAutocomplete", "size"]);
|
|
69
|
+
const { allowNewValues = false, children, defaultValue, error, errorId, filteredOptions, id, isListOpen, isLoading = false, isMultiSelect, onToggleSelected, selectedOptions, maxSelected, options, value, onChange, onClear, shouldAutocomplete, size } = props, rest = __rest(props, ["allowNewValues", "children", "defaultValue", "error", "errorId", "filteredOptions", "id", "isListOpen", "isLoading", "isMultiSelect", "onToggleSelected", "selectedOptions", "maxSelected", "options", "value", "onChange", "onClear", "shouldAutocomplete", "size"]);
|
|
70
70
|
return (react_1.default.createElement(inputContext_1.InputContextProvider, { value: {
|
|
71
71
|
defaultValue,
|
|
72
72
|
error,
|
|
@@ -83,6 +83,7 @@ const ComboboxProvider = (0, react_1.forwardRef)((props, ref) => {
|
|
|
83
83
|
allowNewValues,
|
|
84
84
|
isMultiSelect,
|
|
85
85
|
selectedOptions,
|
|
86
|
+
maxSelected,
|
|
86
87
|
onToggleSelected,
|
|
87
88
|
options,
|
|
88
89
|
} },
|
|
@@ -50,6 +50,6 @@ const ComboboxWrapper = ({ children, className, hasError, inputProps, inputSize,
|
|
|
50
50
|
"navds-combobox--error": hasError,
|
|
51
51
|
"navds-combobox--disabled": !!inputProps.disabled,
|
|
52
52
|
"navds-combobox--focused": hasFocusWithin,
|
|
53
|
-
}), onFocus: onFocusInsideWrapper, onBlur: onBlurWrapper
|
|
53
|
+
}), onFocus: onFocusInsideWrapper, onBlur: onBlurWrapper }, children));
|
|
54
54
|
};
|
|
55
55
|
exports.default = ComboboxWrapper;
|
|
@@ -13,50 +13,68 @@ const selectedOptionsContext_1 = require("../SelectedOptions/selectedOptionsCont
|
|
|
13
13
|
const filtered_options_util_1 = __importDefault(require("./filtered-options-util"));
|
|
14
14
|
const filteredOptionsContext_1 = require("./filteredOptionsContext");
|
|
15
15
|
const FilteredOptions = () => {
|
|
16
|
+
var _a;
|
|
16
17
|
const { inputProps: { id }, size, value, } = (0, inputContext_1.useInputContext)();
|
|
17
18
|
const { allowNewValues, isLoading, isListOpen, filteredOptions, setFilteredOptionsRef, isMouseLastUsedInputDevice, setIsMouseLastUsedInputDevice, isValueNew, toggleIsListOpen, activeDecendantId, virtualFocus, } = (0, filteredOptionsContext_1.useFilteredOptionsContext)();
|
|
18
|
-
const { isMultiSelect, selectedOptions, toggleOption } = (0, selectedOptionsContext_1.useSelectedOptionsContext)();
|
|
19
|
-
|
|
19
|
+
const { isMultiSelect, selectedOptions, toggleOption, maxSelected } = (0, selectedOptionsContext_1.useSelectedOptionsContext)();
|
|
20
|
+
const isDisabled = (option) => (maxSelected === null || maxSelected === void 0 ? void 0 : maxSelected.isLimitReached) && !selectedOptions.includes(option);
|
|
21
|
+
const shouldRenderNonSelectables = (maxSelected === null || maxSelected === void 0 ? void 0 : maxSelected.isLimitReached) || // Render maxSelected message
|
|
22
|
+
isLoading || // Render loading message
|
|
23
|
+
(!isLoading && filteredOptions.length === 0); // Render no hits message
|
|
24
|
+
const shouldRenderFilteredOptionsList = (allowNewValues && isValueNew && !(maxSelected === null || maxSelected === void 0 ? void 0 : maxSelected.isLimitReached)) || // Render add new option
|
|
25
|
+
filteredOptions.length > 0; // Render filtered options
|
|
26
|
+
return (react_1.default.createElement("div", { className: (0, clsx_1.default)("navds-combobox__list", {
|
|
20
27
|
"navds-combobox__list--closed": !isListOpen,
|
|
21
28
|
"navds-combobox__list--with-hover": isMouseLastUsedInputDevice,
|
|
22
|
-
}), id: filtered_options_util_1.default.getFilteredOptionsId(id),
|
|
23
|
-
|
|
24
|
-
react_1.default.createElement(
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
29
|
+
}), id: filtered_options_util_1.default.getFilteredOptionsId(id), tabIndex: -1 },
|
|
30
|
+
shouldRenderNonSelectables && (react_1.default.createElement("div", { className: "navds-combobox__list_non-selectables", role: "status" },
|
|
31
|
+
(maxSelected === null || maxSelected === void 0 ? void 0 : maxSelected.isLimitReached) && (react_1.default.createElement("div", { className: "navds-combobox__list-item--max-selected", id: filtered_options_util_1.default.getMaxSelectedOptionsId(id) }, (_a = maxSelected.message) !== null && _a !== void 0 ? _a : `${selectedOptions.length} av ${maxSelected.limit} er valgt.`)),
|
|
32
|
+
isLoading && (react_1.default.createElement("div", { className: "navds-combobox__list-item--loading", id: filtered_options_util_1.default.getIsLoadingId(id) },
|
|
33
|
+
react_1.default.createElement(loader_1.Loader, { title: "S\u00F8ker..." }))),
|
|
34
|
+
!isLoading && filteredOptions.length === 0 && (react_1.default.createElement("div", { className: "navds-combobox__list-item--no-options", id: filtered_options_util_1.default.getNoHitsId(id) }, "Ingen s\u00F8ketreff")))),
|
|
35
|
+
shouldRenderFilteredOptionsList && (react_1.default.createElement("ul", { ref: setFilteredOptionsRef, role: "listbox", className: "navds-combobox__list-options" },
|
|
36
|
+
isValueNew && !(maxSelected === null || maxSelected === void 0 ? void 0 : maxSelected.isLimitReached) && allowNewValues && (react_1.default.createElement("li", { tabIndex: -1, onMouseMove: () => {
|
|
37
|
+
if (activeDecendantId !==
|
|
38
|
+
filtered_options_util_1.default.getAddNewOptionId(id)) {
|
|
39
|
+
virtualFocus.moveFocusToElement(filtered_options_util_1.default.getAddNewOptionId(id));
|
|
40
|
+
setIsMouseLastUsedInputDevice(true);
|
|
41
|
+
}
|
|
42
|
+
}, onPointerUp: (event) => {
|
|
43
|
+
toggleOption(value, event);
|
|
44
|
+
if (!isMultiSelect && !selectedOptions.includes(value))
|
|
45
|
+
toggleIsListOpen(false);
|
|
46
|
+
}, id: filtered_options_util_1.default.getAddNewOptionId(id), className: (0, clsx_1.default)("navds-combobox__list-item navds-combobox__list-item--new-option", {
|
|
47
|
+
"navds-combobox__list-item--new-option--focus": activeDecendantId ===
|
|
48
|
+
filtered_options_util_1.default.getAddNewOptionId(id),
|
|
49
|
+
}), role: "option", "aria-selected": false },
|
|
50
|
+
react_1.default.createElement(aksel_icons_1.PlusIcon, { "aria-hidden": true }),
|
|
51
|
+
react_1.default.createElement(typography_1.BodyShort, { size: size },
|
|
52
|
+
"Legg til",
|
|
53
|
+
" ",
|
|
54
|
+
react_1.default.createElement(typography_1.Label, { as: "span", size: size },
|
|
55
|
+
"\u201C",
|
|
56
|
+
value,
|
|
57
|
+
"\u201D")))),
|
|
58
|
+
filteredOptions.map((option) => (react_1.default.createElement("li", { className: (0, clsx_1.default)("navds-combobox__list-item", {
|
|
59
|
+
"navds-combobox__list-item--focus": activeDecendantId ===
|
|
60
|
+
filtered_options_util_1.default.getOptionId(id, option),
|
|
61
|
+
"navds-combobox__list-item--selected": selectedOptions.includes(option),
|
|
62
|
+
}), "data-no-focus": isDisabled(option) || undefined, id: filtered_options_util_1.default.getOptionId(id, option), key: option, tabIndex: -1, onMouseMove: () => {
|
|
63
|
+
if (activeDecendantId !==
|
|
64
|
+
filtered_options_util_1.default.getOptionId(id, option)) {
|
|
65
|
+
virtualFocus.moveFocusToElement(filtered_options_util_1.default.getOptionId(id, option));
|
|
66
|
+
setIsMouseLastUsedInputDevice(true);
|
|
67
|
+
}
|
|
68
|
+
}, onPointerUp: (event) => {
|
|
69
|
+
if (isDisabled(option)) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
toggleOption(option, event);
|
|
73
|
+
if (!isMultiSelect && !selectedOptions.includes(option)) {
|
|
74
|
+
toggleIsListOpen(false);
|
|
75
|
+
}
|
|
76
|
+
}, role: "option", "aria-selected": selectedOptions.includes(option), "aria-disabled": isDisabled(option) || undefined },
|
|
77
|
+
react_1.default.createElement(typography_1.BodyShort, { size: size }, option),
|
|
78
|
+
selectedOptions.includes(option) && react_1.default.createElement(aksel_icons_1.CheckmarkIcon, null))))))));
|
|
61
79
|
};
|
|
62
80
|
exports.default = FilteredOptions;
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const normalizeText = (text) => typeof text === "string" ? text.toLocaleLowerCase().trim() : "";
|
|
4
4
|
const isPartOfText = (value, text) => normalizeText(text).startsWith(normalizeText(value !== null && value !== void 0 ? value : ""));
|
|
5
5
|
const isValueInList = (value, list) => list === null || list === void 0 ? void 0 : list.find((listItem) => normalizeText(value) === normalizeText(listItem));
|
|
6
|
-
const getMatchingValuesFromList = (value, list) => list === null || list === void 0 ? void 0 : list.filter((listItem) => isPartOfText(value, listItem));
|
|
6
|
+
const getMatchingValuesFromList = (value, list, alwaysIncluded) => list === null || list === void 0 ? void 0 : list.filter((listItem) => isPartOfText(value, listItem) || alwaysIncluded.includes(listItem));
|
|
7
7
|
const getFilteredOptionsId = (comboboxId) => `${comboboxId}-filtered-options`;
|
|
8
8
|
const getOptionId = (comboboxId, option) => `${comboboxId.toLocaleLowerCase()}-option-${option
|
|
9
9
|
.replace(" ", "-")
|
|
@@ -11,6 +11,7 @@ const getOptionId = (comboboxId, option) => `${comboboxId.toLocaleLowerCase()}-o
|
|
|
11
11
|
const getAddNewOptionId = (comboboxId) => `${comboboxId}-combobox-new-option`;
|
|
12
12
|
const getIsLoadingId = (comboboxId) => `${comboboxId}-is-loading`;
|
|
13
13
|
const getNoHitsId = (comboboxId) => `${comboboxId}-no-hits`;
|
|
14
|
+
const getMaxSelectedOptionsId = (comboboxId) => `${comboboxId}-max-selected-options`;
|
|
14
15
|
exports.default = {
|
|
15
16
|
normalizeText,
|
|
16
17
|
isPartOfText,
|
|
@@ -21,4 +22,5 @@ exports.default = {
|
|
|
21
22
|
getOptionId,
|
|
22
23
|
getIsLoadingId,
|
|
23
24
|
getNoHitsId,
|
|
25
|
+
getMaxSelectedOptionsId,
|
|
24
26
|
};
|
|
@@ -31,6 +31,7 @@ const clsx_1 = __importDefault(require("clsx"));
|
|
|
31
31
|
const react_1 = __importStar(require("react"));
|
|
32
32
|
const hooks_1 = require("../../../util/hooks");
|
|
33
33
|
const inputContext_1 = require("../Input/inputContext");
|
|
34
|
+
const selectedOptionsContext_1 = require("../SelectedOptions/selectedOptionsContext");
|
|
34
35
|
const customOptionsContext_1 = require("../customOptionsContext");
|
|
35
36
|
const filtered_options_util_1 = __importDefault(require("./filtered-options-util"));
|
|
36
37
|
const useVirtualFocus_1 = __importDefault(require("./useVirtualFocus"));
|
|
@@ -40,6 +41,7 @@ const FilteredOptionsProvider = ({ children, value: props, }) => {
|
|
|
40
41
|
const [filteredOptionsRef, setFilteredOptionsRef] = (0, react_1.useState)(null);
|
|
41
42
|
const virtualFocus = (0, useVirtualFocus_1.default)(filteredOptionsRef);
|
|
42
43
|
const { inputProps: { "aria-describedby": partialAriaDescribedBy, id }, value, searchTerm, setValue, setSearchTerm, shouldAutocomplete, } = (0, inputContext_1.useInputContext)();
|
|
44
|
+
const { selectedOptions, maxSelected } = (0, selectedOptionsContext_1.useSelectedOptionsContext)();
|
|
43
45
|
const [isInternalListOpen, setInternalListOpen] = (0, react_1.useState)(false);
|
|
44
46
|
const { customOptions } = (0, customOptionsContext_1.useCustomOptionsContext)();
|
|
45
47
|
const filteredOptions = (0, react_1.useMemo)(() => {
|
|
@@ -47,8 +49,14 @@ const FilteredOptionsProvider = ({ children, value: props, }) => {
|
|
|
47
49
|
return externalFilteredOptions;
|
|
48
50
|
}
|
|
49
51
|
const opts = [...customOptions, ...options];
|
|
50
|
-
return filtered_options_util_1.default.getMatchingValuesFromList(searchTerm, opts);
|
|
51
|
-
}, [
|
|
52
|
+
return filtered_options_util_1.default.getMatchingValuesFromList(searchTerm, opts, selectedOptions);
|
|
53
|
+
}, [
|
|
54
|
+
customOptions,
|
|
55
|
+
externalFilteredOptions,
|
|
56
|
+
options,
|
|
57
|
+
searchTerm,
|
|
58
|
+
selectedOptions,
|
|
59
|
+
]);
|
|
52
60
|
const previousSearchTerm = (0, hooks_1.usePrevious)(searchTerm);
|
|
53
61
|
const [isMouseLastUsedInputDevice, setIsMouseLastUsedInputDevice] = (0, react_1.useState)(false);
|
|
54
62
|
const filteredOptionsMap = (0, react_1.useMemo)(() => options.reduce((map, _option) => (Object.assign(Object.assign({}, map), { [filtered_options_util_1.default.getOptionId(id, _option)]: _option })), {
|
|
@@ -94,10 +102,14 @@ const FilteredOptionsProvider = ({ children, value: props, }) => {
|
|
|
94
102
|
activeOption = filtered_options_util_1.default.getIsLoadingId(id);
|
|
95
103
|
}
|
|
96
104
|
}
|
|
97
|
-
|
|
105
|
+
const maybeMaxSelectedOptionsId = (maxSelected === null || maxSelected === void 0 ? void 0 : maxSelected.isLimitReached) &&
|
|
106
|
+
filtered_options_util_1.default.getMaxSelectedOptionsId(id);
|
|
107
|
+
return ((0, clsx_1.default)(activeOption, maybeMaxSelectedOptionsId, partialAriaDescribedBy) ||
|
|
108
|
+
undefined);
|
|
98
109
|
}, [
|
|
99
110
|
isListOpen,
|
|
100
111
|
isLoading,
|
|
112
|
+
maxSelected === null || maxSelected === void 0 ? void 0 : maxSelected.isLimitReached,
|
|
101
113
|
value,
|
|
102
114
|
partialAriaDescribedBy,
|
|
103
115
|
shouldAutocomplete,
|
|
@@ -2,41 +2,62 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const react_1 = require("react");
|
|
4
4
|
const useVirtualFocus = (containerRef) => {
|
|
5
|
-
const [
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
5
|
+
const [activeElement, setActiveElement] = (0, react_1.useState)(undefined);
|
|
6
|
+
const getListOfAllChildren = () => { var _a; return Array.from((_a = containerRef === null || containerRef === void 0 ? void 0 : containerRef.children) !== null && _a !== void 0 ? _a : []); };
|
|
7
|
+
const getElementsAbleToReceiveFocus = () => getListOfAllChildren().filter((child) => child.getAttribute("data-no-focus") !== "true");
|
|
8
|
+
const getElementById = (id) => getListOfAllChildren().find((element) => element.id === id);
|
|
9
|
+
const isFocusOnTheTop = () => activeElement
|
|
10
|
+
? getElementsAbleToReceiveFocus().indexOf(activeElement) === 0
|
|
11
|
+
: false;
|
|
12
|
+
const isFocusOnTheBottom = () => {
|
|
13
|
+
const elementsAbleToReceiveFocus = getElementsAbleToReceiveFocus();
|
|
14
|
+
return activeElement
|
|
15
|
+
? elementsAbleToReceiveFocus.indexOf(activeElement) ===
|
|
16
|
+
elementsAbleToReceiveFocus.length - 1
|
|
17
|
+
: false;
|
|
18
|
+
};
|
|
19
|
+
const _moveFocusAndScrollTo = (_element) => {
|
|
20
|
+
var _a;
|
|
21
|
+
setActiveElement(_element);
|
|
22
|
+
(_a = _element === null || _element === void 0 ? void 0 : _element.scrollIntoView) === null || _a === void 0 ? void 0 : _a.call(_element, { block: "nearest" });
|
|
23
|
+
};
|
|
24
|
+
const moveFocusUp = () => {
|
|
25
|
+
if (!activeElement) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const elementsAbleToReceiveFocus = getElementsAbleToReceiveFocus();
|
|
29
|
+
const _currentIndex = elementsAbleToReceiveFocus.indexOf(activeElement);
|
|
30
|
+
const elementAbove = elementsAbleToReceiveFocus[_currentIndex - 1];
|
|
31
|
+
if (_currentIndex === 0) {
|
|
32
|
+
setActiveElement(undefined);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
_moveFocusAndScrollTo(elementAbove);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
const moveFocusDown = () => {
|
|
39
|
+
const elementsAbleToReceiveFocus = getElementsAbleToReceiveFocus();
|
|
40
|
+
if (!activeElement) {
|
|
41
|
+
_moveFocusAndScrollTo(elementsAbleToReceiveFocus[0]);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const _currentIndex = elementsAbleToReceiveFocus.indexOf(activeElement);
|
|
45
|
+
if (_currentIndex === elementsAbleToReceiveFocus.length - 1) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
_moveFocusAndScrollTo(elementsAbleToReceiveFocus[_currentIndex + 1]);
|
|
23
50
|
}
|
|
24
51
|
};
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
52
|
+
const moveFocusToTop = () => _moveFocusAndScrollTo(undefined);
|
|
53
|
+
const moveFocusToBottom = () => {
|
|
54
|
+
const elementsAbleToReceiveFocus = getElementsAbleToReceiveFocus();
|
|
55
|
+
return _moveFocusAndScrollTo(elementsAbleToReceiveFocus[elementsAbleToReceiveFocus.length - 1]);
|
|
28
56
|
};
|
|
29
|
-
const moveFocusUp = () => _moveFocusAndScrollTo(Math.max(index - 1, -1));
|
|
30
|
-
const moveFocusDown = () => _moveFocusAndScrollTo(Math.min(index + 1, elementsAbleToReceiveFocus.length - 1));
|
|
31
|
-
const moveFocusToTop = () => _moveFocusAndScrollTo(-1);
|
|
32
|
-
const moveFocusToBottom = () => _moveFocusAndScrollTo(elementsAbleToReceiveFocus.length - 1);
|
|
33
57
|
const moveFocusToElement = (id) => {
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
: -1;
|
|
38
|
-
if (indexOfElement >= 0) {
|
|
39
|
-
setIndex(indexOfElement);
|
|
58
|
+
const _element = getElementsAbleToReceiveFocus().find((_focusableElement) => _focusableElement.getAttribute("id") === id);
|
|
59
|
+
if (_element) {
|
|
60
|
+
setActiveElement(_element);
|
|
40
61
|
}
|
|
41
62
|
};
|
|
42
63
|
return {
|
|
@@ -44,7 +65,6 @@ const useVirtualFocus = (containerRef) => {
|
|
|
44
65
|
getElementById,
|
|
45
66
|
isFocusOnTheTop,
|
|
46
67
|
isFocusOnTheBottom,
|
|
47
|
-
setIndex,
|
|
48
68
|
moveFocusUp,
|
|
49
69
|
moveFocusDown,
|
|
50
70
|
moveFocusToElement,
|
|
@@ -101,9 +101,11 @@ const Input = (0, react_1.forwardRef)((_a, ref) => {
|
|
|
101
101
|
onEnter(e);
|
|
102
102
|
break;
|
|
103
103
|
case "Home":
|
|
104
|
+
toggleIsListOpen(false);
|
|
104
105
|
virtualFocus.moveFocusToTop();
|
|
105
106
|
break;
|
|
106
107
|
case "End":
|
|
108
|
+
toggleIsListOpen(true);
|
|
107
109
|
virtualFocus.moveFocusToBottom();
|
|
108
110
|
break;
|
|
109
111
|
default:
|
|
@@ -134,7 +136,7 @@ const Input = (0, react_1.forwardRef)((_a, ref) => {
|
|
|
134
136
|
// Otherwise ignore keystrokes, so it doesn't interfere with text editing
|
|
135
137
|
if (isListOpen && activeDecendantId) {
|
|
136
138
|
e.preventDefault();
|
|
137
|
-
if (virtualFocus.isFocusOnTheTop) {
|
|
139
|
+
if (virtualFocus.isFocusOnTheTop()) {
|
|
138
140
|
toggleIsListOpen(false);
|
|
139
141
|
}
|
|
140
142
|
virtualFocus.moveFocusUp();
|
|
@@ -32,7 +32,7 @@ const SelectedOptionsContext = (0, react_1.createContext)({});
|
|
|
32
32
|
const SelectedOptionsProvider = ({ children, value, }) => {
|
|
33
33
|
const { clearInput, focusInput } = (0, inputContext_1.useInputContext)();
|
|
34
34
|
const { customOptions, removeCustomOption, addCustomOption, setCustomOptions, } = (0, customOptionsContext_1.useCustomOptionsContext)();
|
|
35
|
-
const { allowNewValues, isMultiSelect, selectedOptions: externalSelectedOptions, onToggleSelected, options, } = value;
|
|
35
|
+
const { allowNewValues, isMultiSelect, selectedOptions: externalSelectedOptions, onToggleSelected, options, maxSelected, } = value;
|
|
36
36
|
const [internalSelectedOptions, setSelectedOptions] = (0, react_1.useState)([]);
|
|
37
37
|
const selectedOptions = (0, react_1.useMemo)(() => externalSelectedOptions !== null && externalSelectedOptions !== void 0 ? externalSelectedOptions : [...customOptions, ...internalSelectedOptions], [customOptions, externalSelectedOptions, internalSelectedOptions]);
|
|
38
38
|
const addSelectedOption = (0, react_1.useCallback)((option) => {
|
|
@@ -90,6 +90,7 @@ const SelectedOptionsProvider = ({ children, value, }) => {
|
|
|
90
90
|
selectedOptions,
|
|
91
91
|
]);
|
|
92
92
|
const prevSelectedOptions = (0, hooks_1.usePrevious)(selectedOptions);
|
|
93
|
+
const isLimitReached = !!(maxSelected === null || maxSelected === void 0 ? void 0 : maxSelected.limit) && selectedOptions.length >= maxSelected.limit;
|
|
93
94
|
const selectedOptionsState = {
|
|
94
95
|
addSelectedOption,
|
|
95
96
|
isMultiSelect,
|
|
@@ -98,6 +99,7 @@ const SelectedOptionsProvider = ({ children, value, }) => {
|
|
|
98
99
|
selectedOptions,
|
|
99
100
|
setSelectedOptions,
|
|
100
101
|
toggleOption,
|
|
102
|
+
maxSelected: maxSelected && Object.assign(Object.assign({}, maxSelected), { isLimitReached }),
|
|
101
103
|
};
|
|
102
104
|
return (react_1.default.createElement(SelectedOptionsContext.Provider, { value: selectedOptionsState }, children));
|
|
103
105
|
};
|
|
@@ -63,7 +63,7 @@ exports.HelpText = (0, react_1.forwardRef)((_a, ref) => {
|
|
|
63
63
|
const mergedRef = (0, useMergeRefs_1.useMergeRefs)(buttonRef, ref);
|
|
64
64
|
const [open, setOpen] = (0, react_1.useState)(false);
|
|
65
65
|
return (react_1.default.createElement("div", { className: (0, clsx_1.default)("navds-help-text", wrapperClassName) },
|
|
66
|
-
react_1.default.createElement("button", Object.assign({}, rest, { ref: mergedRef, onClick: (0, composeEventHandlers_1.composeEventHandlers)(onClick, () => setOpen((x) => x)), className: (0, clsx_1.default)(className, "navds-help-text__button"), type: "button", "aria-expanded": open }),
|
|
66
|
+
react_1.default.createElement("button", Object.assign({}, rest, { ref: mergedRef, onClick: (0, composeEventHandlers_1.composeEventHandlers)(onClick, () => setOpen((x) => !x)), className: (0, clsx_1.default)(className, "navds-help-text__button"), type: "button", "aria-expanded": open }),
|
|
67
67
|
react_1.default.createElement(HelpTextIcon_1.HelpTextIcon, { title: title }),
|
|
68
68
|
react_1.default.createElement(HelpTextIcon_1.HelpTextIcon, { filled: true, title: title })),
|
|
69
69
|
react_1.default.createElement(popover_1.Popover, { onClose: () => setOpen(false), className: "navds-help-text__popover", open: open, anchorEl: buttonRef.current, placement: placement, strategy: strategy, offset: 12 },
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
26
|
+
var t = {};
|
|
27
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
28
|
+
t[p] = s[p];
|
|
29
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
30
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
31
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
32
|
+
t[p[i]] = s[p[i]];
|
|
33
|
+
}
|
|
34
|
+
return t;
|
|
35
|
+
};
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
exports.createContext = void 0;
|
|
38
|
+
/**
|
|
39
|
+
* Custom createContext to consolidate context-implementation across the system
|
|
40
|
+
* Inspired by:
|
|
41
|
+
* - https://github.com/radix-ui/primitives/blob/main/packages/react/context/src/createContext.tsx
|
|
42
|
+
* - https://github.com/chakra-ui/chakra-ui/blob/5ec0be610b5a69afba01a9c22365155c1b519136/packages/hooks/context/src/index.ts
|
|
43
|
+
*/
|
|
44
|
+
const react_1 = __importStar(require("react"));
|
|
45
|
+
function getErrorMessage(hook, provider) {
|
|
46
|
+
return `${hook} returned \`undefined\`. Seems you forgot to wrap component within ${provider}`;
|
|
47
|
+
}
|
|
48
|
+
function createContext(options = {}) {
|
|
49
|
+
const { name, hookName = "useContext", providerName = "Provider", errorMessage, defaultValue, } = options;
|
|
50
|
+
const Context = (0, react_1.createContext)(defaultValue);
|
|
51
|
+
function Provider(_a) {
|
|
52
|
+
var { children } = _a, context = __rest(_a, ["children"]);
|
|
53
|
+
// Only re-memoize when prop values change
|
|
54
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
55
|
+
const value = react_1.default.useMemo(() => context, Object.values(context));
|
|
56
|
+
return react_1.default.createElement(Context.Provider, { value: value }, children);
|
|
57
|
+
}
|
|
58
|
+
function useContext() {
|
|
59
|
+
var _a;
|
|
60
|
+
const context = (0, react_1.useContext)(Context);
|
|
61
|
+
if (!context) {
|
|
62
|
+
const error = new Error(errorMessage !== null && errorMessage !== void 0 ? errorMessage : getErrorMessage(hookName, providerName));
|
|
63
|
+
error.name = "ContextError";
|
|
64
|
+
(_a = Error.captureStackTrace) === null || _a === void 0 ? void 0 : _a.call(Error, error, useContext);
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
return context;
|
|
68
|
+
}
|
|
69
|
+
Context.displayName = name;
|
|
70
|
+
return [Provider, useContext];
|
|
71
|
+
}
|
|
72
|
+
exports.createContext = createContext;
|