@digdir/designsystemet-react 0.59.0 → 0.59.1-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/dist/cjs/components/DropdownMenu/DropdownMenuContent.js +4 -4
  2. package/dist/cjs/components/DropdownMenu/DropdownMenuTrigger.js +1 -1
  3. package/dist/cjs/components/Modal/ModalDialog.js +1 -1
  4. package/dist/cjs/components/Popover/PopoverContent.js +6 -6
  5. package/dist/cjs/components/Popover/PopoverTrigger.js +1 -1
  6. package/dist/cjs/components/Tooltip/Tooltip.js +6 -6
  7. package/dist/cjs/components/form/Checkbox/Checkbox.js +1 -1
  8. package/dist/cjs/components/form/Combobox/Combobox.js +60 -177
  9. package/dist/cjs/components/form/Combobox/ComboboxContext.js +8 -0
  10. package/dist/cjs/components/form/Combobox/ComboboxIdContext.js +42 -0
  11. package/dist/cjs/components/form/Combobox/Custom/Custom.js +14 -9
  12. package/dist/cjs/components/form/Combobox/Empty/Empty.js +4 -4
  13. package/dist/cjs/components/form/Combobox/Option/Option.js +15 -33
  14. package/dist/cjs/components/form/Combobox/Option/useComboboxOption.js +47 -0
  15. package/dist/cjs/components/form/Combobox/internal/ComboboxChips.js +14 -6
  16. package/dist/cjs/components/form/Combobox/internal/ComboboxClearButton.js +4 -4
  17. package/dist/cjs/components/form/Combobox/internal/ComboboxInput.js +40 -35
  18. package/dist/cjs/components/form/Combobox/internal/ComboboxNative.js +2 -2
  19. package/dist/cjs/components/form/Combobox/useCombobox.js +46 -32
  20. package/dist/cjs/components/form/Combobox/useComboboxKeyboard.js +79 -0
  21. package/dist/cjs/components/form/Combobox/useFloatingCombobox.js +78 -0
  22. package/dist/cjs/components/form/Search/Search.js +1 -1
  23. package/dist/cjs/node_modules/@floating-ui/utils/{dom/dist → dist}/floating-ui.utils.dom.js +7 -4
  24. package/dist/cjs/node_modules/@floating-ui/utils/dist/floating-ui.utils.js +5 -0
  25. package/dist/cjs/{node_modules → packages/react/node_modules}/@floating-ui/core/dist/floating-ui.core.js +40 -16
  26. package/dist/cjs/{node_modules → packages/react/node_modules}/@floating-ui/dom/dist/floating-ui.dom.js +83 -31
  27. package/dist/cjs/{node_modules → packages/react/node_modules}/@floating-ui/react/dist/floating-ui.react.js +307 -157
  28. package/dist/cjs/{node_modules/@floating-ui/react/utils → packages/react/node_modules/@floating-ui/react}/dist/floating-ui.react.utils.js +9 -4
  29. package/dist/cjs/{node_modules → packages/react/node_modules}/@floating-ui/react-dom/dist/floating-ui.react-dom.js +22 -18
  30. package/dist/{esm → cjs/packages/react}/node_modules/tabbable/dist/index.esm.js +59 -13
  31. package/dist/cjs/utilities/RovingTabIndex/RovingTabindexItem.js +1 -1
  32. package/dist/cjs/utilities/RovingTabIndex/RovingTabindexRoot.js +1 -1
  33. package/dist/esm/components/DropdownMenu/DropdownMenuContent.js +3 -3
  34. package/dist/esm/components/DropdownMenu/DropdownMenuTrigger.js +1 -1
  35. package/dist/esm/components/Modal/ModalDialog.js +1 -1
  36. package/dist/esm/components/Popover/PopoverContent.js +4 -4
  37. package/dist/esm/components/Popover/PopoverTrigger.js +1 -1
  38. package/dist/esm/components/Tooltip/Tooltip.js +4 -4
  39. package/dist/esm/components/form/Checkbox/Checkbox.js +1 -1
  40. package/dist/esm/components/form/Combobox/Combobox.js +65 -182
  41. package/dist/esm/components/form/Combobox/ComboboxContext.js +6 -0
  42. package/dist/esm/components/form/Combobox/ComboboxIdContext.js +35 -0
  43. package/dist/esm/components/form/Combobox/Custom/Custom.js +13 -8
  44. package/dist/esm/components/form/Combobox/Empty/Empty.js +3 -3
  45. package/dist/esm/components/form/Combobox/Option/Option.js +15 -33
  46. package/dist/esm/components/form/Combobox/Option/useComboboxOption.js +45 -0
  47. package/dist/esm/components/form/Combobox/internal/ComboboxChips.js +13 -5
  48. package/dist/esm/components/form/Combobox/internal/ComboboxClearButton.js +3 -3
  49. package/dist/esm/components/form/Combobox/internal/ComboboxInput.js +39 -34
  50. package/dist/esm/components/form/Combobox/internal/ComboboxNative.js +2 -2
  51. package/dist/esm/components/form/Combobox/useCombobox.js +46 -32
  52. package/dist/esm/components/form/Combobox/useComboboxKeyboard.js +77 -0
  53. package/dist/esm/components/form/Combobox/useFloatingCombobox.js +76 -0
  54. package/dist/esm/components/form/Search/Search.js +1 -1
  55. package/dist/esm/node_modules/@floating-ui/utils/{dom/dist → dist}/floating-ui.utils.dom.js +7 -4
  56. package/dist/esm/node_modules/@floating-ui/utils/dist/floating-ui.utils.js +5 -0
  57. package/dist/esm/{node_modules → packages/react/node_modules}/@floating-ui/core/dist/floating-ui.core.js +40 -16
  58. package/dist/esm/{node_modules → packages/react/node_modules}/@floating-ui/dom/dist/floating-ui.dom.js +82 -30
  59. package/dist/esm/{node_modules → packages/react/node_modules}/@floating-ui/react/dist/floating-ui.react.js +282 -135
  60. package/dist/esm/{node_modules/@floating-ui/react/utils → packages/react/node_modules/@floating-ui/react}/dist/floating-ui.react.utils.js +9 -5
  61. package/dist/esm/{node_modules → packages/react/node_modules}/@floating-ui/react-dom/dist/floating-ui.react-dom.js +19 -14
  62. package/dist/{cjs → esm/packages/react}/node_modules/tabbable/dist/index.esm.js +55 -15
  63. package/dist/esm/utilities/RovingTabIndex/RovingTabindexItem.js +1 -1
  64. package/dist/esm/utilities/RovingTabIndex/RovingTabindexRoot.js +1 -1
  65. package/dist/types/components/form/Combobox/Combobox.d.ts +104 -39
  66. package/dist/types/components/form/Combobox/Combobox.d.ts.map +1 -1
  67. package/dist/types/components/form/Combobox/ComboboxContext.d.ts +48 -0
  68. package/dist/types/components/form/Combobox/ComboboxContext.d.ts.map +1 -0
  69. package/dist/types/components/form/Combobox/ComboboxIdContext.d.ts +19 -0
  70. package/dist/types/components/form/Combobox/ComboboxIdContext.d.ts.map +1 -0
  71. package/dist/types/components/form/Combobox/Custom/Custom.d.ts.map +1 -1
  72. package/dist/types/components/form/Combobox/Option/Option.d.ts +2 -2
  73. package/dist/types/components/form/Combobox/Option/Option.d.ts.map +1 -1
  74. package/dist/types/components/form/Combobox/Option/useComboboxOption.d.ts +14 -0
  75. package/dist/types/components/form/Combobox/Option/useComboboxOption.d.ts.map +1 -0
  76. package/dist/types/components/form/Combobox/internal/ComboboxChips.d.ts.map +1 -1
  77. package/dist/types/components/form/Combobox/internal/ComboboxInput.d.ts +0 -1
  78. package/dist/types/components/form/Combobox/internal/ComboboxInput.d.ts.map +1 -1
  79. package/dist/types/components/form/Combobox/internal/ComboboxNative.d.ts +3 -1
  80. package/dist/types/components/form/Combobox/internal/ComboboxNative.d.ts.map +1 -1
  81. package/dist/types/components/form/Combobox/useCombobox.d.ts +13 -5
  82. package/dist/types/components/form/Combobox/useCombobox.d.ts.map +1 -1
  83. package/dist/types/components/form/Combobox/useComboboxKeyboard.d.ts +20 -0
  84. package/dist/types/components/form/Combobox/useComboboxKeyboard.d.ts.map +1 -0
  85. package/dist/types/components/form/Combobox/useFloatingCombobox.d.ts +41 -0
  86. package/dist/types/components/form/Combobox/useFloatingCombobox.d.ts.map +1 -0
  87. package/package.json +3 -3
  88. package/dist/cjs/node_modules/@floating-ui/react/node_modules/@floating-ui/utils/dist/floating-ui.utils.js +0 -6
  89. package/dist/cjs/node_modules/@floating-ui/react/node_modules/@floating-ui/utils/dom/dist/floating-ui.utils.dom.js +0 -68
  90. package/dist/esm/node_modules/@floating-ui/react/node_modules/@floating-ui/utils/dist/floating-ui.utils.js +0 -4
  91. package/dist/esm/node_modules/@floating-ui/react/node_modules/@floating-ui/utils/dom/dist/floating-ui.utils.dom.js +0 -57
