@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
@@ -2,18 +2,14 @@ import React, {
2
2
  ElementType,
3
3
  forwardRef,
4
4
  HTMLAttributes,
5
- ReactElement,
6
- ReactNode,
5
+ useCallback,
6
+ useMemo,
7
7
  useState,
8
8
  } from 'react'
9
9
  import PropTypes from 'prop-types'
10
10
  import classNames from 'classnames'
11
11
 
12
- import type { CNavGroupProps } from '../nav/CNavGroup'
13
- import type { CNavLinkProps } from '../nav/CNavLink'
14
- import type { CNavItemProps } from '../nav/CNavItem'
15
-
16
- import { CSidebarNavContext } from './CSidebarNavContext'
12
+ import { CNavGroupContext, CNavGroupContextValue } from '../nav/CNavGroupContext'
17
13
 
18
14
  import { PolymorphicRefForwardingComponent } from '../../helpers'
19
15
 
@@ -42,54 +38,19 @@ export interface CSidebarNavProps extends HTMLAttributes<HTMLUListElement> {
42
38
  variant?: 'tree'
43
39
  }
44
40
 
45
- const isNavElement = (
46
- child: ReactNode
47
- ): child is ReactElement<CNavGroupProps | CNavLinkProps | CNavItemProps> => {
48
- if (!React.isValidElement(child)) return false
49
- const type = child.type as { displayName?: string }
50
- return (
51
- type.displayName === 'CNavGroup' ||
52
- type.displayName === 'CNavLink' ||
53
- type.displayName === 'CNavItem'
54
- )
55
- }
56
-
57
- const recursiveClone = (children: ReactNode, id?: string, updateId?: boolean): ReactNode => {
58
- return React.Children.map(children, (child, index) => {
59
- if (!isNavElement(child)) {
60
- return child
61
- }
62
-
63
- const _id = id ? (updateId ? `${id}.${index}` : `${id}`) : `${index}`
64
-
65
- if (child.props.children) {
66
- const type = child.type as { displayName?: string }
67
- const shouldUpdateId = type.displayName !== 'CNavItem'
68
-
69
- return React.cloneElement(child, {
70
- idx: _id,
71
- children: recursiveClone(child.props.children, _id, shouldUpdateId),
72
- })
73
- }
74
-
75
- return React.cloneElement(child, {
76
- idx: _id,
77
- })
78
- })
79
- }
80
-
81
41
  export const CSidebarNav: PolymorphicRefForwardingComponent<'ul', CSidebarNavProps> = forwardRef<
82
42
  HTMLUListElement,
83
43
  CSidebarNavProps
