@coreui/react-pro 5.25.0 → 5.26.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 (291) hide show
  1. package/dist/cjs/components/card/CCardImage.js.map +1 -1
  2. package/dist/cjs/components/carousel/CCarousel.js +44 -33
  3. package/dist/cjs/components/carousel/CCarousel.js.map +1 -1
  4. package/dist/cjs/components/carousel/CCarouselItem.js +19 -28
  5. package/dist/cjs/components/carousel/CCarouselItem.js.map +1 -1
  6. package/dist/cjs/components/chip/CChip.d.ts +12 -0
  7. package/dist/cjs/components/chip/CChip.js +16 -63
  8. package/dist/cjs/components/chip/CChip.js.map +1 -1
  9. package/dist/cjs/components/chip/const.d.ts +1 -0
  10. package/dist/cjs/components/chip/const.js +8 -0
  11. package/dist/cjs/components/chip/const.js.map +1 -0
  12. package/dist/cjs/components/chip-set/CChipSet.d.ts +73 -0
  13. package/dist/cjs/components/chip-set/CChipSet.js +66 -0
  14. package/dist/cjs/components/chip-set/CChipSet.js.map +1 -0
  15. package/dist/cjs/components/chip-set/index.d.ts +2 -0
  16. package/dist/cjs/components/chip-set/useChipSet.d.ts +48 -0
  17. package/dist/cjs/components/chip-set/useChipSet.js +163 -0
  18. package/dist/cjs/components/chip-set/useChipSet.js.map +1 -0
  19. package/dist/cjs/components/chip-set/utils.d.ts +7 -0
  20. package/dist/cjs/components/chip-set/utils.js +18 -0
  21. package/dist/cjs/components/chip-set/utils.js.map +1 -0
  22. package/dist/cjs/components/collapse/CCollapse.js +14 -6
  23. package/dist/cjs/components/collapse/CCollapse.js.map +1 -1
  24. package/dist/cjs/components/conditional-portal/CConditionalPortal.js +3 -1
  25. package/dist/cjs/components/conditional-portal/CConditionalPortal.js.map +1 -1
  26. package/dist/cjs/components/dropdown/CDropdown.js +22 -29
  27. package/dist/cjs/components/dropdown/CDropdown.js.map +1 -1
  28. package/dist/cjs/components/dropdown/CDropdownToggle.js.map +1 -1
  29. package/dist/cjs/components/form/CChipInput.d.ts +14 -2
  30. package/dist/cjs/components/form/CChipInput.js +77 -49
  31. package/dist/cjs/components/form/CChipInput.js.map +1 -1
  32. package/dist/cjs/components/form/CFormCheck.js.map +1 -1
  33. package/dist/cjs/components/form/CFormInput.js.map +1 -1
  34. package/dist/cjs/components/form/CFormSelect.js.map +1 -1
  35. package/dist/cjs/components/form/CFormTextarea.js.map +1 -1
  36. package/dist/cjs/components/form/CInputGroupText.js.map +1 -1
  37. package/dist/cjs/components/grid/CContainer.js +3 -1
  38. package/dist/cjs/components/grid/CContainer.js.map +1 -1
  39. package/dist/cjs/components/header/CHeaderBrand.js.map +1 -1
  40. package/dist/cjs/components/index.d.ts +1 -0
  41. package/dist/cjs/components/link/CLink.js +4 -2
  42. package/dist/cjs/components/link/CLink.js.map +1 -1
  43. package/dist/cjs/components/list-group/CListGroupItem.js.map +1 -1
  44. package/dist/cjs/components/modal/CModal.js +14 -2
  45. package/dist/cjs/components/modal/CModal.js.map +1 -1
  46. package/dist/cjs/components/multi-select/CMultiSelect.d.ts +73 -1
  47. package/dist/cjs/components/multi-select/CMultiSelect.js +157 -18
  48. package/dist/cjs/components/multi-select/CMultiSelect.js.map +1 -1
  49. package/dist/cjs/components/multi-select/CMultiSelectNativeSelect.js +1 -1
  50. package/dist/cjs/components/multi-select/CMultiSelectNativeSelect.js.map +1 -1
  51. package/dist/cjs/components/multi-select/CMultiSelectOptions.d.ts +3 -0
  52. package/dist/cjs/components/multi-select/CMultiSelectOptions.js +45 -2
  53. package/dist/cjs/components/multi-select/CMultiSelectOptions.js.map +1 -1
  54. package/dist/cjs/components/multi-select/CMultiSelectSelection.d.ts +1 -0
  55. package/dist/cjs/components/multi-select/CMultiSelectSelection.js +4 -3
  56. package/dist/cjs/components/multi-select/CMultiSelectSelection.js.map +1 -1
  57. package/dist/cjs/components/multi-select/types.d.ts +12 -0
  58. package/dist/cjs/components/multi-select/utils.js +1 -4
  59. package/dist/cjs/components/multi-select/utils.js.map +1 -1
  60. package/dist/cjs/components/nav/CNav.js.map +1 -1
  61. package/dist/cjs/components/nav/CNavGroup.d.ts +10 -5
  62. package/dist/cjs/components/nav/CNavGroup.js +83 -24
  63. package/dist/cjs/components/nav/CNavGroup.js.map +1 -1
  64. package/dist/cjs/components/nav/CNavGroupContext.d.ts +25 -0
  65. package/dist/cjs/components/nav/CNavGroupContext.js +13 -0
  66. package/dist/cjs/components/nav/CNavGroupContext.js.map +1 -0
  67. package/dist/cjs/components/nav/CNavLink.d.ts +1 -5
  68. package/dist/cjs/components/nav/CNavLink.js +21 -8
  69. package/dist/cjs/components/nav/CNavLink.js.map +1 -1
  70. package/dist/cjs/components/popover/CPopover.js +57 -9
  71. package/dist/cjs/components/popover/CPopover.js.map +1 -1
  72. package/dist/cjs/components/progress/CProgress.js.map +1 -1
  73. package/dist/cjs/components/rating/CRating.d.ts +1 -1
  74. package/dist/cjs/components/rating/CRating.js.map +1 -1
  75. package/dist/cjs/components/sidebar/CSidebar.js +40 -23
  76. package/dist/cjs/components/sidebar/CSidebar.js.map +1 -1
  77. package/dist/cjs/components/sidebar/CSidebarNav.js +6 -35
  78. package/dist/cjs/components/sidebar/CSidebarNav.js.map +1 -1
  79. package/dist/cjs/components/table/CTableDataCell.js.map +1 -1
  80. package/dist/cjs/components/table/types.d.ts +2 -1
  81. package/dist/cjs/components/table/utils.js +4 -4
  82. package/dist/cjs/components/table/utils.js.map +1 -1
  83. package/dist/cjs/components/tabs/CTabPanel.js +3 -1
  84. package/dist/cjs/components/tabs/CTabPanel.js.map +1 -1
  85. package/dist/cjs/components/tooltip/CTooltip.js +57 -9
  86. package/dist/cjs/components/tooltip/CTooltip.js.map +1 -1
  87. package/dist/cjs/helpers/polymorphicComponent.d.ts +1 -2
  88. package/dist/cjs/hooks/useForkedRef.d.ts +1 -1
  89. package/dist/cjs/hooks/useForkedRef.js +6 -3
  90. package/dist/cjs/hooks/useForkedRef.js.map +1 -1
  91. package/dist/cjs/hooks/usePopper.js +8 -6
  92. package/dist/cjs/hooks/usePopper.js.map +1 -1
  93. package/dist/cjs/index.js +2 -0
  94. package/dist/cjs/index.js.map +1 -1
  95. package/dist/cjs/node_modules/@babel/runtime/helpers/esm/objectWithoutPropertiesLoose.js +1 -1
  96. package/dist/cjs/node_modules/@babel/runtime/helpers/esm/objectWithoutPropertiesLoose.js.map +1 -1
  97. package/dist/cjs/node_modules/ua-parser-js/dist/ua-parser.min.js +3 -3
  98. package/dist/cjs/node_modules/ua-parser-js/dist/ua-parser.min.js.map +1 -1
  99. package/dist/cjs/utils/executeAfterTransition.js.map +1 -1
  100. package/dist/cjs/utils/index.d.ts +2 -1
  101. package/dist/cjs/utils/swipe.d.ts +26 -0
  102. package/dist/cjs/utils/swipe.js +94 -0
  103. package/dist/cjs/utils/swipe.js.map +1 -0
  104. package/dist/esm/components/card/CCardImage.js.map +1 -1
  105. package/dist/esm/components/carousel/CCarousel.js +44 -33
  106. package/dist/esm/components/carousel/CCarousel.js.map +1 -1
  107. package/dist/esm/components/carousel/CCarouselItem.js +19 -28
  108. package/dist/esm/components/carousel/CCarouselItem.js.map +1 -1
  109. package/dist/esm/components/chip/CChip.d.ts +12 -0
  110. package/dist/esm/components/chip/CChip.js +16 -63
  111. package/dist/esm/components/chip/CChip.js.map +1 -1
  112. package/dist/esm/components/chip/const.d.ts +1 -0
  113. package/dist/esm/components/chip/const.js +6 -0
  114. package/dist/esm/components/chip/const.js.map +1 -0
  115. package/dist/esm/components/chip-set/CChipSet.d.ts +73 -0
  116. package/dist/esm/components/chip-set/CChipSet.js +64 -0
  117. package/dist/esm/components/chip-set/CChipSet.js.map +1 -0
  118. package/dist/esm/components/chip-set/index.d.ts +2 -0
  119. package/dist/esm/components/chip-set/useChipSet.d.ts +48 -0
  120. package/dist/esm/components/chip-set/useChipSet.js +161 -0
  121. package/dist/esm/components/chip-set/useChipSet.js.map +1 -0
  122. package/dist/esm/components/chip-set/utils.d.ts +7 -0
  123. package/dist/esm/components/chip-set/utils.js +16 -0
  124. package/dist/esm/components/chip-set/utils.js.map +1 -0
  125. package/dist/esm/components/collapse/CCollapse.js +14 -6
  126. package/dist/esm/components/collapse/CCollapse.js.map +1 -1
  127. package/dist/esm/components/conditional-portal/CConditionalPortal.js +3 -1
  128. package/dist/esm/components/conditional-portal/CConditionalPortal.js.map +1 -1
  129. package/dist/esm/components/dropdown/CDropdown.js +22 -29
  130. package/dist/esm/components/dropdown/CDropdown.js.map +1 -1
  131. package/dist/esm/components/dropdown/CDropdownToggle.js.map +1 -1
  132. package/dist/esm/components/form/CChipInput.d.ts +14 -2
  133. package/dist/esm/components/form/CChipInput.js +78 -50
  134. package/dist/esm/components/form/CChipInput.js.map +1 -1
  135. package/dist/esm/components/form/CFormCheck.js.map +1 -1
  136. package/dist/esm/components/form/CFormInput.js.map +1 -1
  137. package/dist/esm/components/form/CFormSelect.js.map +1 -1
  138. package/dist/esm/components/form/CFormTextarea.js.map +1 -1
  139. package/dist/esm/components/form/CInputGroupText.js.map +1 -1
  140. package/dist/esm/components/grid/CContainer.js +3 -1
  141. package/dist/esm/components/grid/CContainer.js.map +1 -1
  142. package/dist/esm/components/header/CHeaderBrand.js.map +1 -1
  143. package/dist/esm/components/index.d.ts +1 -0
  144. package/dist/esm/components/link/CLink.js +4 -2
  145. package/dist/esm/components/link/CLink.js.map +1 -1
  146. package/dist/esm/components/list-group/CListGroupItem.js.map +1 -1
  147. package/dist/esm/components/modal/CModal.js +14 -2
  148. package/dist/esm/components/modal/CModal.js.map +1 -1
  149. package/dist/esm/components/multi-select/CMultiSelect.d.ts +73 -1
  150. package/dist/esm/components/multi-select/CMultiSelect.js +158 -19
  151. package/dist/esm/components/multi-select/CMultiSelect.js.map +1 -1
  152. package/dist/esm/components/multi-select/CMultiSelectNativeSelect.js +1 -1
  153. package/dist/esm/components/multi-select/CMultiSelectNativeSelect.js.map +1 -1
  154. package/dist/esm/components/multi-select/CMultiSelectOptions.d.ts +3 -0
  155. package/dist/esm/components/multi-select/CMultiSelectOptions.js +45 -2
  156. package/dist/esm/components/multi-select/CMultiSelectOptions.js.map +1 -1
  157. package/dist/esm/components/multi-select/CMultiSelectSelection.d.ts +1 -0
  158. package/dist/esm/components/multi-select/CMultiSelectSelection.js +4 -3
  159. package/dist/esm/components/multi-select/CMultiSelectSelection.js.map +1 -1
  160. package/dist/esm/components/multi-select/types.d.ts +12 -0
  161. package/dist/esm/components/multi-select/utils.js +1 -4
  162. package/dist/esm/components/multi-select/utils.js.map +1 -1
  163. package/dist/esm/components/nav/CNav.js.map +1 -1
  164. package/dist/esm/components/nav/CNavGroup.d.ts +10 -5
  165. package/dist/esm/components/nav/CNavGroup.js +84 -25
  166. package/dist/esm/components/nav/CNavGroup.js.map +1 -1
  167. package/dist/esm/components/nav/CNavGroupContext.d.ts +25 -0
  168. package/dist/esm/components/nav/CNavGroupContext.js +11 -0
  169. package/dist/esm/components/nav/CNavGroupContext.js.map +1 -0
  170. package/dist/esm/components/nav/CNavLink.d.ts +1 -5
  171. package/dist/esm/components/nav/CNavLink.js +21 -8
  172. package/dist/esm/components/nav/CNavLink.js.map +1 -1
  173. package/dist/esm/components/popover/CPopover.js +57 -9
  174. package/dist/esm/components/popover/CPopover.js.map +1 -1
  175. package/dist/esm/components/progress/CProgress.js.map +1 -1
  176. package/dist/esm/components/rating/CRating.d.ts +1 -1
  177. package/dist/esm/components/rating/CRating.js.map +1 -1
  178. package/dist/esm/components/sidebar/CSidebar.js +40 -23
  179. package/dist/esm/components/sidebar/CSidebar.js.map +1 -1
  180. package/dist/esm/components/sidebar/CSidebarNav.js +7 -36
  181. package/dist/esm/components/sidebar/CSidebarNav.js.map +1 -1
  182. package/dist/esm/components/table/CTableDataCell.js.map +1 -1
  183. package/dist/esm/components/table/types.d.ts +2 -1
  184. package/dist/esm/components/table/utils.js +4 -4
  185. package/dist/esm/components/table/utils.js.map +1 -1
  186. package/dist/esm/components/tabs/CTabPanel.js +3 -1
  187. package/dist/esm/components/tabs/CTabPanel.js.map +1 -1
  188. package/dist/esm/components/tooltip/CTooltip.js +57 -9
  189. package/dist/esm/components/tooltip/CTooltip.js.map +1 -1
  190. package/dist/esm/helpers/polymorphicComponent.d.ts +1 -2
  191. package/dist/esm/hooks/useForkedRef.d.ts +1 -1
  192. package/dist/esm/hooks/useForkedRef.js +6 -3
  193. package/dist/esm/hooks/useForkedRef.js.map +1 -1
  194. package/dist/esm/hooks/usePopper.js +9 -7
  195. package/dist/esm/hooks/usePopper.js.map +1 -1
  196. package/dist/esm/index.js +1 -0
  197. package/dist/esm/index.js.map +1 -1
  198. package/dist/esm/node_modules/@babel/runtime/helpers/esm/objectWithoutPropertiesLoose.js +1 -1
  199. package/dist/esm/node_modules/@babel/runtime/helpers/esm/objectWithoutPropertiesLoose.js.map +1 -1
  200. package/dist/esm/node_modules/ua-parser-js/dist/ua-parser.min.js +3 -3
  201. package/dist/esm/node_modules/ua-parser-js/dist/ua-parser.min.js.map +1 -1
  202. package/dist/esm/utils/executeAfterTransition.js.map +1 -1
  203. package/dist/esm/utils/index.d.ts +2 -1
  204. package/dist/esm/utils/swipe.d.ts +26 -0
  205. package/dist/esm/utils/swipe.js +90 -0
  206. package/dist/esm/utils/swipe.js.map +1 -0
  207. package/package.json +6 -6
  208. package/src/components/autocomplete/CAutocompleteOptions.tsx +1 -1
  209. package/src/components/card/CCardImage.tsx +3 -2
  210. package/src/components/carousel/CCarousel.tsx +48 -51
  211. package/src/components/carousel/CCarouselContext.ts +1 -1
  212. package/src/components/carousel/CCarouselItem.tsx +25 -26
  213. package/src/components/carousel/__tests__/CCarousel.spec.tsx +47 -4
  214. package/src/components/carousel/__tests__/CCarouselItem.spec.tsx +37 -0
  215. package/src/components/chip/CChip.tsx +45 -78
  216. package/src/components/chip/__tests__/CChip.spec.tsx +26 -19
  217. package/src/components/chip/const.ts +3 -0
  218. package/src/components/chip-set/CChipSet.tsx +183 -0
  219. package/src/components/chip-set/__tests__/CChipSet.spec.tsx +215 -0
  220. package/src/components/chip-set/__tests__/__snapshots__/CChipSet.spec.tsx.snap +22 -0
  221. package/src/components/chip-set/index.ts +3 -0
  222. package/src/components/chip-set/useChipSet.ts +243 -0
  223. package/src/components/chip-set/utils.ts +19 -0
  224. package/src/components/collapse/CCollapse.tsx +18 -6
  225. package/src/components/conditional-portal/CConditionalPortal.tsx +4 -2
  226. package/src/components/dropdown/CDropdown.tsx +28 -33
  227. package/src/components/dropdown/CDropdownToggle.tsx +1 -1
  228. package/src/components/dropdown/__tests__/CDropdown.spec.tsx +55 -0
  229. package/src/components/focus-trap/__tests__/CFocusTrap.spec.tsx +1 -1
  230. package/src/components/form/CChipInput.tsx +105 -67
  231. package/src/components/form/CFormCheck.tsx +1 -2
  232. package/src/components/form/CFormInput.tsx +1 -2
  233. package/src/components/form/CFormSelect.tsx +1 -2
  234. package/src/components/form/CFormTextarea.tsx +1 -2
  235. package/src/components/form/CInputGroupText.tsx +3 -2
  236. package/src/components/form/__tests__/CChipInput.spec.tsx +67 -1
  237. package/src/components/form/__tests__/__snapshots__/CChipInput.spec.tsx.snap +7 -0
  238. package/src/components/grid/CContainer.tsx +3 -1
  239. package/src/components/header/CHeaderBrand.tsx +3 -2
  240. package/src/components/index.ts +1 -0
  241. package/src/components/link/CLink.tsx +7 -4
  242. package/src/components/list-group/CListGroupItem.tsx +3 -2
  243. package/src/components/modal/CModal.tsx +19 -2
  244. package/src/components/modal/__tests__/CModal.spec.tsx +43 -0
  245. package/src/components/multi-select/CMultiSelect.tsx +341 -46
  246. package/src/components/multi-select/CMultiSelectNativeSelect.tsx +1 -1
  247. package/src/components/multi-select/CMultiSelectOptions.tsx +74 -3
  248. package/src/components/multi-select/CMultiSelectSelection.tsx +5 -1
  249. package/src/components/multi-select/__tests__/CMultiSelect.spec.tsx +12 -8
  250. package/src/components/multi-select/__tests__/__snapshots__/CMultiSelect.spec.tsx.snap +62 -45
  251. package/src/components/multi-select/__tests__/__snapshots__/CMultiSelectNativeSelect.spec.tsx.snap +2 -2
  252. package/src/components/multi-select/__tests__/__snapshots__/CMultiSelectOptions.spec.tsx.snap +1 -0
  253. package/src/components/multi-select/__tests__/__snapshots__/CMultiSelectSelection.spec.tsx.snap +5 -1
  254. package/src/components/multi-select/types.ts +14 -0
  255. package/src/components/nav/CNav.tsx +3 -2
  256. package/src/components/nav/CNavGroup.tsx +193 -107
  257. package/src/components/nav/CNavGroupContext.ts +27 -0
  258. package/src/components/nav/CNavLink.tsx +26 -12
  259. package/src/components/nav/__tests__/CNavGroup.spec.tsx +195 -20
  260. package/src/components/nav/__tests__/__snapshots__/CNavGroup.spec.tsx.snap +2 -0
  261. package/src/components/popover/CPopover.tsx +59 -9
  262. package/src/components/popover/__tests__/CPopover.spec.tsx +76 -1
  263. package/src/components/progress/CProgress.tsx +1 -2
  264. package/src/components/rating/CRating.tsx +1 -0
  265. package/src/components/search-button/__tests__/CSearchButton.spec.tsx +2 -2
  266. package/src/components/sidebar/CSidebar.tsx +51 -26
  267. package/src/components/sidebar/CSidebarNav.tsx +12 -51
  268. package/src/components/sidebar/__tests__/CSidebar.spec.tsx +26 -0
  269. package/src/components/table/CTableDataCell.tsx +2 -1
  270. package/src/components/table/types.ts +3 -1
  271. package/src/components/table/utils.ts +4 -4
  272. package/src/components/tabs/CTabPanel.tsx +3 -1
  273. package/src/components/tabs/CTabsContext.ts +1 -1
  274. package/src/components/tooltip/CTooltip.tsx +59 -9
  275. package/src/components/tooltip/__tests__/CTooltip.spec.tsx +96 -1
  276. package/src/helpers/polymorphicComponent.ts +1 -1
  277. package/src/hooks/useColorModes.ts +2 -2
  278. package/src/hooks/useForkedRef.ts +20 -15
  279. package/src/hooks/usePopper.ts +13 -9
  280. package/src/utils/__tests__/swipe.spec.ts +82 -0
  281. package/src/utils/executeAfterTransition.ts +1 -1
  282. package/src/utils/index.ts +2 -0
  283. package/src/utils/mergeClassNames.ts +1 -1
  284. package/src/utils/swipe.ts +114 -0
  285. package/dist/cjs/components/sidebar/CSidebarNavContext.d.ts +0 -5
  286. package/dist/cjs/components/sidebar/CSidebarNavContext.js +0 -8
  287. package/dist/cjs/components/sidebar/CSidebarNavContext.js.map +0 -1
  288. package/dist/esm/components/sidebar/CSidebarNavContext.d.ts +0 -5
  289. package/dist/esm/components/sidebar/CSidebarNavContext.js +0 -6
  290. package/dist/esm/components/sidebar/CSidebarNavContext.js.map +0 -1
  291. package/src/components/sidebar/CSidebarNavContext.ts +0 -8
