@digdir/designsystemet-react 0.58.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 (107) hide show
  1. package/dist/cjs/components/Alert/Alert.js +2 -2
  2. package/dist/cjs/components/Button/Button.js +1 -2
  3. package/dist/cjs/components/DropdownMenu/DropdownMenuContent.js +4 -4
  4. package/dist/cjs/components/DropdownMenu/DropdownMenuTrigger.js +1 -1
  5. package/dist/cjs/components/Modal/ModalDialog.js +1 -1
  6. package/dist/cjs/components/Popover/PopoverContent.js +6 -6
  7. package/dist/cjs/components/Popover/PopoverTrigger.js +1 -1
  8. package/dist/cjs/components/SkipLink/SkipLink.js +1 -2
  9. package/dist/cjs/components/Tooltip/Tooltip.js +6 -6
  10. package/dist/cjs/components/form/Checkbox/Checkbox.js +1 -1
  11. package/dist/cjs/components/form/Combobox/Combobox.js +60 -177
  12. package/dist/cjs/components/form/Combobox/ComboboxContext.js +8 -0
  13. package/dist/cjs/components/form/Combobox/ComboboxIdContext.js +42 -0
  14. package/dist/cjs/components/form/Combobox/Custom/Custom.js +14 -9
  15. package/dist/cjs/components/form/Combobox/Empty/Empty.js +4 -4
  16. package/dist/cjs/components/form/Combobox/Option/Option.js +15 -33
  17. package/dist/cjs/components/form/Combobox/Option/useComboboxOption.js +47 -0
  18. package/dist/cjs/components/form/Combobox/internal/ComboboxChips.js +14 -6
  19. package/dist/cjs/components/form/Combobox/internal/ComboboxClearButton.js +4 -4
  20. package/dist/cjs/components/form/Combobox/internal/ComboboxInput.js +40 -35
  21. package/dist/cjs/components/form/Combobox/internal/ComboboxNative.js +2 -2
  22. package/dist/cjs/components/form/Combobox/useCombobox.js +46 -32
  23. package/dist/cjs/components/form/Combobox/useComboboxKeyboard.js +79 -0
  24. package/dist/cjs/components/form/Combobox/useFloatingCombobox.js +78 -0
  25. package/dist/cjs/components/form/Search/Search.js +1 -1
  26. package/dist/cjs/node_modules/@floating-ui/utils/{dom/dist → dist}/floating-ui.utils.dom.js +7 -4
  27. package/dist/cjs/node_modules/@floating-ui/utils/dist/floating-ui.utils.js +5 -0
  28. package/dist/cjs/{node_modules → packages/react/node_modules}/@floating-ui/core/dist/floating-ui.core.js +40 -16
  29. package/dist/cjs/{node_modules → packages/react/node_modules}/@floating-ui/dom/dist/floating-ui.dom.js +83 -31
  30. package/dist/cjs/{node_modules → packages/react/node_modules}/@floating-ui/react/dist/floating-ui.react.js +307 -157
  31. package/dist/cjs/{node_modules/@floating-ui/react/utils → packages/react/node_modules/@floating-ui/react}/dist/floating-ui.react.utils.js +9 -4
  32. package/dist/cjs/{node_modules → packages/react/node_modules}/@floating-ui/react-dom/dist/floating-ui.react-dom.js +22 -18
  33. package/dist/{esm → cjs/packages/react}/node_modules/tabbable/dist/index.esm.js +59 -13
  34. package/dist/cjs/react-css-modules.css +0 -315
  35. package/dist/cjs/utilities/RovingTabIndex/RovingTabindexItem.js +1 -1
  36. package/dist/cjs/utilities/RovingTabIndex/RovingTabindexRoot.js +1 -1
  37. package/dist/esm/components/Alert/Alert.js +2 -2
  38. package/dist/esm/components/Button/Button.js +1 -2
  39. package/dist/esm/components/DropdownMenu/DropdownMenuContent.js +3 -3
  40. package/dist/esm/components/DropdownMenu/DropdownMenuTrigger.js +1 -1
  41. package/dist/esm/components/Modal/ModalDialog.js +1 -1
  42. package/dist/esm/components/Popover/PopoverContent.js +4 -4
  43. package/dist/esm/components/Popover/PopoverTrigger.js +1 -1
  44. package/dist/esm/components/SkipLink/SkipLink.js +1 -2
  45. package/dist/esm/components/Tooltip/Tooltip.js +4 -4
  46. package/dist/esm/components/form/Checkbox/Checkbox.js +1 -1
  47. package/dist/esm/components/form/Combobox/Combobox.js +65 -182
  48. package/dist/esm/components/form/Combobox/ComboboxContext.js +6 -0
  49. package/dist/esm/components/form/Combobox/ComboboxIdContext.js +35 -0
  50. package/dist/esm/components/form/Combobox/Custom/Custom.js +13 -8
  51. package/dist/esm/components/form/Combobox/Empty/Empty.js +3 -3
  52. package/dist/esm/components/form/Combobox/Option/Option.js +15 -33
  53. package/dist/esm/components/form/Combobox/Option/useComboboxOption.js +45 -0
  54. package/dist/esm/components/form/Combobox/internal/ComboboxChips.js +13 -5
  55. package/dist/esm/components/form/Combobox/internal/ComboboxClearButton.js +3 -3
  56. package/dist/esm/components/form/Combobox/internal/ComboboxInput.js +39 -34
  57. package/dist/esm/components/form/Combobox/internal/ComboboxNative.js +2 -2
  58. package/dist/esm/components/form/Combobox/useCombobox.js +46 -32
  59. package/dist/esm/components/form/Combobox/useComboboxKeyboard.js +77 -0
  60. package/dist/esm/components/form/Combobox/useFloatingCombobox.js +76 -0
  61. package/dist/esm/components/form/Search/Search.js +1 -1
  62. package/dist/esm/node_modules/@floating-ui/utils/{dom/dist → dist}/floating-ui.utils.dom.js +7 -4
  63. package/dist/esm/node_modules/@floating-ui/utils/dist/floating-ui.utils.js +5 -0
  64. package/dist/esm/{node_modules → packages/react/node_modules}/@floating-ui/core/dist/floating-ui.core.js +40 -16
  65. package/dist/esm/{node_modules → packages/react/node_modules}/@floating-ui/dom/dist/floating-ui.dom.js +82 -30
  66. package/dist/esm/{node_modules → packages/react/node_modules}/@floating-ui/react/dist/floating-ui.react.js +282 -135
  67. package/dist/esm/{node_modules/@floating-ui/react/utils → packages/react/node_modules/@floating-ui/react}/dist/floating-ui.react.utils.js +9 -5
  68. package/dist/esm/{node_modules → packages/react/node_modules}/@floating-ui/react-dom/dist/floating-ui.react-dom.js +19 -14
  69. package/dist/{cjs → esm/packages/react}/node_modules/tabbable/dist/index.esm.js +55 -15
  70. package/dist/esm/react-css-modules.css +0 -315
  71. package/dist/esm/utilities/RovingTabIndex/RovingTabindexItem.js +1 -1
  72. package/dist/esm/utilities/RovingTabIndex/RovingTabindexRoot.js +1 -1
  73. package/dist/types/components/Alert/Alert.d.ts +12 -0
  74. package/dist/types/components/Alert/Alert.d.ts.map +1 -1
  75. package/dist/types/components/Button/Button.d.ts.map +1 -1
  76. package/dist/types/components/SkipLink/SkipLink.d.ts.map +1 -1
  77. package/dist/types/components/form/Combobox/Combobox.d.ts +104 -39
  78. package/dist/types/components/form/Combobox/Combobox.d.ts.map +1 -1
  79. package/dist/types/components/form/Combobox/ComboboxContext.d.ts +48 -0
  80. package/dist/types/components/form/Combobox/ComboboxContext.d.ts.map +1 -0
  81. package/dist/types/components/form/Combobox/ComboboxIdContext.d.ts +19 -0
  82. package/dist/types/components/form/Combobox/ComboboxIdContext.d.ts.map +1 -0
  83. package/dist/types/components/form/Combobox/Custom/Custom.d.ts.map +1 -1
  84. package/dist/types/components/form/Combobox/Option/Option.d.ts +2 -2
  85. package/dist/types/components/form/Combobox/Option/Option.d.ts.map +1 -1
  86. package/dist/types/components/form/Combobox/Option/useComboboxOption.d.ts +14 -0
  87. package/dist/types/components/form/Combobox/Option/useComboboxOption.d.ts.map +1 -0
  88. package/dist/types/components/form/Combobox/internal/ComboboxChips.d.ts.map +1 -1
  89. package/dist/types/components/form/Combobox/internal/ComboboxInput.d.ts +0 -1
  90. package/dist/types/components/form/Combobox/internal/ComboboxInput.d.ts.map +1 -1
  91. package/dist/types/components/form/Combobox/internal/ComboboxNative.d.ts +3 -1
  92. package/dist/types/components/form/Combobox/internal/ComboboxNative.d.ts.map +1 -1
  93. package/dist/types/components/form/Combobox/useCombobox.d.ts +13 -5
  94. package/dist/types/components/form/Combobox/useCombobox.d.ts.map +1 -1
  95. package/dist/types/components/form/Combobox/useComboboxKeyboard.d.ts +20 -0
  96. package/dist/types/components/form/Combobox/useComboboxKeyboard.d.ts.map +1 -0
  97. package/dist/types/components/form/Combobox/useFloatingCombobox.d.ts +41 -0
  98. package/dist/types/components/form/Combobox/useFloatingCombobox.d.ts.map +1 -0
  99. package/package.json +3 -3
  100. package/dist/cjs/components/Button/Button.module.css.js +0 -6
  101. package/dist/cjs/components/SkipLink/SkipLink.module.css.js +0 -6
  102. package/dist/cjs/node_modules/@floating-ui/react/node_modules/@floating-ui/utils/dist/floating-ui.utils.js +0 -6
  103. package/dist/cjs/node_modules/@floating-ui/react/node_modules/@floating-ui/utils/dom/dist/floating-ui.utils.dom.js +0 -68
  104. package/dist/esm/components/Button/Button.module.css.js +0 -4
  105. package/dist/esm/components/SkipLink/SkipLink.module.css.js +0 -4
  106. package/dist/esm/node_modules/@floating-ui/react/node_modules/@floating-ui/utils/dist/floating-ui.utils.js +0 -4
  107. package/dist/esm/node_modules/@floating-ui/react/node_modules/@floating-ui/utils/dom/dist/floating-ui.utils.dom.js +0 -57
