downshift-input.tsx 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. import { useState } from 'react'
  2. import { useCombobox } from 'downshift'
  3. import classnames from 'classnames'
  4. type DownshiftInputProps = {
  5. items: string[]
  6. inputValue: string
  7. setValue: React.Dispatch<React.SetStateAction<string>>
  8. } & React.InputHTMLAttributes<HTMLInputElement>
  9. const filterItemsByInputValue = (
  10. items: DownshiftInputProps['items'],
  11. inputValue: DownshiftInputProps['inputValue']
  12. ) => items.filter(item => item.toLowerCase().includes(inputValue.toLowerCase()))
  13. function DownshiftInput({
  14. items,
  15. inputValue,
  16. placeholder,
  17. setValue,
  18. }: DownshiftInputProps) {
  19. const [inputItems, setInputItems] = useState(items)
  20. const {
  21. isOpen,
  22. getMenuProps,
  23. getInputProps,
  24. getComboboxProps,
  25. getItemProps,
  26. openMenu,
  27. selectedItem,
  28. } = useCombobox({
  29. inputValue,
  30. items: inputItems,
  31. initialSelectedItem: inputValue,
  32. onSelectedItemChange: ({ selectedItem }) => {
  33. setValue(selectedItem ?? '')
  34. },
  35. onInputValueChange: ({ inputValue = '' }) => {
  36. setInputItems(filterItemsByInputValue(items, inputValue))
  37. },
  38. onStateChange: ({ type }) => {
  39. if (type === useCombobox.stateChangeTypes.FunctionOpenMenu) {
  40. setInputItems(filterItemsByInputValue(items, inputValue))
  41. }
  42. },
  43. })
  44. return (
  45. <div
  46. className={classnames(
  47. 'ui-select-container ui-select-bootstrap dropdown',
  48. {
  49. open: isOpen && inputItems.length,
  50. }
  51. )}
  52. >
  53. <div {...getComboboxProps()} className="form-group mb-2">
  54. <input
  55. {...getInputProps({
  56. onChange: (event: React.ChangeEvent<HTMLInputElement>) => {
  57. setValue(event.target.value)
  58. },
  59. onFocus: () => {
  60. if (!isOpen) {
  61. openMenu()
  62. }
  63. },
  64. })}
  65. className="form-control"
  66. type="text"
  67. placeholder={placeholder}
  68. />
  69. </div>
  70. <ul
  71. {...getMenuProps()}
  72. className="ui-select-choices ui-select-choices-content ui-select-dropdown dropdown-menu"
  73. >
  74. {inputItems.map((item, index) => (
  75. <li
  76. className="ui-select-choices-group"
  77. key={`${item}${index}`}
  78. {...getItemProps({ item, index })}
  79. >
  80. <div
  81. className={classnames('ui-select-choices-row', {
  82. active: selectedItem === item,
  83. })}
  84. >
  85. <span className="ui-select-choices-row-inner">
  86. <span>{item}</span>
  87. </span>
  88. </div>
  89. </li>
  90. ))}
  91. </ul>
  92. </div>
  93. )
  94. }
  95. export default DownshiftInput