@@ -30,7 +30,14 @@ import {
30
30
  isGlobalSearch,
31
31
  selectOptions,
32
32
  } from './utils'
33
- import type { Option, OptionsGroup, Search, SelectedOption } from './types'
33
+ import type {
34
+ Option,
35
+ OptionsGroup,
36
+ Search,
37
+ SelectedOption,
38
+ SelectionActions,
39
+ SelectionState,
40
+ } from './types'
34
41
 
35
42
  export interface CMultiSelectProps
36
43
  extends Omit<CFormControlWrapperProps, 'floatingClassName' | 'floatingLabel'>,
@@ -53,6 +60,18 @@ export interface CMultiSelectProps
53
60
  * @since 5.19.0
54
61
  */
55
62
  ariaIndicatorLabel?: string
63
+ /**
64
+ * Accessible label for the search input (when `search` is enabled).
65
+ *
66
+ * @since 5.26.0
67
+ */
68
+ ariaSearchLabel?: string
69
+ /**
70
+ * Accessible label prefix for a tag's delete button (selection type `tags`). The selected option's label is appended, so screen readers announce e.g. "Remove Angular".
71
+ *
72
+ * @since 5.26.0
73
+ */
74
+ ariaTagDeleteLabel?: string
56
75
  /**
57
76
  * A string of all className you want applied to the base component.
58
77
  */
