@simplybusiness/mobius 5.24.0 → 5.24.2

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 (39) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/cjs/components/Combobox/Combobox.js +12 -8
  3. package/dist/cjs/components/Combobox/Combobox.js.map +1 -1
  4. package/dist/cjs/components/Combobox/Listbox.js +13 -2
  5. package/dist/cjs/components/Combobox/Listbox.js.map +1 -1
  6. package/dist/cjs/components/Combobox/useComboboxHighlight.js +8 -2
  7. package/dist/cjs/components/Combobox/useComboboxHighlight.js.map +1 -1
  8. package/dist/cjs/components/Combobox/useComboboxOptions.js +6 -4
  9. package/dist/cjs/components/Combobox/useComboboxOptions.js.map +1 -1
  10. package/dist/cjs/components/Combobox/utils.js +1 -0
  11. package/dist/cjs/components/Combobox/utils.js.map +1 -1
  12. package/dist/cjs/tsconfig.tsbuildinfo +1 -1
  13. package/dist/esm/components/Combobox/Combobox.js +12 -8
  14. package/dist/esm/components/Combobox/Combobox.js.map +1 -1
  15. package/dist/esm/components/Combobox/Listbox.js +13 -2
  16. package/dist/esm/components/Combobox/Listbox.js.map +1 -1
  17. package/dist/esm/components/Combobox/types.js.map +1 -1
  18. package/dist/esm/components/Combobox/useComboboxHighlight.js +8 -2
  19. package/dist/esm/components/Combobox/useComboboxHighlight.js.map +1 -1
  20. package/dist/esm/components/Combobox/useComboboxOptions.js +6 -4
  21. package/dist/esm/components/Combobox/useComboboxOptions.js.map +1 -1
  22. package/dist/esm/components/Combobox/utils.js +1 -0
  23. package/dist/esm/components/Combobox/utils.js.map +1 -1
  24. package/dist/types/src/components/Combobox/Listbox.d.ts +2 -1
  25. package/dist/types/src/components/Combobox/types.d.ts +2 -0
  26. package/dist/types/src/components/Combobox/useComboboxHighlight.d.ts +1 -1
  27. package/dist/types/src/components/Combobox/useComboboxOptions.d.ts +3 -2
  28. package/dist/types/src/components/Combobox/utils.d.ts +1 -1
  29. package/package.json +2 -2
  30. package/src/components/Combobox/Combobox.css +10 -3
  31. package/src/components/Combobox/Combobox.test.tsx +4 -1
  32. package/src/components/Combobox/Combobox.tsx +13 -6
  33. package/src/components/Combobox/Listbox.tsx +22 -11
  34. package/src/components/Combobox/types.tsx +2 -0
  35. package/src/components/Combobox/useComboboxHighlight.tsx +13 -3
  36. package/src/components/Combobox/useComboboxOptions.test.ts +68 -4
  37. package/src/components/Combobox/useComboboxOptions.ts +8 -4
  38. package/src/components/Combobox/utils.tsx +2 -1
  39. package/dist/types/turbowatch.d.ts +0 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## 5.24.2
4
+
5
+ ### Patch Changes
6
+
7
+ - b892045: Change watch mode
8
+ - Updated dependencies [b892045]
9
+ - @simplybusiness/icons@4.22.1
10
+
11
+ ## 5.24.1
12
+
13
+ ### Patch Changes
14
+
15
+ - d1b9571: Update styling for highlighted combobox options
16
+
3
17
  ## 5.24.0
4
18
 
5
19
  ### Minor Changes
@@ -24,13 +24,13 @@ function _interop_require_default(obj) {
24
24
  };
25
25
  }