@@ -2,12 +2,12 @@
2
2
  import { jsx, Fragment } from 'react/jsx-runtime';
3
3
  import * as React from 'react';
4
4
  import { forwardRef, useContext, useRef } from 'react';
5
- import { useFloating, useInteractions, useFocus, useClick, useDismiss, useRole, useMergeRefs, FloatingFocusManager, FloatingPortal } from '../../node_modules/@floating-ui/react/dist/floating-ui.react.js';
5
+ import { useFloating, useInteractions, useFocus, useClick, useDismiss, useRole, useMergeRefs, FloatingFocusManager, FloatingPortal } from '../../packages/react/node_modules/@floating-ui/react/dist/floating-ui.react.js';
6
6
  import { clsx } from '../../node_modules/clsx/dist/clsx.js';
7
7
  import classes from './DropdownMenu.module.css.js';
8
8
  import { DropdownMenuContext } from './DropdownMenu.js';
9
- import { autoUpdate } from '../../node_modules/@floating-ui/dom/dist/floating-ui.dom.js';
10
- import { offset, shift } from '../../node_modules/@floating-ui/core/dist/floating-ui.core.js';
9
+ import { autoUpdate, shift } from '../../packages/react/node_modules/@floating-ui/dom/dist/floating-ui.dom.js';
10
+ import { offset } from '../../packages/react/node_modules/@floating-ui/core/dist/floating-ui.core.js';
11
11
  import { useIsomorphicLayoutEffect } from '../../hooks/useIsomorphicLayoutEffect.js';