@@ -77,6 +96,12 @@ export interface CMultiSelectProps
77
96
  * Toggle the disabled state for the component.
78
97
  */
79
98
  disabled?: boolean
99
+ /**
100
+ * Render a custom dropdown header (the area above the options list), replacing the built-in select all button. Receives a `state` object (`{ selected, total, filtered, filteredSelected }`) and an `actions` object (`{ selectAll, deselectAll, selectFiltered, deselectFiltered }`) so you can wire your own controls. Renders independently of `selectAll`.
101
+ *
102
+ * @since 5.26.0
103
+ */
104
+ headerTemplate?: (state: SelectionState, actions: SelectionActions) => ReactNode
80
105
  /**
81
106
  * Set the id attribute for the native select element.
82
107
  *
@@ -114,6 +139,12 @@ export interface CMultiSelectProps
114
139
  * The callback is fired when the Multi Select component requests to be hidden.
115
140
  */
116
141
  onHide?: () => void
142
+ /**
143
+ * Execute a function when the user tries to select more options than allowed by `selectionLimit`.
144
+ *
145
+ * @since 5.26.0
146
+ */
147
+ onSelectionLimit?: (selectionLimit: number) => void
117
148
  /**
118
149
  * The callback is fired when the Multi Select component requests to be shown.
119
150
  */