26
26
  const ComboboxInner = /*#__PURE__*/ (0, _react.forwardRef)((props, ref)=>{
27
- const { id, defaultValue, value, options, asyncOptions, delay, minSearchLength, onSelected, className, placeholder, icon, onBlur, onFocus, onChange, optionComponent, ...otherProps } = props;
27
+ const { id, defaultValue, value, options, asyncOptions, delay, minSearchLength, onSelected, className, placeholder, icon, onBlur, onFocus, onChange, onSearched, optionComponent, errorMessage, ...otherProps } = props;
28
28
  // Avoid re-fetching after selecting an option
29
29
  const skipNextDebounceRef = (0, _react.useRef)(false);
30
30
  const fallbackRef = (0, _react.useRef)(null);
31
31
  const [inputValue, setInputValue] = (0, _react.useState)(defaultValue || "");
32
32
  const [isOpen, setIsOpen] = (0, _react.useState)(false);
33
- const { filteredOptions, updateFilteredOptions, isLoading } = (0, _useComboboxOptions.useComboboxOptions)({
33
+ const { filteredOptions, updateFilteredOptions, isLoading, error } = (0, _useComboboxOptions.useComboboxOptions)({
34
34
  options,
35
35
  asyncOptions,
36
36
  inputValue,
@@ -39,15 +39,15 @@ const ComboboxInner = /*#__PURE__*/ (0, _react.forwardRef)((props, ref)=>{
39
39
  skipNextDebounceRef
40
40
  });
41
41
  const { highlightedIndex, highlightedGroupIndex, highlightNextOption, highlightPreviousOption, highlightFirstOption, highlightLastOption, clearHighlight } = (0, _useComboboxHighlight.useComboboxHighlight)(filteredOptions);
42
+ const showListbox = isOpen && filteredOptions && filteredOptions.length > 0;
42
43
  const inputRef = ref || fallbackRef;
43
44
  const listboxId = (0, _react.useId)();
44
45
  const statusId = (0, _react.useId)();
45
46
  const blurTimeoutRef = (0, _react.useRef)(null);
46
- const showListbox = isOpen && filteredOptions.length > 0;
47
47
  const { down } = (0, _hooks.useBreakpoint)();
48
48
  const isMobile = down("md");
49
49
  const handleFocus = (e)=>{
50
- if (filteredOptions.length === 0) return;
50
+ if (!filteredOptions || filteredOptions.length === 0) return;
51
51
  if (blurTimeoutRef.current) {
52
52
  clearTimeout(blurTimeoutRef.current);
53
53
  blurTimeoutRef.current = null;
@@ -85,6 +85,7 @@ const ComboboxInner = /*#__PURE__*/ (0, _react.forwardRef)((props, ref)=>{
85
85
  onSelected === null || onSelected === void 0 ? void 0 : onSelected(option);
86
86
  };
87
87
  const getFirstOption = ()=>{
88
+ if (!filteredOptions) return undefined;
88
89
  if ((0, _utils.isOptionGroup)(filteredOptions)) {
89
90
  var _filteredOptions_;
90
91
  return (_filteredOptions_ = filteredOptions[0]) === null || _filteredOptions_ === void 0 ? void 0 : _filteredOptions_.options[0];
@@ -92,6 +93,7 @@ const ComboboxInner = /*#__PURE__*/ (0, _react.forwardRef)((props, ref)=>{
92
93
  return filteredOptions[0];
93
94
  };
94
95
  const getHighlightedOption = ()=>{
96
+ if (!filteredOptions) return undefined;
95
97
  if (highlightedIndex === -1) return undefined;
96
98
  if ((0, _utils.isOptionGroup)(filteredOptions)) {
97
99
  const group = filteredOptions[highlightedGroupIndex];
@@ -144,7 +146,7 @@ const ComboboxInner = /*#__PURE__*/ (0, _react.forwardRef)((props, ref)=>{
144
146
  break;
145
147
  case "Enter":
146
148
  e.preventDefault();
147
- if (showListbox) {
149
+ if (isOpen) {
148
150
  const selectedOption = getHighlightedOption() || getFirstOption();
149
151
  if (selectedOption) {
150
152
  handleOptionSelect(selectedOption);
@@ -168,7 +170,7 @@ const ComboboxInner = /*#__PURE__*/ (0, _react.forwardRef)((props, ref)=>{
168
170
  value
169
171
  ]);
170
172
  const classes = (0, _dedupe.default)("mobius mobius-combobox", {
171
- "mobius-combobox--is-expanded": showListbox,
173
+ "mobius-combobox--is-expanded": isOpen,
172
174
  "mobius-combobox--is-loading": isLoading,
173
175
  "mobius-combobox--is-mobile": isMobile
174
176
  }, className);
@@ -200,13 +202,15 @@ const ComboboxInner = /*#__PURE__*/ (0, _react.forwardRef)((props, ref)=>{
200
202
  "aria-autocomplete": "list",
201
203
  "aria-haspopup": "listbox",
202
204
  "aria-controls": showListbox ? listboxId : undefined,
203
- "aria-expanded": showListbox,
205
+ "aria-expanded": showListbox ? true : undefined,
204
206
  "aria-activedescendant": highlightedIndex === -1 ? undefined : getHighlightedOptionId(),
205
207
  prefixInside: icon,
206
- ref: inputRef
208
+ ref: inputRef,
209
+ errorMessage: errorMessage || (error === null || error === void 0 ? void 0 : error.message) || undefined
207
210
  }),
208
211
  showListbox && /*#__PURE__*/ (0, _jsxruntime.jsx)(_Listbox.Listbox, {
209
212
  id: listboxId,
213
+ isLoading: isLoading,
210
214
  options: filteredOptions,
211
215
  highlightedIndex: highlightedIndex,
212
216
  highlightedGroupIndex: highlightedGroupIndex,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/components/Combobox/Combobox.tsx"],"sourcesContent":["import classNames from \"classnames/dedupe\";\nimport type { FocusEvent } from \"react\";\nimport { forwardRef, useEffect, useId, useRef, useState } from \"react\";\nimport { useOnUnmount, useBreakpoint } from \"../../hooks\";\nimport { TextField } from \"../TextField\";\nimport { VisuallyHidden } from \"../VisuallyHidden\";\nimport { Listbox } from \"./Listbox\"; // Import Listbox component\nimport type { ComboboxOption, ComboboxProps, ComboboxRef } from \"./types\";\nimport { useComboboxHighlight } from \"./useComboboxHighlight\";\nimport { useComboboxOptions } from \"./useComboboxOptions\";\nimport { getOptionLabel, getOptionValue, isOptionGroup } from \"./utils\";\n\nconst ComboboxInner = forwardRef(\n <T extends ComboboxOption>(props: ComboboxProps<T>, ref: ComboboxRef) => {\n const {\n id,\n defaultValue,\n value,\n options,\n asyncOptions,\n delay,\n minSearchLength,\n onSelected,\n className,\n placeholder,\n icon,\n onBlur,\n onFocus,\n onChange,\n optionComponent,\n ...otherProps\n } = props;\n\n // Avoid re-fetching after selecting an option\n const skipNextDebounceRef = useRef(false);\n const fallbackRef = useRef<HTMLInputElement>(null);\n const [inputValue, setInputValue] = useState(defaultValue || \"\");\n const [isOpen, setIsOpen] = useState(false);\n const { filteredOptions, updateFilteredOptions, isLoading } =\n useComboboxOptions({\n options,\n asyncOptions,\n inputValue,\n delay,\n minSearchLength,\n skipNextDebounceRef,\n });\n const {\n highlightedIndex,\n highlightedGroupIndex,\n highlightNextOption,\n highlightPreviousOption,\n highlightFirstOption,\n highlightLastOption,\n clearHighlight,\n } = useComboboxHighlight(filteredOptions);\n\n const inputRef = ref || fallbackRef;\n const listboxId = useId();\n const statusId = useId();\n const blurTimeoutRef = useRef<NodeJS.Timeout | null>(null);\n const showListbox = isOpen && filteredOptions.length > 0;\n const { down } = useBreakpoint();\n const isMobile = down(\"md\");\n\n const handleFocus = (e: FocusEvent) => {\n if (filteredOptions.length === 0) return;\n if (blurTimeoutRef.current) {\n clearTimeout(blurTimeoutRef.current);\n blurTimeoutRef.current = null;\n } else {\n onFocus?.(e);\n }\n setIsOpen(true);\n };\n\n useOnUnmount(() => {\n if (blurTimeoutRef.current) {\n clearTimeout(blurTimeoutRef.current);\n }\n });\n\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const newValue = e.target.value;\n setInputValue(newValue);\n setIsOpen(true);\n clearHighlight();\n onChange?.(e);\n };\n\n const handleOptionSelect = (option: T) => {\n const val = getOptionValue(option);\n if (!val) return;\n\n // TODO: Declare this in the types\n if (\n typeof option === \"object\" &&\n \"callback\" in option &&\n option.callback &&\n typeof option.callback === \"function\"\n ) {\n // @ts-expect-error ref types are hard\n setTimeout(() => inputRef.current.focus(), 0);\n updateFilteredOptions(option.callback());\n return;\n }\n\n // Prevent re-fetching options after selecting an option\n skipNextDebounceRef.current = true;\n\n setIsOpen(false);\n setInputValue(val);\n onSelected?.(option);\n };\n\n const getFirstOption = () => {\n if (isOptionGroup(filteredOptions)) {\n return filteredOptions[0]?.options[0];\n }\n\n return filteredOptions[0];\n };\n\n const getHighlightedOption = () => {\n if (highlightedIndex === -1) return undefined;\n\n if (isOptionGroup(filteredOptions)) {\n const group = filteredOptions[highlightedGroupIndex];\n return group?.options[highlightedIndex];\n }\n\n return filteredOptions[highlightedIndex];\n };\n\n const getHighlightedOptionId = () => {\n const option = getHighlightedOption();\n if (!option) return undefined;\n\n if (isOptionGroup(filteredOptions)) {\n return `${listboxId}-option-${highlightedGroupIndex}-${highlightedIndex}`;\n }\n\n return `${listboxId}-option-${highlightedIndex}`;\n };\n\n const handleBlur = (e: FocusEvent<Element, Element>) => {\n // Force selection if user has matched an entry\n const typedText = inputValue.trim().toLowerCase();\n const highlightedOption = getHighlightedOption();\n const label = getOptionLabel(highlightedOption);\n\n if (typedText === label?.toLowerCase()) {\n handleOptionSelect(highlightedOption as T);\n }\n\n blurTimeoutRef.current = setTimeout(() => {\n onBlur?.(e);\n setIsOpen(false);\n }, 150);\n };\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n switch (e.key) {\n case \"ArrowDown\":\n e.preventDefault();\n setIsOpen(true);\n highlightNextOption();\n break;\n case \"ArrowUp\":\n e.preventDefault();\n setIsOpen(true);\n highlightPreviousOption();\n break;\n case \"Home\":\n e.preventDefault();\n setIsOpen(true);\n highlightFirstOption();\n break;\n case \"End\":\n e.preventDefault();\n setIsOpen(true);\n highlightLastOption();\n break;\n case \"Enter\":\n e.preventDefault();\n if (showListbox) {\n const selectedOption = getHighlightedOption() || getFirstOption();\n if (selectedOption) {\n handleOptionSelect(selectedOption);\n }\n }\n break;\n case \"Escape\":\n e.preventDefault();\n setInputValue(\"\");\n setIsOpen(false);\n clearHighlight();\n break;\n default:\n // Do nothing\n }\n };\n\n useEffect(() => {\n if (value) {\n setInputValue(value);\n }\n }, [value]);\n\n const classes = classNames(\n \"mobius mobius-combobox\",\n {\n \"mobius-combobox--is-expanded\": showListbox,\n \"mobius-combobox--is-loading\": isLoading,\n \"mobius-combobox--is-mobile\": isMobile,\n },\n className,\n );\n\n return (\n <div id={id} data-testid=\"mobius-combobox__wrapper\" className={classes}>\n {isLoading && (\n <VisuallyHidden\n role=\"status\"\n aria-live=\"polite\"\n id={statusId}\n elementType=\"div\"\n className=\"mobius-combobox__status\"\n >\n Loading options\n </VisuallyHidden>\n )}\n <TextField\n {...otherProps}\n className=\"mobius-combobox__input\"\n role=\"combobox\"\n value={inputValue}\n placeholder={placeholder}\n onFocus={handleFocus}\n onBlur={handleBlur}\n onKeyDown={handleKeyDown}\n onChange={handleInputChange}\n autoComplete=\"off\"\n aria-describedby={isLoading ? statusId : undefined}\n aria-autocomplete=\"list\"\n aria-haspopup=\"listbox\"\n aria-controls={showListbox ? listboxId : undefined}\n aria-expanded={showListbox}\n aria-activedescendant={\n highlightedIndex === -1 ? undefined : getHighlightedOptionId()\n }\n prefixInside={icon}\n ref={inputRef}\n />\n {showListbox && (\n <Listbox\n id={listboxId}\n options={filteredOptions}\n highlightedIndex={highlightedIndex}\n highlightedGroupIndex={highlightedGroupIndex}\n onOptionSelect={handleOptionSelect}\n optionComponent={optionComponent}\n />\n )}\n </div>\n );\n },\n);\n\nexport const Combobox = ComboboxInner as <T extends ComboboxOption>(\n props: ComboboxProps<T> & { ref?: ComboboxRef },\n) => JSX.Element;\n"],"names":["Combobox","ComboboxInner","forwardRef","props","ref","id","defaultValue","value","options","asyncOptions","delay","minSearchLength","onSelected","className","placeholder","icon","onBlur","onFocus","onChange","optionComponent","otherProps","skipNextDebounceRef","useRef","fallbackRef","inputValue","setInputValue","useState","isOpen","setIsOpen","filteredOptions","updateFilteredOptions","isLoading","useComboboxOptions","highlightedIndex","highlightedGroupIndex","highlightNextOption","highlightPreviousOption","highlightFirstOption","highlightLastOption","clearHighlight","useComboboxHighlight","inputRef","listboxId","useId","statusId","blurTimeoutRef","showListbox","length","down","useBreakpoint","isMobile","handleFocus","e","current","clearTimeout","useOnUnmount","handleInputChange","newValue","target","handleOptionSelect","option","val","getOptionValue","callback","setTimeout","focus","getFirstOption","isOptionGroup","getHighlightedOption","undefined","group","getHighlightedOptionId","handleBlur","typedText","trim","toLowerCase","highlightedOption","label","getOptionLabel","handleKeyDown","key","preventDefault","selectedOption","useEffect","classes","classNames","div","data-testid","VisuallyHidden","role","aria-live","elementType","TextField","onKeyDown","autoComplete","aria-describedby","aria-autocomplete","aria-haspopup","aria-controls","aria-expanded","aria-activedescendant","prefixInside","Listbox","onOptionSelect"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../src/components/Combobox/Combobox.tsx"],"sourcesContent":["import classNames from \"classnames/dedupe\";\nimport type { FocusEvent } from \"react\";\nimport { forwardRef, useEffect, useId, useRef, useState } from \"react\";\nimport { useOnUnmount, useBreakpoint } from \"../../hooks\";\nimport { TextField } from \"../TextField\";\nimport { VisuallyHidden } from \"../VisuallyHidden\";\nimport { Listbox } from \"./Listbox\"; // Import Listbox component\nimport type { ComboboxOption, ComboboxProps, ComboboxRef } from \"./types\";\nimport { useComboboxHighlight } from \"./useComboboxHighlight\";\nimport { useComboboxOptions } from \"./useComboboxOptions\";\nimport { getOptionLabel, getOptionValue, isOptionGroup } from \"./utils\";\n\nconst ComboboxInner = forwardRef(\n <T extends ComboboxOption>(props: ComboboxProps<T>, ref: ComboboxRef) => {\n const {\n id,\n defaultValue,\n value,\n options,\n asyncOptions,\n delay,\n minSearchLength,\n onSelected,\n className,\n placeholder,\n icon,\n onBlur,\n onFocus,\n onChange,\n onSearched,\n optionComponent,\n errorMessage,\n ...otherProps\n } = props;\n\n // Avoid re-fetching after selecting an option\n const skipNextDebounceRef = useRef(false);\n const fallbackRef = useRef<HTMLInputElement>(null);\n const [inputValue, setInputValue] = useState(defaultValue || \"\");\n const [isOpen, setIsOpen] = useState(false);\n const { filteredOptions, updateFilteredOptions, isLoading, error } =\n useComboboxOptions({\n options,\n asyncOptions,\n inputValue,\n delay,\n minSearchLength,\n skipNextDebounceRef,\n });\n const {\n highlightedIndex,\n highlightedGroupIndex,\n highlightNextOption,\n highlightPreviousOption,\n highlightFirstOption,\n highlightLastOption,\n clearHighlight,\n } = useComboboxHighlight(filteredOptions);\n\n const showListbox = isOpen && filteredOptions && filteredOptions.length > 0;\n\n const inputRef = ref || fallbackRef;\n const listboxId = useId();\n const statusId = useId();\n const blurTimeoutRef = useRef<NodeJS.Timeout | null>(null);\n const { down } = useBreakpoint();\n const isMobile = down(\"md\");\n\n const handleFocus = (e: FocusEvent) => {\n if (!filteredOptions || filteredOptions.length === 0) return;\n if (blurTimeoutRef.current) {\n clearTimeout(blurTimeoutRef.current);\n blurTimeoutRef.current = null;\n } else {\n onFocus?.(e);\n }\n setIsOpen(true);\n };\n\n useOnUnmount(() => {\n if (blurTimeoutRef.current) {\n clearTimeout(blurTimeoutRef.current);\n }\n });\n\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const newValue = e.target.value;\n setInputValue(newValue);\n setIsOpen(true);\n clearHighlight();\n onChange?.(e);\n };\n\n const handleOptionSelect = (option: T) => {\n const val = getOptionValue(option);\n if (!val) return;\n\n // TODO: Declare this in the types\n if (\n typeof option === \"object\" &&\n \"callback\" in option &&\n option.callback &&\n typeof option.callback === \"function\"\n ) {\n // @ts-expect-error ref types are hard\n setTimeout(() => inputRef.current.focus(), 0);\n updateFilteredOptions(option.callback());\n return;\n }\n\n // Prevent re-fetching options after selecting an option\n skipNextDebounceRef.current = true;\n\n setIsOpen(false);\n setInputValue(val);\n onSelected?.(option);\n };\n\n const getFirstOption = () => {\n if (!filteredOptions) return undefined;\n if (isOptionGroup(filteredOptions)) {\n return filteredOptions[0]?.options[0];\n }\n\n return filteredOptions[0];\n };\n\n const getHighlightedOption = () => {\n if (!filteredOptions) return undefined;\n if (highlightedIndex === -1) return undefined;\n\n if (isOptionGroup(filteredOptions)) {\n const group = filteredOptions[highlightedGroupIndex];\n return group?.options[highlightedIndex];\n }\n\n return filteredOptions[highlightedIndex];\n };\n\n const getHighlightedOptionId = () => {\n const option = getHighlightedOption();\n if (!option) return undefined;\n\n if (isOptionGroup(filteredOptions)) {\n return `${listboxId}-option-${highlightedGroupIndex}-${highlightedIndex}`;\n }\n\n return `${listboxId}-option-${highlightedIndex}`;\n };\n\n const handleBlur = (e: FocusEvent<Element, Element>) => {\n // Force selection if user has matched an entry\n const typedText = inputValue.trim().toLowerCase();\n const highlightedOption = getHighlightedOption();\n const label = getOptionLabel(highlightedOption);\n\n if (typedText === label?.toLowerCase()) {\n handleOptionSelect(highlightedOption as T);\n }\n\n blurTimeoutRef.current = setTimeout(() => {\n onBlur?.(e);\n setIsOpen(false);\n }, 150);\n };\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n switch (e.key) {\n case \"ArrowDown\":\n e.preventDefault();\n setIsOpen(true);\n highlightNextOption();\n break;\n case \"ArrowUp\":\n e.preventDefault();\n setIsOpen(true);\n highlightPreviousOption();\n break;\n case \"Home\":\n e.preventDefault();\n setIsOpen(true);\n highlightFirstOption();\n break;\n case \"End\":\n e.preventDefault();\n setIsOpen(true);\n highlightLastOption();\n break;\n case \"Enter\":\n e.preventDefault();\n if (isOpen) {\n const selectedOption = getHighlightedOption() || getFirstOption();\n if (selectedOption) {\n handleOptionSelect(selectedOption);\n }\n }\n break;\n case \"Escape\":\n e.preventDefault();\n setInputValue(\"\");\n setIsOpen(false);\n clearHighlight();\n break;\n default:\n // Do nothing\n }\n };\n\n useEffect(() => {\n if (value) {\n setInputValue(value);\n }\n }, [value]);\n\n const classes = classNames(\n \"mobius mobius-combobox\",\n {\n \"mobius-combobox--is-expanded\": isOpen,\n \"mobius-combobox--is-loading\": isLoading,\n \"mobius-combobox--is-mobile\": isMobile,\n },\n className,\n );\n\n return (\n <div id={id} data-testid=\"mobius-combobox__wrapper\" className={classes}>\n {isLoading && (\n <VisuallyHidden\n role=\"status\"\n aria-live=\"polite\"\n id={statusId}\n elementType=\"div\"\n className=\"mobius-combobox__status\"\n >\n Loading options\n </VisuallyHidden>\n )}\n <TextField\n {...otherProps}\n className=\"mobius-combobox__input\"\n role=\"combobox\"\n value={inputValue}\n placeholder={placeholder}\n onFocus={handleFocus}\n onBlur={handleBlur}\n onKeyDown={handleKeyDown}\n onChange={handleInputChange}\n autoComplete=\"off\"\n aria-describedby={isLoading ? statusId : undefined}\n aria-autocomplete=\"list\"\n aria-haspopup=\"listbox\"\n aria-controls={showListbox ? listboxId : undefined}\n aria-expanded={showListbox ? true : undefined}\n aria-activedescendant={\n highlightedIndex === -1 ? undefined : getHighlightedOptionId()\n }\n prefixInside={icon}\n ref={inputRef}\n errorMessage={errorMessage || error?.message || undefined}\n />\n {showListbox && (\n <Listbox\n id={listboxId}\n isLoading={isLoading}\n options={filteredOptions}\n highlightedIndex={highlightedIndex}\n highlightedGroupIndex={highlightedGroupIndex}\n onOptionSelect={handleOptionSelect}\n optionComponent={optionComponent}\n />\n )}\n </div>\n );\n },\n);\n\nexport const Combobox = ComboboxInner as <T extends ComboboxOption>(\n props: ComboboxProps<T> & { ref?: ComboboxRef },\n) => JSX.Element;\n"],"names":["Combobox","ComboboxInner","forwardRef","props","ref","id","defaultValue","value","options","asyncOptions","delay","minSearchLength","onSelected","className","placeholder","icon","onBlur","onFocus","onChange","onSearched","optionComponent","errorMessage","otherProps","skipNextDebounceRef","useRef","fallbackRef","inputValue","setInputValue","useState","isOpen","setIsOpen","filteredOptions","updateFilteredOptions","isLoading","error","useComboboxOptions","highlightedIndex","highlightedGroupIndex","highlightNextOption","highlightPreviousOption","highlightFirstOption","highlightLastOption","clearHighlight","useComboboxHighlight","showListbox","length","inputRef","listboxId","useId","statusId","blurTimeoutRef","down","useBreakpoint","isMobile","handleFocus","e","current","clearTimeout","useOnUnmount","handleInputChange","newValue","target","handleOptionSelect","option","val","getOptionValue","callback","setTimeout","focus","getFirstOption","undefined","isOptionGroup","getHighlightedOption","group","getHighlightedOptionId","handleBlur","typedText","trim","toLowerCase","highlightedOption","label","getOptionLabel","handleKeyDown","key","preventDefault","selectedOption","useEffect","classes","classNames","div","data-testid","VisuallyHidden","role","aria-live","elementType","TextField","onKeyDown","autoComplete","aria-describedby","aria-autocomplete","aria-haspopup","aria-controls","aria-expanded","aria-activedescendant","prefixInside","message","Listbox","onOptionSelect"],"mappings":"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"}
@@ -25,6 +25,17 @@ const Listbox = ({ id, options, highlightedIndex, highlightedGroupIndex, onOptio
25
25
  }
26
26
  return (0, _utils.isOptionGroup)(options) ? `${id}-option-${groupIndex}-${index}` : `${id}-option-${index}`;
27
27
  }
28
+ if (options && options.length === 0) {
29
+ return /*#__PURE__*/ (0, _jsxruntime.jsx)("div", {
30
+ role: "listbox",
31
+ id: id,
32
+ className: classes,
33
+ children: /*#__PURE__*/ (0, _jsxruntime.jsx)("div", {
34
+ className: "mobius-combobox__no-options",
35
+ children: "No options"
36
+ })
37
+ });
38
+ }
28
39
  return /*#__PURE__*/ (0, _jsxruntime.jsx)("div", {
29
40
  role: "listbox",
30
41
  id: id,
@@ -48,13 +59,13 @@ const Listbox = ({ id, options, highlightedIndex, highlightedGroupIndex, onOptio
48
59
  id: getOptionId(groupOption, groupIndex, index)
49
60
  }, `${id}-option-${groupIndex}-${index}`))
50
61
  ]
51
- }, option.heading)) : options.map((option, index)=>/*#__PURE__*/ (0, _jsxruntime.jsx)(_Option.Option, {
62
+ }, option.heading)) : typeof options !== "undefined" ? options.map((option, index)=>/*#__PURE__*/ (0, _jsxruntime.jsx)(_Option.Option, {
52
63
  option: option,
53
64
  isHighlighted: highlightedIndex === index,
54
65
  onOptionSelect: onOptionSelect,
55
66
  optionComponent: optionComponent,
56
67
  id: getOptionId(option, 0, index)
57
- }, index))
68
+ }, index)) : null
58
69
  });
59
70
  };
60
71
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/components/Combobox/Listbox.tsx"],"sourcesContent":["import classNames from \"classnames\";\nimport { Option } from \"./Option\";\nimport type {\n ComboboxBaseProps,\n ComboboxOption,\n ComboboxOptions,\n} from \"./types\";\nimport { isOptionGroup } from \"./utils\";\n\nexport type ListboxProps<T extends ComboboxOption> = {\n id: string;\n options: ComboboxOptions<T>;\n highlightedIndex: number;\n highlightedGroupIndex: number;\n onOptionSelect: (option: T) => void;\n optionComponent?: ComboboxBaseProps<T>[\"optionComponent\"];\n};\n\nexport const Listbox = <T extends ComboboxOption>({\n id,\n options,\n highlightedIndex,\n highlightedGroupIndex,\n onOptionSelect,\n optionComponent,\n}: ListboxProps<T>) => {\n const classes = classNames(\"mobius-combobox__list\");\n\n function getOptionId(\n option: ComboboxOption,\n groupIndex: number,\n index: number,\n ) {\n if (\n typeof option === \"object\" &&\n \"id\" in option &&\n typeof option.id === \"string\"\n ) {\n return option.id;\n }\n return isOptionGroup(options)\n ? `${id}-option-${groupIndex}-${index}`\n : `${id}-option-${index}`;\n }\n\n return (\n <div role=\"listbox\" id={id} className={classes}>\n {isOptionGroup(options)\n ? options.map((option, groupIndex) => (\n <ul\n role=\"group\"\n key={option.heading}\n aria-labelledby={`${id}-group-${groupIndex}`}\n className=\"mobius-combobox__group\"\n >\n <li\n role=\"presentation\"\n id={`${id}-group-${groupIndex}`}\n className=\"mobius-combobox__group-label\"\n >\n {option.heading}\n </li>\n {option.options.map((groupOption, index) => (\n <Option\n key={`${id}-option-${groupIndex}-${index}`}\n option={groupOption}\n isHighlighted={\n highlightedIndex === index &&\n highlightedGroupIndex === groupIndex\n }\n onOptionSelect={onOptionSelect}\n optionComponent={optionComponent}\n id={getOptionId(groupOption, groupIndex, index)}\n />\n ))}\n </ul>\n ))\n : options.map((option, index) => (\n <Option\n key={index}\n option={option}\n isHighlighted={highlightedIndex === index}\n onOptionSelect={onOptionSelect}\n optionComponent={optionComponent}\n id={getOptionId(option, 0, index)}\n />\n ))}\n </div>\n );\n};\n"],"names":["Listbox","id","options","highlightedIndex","highlightedGroupIndex","onOptionSelect","optionComponent","classes","classNames","getOptionId","option","groupIndex","index","isOptionGroup","div","role","className","map","ul","aria-labelledby","li","heading","groupOption","Option","isHighlighted"],"mappings":";;;;+BAkBaA;;;eAAAA;;;;mEAlBU;wBACA;uBAMO;;;;;;AAWvB,MAAMA,UAAU,CAA2B,EAChDC,EAAE,EACFC,OAAO,EACPC,gBAAgB,EAChBC,qBAAqB,EACrBC,cAAc,EACdC,eAAe,EACC;IAChB,MAAMC,UAAUC,IAAAA,mBAAU,EAAC;IAE3B,SAASC,YACPC,MAAsB,EACtBC,UAAkB,EAClBC,KAAa;QAEb,IACE,OAAOF,WAAW,YAClB,QAAQA,UACR,OAAOA,OAAOT,EAAE,KAAK,UACrB;YACA,OAAOS,OAAOT,EAAE;QAClB;QACA,OAAOY,IAAAA,oBAAa,EAACX,WACjB,GAAGD,GAAG,QAAQ,EAAEU,WAAW,CAAC,EAAEC,OAAO,GACrC,GAAGX,GAAG,QAAQ,EAAEW,OAAO;IAC7B;IAEA,qBACE,qBAACE;QAAIC,MAAK;QAAUd,IAAIA;QAAIe,WAAWT;kBACpCM,IAAAA,oBAAa,EAACX,WACXA,QAAQe,GAAG,CAAC,CAACP,QAAQC,2BACnB,sBAACO;gBACCH,MAAK;gBAELI,mBAAiB,GAAGlB,GAAG,OAAO,EAAEU,YAAY;gBAC5CK,WAAU;;kCAEV,qBAACI;wBACCL,MAAK;wBACLd,IAAI,GAAGA,GAAG,OAAO,EAAEU,YAAY;wBAC/BK,WAAU;kCAETN,OAAOW,OAAO;;oBAEhBX,OAAOR,OAAO,CAACe,GAAG,CAAC,CAACK,aAAaV,sBAChC,qBAACW,cAAM;4BAELb,QAAQY;4BACRE,eACErB,qBAAqBS,SACrBR,0BAA0BO;4BAE5BN,gBAAgBA;4BAChBC,iBAAiBA;4BACjBL,IAAIQ,YAAYa,aAAaX,YAAYC;2BARpC,GAAGX,GAAG,QAAQ,EAAEU,WAAW,CAAC,EAAEC,OAAO;;eAbzCF,OAAOW,OAAO,KA0BvBnB,QAAQe,GAAG,CAAC,CAACP,QAAQE,sBACnB,qBAACW,cAAM;gBAELb,QAAQA;gBACRc,eAAerB,qBAAqBS;gBACpCP,gBAAgBA;gBAChBC,iBAAiBA;gBACjBL,IAAIQ,YAAYC,QAAQ,GAAGE;eALtBA;;AAUnB"}
1
+ {"version":3,"sources":["../../../../src/components/Combobox/Listbox.tsx"],"sourcesContent":["import classNames from \"classnames\";\nimport { Option } from \"./Option\";\nimport type {\n ComboboxBaseProps,\n ComboboxOption,\n ComboboxOptions,\n} from \"./types\";\nimport { isOptionGroup } from \"./utils\";\n\nexport type ListboxProps<T extends ComboboxOption> = {\n id: string;\n isLoading?: boolean;\n options: ComboboxOptions<T> | undefined;\n highlightedIndex: number;\n highlightedGroupIndex: number;\n onOptionSelect: (option: T) => void;\n optionComponent?: ComboboxBaseProps<T>[\"optionComponent\"];\n};\n\nexport const Listbox = <T extends ComboboxOption>({\n id,\n options,\n highlightedIndex,\n highlightedGroupIndex,\n onOptionSelect,\n optionComponent,\n}: ListboxProps<T>) => {\n const classes = classNames(\"mobius-combobox__list\");\n\n function getOptionId(\n option: ComboboxOption,\n groupIndex: number,\n index: number,\n ) {\n if (\n typeof option === \"object\" &&\n \"id\" in option &&\n typeof option.id === \"string\"\n ) {\n return option.id;\n }\n return isOptionGroup(options)\n ? `${id}-option-${groupIndex}-${index}`\n : `${id}-option-${index}`;\n }\n\n if (options && options.length === 0) {\n return (\n <div role=\"listbox\" id={id} className={classes}>\n <div className=\"mobius-combobox__no-options\">No options</div>\n </div>\n );\n }\n\n return (\n <div role=\"listbox\" id={id} className={classes}>\n {isOptionGroup(options)\n ? options.map((option, groupIndex) => (\n <ul\n role=\"group\"\n key={option.heading}\n aria-labelledby={`${id}-group-${groupIndex}`}\n className=\"mobius-combobox__group\"\n >\n <li\n role=\"presentation\"\n id={`${id}-group-${groupIndex}`}\n className=\"mobius-combobox__group-label\"\n >\n {option.heading}\n </li>\n {option.options.map((groupOption, index) => (\n <Option\n key={`${id}-option-${groupIndex}-${index}`}\n option={groupOption}\n isHighlighted={\n highlightedIndex === index &&\n highlightedGroupIndex === groupIndex\n }\n onOptionSelect={onOptionSelect}\n optionComponent={optionComponent}\n id={getOptionId(groupOption, groupIndex, index)}\n />\n ))}\n </ul>\n ))\n : typeof options !== \"undefined\"\n ? options.map((option, index) => (\n <Option\n key={index}\n option={option}\n isHighlighted={highlightedIndex === index}\n onOptionSelect={onOptionSelect}\n optionComponent={optionComponent}\n id={getOptionId(option, 0, index)}\n />\n ))\n : null}\n </div>\n );\n};\n"],"names":["Listbox","id","options","highlightedIndex","highlightedGroupIndex","onOptionSelect","optionComponent","classes","classNames","getOptionId","option","groupIndex","index","isOptionGroup","length","div","role","className","map","ul","aria-labelledby","li","heading","groupOption","Option","isHighlighted"],"mappings":";;;;+BAmBaA;;;eAAAA;;;;mEAnBU;wBACA;uBAMO;;;;;;AAYvB,MAAMA,UAAU,CAA2B,EAChDC,EAAE,EACFC,OAAO,EACPC,gBAAgB,EAChBC,qBAAqB,EACrBC,cAAc,EACdC,eAAe,EACC;IAChB,MAAMC,UAAUC,IAAAA,mBAAU,EAAC;IAE3B,SAASC,YACPC,MAAsB,EACtBC,UAAkB,EAClBC,KAAa;QAEb,IACE,OAAOF,WAAW,YAClB,QAAQA,UACR,OAAOA,OAAOT,EAAE,KAAK,UACrB;YACA,OAAOS,OAAOT,EAAE;QAClB;QACA,OAAOY,IAAAA,oBAAa,EAACX,WACjB,GAAGD,GAAG,QAAQ,EAAEU,WAAW,CAAC,EAAEC,OAAO,GACrC,GAAGX,GAAG,QAAQ,EAAEW,OAAO;IAC7B;IAEA,IAAIV,WAAWA,QAAQY,MAAM,KAAK,GAAG;QACnC,qBACE,qBAACC;YAAIC,MAAK;YAAUf,IAAIA;YAAIgB,WAAWV;sBACrC,cAAA,qBAACQ;gBAAIE,WAAU;0BAA8B;;;IAGnD;IAEA,qBACE,qBAACF;QAAIC,MAAK;QAAUf,IAAIA;QAAIgB,WAAWV;kBACpCM,IAAAA,oBAAa,EAACX,WACXA,QAAQgB,GAAG,CAAC,CAACR,QAAQC,2BACnB,sBAACQ;gBACCH,MAAK;gBAELI,mBAAiB,GAAGnB,GAAG,OAAO,EAAEU,YAAY;gBAC5CM,WAAU;;kCAEV,qBAACI;wBACCL,MAAK;wBACLf,IAAI,GAAGA,GAAG,OAAO,EAAEU,YAAY;wBAC/BM,WAAU;kCAETP,OAAOY,OAAO;;oBAEhBZ,OAAOR,OAAO,CAACgB,GAAG,CAAC,CAACK,aAAaX,sBAChC,qBAACY,cAAM;4BAELd,QAAQa;4BACRE,eACEtB,qBAAqBS,SACrBR,0BAA0BO;4BAE5BN,gBAAgBA;4BAChBC,iBAAiBA;4BACjBL,IAAIQ,YAAYc,aAAaZ,YAAYC;2BARpC,GAAGX,GAAG,QAAQ,EAAEU,WAAW,CAAC,EAAEC,OAAO;;eAbzCF,OAAOY,OAAO,KA0BvB,OAAOpB,YAAY,cACjBA,QAAQgB,GAAG,CAAC,CAACR,QAAQE,sBACnB,qBAACY,cAAM;gBAELd,QAAQA;gBACRe,eAAetB,qBAAqBS;gBACpCP,gBAAgBA;gBAChBC,iBAAiBA;gBACjBL,IAAIQ,YAAYC,QAAQ,GAAGE;eALtBA,UAQT;;AAGZ"}
@@ -11,10 +11,13 @@ Object.defineProperty(exports, "useComboboxHighlight", {
11
11
  const _react = require("react");
12
12
  const _utils = require("./utils");
13
13
  function useComboboxHighlight(options) {
14
- const [highlightedIndex, setHighlightedIndex] = (0, _react.useState)(options.length ? 0 : -1);
14
+ const [highlightedIndex, setHighlightedIndex] = (0, _react.useState)(options && options.length ? 0 : -1);
15
15
  const [highlightedGroupIndex, setHighlightedGroupIndex] = (0, _react.useState)(0);
16
16
  function highlightNextOption() {
17
17
  const isGroup = (0, _utils.isOptionGroup)(options);
18
+ if (!options) {
19
+ return;
20
+ }
18
21
  if (isGroup) {
19
22
  const group = options[highlightedGroupIndex];
20
23
  if (highlightedIndex === group.options.length - 1) {
@@ -59,6 +62,9 @@ function useComboboxHighlight(options) {
59
62
  }
60
63
  function highlightLastOption() {
61
64
  const isGroup = (0, _utils.isOptionGroup)(options);
65
+ if (!options) {
66
+ return;
67
+ }
62
68
  if (isGroup) {
63
69
  const lastGroupIndex = options.length - 1;
64
70
  const lastGroup = options[lastGroupIndex];
@@ -69,7 +75,7 @@ function useComboboxHighlight(options) {
69
75
  }
70
76
  }
71
77
  const clearHighlight = ()=>{
72
- setHighlightedIndex(options.length ? 0 : -1);
78
+ setHighlightedIndex(typeof options === "undefined" || options.length ? 0 : -1);
73
79
  setHighlightedGroupIndex(0);
74
80
  };
75
81
  return {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/components/Combobox/useComboboxHighlight.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport type { ComboboxOptions } from \"./types\";\nimport { isOptionGroup } from \"./utils\";\n\nexport function useComboboxHighlight(options: ComboboxOptions) {\n const [highlightedIndex, setHighlightedIndex] = useState(\n options.length ? 0 : -1,\n );\n const [highlightedGroupIndex, setHighlightedGroupIndex] = useState(0);\n\n function highlightNextOption() {\n const isGroup = isOptionGroup(options);\n\n if (isGroup) {\n const group = options[highlightedGroupIndex];\n if (highlightedIndex === group.options.length - 1) {\n if (highlightedGroupIndex === options.length - 1) {\n return; // At the end of last group\n }\n // Move to next group\n setHighlightedIndex(0);\n setHighlightedGroupIndex(highlightedGroupIndex + 1);\n } else {\n setHighlightedIndex(highlightedIndex + 1);\n }\n } else {\n if (highlightedIndex === options.length - 1) {\n return; // At the end of options\n }\n setHighlightedIndex(highlightedIndex + 1);\n }\n }\n\n function highlightPreviousOption() {\n const isGroup = isOptionGroup(options);\n\n if (highlightedIndex === 0 && highlightedGroupIndex === 0) {\n return; // Already at start\n }\n\n if (isGroup) {\n if (highlightedIndex === 0) {\n // Move to previous group\n const prevGroupIndex = highlightedGroupIndex - 1;\n const prevGroup = options[prevGroupIndex];\n setHighlightedGroupIndex(prevGroupIndex);\n setHighlightedIndex(prevGroup.options.length - 1);\n } else {\n setHighlightedIndex(highlightedIndex - 1);\n }\n } else {\n setHighlightedIndex(highlightedIndex - 1);\n }\n }\n\n function highlightFirstOption() {\n setHighlightedIndex(0);\n setHighlightedGroupIndex(0);\n }\n\n function highlightLastOption() {\n const isGroup = isOptionGroup(options);\n\n if (isGroup) {\n const lastGroupIndex = options.length - 1;\n const lastGroup = options[lastGroupIndex];\n setHighlightedGroupIndex(lastGroupIndex);\n setHighlightedIndex(lastGroup.options.length - 1);\n } else {\n setHighlightedIndex(options.length - 1);\n }\n }\n\n const clearHighlight = () => {\n setHighlightedIndex(options.length ? 0 : -1);\n setHighlightedGroupIndex(0);\n };\n\n return {\n highlightedIndex,\n highlightedGroupIndex,\n highlightPreviousOption,\n highlightNextOption,\n highlightFirstOption,\n highlightLastOption,\n clearHighlight,\n };\n}\n"],"names":["useComboboxHighlight","options","highlightedIndex","setHighlightedIndex","useState","length","highlightedGroupIndex","setHighlightedGroupIndex","highlightNextOption","isGroup","isOptionGroup","group","highlightPreviousOption","prevGroupIndex","prevGroup","highlightFirstOption","highlightLastOption","lastGroupIndex","lastGroup","clearHighlight"],"mappings":";;;;+BAIgBA;;;eAAAA;;;uBAJS;uBAEK;AAEvB,SAASA,qBAAqBC,OAAwB;IAC3D,MAAM,CAACC,kBAAkBC,oBAAoB,GAAGC,IAAAA,eAAQ,EACtDH,QAAQI,MAAM,GAAG,IAAI,CAAC;IAExB,MAAM,CAACC,uBAAuBC,yBAAyB,GAAGH,IAAAA,eAAQ,EAAC;IAEnE,SAASI;QACP,MAAMC,UAAUC,IAAAA,oBAAa,EAACT;QAE9B,IAAIQ,SAAS;YACX,MAAME,QAAQV,OAAO,CAACK,sBAAsB;YAC5C,IAAIJ,qBAAqBS,MAAMV,OAAO,CAACI,MAAM,GAAG,GAAG;gBACjD,IAAIC,0BAA0BL,QAAQI,MAAM,GAAG,GAAG;oBAChD,QAAQ,2BAA2B;gBACrC;gBACA,qBAAqB;gBACrBF,oBAAoB;gBACpBI,yBAAyBD,wBAAwB;YACnD,OAAO;gBACLH,oBAAoBD,mBAAmB;YACzC;QACF,OAAO;YACL,IAAIA,qBAAqBD,QAAQI,MAAM,GAAG,GAAG;gBAC3C,QAAQ,wBAAwB;YAClC;YACAF,oBAAoBD,mBAAmB;QACzC;IACF;IAEA,SAASU;QACP,MAAMH,UAAUC,IAAAA,oBAAa,EAACT;QAE9B,IAAIC,qBAAqB,KAAKI,0BAA0B,GAAG;YACzD,QAAQ,mBAAmB;QAC7B;QAEA,IAAIG,SAAS;YACX,IAAIP,qBAAqB,GAAG;gBAC1B,yBAAyB;gBACzB,MAAMW,iBAAiBP,wBAAwB;gBAC/C,MAAMQ,YAAYb,OAAO,CAACY,eAAe;gBACzCN,yBAAyBM;gBACzBV,oBAAoBW,UAAUb,OAAO,CAACI,MAAM,GAAG;YACjD,OAAO;gBACLF,oBAAoBD,mBAAmB;YACzC;QACF,OAAO;YACLC,oBAAoBD,mBAAmB;QACzC;IACF;IAEA,SAASa;QACPZ,oBAAoB;QACpBI,yBAAyB;IAC3B;IAEA,SAASS;QACP,MAAMP,UAAUC,IAAAA,oBAAa,EAACT;QAE9B,IAAIQ,SAAS;YACX,MAAMQ,iBAAiBhB,QAAQI,MAAM,GAAG;YACxC,MAAMa,YAAYjB,OAAO,CAACgB,eAAe;YACzCV,yBAAyBU;YACzBd,oBAAoBe,UAAUjB,OAAO,CAACI,MAAM,GAAG;QACjD,OAAO;YACLF,oBAAoBF,QAAQI,MAAM,GAAG;QACvC;IACF;IAEA,MAAMc,iBAAiB;QACrBhB,oBAAoBF,QAAQI,MAAM,GAAG,IAAI,CAAC;QAC1CE,yBAAyB;IAC3B;IAEA,OAAO;QACLL;QACAI;QACAM;QACAJ;QACAO;QACAC;QACAG;IACF;AACF"}
1
+ {"version":3,"sources":["../../../../src/components/Combobox/useComboboxHighlight.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport type { ComboboxOptions } from \"./types\";\nimport { isOptionGroup } from \"./utils\";\n\nexport function useComboboxHighlight(options: ComboboxOptions | undefined) {\n const [highlightedIndex, setHighlightedIndex] = useState(\n options && options.length ? 0 : -1,\n );\n const [highlightedGroupIndex, setHighlightedGroupIndex] = useState(0);\n\n function highlightNextOption() {\n const isGroup = isOptionGroup(options);\n\n if (!options) {\n return;\n }\n\n if (isGroup) {\n const group = options[highlightedGroupIndex];\n if (highlightedIndex === group.options.length - 1) {\n if (highlightedGroupIndex === options.length - 1) {\n return; // At the end of last group\n }\n // Move to next group\n setHighlightedIndex(0);\n setHighlightedGroupIndex(highlightedGroupIndex + 1);\n } else {\n setHighlightedIndex(highlightedIndex + 1);\n }\n } else {\n if (highlightedIndex === options.length - 1) {\n return; // At the end of options\n }\n setHighlightedIndex(highlightedIndex + 1);\n }\n }\n\n function highlightPreviousOption() {\n const isGroup = isOptionGroup(options);\n\n if (highlightedIndex === 0 && highlightedGroupIndex === 0) {\n return; // Already at start\n }\n\n if (isGroup) {\n if (highlightedIndex === 0) {\n // Move to previous group\n const prevGroupIndex = highlightedGroupIndex - 1;\n const prevGroup = options[prevGroupIndex];\n setHighlightedGroupIndex(prevGroupIndex);\n setHighlightedIndex(prevGroup.options.length - 1);\n } else {\n setHighlightedIndex(highlightedIndex - 1);\n }\n } else {\n setHighlightedIndex(highlightedIndex - 1);\n }\n }\n\n function highlightFirstOption() {\n setHighlightedIndex(0);\n setHighlightedGroupIndex(0);\n }\n\n function highlightLastOption() {\n const isGroup = isOptionGroup(options);\n\n if (!options) {\n return;\n }\n\n if (isGroup) {\n const lastGroupIndex = options.length - 1;\n const lastGroup = options[lastGroupIndex];\n setHighlightedGroupIndex(lastGroupIndex);\n setHighlightedIndex(lastGroup.options.length - 1);\n } else {\n setHighlightedIndex(options.length - 1);\n }\n }\n\n const clearHighlight = () => {\n setHighlightedIndex(\n typeof options === \"undefined\" || options.length ? 0 : -1,\n );\n setHighlightedGroupIndex(0);\n };\n\n return {\n highlightedIndex,\n highlightedGroupIndex,\n highlightPreviousOption,\n highlightNextOption,\n highlightFirstOption,\n highlightLastOption,\n clearHighlight,\n };\n}\n"],"names":["useComboboxHighlight","options","highlightedIndex","setHighlightedIndex","useState","length","highlightedGroupIndex","setHighlightedGroupIndex","highlightNextOption","isGroup","isOptionGroup","group","highlightPreviousOption","prevGroupIndex","prevGroup","highlightFirstOption","highlightLastOption","lastGroupIndex","lastGroup","clearHighlight"],"mappings":";;;;+BAIgBA;;;eAAAA;;;uBAJS;uBAEK;AAEvB,SAASA,qBAAqBC,OAAoC;IACvE,MAAM,CAACC,kBAAkBC,oBAAoB,GAAGC,IAAAA,eAAQ,EACtDH,WAAWA,QAAQI,MAAM,GAAG,IAAI,CAAC;IAEnC,MAAM,CAACC,uBAAuBC,yBAAyB,GAAGH,IAAAA,eAAQ,EAAC;IAEnE,SAASI;QACP,MAAMC,UAAUC,IAAAA,oBAAa,EAACT;QAE9B,IAAI,CAACA,SAAS;YACZ;QACF;QAEA,IAAIQ,SAAS;YACX,MAAME,QAAQV,OAAO,CAACK,sBAAsB;YAC5C,IAAIJ,qBAAqBS,MAAMV,OAAO,CAACI,MAAM,GAAG,GAAG;gBACjD,IAAIC,0BAA0BL,QAAQI,MAAM,GAAG,GAAG;oBAChD,QAAQ,2BAA2B;gBACrC;gBACA,qBAAqB;gBACrBF,oBAAoB;gBACpBI,yBAAyBD,wBAAwB;YACnD,OAAO;gBACLH,oBAAoBD,mBAAmB;YACzC;QACF,OAAO;YACL,IAAIA,qBAAqBD,QAAQI,MAAM,GAAG,GAAG;gBAC3C,QAAQ,wBAAwB;YAClC;YACAF,oBAAoBD,mBAAmB;QACzC;IACF;IAEA,SAASU;QACP,MAAMH,UAAUC,IAAAA,oBAAa,EAACT;QAE9B,IAAIC,qBAAqB,KAAKI,0BAA0B,GAAG;YACzD,QAAQ,mBAAmB;QAC7B;QAEA,IAAIG,SAAS;YACX,IAAIP,qBAAqB,GAAG;gBAC1B,yBAAyB;gBACzB,MAAMW,iBAAiBP,wBAAwB;gBAC/C,MAAMQ,YAAYb,OAAO,CAACY,eAAe;gBACzCN,yBAAyBM;gBACzBV,oBAAoBW,UAAUb,OAAO,CAACI,MAAM,GAAG;YACjD,OAAO;gBACLF,oBAAoBD,mBAAmB;YACzC;QACF,OAAO;YACLC,oBAAoBD,mBAAmB;QACzC;IACF;IAEA,SAASa;QACPZ,oBAAoB;QACpBI,yBAAyB;IAC3B;IAEA,SAASS;QACP,MAAMP,UAAUC,IAAAA,oBAAa,EAACT;QAE9B,IAAI,CAACA,SAAS;YACZ;QACF;QAEA,IAAIQ,SAAS;YACX,MAAMQ,iBAAiBhB,QAAQI,MAAM,GAAG;YACxC,MAAMa,YAAYjB,OAAO,CAACgB,eAAe;YACzCV,yBAAyBU;YACzBd,oBAAoBe,UAAUjB,OAAO,CAACI,MAAM,GAAG;QACjD,OAAO;YACLF,oBAAoBF,QAAQI,MAAM,GAAG;QACvC;IACF;IAEA,MAAMc,iBAAiB;QACrBhB,oBACE,OAAOF,YAAY,eAAeA,QAAQI,MAAM,GAAG,IAAI,CAAC;QAE1DE,yBAAyB;IAC3B;IAEA,OAAO;QACLL;QACAI;QACAM;QACAJ;QACAO;QACAC;QACAG;IACF;AACF"}
@@ -11,8 +11,8 @@ Object.defineProperty(exports, "useComboboxOptions", {
11
11
  const _react = require("react");
12
12
  const _utils = require("./utils");
13
13
  const _hooks = require("../../hooks");
14
- function useComboboxOptions({ options, asyncOptions, delay = 300, minSearchLength = 3, inputValue = "", skipNextDebounceRef }) {
15
- const [filteredOptions, setFilteredOptions] = (0, _react.useState)([]);
14
+ function useComboboxOptions({ options, asyncOptions, delay = 300, minSearchLength = 3, inputValue = "", skipNextDebounceRef, onSearched }) {
15
+ const [filteredOptions, setFilteredOptions] = (0, _react.useState)(undefined);
16
16
  const debouncedInputValue = (0, _hooks.useDebouncedValue)(inputValue, // Don't debounce synchronous options
17
17
  options ? 0 : delay);
18
18
  const [isLoading, setIsLoading] = (0, _react.useState)(false);
@@ -26,13 +26,14 @@ function useComboboxOptions({ options, asyncOptions, delay = 300, minSearchLengt
26
26
  try {
27
27
  if (asyncOptions) {
28
28
  if (debouncedInputValue.length < minSearchLength) {
29
- setFilteredOptions([]);
29
+ setFilteredOptions(undefined);
30
30
  return;
31
31
  }
32
32
  const result = await asyncOptions(debouncedInputValue, {
33
33
  signal
34
34
  });
35
35
  setFilteredOptions(result);
36
+ onSearched === null || onSearched === void 0 ? void 0 : onSearched(debouncedInputValue);
36
37
  } else {
37
38
  // @ts-expect-error options is erroneously typed as possibly undefined
38
39
  setFilteredOptions((0, _utils.filterOptions)(options, debouncedInputValue));
@@ -61,7 +62,8 @@ function useComboboxOptions({ options, asyncOptions, delay = 300, minSearchLengt
61
62
  asyncOptions,
62
63
  delay,
63
64
  minSearchLength,
64
- skipNextDebounceRef
65
+ skipNextDebounceRef,
66
+ onSearched
65
67
  ]);
66
68
  function updateFilteredOptions(newOptions) {
67
69
  setIsLoading(true);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/components/Combobox/useComboboxOptions.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport type { ComboboxOption, ComboboxOptions, ComboboxProps } from \"./types\";\nimport { filterOptions } from \"./utils\";\nimport { useDebouncedValue } from \"../../hooks\";\n\nexport type UseComboboxOptionsProps<T extends ComboboxOption> = Pick<\n ComboboxProps<T>,\n \"options\" | \"asyncOptions\" | \"delay\" | \"minSearchLength\"\n> & {\n skipNextDebounceRef?: React.MutableRefObject<boolean>;\n inputValue?: string;\n};\n\nexport function useComboboxOptions<T extends ComboboxOption>({\n options,\n asyncOptions,\n delay = 300,\n minSearchLength = 3,\n inputValue = \"\",\n skipNextDebounceRef,\n}: UseComboboxOptionsProps<T>) {\n const [filteredOptions, setFilteredOptions] = useState<ComboboxOptions<T>>(\n [],\n );\n const debouncedInputValue = useDebouncedValue(\n inputValue,\n // Don't debounce synchronous options\n options ? 0 : delay,\n );\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n const controller = new AbortController();\n const { signal } = controller;\n\n const fetchOptions = async () => {\n setIsLoading(true);\n setError(null);\n try {\n if (asyncOptions) {\n if (debouncedInputValue.length < minSearchLength) {\n setFilteredOptions([]);\n return;\n }\n const result = await asyncOptions(debouncedInputValue, { signal });\n setFilteredOptions(result);\n } else {\n // @ts-expect-error options is erroneously typed as possibly undefined\n setFilteredOptions(filterOptions(options, debouncedInputValue));\n }\n } catch (e: unknown) {\n if (e instanceof DOMException && e.name === \"AbortError\") {\n // Ignore abort errors\n return;\n }\n setError(e as Error);\n } finally {\n setIsLoading(false);\n }\n };\n\n if (!skipNextDebounceRef?.current) {\n void fetchOptions();\n } else {\n skipNextDebounceRef.current = false;\n }\n\n return () => {\n controller.abort();\n };\n }, [\n debouncedInputValue,\n options,\n asyncOptions,\n delay,\n minSearchLength,\n skipNextDebounceRef,\n ]);\n\n function updateFilteredOptions(newOptions: Promise<ComboboxOptions<T>>) {\n setIsLoading(true);\n return newOptions\n .then(setFilteredOptions)\n .catch(setError)\n .finally(() => setIsLoading(false));\n }\n\n return {\n filteredOptions,\n updateFilteredOptions,\n isLoading,\n error,\n isError: error != null,\n };\n}\n"],"names":["useComboboxOptions","options","asyncOptions","delay","minSearchLength","inputValue","skipNextDebounceRef","filteredOptions","setFilteredOptions","useState","debouncedInputValue","useDebouncedValue","isLoading","setIsLoading","error","setError","useEffect","controller","AbortController","signal","fetchOptions","length","result","filterOptions","e","DOMException","name","current","abort","updateFilteredOptions","newOptions","then","catch","finally","isError"],"mappings":";;;;+BAagBA;;;eAAAA;;;uBAboB;uBAEN;uBACI;AAU3B,SAASA,mBAA6C,EAC3DC,OAAO,EACPC,YAAY,EACZC,QAAQ,GAAG,EACXC,kBAAkB,CAAC,EACnBC,aAAa,EAAE,EACfC,mBAAmB,EACQ;IAC3B,MAAM,CAACC,iBAAiBC,mBAAmB,GAAGC,IAAAA,eAAQ,EACpD,EAAE;IAEJ,MAAMC,sBAAsBC,IAAAA,wBAAiB,EAC3CN,YACA,qCAAqC;IACrCJ,UAAU,IAAIE;IAEhB,MAAM,CAACS,WAAWC,aAAa,GAAGJ,IAAAA,eAAQ,EAAC;IAC3C,MAAM,CAACK,OAAOC,SAAS,GAAGN,IAAAA,eAAQ,EAAe;IAEjDO,IAAAA,gBAAS,EAAC;QACR,MAAMC,aAAa,IAAIC;QACvB,MAAM,EAAEC,MAAM,EAAE,GAAGF;QAEnB,MAAMG,eAAe;YACnBP,aAAa;YACbE,SAAS;YACT,IAAI;gBACF,IAAIb,cAAc;oBAChB,IAAIQ,oBAAoBW,MAAM,GAAGjB,iBAAiB;wBAChDI,mBAAmB,EAAE;wBACrB;oBACF;oBACA,MAAMc,SAAS,MAAMpB,aAAaQ,qBAAqB;wBAAES;oBAAO;oBAChEX,mBAAmBc;gBACrB,OAAO;oBACL,sEAAsE;oBACtEd,mBAAmBe,IAAAA,oBAAa,EAACtB,SAASS;gBAC5C;YACF,EAAE,OAAOc,GAAY;gBACnB,IAAIA,aAAaC,gBAAgBD,EAAEE,IAAI,KAAK,cAAc;oBACxD,sBAAsB;oBACtB;gBACF;gBACAX,SAASS;YACX,SAAU;gBACRX,aAAa;YACf;QACF;QAEA,IAAI,EAACP,gCAAAA,0CAAAA,oBAAqBqB,OAAO,GAAE;YACjC,KAAKP;QACP,OAAO;YACLd,oBAAoBqB,OAAO,GAAG;QAChC;QAEA,OAAO;YACLV,WAAWW,KAAK;QAClB;IACF,GAAG;QACDlB;QACAT;QACAC;QACAC;QACAC;QACAE;KACD;IAED,SAASuB,sBAAsBC,UAAuC;QACpEjB,aAAa;QACb,OAAOiB,WACJC,IAAI,CAACvB,oBACLwB,KAAK,CAACjB,UACNkB,OAAO,CAAC,IAAMpB,aAAa;IAChC;IAEA,OAAO;QACLN;QACAsB;QACAjB;QACAE;QACAoB,SAASpB,SAAS;IACpB;AACF"}
1
+ {"version":3,"sources":["../../../../src/components/Combobox/useComboboxOptions.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport type { ComboboxOption, ComboboxOptions, ComboboxProps } from \"./types\";\nimport { filterOptions } from \"./utils\";\nimport { useDebouncedValue } from \"../../hooks\";\n\nexport type UseComboboxOptionsProps<T extends ComboboxOption> = Pick<\n ComboboxProps<T>,\n \"options\" | \"asyncOptions\" | \"delay\" | \"minSearchLength\"\n> & {\n skipNextDebounceRef?: React.MutableRefObject<boolean>;\n inputValue?: string;\n onSearched?: (searchTerm: string) => void;\n};\n\nexport function useComboboxOptions<T extends ComboboxOption>({\n options,\n asyncOptions,\n delay = 300,\n minSearchLength = 3,\n inputValue = \"\",\n skipNextDebounceRef,\n onSearched,\n}: UseComboboxOptionsProps<T>) {\n const [filteredOptions, setFilteredOptions] = useState<\n ComboboxOptions<T> | undefined\n >(undefined);\n const debouncedInputValue = useDebouncedValue(\n inputValue,\n // Don't debounce synchronous options\n options ? 0 : delay,\n );\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n const controller = new AbortController();\n const { signal } = controller;\n\n const fetchOptions = async () => {\n setIsLoading(true);\n setError(null);\n try {\n if (asyncOptions) {\n if (debouncedInputValue.length < minSearchLength) {\n setFilteredOptions(undefined);\n return;\n }\n const result = await asyncOptions(debouncedInputValue, { signal });\n setFilteredOptions(result);\n onSearched?.(debouncedInputValue);\n } else {\n // @ts-expect-error options is erroneously typed as possibly undefined\n setFilteredOptions(filterOptions(options, debouncedInputValue));\n }\n } catch (e: unknown) {\n if (e instanceof DOMException && e.name === \"AbortError\") {\n // Ignore abort errors\n return;\n }\n setError(e as Error);\n } finally {\n setIsLoading(false);\n }\n };\n\n if (!skipNextDebounceRef?.current) {\n void fetchOptions();\n } else {\n skipNextDebounceRef.current = false;\n }\n\n return () => {\n controller.abort();\n };\n }, [\n debouncedInputValue,\n options,\n asyncOptions,\n delay,\n minSearchLength,\n skipNextDebounceRef,\n onSearched,\n ]);\n\n function updateFilteredOptions(newOptions: Promise<ComboboxOptions<T>>) {\n setIsLoading(true);\n return newOptions\n .then(setFilteredOptions)\n .catch(setError)\n .finally(() => setIsLoading(false));\n }\n\n return {\n filteredOptions,\n updateFilteredOptions,\n isLoading,\n error,\n isError: error != null,\n };\n}\n"],"names":["useComboboxOptions","options","asyncOptions","delay","minSearchLength","inputValue","skipNextDebounceRef","onSearched","filteredOptions","setFilteredOptions","useState","undefined","debouncedInputValue","useDebouncedValue","isLoading","setIsLoading","error","setError","useEffect","controller","AbortController","signal","fetchOptions","length","result","filterOptions","e","DOMException","name","current","abort","updateFilteredOptions","newOptions","then","catch","finally","isError"],"mappings":";;;;+BAcgBA;;;eAAAA;;;uBAdoB;uBAEN;uBACI;AAW3B,SAASA,mBAA6C,EAC3DC,OAAO,EACPC,YAAY,EACZC,QAAQ,GAAG,EACXC,kBAAkB,CAAC,EACnBC,aAAa,EAAE,EACfC,mBAAmB,EACnBC,UAAU,EACiB;IAC3B,MAAM,CAACC,iBAAiBC,mBAAmB,GAAGC,IAAAA,eAAQ,EAEpDC;IACF,MAAMC,sBAAsBC,IAAAA,wBAAiB,EAC3CR,YACA,qCAAqC;IACrCJ,UAAU,IAAIE;IAEhB,MAAM,CAACW,WAAWC,aAAa,GAAGL,IAAAA,eAAQ,EAAC;IAC3C,MAAM,CAACM,OAAOC,SAAS,GAAGP,IAAAA,eAAQ,EAAe;IAEjDQ,IAAAA,gBAAS,EAAC;QACR,MAAMC,aAAa,IAAIC;QACvB,MAAM,EAAEC,MAAM,EAAE,GAAGF;QAEnB,MAAMG,eAAe;YACnBP,aAAa;YACbE,SAAS;YACT,IAAI;gBACF,IAAIf,cAAc;oBAChB,IAAIU,oBAAoBW,MAAM,GAAGnB,iBAAiB;wBAChDK,mBAAmBE;wBACnB;oBACF;oBACA,MAAMa,SAAS,MAAMtB,aAAaU,qBAAqB;wBAAES;oBAAO;oBAChEZ,mBAAmBe;oBACnBjB,uBAAAA,iCAAAA,WAAaK;gBACf,OAAO;oBACL,sEAAsE;oBACtEH,mBAAmBgB,IAAAA,oBAAa,EAACxB,SAASW;gBAC5C;YACF,EAAE,OAAOc,GAAY;gBACnB,IAAIA,aAAaC,gBAAgBD,EAAEE,IAAI,KAAK,cAAc;oBACxD,sBAAsB;oBACtB;gBACF;gBACAX,SAASS;YACX,SAAU;gBACRX,aAAa;YACf;QACF;QAEA,IAAI,EAACT,gCAAAA,0CAAAA,oBAAqBuB,OAAO,GAAE;YACjC,KAAKP;QACP,OAAO;YACLhB,oBAAoBuB,OAAO,GAAG;QAChC;QAEA,OAAO;YACLV,WAAWW,KAAK;QAClB;IACF,GAAG;QACDlB;QACAX;QACAC;QACAC;QACAC;QACAE;QACAC;KACD;IAED,SAASwB,sBAAsBC,UAAuC;QACpEjB,aAAa;QACb,OAAOiB,WACJC,IAAI,CAACxB,oBACLyB,KAAK,CAACjB,UACNkB,OAAO,CAAC,IAAMpB,aAAa;IAChC;IAEA,OAAO;QACLP;QACAuB;QACAjB;QACAE;QACAoB,SAASpB,SAAS;IACpB;AACF"}
@@ -26,6 +26,7 @@ _export(exports, {
26
26
  }
27
27
  });
28
28
  function isOptionGroup(options) {
29
+ if (!options) return false;
29
30
  return typeof options[0] === "object" && "options" in options[0] && options[0].options !== undefined && "heading" in options[0] && options[0].heading !== undefined;
30
31
  }
31
32
  const getOptionValue = (option)=>typeof option === "string" ? option : option === null || option === void 0 ? void 0 : option.value;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/components/Combobox/utils.tsx"],"sourcesContent":["import type {\n ComboboxOption,\n ComboboxOptionGroup,\n ComboboxOptions,\n} from \"./types\";\n\n// FIXME: This might be better handled with Zod\nexport function isOptionGroup<T extends ComboboxOption>(\n options: ComboboxOptions<T>,\n): options is ComboboxOptionGroup<T>[] {\n return (\n typeof options[0] === \"object\" &&\n \"options\" in options[0] &&\n options[0].options !== undefined &&\n \"heading\" in options[0] &&\n options[0].heading !== undefined\n );\n}\n\nexport const getOptionValue = (option: ComboboxOption | undefined) =>\n typeof option === \"string\" ? option : option?.value;\n\nexport const getOptionLabel = (option: ComboboxOption | undefined) =>\n typeof option === \"string\" ? option : option?.label;\n\nexport function filterOptions<T extends ComboboxOption>(\n options: ComboboxOptions<T>,\n inputValue: string,\n): ComboboxOptions<T> {\n if (isOptionGroup(options)) {\n return options\n .map(optionGroup => ({\n ...optionGroup,\n options: optionGroup.options.filter(option =>\n getOptionLabel(option)!\n .toLowerCase()\n .includes(inputValue.toLowerCase()),\n ),\n }))\n .filter(optionGroup => optionGroup.options.length > 0);\n }\n\n return options.filter(option =>\n getOptionLabel(option)!.toLowerCase().includes(inputValue.toLowerCase()),\n );\n}\n\nexport function clamp(value: number, min: number, max: number) {\n return Math.min(Math.max(value, min), max);\n}\n"],"names":["clamp","filterOptions","getOptionLabel","getOptionValue","isOptionGroup","options","undefined","heading","option","value","label","inputValue","map","optionGroup","filter","toLowerCase","includes","length","min","max","Math"],"mappings":";;;;;;;;;;;IA+CgBA,KAAK;eAALA;;IAtBAC,aAAa;eAAbA;;IAHHC,cAAc;eAAdA;;IAHAC,cAAc;eAAdA;;IAZGC,aAAa;eAAbA;;;AAAT,SAASA,cACdC,OAA2B;IAE3B,OACE,OAAOA,OAAO,CAAC,EAAE,KAAK,YACtB,aAAaA,OAAO,CAAC,EAAE,IACvBA,OAAO,CAAC,EAAE,CAACA,OAAO,KAAKC,aACvB,aAAaD,OAAO,CAAC,EAAE,IACvBA,OAAO,CAAC,EAAE,CAACE,OAAO,KAAKD;AAE3B;AAEO,MAAMH,iBAAiB,CAACK,SAC7B,OAAOA,WAAW,WAAWA,SAASA,mBAAAA,6BAAAA,OAAQC,KAAK;AAE9C,MAAMP,iBAAiB,CAACM,SAC7B,OAAOA,WAAW,WAAWA,SAASA,mBAAAA,6BAAAA,OAAQE,KAAK;AAE9C,SAAST,cACdI,OAA2B,EAC3BM,UAAkB;IAElB,IAAIP,cAAcC,UAAU;QAC1B,OAAOA,QACJO,GAAG,CAACC,CAAAA,cAAgB,CAAA;gBACnB,GAAGA,WAAW;gBACdR,SAASQ,YAAYR,OAAO,CAACS,MAAM,CAACN,CAAAA,SAClCN,eAAeM,QACZO,WAAW,GACXC,QAAQ,CAACL,WAAWI,WAAW;YAEtC,CAAA,GACCD,MAAM,CAACD,CAAAA,cAAeA,YAAYR,OAAO,CAACY,MAAM,GAAG;IACxD;IAEA,OAAOZ,QAAQS,MAAM,CAACN,CAAAA,SACpBN,eAAeM,QAASO,WAAW,GAAGC,QAAQ,CAACL,WAAWI,WAAW;AAEzE;AAEO,SAASf,MAAMS,KAAa,EAAES,GAAW,EAAEC,GAAW;IAC3D,OAAOC,KAAKF,GAAG,CAACE,KAAKD,GAAG,CAACV,OAAOS,MAAMC;AACxC"}
1
+ {"version":3,"sources":["../../../../src/components/Combobox/utils.tsx"],"sourcesContent":["import type {\n ComboboxOption,\n ComboboxOptionGroup,\n ComboboxOptions,\n} from \"./types\";\n\n// FIXME: This might be better handled with Zod\nexport function isOptionGroup<T extends ComboboxOption>(\n options: ComboboxOptions<T> | undefined,\n): options is ComboboxOptionGroup<T>[] {\n if (!options) return false;\n return (\n typeof options[0] === \"object\" &&\n \"options\" in options[0] &&\n options[0].options !== undefined &&\n \"heading\" in options[0] &&\n options[0].heading !== undefined\n );\n}\n\nexport const getOptionValue = (option: ComboboxOption | undefined) =>\n typeof option === \"string\" ? option : option?.value;\n\nexport const getOptionLabel = (option: ComboboxOption | undefined) =>\n typeof option === \"string\" ? option : option?.label;\n\nexport function filterOptions<T extends ComboboxOption>(\n options: ComboboxOptions<T>,\n inputValue: string,\n): ComboboxOptions<T> {\n if (isOptionGroup(options)) {\n return options\n .map(optionGroup => ({\n ...optionGroup,\n options: optionGroup.options.filter(option =>\n getOptionLabel(option)!\n .toLowerCase()\n .includes(inputValue.toLowerCase()),\n ),\n }))\n .filter(optionGroup => optionGroup.options.length > 0);\n }\n\n return options.filter(option =>\n getOptionLabel(option)!.toLowerCase().includes(inputValue.toLowerCase()),\n );\n}\n\nexport function clamp(value: number, min: number, max: number) {\n return Math.min(Math.max(value, min), max);\n}\n"],"names":["clamp","filterOptions","getOptionLabel","getOptionValue","isOptionGroup","options","undefined","heading","option","value","label","inputValue","map","optionGroup","filter","toLowerCase","includes","length","min","max","Math"],"mappings":";;;;;;;;;;;IAgDgBA,KAAK;eAALA;;IAtBAC,aAAa;eAAbA;;IAHHC,cAAc;eAAdA;;IAHAC,cAAc;eAAdA;;IAbGC,aAAa;eAAbA;;;AAAT,SAASA,cACdC,OAAuC;IAEvC,IAAI,CAACA,SAAS,OAAO;IACrB,OACE,OAAOA,OAAO,CAAC,EAAE,KAAK,YACtB,aAAaA,OAAO,CAAC,EAAE,IACvBA,OAAO,CAAC,EAAE,CAACA,OAAO,KAAKC,aACvB,aAAaD,OAAO,CAAC,EAAE,IACvBA,OAAO,CAAC,EAAE,CAACE,OAAO,KAAKD;AAE3B;AAEO,MAAMH,iBAAiB,CAACK,SAC7B,OAAOA,WAAW,WAAWA,SAASA,mBAAAA,6BAAAA,OAAQC,KAAK;AAE9C,MAAMP,iBAAiB,CAACM,SAC7B,OAAOA,WAAW,WAAWA,SAASA,mBAAAA,6BAAAA,OAAQE,KAAK;AAE9C,SAAST,cACdI,OAA2B,EAC3BM,UAAkB;IAElB,IAAIP,cAAcC,UAAU;QAC1B,OAAOA,QACJO,GAAG,CAACC,CAAAA,cAAgB,CAAA;gBACnB,GAAGA,WAAW;gBACdR,SAASQ,YAAYR,OAAO,CAACS,MAAM,CAACN,CAAAA,SAClCN,eAAeM,QACZO,WAAW,GACXC,QAAQ,CAACL,WAAWI,WAAW;YAEtC,CAAA,GACCD,MAAM,CAACD,CAAAA,cAAeA,YAAYR,OAAO,CAACY,MAAM,GAAG;IACxD;IAEA,OAAOZ,QAAQS,MAAM,CAACN,CAAAA,SACpBN,eAAeM,QAASO,WAAW,GAAGC,QAAQ,CAACL,WAAWI,WAAW;AAEzE;AAEO,SAASf,MAAMS,KAAa,EAAES,GAAW,EAAEC,GAAW;IAC3D,OAAOC,KAAKF,GAAG,CAACE,KAAKD,GAAG,CAACV,OAAOS,MAAMC;AACxC"}