@simplybusiness/mobius 5.16.0 → 5.17.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 (28) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/cjs/components/Combobox/Combobox.js +7 -6
  3. package/dist/cjs/components/Combobox/Combobox.js.map +1 -1
  4. package/dist/cjs/components/Combobox/Listbox.js +3 -1
  5. package/dist/cjs/components/Combobox/Listbox.js.map +1 -1
  6. package/dist/cjs/components/Combobox/Option.js +5 -2
  7. package/dist/cjs/components/Combobox/Option.js.map +1 -1
  8. package/dist/cjs/tsconfig.tsbuildinfo +1 -1
  9. package/dist/esm/components/Combobox/Combobox.js +7 -6
  10. package/dist/esm/components/Combobox/Combobox.js.map +1 -1
  11. package/dist/esm/components/Combobox/Listbox.js +3 -1
  12. package/dist/esm/components/Combobox/Listbox.js.map +1 -1
  13. package/dist/esm/components/Combobox/Option.js +5 -2
  14. package/dist/esm/components/Combobox/Option.js.map +1 -1
  15. package/dist/esm/components/Combobox/types.js.map +1 -1
  16. package/dist/types/src/components/Combobox/Combobox.stories.d.ts +20 -0
  17. package/dist/types/src/components/Combobox/Listbox.d.ts +3 -2
  18. package/dist/types/src/components/Combobox/Option.d.ts +1 -1
  19. package/dist/types/src/components/Combobox/types.d.ts +2 -0
  20. package/package.json +1 -1
  21. package/src/components/Combobox/Combobox.css +3 -4
  22. package/src/components/Combobox/Combobox.mdx +31 -0
  23. package/src/components/Combobox/Combobox.stories.tsx +27 -0
  24. package/src/components/Combobox/Combobox.test.tsx +32 -0
  25. package/src/components/Combobox/Combobox.tsx +6 -4
  26. package/src/components/Combobox/Listbox.tsx +9 -1
  27. package/src/components/Combobox/Option.tsx +6 -1
  28. package/src/components/Combobox/types.tsx +4 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 5.17.0
4
+
5
+ ### Minor Changes
6
+
7
+ - a19a98d: Added `optionComponent` prop to `Combobox` component to allow customisation of how options are rendered. This allows for custom layouts, adding icons, or applying custom styling to individual options.
8
+
3
9
  ## 5.16.0
4
10
 
5
11
  ### Minor Changes