@@ -136,6 +167,18 @@ export interface CMultiSelectProps
136
167
  * @since 4.12.0
137
168
  */
138
169
  optionsTemplate?: (option: Option) => ReactNode
170
+ /**
171
+ * Makes each options group label a toggle that selects or deselects all options in that group. With `optionsGroupsStyle="checkbox"` (and `multiple`) the label shows a tri-state checkbox indicator (`none` / `all` / `indeterminate`) reflecting the group's selection.
172
+ *
173
+ * @since 5.26.0
174
+ */
175
+ optionsGroupsSelectable?: boolean
176
+ /**
177
+ * Sets the options group label style when `optionsGroupsSelectable` is enabled.
178
+ *
179
+ * @since 5.26.0
180
+ */
181
+ optionsGroupsStyle?: 'checkbox' | 'text'
139
182
  /**
140
183
  * Custom template for options groups.
141
184
  *
@@ -175,6 +218,18 @@ export interface CMultiSelectProps
175
218
  * Sets the label for no results when filtering.
176
219
  */
177
220
  searchNoResultsLabel?: string | ReactNode
221
+ /**
222
+ * Sets the select all button label shown once everything is selected. The button is a toggle: it shows `selectAllLabel` (and selects all) until everything is selected, then shows `deselectAllLabel` (and deselects all).
223
+ *
224
+ * @since 5.26.0
225
+ */
226
+ deselectAllLabel?: string | ReactNode
227
+ /**
228
+ * Sets the deselect filtered button label (selection type `selectAllMode="filtered"`).
229
+ *
230
+ * @since 5.26.0
231
+ */
232
+ deselectFilteredLabel?: string | ReactNode
178
233
  /**
179
234
  * Enables select all button.
180
235
  */