12
12
 
13
13
  const GAP = 4;
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import { jsx } from 'react/jsx-runtime';
3
3
  import { forwardRef, useContext } from 'react';
4
- import { useMergeRefs } from '../../node_modules/@floating-ui/react/dist/floating-ui.react.js';
4
+ import { useMergeRefs } from '../../packages/react/node_modules/@floating-ui/react/dist/floating-ui.react.js';
5
5
  import { Slot as $5e63c961fc1ce211$export$8c6ed5c666ac1360 } from '../../node_modules/@radix-ui/react-slot/dist/index.js';
6
6
  import { DropdownMenuContext } from './DropdownMenu.js';
7
7
  import { Button } from '../Button/Button.js';
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
  import { jsx, Fragment } from 'react/jsx-runtime';
3
- import { useFloating, useMergeRefs, FloatingFocusManager } from '../../node_modules/@floating-ui/react/dist/floating-ui.react.js';
3
+ import { useFloating, useMergeRefs, FloatingFocusManager } from '../../packages/react/node_modules/@floating-ui/react/dist/floating-ui.react.js';
4
4
  import { forwardRef, useRef, useContext, useEffect } from 'react';
5
5
  import { Slot as $5e63c961fc1ce211$export$8c6ed5c666ac1360 } from '../../node_modules/@radix-ui/react-slot/dist/index.js';
6
6
  import { clsx } from '../../node_modules/clsx/dist/clsx.js';
@@ -2,13 +2,13 @@
2
2
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
3
3
  import * as React from 'react';
4
4
  import { forwardRef, useContext, useRef, useEffect, useMemo } from 'react';
5
- import { useFloating, useInteractions, useFocus, useClick, useDismiss, useRole, useMergeRefs, FloatingPortal } from '../../node_modules/@floating-ui/react/dist/floating-ui.react.js';
5
+ import { useFloating, useInteractions, useFocus, useClick, useDismiss, useRole, useMergeRefs, FloatingPortal } from '../../packages/react/node_modules/@floating-ui/react/dist/floating-ui.react.js';
6
6
  import { clsx } from '../../node_modules/clsx/dist/clsx.js';
7
7
  import { PopoverContext } from './Popover.js';
8
8
  import classes from './Popover.module.css.js';
9
- import { autoUpdate } from '../../node_modules/@floating-ui/dom/dist/floating-ui.dom.js';
10
- import { offset, flip, shift } from '../../node_modules/@floating-ui/core/dist/floating-ui.core.js';
11
- import { arrow } from '../../node_modules/@floating-ui/react-dom/dist/floating-ui.react-dom.js';
9
+ import { autoUpdate, flip, shift } from '../../packages/react/node_modules/@floating-ui/dom/dist/floating-ui.dom.js';
10
+ import { offset } from '../../packages/react/node_modules/@floating-ui/core/dist/floating-ui.core.js';
11
+ import { arrow } from '../../packages/react/node_modules/@floating-ui/react-dom/dist/floating-ui.react-dom.js';
12
12
  import { useIsomorphicLayoutEffect } from '../../hooks/useIsomorphicLayoutEffect.js';
13
13
  import { Paragraph } from '../Typography/Paragraph/Paragraph.js';
14
14
 
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import { jsx } from 'react/jsx-runtime';
3
3
  import { forwardRef, useContext, useEffect } from 'react';
4
- import { useMergeRefs } from '../../node_modules/@floating-ui/react/dist/floating-ui.react.js';
4
+ import { useMergeRefs } from '../../packages/react/node_modules/@floating-ui/react/dist/floating-ui.react.js';
5
5
  import { Slot as $5e63c961fc1ce211$export$8c6ed5c666ac1360 } from '../../node_modules/@radix-ui/react-slot/dist/index.js';
6
6
  import { PopoverContext } from './Popover.js';
7
7
  import { Button } from '../Button/Button.js';
@@ -1,10 +1,9 @@
1
1
  'use client';
2
2
  import { jsx } from 'react/jsx-runtime';
3
3
  import { clsx } from '../../node_modules/clsx/dist/clsx.js';
4
- import classes from './SkipLink.module.css.js';
5
4
 
6
5
  const SkipLink = ({ href, children, className, ...rest }) => {
7
- return (jsx("a", { href: href, className: clsx(`fds-sr-only`, classes.skiplink, className), ...rest, children: children }));
6
+ return (jsx("a", { href: href, className: clsx(`fds-sr-only`, 'fds-skiplink', className), ...rest, children: children }));
8
7
  };
9
8
  SkipLink.displayName = 'SkipLink';
10
9
 
