@redis-ui/components 42.8.0 → 43.2.1

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 (228) hide show
  1. package/dist/AutoCompleteSelect/AutoCompleteSelect.cjs +7 -1
  2. package/dist/AutoCompleteSelect/AutoCompleteSelect.js +7 -1
  3. package/dist/AutoCompleteSelect/hooks/useAutoCompleteSelect.cjs +7 -1
  4. package/dist/AutoCompleteSelect/hooks/useAutoCompleteSelect.d.ts +8 -1
  5. package/dist/AutoCompleteSelect/hooks/useAutoCompleteSelect.js +7 -1
  6. package/dist/BoxSelectionGroup/components/Item/components/BoxStateIndicator/BoxStateIndicator.cjs +8 -4
  7. package/dist/BoxSelectionGroup/components/Item/components/BoxStateIndicator/BoxStateIndicator.js +8 -4
  8. package/dist/BoxSelectionGroup/components/Item/components/Compose/Compose.style.cjs +18 -4
  9. package/dist/BoxSelectionGroup/components/Item/components/Compose/Compose.style.js +18 -4
  10. package/dist/BoxSelectionGroup/hooks/useBoxSelectionGroup.cjs +3 -1
  11. package/dist/BoxSelectionGroup/hooks/useBoxSelectionGroup.js +3 -1
  12. package/dist/Button/Button.style.cjs +4 -1
  13. package/dist/Button/Button.style.js +4 -1
  14. package/dist/Button/Button.style.utils.cjs +16 -1
  15. package/dist/Button/Button.style.utils.js +16 -1
  16. package/dist/Button/Button.types.cjs +1 -1
  17. package/dist/Button/Button.types.d.ts +1 -1
  18. package/dist/Button/Button.types.js +1 -1
  19. package/dist/Button/CopyToClipboardButton/CopyToClipboardButton.cjs +59 -0
  20. package/dist/Button/CopyToClipboardButton/CopyToClipboardButton.d.ts +4 -0
  21. package/dist/Button/CopyToClipboardButton/CopyToClipboardButton.js +57 -0
  22. package/dist/Button/CopyToClipboardButton/CopyToClipboardButton.style.cjs +40 -0
  23. package/dist/Button/CopyToClipboardButton/CopyToClipboardButton.style.d.ts +8 -0
  24. package/dist/Button/CopyToClipboardButton/CopyToClipboardButton.style.js +38 -0
  25. package/dist/Button/CopyToClipboardButton/CopyToClipboardButton.types.d.ts +5 -0
  26. package/dist/Button/TextButton/TextButton.style.cjs +4 -1
  27. package/dist/Button/TextButton/TextButton.style.js +4 -1
  28. package/dist/Button/TextButton/TextButton.types.cjs +1 -1
  29. package/dist/Button/TextButton/TextButton.types.d.ts +1 -1
  30. package/dist/Button/TextButton/TextButton.types.js +1 -1
  31. package/dist/Button/index.d.ts +2 -0
  32. package/dist/Checkbox/components/Label/Label.style.cjs +15 -3
  33. package/dist/Checkbox/components/Label/Label.style.js +15 -3
  34. package/dist/Chip/components/CloseButton/CloseButton.cjs +3 -1
  35. package/dist/Chip/components/CloseButton/CloseButton.js +3 -1
  36. package/dist/Chip/components/Compose/Compose.style.cjs +4 -1
  37. package/dist/Chip/components/Compose/Compose.style.js +4 -1
  38. package/dist/Drawer/components/Description/Description.cjs +3 -1
  39. package/dist/Drawer/components/Description/Description.js +3 -1
  40. package/dist/Helpers/contexts/Popper/PopperCollisionBoundaryManager.cjs +3 -1
  41. package/dist/Helpers/contexts/Popper/PopperCollisionBoundaryManager.js +3 -1
  42. package/dist/Helpers/contexts/PrimitiveContextState.cjs +28 -16
  43. package/dist/Helpers/contexts/PrimitiveContextState.js +28 -16
  44. package/dist/Helpers/contexts/SharedId.context.cjs +9 -5
  45. package/dist/Helpers/contexts/SharedId.context.js +9 -5
  46. package/dist/Helpers/css.utils.cjs +18 -4
  47. package/dist/Helpers/css.utils.d.ts +15 -3
  48. package/dist/Helpers/css.utils.js +18 -4
  49. package/dist/Helpers/hooks/useScrollable.cjs +3 -1
  50. package/dist/Helpers/hooks/useScrollable.js +3 -1
  51. package/dist/Helpers/react.utils.cjs +6 -2
  52. package/dist/Helpers/react.utils.js +6 -2
  53. package/dist/HighlightedIcon/HighlightedIcon.cjs +26 -0
  54. package/dist/HighlightedIcon/HighlightedIcon.d.ts +3 -0
  55. package/dist/HighlightedIcon/HighlightedIcon.js +26 -0
  56. package/dist/HighlightedIcon/HighlightedIcon.style.cjs +49 -0
  57. package/dist/HighlightedIcon/HighlightedIcon.style.d.ts +5 -0
  58. package/dist/HighlightedIcon/HighlightedIcon.style.js +47 -0
  59. package/dist/HighlightedIcon/HighlightedIcon.types.d.ts +8 -0
  60. package/dist/HighlightedIcon/index.d.ts +3 -0
  61. package/dist/Inputs/QuantityCounter/components/InputGroup/components/ValueLabel/ValueLabel.cjs +9 -5
  62. package/dist/Inputs/QuantityCounter/components/InputGroup/components/ValueLabel/ValueLabel.js +9 -5
  63. package/dist/Inputs/components/Compose/Compose.style.cjs +29 -6
  64. package/dist/Inputs/components/Compose/Compose.style.js +29 -6
  65. package/dist/Inputs/hooks/numericInput/numericInput.utils.cjs +12 -4
  66. package/dist/Inputs/hooks/numericInput/numericInput.utils.js +12 -4
  67. package/dist/Inputs/hooks/numericInput/useNumericInput.cjs +15 -3
  68. package/dist/Inputs/hooks/numericInput/useNumericInput.js +15 -3
  69. package/dist/Loader/Loader.cjs +1 -0
  70. package/dist/Loader/Loader.js +1 -0
  71. package/dist/Menu/components/Content/components/Item/Components/Compose/Compose.style.cjs +19 -4
  72. package/dist/Menu/components/Content/components/Item/Components/Compose/Compose.style.js +19 -4
  73. package/dist/Menu/components/Content/components/Label/components/Compose/Compose.style.cjs +4 -1
  74. package/dist/Menu/components/Content/components/Label/components/Compose/Compose.style.js +4 -1
  75. package/dist/Modal/components/Content/components/Compose/Compose.cjs +3 -1
  76. package/dist/Modal/components/Content/components/Compose/Compose.js +3 -1
  77. package/dist/Modal/components/Content/components/Description/Description.cjs +3 -1
  78. package/dist/Modal/components/Content/components/Description/Description.js +3 -1
  79. package/dist/MultiSelect/components/Compose/hooks/useMultiSelectContextApi.cjs +3 -1
  80. package/dist/MultiSelect/components/Compose/hooks/useMultiSelectContextApi.js +3 -1
  81. package/dist/MultiSelect/components/Trigger/components/MultiValue/MultiValue.cjs +3 -1
  82. package/dist/MultiSelect/components/Trigger/components/MultiValue/MultiValue.js +3 -1
  83. package/dist/Overflow/Overflow.cjs +3 -1
  84. package/dist/Overflow/Overflow.js +3 -1
  85. package/dist/Overflow/Overflow.utils.cjs +15 -6
  86. package/dist/Overflow/Overflow.utils.js +15 -6
  87. package/dist/Overflow/components/OverflowContainer/OverflowContainer.cjs +3 -1
  88. package/dist/Overflow/components/OverflowContainer/OverflowContainer.js +3 -1
  89. package/dist/Pagination/components/PageSizeSelect.cjs +3 -1
  90. package/dist/Pagination/components/PageSizeSelect.js +3 -1
  91. package/dist/Popover/components/Content/Content.cjs +3 -1
  92. package/dist/Popover/components/Content/Content.js +3 -1
  93. package/dist/Popover/components/Content/components/Footer/Footer.cjs +3 -1
  94. package/dist/Popover/components/Content/components/Footer/Footer.js +3 -1
  95. package/dist/RadioGroup/components/Item/components/Label/Label.style.cjs +15 -3
  96. package/dist/RadioGroup/components/Item/components/Label/Label.style.js +15 -3
  97. package/dist/ScreenReaderAnnounce/ScreenReaderAnnounce.cjs +3 -1
  98. package/dist/ScreenReaderAnnounce/ScreenReaderAnnounce.js +3 -1
  99. package/dist/Section/components/Header/components/CollapseButton/CollapseButton.cjs +3 -1
  100. package/dist/Section/components/Header/components/CollapseButton/CollapseButton.js +3 -1
  101. package/dist/Select/components/Content/components/Option/components/Compose/Compose.style.cjs +16 -4
  102. package/dist/Select/components/Content/components/Option/components/Compose/Compose.style.js +16 -4
  103. package/dist/Select/components/Content/components/OptionList/OptionList.cjs +6 -4
  104. package/dist/Select/components/Content/components/OptionList/OptionList.js +6 -4
  105. package/dist/Select/components/Content/components/OptionList/Virtual.cjs +9 -5
  106. package/dist/Select/components/Content/components/OptionList/Virtual.js +9 -5
  107. package/dist/Select/components/Context/hooks/useSearch.cjs +3 -1
  108. package/dist/Select/components/Context/hooks/useSearch.js +3 -1
  109. package/dist/Select/components/Trigger/components/Compose/Compose.style.cjs +33 -7
  110. package/dist/Select/components/Trigger/components/Compose/Compose.style.js +33 -7
  111. package/dist/SideBar/components/Item/Item.style.cjs +14 -3
  112. package/dist/SideBar/components/Item/Item.style.js +14 -3
  113. package/dist/Skeleton/components/Circle/Circle.cjs +1 -1
  114. package/dist/Skeleton/components/Circle/Circle.js +1 -1
  115. package/dist/Skeleton/components/Square/Square.cjs +1 -1
  116. package/dist/Skeleton/components/Square/Square.js +1 -1
  117. package/dist/Slider/components/Label/Compose/Compose.cjs +3 -1
  118. package/dist/Slider/components/Label/Compose/Compose.js +3 -1
  119. package/dist/Slider/components/Mark/Compose/Compose.cjs +3 -1
  120. package/dist/Slider/components/Mark/Compose/Compose.js +3 -1
  121. package/dist/Slider/hooks/useOffset.cjs +3 -1
  122. package/dist/Slider/hooks/useOffset.js +3 -1
  123. package/dist/Stepper/Stepper.cjs +14 -5
  124. package/dist/Stepper/Stepper.d.ts +2 -0
  125. package/dist/Stepper/Stepper.js +14 -5
  126. package/dist/Stepper/Stepper.utils.cjs +12 -0
  127. package/dist/Stepper/Stepper.utils.d.ts +2 -0
  128. package/dist/Stepper/Stepper.utils.js +12 -0
  129. package/dist/Stepper/components/Compose/Compose.d.ts +1 -0
  130. package/dist/Stepper/components/Step/Step.cjs +2 -0
  131. package/dist/Stepper/components/Step/Step.d.ts +1 -0
  132. package/dist/Stepper/components/Step/Step.js +2 -0
  133. package/dist/Stepper/components/Step/components/Compose/Compose.cjs +5 -10
  134. package/dist/Stepper/components/Step/components/Compose/Compose.js +5 -10
  135. package/dist/Stepper/components/Step/components/Separator/Separator.cjs +15 -0
  136. package/dist/Stepper/components/Step/components/Separator/Separator.d.ts +3 -0
  137. package/dist/Stepper/components/Step/components/Separator/Separator.js +15 -0
  138. package/dist/Stepper/components/Step/components/Separator/Separator.style.cjs +24 -0
  139. package/dist/Stepper/components/Step/components/Separator/Separator.style.d.ts +1 -0
  140. package/dist/Stepper/components/Step/components/Separator/Separator.style.js +22 -0
  141. package/dist/Stepper/components/Step/components/Separator/Separator.types.d.ts +5 -0
  142. package/dist/Stepper/hooks/useStepperInteractive.cjs +12 -4
  143. package/dist/Stepper/hooks/useStepperInteractive.js +12 -4
  144. package/dist/Switch/components/Switcher/Switcher.cjs +3 -1
  145. package/dist/Switch/components/Switcher/Switcher.js +3 -1
  146. package/dist/Switch/components/Switcher/Switcher.style.cjs +31 -8
  147. package/dist/Switch/components/Switcher/Switcher.style.js +31 -8
  148. package/dist/Tabs/components/TabBar/components/Trigger/components/Marker/Marker.style.cjs +24 -5
  149. package/dist/Tabs/components/TabBar/components/Trigger/components/Marker/Marker.style.js +24 -5
  150. package/dist/Tabs/components/TabBar/components/Trigger/components/Tab/Tab.style.cjs +24 -5
  151. package/dist/Tabs/components/TabBar/components/Trigger/components/Tab/Tab.style.js +24 -5
  152. package/dist/ThemeModeSwitch/useThemeModeSwitch.cjs +6 -2
  153. package/dist/ThemeModeSwitch/useThemeModeSwitch.js +6 -2
  154. package/dist/Toast/core/content.helper.cjs +8 -4
  155. package/dist/Toast/core/content.helper.js +8 -4
  156. package/dist/Tooltip/components/Content/Content.cjs +3 -1
  157. package/dist/Tooltip/components/Content/Content.js +3 -1
  158. package/dist/TreeView/TreeView.cjs +3 -1
  159. package/dist/TreeView/TreeView.js +3 -1
  160. package/dist/TreeView/components/TreeItem/components/Compose/Compose.cjs +6 -4
  161. package/dist/TreeView/components/TreeItem/components/Compose/Compose.js +6 -4
  162. package/dist/Typography/Typography.types.cjs +4 -0
  163. package/dist/Typography/Typography.types.d.ts +2 -1
  164. package/dist/Typography/Typography.types.js +4 -0
  165. package/dist/index.cjs +8 -0
  166. package/dist/index.d.ts +1 -0
  167. package/dist/index.js +98 -90
  168. package/package.json +10 -9
  169. package/skills/redis-ui-components/SKILL.md +126 -0
  170. package/skills/redis-ui-components/references/ActionIconButton.md +96 -0
  171. package/skills/redis-ui-components/references/AppBar.md +161 -0
  172. package/skills/redis-ui-components/references/AppSelectionMenu.md +184 -0
  173. package/skills/redis-ui-components/references/AutoCompleteSelect.md +193 -0
  174. package/skills/redis-ui-components/references/Badge.md +77 -0
  175. package/skills/redis-ui-components/references/Banner.md +563 -0
  176. package/skills/redis-ui-components/references/BoxSelectionGroup.md +487 -0
  177. package/skills/redis-ui-components/references/Breadcrumbs.md +214 -0
  178. package/skills/redis-ui-components/references/Button.md +169 -0
  179. package/skills/redis-ui-components/references/ButtonGroup.md +126 -0
  180. package/skills/redis-ui-components/references/Card.md +56 -0
  181. package/skills/redis-ui-components/references/Checkbox.md +171 -0
  182. package/skills/redis-ui-components/references/Chip.md +154 -0
  183. package/skills/redis-ui-components/references/ChipList.md +307 -0
  184. package/skills/redis-ui-components/references/CopyToClipboardButton.md +47 -0
  185. package/skills/redis-ui-components/references/CountryFlag.md +57 -0
  186. package/skills/redis-ui-components/references/Drawer.md +298 -0
  187. package/skills/redis-ui-components/references/Filters.md +162 -0
  188. package/skills/redis-ui-components/references/FlexDivider.md +152 -0
  189. package/skills/redis-ui-components/references/FlexGroup.md +149 -0
  190. package/skills/redis-ui-components/references/FlexItem.md +58 -0
  191. package/skills/redis-ui-components/references/FlexSplit.md +37 -0
  192. package/skills/redis-ui-components/references/FormField.md +678 -0
  193. package/skills/redis-ui-components/references/IconButton.md +63 -0
  194. package/skills/redis-ui-components/references/Input.md +295 -0
  195. package/skills/redis-ui-components/references/KeyValueList.md +501 -0
  196. package/skills/redis-ui-components/references/Label.md +238 -0
  197. package/skills/redis-ui-components/references/Link.md +402 -0
  198. package/skills/redis-ui-components/references/Loader.md +100 -0
  199. package/skills/redis-ui-components/references/Menu.md +988 -0
  200. package/skills/redis-ui-components/references/MidBar.md +358 -0
  201. package/skills/redis-ui-components/references/Modal.md +525 -0
  202. package/skills/redis-ui-components/references/MoreInfoIcon.md +119 -0
  203. package/skills/redis-ui-components/references/MultiSelect.md +558 -0
  204. package/skills/redis-ui-components/references/NumericInput.md +322 -0
  205. package/skills/redis-ui-components/references/Overflow.md +127 -0
  206. package/skills/redis-ui-components/references/Pagination.md +151 -0
  207. package/skills/redis-ui-components/references/PasswordInput.md +262 -0
  208. package/skills/redis-ui-components/references/Popover.md +868 -0
  209. package/skills/redis-ui-components/references/ProfileIcon.md +65 -0
  210. package/skills/redis-ui-components/references/ProgressBar.md +103 -0
  211. package/skills/redis-ui-components/references/QuantityCounter.md +555 -0
  212. package/skills/redis-ui-components/references/RadioGroup.md +265 -0
  213. package/skills/redis-ui-components/references/ScreenReaderAnnounce.md +147 -0
  214. package/skills/redis-ui-components/references/SearchBar.md +242 -0
  215. package/skills/redis-ui-components/references/SearchInput.md +213 -0
  216. package/skills/redis-ui-components/references/Section.md +349 -0
  217. package/skills/redis-ui-components/references/Select.md +517 -0
  218. package/skills/redis-ui-components/references/SideBar.md +468 -0
  219. package/skills/redis-ui-components/references/Slider.md +398 -0
  220. package/skills/redis-ui-components/references/Stepper.md +288 -0
  221. package/skills/redis-ui-components/references/Switch.md +193 -0
  222. package/skills/redis-ui-components/references/Tabs.md +383 -0
  223. package/skills/redis-ui-components/references/TextArea.md +139 -0
  224. package/skills/redis-ui-components/references/TextButton.md +217 -0
  225. package/skills/redis-ui-components/references/Toast.md +399 -0
  226. package/skills/redis-ui-components/references/ToggleButton.md +163 -0
  227. package/skills/redis-ui-components/references/Tooltip.md +636 -0
  228. package/skills/redis-ui-components/references/Typography.md +323 -0