@@ -183,6 +238,30 @@ export interface CMultiSelectProps
183
238
  * Sets the select all button label.
184
239
  */
185
240
  selectAllLabel?: string | ReactNode
241
+ /**
242
+ * Determines what the select all button operates on: all options (`'all'`) or only the currently filtered (search-visible) options (`'filtered'`).
243
+ *
244
+ * @since 5.26.0
245
+ */
246
+ selectAllMode?: 'all' | 'filtered'
247
+ /**
248
+ * Sets the select all button style. With `'checkbox'` (and `multiple`), the button shows a tri-state checkbox indicator (`none` / `all` / `indeterminate`) and toggles between selecting and deselecting.
249
+ *
250
+ * @since 5.26.0
251
+ */
252
+ selectAllStyle?: 'checkbox' | 'text'
253
+ /**
254
+ * Sets the select filtered button label (used with `selectAllMode="filtered"`).
255
+ *
256
+ * @since 5.26.0
257
+ */
258
+ selectFilteredLabel?: string | ReactNode
259
+ /**
260
+ * Sets the maximum number of options that can be selected. The select all button stays enabled and selects options up to the limit, then toggles to deselect all once the limit is reached. The `onSelectionLimit` callback fires when a user tries to select more options than allowed.
261
+ *
262
+ * @since 5.26.0
263
+ */
264
+ selectionLimit?: number
186
265
  /**
187
266
  * Sets the selection style.
188
267
  */
@@ -225,14 +304,19 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
225
304
  allowCreateOptions,
226
305
  ariaCleanerLabel = 'Clear all selections',
227
306
  ariaIndicatorLabel = 'Toggle visibility of options menu',
307
+ ariaSearchLabel = 'Search',
308
+ ariaTagDeleteLabel = 'Remove',
228
309
  className,
229
310
  cleaner = true,
230
311
  clearSearchOnSelect,
231
312
  container,
313
+ deselectAllLabel = 'Deselect all',
314
+ deselectFilteredLabel = 'Deselect filtered',
232
315
  disabled,
233
316
  feedback,
234
317
  feedbackInvalid,
235
318
  feedbackValid,
319
+ headerTemplate,
236
320
  id,
237
321
  invalid,
238
322
  label,
@@ -242,11 +326,14 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
242
326
  onChange,
243
327
  onFilterChange,
244
328
  onHide,
329
+ onSelectionLimit,
245
330
  onShow,
246
331
  options,
247
332
  optionsMaxHeight = 'auto',
248
333
  optionsStyle = 'checkbox',
249
334
  optionsTemplate,
335
+ optionsGroupsSelectable = false,
336
+ optionsGroupsStyle = 'checkbox',
250
337
  optionsGroupsTemplate,
251
338
  placeholder = 'Select...',
252
339
  portal = false,
@@ -255,7 +342,11 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
255
342
  search = true,
256
343
  searchNoResultsLabel = 'No results found',
257
344
  selectAll = true,
258
- selectAllLabel = 'Select all options',
345
+ selectAllLabel = 'Select all',
346
+ selectAllMode = 'all',
347
+ selectAllStyle = 'checkbox',
348
+ selectFilteredLabel = 'Select filtered',
349
+ selectionLimit,
259
350
  selectionType = 'tags',
260
351
  selectionTypeCounterText = 'item(s) selected',
261
352
  size,
@@ -288,16 +379,22 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
288
379
  const [selected, setSelected] = useState<SelectedOption[]>([])