@@ -4,50 +4,32 @@
4
4
  var jsxRuntime = require('react/jsx-runtime');
5
5
  var React = require('react');
6
6
  var clsx = require('../../../../node_modules/clsx/dist/clsx.js');
7
- var floatingUi_react = require('../../../../node_modules/@floating-ui/react/dist/floating-ui.react.js');
8
- var Combobox = require('../Combobox.js');
9
- var useDebounce = require('../../../../utilities/useDebounce.js');
7
+ var ComboboxContext = require('../ComboboxContext.js');
10
8
  var SelectedIcon = require('./Icon/SelectedIcon.js');
11
9
  var Option_module = require('./Option.module.css.js');
12
10
  var Description = require('./Description/Description.js');
11
+ var useComboboxOption = require('./useComboboxOption.js');
13
12
  var objectUtils = require('../../../../utilities/objectUtils.js');
14
13
  var Label = require('../../../Typography/Label/Label.js');
15
14
 
16
- const ComboboxOption = React.forwardRef(({ value, description, children, className, ...rest }, ref) => {
15
+ const ComboboxOption = React.memo(React.forwardRef(({ value, description, children, className, ...rest }, forwardedRef) => {
17
16
  const labelId = React.useId();
18
- const generatedId = React.useId();
19
- const context = React.useContext(Combobox.ComboboxContext);
17
+ const { id, ref, selected, active, onOptionClick } = useComboboxOption({
18
+ id: rest.id,
19
+ ref: forwardedRef,
20
+ value,
21
+ });
22
+ const context = React.useContext(ComboboxContext.ComboboxContext);
20
23
  if (!context) {
21
24
  throw new Error('ComboboxOption must be used within a Combobox');
22
25
  }
23
- const { selectedOptions, activeIndex, setActiveOption, onOptionClick, size, listRef, optionValues, multiple, } = context;
24
- const index = React.useMemo(() => optionValues.indexOf(value), [optionValues, value]);
25
- const combinedRef = floatingUi_react.useMergeRefs([
26
- (node) => {
27
- listRef.current[index] = node;
28
- },
29
- ref,
30
- ]);
31
- if (index === -1) {
32
- throw new Error('Internal error: ComboboxOption did not find index');
33
- }
34
- const selected = selectedOptions.find((option) => option.value === value);
35
- React.useEffect(() => {
36
- if (activeIndex === index)
37
- setActiveOption(index, rest.id || generatedId);
38
- }, [activeIndex, generatedId, index, rest.id, setActiveOption]);
39
- const onOptionClickDebounced = useDebounce(() => onOptionClick(value), 50);
40
- return (jsxRuntime.jsxs("button", { id: rest.id || generatedId, role: 'option', type: 'button', "aria-selected": !!selected, "aria-labelledby": labelId, tabIndex: -1, onClick: (e) => {
41
- onOptionClickDebounced();
26
+ const { size, multiple, getItemProps } = context;
27
+ const props = getItemProps();
28
+ return (jsxRuntime.jsxs("button", { ref: ref, id: id, role: 'option', type: 'button', "aria-selected": !!selected, "aria-labelledby": labelId, tabIndex: -1, onClick: (e) => {
29
+ onOptionClick();
42
30
  rest.onClick?.(e);
43
- }, onMouseEnter: (e) => {
44
- setActiveOption(index, labelId);
45
- rest.onMouseEnter?.(e);
46
- }, onFocus: (e) => {
47
- setActiveOption(index, labelId);
48
- rest.onFocus?.(e);
49
- }, className: clsx.clsx(Option_module.option, Option_module[size], activeIndex === index && Option_module.active, multiple && Option_module.multiple, className), ref: combinedRef, ...objectUtils.omit(['displayValue'], rest), children: [jsxRuntime.jsx(Label.Label, { asChild: true, size: size, children: jsxRuntime.jsx("span", { children: jsxRuntime.jsx(SelectedIcon.SelectedIcon, { multiple: multiple, selected: !!selected }) }) }), jsxRuntime.jsxs(Label.Label, { className: Option_module.optionText, size: size, id: labelId, children: [children, description && (jsxRuntime.jsx(Description.default, { children: description }))] })] }));
50
- });
31
+ }, className: clsx.clsx(Option_module.option, Option_module[size], active && Option_module.active, multiple && Option_module.multiple, className), ...objectUtils.omit(['displayValue'], rest), ...objectUtils.omit(['onClick', 'onPointerLeave'], props), children: [jsxRuntime.jsx(Label.Label, { asChild: true, size: size, children: jsxRuntime.jsx("span", { children: jsxRuntime.jsx(SelectedIcon.SelectedIcon, { multiple: multiple, selected: !!selected }) }) }), jsxRuntime.jsxs(Label.Label, { className: Option_module.optionText, size: size, id: labelId, children: [children, description && (jsxRuntime.jsx(Description.default, { children: description }))] })] }));
32
+ }));
51
33
  ComboboxOption.displayName = 'ComboboxOption';