@@ -3,11 +3,11 @@ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
3
3
  import * as React from 'react';
4
4
  import { forwardRef, useState, cloneElement } from 'react';
5
5
  import { clsx } from '../../node_modules/clsx/dist/clsx.js';
6
- import { useFloating, useTransitionStyles, useInteractions, useHover, useFocus, useDismiss, useRole, useMergeRefs, FloatingPortal, FloatingArrow } from '../../node_modules/@floating-ui/react/dist/floating-ui.react.js';
6
+ import { useFloating, useTransitionStyles, useInteractions, useHover, useFocus, useDismiss, useRole, useMergeRefs, FloatingPortal, FloatingArrow } from '../../packages/react/node_modules/@floating-ui/react/dist/floating-ui.react.js';
7
7
  import classes from './Tooltip.module.css.js';
8
- import { autoUpdate } from '../../node_modules/@floating-ui/dom/dist/floating-ui.dom.js';
9
- import { offset, flip, shift } from '../../node_modules/@floating-ui/core/dist/floating-ui.core.js';
10
- import { arrow } from '../../node_modules/@floating-ui/react-dom/dist/floating-ui.react-dom.js';
8
+ import { autoUpdate, flip, shift } from '../../packages/react/node_modules/@floating-ui/dom/dist/floating-ui.dom.js';
9
+ import { offset } from '../../packages/react/node_modules/@floating-ui/core/dist/floating-ui.core.js';
10
+ import { arrow } from '../../packages/react/node_modules/@floating-ui/react-dom/dist/floating-ui.react-dom.js';
11
11
 
12
12
  const ARROW_HEIGHT = 7;
13
13
  const ARROW_GAP = 4;
@@ -2,7 +2,7 @@
2
2
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
3
  import { forwardRef } from 'react';
4
4
  import { clsx } from '../../../node_modules/clsx/dist/clsx.js';
5
- import { useMergeRefs } from '../../../node_modules/@floating-ui/react/dist/floating-ui.react.js';
5
+ import { useMergeRefs } from '../../../packages/react/node_modules/@floating-ui/react/dist/floating-ui.react.js';
6
6
  import classes from './Checkbox.module.css.js';
7
7
  import { useCheckbox } from './useCheckbox.js';
8
8
  import { Paragraph } from '../../Typography/Paragraph/Paragraph.js';
@@ -1,150 +1,105 @@
1
1
  'use client';
2
2
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
3
- import { forwardRef, useRef, useId, useState, useEffect, createContext } from 'react';
4
- import { useFloating, useRole, useDismiss, useListNavigation, useInteractions, FloatingPortal, FloatingFocusManager } from '../../../node_modules/@floating-ui/react/dist/floating-ui.react.js';
3
+ import { forwardRef, useRef, useId, useState, useEffect } from 'react';
4
+ import { FloatingPortal, FloatingFocusManager } from '../../../packages/react/node_modules/@floating-ui/react/dist/floating-ui.react.js';
5
5
  import { clsx } from '../../../node_modules/clsx/dist/clsx.js';
6
6
  import { useVirtualizer } from '../../../node_modules/@tanstack/react-virtual/dist/esm/index.js';
7
- import { flushSync } from 'react-dom';
8
7
  import { useFormField } from '../useFormField.js';
9
8
  import useDebounce from '../../../utilities/useDebounce.js';
10
- import useCombobox, { isInteractiveComboboxCustom, isComboboxOption } from './useCombobox.js';
9
+ import useCombobox from './useCombobox.js';
11
10
  import classes from './Combobox.module.css.js';
12
11
  import ComboboxInput from './internal/ComboboxInput.js';
13
12
  import ComboboxLabel from './internal/ComboboxLabel.js';
14
13
  import ComboboxError from './internal/ComboboxError.js';
15
14
  import ComboboxNative from './internal/ComboboxNative.js';
16
15
  import ComboboxCustom from './Custom/Custom.js';
16
+ import { useFloatingCombobox } from './useFloatingCombobox.js';
17
+ import { useComboboxKeyboard } from './useComboboxKeyboard.js';
18
+ import { ComboboxIdProvider } from './ComboboxIdContext.js';
19
+ import { ComboboxContext } from './ComboboxContext.js';
17
20
  import { Box } from '../../Box/Box.js';
18
21
  import { Spinner } from '../../Spinner/Spinner.js';
19
- import { autoUpdate } from '../../../node_modules/@floating-ui/dom/dist/floating-ui.dom.js';
20
- import { flip, size, offset } from '../../../node_modules/@floating-ui/core/dist/floating-ui.core.js';
21
22
  import { omit } from '../../../utilities/objectUtils.js';
22
23
 