289
380
  const [userOptions, setUserOptions] = useState<Option[]>([])
290
381
 
291
- const filteredOptions = useMemo(
292
- () =>
293
- flattenOptionsArray(
294
- isExternalSearch(search)
295
- ? [...options, ...filterOptionsList(searchValue, userOptions)]
296
- : filterOptionsList(searchValue, [...options, ...userOptions]),
297
- true
298
- ),
299
- [options, searchValue, userOptions]
300
- )
382
+ const filteredOptions = useMemo(() => {
383
+ const source = isExternalSearch(search)
384
+ ? [...options, ...filterOptionsList(searchValue, userOptions)]
385
+ : filterOptionsList(searchValue, [...options, ...userOptions])
386
+
387
+ const flattened: (Option | OptionsGroup)[] = []
388
+ for (const option of source) {
389
+ if (Array.isArray((option as OptionsGroup).options)) {
390
+ flattened.push(option, ...((option as OptionsGroup).options as Option[]))
391
+ } else {
392
+ flattened.push(option)
393
+ }
394
+ }
395
+
396
+ return flattened
397
+ }, [options, searchValue, userOptions])
301
398
 
302
399
  const flattenedOptions = useMemo(
303
400
  () =>
@@ -334,15 +431,94 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
334
431
  return searchRef.current && createOption(String(searchValue), flattenedOptions)
335
432
  }, [filteredOptions, searchValue])
336
433
 