84
44
  >(({ children, as: Component = 'ul', className, compact, variant, ...rest }, ref) => {
85
- const [visibleGroup, setVisibleGroup] = useState('')
86
- const CNavContextValues = {
87
- visibleGroup,
88
- setVisibleGroup,
89
- }
45
+ const [activeId, setActiveId] = useState<string | undefined>()
46
+ const openBranch = useCallback(() => {}, [])
47
+ const contextValue = useMemo<CNavGroupContextValue>(
48
+ () => ({ activeId, setActiveId, openBranch }),
49
+ [activeId, openBranch]
50
+ )
90
51
 
91
52
  return (
92
- <CSidebarNavContext.Provider value={CNavContextValues}>
53
+ <CNavGroupContext.Provider value={contextValue}>
93
54
  <Component
94
55
  className={classNames(
95
56
  'sidebar-nav',
@@ -102,9 +63,9 @@ export const CSidebarNav: PolymorphicRefForwardingComponent<'ul', CSidebarNavPro
102
63
  ref={ref}
103
64
  {...rest}
104
65
  >
105
- {recursiveClone(children)}
66
+ {children}
106
67
  </Component>
107
- </CSidebarNavContext.Provider>
68
+ </CNavGroupContext.Provider>
108
69
  )
109
70
  })
110
71
 
@@ -44,3 +44,29 @@ test('CSidebar customize hide', async () => {
44
44
  expect(container.firstChild).toHaveClass('sidebar')
45
45
  expect(container.firstChild).toHaveClass('sidebar-sticky')
46
46
  })
47
+
48
+ test('CSidebar removes the exact transitionend listener reference on cleanup', async () => {
49
+ const addSpy = jest.spyOn(HTMLElement.prototype, 'addEventListener')
50
+ const removeSpy = jest.spyOn(HTMLElement.prototype, 'removeEventListener')
51
+
52
+ const { container, unmount } = render(<CSidebar>Test</CSidebar>)
53
+ const sidebar = container.querySelector('.sidebar')
54
+ unmount()
55
+
56
+ const handlersOn = (spy: jest.SpyInstance) =>
57
+ spy.mock.calls
58
+ .map((args, index) => ({ args, context: spy.mock.contexts[index] }))
59
+ .filter(({ args, context }) => args[0] === 'transitionend' && context === sidebar)
60
+ .map(({ args }) => args[1])
61
+
62
+ const added = handlersOn(addSpy)
63
+ const removed = handlersOn(removeSpy)
64
+
65
+ expect(added.length).toBeGreaterThan(0)
66
+ for (const handler of added) {
67
+ expect(removed).toContain(handler)
68
+ }
69
+
70
+ addSpy.mockRestore()
71
+ removeSpy.mockRestore()
72
+ })
@@ -6,7 +6,8 @@ import { colorPropType } from '../../props'
6
6
  import type { Colors } from '../../types'
7
7
 
8
8
  export interface CTableDataCellProps
9
- extends Omit<TdHTMLAttributes<HTMLTableCellElement>, 'align'>,
9
+ extends
10
+ Omit<TdHTMLAttributes<HTMLTableCellElement>, 'align'>,
10
11
  Omit<ThHTMLAttributes<HTMLTableCellElement>, 'align'> {
11
12
  /**
12
13
  * Highlight a table row or cell.
@@ -1,3 +1,5 @@
1
+ import { CSSProperties } from 'react'
2
+
1
3
  import { CTableDataCellProps } from '../table/CTableDataCell'
2
4
  import { CTableHeaderCellProps } from '../table/CTableHeaderCell'
3
5
  import { CTableRowProps } from '../table/CTableRow'
@@ -5,7 +7,7 @@ import { CTableRowProps } from '../table/CTableRow'
5
7
  export type Column = {
6
8
  label?: string
7
9
  key: string
8
- _style?: any
10
+ _style?: CSSProperties
9
11
  _props?: CTableHeaderCellProps
10
12
  }
11
13
 
@@ -2,16 +2,16 @@ import type { Column, Item } from './types'
2
2
 
3
3
  export const pretifyName = (name: string) => {
4
4
  return name
5
- .replace(/[-_.]/g, ' ')
6
- .replace(/ +/g, ' ')
7
- .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
5
+ .replaceAll(/[-_.]/g, ' ')
6
+ .replaceAll(/ +/g, ' ')
7
+ .replaceAll(/([a-z0-9])([A-Z])/g, '$1 $2')
8
8
  .split(' ')
9
9
  .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
10
10
  .join(' ')
11
11
  }
12
12
 
13
13
  export const getColumnLabel = (column: Column | string) =>
14
- typeof column === 'object' ? column.label ?? pretifyName(column.key) : pretifyName(column)
14
+ typeof column === 'object' ? (column.label ?? pretifyName(column.key)) : pretifyName(column)
15
15
 
16
16
  export const getColumnNames = (columns: (string | Column)[] | undefined, items?: Item[]) =>
17
17
  columns
@@ -44,7 +44,9 @@ export const CTabPanel = forwardRef<HTMLDivElement, CTabPanelProps>(
44
44
  const [_visible, setVisible] = useState(visible || _activeItemKey === itemKey)
45
45
 
46
46
  useEffect(() => {
47
- visible !== undefined && setVisible(visible)
47
+ if (visible !== undefined) {
48
+ setVisible(visible)
49
+ }
48
50
  }, [visible])
49
51
 
50
52
  useEffect(() => {
@@ -6,4 +6,4 @@ export interface CTabsContextProps {
6
6
  id?: string
7
7
  }
8
8
 
9
- export const CTabsContext = createContext({} as CTabsContextProps)
9
+ export const CTabsContext = createContext({} as CTabsContextProps)
@@ -173,6 +173,14 @@ export const CTooltip = forwardRef<HTMLDivElement, CTooltipProps>(
173
173
  const { initPopper, destroyPopper, updatePopper } = usePopper()
174
174
 
175
175
  const _delay = typeof delay === 'number' ? { show: delay, hide: delay } : delay
176
+ const timer = useRef<ReturnType<typeof setTimeout>>(undefined)
177
+
178
+ const clearTimer = () => {
179
+ if (timer.current) {
180
+ clearTimeout(timer.current)
181
+ timer.current = undefined
182
+ }
183
+ }
176
184
 
177
185
  const defaultPopperConfig: Partial<Options> = {
178
186
  modifiers: [
@@ -189,12 +197,23 @@ export const CTooltip = forwardRef<HTMLDivElement, CTooltipProps>(
189
197
  }
190
198
 
191
199
  const handleShow = () => {
192
- setMounted(true)
200
+ clearTimer()
201
+
202
+ if (mounted) {
203
+ timer.current = setTimeout(() => {
204
+ setVisible(true)
205
+ }, _delay.show)
206
+ } else {
207
+ setMounted(true)
208
+ }
209
+
193
210
  onShow?.()
194
211
  }
195
212
 
196
213
  const handleHide = () => {
197
- setTimeout(() => {
214
+ clearTimer()
215
+
216
+ timer.current = setTimeout(() => {
198
217
  setVisible(false)
199
218
  onHide?.()
200
219
  }, _delay.hide)
@@ -211,7 +230,7 @@ export const CTooltip = forwardRef<HTMLDivElement, CTooltipProps>(
211
230
  useEffect(() => {
212
231
  if (mounted && togglerRef.current && tooltipRef.current) {
213
232
  initPopper(togglerRef.current, tooltipRef.current, computedPopperConfig)
214
- setTimeout(() => {
233
+ timer.current = setTimeout(() => {
215
234
  setVisible(true)
216
235
  }, _delay.show)
217
236
 
@@ -223,6 +242,13 @@ export const CTooltip = forwardRef<HTMLDivElement, CTooltipProps>(
223
242
  }
224
243
  }, [mounted])
225
244
 
245
+ useEffect(() => {
246
+ return () => {
247
+ clearTimer()
248
+ destroyPopper()
249
+ }
250
+ }, [])
251
+
226
252
  useEffect(() => {
227
253
  if (!_visible && togglerRef.current && tooltipRef.current) {
228
254
  executeAfterTransition(() => {
@@ -235,23 +261,47 @@ export const CTooltip = forwardRef<HTMLDivElement, CTooltipProps>(
235
261
  updatePopper()
236
262
  }, [content])
237
263
 
264
+ const child = children as React.ReactElement<
265
+ React.HTMLAttributes<HTMLElement> & { ref?: React.Ref<HTMLElement> }
266
+ >
267
+
238
268
  return (
239
269
  <>
240
- {React.cloneElement(children as React.ReactElement<any>, {
270
+ {React.cloneElement(child, {
241
271
  ...(_visible && {
242
272
  'aria-describedby': id,
243
273
  }),
244
274
  ref: togglerRef,
245
275
  ...((trigger === 'click' || trigger.includes('click')) && {
246
- onClick: () => (_visible ? handleHide() : handleShow()),
276
+ onClick: (event: React.MouseEvent<HTMLElement>) => {
277
+ child.props.onClick?.(event)
278
+
279
+ if (_visible) {
280
+ handleHide()
281
+ } else {
282
+ handleShow()
283
+ }
284
+ },
247
285
  }),
248
286
  ...((trigger === 'focus' || trigger.includes('focus')) && {
249
- onFocus: () => handleShow(),
250
- onBlur: () => handleHide(),
287
+ onFocus: (event: React.FocusEvent<HTMLElement>) => {
288
+ child.props.onFocus?.(event)
289
+ handleShow()
290
+ },
291
+ onBlur: (event: React.FocusEvent<HTMLElement>) => {
292
+ child.props.onBlur?.(event)
293
+ handleHide()
294
+ },
251
295
  }),
252
296
  ...((trigger === 'hover' || trigger.includes('hover')) && {
253
- onMouseEnter: () => handleShow(),
254
- onMouseLeave: () => handleHide(),
297
+ onMouseEnter: (event: React.MouseEvent<HTMLElement>) => {
298
+ child.props.onMouseEnter?.(event)
299
+ handleShow()
300
+ },
301
+ onMouseLeave: (event: React.MouseEvent<HTMLElement>) => {
302
+ child.props.onMouseLeave?.(event)
303
+ handleHide()
304
+ },
255
305
  }),
256
306
  })}
257
307
  <CConditionalPortal container={container} portal={true}>
@@ -13,7 +13,7 @@ beforeEach(() => {
13
13
  })
14
14
 
15
15
  afterEach(() => {
16
- container && container.remove()
16
+ container?.remove()
17
17
  container = null
18
18
  })
19
19
 
@@ -47,6 +47,101 @@ test('CTooltip customize', async () => {
47
47
  jest.useRealTimers()
48
48
  })
49
49
 
50
+ test('CTooltip stays visible when re-hovered during the hide delay', async () => {
51
+ jest.useFakeTimers()
52
+
53
+ render(
54
+ <CTooltip trigger="hover" delay={{ show: 0, hide: 300 }} content="content">
55
+ <CLink>Test</CLink>
56
+ </CTooltip>
57
+ )
58
+
59
+ const link = screen.getByText('Test')
60
+
61
+ act(() => {
62
+ fireEvent.mouseOver(link)
63
+ })
64
+
65
+ act(() => {
66
+ jest.runAllTimers()
67
+ })
68
+
69
+ expect(document.querySelector('.tooltip')).toHaveClass('show')
70
+
71
+ act(() => {
72
+ fireEvent.mouseOut(link)
73
+ jest.advanceTimersByTime(100)
74
+ })
75
+
76
+ act(() => {
77
+ fireEvent.mouseOver(link)
78
+ jest.advanceTimersByTime(1000)
79
+ })
80
+
81
+ expect(document.querySelector('.tooltip')).toHaveClass('show')
82
+
83
+ jest.useRealTimers()
84
+ })
85
+
86
+ test('CTooltip cancels the pending show timer when pointer leaves before the show delay', async () => {
87
+ jest.useFakeTimers()
88
+
89
+ render(
90
+ <CTooltip trigger="hover" delay={{ show: 300, hide: 0 }} content="content">
91
+ <CLink>Test</CLink>
92
+ </CTooltip>
93
+ )
94
+
95
+ const link = screen.getByText('Test')
96
+
97
+ act(() => {
98
+ fireEvent.mouseOver(link)
99
+ jest.advanceTimersByTime(100)
100
+ })
101
+
102
+ act(() => {
103
+ fireEvent.mouseOut(link)
104
+ jest.advanceTimersByTime(1000)
105
+ })
106
+
107
+ expect(document.querySelector('.tooltip.show')).toBeNull()
108
+ expect(link).not.toHaveAttribute('aria-describedby')
109
+
110
+ jest.useRealTimers()
111
+ })
112
+
113
+ test('CTooltip preserves event handlers of the child element', async () => {
114
+ jest.useFakeTimers()
115
+
116
+ const onClick = jest.fn()
117
+ const onMouseEnter = jest.fn()
118
+
119
+ render(
120
+ <CTooltip trigger={['hover', 'click']} content="content">
121
+ <CButton onClick={onClick} onMouseEnter={onMouseEnter}>
122
+ Test
123
+ </CButton>
124
+ </CTooltip>
125
+ )
126
+
127
+ const btn = screen.getByRole('button', { name: /test/i })
128
+
129
+ act(() => {
130
+ fireEvent.mouseOver(btn)
131
+ jest.runAllTimers()
132
+ })
133
+
134
+ act(() => {
135
+ fireEvent.click(btn)
136
+ jest.runAllTimers()
137
+ })
138
+
139
+ expect(onMouseEnter).toHaveBeenCalledTimes(1)
140
+ expect(onClick).toHaveBeenCalledTimes(1)
141
+
142
+ jest.useRealTimers()
143
+ })
144
+
50
145
  test('CTooltip onShow and onHide', async () => {
51
146
  jest.useFakeTimers()
52
147
 
@@ -8,7 +8,7 @@ export interface AsProp<As extends ElementType = ElementType> {
8
8
  as?: As
9
9
  }
10
10
 
11
- export interface Props<As extends ElementType = ElementType> extends AsProp<As> {}
11
+ export type Props<As extends ElementType = ElementType> = AsProp<As>
12
12
 
13
13
  export interface PolymorphicRefForwardingComponent<TInitial extends ElementType, P = unknown> {
14
14
  <As extends ElementType = TInitial>(
@@ -37,10 +37,10 @@ const setTheme = (colorMode: string) => {
37
37
  }
38
38
 
39
39
  export const useColorModes = (
40
- localStorageItemName = 'coreui-react-color-scheme',
40
+ localStorageItemName = 'coreui-react-color-scheme'
41
41
  ): UseColorModesOutput => {
42
42
  const [colorMode, setColorMode] = useState<string | undefined>(
43
- getPreferredColorScheme(localStorageItemName),
43
+ getPreferredColorScheme(localStorageItemName)
44
44
  )
45
45
 
46
46
  useEffect(() => {
@@ -13,24 +13,30 @@ export type AssignableRef<ValueType> =
13
13
  export function useForkedRef<RefValueType = any>(
14
14
  ...refs: (AssignableRef<RefValueType> | null | undefined)[]
15
15
  ) {
16
- return useMemo(() => {
17
- if (refs.every((ref) => ref == null)) {
18
- return null
19
- }
20
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
21
- return (node: any) => {
22
- refs.forEach((ref) => {
23
- assignRef(ref, node)
24
- })
25
- }
26
- }, refs)
16
+ return useMemo(
17
+ () => {
18
+ if (refs.every((ref) => ref == null)) {
19
+ return null
20
+ }
21
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
22
+ return (node: any) => {
23
+ refs.forEach((ref) => {
24
+ assignRef(ref, node)
25
+ })
26
+ }
27
+ },
28
+ // useForkedRef accepts a variable number of refs, so the dependency list
29
+ // cannot be a static array literal; the refs are compared element-wise.
30
+ // eslint-disable-next-line react-hooks/use-memo, react-hooks/exhaustive-deps
31
+ refs
32
+ )
27
33
  }
28
34
 
29
35
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
30
36
  export function assignRef<RefValueType = any>(
31
37
  ref: AssignableRef<RefValueType> | null | undefined,
32
38
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
33
- value: any,
39
+ value: any
34
40
  ) {
35
41
  if (ref == null) return
36
42
  if (isFunction(ref)) {
@@ -44,7 +50,6 @@ export function assignRef<RefValueType = any>(
44
50
  }
45
51
  }
46
52
 
47
- // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
48
- export function isFunction(value: any): value is Function {
49
- return !!(value && {}.toString.call(value) == '[object Function]')
53
+ export function isFunction(value: unknown): value is (...args: unknown[]) => unknown {
54
+ return typeof value === 'function'
50
55
  }
@@ -1,4 +1,4 @@
1
- import { useRef } from 'react'
1
+ import { useCallback, useRef } from 'react'
2
2
  import { createPopper } from '@popperjs/core'
3
3
  import type { Instance, Options } from '@popperjs/core'
4
4
 
@@ -13,12 +13,16 @@ export const usePopper = (): UsePopperOutput => {
13
13
  const _popper = useRef<Instance>(undefined)
14
14
  const el = useRef<HTMLElement>(null)
15
15
 
16
- const initPopper = (reference: HTMLElement, popper: HTMLElement, options: Partial<Options>) => {
17
- _popper.current = createPopper(reference, popper, options)
18
- el.current = popper
19
- }
16
+ const initPopper = useCallback(
17
+ (reference: HTMLElement, popper: HTMLElement, options: Partial<Options>) => {
18
+ _popper.current?.destroy()
19
+ _popper.current = createPopper(reference, popper, options)
20
+ el.current = popper
21
+ },
22
+ []
23
+ )
20
24
 
21
- const destroyPopper = () => {
25
+ const destroyPopper = useCallback(() => {
22
26
  const popperInstance = _popper.current
23
27
 
24
28
  if (popperInstance && el.current) {
@@ -26,9 +30,9 @@ export const usePopper = (): UsePopperOutput => {
26
30
  }
27
31
 
28
32
  _popper.current = undefined
29
- }
33
+ }, [])
30
34
 
31
- const updatePopper = (options?: Partial<Options>) => {
35
+ const updatePopper = useCallback((options?: Partial<Options>) => {
32
36
  const popperInstance = _popper.current
33
37
 
34
38
  if (popperInstance && options) {
@@ -38,7 +42,7 @@ export const usePopper = (): UsePopperOutput => {
38
42
  if (popperInstance) {
39
43
  popperInstance.update()
40
44
  }
41
- }
45
+ }, [])
42
46
 
43
47
  return {
44
48
  popper: _popper.current,
@@ -0,0 +1,82 @@
1
+ import Swipe from '../swipe'
2
+
3
+ const touchEvent = (type: string, clientX: number): Event => {
4
+ const event = new Event(type) as Event & { touches: { clientX: number }[] }
5
+ event.touches = [{ clientX }]
6
+ return event
7
+ }
8
+
9
+ describe('Swipe', () => {
10
+ let element: HTMLElement
11
+ const originalPointerEvent = window.PointerEvent
12
+
13
+ beforeEach(() => {
14
+ element = document.createElement('div')
15
+ document.body.appendChild(element)
16
+ // Force the touch-event path and make the helper consider touch supported.
17
+ ;(window as unknown as { PointerEvent?: unknown }).PointerEvent = undefined
18
+ Object.defineProperty(navigator, 'maxTouchPoints', { value: 1, configurable: true })
19
+ })
20
+
21
+ afterEach(() => {
22
+ element.remove()
23
+ ;(window as unknown as { PointerEvent?: unknown }).PointerEvent = originalPointerEvent
24
+ })
25
+
26
+ it('reports support when touch points are available', () => {
27
+ expect(Swipe.isSupported()).toBe(true)
28
+ })
29
+
30
+ it('calls onLeft when swiping left past the threshold', () => {
31
+ const onLeft = jest.fn()
32
+ const onRight = jest.fn()
33
+ const onEnd = jest.fn()
34
+ new Swipe(element, { onLeft, onRight, onEnd })
35
+
36
+ element.dispatchEvent(touchEvent('touchstart', 200))
37
+ element.dispatchEvent(touchEvent('touchmove', 100))
38
+ element.dispatchEvent(touchEvent('touchend', 100))
39
+
40
+ expect(onLeft).toHaveBeenCalledTimes(1)
41
+ expect(onRight).not.toHaveBeenCalled()
42
+ expect(onEnd).toHaveBeenCalledTimes(1)
43
+ })
44
+
45
+ it('calls onRight when swiping right past the threshold', () => {
46
+ const onLeft = jest.fn()
47
+ const onRight = jest.fn()
48
+ new Swipe(element, { onLeft, onRight })
49
+
50
+ element.dispatchEvent(touchEvent('touchstart', 100))
51
+ element.dispatchEvent(touchEvent('touchmove', 200))
52
+ element.dispatchEvent(touchEvent('touchend', 200))
53
+
54
+ expect(onRight).toHaveBeenCalledTimes(1)
55
+ expect(onLeft).not.toHaveBeenCalled()
56
+ })
57
+
58
+ it('ignores swipes below the threshold', () => {
59
+ const onLeft = jest.fn()
60
+ const onRight = jest.fn()
61
+ new Swipe(element, { onLeft, onRight })
62
+
63
+ element.dispatchEvent(touchEvent('touchstart', 200))
64
+ element.dispatchEvent(touchEvent('touchmove', 180))
65
+ element.dispatchEvent(touchEvent('touchend', 180))
66
+
67
+ expect(onLeft).not.toHaveBeenCalled()
68
+ expect(onRight).not.toHaveBeenCalled()
69
+ })
70
+
71
+ it('stops detecting swipes after dispose', () => {
72
+ const onLeft = jest.fn()
73
+ const swipe = new Swipe(element, { onLeft })
74
+ swipe.dispose()
75
+
76
+ element.dispatchEvent(touchEvent('touchstart', 200))
77
+ element.dispatchEvent(touchEvent('touchmove', 100))
78
+ element.dispatchEvent(touchEvent('touchend', 100))
79
+
80
+ expect(onLeft).not.toHaveBeenCalled()
81
+ })
82
+ })
@@ -25,7 +25,7 @@ const executeAfterTransition = (
25
25
 
26
26
  let called = false
27
27
 
28
- const handler = ({ target }: { target: any }) => {
28
+ const handler = ({ target }: Event) => {
29
29
  if (target !== transitionElement) {
30
30
  return
31
31
  }
@@ -8,6 +8,7 @@ import isInViewport from './isInViewport'
8
8
  import isObjectInArray from './isObjectInArray'
9
9
  import isRTL from './isRTL'
10
10
  import mergeClassNames from './mergeClassNames'
11
+ import Swipe from './swipe'
11
12
 
12
13
  export {
13
14
  executeAfterTransition,
@@ -20,4 +21,5 @@ export {
20
21
  isObjectInArray,
21
22
  isRTL,
22
23
  mergeClassNames,
24
+ Swipe,
23
25
  }
@@ -9,7 +9,7 @@ const mergeClassNames = <T extends object>(
9
9
  const classNames: T = { ...defaultClassNames }
10
10
 
11
11
  for (const key in customClassNames) {
12
- if (customClassNames.hasOwnProperty(key)) {
12
+ if (Object.hasOwn(customClassNames, key)) {
13
13
  const typedKey = key as keyof T
14
14
  const customClassName = customClassNames[typedKey]?.toString()
15
15