@@ -24,7 +24,7 @@ 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, ...otherProps } = props;
27
+ const { id, defaultValue, value, options, asyncOptions, delay, minSearchLength, onSelected, className, placeholder, icon, onBlur, onFocus, onChange, optionComponent, ...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);
@@ -142,7 +142,7 @@ const ComboboxInner = /*#__PURE__*/ (0, _react.forwardRef)((props, ref)=>{
142
142
  break;
143
143
  case "Enter":
144
144
  e.preventDefault();
145
- if (isOpen) {
145
+ if (showListbox) {
146
146
  const selectedOption = getHighlightedOption() || getFirstOption();
147
147
  if (selectedOption) {
148
148
  handleOptionSelect(selectedOption);
@@ -166,7 +166,7 @@ const ComboboxInner = /*#__PURE__*/ (0, _react.forwardRef)((props, ref)=>{
166
166
  value
167
167
  ]);
168
168
  const classes = (0, _dedupe.default)("mobius mobius-combobox", {
169
- "mobius-combobox--is-expanded": isOpen,
169
+ "mobius-combobox--is-expanded": showListbox,
170
170
  "mobius-combobox--is-loading": isLoading
171
171
  }, className);
172
172
  return /*#__PURE__*/ (0, _jsxruntime.jsxs)("div", {
@@ -196,8 +196,8 @@ const ComboboxInner = /*#__PURE__*/ (0, _react.forwardRef)((props, ref)=>{
196
196
  "aria-describedby": isLoading ? statusId : undefined,
197
197
  "aria-autocomplete": "list",
198
198
  "aria-haspopup": "listbox",
199
- "aria-controls": listboxId,
200
- "aria-expanded": isOpen,
199
+ "aria-controls": showListbox ? listboxId : undefined,
200
+ "aria-expanded": showListbox,
201
201
  "aria-activedescendant": highlightedIndex === -1 ? undefined : getHighlightedOptionId(),
202
202
  prefixInside: icon,
203
203
  ref: inputRef
@@ -207,7 +207,8 @@ const ComboboxInner = /*#__PURE__*/ (0, _react.forwardRef)((props, ref)=>{
207
207
  options: filteredOptions,
208
208
  highlightedIndex: highlightedIndex,
209
209
  highlightedGroupIndex: highlightedGroupIndex,
210
- onOptionSelect: handleOptionSelect
210
+ onOptionSelect: handleOptionSelect,
211
+ optionComponent: optionComponent
211
212
  })
212
213
  ]
213
214
  });
@@ -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 } 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 ...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\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 (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 },\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={listboxId}\n aria-expanded={isOpen}\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 />\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","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","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 } 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\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 },\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","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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"}
@@ -17,7 +17,7 @@ function _interop_require_default(obj) {
17
17
  default: obj
18
18
  };
19
19
  }
20
- const Listbox = ({ id, options, highlightedIndex, highlightedGroupIndex, onOptionSelect })=>{
20
+ const Listbox = ({ id, options, highlightedIndex, highlightedGroupIndex, onOptionSelect, optionComponent })=>{
21
21
  const classes = (0, _classnames.default)("mobius-combobox__list");
22
22
  function getOptionId(option, groupIndex, index) {
23
23
  if (typeof option === "object" && "id" in option && typeof option.id === "string") {
@@ -44,6 +44,7 @@ const Listbox = ({ id, options, highlightedIndex, highlightedGroupIndex, onOptio
44
44
  option: groupOption,
45
45
  isHighlighted: highlightedIndex === index && highlightedGroupIndex === groupIndex,
46
46
  onOptionSelect: onOptionSelect,
47
+ optionComponent: optionComponent,
47
48
  id: getOptionId(groupOption, groupIndex, index)
48
49
  }, `${id}-option-${groupIndex}-${index}`))
49
50
  ]
@@ -51,6 +52,7 @@ const Listbox = ({ id, options, highlightedIndex, highlightedGroupIndex, onOptio
51
52
  option: option,
52
53
  isHighlighted: highlightedIndex === index,
53
54
  onOptionSelect: onOptionSelect,
55
+ optionComponent: optionComponent,
54
56
  id: getOptionId(option, 0, index)
55
57
  }, index))
56
58
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/components/Combobox/Listbox.tsx"],"sourcesContent":["import classNames from \"classnames\";\nimport { Option } from \"./Option\";\nimport type { ComboboxOption, ComboboxOptions } 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};\n\nexport const Listbox = <T extends ComboboxOption>({\n id,\n options,\n highlightedIndex,\n highlightedGroupIndex,\n onOptionSelect,\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 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 id={getOptionId(option, 0, index)}\n />\n ))}\n </div>\n );\n};\n"],"names":["Listbox","id","options","highlightedIndex","highlightedGroupIndex","onOptionSelect","classes","classNames","getOptionId","option","groupIndex","index","isOptionGroup","div","role","className","map","ul","aria-labelledby","li","heading","groupOption","Option","isHighlighted"],"mappings":";;;;+BAaaA;;;eAAAA;;;;mEAbU;wBACA;uBAEO;;;;;;AAUvB,MAAMA,UAAU,CAA2B,EAChDC,EAAE,EACFC,OAAO,EACPC,gBAAgB,EAChBC,qBAAqB,EACrBC,cAAc,EACE;IAChB,MAAMC,UAAUC,IAAAA,mBAAU,EAAC;IAE3B,SAASC,YACPC,MAAsB,EACtBC,UAAkB,EAClBC,KAAa;QAEb,IACE,OAAOF,WAAW,YAClB,QAAQA,UACR,OAAOA,OAAOR,EAAE,KAAK,UACrB;YACA,OAAOQ,OAAOR,EAAE;QAClB;QACA,OAAOW,IAAAA,oBAAa,EAACV,WACjB,GAAGD,GAAG,QAAQ,EAAES,WAAW,CAAC,EAAEC,OAAO,GACrC,GAAGV,GAAG,QAAQ,EAAEU,OAAO;IAC7B;IAEA,qBACE,qBAACE;QAAIC,MAAK;QAAUb,IAAIA;QAAIc,WAAWT;kBACpCM,IAAAA,oBAAa,EAACV,WACXA,QAAQc,GAAG,CAAC,CAACP,QAAQC,2BACnB,sBAACO;gBACCH,MAAK;gBAELI,mBAAiB,GAAGjB,GAAG,OAAO,EAAES,YAAY;gBAC5CK,WAAU;;kCAEV,qBAACI;wBACCL,MAAK;wBACLb,IAAI,GAAGA,GAAG,OAAO,EAAES,YAAY;wBAC/BK,WAAU;kCAETN,OAAOW,OAAO;;oBAEhBX,OAAOP,OAAO,CAACc,GAAG,CAAC,CAACK,aAAaV,sBAChC,qBAACW,cAAM;4BAELb,QAAQY;4BACRE,eACEpB,qBAAqBQ,SACrBP,0BAA0BM;4BAE5BL,gBAAgBA;4BAChBJ,IAAIO,YAAYa,aAAaX,YAAYC;2BAPpC,GAAGV,GAAG,QAAQ,EAAES,WAAW,CAAC,EAAEC,OAAO;;eAbzCF,OAAOW,OAAO,KAyBvBlB,QAAQc,GAAG,CAAC,CAACP,QAAQE,sBACnB,qBAACW,cAAM;gBAELb,QAAQA;gBACRc,eAAepB,qBAAqBQ;gBACpCN,gBAAgBA;gBAChBJ,IAAIO,YAAYC,QAAQ,GAAGE;eAJtBA;;AASnB"}
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"}
@@ -17,7 +17,7 @@ function _interop_require_default(obj) {
17
17
  default: obj
18
18
  };
19
19
  }