@@ -0,0 +1,558 @@
1
+ # MultiSelect
2
+
3
+ A multi-select dropdown built on the Select primitives. Supports chip-based multi-value selection, searchable lists, virtualized rendering, select-all actions, and compound composition for custom trigger, content, and option layouts.
4
+
5
+ ## Import
6
+
7
+ ```tsx
8
+ import { MultiSelect } from '@redislabsdev/redis-ui-components';
9
+ ```
10
+
11
+ ## Props
12
+
13
+ | Prop | Type | Default | Description |
14
+ |------|------|---------|-------------|
15
+ | options | `TOption[]` (extends `SelectOption`) | *required* | Array of selectable options. |
16
+ | disabled | `boolean` | `false` | Disables the select. |
17
+ | defaultOpen | `boolean` | - | Uncontrolled initial open state. |
18
+ | open | `boolean` | - | Controlled open state. |
19
+ | onOpenChange | `(open: boolean) => void` | - | Called when the dropdown open state changes. |
20
+ | defaultValue | `TValue[]` | - | Uncontrolled initial selected values. |
21
+ | value | `TValue[]` | - | Controlled selected values. |
22
+ | onChange | `(value: TValue[]) => void` | - | Called when selected values change. |
23
+ | customCompare | `SelectCompareHandler<TOption>` | - | Custom search comparison handler for filtering options. |
24
+ | error | `string \| ReactElement` | - | Error message or element. Enables error state and tooltip content. |
25
+ | valid | `boolean` | - | Enables valid status. |
26
+ | placeholder | `ReactNode` | `'Select...'` | Placeholder text shown in the trigger when nothing is selected. |
27
+ | valueRender | `SelectValueRender<TOption>` | - | Custom render function for trigger chips and dropdown option values. |
28
+ | searchable | `boolean` | `false` | Enables the search input in the dropdown content. |
29
+ | placement | `'top' \| 'bottom' \| 'left' \| 'right'` | - | Dropdown placement relative to the trigger. |
30
+ | maxVisibleItems | `number` | - | Maximum visible items before scrolling. |
31
+ | virtualized | `boolean` | `false` | Enables virtualized rendering for long lists. |
32
+ | contentWidth | `string` | - | Custom width for the dropdown content. |
33
+ | allowSelectAll | `boolean` | `false` | Adds the select/unselect-all footer action. |
34
+ | allowReset | `boolean` | `true` | Shows the trigger reset button. |
35
+ | loading | `boolean` | `false` | Shows the loading indicator in the trigger. |
36
+ | id | `string` | - | Custom trigger id for label and accessibility wiring. |
37
+ | className | `string` | - | Class name passed through to the trigger wrapper. |
38
+
39
+ The component also extends Radix UI `SelectTriggerProps` through `RestSelectTriggerComposeProps` (excluding `children`, `placeholder`, `asChild`, and `onChange`).
40
+
41
+ ### SelectOption Type
42
+
43
+ | Field | Type | Description |
44
+ |-------|------|-------------|
45
+ | value | `string` | *required* - Unique option value. |
46
+ | label | `string` | Optional display label. |
47
+ | icon | `IconType \| ComponentType` | Optional icon component. |
48
+ | disabled | `boolean` | Whether the option is disabled. |
49
+ | hidden | `boolean` | Whether the option is hidden. |
50
+
51
+ ## Sub-components
52
+
53
+ - `MultiSelect.Compose` - Context provider that wires up selection state, validation state, and the trigger/content pair. Use this when building custom layouts or when you need `allowResetDisabledItems`.
54
+ - `MultiSelect.Trigger` - Default trigger that renders selected values as chips, plus reset, status, loading, and arrow indicators.
55
+ - `MultiSelect.Trigger.Compose` - Composable trigger wrapper for custom trigger UI. Use `customContainer` when you need to replace the default combobox-style button.
56
+ - `MultiSelect.Trigger.MultiValue` - Renders selected values as chips and the placeholder when nothing is selected. Accepts `allowRemove` to hide chip remove controls.
57
+ - `MultiSelect.Trigger.ResetButton` - Clears the current selection.
58
+ - `MultiSelect.Trigger.StatusIndicator` - Shows the validation state indicator.
59
+ - `MultiSelect.Trigger.LoadingIndicator` - Shows the loading indicator.
60
+ - `MultiSelect.Trigger.Arrow` - Dropdown arrow indicator.
61
+ - `MultiSelect.Trigger.ErrorIcon` - Deprecated alias for `MultiSelect.Trigger.StatusIndicator`.
62
+ - `MultiSelect.Content` - Default dropdown content with optional search, option list, custom `optionValueRender` / `optionComponent`, and select-all footer.
63
+ - `MultiSelect.Content.Compose` - Composable content wrapper for custom dropdown layouts and static elements.
64
+ - `MultiSelect.Content.Search` - Search input for filtering options.
65
+ - `MultiSelect.Content.OptionList` - Renders the list of options.
66
+ - `MultiSelect.Content.Footer` - Footer container used for select-all and custom footer content.
67
+ - `MultiSelect.Content.SelectAll` - Toggle button that selects or unselects all options. Accepts `selectAllTitle` and `unselectAllTitle`.
68
+ - `MultiSelect.Option` - Default multi-select option row.
69
+ - `MultiSelect.Option.Compose` - Composable option wrapper for custom option rendering. This is the required root for custom option components.
70
+ - `MultiSelect.Option.Content` - Content area inside an option row.
71
+ - `MultiSelect.Option.Indicator` - Selection indicator rendered for each option.
72
+
73
+ ## Hooks
74
+
75
+ - `useMultiSelectContext` - Reads multi-select state inside custom trigger, content, or option components. The context exposes `getSelectAllApi(inSearchResults?)` for select/unselect-all flows, and the returned API should be memoized before reuse. `selectAll()` is deprecated.
76
+
77
+ ## Examples
78
+
79
+ ### MultiSelect
80
+
81
+ #### Playground
82
+
83
+ ```tsx
84
+ import { MultiSelect } from '@redislabsdev/redis-ui-components';
85
+
86
+ <MultiSelect
87
+ options={options}
88
+ searchable
89
+ onChange={(value) => console.log(value)}
90
+ onOpenChange={(open) => console.log(open)}
91
+ />
92
+ ```
93
+
94
+ #### WithLabel
95
+
96
+ > Component doesn't have internal label or any other external decoration elements.
97
+ > For these purposes, use `FormField`.
98
+ > If a custom id is specified, `FormField` inherits it; otherwise the label is auto-connected to the select with a generated id.
99
+ > Custom ids must start with a letter to work correctly.
100
+
101
+ ```tsx
102
+ import { FormField, MultiSelect } from '@redislabsdev/redis-ui-components';
103
+
104
+ <FormField label="FormField Label with custom id" additionalText="Additional text">
105
+ <MultiSelect
106
+ options={options}
107
+ placeholder="MultiSelect placeholder"
108
+ id="custom-id"
109
+ onChange={(value) => console.log(value)}
110
+ onOpenChange={(open) => console.log(open)}
111
+ />
112
+ </FormField>
113
+ ```
114
+
115
+ #### Status
116
+
117
+ > Adding `valid` enables valid status.
118
+ > Adding text to `error` enables error state and shows the tooltip on hover.
119
+ > Error status overrides valid status.
120
+
121
+ ```tsx
122
+ import { MultiSelect } from '@redislabsdev/redis-ui-components';
123
+
124
+ <>
125
+ Valid status
126
+ <MultiSelect options={options} valid onChange={(value) => console.log(value)} onOpenChange={(open) => console.log(open)} />
127
+ Error status
128
+ <MultiSelect options={options} error="Error message" onChange={(value) => console.log(value)} onOpenChange={(open) => console.log(open)} />
129
+ </>
130
+ ```
131
+
132
+ #### ResetButton
133
+
134
+ > The reset button appears only when something is selected.
135
+ > Set `allowReset` to `false` to hide it.
136
+
137
+ ```tsx
138
+ import { MultiSelect } from '@redislabsdev/redis-ui-components';
139
+
140
+ <MultiSelect
141
+ options={options}
142
+ defaultValue={['orange']}
143
+ allowReset={false}
144
+ onChange={(value) => console.log(value)}
145
+ onOpenChange={(open) => console.log(open)}
146
+ />
147
+ ```
148
+
149
+ #### Loading
150
+
151
+ > Adding `loading` enables the loading indicator.
152
+ > It can be used together with other indicators and states.
153
+
154
+ ```tsx
155
+ import { MultiSelect } from '@redislabsdev/redis-ui-components';
156
+
157
+ <>
158
+ Loading indicator
159
+ <MultiSelect options={options} loading onChange={(value) => console.log(value)} onOpenChange={(open) => console.log(open)} />
160
+ With other elements
161
+ <MultiSelect
162
+ options={options}
163
+ defaultValue={['banana']}
164
+ valid
165
+ allowReset
166
+ loading
167
+ onChange={(value) => console.log(value)}
168
+ onOpenChange={(open) => console.log(open)}
169
+ />
170
+ </>
171
+ ```
172
+
173
+ #### Disabled
174
+
175
+ ```tsx
176
+ import { MultiSelect } from '@redislabsdev/redis-ui-components';
177
+
178
+ <>
179
+ Default
180
+ <MultiSelect options={options} disabled onChange={() => {}} onOpenChange={() => {}} />
181
+ With selected items
182
+ <MultiSelect options={options} disabled value={['orange']} onChange={() => {}} onOpenChange={() => {}} />
183
+ With error message
184
+ <MultiSelect
185
+ options={options}
186
+ disabled
187
+ value={['banana']}
188
+ error="Error message"
189
+ loading
190
+ onChange={() => {}}
191
+ onOpenChange={() => {}}
192
+ />
193
+ </>
194
+ ```
195
+
196
+ #### ValueRender
197
+
198
+ > `valueRender` customizes both trigger chips and dropdown option values.
199
+ > The handler receives the current `option`, `isSelected`, and `isOptionValue` flags.
200
+ > Returning `undefined` or `null` falls back to the default rendering.
201
+ > When `isOptionValue` is falsy, plain text is extracted from the rendered result for the trigger chip.
202
+
203
+ ```tsx
204
+ import { MultiSelect, SelectValueRender } from '@redislabsdev/redis-ui-components';
205
+
206
+ const customValueRender: SelectValueRender = ({ option, isSelected, isOptionValue }) => {
207
+ const color = !isOptionValue ? 'green' : (isSelected && 'blueviolet') || 'orange';
208
+ const stateLabel = isOptionValue ? `${isSelected ? 'Selected Option' : 'Option'}` : 'Value';
209
+
210
+ return (
211
+ <div>
212
+ <span style={{ fontSize: '0.7em', color }}>{stateLabel}: </span>
213
+ {option.label}
214
+ </div>
215
+ );
216
+ };
217
+
218
+ <MultiSelect
219
+ options={options}
220
+ defaultValue={['orange']}
221
+ valueRender={customValueRender}
222
+ onChange={(value) => console.log(value)}
223
+ onOpenChange={(open) => console.log(open)}
224
+ />
225
+ ```
226
+
227
+ #### SelectAll
228
+
229
+ > `allowSelectAll` adds a footer action to select or unselect all options.
230
+ > When search is active, only search results are affected.
231
+ > Disabled options are ignored by default.
232
+ > Use `Ctrl+A` or `Cmd+A` while focus is on the list to toggle select-all.
233
+
234
+ ```tsx
235
+ import { MultiSelect } from '@redislabsdev/redis-ui-components';
236
+
237
+ <MultiSelect
238
+ options={usStates}
239
+ defaultValue={['Arizona', 'California']}
240
+ searchable
241
+ allowSelectAll
242
+ maxVisibleItems={6}
243
+ onChange={(value) => console.log(value)}
244
+ onOpenChange={(open) => console.log(open)}
245
+ />
246
+ ```
247
+
248
+ #### CustomSearch
249
+
250
+ > `customCompare` replaces the default label search.
251
+ > The handler receives an option and a lowercase search string.
252
+ > Include the label in your custom comparison if you still want label matches.
253
+
254
+ ```tsx
255
+ import {
256
+ MultiSelect,
257
+ SelectCompareHandler,
258
+ SelectOption,
259
+ SelectValueRender
260
+ } from '@redislabsdev/redis-ui-components';
261
+
262
+ const customCompare: SelectCompareHandler<SelectOption> = (option, searchValue) => {
263
+ const fieldsForSearch = [option.label, option.state as string];
264
+ return fieldsForSearch.some((field) => field?.toLowerCase().includes(searchValue));
265
+ };
266
+
267
+ const valueRender: SelectValueRender = ({ option }) => {
268
+ return (
269
+ <>
270
+ <span>{option.label}</span>
271
+ &nbsp;
272
+ <span style={{ fontSize: '0.8em' }}>{`(${option.state})`}</span>
273
+ </>
274
+ );
275
+ };
276
+
277
+ <MultiSelect
278
+ options={usStates}
279
+ searchable
280
+ customCompare={customCompare}
281
+ valueRender={valueRender}
282
+ onChange={(value) => console.log(value)}
283
+ onOpenChange={(open) => console.log(open)}
284
+ />
285
+ ```
286
+
287
+ #### VirtualList
288
+
289
+ > List of options can be virtualized by adding `virtualized`.
290
+ > It helps with long lists without performance drops.
291
+
292
+ ```tsx
293
+ import { FormField, MultiSelect } from '@redislabsdev/redis-ui-components';
294
+
295
+ <FormField label="Countries">
296
+ <MultiSelect
297
+ virtualized
298
+ searchable
299
+ options={countryOptions}
300
+ allowReset
301
+ defaultValue={['ua', 'us']}
302
+ maxVisibleItems={6}
303
+ onChange={(value) => console.log(value)}
304
+ onOpenChange={(open) => console.log(open)}
305
+ />
306
+ </FormField>
307
+ ```
308
+
309
+ ### MultiSelect.Trigger
310
+
311
+ #### Playground
312
+
313
+ ```tsx
314
+ import { MultiSelect } from '@redislabsdev/redis-ui-components';
315
+
316
+ <MultiSelect.Compose options={options} onChange={(value) => console.log(value)} onOpenChange={(open) => console.log(open)}>
317
+ <MultiSelect.Trigger placeholder="MultiSelect fruit..." allowReset={false} />
318
+ <MultiSelect.Content />
319
+ </MultiSelect.Compose>
320
+ ```
321
+
322
+ #### ValueRender
323
+
324
+ > Trigger value rendering can be customized by specifying `valueRender` on `MultiSelect.Trigger`.
325
+ > The handler receives the current `option`.
326
+ > When `isOptionValue` is falsy, plain text is extracted for the trigger chip.
327
+
328
+ ```tsx
329
+ import { MultiSelect } from '@redislabsdev/redis-ui-components';
330
+
331
+ const valueRender = ({ option }) => {
332
+ return (
333
+ <div>
334
+ <span style={{ fontSize: '0.7em', color: 'green' }}> Option: </span> {option.label}
335
+ </div>
336
+ );
337
+ };
338
+
339
+ <MultiSelect.Compose
340
+ options={options}
341
+ defaultValue={['orange']}
342
+ onChange={(value) => console.log(value)}
343
+ onOpenChange={(open) => console.log(open)}
344
+ >
345
+ <MultiSelect.Trigger valueRender={valueRender} />
346
+ <MultiSelect.Content />
347
+ </MultiSelect.Compose>
348
+ ```
349
+
350
+ #### CustomContainer
351
+
352
+ > `customContainer` replaces the default combobox-style button container.
353
+ > Wrap the custom container in a `span` or `div` if you need to avoid attribute conflicts.
354
+
355
+ ```tsx
356
+ import { useState } from 'react';
357
+ import { MultiSelect, ToggleButton, useSelectContext } from '@redislabsdev/redis-ui-components';
358
+
359
+ function Render() {
360
+ const CustomTriggerValue = () => {
361
+ const { hasSelection, isOptionSelected, options } = useSelectContext();
362
+
363
+ if (!hasSelection) {
364
+ return <>No selection</>;
365
+ }
366
+
367
+ return <>Selected {options.filter(isOptionSelected).length} item(s)</>;
368
+ };
369
+
370
+ const [open, setOpen] = useState(false);
371
+
372
+ return (
373
+ <MultiSelect.Compose
374
+ options={options}
375
+ defaultValue={['orange']}
376
+ open={open}
377
+ onOpenChange={setOpen}
378
+ onChange={(value) => console.log(value)}
379
+ >
380
+ <MultiSelect.Trigger.Compose customContainer>
381
+ <span>
382
+ <ToggleButton pressed={open}>
383
+ <CustomTriggerValue />
384
+ <MultiSelect.Trigger.Arrow />
385
+ </ToggleButton>
386
+ </span>
387
+ </MultiSelect.Trigger.Compose>
388
+ <MultiSelect.Content />
389
+ </MultiSelect.Compose>
390
+ );
391
+ }
392
+ ```
393
+
394
+ #### TriggerComposition
395
+
396
+ > Default composition example includes all parts of `MultiSelect.Trigger`.
397
+
398
+ ```tsx
399
+ import { MultiSelect } from '@redislabsdev/redis-ui-components';
400
+
401
+ <MultiSelect.Compose
402
+ options={options}
403
+ defaultValue={['orange']}
404
+ error="error message"
405
+ onChange={(value) => console.log(value)}
406
+ onOpenChange={(open) => console.log(open)}
407
+ >
408
+ <MultiSelect.Trigger.Compose>
409
+ <MultiSelect.Trigger.MultiValue placeholder="Placeholder" />
410
+ <MultiSelect.Trigger.ResetButton />
411
+ <MultiSelect.Trigger.StatusIndicator />
412
+ <MultiSelect.Trigger.LoadingIndicator loading />
413
+ <MultiSelect.Trigger.Arrow />
414
+ </MultiSelect.Trigger.Compose>
415
+
416
+ <MultiSelect.Content />
417
+ </MultiSelect.Compose>
418
+ ```
419
+
420
+ ### MultiSelect.Content
421
+
422
+ #### Playground
423
+
424
+ ```tsx
425
+ import { MultiSelect } from '@redislabsdev/redis-ui-components';
426
+
427
+ <MultiSelect.Compose options={options} onChange={(value) => console.log(value)} onOpenChange={(open) => console.log(open)}>
428
+ <MultiSelect.Trigger />
429
+ <MultiSelect.Content />
430
+ </MultiSelect.Compose>
431
+ ```
432
+
433
+ #### OptionValueRender
434
+
435
+ > `optionValueRender` customizes option values separately from trigger values.
436
+ > The handler receives the current `option` and `isSelected` flag.
437
+
438
+ ```tsx
439
+ import { MultiSelect } from '@redislabsdev/redis-ui-components';
440
+
441
+ <MultiSelect.Compose
442
+ options={options}
443
+ defaultValue={['orange']}
444
+ onChange={(value) => console.log(value)}
445
+ onOpenChange={(open) => console.log(open)}
446
+ >
447
+ <MultiSelect.Trigger />
448
+ <MultiSelect.Content
449
+ optionValueRender={({ option, isSelected }) => {
450
+ return (
451
+ <div>
452
+ <span
453
+ style={{
454
+ fontSize: '0.7em',
455
+ color: (isSelected && 'blueviolet') || 'orange'
456
+ }}
457
+ >
458
+ {isSelected ? 'Selected Option' : 'Option'}:
459
+ </span>{' '}
460
+ {option.label}
461
+ </div>
462
+ );
463
+ }}
464
+ />
465
+ </MultiSelect.Compose>
466
+ ```
467
+
468
+ #### OptionComponent
469
+
470
+ > `optionComponent` renders the entire option, including the multi-selection indicator.
471
+ > If it is not specified, `MultiSelect` renders options using the default `MultiSelect.Option`.
472
+ > A custom option component can still be used together with `optionValueRender`.
473
+ > The custom option component must use `MultiSelect.Option.Compose` as the root element.
474
+
475
+ ```tsx
476
+ import { MultiSelect, SelectOptionComponent } from '@redislabsdev/redis-ui-components';
477
+
478
+ const CustomOption: SelectOptionComponent = ({ option, content, ...restProps }) => (
479
+ <MultiSelect.Option.Compose option={option} {...restProps}>
480
+ <MultiSelect.Option.Content>{content}</MultiSelect.Option.Content>
481
+ <MultiSelect.Option.Indicator option={option} />
482
+ </MultiSelect.Option.Compose>
483
+ );
484
+
485
+ <MultiSelect.Compose
486
+ options={options}
487
+ defaultValue={['orange']}
488
+ onChange={(value) => console.log(value)}
489
+ onOpenChange={(open) => console.log(open)}
490
+ >
491
+ <MultiSelect.Trigger />
492
+ <MultiSelect.Content optionComponent={CustomOption} />
493
+ </MultiSelect.Compose>
494
+ ```
495
+
496
+ #### ContentComposition
497
+
498
+ > This example includes all parts of `MultiSelect.Content` and shows how to place static elements inside the composed content.
499
+
500
+ ```tsx
501
+ import { MultiSelect } from '@redislabsdev/redis-ui-components';
502
+
503
+ <MultiSelect.Compose
504
+ options={options}
505
+ allowResetDisabledItems
506
+ onChange={(value) => console.log(value)}
507
+ onOpenChange={(open) => console.log(open)}
508
+ >
509
+ <MultiSelect.Trigger />
510
+ <MultiSelect.Content.Compose>
511
+ <div style={{ textAlign: 'center' }}>Top static element</div>
512
+ <MultiSelect.Content.Search />
513
+ <div style={{ textAlign: 'center' }}>Middle static element</div>
514
+ <MultiSelect.Content.OptionList style={{ maxHeight: 100 }} />
515
+ <div style={{ textAlign: 'center' }}>Bottom static element</div>
516
+ <MultiSelect.Content.Footer>
517
+ <div style={{ textAlign: 'center' }}>Custom element in Footer</div>
518
+ <MultiSelect.Content.SelectAll selectAllTitle="Select all fruits" unselectAllTitle="Select none" />
519
+ </MultiSelect.Content.Footer>
520
+ </MultiSelect.Content.Compose>
521
+ </MultiSelect.Compose>
522
+ ```
523
+
524
+ #### ContentListComposition
525
+
526
+ > Adding an extra list container lets static elements scroll with the options.
527
+
528
+ ```tsx
529
+ import { MultiSelect } from '@redislabsdev/redis-ui-components';
530
+
531
+ <MultiSelect.Compose
532
+ options={options}
533
+ allowResetDisabledItems
534
+ onChange={(value) => console.log(value)}
535
+ onOpenChange={(open) => console.log(open)}
536
+ >
537
+ <MultiSelect.Trigger />
538
+ <MultiSelect.Content.Compose>
539
+ <MultiSelect.Content.Search />
540
+ <div style={{ overflowY: 'auto', maxHeight: 200 }}>
541
+ <div style={{ textAlign: 'center' }}>Top scrollable element</div>
542
+ <MultiSelect.Content.OptionList />
543
+ <div style={{ textAlign: 'center' }}>Bottom scrollable element</div>
544
+ </div>
545
+ </MultiSelect.Content.Compose>
546
+ </MultiSelect.Compose>
547
+ ```
548
+
549
+ ## Notes
550
+
551
+ - `FormField` is the recommended way to add an external label or additional text.
552
+ - `error` overrides `valid`, and the error indicator tooltip shows the error text.
553
+ - `valueRender` customizes both trigger chips and dropdown option values; return `undefined` or `null` to fall back to the default rendering.
554
+ - Use `MultiSelect.Content` or `MultiSelect.Compose` directly when you need `optionValueRender` or `optionComponent`; the top-level wrapper only exposes `valueRender`.
555
+ - `allowSelectAll` affects only the current search results when search is active.
556
+ - `MultiSelect.Trigger.ErrorIcon` is deprecated; use `StatusIndicator`.
557
+ - `allowResetDisabledItems` is available on `MultiSelect.Compose`, not on the top-level `MultiSelect` wrapper.
558
+ - `useMultiSelectContext` is exported for custom trigger and option components.