23
- const Combobox = forwardRef(({ value, initialValue = [], onValueChange, label, hideLabel = false, description, multiple = false, size: size$1 = 'medium', disabled = false, readOnly = false, hideChips = false, cleanButtonLabel = 'Fjern alt', clearButtonLabel = 'Fjern alt', hideClearButton = false, error, errorId, id, name, portal = true, htmlSize = 0, virtual = false, children, style, loading, loadingLabel = 'Laster...', filter = (inputValue, option) => {
24
- return option.label.toLowerCase().startsWith(inputValue.toLowerCase());
25
- }, chipSrLabel = (option) => 'Slett ' + option.label, className, ...rest }, forwareddRef) => {
24
+ const ComboboxComponent = forwardRef(({ value, initialValue = [], onValueChange, label, hideLabel = false, description, multiple = false, size = 'medium', disabled = false, readOnly = false, hideChips = false, cleanButtonLabel = 'Fjern alt', clearButtonLabel = 'Fjern alt', hideClearButton = false, error, errorId, id, name, portal = true, htmlSize = 0, virtual = false, children, style, loading, loadingLabel = 'Laster...', filter, chipSrLabel = (option) => 'Slett ' + option.label, className, ...rest }, forwareddRef) => {
26
25
  const inputRef = useRef(null);
27
26
  const portalRef = useRef(null);
27
+ const listRef = useRef([]);
28
28
  const listId = useId();
29
- const [open, setOpen] = useState(false);
30
29
  const [inputValue, setInputValue] = useState(rest.inputValue || '');
31
- const [activeIndex, setActiveIndex] = useState(null);
32
- const { selectedOptions, setSelectedOptions, options, optionsChildren, restChildren, optionValues, customIds, prevSelectedHash, setPrevSelectedHash, } = useCombobox({
30
+ /* const idDispatch = useComboboxIdDispatch(); */
31
+ const { selectedOptions, options, restChildren, interactiveChildren, optionValues, customIds, filteredOptionsChildren, filteredOptions, prevSelectedHash, setSelectedOptions, setPrevSelectedHash, } = useCombobox({
33
32
  children,
34
33
  inputValue,
35
34
  filter,
36
35
  multiple,
37
36
  initialValue,
38
37
  });
39
- const [activeDescendant, setActiveDescendant] = useState(undefined);
40
- useEffect(() => {
41
- if (rest.inputValue !== undefined) {
42
- setInputValue(rest.inputValue);
43
- }
44
- }, [rest.inputValue]);
38
+ const { open, setOpen, refs, floatingStyles, context, getReferenceProps, getFloatingProps, getItemProps, } = useFloatingCombobox({
39
+ listRef,
40
+ });
45
41
  const formFieldProps = useFormField({
46
42
  disabled,
47
43
  readOnly,
48
44
  error,
49
45
  errorId,
50
- size: size$1,
46
+ size,
51
47
  description,
52
48
  id,
53
49
  }, 'combobox');
54
- const listRef = useRef([]);
55
50
  // if value is set, set input value to the label of the value
56
51
  useEffect(() => {
57
- if (value && value.length > 0 && !multiple) {
58
- const option = options.find((option) => option.value === value[0]);
52
+ if (value && value.length >= 0 && !multiple) {
53
+ const option = options[value[0]];
59
54
  setInputValue(option?.label || '');
60
55
  }
61
56
  }, [multiple, value, options]);
62
- // floating UI
63
- const { refs, floatingStyles, context } = useFloating({
64
- open,
65
- onOpenChange: (newOpen) => {
66
- flushSync(() => {
67
- if (refs.floating.current && !newOpen) {
68
- refs.floating.current.scrollTop = 0;
69
- }
70
- setTimeout(() => {
71
- setOpen(newOpen);
72
- }, 1);
73
- });
74
- },
75
- whileElementsMounted: (reference, floating, update) => {
76
- autoUpdate(reference, floating, update);
77
- return () => {
78
- floating.scrollTop = 0;
79
- };
80
- },
81
- middleware: [
82
- flip({ padding: 10 }),
83
- size({
84
- apply({ rects, elements }) {
85
- requestAnimationFrame(() => {
86
- Object.assign(elements.floating.style, {
87
- width: `calc(${rects.reference.width}px - calc(var(--fds-spacing-2) * 2))`,
88
- maxHeight: `200px`,
89
- });
90
- });
91
- },
92
- }),
93
- offset(10),
94
- ],
95
- });
96
- const role = useRole(context, { role: 'listbox' });
97
- const dismiss = useDismiss(context);
98
- const listNav = useListNavigation(context, {
99
- listRef,
100
- activeIndex,
101
- virtual: true,
102
- scrollItemIntoView: true,
103
- enabled: open,
104
- });
105
- const { getReferenceProps, getFloatingProps } = useInteractions([
106
- role,
107
- dismiss,
108
- listNav,
109
- ]);
110
- // remove active index if combobox is closed
111
- useEffect(() => {
112
- if (!open) {
113
- setActiveIndex(null);
114
- }
115
- }, [open]);
116
57
  // Send new value if option was clicked
117
58
  useEffect(() => {
118
59
  const selectedHash = JSON.stringify(selectedOptions);
119
60
  if (prevSelectedHash === selectedHash)
120
61
  return;
121
- const values = selectedOptions.map((option) => option.value);
62
+ const values = Object.keys(selectedOptions);
122
63
  onValueChange?.(values);
123
64
  setPrevSelectedHash(selectedHash);
124
65
  }, [onValueChange, selectedOptions, prevSelectedHash, setPrevSelectedHash]);
125
66
  useEffect(() => {
126
- if (value && options.length > 0) {
67
+ if (value && Object.keys(options).length >= 0) {
127
68
  const updatedSelectedOptions = value.map((option) => {
128
- const value = options.find((value) => value.value === option);
69
+ const value = options[option];
129
70
  return value;
130
71
  });
131
- setSelectedOptions(updatedSelectedOptions);
72
+ setSelectedOptions(updatedSelectedOptions.reduce((acc, value) => {
73
+ acc[value.value] = value;
74
+ return acc;
75
+ }, {}));
132
76
  }
133
77
  }, [multiple, prevSelectedHash, value, options, setSelectedOptions]);
134
78
  // handle click on option, either select or deselect - Handles single or multiple
135
79
  const handleSelectOption = (option) => {
136
80
  // if option is already selected, remove it
137
81
  if (value && value.includes(option.value)) {
138
- setSelectedOptions((prev) => prev.filter((i) => i.value !== option.value));
82
+ setSelectedOptions((prev) => {
83
+ const updated = { ...prev };
84
+ delete updated[option.value];
85
+ return updated;
86
+ });
139
87
  return;
140
88
  }
141
89
  if (multiple) {
142
- setSelectedOptions([...selectedOptions, option]);
90
+ /* setSelectedOptions([...selectedOptions, option]); */
91
+ setSelectedOptions((prev) => {
92
+ const updated = { ...prev };
93
+ updated[option.value] = option;
94
+ return updated;
95
+ });
143
96
  setInputValue('');
144
97
  inputRef.current?.focus();
145
98
  }
146
99
  else {
147
- setSelectedOptions([option]);
100
+ setSelectedOptions({
101
+ [option.value]: option,
102
+ });
148
103
  setInputValue(option?.label || '');
149
104
  // move cursor to the end of the input
150
105
  setTimeout(() => {
@@ -155,87 +110,24 @@ const Combobox = forwardRef(({ value, initialValue = [], onValueChange, label, h
155
110
  refs.domReference.current?.focus();
156
111
  };
157
112
  const debouncedHandleSelectOption = useDebounce(handleSelectOption, 50);
158
- // handle keyboard navigation in the list
159
- const handleKeyDownFunc = (event) => {
160
- const navigateable = customIds.length + optionsChildren.length;
161
- if (formFieldProps.readOnly || disabled)
162
- return;
163
- if (!event)
164
- return;
165
- switch (event.key) {
166
- case 'ArrowDown':
167
- event.preventDefault();
168
- if (!open)
169
- setOpen(true);
170
- setActiveIndex((prevActiveIndex) => {
171
- if (prevActiveIndex === null) {
172
- return 0;
173
- }
174
- return Math.min(prevActiveIndex + 1, navigateable - 1);
175
- });
176
- break;
177
- case 'ArrowUp':
178
- event.preventDefault();
179
- /* If we are on the first item, close */
180
- setActiveIndex((prevActiveIndex) => {
181
- if (prevActiveIndex === 0) {
182
- setOpen(false);
183
- return null;
184
- }
185
- if (prevActiveIndex === null) {
186
- return null;
187
- }
188
- return Math.max(prevActiveIndex - 1, 0);
189
- });
190
- break;
191
- case 'Enter':
192
- event.preventDefault();
193
- if (activeIndex !== null &&
194
- (optionsChildren[activeIndex] || customIds.length > 0)) {
195
- // check if we are in the custom components
196
- if (activeIndex <= customIds.length) {
197
- // send `onSelect` event to the custom component
198
- const selectedId = customIds[activeIndex];
199
- const selectedComponent = restChildren.find((component) => isInteractiveComboboxCustom(component) &&
200
- component.props?.id === selectedId);
201
- if (isInteractiveComboboxCustom(selectedComponent) &&
202
- selectedComponent.props.onSelect) {
203
- selectedComponent.props.onSelect();
204
- }
205
- }
206
- // if we are in the options, find the actual index
207
- const valueIndex = activeIndex - customIds.length;
208
- const child = optionsChildren[valueIndex];
209
- if (isComboboxOption(child)) {
210
- const props = child.props;
211
- const option = options.find((option) => option.value === props.value);
212
- if (!multiple) {
213
- // check if option is already selected, if so, deselect it
214
- if (selectedOptions.find((i) => i.value === option?.value)) {
215
- setSelectedOptions([]);
216
- setInputValue('');
217
- return;
218
- }
219
- }
220
- debouncedHandleSelectOption(option);
221
- }
222
- }
223
- break;
224
- case 'Backspace':
225
- if (inputValue === '' && multiple && selectedOptions.length > 0) {
226
- setSelectedOptions((prev) => prev.slice(0, prev.length - 1));
227
- }
228
- // if we are in single mode, we need to set activeValue to null
229
- if (!multiple) {
230
- setSelectedOptions([]);
231
- }
232
- break;
233
- }
234
- };
235
- const handleKeyDown = useDebounce(handleKeyDownFunc, 20);
113
+ const handleKeyDown = useComboboxKeyboard({
114
+ filteredOptions,
115
+ selectedOptions,
116
+ readOnly: formFieldProps.readOnly || false,
117
+ disabled: disabled,
118
+ multiple,
119
+ inputValue,
120
+ options,
121
+ open,
122
+ interactiveChildren,
123
+ setOpen,
124
+ setInputValue,
125
+ setSelectedOptions,
126
+ handleSelectOption: debouncedHandleSelectOption,
127
+ });
236
128
  const rowVirtualizer = useVirtualizer({
237
- count: optionsChildren.length,
238
- getScrollElement: () => refs.floating.current,
129
+ count: Object.keys(filteredOptionsChildren).length,
130
+ getScrollElement: () => (virtual ? refs.floating.current : null),
239
131
  estimateSize: () => 70,
240
132
  measureElement: (elem) => {
241
133
  return elem.getBoundingClientRect().height;
@@ -243,18 +135,16 @@ const Combobox = forwardRef(({ value, initialValue = [], onValueChange, label, h
243
135
  overscan: 1,
244
136
  });
245
137
  return (jsxs(ComboboxContext.Provider, { value: {
246
- size: size$1,
138
+ size,
247
139
  options,
248
140
  selectedOptions,
249
141
  multiple,
250
- activeIndex,
251
142
  disabled,
252
143
  readOnly,
253
144
  open,
254
145
  inputRef,
255
146
  refs,
256
147
  inputValue,
257
- activeDescendant,
258
148
  error,
259
149
  formFieldProps,
260
150
  name,
@@ -264,40 +154,33 @@ const Combobox = forwardRef(({ value, initialValue = [], onValueChange, label, h
264
154
  clearButtonLabel: cleanButtonLabel || clearButtonLabel,
265
155
  hideClearButton,
266
156
  listId,
157
+ customIds,
158
+ filteredOptions,
267
159
  setInputValue,
268
- setActiveIndex,
269
160
  handleKeyDown,
270
161
  setOpen,
271
162
  getReferenceProps,
163
+ getItemProps,
272
164
  setSelectedOptions,
273
- /* Recieves index of option, and the ID of the button element */
274
- setActiveOption: (index, id) => {
275
- if (readOnly)
276
- return;
277
- if (disabled)
278
- return;
279
- setActiveIndex(index);
280
- setActiveDescendant(id);
281
- },
282
165
  /* Recieves the value of the option, and searches for it in our values lookup */
283
166
  onOptionClick: (value) => {
284
167
  if (readOnly)
285
168
  return;
286
169
  if (disabled)
287
170
  return;
288
- const option = options.find((option) => option.value === value);
171
+ const option = options[value];
289
172
  debouncedHandleSelectOption(option);
290
173
  },
291
174
  handleSelectOption: debouncedHandleSelectOption,
292
175
  chipSrLabel,
293
176
  listRef,
294
177
  forwareddRef,
295
- }, children: [jsxs(Box, { className: clsx(classes.combobox, disabled && classes.disabled, className), style: style, ref: portalRef, children: [name && (jsx(ComboboxNative, { name: name, selectedOptions: selectedOptions, multiple: multiple })), jsx(ComboboxLabel, { label: label, description: description, size: size$1, readOnly: readOnly, hideLabel: hideLabel, formFieldProps: formFieldProps }), jsx(ComboboxInput, { ...omit(['inputValue'], rest), "aria-busy": loading }), jsx(ComboboxError, { size: size$1, error: error, formFieldProps: formFieldProps })] }), open && (jsx(FloatingPortal, { root: portal ? null : portalRef, children: jsx(FloatingFocusManager, { context: context, initialFocus: -1, visuallyHiddenDismiss: true, children: jsxs(Box, { id: listId, shadow: 'medium', borderRadius: 'medium', borderColor: 'default', "aria-labelledby": formFieldProps.inputProps.id, "aria-autocomplete": 'list', tabIndex: -1, ...getFloatingProps({
178
+ }, children: [jsxs(Box, { className: clsx(classes.combobox, disabled && classes.disabled, className), style: style, ref: portalRef, children: [name && (jsx(ComboboxNative, { name: name, selectedOptions: selectedOptions, multiple: multiple })), jsx(ComboboxLabel, { label: label, description: description, size: size, readOnly: readOnly, hideLabel: hideLabel, formFieldProps: formFieldProps }), jsx(ComboboxInput, { ...omit(['inputValue'], rest), "aria-busy": loading }), jsx(ComboboxError, { size: size, error: error, formFieldProps: formFieldProps })] }), open && (jsx(FloatingPortal, { root: portal ? null : portalRef, children: jsx(FloatingFocusManager, { context: context, initialFocus: -1, visuallyHiddenDismiss: true, children: jsxs(Box, { id: listId, shadow: 'medium', borderRadius: 'medium', borderColor: 'default', "aria-labelledby": formFieldProps.inputProps.id, "aria-autocomplete": 'list', tabIndex: -1, ...getFloatingProps({
296
179
  ref: refs.setFloating,
297
180
  style: {
298
181
  ...floatingStyles,
299
182
  },
300
- }), className: clsx(classes.optionsWrapper, classes[size$1]), children: [virtual && (jsx("div", { style: {
183
+ }), className: clsx(classes.optionsWrapper, classes[size]), children: [virtual && (jsx("div", { style: {
301
184
  height: `${rowVirtualizer.getTotalSize()}px`,
302
185
  width: '100%',
303
186
  position: 'relative',
@@ -307,9 +190,9 @@ const Combobox = forwardRef(({ value, initialValue = [], onValueChange, label, h
307
190
  left: 0,
308
191
  width: '100%',
309
192
  transform: `translateY(${virtualRow.start}px)`,
310
- }, children: optionsChildren[virtualRow.index] }, virtualRow.index))) })), loading ? (jsxs(ComboboxCustom, { className: classes.loading, children: [jsx(Spinner, { title: 'Laster', size: 'small' }), loadingLabel] })) : (jsxs(Fragment, { children: [restChildren, !virtual && optionsChildren] }))] }) }) }))] }));
193
+ }, children: filteredOptionsChildren[virtualRow.index] }, virtualRow.index))) })), loading ? (jsxs(ComboboxCustom, { className: classes.loading, children: [jsx(Spinner, { title: 'Laster', size: 'small' }), loadingLabel] })) : (jsxs(Fragment, { children: [restChildren, !virtual && filteredOptionsChildren] }))] }) }) }))] }));
311
194
  });
312
- const ComboboxContext = createContext(undefined);
195
+ const Combobox = forwardRef((props, ref) => (jsx(ComboboxIdProvider, { children: jsx(ComboboxComponent, { ...props, ref: ref }) })));
313
196
  Combobox.displayName = 'Combobox';
314
197
 
315
- export { Combobox, ComboboxContext };
198
+ export { Combobox, ComboboxComponent };
@@ -0,0 +1,6 @@
1
+ 'use client';
2
+ import { createContext } from 'react';
3
+
4
+ const ComboboxContext = createContext(undefined);
5
+
6
+ export { ComboboxContext };
@@ -0,0 +1,35 @@
1
+ 'use client';
2
+ import { jsx } from 'react/jsx-runtime';
3
+ import { createContext, useContext, useReducer } from 'react';
4
+
5
+ const ComboboxIdContext = createContext({
6
+ activeIndex: 0,
7
+ });
8
+ const ComboboxIdReducer = (state, action) => {
9
+ switch (action.type) {
10
+ case 'SET_ACTIVE_INDEX':
11
+ return {
12
+ ...state,
13
+ activeIndex: action.payload,
14
+ };
15
+ default:
16
+ return state;
17
+ }
18
+ };
19
+ const ComboboxIdDispatch = createContext(() => {
20
+ throw new Error('ComboboxIdDispatch must be used within a provider');
21
+ });
22
+ const ComboboxIdProvider = ({ children, }) => {
23
+ const [state, dispatch] = useReducer(ComboboxIdReducer, {
24
+ activeIndex: 0,
25
+ });
26
+ return (jsx(ComboboxIdContext.Provider, { value: state, children: jsx(ComboboxIdDispatch.Provider, { value: dispatch, children: children }) }));
27
+ };
28
+ function useComboboxIdDispatch() {
29
+ return useContext(ComboboxIdDispatch);
30
+ }
31
+ function useComboboxId() {
32
+ return useContext(ComboboxIdContext);
33
+ }
34
+
35
+ export { ComboboxIdContext, ComboboxIdDispatch, ComboboxIdProvider, ComboboxIdReducer, useComboboxId, useComboboxIdDispatch };
@@ -3,7 +3,9 @@ import { jsx } from 'react/jsx-runtime';
3
3
  import { forwardRef, useId, useContext, useMemo } from 'react';
4
4
  import { clsx } from '../../../../node_modules/clsx/dist/clsx.js';
5
5
  import { Slot as $5e63c961fc1ce211$export$8c6ed5c666ac1360 } from '../../../../node_modules/@radix-ui/react-slot/dist/index.js';
6
- import { ComboboxContext } from '../Combobox.js';
6
+ import { useMergeRefs } from '../../../../packages/react/node_modules/@floating-ui/react/dist/floating-ui.react.js';
7
+ import { ComboboxContext } from '../ComboboxContext.js';
8
+ import { useComboboxId } from '../ComboboxIdContext.js';
7
9
  import classes from './Custom.module.css.js';
8
10
  import { omit } from '../../../../utilities/objectUtils.js';
9
11
 
@@ -13,17 +15,20 @@ const ComboboxCustom = forwardRef(({ asChild, interactive, id, className, ...res
13
15
  }
14
16
  const Component = asChild ? $5e63c961fc1ce211$export$8c6ed5c666ac1360 : 'div';
15
17
  const randomId = useId();
18
+ const { activeIndex } = useComboboxId();
16
19
  const context = useContext(ComboboxContext);
17
20
  if (!context) {
18
21
  throw new Error('ComboboxCustom must be used within a Combobox');
19
22
  }
20
- const { size, activeIndex, optionValues, setActiveIndex } = context;
21
- const index = useMemo(() => id && optionValues.indexOf(id), [optionValues, id]);
22
- return (jsx(Component, { ref: ref, tabIndex: -1, className: clsx(classes.custom, classes[size], className), id: id || randomId, role: 'option', "aria-selected": activeIndex === index, "data-active": activeIndex === index, onMouseEnter: () => {
23
- typeof index === 'number' && setActiveIndex(index);
24
- }, onFocus: () => {
25
- typeof index === 'number' && setActiveIndex(index);
26
- }, ...omit(['interactive'], rest) }));
23
+ const { size, customIds, listRef, getItemProps } = context;
24
+ const index = useMemo(() => (id && customIds.indexOf(id)) || 0, [id, customIds]);
25
+ const combinedRef = useMergeRefs([
26
+ (node) => {
27
+ listRef.current[index] = node;
28
+ },
29
+ ref,
30
+ ]);
31
+ return (jsx(Component, { ref: combinedRef, tabIndex: -1, className: clsx(classes.custom, classes[size], className), id: id || randomId, role: 'option', "aria-selected": activeIndex === index, "data-active": activeIndex === index, ...omit(['interactive'], rest), ...omit(['onClick', 'onPointerLeave'], getItemProps()) }));
27
32
  });
28
33
  var ComboboxCustom$1 = ComboboxCustom;
29
34
 
@@ -2,7 +2,7 @@
2
2
  import { jsx } from 'react/jsx-runtime';
3
3
  import { forwardRef, useContext } from 'react';
4
4
  import { clsx } from '../../../../node_modules/clsx/dist/clsx.js';
5
- import { ComboboxContext } from '../Combobox.js';
5
+ import { ComboboxContext } from '../ComboboxContext.js';
6
6
  import classes from './Empty.module.css.js';
7
7
 
8
8
  const ComboboxEmpty = forwardRef(({ children, className, ...rest }, ref) => {
@@ -10,8 +10,8 @@ const ComboboxEmpty = forwardRef(({ children, className, ...rest }, ref) => {
10
10
  if (!context) {
11
11
  throw new Error('ComboboxEmpty must be used within a Combobox');
12
12
  }
13
- const { optionValues, size } = context;
14
- return (optionValues.length === 0 && (jsx("div", { ref: ref, className: clsx(classes.empty, classes[size], className), ...rest, children: children })));
13
+ const { filteredOptions, size } = context;
14
+ return (filteredOptions.length === 0 && (jsx("div", { ref: ref, className: clsx(classes.empty, classes[size], className), ...rest, children: children })));
15
15
  });
16
16
  ComboboxEmpty.displayName = 'ComboboxEmpty';
17
17