52
34
 
53
35
  exports.ComboboxOption = ComboboxOption;
@@ -0,0 +1,47 @@
1
+ 'use client';
2
+ 'use strict';
3
+
4
+ var React = require('react');
5
+ var floatingUi_react = require('../../../../packages/react/node_modules/@floating-ui/react/dist/floating-ui.react.js');
6
+ var ComboboxContext = require('../ComboboxContext.js');
7
+ var useDebounce = require('../../../../utilities/useDebounce.js');
8
+ var ComboboxIdContext = require('../ComboboxIdContext.js');
9
+
10
+ function useComboboxOption({ id, ref, value, }) {
11
+ const generatedId = React.useId();
12
+ const newId = id || generatedId;
13
+ const context = React.useContext(ComboboxContext.ComboboxContext);
14
+ const { activeIndex } = ComboboxIdContext.useComboboxId();
15
+ const dispatch = ComboboxIdContext.useComboboxIdDispatch();
16
+ if (!context) {
17
+ throw new Error('ComboboxOption must be used within a Combobox');
18
+ }
19
+ const { selectedOptions, onOptionClick, listRef, customIds, filteredOptions, } = context;
20
+ const index = React.useMemo(() => filteredOptions.indexOf(value) + customIds.length, [customIds.length, filteredOptions, value]);
21
+ const combinedRef = floatingUi_react.useMergeRefs([
22
+ (node) => {
23
+ listRef.current[index] = node;
24
+ },
25
+ ref,
26
+ ]);
27
+ if (index === -1) {
28
+ throw new Error('Internal error: ComboboxOption did not find index');
29
+ }
30
+ const selected = selectedOptions[value];
31
+ const active = activeIndex === index;
32
+ React.useEffect(() => {
33
+ if (active) {
34
+ dispatch?.({ type: 'SET_ACTIVE_INDEX', payload: index });
35
+ }
36
+ }, [generatedId, id, dispatch, active, index]);
37
+ const onOptionClickDebounced = useDebounce(() => onOptionClick(value), 50);
38
+ return {
39
+ id: newId,
40
+ ref: combinedRef,
41
+ selected,
42
+ active,
43
+ onOptionClick: onOptionClickDebounced,
44
+ };
45
+ }
46
+
47
+ module.exports = useComboboxOption;
@@ -6,16 +6,16 @@ Object.defineProperty(exports, '__esModule', { value: true });
6
6
  var jsxRuntime = require('react/jsx-runtime');