434
+ const selectableOptions = useMemo(
435
+ () =>
436
+ flattenOptionsArray([...options, ...userOptions]).filter(
437
+ (option): option is Option => !option.disabled
438
+ ),
439
+ [options, userOptions]
440
+ )
441
+
442
+ const filteredSelectableOptions = useMemo(
443
+ () =>
444
+ flattenOptionsArray(
445
+ isExternalSearch(search)
446
+ ? [...options, ...filterOptionsList(searchValue, userOptions)]
447
+ : filterOptionsList(searchValue, [...options, ...userOptions])
448
+ ).filter((option): option is Option => !option.disabled),
449
+ [options, searchValue, userOptions]
450
+ )
451
+
452
+ const selectedValues = new Set(selected.map((option) => option.value))
453
+ const countSelected = (scope: Option[]) =>
454
+ scope.filter((option) => selectedValues.has(option.value)).length
455
+
456
+ const hasSelectionLimit = multiple && typeof selectionLimit === 'number'
457
+ const isSelectionLimitReached = hasSelectionLimit && selected.length >= (selectionLimit as number)
458
+ const selectableTarget = (total: number) =>
459
+ hasSelectionLimit ? Math.min(total, selectionLimit as number) : total
460
+
461
+ const selectWithLimit = (scope: Option[]) => {
462
+ if (!hasSelectionLimit) {
463
+ setSelected(selectOptions(multiple, scope, selected))
464
+ return
465
+ }
466
+
467
+ const remaining = (selectionLimit as number) - selected.length
468
+
469
+ if (remaining <= 0) {
470
+ onSelectionLimit?.(selectionLimit as number)
471
+ return
472
+ }
473
+
474
+ const toAdd = scope.filter((option) => !selectedValues.has(option.value))
475
+ setSelected(selectOptions(multiple, toAdd.slice(0, remaining), selected))
476
+
477
+ if (toAdd.length > remaining) {
478
+ onSelectionLimit?.(selectionLimit as number)
479
+ }
480
+ }
481
+
482
+ const selectAllScope =
483
+ selectAllMode === 'filtered'
484
+ ? { selected: countSelected(filteredSelectableOptions), total: filteredSelectableOptions.length }
485
+ : { selected: countSelected(selectableOptions), total: selectableOptions.length }
486
+
487
+ const selectAllTarget = selectableTarget(selectAllScope.total)
488
+ const isAllSelected = selectAllTarget > 0 && selectAllScope.selected >= selectAllTarget
489
+ const selectAllCheckboxState = isAllSelected
490
+ ? 'all'
491
+ : selectAllScope.selected === 0
492
+ ? 'none'
493
+ : 'indeterminate'
494
+ const isFilteredScopeNarrowed =
495
+ selectAllMode === 'filtered' && filteredSelectableOptions.length < selectableOptions.length
496
+ const selectAllButtonLabel = isFilteredScopeNarrowed
497
+ ? isAllSelected
498
+ ? deselectFilteredLabel
499
+ : selectFilteredLabel
500
+ : isAllSelected
501
+ ? deselectAllLabel
502
+ : selectAllLabel
503
+
504
+ const headerState: SelectionState = {
505
+ selected: selected.length,
506
+ total: selectableOptions.length,
507
+ filtered: filteredSelectableOptions.length,
508
+ filteredSelected: countSelected(filteredSelectableOptions),
509
+ }
510
+
337
511
  useEffect(() => {
338
512
  if (!isInitialMount.current && resetSelectionOnOptionsChange) {
339
513
  return setSelected([])
340
514
  }
341
515
 
342
- const _selected = flattenedOptions.filter((option: Option) => option.selected === true)
516
+ const _selected = flattenedOptions.filter(
517
+ (option): option is Option => option.selected === true
518
+ )
343
519
  const deselected = flattenedOptions.filter(
344
- (option: Option) => option.selected === false
345
- ) as Option[]
520
+ (option): option is Option => option.selected === false
521
+ )
346
522
 
347
523
  if (_selected.length > 0 || deselected.length > 0) {
348
524
  setSelected(selectOptions(multiple, _selected, selected, deselected))
@@ -381,6 +557,20 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
381
557
  return () => {
382
558
  if (onHide) onHide()
383
559
 
560
+ if (isOpen) {
561
+ const active = document.activeElement
562
+ const menu = dropdownMenuElement.current as HTMLElement | null
563
+ const toggler = dropdownRefElement.current as HTMLElement | null
564
+
565
+ if (menu?.contains(active) || toggler?.contains(active)) {
566
+ if (search && searchRef.current) {
567
+ searchRef.current.focus()
568
+ } else {
569
+ toggler?.focus()
570
+ }
571
+ }
572
+ }
573
+
384
574
  setSearchValue('')
385
575
  if (searchRef.current) {
386
576
  searchRef.current.value = ''
@@ -483,6 +673,36 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
483
673
  }
484
674
  }
485
675
 
676
+ const handleNativeSelectKeyDown = (event: React.KeyboardEvent<HTMLSelectElement>) => {
677
+ if (event.key === 'Tab' || event.key === 'Escape') {
678
+ return
679
+ }
680
+
681
+ event.preventDefault()
682
+
683
+ const isPrintable =
684
+ event.key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey
685
+
686
+ if (
687
+ !isOpen &&
688
+ (event.key === 'Enter' || event.key === 'ArrowDown' || (search && isPrintable))
689
+ ) {
690
+ openDropdown()
691
+ }
692
+
693
+ if (search && searchRef.current) {
694
+ searchRef.current.focus()
695
+
696
+ if (isPrintable) {
697
+ const nextValue = searchRef.current.value + event.key
698
+ searchRef.current.value = nextValue
699
+ setSearchValue(nextValue)
700
+ }
701
+ } else {
702
+ ;(dropdownRefElement.current as HTMLElement | null)?.focus()
703
+ }
704
+ }
705
+
486
706
  const handleGlobalSearch = (event: React.KeyboardEvent<HTMLDivElement>) => {
487
707
  if (
488
708
  isGlobalSearch(search) &&
@@ -505,6 +725,13 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
505
725
  return
506
726
  }
507
727
 
728
+ const alreadySelected = selected.some((_option) => _option.value === option.value)
729
+
730
+ if (!alreadySelected && isSelectionLimitReached) {
731
+ onSelectionLimit?.(selectionLimit as number)
732
+ return
733
+ }
734
+
508
735
  if (option.custom && !userOptions.some((_option) => _option.value === option.value)) {
509
736
  setUserOptions([...userOptions, option])
510
737
  }
@@ -517,7 +744,7 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
517
744
  }
518
745
  }
519
746
 
520
- if (selected.some((_option) => _option.value === option.value)) {
747
+ if (alreadySelected) {
521
748
  setSelected(selected.filter((_option) => _option.value !== option.value))
522
749
  } else {
523
750
  setSelected([...selected, option] as SelectedOption[])
@@ -525,19 +752,52 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
525
752
  }
526
753
 
527
754
  const handleSelectAll = () => {
528
- setSelected(
529
- selectOptions(
530
- multiple,
531
- [...flattenedOptions.filter((option: Option) => !option.disabled), ...userOptions],
532
- selected
533
- )
534
- )
755
+ selectWithLimit(selectableOptions)
535
756
  }
536
757
 
537
758
  const handleDeselectAll = () => {
538
759
  setSelected(selected.filter((option) => option.disabled))
539
760
  }
540
761
 
762
+ const handleSelectFiltered = () => {
763
+ selectWithLimit(filteredSelectableOptions)
764
+ }
765
+
766
+ const handleDeselectFiltered = () => {
767
+ const filteredValues = new Set(filteredSelectableOptions.map((option) => option.value))
768
+ setSelected(
769
+ selected.filter((option) => option.disabled || !filteredValues.has(option.value))
770
+ )
771
+ }
772
+
773
+ const handleSelectAllToggle = () => {
774
+ if (selectAllMode === 'filtered') {
775
+ isAllSelected ? handleDeselectFiltered() : handleSelectFiltered()
776
+ } else {
777
+ isAllSelected ? handleDeselectAll() : handleSelectAll()
778
+ }
779
+ }
780
+
781
+ const handleGroupClick = (group: OptionsGroup) => {
782
+ const groupOptions = (group.options ?? []).filter((option) => !option.disabled)
783
+ const allSelected =
784
+ groupOptions.length > 0 && groupOptions.every((option) => selectedValues.has(option.value))
785
+
786
+ if (allSelected) {
787
+ const groupValues = new Set(groupOptions.map((option) => option.value))
788
+ setSelected(selected.filter((option) => option.disabled || !groupValues.has(option.value)))
789
+ } else {
790
+ selectWithLimit(groupOptions)
791
+ }
792
+ }
793
+
794
+ const headerActions: SelectionActions = {
795
+ selectAll: handleSelectAll,
796
+ deselectAll: handleDeselectAll,
797
+ selectFiltered: handleSelectFiltered,
798
+ deselectFiltered: handleDeselectFiltered,
799
+ }
800
+
541
801
  return (
542
802
  <CFormControlWrapper
543
803
  describedby={rest['aria-describedby']}
@@ -551,20 +811,6 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
551
811
  tooltipFeedback={tooltipFeedback}
552
812
  valid={valid}
553
813
  >
554
- <CMultiSelectNativeSelect
555
- id={id ?? uniqueId}
556
- multiple={multiple}
557
- name={name ?? uniqueId}
558
- options={selected}
559
- required={required}
560
- value={
561
- multiple
562
- ? selected.map((option: SelectedOption) => option.value.toString())
563
- : selected.map((option: SelectedOption) => option.value)[0]
564
- }
565
- onChange={() => onChange && onChange(selected)}
566
- ref={nativeSelectRef}
567
- />
568
814
  <div
569
815
  className={classNames(
570
816
  'form-multi-select',
@@ -578,9 +824,23 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
578
824
  className
579
825
  )}
580
826
  onKeyDown={handleGlobalSearch}
581
- aria-expanded={isOpen}
582
827
  ref={ref}
583
828
  >
829
+ <CMultiSelectNativeSelect
830
+ id={id ?? uniqueId}
831
+ multiple={multiple}
832
+ name={name ?? uniqueId}
833
+ options={selected}
834
+ required={required}
835
+ value={
836
+ multiple
837
+ ? selected.map((option: SelectedOption) => option.value.toString())
838
+ : selected.map((option: SelectedOption) => option.value)[0]
839
+ }
840
+ onChange={() => onChange && onChange(selected)}
841
+ onKeyDown={handleNativeSelectKeyDown}
842
+ ref={nativeSelectRef}
843
+ />
584
844
  <div
585
845
  className="form-multi-select-input-group"
586
846
  {...(!search && !disabled && { tabIndex: 0 })}
@@ -589,10 +849,13 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
589
849
  role="combobox"
590
850
  aria-haspopup="listbox"
591
851
  aria-expanded={isOpen}
852
+ aria-controls={`multiselect-listbox-${uniqueId}`}
592
853
  {...(portal && { 'aria-owns': `multiselect-listbox-${uniqueId}` })}
854
+ {...(disabled && { 'aria-disabled': true })}
593
855
  ref={dropdownRefElement}
594
856
  >
595
857
  <CMultiSelectSelection
858
+ ariaTagDeleteLabel={ariaTagDeleteLabel}
596
859
  disabled={disabled}
597
860
  multiple={multiple}
598
861
  onRemove={(option) => !disabled && handleOnOptionClick(option)}
@@ -610,6 +873,9 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
610
873
  id={`search${id ?? uniqueId}`}
611
874
  name={`search${name ?? uniqueId}`}
612
875
  autoComplete="off"
876
+ aria-label={ariaSearchLabel}
877
+ aria-autocomplete="list"
878
+ aria-controls={`multiselect-listbox-${uniqueId}`}
613
879
  onChange={handleSearchChange}
614
880
  onKeyDown={handleSearchKeyDown}
615
881
  {...(selected.length === 0 && { placeholder: placeholder })}
@@ -665,23 +931,40 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
665
931
  aria-multiselectable={multiple}
666
932
  ref={dropdownMenuElement}
667
933
  >
668
- {multiple && selectAll && (
669
- <button
670
- type="button"
671
- className="form-multi-select-all"
672
- onClick={() => handleSelectAll()}
673
- >
674
- {selectAllLabel}
675
- </button>
934
+ {(headerTemplate || (multiple && selectAll)) && (
935
+ <div className="form-multi-select-dropdown-header">
936
+ {headerTemplate ? (
937
+ <div onClick={(event) => event.stopPropagation()}>
938
+ {headerTemplate(headerState, headerActions)}
939
+ </div>
940
+ ) : (
941
+ <button
942
+ type="button"
943
+ className={classNames('form-multi-select-all', {
944
+ 'form-multi-select-all-with-checkbox': selectAllStyle === 'checkbox',
945
+ 'form-multi-selected':
946
+ selectAllStyle === 'checkbox' && selectAllCheckboxState === 'all',
947
+ 'form-multi-select-indeterminate':
948
+ selectAllStyle === 'checkbox' && selectAllCheckboxState === 'indeterminate',
949
+ })}
950
+ onClick={() => handleSelectAllToggle()}
951
+ >
952
+ {selectAllButtonLabel}
953
+ </button>
954
+ )}
955
+ </div>
676
956
  )}
677
957
  <CMultiSelectOptions
678
958
  loading={loading}
959
+ onGroupOnClick={(group) => !disabled && handleGroupClick(group)}
679
960
  onOptionOnClick={(option) => !disabled && handleOnOptionClick(option)}
680
961
  options={
681
962
  filteredOptions.length === 0 && allowCreateOptions
682
963
  ? userOption || []
683
964
  : filteredOptions
684
965
  }
966
+ optionsGroupsSelectable={optionsGroupsSelectable && multiple}
967
+ optionsGroupsStyle={optionsGroupsStyle}
685
968
  optionsMaxHeight={optionsMaxHeight}
686
969
  optionsStyle={optionsStyle}
687
970
  optionsTemplate={optionsTemplate}
@@ -703,22 +986,30 @@ CMultiSelect.propTypes = {
703
986
  allowCreateOptions: PropTypes.bool,
704
987
  ariaCleanerLabel: PropTypes.string,
705
988
  ariaIndicatorLabel: PropTypes.string,
989
+ ariaSearchLabel: PropTypes.string,
990
+ ariaTagDeleteLabel: PropTypes.string,
706
991
  className: PropTypes.string,
707
992
  cleaner: PropTypes.bool,
708
993
  clearSearchOnSelect: PropTypes.bool,
709
994
  container: PropTypes.any,
995
+ deselectAllLabel: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
996
+ deselectFilteredLabel: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
710
997
  disabled: PropTypes.bool,
998
+ headerTemplate: PropTypes.func,
711
999
  loading: PropTypes.bool,
712
1000
  multiple: PropTypes.bool,
713
1001
  name: PropTypes.string,
714
1002
  onChange: PropTypes.func,
715
1003
  onFilterChange: PropTypes.func,
716
1004
  onHide: PropTypes.func,
1005
+ onSelectionLimit: PropTypes.func,
717
1006
  onShow: PropTypes.func,
718
1007
  options: PropTypes.array.isRequired,
719
1008
  optionsMaxHeight: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
720
1009
  optionsStyle: PropTypes.oneOf(['checkbox', 'text']),
721
1010
  optionsTemplate: PropTypes.func,
1011
+ optionsGroupsSelectable: PropTypes.bool,
1012
+ optionsGroupsStyle: PropTypes.oneOf(['checkbox', 'text']),
722
1013
  optionsGroupsTemplate: PropTypes.func,
723
1014
  placeholder: PropTypes.string,
724
1015
  portal: PropTypes.bool,
@@ -735,6 +1026,10 @@ CMultiSelect.propTypes = {
735
1026
  searchNoResultsLabel: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
736
1027
  selectAll: PropTypes.bool,
737
1028
  selectAllLabel: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
1029
+ selectAllMode: PropTypes.oneOf(['all', 'filtered']),
1030
+ selectAllStyle: PropTypes.oneOf(['checkbox', 'text']),
1031
+ selectFilteredLabel: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
1032
+ selectionLimit: PropTypes.number,
738
1033
  selectionType: PropTypes.oneOf(['counter', 'tags', 'text']),
739
1034
  selectionTypeCounterText: PropTypes.string,
740
1035
  size: PropTypes.oneOf(['sm', 'lg']),
@@ -29,11 +29,11 @@ export const CMultiSelectNativeSelect = forwardRef<
29
29
  >(({ id, name, options, ...rest }, ref) => {
30
30
  return (
31
31
  <select
32
+ className="form-multi-select"
32
33
  id={id}
33
34
  {...(id && !name && { name: `${id}-multi-select` })} // TODO: remove in v6
34
35
  {...(name && { name: name })}
35
36
  tabIndex={-1}
36
- style={{ display: 'none' }}
37
37
  {...rest}
38
38
  ref={ref}
39
39
  >