20
- const Option = ({ option, isHighlighted, onOptionSelect, id })=>{
20
+ const Option = ({ option, isHighlighted, onOptionSelect, optionComponent: OptionComponent, id })=>{
21
21
  const optionRef = (0, _react.useRef)(null);
22
22
  (0, _react.useEffect)(()=>{
23
23
  if (isHighlighted && optionRef.current && optionRef.current.scrollIntoView) {
@@ -37,7 +37,10 @@ const Option = ({ option, isHighlighted, onOptionSelect, id })=>{
37
37
  className: (0, _dedupe.default)("mobius-combobox__option", {
38
38
  "mobius-combobox__option--is-highlighted": isHighlighted
39
39
  }),
40
- children: (0, _utils.getOptionLabel)(option)
40
+ children: OptionComponent ? /*#__PURE__*/ (0, _jsxruntime.jsx)(OptionComponent, {
41
+ option: option,
42
+ isHighlighted: isHighlighted
43
+ }) : (0, _utils.getOptionLabel)(option)
41
44
  }, (0, _utils.getOptionValue)(option));
42
45
  };
43
46
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/components/Combobox/Option.tsx"],"sourcesContent":["import { useEffect, useRef } from \"react\";\nimport classNames from \"classnames/dedupe\";\nimport { getOptionValue, getOptionLabel } from \"./utils\";\nimport type { ComboboxOption, ComboboxOptionProps } from \"./types\";\n\nexport const Option = <T extends ComboboxOption>({\n option,\n isHighlighted,\n onOptionSelect,\n id,\n}: ComboboxOptionProps<T>) => {\n const optionRef = useRef<HTMLLIElement>(null);\n\n useEffect(() => {\n if (\n isHighlighted &&\n optionRef.current &&\n optionRef.current.scrollIntoView\n ) {\n optionRef.current.scrollIntoView({ block: \"nearest\" });\n }\n }, [isHighlighted]);\n\n return (\n <li\n ref={optionRef}\n role=\"option\"\n key={getOptionValue(option)}\n id={id}\n aria-selected={isHighlighted}\n onMouseDown={() => onOptionSelect(option)}\n className={classNames(\"mobius-combobox__option\", {\n \"mobius-combobox__option--is-highlighted\": isHighlighted,\n })}\n >\n {getOptionLabel(option)}\n </li>\n );\n};\n"],"names":["Option","option","isHighlighted","onOptionSelect","id","optionRef","useRef","useEffect","current","scrollIntoView","block","li","ref","role","aria-selected","onMouseDown","className","classNames","getOptionLabel","getOptionValue"],"mappings":";;;;+BAKaA;;;eAAAA;;;;uBALqB;+DACX;uBACwB;;;;;;AAGxC,MAAMA,SAAS,CAA2B,EAC/CC,MAAM,EACNC,aAAa,EACbC,cAAc,EACdC,EAAE,EACqB;IACvB,MAAMC,YAAYC,IAAAA,aAAM,EAAgB;IAExCC,IAAAA,gBAAS,EAAC;QACR,IACEL,iBACAG,UAAUG,OAAO,IACjBH,UAAUG,OAAO,CAACC,cAAc,EAChC;YACAJ,UAAUG,OAAO,CAACC,cAAc,CAAC;gBAAEC,OAAO;YAAU;QACtD;IACF,GAAG;QAACR;KAAc;IAElB,qBACE,qBAACS;QACCC,KAAKP;QACLQ,MAAK;QAELT,IAAIA;QACJU,iBAAeZ;QACfa,aAAa,IAAMZ,eAAeF;QAClCe,WAAWC,IAAAA,eAAU,EAAC,2BAA2B;YAC/C,2CAA2Cf;QAC7C;kBAECgB,IAAAA,qBAAc,EAACjB;OARXkB,IAAAA,qBAAc,EAAClB;AAW1B"}
1
+ {"version":3,"sources":["../../../../src/components/Combobox/Option.tsx"],"sourcesContent":["import { useEffect, useRef } from \"react\";\nimport classNames from \"classnames/dedupe\";\nimport { getOptionValue, getOptionLabel } from \"./utils\";\nimport type { ComboboxOption, ComboboxOptionProps } from \"./types\";\n\nexport const Option = <T extends ComboboxOption>({\n option,\n isHighlighted,\n onOptionSelect,\n optionComponent: OptionComponent,\n id,\n}: ComboboxOptionProps<T>) => {\n const optionRef = useRef<HTMLLIElement>(null);\n\n useEffect(() => {\n if (\n isHighlighted &&\n optionRef.current &&\n optionRef.current.scrollIntoView\n ) {\n optionRef.current.scrollIntoView({ block: \"nearest\" });\n }\n }, [isHighlighted]);\n\n return (\n <li\n ref={optionRef}\n role=\"option\"\n key={getOptionValue(option)}\n id={id}\n aria-selected={isHighlighted}\n onMouseDown={() => onOptionSelect(option)}\n className={classNames(\"mobius-combobox__option\", {\n \"mobius-combobox__option--is-highlighted\": isHighlighted,\n })}\n >\n {OptionComponent ? (\n <OptionComponent option={option} isHighlighted={isHighlighted} />\n ) : (\n getOptionLabel(option)\n )}\n </li>\n );\n};\n"],"names":["Option","option","isHighlighted","onOptionSelect","optionComponent","OptionComponent","id","optionRef","useRef","useEffect","current","scrollIntoView","block","li","ref","role","aria-selected","onMouseDown","className","classNames","getOptionLabel","getOptionValue"],"mappings":";;;;+BAKaA;;;eAAAA;;;;uBALqB;+DACX;uBACwB;;;;;;AAGxC,MAAMA,SAAS,CAA2B,EAC/CC,MAAM,EACNC,aAAa,EACbC,cAAc,EACdC,iBAAiBC,eAAe,EAChCC,EAAE,EACqB;IACvB,MAAMC,YAAYC,IAAAA,aAAM,EAAgB;IAExCC,IAAAA,gBAAS,EAAC;QACR,IACEP,iBACAK,UAAUG,OAAO,IACjBH,UAAUG,OAAO,CAACC,cAAc,EAChC;YACAJ,UAAUG,OAAO,CAACC,cAAc,CAAC;gBAAEC,OAAO;YAAU;QACtD;IACF,GAAG;QAACV;KAAc;IAElB,qBACE,qBAACW;QACCC,KAAKP;QACLQ,MAAK;QAELT,IAAIA;QACJU,iBAAed;QACfe,aAAa,IAAMd,eAAeF;QAClCiB,WAAWC,IAAAA,eAAU,EAAC,2BAA2B;YAC/C,2CAA2CjB;QAC7C;kBAECG,gCACC,qBAACA;YAAgBJ,QAAQA;YAAQC,eAAeA;aAEhDkB,IAAAA,qBAAc,EAACnB;OAXZoB,IAAAA,qBAAc,EAACpB;AAe1B"}