7
7
  var React = require('react');
8
8
  require('../../../Chip/index.js');
9
- var Combobox = require('../Combobox.js');
9
+ var ComboboxContext = require('../ComboboxContext.js');
10
10
  var Removable = require('../../../Chip/Removable/Removable.js');
11
11
 
12
12
  const ComboboxChips = () => {
13
- const context = React.useContext(Combobox.ComboboxContext);
13
+ const context = React.useContext(ComboboxContext.ComboboxContext);
14
14
  if (!context) {
15
15
  throw new Error('ComboboxContext is missing');
16
16
  }
17
17
  const { size, readOnly, disabled, selectedOptions, setSelectedOptions, chipSrLabel, inputRef, } = context;
18
- return (jsxRuntime.jsx(jsxRuntime.Fragment, { children: selectedOptions.map((option) => {
18
+ return (jsxRuntime.jsx(jsxRuntime.Fragment, { children: Object.keys(selectedOptions).map((value) => {
19
19
  return (jsxRuntime.jsx(Removable.RemovableChip, { size: size, disabled: disabled, onKeyDown: (e) => {
20
20
  if (readOnly)
21
21
  return;
@@ -23,7 +23,11 @@ const ComboboxChips = () => {
23
23
  return;
24
24
  if (e.key === 'Enter') {
25
25
  e.stopPropagation();
26
- setSelectedOptions(selectedOptions.filter((i) => i.value !== option.value));
26
+ setSelectedOptions((prev) => {
27
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
28
+ const { [value]: _, ...rest } = prev;
29
+ return rest;
30
+ });
27
31
  inputRef.current?.focus();
28
32
  }
29
33
  }, onClick: () => {
@@ -32,11 +36,15 @@ const ComboboxChips = () => {
32
36
  if (disabled)
33
37
  return;
34
38
  /* If we click a chip, filter the active values and remove the one we clicked */
35
- setSelectedOptions(selectedOptions.filter((i) => i.value !== option.value));
39
+ setSelectedOptions((prev) => {
40
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
41
+ const { [value]: _, ...rest } = prev;
42
+ return rest;
43
+ });
36
44
  }, style: {
37
45
  /* We already set the opacity on Combobox */
38
46
  opacity: 1,
39
- }, "aria-label": chipSrLabel(option), children: option.label }, option.value));
47
+ }, "aria-label": chipSrLabel(selectedOptions[value]), children: selectedOptions[value].label }, value));
40
48
  }) }));
41
49
  };
42
50
  ComboboxChips.displayName = 'ComboboxChips';
@@ -7,11 +7,11 @@ var jsxRuntime = require('react/jsx-runtime');
7
7
  var React = require('react');
8
8
  var akselIcons = require('@navikt/aksel-icons');
9
9
  var clsx = require('../../../../node_modules/clsx/dist/clsx.js');
10
- var Combobox = require('../Combobox.js');
10
+ var ComboboxContext = require('../ComboboxContext.js');
11
11
  var Combobox_module = require('../Combobox.module.css.js');
12
12
 
13
13
  const ComboboxClearButton = () => {
14
- const context = React.useContext(Combobox.ComboboxContext);
14
+ const context = React.useContext(ComboboxContext.ComboboxContext);
15
15
  if (!context) {
16
16
  throw new Error('ComboboxContext is missing');
17
17
  }
@@ -21,7 +21,7 @@ const ComboboxClearButton = () => {
21
21
  return;
22
22
  if (disabled)
23
23
  return;
24
- setSelectedOptions([]);
24
+ setSelectedOptions({});
25
25
  setInputValue('');
26
26
  }, onKeyDown: (e) => {
27
27
  if (readOnly)
@@ -30,7 +30,7 @@ const ComboboxClearButton = () => {
30
30
  return;
31
31
  if (e.key === 'Enter') {
32
32
  e.stopPropagation();
33
- setSelectedOptions([]);
33
+ setSelectedOptions({});
34
34
  setInputValue('');
35
35
  inputRef.current?.focus();
36
36
  }
@@ -7,21 +7,26 @@ var jsxRuntime = require('react/jsx-runtime');
7
7
  var React = require('react');
8
8
  var clsx = require('../../../../node_modules/clsx/dist/clsx.js');
9
9
  var akselIcons = require('@navikt/aksel-icons');
10
- var floatingUi_react = require('../../../../node_modules/@floating-ui/react/dist/floating-ui.react.js');
11
- var Combobox = require('../Combobox.js');
10
+ var floatingUi_react = require('../../../../packages/react/node_modules/@floating-ui/react/dist/floating-ui.react.js');
11
+ var ComboboxContext = require('../ComboboxContext.js');
12
12
  var Combobox_module = require('../Combobox.module.css.js');
13
13
  var Textfield_module = require('../../Textfield/Textfield.module.css.js');
14
+ var ComboboxIdContext = require('../ComboboxIdContext.js');
14
15
  var ComboboxChips = require('./ComboboxChips.js');
15
16
  var ComboboxClearButton = require('./ComboboxClearButton.js');
16
17
  var Box = require('../../../Box/Box.js');
17
18
  var objectUtils = require('../../../../utilities/objectUtils.js');
18
19
 
19
20
  const ComboboxInput = ({ ...rest }) => {
20
- const context = React.useContext(Combobox.ComboboxContext);
21
+ const context = React.useContext(ComboboxContext.ComboboxContext);
22
+ const idDispatch = ComboboxIdContext.useComboboxIdDispatch();
21
23
  if (!context) {
22
24
  throw new Error('ComboboxContext is missing');
23
25
  }
24
- const { forwareddRef, listId, size, readOnly, disabled, open, inputRef, refs, inputValue, activeDescendant, error, multiple, selectedOptions, formFieldProps, htmlSize, options, hideChips, hideClearButton, setOpen, setActiveIndex, handleKeyDown, getReferenceProps, setInputValue, handleSelectOption, } = context;
26
+ const setActiveIndex = (id) => {
27
+ idDispatch?.({ type: 'SET_ACTIVE_INDEX', payload: id });
28
+ };
29
+ const { forwareddRef, listId, size, readOnly, disabled, open, inputRef, refs, inputValue, error, multiple, selectedOptions, formFieldProps, htmlSize, options, hideChips, hideClearButton, setOpen, handleKeyDown, getReferenceProps, setInputValue, handleSelectOption, } = context;
25
30
  const mergedRefs = floatingUi_react.useMergeRefs([forwareddRef, inputRef]);
26
31
  // we need to check if input is in focus, to add focus styles to the wrapper
27
32
  const [inputInFocus, setInputInFocus] = React.useState(false);
@@ -52,10 +57,10 @@ const ComboboxInput = ({ ...rest }) => {
52
57
  setOpen(false);
53
58
  }
54
59
  // check if input value is the same as a label, if so, select it
55
- const option = options.find((option) => option.label === value);
60
+ const option = options[value];
56
61
  if (!option)
57
62
  return;
58
- if (selectedOptions.find((selectedOption) => selectedOption.value === option.value))
63
+ if (selectedOptions[option.value])
59
64
  return;
60
65
  handleSelectOption(option);
61
66
  if (multiple) {
@@ -68,36 +73,36 @@ const ComboboxInput = ({ ...rest }) => {
68
73
  }, 0);
69
74
  }
70
75
  };
71
- const showClearButton = multiple && !hideClearButton && selectedOptions.length > 0;
72
- return (jsxRuntime.jsxs(Box.Box
76
+ const showClearButton = multiple && !hideClearButton && Object.keys(selectedOptions).length > 0;
73
77
  /* Props from floating-ui */
74
- , { ...getReferenceProps({
75
- ref: refs?.setReference,
76
- role: null,
77
- 'aria-controls': null,
78
- 'aria-expanded': null,
79
- 'aria-haspopup': null,
80
- /* If we click the wrapper, open the list, set index to first option, and focus the input */
81
- onClick() {
82
- if (disabled)
83
- return;
84
- if (readOnly)
85
- return;
86
- setOpen(true);
87
- setActiveIndex(0);
88
- inputRef.current?.focus();
89
- },
90
- /* Handles list navigation */
91
- onKeyDown(event) {
92
- handleKeyDown(event);
93
- },
94
- // preventDefault on keydown to avoid sending in form
95
- onKeyPress(event) {
96
- if (event.key === 'Enter') {
97
- event.preventDefault();
98
- }
99
- },
100
- }), "aria-disabled": disabled, className: clsx.clsx(Textfield_module.input, Combobox_module.inputWrapper, Combobox_module[size], inputInFocus && Combobox_module.inFocus, readOnly && Combobox_module.readonly, error && Combobox_module.error), children: [jsxRuntime.jsxs("div", { className: Combobox_module.chipAndInput, children: [multiple && !hideChips && jsxRuntime.jsx(ComboboxChips.default, {}), jsxRuntime.jsx("input", { ref: mergedRefs, "aria-activedescendant": activeDescendant, readOnly: readOnly, "aria-autocomplete": 'list', role: 'combobox', "aria-expanded": open, "aria-controls": listId, autoComplete: 'off', size: htmlSize, value: inputValue, ...objectUtils.omit(['style', 'className'], rest), ...formFieldProps.inputProps, onChange: (e) => {
78
+ const props = getReferenceProps({
79
+ ref: refs?.setReference,
80
+ role: null,
81
+ 'aria-controls': null,
82
+ 'aria-expanded': null,
83
+ 'aria-haspopup': null,
84
+ /* If we click the wrapper, open the list, set index to first option, and focus the input */
85
+ onClick() {
86
+ if (disabled)
87
+ return;
88
+ if (readOnly)
89
+ return;
90
+ setOpen(true);
91
+ setActiveIndex(0);
92
+ inputRef.current?.focus();
93
+ },
94
+ /* Handles list navigation */
95
+ onKeyDown(event) {
96
+ handleKeyDown(event);
97
+ },
98
+ // preventDefault on keydown to avoid sending in form
99
+ onKeyPress(event) {
100
+ if (event.key === 'Enter') {
101
+ event.preventDefault();
102
+ }
103
+ },
104
+ });
105
+ return (jsxRuntime.jsxs(Box.Box, { ...props, "aria-disabled": disabled, className: clsx.clsx(Textfield_module.input, Combobox_module.inputWrapper, Combobox_module[size], inputInFocus && Combobox_module.inFocus, readOnly && Combobox_module.readonly, error && Combobox_module.error), children: [jsxRuntime.jsxs("div", { className: Combobox_module.chipAndInput, children: [multiple && !hideChips && jsxRuntime.jsx(ComboboxChips.default, {}), jsxRuntime.jsx("input", { ref: mergedRefs, "aria-activedescendant": props['aria-activedescendant'], readOnly: readOnly, "aria-autocomplete": 'list', role: 'combobox', "aria-expanded": open, "aria-controls": listId, autoComplete: 'off', size: htmlSize, value: inputValue, ...objectUtils.omit(['style', 'className'], rest), ...formFieldProps.inputProps, onChange: (e) => {
101
106
  onChange(e);
102
107
  rest.onChange && rest.onChange(e);
103
108
  } })] }), showClearButton && jsxRuntime.jsx(ComboboxClearButton.default, {}), jsxRuntime.jsx("div", { className: Combobox_module.arrow, children: open ? (jsxRuntime.jsx(akselIcons.ChevronUpIcon, { title: 'arrow up', fontSize: '1.5em' })) : (jsxRuntime.jsx(akselIcons.ChevronDownIcon, { title: 'arrow down', fontSize: '1.5em' })) })] }));
@@ -7,8 +7,8 @@ var jsxRuntime = require('react/jsx-runtime');
7
7
 
8
8
  const ComboboxNative = ({ selectedOptions, multiple, name, }) => {
9
9
  return (jsxRuntime.jsx("select", { name: name, multiple: multiple, style: { display: 'none' }, value: multiple
10
- ? selectedOptions.map((option) => option.value)
11
- : selectedOptions[0]?.value, onChange: () => { }, children: selectedOptions.map((option) => (jsxRuntime.jsx("option", { value: option.value }, option.value))) }));
10
+ ? Object.keys(selectedOptions)
11
+ : Object.keys(selectedOptions)[0], onChange: () => { }, children: Object.keys(selectedOptions).map((value) => (jsxRuntime.jsx("option", { value: value }, value))) }));
12
12
  };
13
13
  ComboboxNative.displayName = 'ComboboxNative';
14
14
  var ComboboxNative$1 = ComboboxNative;
@@ -17,9 +17,11 @@ function isComboboxCustom(child) {
17
17
  function isInteractiveComboboxCustom(child) {
18
18
  return isComboboxCustom(child) && child.props.interactive === true;
19
19
  }
20
- function useCombobox({ children, inputValue, multiple, filter, initialValue, }) {
20
+ function useCombobox({ children, inputValue, multiple, filter = (inputValue, option) => {
21
+ return option.label.toLowerCase().startsWith(inputValue.toLowerCase());
22
+ }, initialValue, }) {
21
23
  const options = React.useMemo(() => {
22
- const allOptions = [];
24
+ const allOptions = {};
23
25
  React.Children.forEach(children, (child) => {
24
26
  if (isComboboxOption(child)) {
25
27
  const props = child.props;
@@ -37,51 +39,57 @@ function useCombobox({ children, inputValue, multiple, filter, initialValue, })
37
39
  });
38
40
  label = childrenLabel;
39
41
  }
40
- allOptions.push({
42
+ allOptions[props.value] = {
41
43
  value: props.value,
42
44
  label,
43
45
  displayValue: props.displayValue,
44
46
  description: props.description,
45
- });
47
+ };
46
48
  }
47
49
  });
48
50
  return allOptions;
49
51
  }, [children]);
50
- const preSelectedOptions = (initialValue || [])
51
- .map((value) => options.find((option) => option.value === value))
52
- .filter(isOption);
52
+ const preSelectedOptions = React.useMemo(() => (initialValue || []).reduce((acc, value) => {
53
+ const option = options[value];
54
+ if (isOption(option)) {
55
+ acc[value] = option;
56
+ }
57
+ return acc;
58
+ }, {}), [initialValue, options]);
53
59
  const [selectedOptions, setSelectedOptions] = React.useState(preSelectedOptions);
54
60
  const [prevSelectedHash, setPrevSelectedHash] = React.useState(JSON.stringify(selectedOptions));
55
- const optionsChildren = React.useMemo(() => {
56
- const valuesArray = Array.from(options);
57
- const children_ = React.Children.toArray(children).filter((child) => isComboboxOption(child));
58
- const activeValue = valuesArray.find((item) => item.label === inputValue);
59
- if (activeValue && !multiple)
60
- return children_;
61
- if (inputValue === '' && !multiple)
62
- return children_;
63
- return children_.filter((child) => {
64
- const { value } = child.props;
65
- const option = valuesArray.find((item) => item.value === value);
66
- if (!option)
67
- return false;
68
- const isSelected = selectedOptions.some((selectedOption) => selectedOption.value === value);
69
- // show what we search for, and all selected options
70
- return filter(inputValue, { ...option }) || isSelected;
71
- });
72
- }, [options, children, multiple, inputValue, selectedOptions, filter]);
73
- const customIds = React.useMemo(() => {
61
+ const { optionsChildren, customIds } = React.useMemo(() => {
62
+ const allChildren = React.Children.toArray(children);
63
+ const optionsChildren = allChildren.filter((child) => isComboboxOption(child));
74
64
  // find all custom components with `interactive=true` and generate random values for them
75
- const children_ = React.Children.toArray(children).filter((child) => {
65
+ const customChildren = allChildren.filter((child) => {
76
66
  return isInteractiveComboboxCustom(child);
77
67
  });
78
68
  // return all ids
79
- return children_.map((child) => {
69
+ const customIds = customChildren.map((child) => {
80
70
  if (!child.props.id)
81
71
  throw new Error('If ComboboxCustom is interactive, it must have an id');
82
72
  return child.props.id;
83
73
  });
74
+ return { optionsChildren, customIds };
84
75
  }, [children]);
76
+ const { filteredOptions, filteredOptionsChildren } = React.useMemo(() => {
77
+ const filteredOptions = [];
78
+ const filteredOptionsChildren = Object.keys(options)
79
+ .map((option, index) => {
80
+ if (multiple && selectedOptions[option]) {
81
+ filteredOptions.push(options[option].value);
82
+ return optionsChildren[index];
83
+ }
84
+ if (filter(inputValue, options[option])) {
85
+ filteredOptions.push(options[option].value);
86
+ return optionsChildren[index];
87
+ }
88
+ })
89
+ .filter((child) => child);
90
+ return { filteredOptions, filteredOptionsChildren };
91
+ // eslint-disable-next-line react-hooks/exhaustive-deps
92
+ }, [inputValue, multiple, options, optionsChildren, selectedOptions]);
85
93
  const optionValues = React.useMemo(() => {
86
94
  // create an index map of values from optionsChildren
87
95
  const options = optionsChildren.map((child) => {
@@ -90,20 +98,26 @@ function useCombobox({ children, inputValue, multiple, filter, initialValue, })
90
98
  });
91
99
  return [...customIds, ...options];
92
100
  }, [customIds, optionsChildren]);
93
- const restChildren = React.useMemo(() => {
94
- return React.Children.toArray(children).filter((child) => {
101
+ const { restChildren, interactiveChildren } = React.useMemo(() => {
102
+ const restChildren = React.Children.toArray(children).filter((child) => {
95
103
  return !isComboboxOption(child);
96
104
  });
105
+ const interactiveChildren = restChildren.filter((child) => {
106
+ return isInteractiveComboboxCustom(child);
107
+ });
108
+ return { restChildren, interactiveChildren };
97
109
  }, [children]);
98
110
  return {
99
- optionsChildren,
111
+ filteredOptionsChildren,
112
+ filteredOptions,
100
113
  optionValues,
101
114
  restChildren,
102
115
  options,
103
116
  customIds,
104
117
  selectedOptions,
105
- setSelectedOptions,
106
118
  prevSelectedHash,
119
+ interactiveChildren,
120
+ setSelectedOptions,
107
121
  setPrevSelectedHash,
108
122
  };
109
123
  }
@@ -0,0 +1,79 @@
1
+ 'use client';
2
+ 'use strict';
3
+
4
+ var useDebounce = require('../../../utilities/useDebounce.js');
5
+ var ComboboxIdContext = require('./ComboboxIdContext.js');
6
+
7
+ const useComboboxKeyboard = ({ readOnly, disabled, interactiveChildren, filteredOptions, inputValue, selectedOptions, multiple, open, options, setOpen, setInputValue, setSelectedOptions, handleSelectOption, }) => {
8
+ const { activeIndex } = ComboboxIdContext.useComboboxId();
9
+ // handle keyboard navigation in the list
10
+ const handleKeyDownFunc = (event) => {
11
+ if (readOnly || disabled)
12
+ return;
13
+ if (!event)
14
+ return;
15
+ switch (event.key) {
16
+ case 'ArrowDown':
17
+ event.preventDefault();
18
+ if (!open) {
19
+ setOpen(true);
20
+ }
21
+ break;
22
+ case 'ArrowUp':
23
+ event.preventDefault();
24
+ /* If we are on the first item, close */
25
+ if (activeIndex === 0) {
26
+ setOpen(false);
27
+ }
28
+ break;
29
+ case 'Enter':
30
+ event.preventDefault();
31
+ // ignore if it is closed
32
+ if (!open)
33
+ break;
34
+ // check if we are in the custom components
35
+ if (activeIndex <= interactiveChildren.length - 1) {
36
+ const selectedComponent = interactiveChildren[activeIndex];
37
+ if (selectedComponent.props.onSelect) {
38
+ selectedComponent?.props.onSelect();
39
+ return;
40
+ }
41
+ }
42
+ // if we are in the options, find the actual index
43
+ // eslint-disable-next-line no-case-declarations
44
+ const valueIndex = activeIndex - interactiveChildren.length;
45
+ // eslint-disable-next-line no-case-declarations
46
+ const option = filteredOptions[valueIndex];
47
+ if (!multiple) {
48
+ // check if option is already selected, if so, deselect it
49
+ if (selectedOptions[option]) {
50
+ setSelectedOptions({});
51
+ setInputValue('');
52
+ return;
53
+ }
54
+ }
55
+ handleSelectOption(options[option]);
56
+ break;
57
+ case 'Backspace':
58
+ if (inputValue === '' &&
59
+ multiple &&
60
+ Object.keys(selectedOptions).length >= 0) {
61
+ setSelectedOptions((prev) => {
62
+ const updated = { ...prev };
63
+ const keys = Object.keys(updated);
64
+ delete updated[keys[keys.length - 1]];
65
+ return updated;
66
+ });
67
+ }
68
+ // if we are in single mode, we need to set activeValue to null
69
+ if (!multiple) {
70
+ setSelectedOptions({});
71
+ }
72
+ break;
73
+ }
74
+ };
75
+ const handleKeyDown = useDebounce(handleKeyDownFunc, 20);
76
+ return handleKeyDown;
77
+ };
78
+
79
+ exports.useComboboxKeyboard = useComboboxKeyboard;
@@ -0,0 +1,78 @@
1
+ 'use client';
2
+ 'use strict';
3
+
4
+ var floatingUi_react = require('../../../packages/react/node_modules/@floating-ui/react/dist/floating-ui.react.js');
5
+ var React = require('react');
6
+ var ReactDOM = require('react-dom');
7
+ var ComboboxIdContext = require('./ComboboxIdContext.js');
8
+ var floatingUi_dom = require('../../../packages/react/node_modules/@floating-ui/dom/dist/floating-ui.dom.js');
9
+ var floatingUi_core = require('../../../packages/react/node_modules/@floating-ui/core/dist/floating-ui.core.js');
10
+
11
+ const useFloatingCombobox = ({ listRef }) => {
12
+ const [open, setOpen] = React.useState(false);
13
+ const { activeIndex } = ComboboxIdContext.useComboboxId();
14
+ const dispatch = ComboboxIdContext.useComboboxIdDispatch();
15
+ // floating UI
16
+ const { refs, floatingStyles, context } = floatingUi_react.useFloating({
17
+ open,
18
+ onOpenChange: (newOpen) => {
19
+ if (!newOpen)
20
+ dispatch?.({ type: 'SET_ACTIVE_INDEX', payload: 0 });
21
+ ReactDOM.flushSync(() => {
22
+ if (refs.floating.current && !newOpen) {
23
+ refs.floating.current.scrollTop = 0;
24
+ }
25
+ setTimeout(() => {
26
+ setOpen(newOpen);
27
+ }, 1);
28
+ });
29
+ },
30
+ whileElementsMounted: (reference, floating, update) => {
31
+ floatingUi_dom.autoUpdate(reference, floating, update);
32
+ return () => {
33
+ floating.scrollTop = 0;
34
+ };
35
+ },
36
+ middleware: [
37
+ floatingUi_dom.flip({ padding: 10 }),
38
+ floatingUi_dom.size({
39
+ apply({ rects, elements }) {
40
+ requestAnimationFrame(() => {
41
+ Object.assign(elements.floating.style, {
42
+ width: `calc(${rects.reference.width}px - calc(var(--fds-spacing-2) * 2))`,
43
+ maxHeight: `200px`,
44
+ });
45
+ });
46
+ },
47
+ }),
48
+ floatingUi_core.offset(10),
49
+ ],
50
+ });
51
+ const role = floatingUi_react.useRole(context, { role: 'listbox' });
52
+ const dismiss = floatingUi_react.useDismiss(context);
53
+ const listNav = floatingUi_react.useListNavigation(context, {
54
+ listRef,
55
+ activeIndex,
56
+ virtual: true,
57
+ scrollItemIntoView: true,
58
+ enabled: open,
59
+ focusItemOnHover: true,
60
+ onNavigate: (index) => {
61
+ dispatch?.({ type: 'SET_ACTIVE_INDEX', payload: index || 0 });
62
+ },
63
+ });
64
+ const { getReferenceProps, getFloatingProps, getItemProps } = floatingUi_react.useInteractions([role, dismiss, listNav]);
65
+ return {
66
+ open,
67
+ setOpen,
68
+ activeIndex,
69
+ refs,
70
+ floatingStyles,
71
+ context,
72
+ getReferenceProps,
73
+ getFloatingProps,
74
+ getItemProps,
75
+ };
76
+ };
77
+
78
+ exports.useFloatingCombobox = useFloatingCombobox;
@@ -5,7 +5,7 @@ var jsxRuntime = require('react/jsx-runtime');
5
5
  var React = require('react');
6
6
  var clsx = require('../../../node_modules/clsx/dist/clsx.js');
7
7
  var akselIcons = require('@navikt/aksel-icons');
8
- var floatingUi_react = require('../../../node_modules/@floating-ui/react/dist/floating-ui.react.js');
8
+ var floatingUi_react = require('../../../packages/react/node_modules/@floating-ui/react/dist/floating-ui.react.js');
9
9
  var useSearch = require('./useSearch.js');
10
10
  var Search_module = require('./Search.module.css.js');
11
11
  var Button = require('../../Button/Button.js');