@coreui/react-pro 4.9.0-next.0 → 4.9.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 (239) hide show
  1. package/README.md +1 -1
  2. package/dist/components/alert/CAlert.d.ts +1 -1
  3. package/dist/components/avatar/CAvatar.d.ts +1 -1
  4. package/dist/components/badge/CBadge.d.ts +1 -1
  5. package/dist/components/button/CButton.d.ts +1 -1
  6. package/dist/components/calendar/utils.d.ts +0 -1
  7. package/dist/components/callout/CCallout.d.ts +1 -1
  8. package/dist/components/card/CCard.d.ts +1 -1
  9. package/dist/components/conditional-portal/CConditionalPortal.d.ts +12 -0
  10. package/dist/components/conditional-portal/index.d.ts +2 -0
  11. package/dist/components/date-range-picker/CDateRangePicker.d.ts +1 -1
  12. package/dist/components/date-range-picker/utils.d.ts +1 -0
  13. package/dist/components/dropdown/CDropdown.d.ts +1 -1
  14. package/dist/components/dropdown/CDropdownToggle.d.ts +1 -1
  15. package/dist/components/form/CFormCheck.d.ts +8 -2
  16. package/dist/components/form/CFormSwitch.d.ts +6 -0
  17. package/dist/components/index.d.ts +1 -0
  18. package/dist/components/list-group/CListGroupItem.d.ts +4 -5
  19. package/dist/components/multi-select/CMultiSelect.d.ts +1 -13
  20. package/dist/components/multi-select/CMultiSelectNativeSelect.d.ts +1 -1
  21. package/dist/components/multi-select/CMultiSelectOptions.d.ts +1 -1
  22. package/dist/components/multi-select/CMultiSelectSelection.d.ts +1 -1
  23. package/dist/components/multi-select/types.d.ts +14 -0
  24. package/dist/components/multi-select/utils.d.ts +6 -0
  25. package/dist/components/navbar/CNavbar.d.ts +1 -1
  26. package/dist/components/picker/CPicker.d.ts +1 -1
  27. package/dist/components/placeholder/CPlaceholder.d.ts +1 -1
  28. package/dist/components/popover/CPopover.d.ts +1 -1
  29. package/dist/components/progress/CProgressBar.d.ts +1 -1
  30. package/dist/components/smart-table/CSmartTableHead.d.ts +1 -1
  31. package/dist/components/smart-table/CSmartTableInterface.d.ts +8 -0
  32. package/dist/components/smart-table/types.d.ts +1 -1
  33. package/dist/components/spinner/CSpinner.d.ts +1 -1
  34. package/dist/components/table/CTable.d.ts +2 -18
  35. package/dist/components/table/CTableBody.d.ts +1 -1
  36. package/dist/components/table/CTableDataCell.d.ts +1 -1
  37. package/dist/components/table/CTableFoot.d.ts +1 -1
  38. package/dist/components/table/CTableHead.d.ts +1 -1
  39. package/dist/components/table/CTableHeaderCell.d.ts +1 -1
  40. package/dist/components/table/CTableResponsiveWrapper.d.ts +0 -1
  41. package/dist/components/table/CTableRow.d.ts +1 -1
  42. package/dist/components/table/types.d.ts +17 -0
  43. package/dist/components/table/utils.d.ts +5 -0
  44. package/dist/components/toast/CToast.d.ts +1 -1
  45. package/dist/components/toast/CToastClose.d.ts +1 -1
  46. package/dist/components/tooltip/CTooltip.d.ts +1 -1
  47. package/dist/components/virtual-scroller/CVirtualScroller.d.ts +0 -8
  48. package/dist/components/widgets/CWidgetStatsA.d.ts +1 -1
  49. package/dist/components/widgets/CWidgetStatsB.d.ts +1 -1
  50. package/dist/components/widgets/CWidgetStatsC.d.ts +1 -1
  51. package/dist/components/widgets/CWidgetStatsD.d.ts +1 -1
  52. package/dist/components/widgets/CWidgetStatsF.d.ts +1 -1
  53. package/dist/index.es.js +696 -927
  54. package/dist/index.es.js.map +1 -1
  55. package/dist/index.js +695 -925
  56. package/dist/index.js.map +1 -1
  57. package/dist/props.d.ts +8 -0
  58. package/dist/types.d.ts +7 -0
  59. package/dist/utils/index.d.ts +1 -3
  60. package/package.json +11 -6
  61. package/src/components/accordion/CAccordion.tsx +7 -3
  62. package/src/components/accordion/CAccordionBody.tsx +2 -2
  63. package/src/components/accordion/CAccordionButton.tsx +1 -3
  64. package/src/components/accordion/CAccordionCollapse.tsx +1 -0
  65. package/src/components/accordion/CAccordionHeader.tsx +1 -3
  66. package/src/components/accordion/CAccordionItem.tsx +2 -4
  67. package/src/components/accordion/__tests__/__snapshots__/CAccordionBody.spec.tsx.snap +2 -2
  68. package/src/components/alert/CAlert.tsx +21 -28
  69. package/src/components/alert/CAlertHeading.tsx +1 -3
  70. package/src/components/alert/CAlertLink.tsx +1 -3
  71. package/src/components/alert/__tests__/__snapshots__/CAlert.spec.tsx.snap +1 -1
  72. package/src/components/avatar/CAvatar.tsx +16 -19
  73. package/src/components/backdrop/CBackdrop.tsx +10 -13
  74. package/src/components/badge/CBadge.tsx +21 -27
  75. package/src/components/breadcrumb/CBreadcrumb.tsx +1 -2
  76. package/src/components/breadcrumb/CBreadcrumbItem.tsx +12 -8
  77. package/src/components/button/CButton.tsx +10 -10
  78. package/src/components/button-group/CButtonGroup.tsx +9 -7
  79. package/src/components/button-group/CButtonToolbar.tsx +1 -3
  80. package/src/components/calendar/utils.ts +7 -55
  81. package/src/components/callout/CCallout.tsx +13 -10
  82. package/src/components/card/CCard.tsx +14 -11
  83. package/src/components/card/CCardBody.tsx +1 -3
  84. package/src/components/card/CCardFooter.tsx +1 -3
  85. package/src/components/card/CCardGroup.tsx +1 -3
  86. package/src/components/card/CCardHeader.tsx +1 -3
  87. package/src/components/card/CCardImage.tsx +5 -3
  88. package/src/components/card/CCardImageOverlay.tsx +1 -3
  89. package/src/components/card/CCardLink.tsx +1 -3
  90. package/src/components/card/CCardSubtitle.tsx +1 -3
  91. package/src/components/card/CCardText.tsx +1 -3
  92. package/src/components/card/CCardTitle.tsx +1 -3
  93. package/src/components/carousel/CCarousel.tsx +8 -8
  94. package/src/components/carousel/CCarouselCaption.tsx +1 -3
  95. package/src/components/carousel/CCarouselItem.tsx +11 -9
  96. package/src/components/close-button/CCloseButton.tsx +9 -10
  97. package/src/components/collapse/CCollapse.tsx +8 -20
  98. package/src/components/collapse/__tests__/CCollapse.spec.tsx +2 -3
  99. package/src/components/conditional-portal/CConditionalPortal.tsx +29 -0
  100. package/src/components/conditional-portal/index.ts +3 -0
  101. package/src/components/date-range-picker/CDateRangePicker.tsx +11 -13
  102. package/src/components/date-range-picker/utils.ts +49 -0
  103. package/src/components/dropdown/CDropdown.tsx +26 -23
  104. package/src/components/dropdown/CDropdownDivider.tsx +1 -3
  105. package/src/components/dropdown/CDropdownHeader.tsx +1 -3
  106. package/src/components/dropdown/CDropdownItem.tsx +7 -5
  107. package/src/components/dropdown/CDropdownItemPlain.tsx +1 -3
  108. package/src/components/dropdown/CDropdownMenu.tsx +29 -32
  109. package/src/components/dropdown/CDropdownToggle.tsx +34 -27
  110. package/src/components/dropdown/__tests__/CDropdown.spec.tsx +2 -4
  111. package/src/components/dropdown/__tests__/__snapshots__/CDropdown.spec.tsx.snap +1 -1
  112. package/src/components/footer/CFooter.tsx +5 -3
  113. package/src/components/form/CForm.tsx +5 -2
  114. package/src/components/form/CFormCheck.tsx +90 -66
  115. package/src/components/form/CFormControlValidation.tsx +0 -1
  116. package/src/components/form/CFormControlWrapper.tsx +3 -4
  117. package/src/components/form/CFormFeedback.tsx +11 -8
  118. package/src/components/form/CFormFloating.tsx +1 -2
  119. package/src/components/form/CFormInput.tsx +18 -16
  120. package/src/components/form/CFormLabel.tsx +1 -2
  121. package/src/components/form/CFormRange.tsx +2 -4
  122. package/src/components/form/CFormSelect.tsx +16 -12
  123. package/src/components/form/CFormSwitch.tsx +31 -20
  124. package/src/components/form/CFormText.tsx +1 -2
  125. package/src/components/form/CFormTextarea.tsx +14 -11
  126. package/src/components/form/CInputGroup.tsx +11 -8
  127. package/src/components/form/CInputGroupText.tsx +1 -2
  128. package/src/components/form/__tests__/CFormRange.spec.tsx +1 -1
  129. package/src/components/form/__tests__/__snapshots__/CForm.spec.tsx.snap +2 -6
  130. package/src/components/form/__tests__/__snapshots__/CFormRange.spec.tsx.snap +2 -2
  131. package/src/components/grid/CCol.tsx +8 -6
  132. package/src/components/grid/CContainer.tsx +8 -6
  133. package/src/components/grid/CRow.tsx +4 -3
  134. package/src/components/header/CHeader.tsx +12 -13
  135. package/src/components/header/CHeaderBrand.tsx +1 -3
  136. package/src/components/header/CHeaderBrand.tsx~HEAD +1 -3
  137. package/src/components/header/CHeaderDivider.tsx +1 -3
  138. package/src/components/header/CHeaderNav.tsx +6 -3
  139. package/src/components/header/CHeaderText.tsx +1 -3
  140. package/src/components/header/CHeaderToggler.tsx +2 -4
  141. package/src/components/image/CImage.tsx +17 -10
  142. package/src/components/image/__tests__/__snapshots__/CImage.spec.tsx.snap +1 -3
  143. package/src/components/index.ts +1 -0
  144. package/src/components/link/CLink.tsx +2 -4
  145. package/src/components/list-group/CListGroup.tsx +11 -10
  146. package/src/components/list-group/CListGroupItem.tsx +21 -20
  147. package/src/components/list-group/CListGroupItem.tsx~HEAD +3 -1
  148. package/src/components/loading-button/CLoadingButton.tsx +1 -3
  149. package/src/components/modal/CModal.tsx +57 -72
  150. package/src/components/modal/CModalBody.tsx +1 -3
  151. package/src/components/modal/CModalContent.tsx +1 -3
  152. package/src/components/modal/CModalDialog.tsx +16 -14
  153. package/src/components/modal/CModalFooter.tsx +1 -3
  154. package/src/components/modal/CModalHeader.tsx +4 -4
  155. package/src/components/modal/CModalTitle.tsx +1 -3
  156. package/src/components/modal/__tests__/CModal.spec.tsx +19 -20
  157. package/src/components/multi-select/CMultiSelect.tsx +34 -93
  158. package/src/components/multi-select/CMultiSelectNativeSelect.tsx +1 -1
  159. package/src/components/multi-select/CMultiSelectOptions.tsx +2 -2
  160. package/src/components/multi-select/CMultiSelectSelection.tsx +1 -1
  161. package/src/components/multi-select/types.ts +15 -0
  162. package/src/components/multi-select/utils.ts +92 -0
  163. package/src/components/nav/CNav.tsx +13 -10
  164. package/src/components/nav/CNavGroup.tsx +1 -3
  165. package/src/components/nav/CNavGroupItems.tsx +1 -2
  166. package/src/components/nav/CNavItem.tsx +9 -10
  167. package/src/components/nav/CNavLink.tsx +4 -5
  168. package/src/components/nav/CNavTitle.tsx +1 -2
  169. package/src/components/navbar/CNavbar.tsx +23 -23
  170. package/src/components/navbar/CNavbarBrand.tsx +2 -3
  171. package/src/components/navbar/CNavbarNav.tsx +6 -3
  172. package/src/components/navbar/CNavbarText.tsx +1 -3
  173. package/src/components/navbar/CNavbarToggler.tsx +2 -4
  174. package/src/components/offcanvas/COffcanvas.tsx +42 -68
  175. package/src/components/offcanvas/COffcanvasBody.tsx +1 -3
  176. package/src/components/offcanvas/COffcanvasHeader.tsx +1 -3
  177. package/src/components/offcanvas/COffcanvasTitle.tsx +1 -3
  178. package/src/components/offcanvas/__tests__/COffcanvas.spec.tsx +7 -2
  179. package/src/components/offcanvas/__tests__/__snapshots__/COffcanvas.spec.tsx.snap +9 -4
  180. package/src/components/pagination/CPagination.tsx +12 -9
  181. package/src/components/pagination/CPaginationItem.tsx +12 -11
  182. package/src/components/pagination/__tests__/CPagination.spec.tsx +3 -3
  183. package/src/components/pagination/__tests__/CPaginationItem.spec.tsx +3 -3
  184. package/src/components/picker/CPicker.tsx +7 -7
  185. package/src/components/placeholder/CPlaceholder.tsx +15 -12
  186. package/src/components/popover/CPopover.tsx +24 -34
  187. package/src/components/progress/CProgress.tsx +13 -10
  188. package/src/components/progress/CProgressBar.tsx +11 -12
  189. package/src/components/sidebar/CSidebar.tsx +22 -19
  190. package/src/components/sidebar/CSidebarBrand.tsx +1 -2
  191. package/src/components/sidebar/CSidebarFooter.tsx +1 -2
  192. package/src/components/sidebar/CSidebarHeader.tsx +1 -2
  193. package/src/components/sidebar/CSidebarNav.tsx +2 -2
  194. package/src/components/sidebar/CSidebarToggler.tsx +1 -2
  195. package/src/components/smart-pagination/CSmartPagination.tsx +22 -20
  196. package/src/components/smart-table/CSmartTable.tsx +5 -9
  197. package/src/components/smart-table/CSmartTableBody.tsx +82 -81
  198. package/src/components/smart-table/CSmartTableHead.tsx +5 -2
  199. package/src/components/smart-table/CSmartTableInterface.tsx +8 -0
  200. package/src/components/smart-table/{utils.tsx → utils.ts} +4 -5
  201. package/src/components/spinner/CSpinner.tsx +13 -9
  202. package/src/components/table/CTable.tsx +45 -77
  203. package/src/components/table/CTableBody.tsx +14 -9
  204. package/src/components/table/CTableDataCell.tsx +16 -11
  205. package/src/components/table/CTableFoot.tsx +14 -9
  206. package/src/components/table/CTableHead.tsx +14 -9
  207. package/src/components/table/CTableHeaderCell.tsx +14 -9
  208. package/src/components/table/CTableResponsiveWrapper.tsx +2 -3
  209. package/src/components/table/CTableRow.tsx +16 -11
  210. package/src/components/table/__tests__/__snapshots__/CTable.spec.tsx.snap +1 -5
  211. package/src/components/table/types.ts +20 -0
  212. package/src/components/table/utils.ts +24 -0
  213. package/src/components/tabs/CTabContent.tsx +1 -2
  214. package/src/components/tabs/CTabPane.tsx +18 -20
  215. package/src/components/tabs/__tests__/CTabPane.spec.tsx +2 -5
  216. package/src/components/tabs/__tests__/__snapshots__/CTabPane.spec.tsx.snap +1 -1
  217. package/src/components/time-picker/CTimePicker.tsx +14 -18
  218. package/src/components/toast/CToast.tsx +32 -45
  219. package/src/components/toast/CToastBody.tsx +1 -2
  220. package/src/components/toast/CToastClose.tsx +4 -2
  221. package/src/components/toast/CToastHeader.tsx +1 -2
  222. package/src/components/toast/CToaster.tsx +27 -26
  223. package/src/components/toast/__tests__/CToast.spec.tsx +2 -1
  224. package/src/components/toast/__tests__/__snapshots__/CToast.spec.tsx.snap +1 -66
  225. package/src/components/tooltip/CTooltip.tsx +24 -34
  226. package/src/components/virtual-scroller/CVirtualScroller.tsx +35 -60
  227. package/src/components/widgets/CWidgetStatsA.tsx +11 -9
  228. package/src/components/widgets/CWidgetStatsB.tsx +4 -4
  229. package/src/components/widgets/CWidgetStatsC.tsx +4 -4
  230. package/src/components/widgets/CWidgetStatsD.tsx +13 -15
  231. package/src/components/widgets/CWidgetStatsE.tsx +2 -5
  232. package/src/components/widgets/CWidgetStatsF.tsx +4 -8
  233. package/src/{components/Types.tsx → props.ts} +1 -68
  234. package/src/types.ts +68 -0
  235. package/src/utils/index.ts +1 -3
  236. package/src/components/multi-select/CMultiSelectCleaner.tsx +0 -18
  237. package/src/utils/getNextSibling.ts +0 -18
  238. package/src/utils/getPreviousSibling.ts +0 -18
  239. package/src/components/smart-table/{types.tsx → types.ts} +1 -1
@@ -2,23 +2,22 @@ import React, {
2
2
  createContext,
3
3
  forwardRef,
4
4
  HTMLAttributes,
5
- useCallback,
6
5
  useEffect,
7
6
  useLayoutEffect,
8
7
  useRef,
9
8
  useState,
10
9
  } from 'react'
11
- import { createPortal } from 'react-dom'
12
10
  import PropTypes from 'prop-types'
13
11
  import classNames from 'classnames'
14
12
  import { Transition } from 'react-transition-group'
15
13
 
16
- import { useForkedRef } from '../../hooks'
17
-
18
14
  import { CBackdrop } from '../backdrop/CBackdrop'
15
+ import { CConditionalPortal } from '../conditional-portal'
19
16
  import { CModalContent } from './CModalContent'
20
17
  import { CModalDialog } from './CModalDialog'
21
18
 
19
+ import { useForkedRef } from '../../hooks'
20
+
22
21
  export interface CModalProps extends HTMLAttributes<HTMLDivElement> {
23
22
  /**
24
23
  * Align the modal in the center or top of the screen.
@@ -119,22 +118,22 @@ export const CModal = forwardRef<HTMLDivElement, CModalProps>(
119
118
  const [_visible, setVisible] = useState(visible)
120
119
  const [staticBackdrop, setStaticBackdrop] = useState(false)
121
120
 
122
- useEffect(() => {
123
- setVisible(visible)
124
- }, [visible])
125
-
126
121
  const contextValues = {
127
122
  visible: _visible,
128
123
  setVisible,
129
124
  }
130
125
 
131
126
  useEffect(() => {
132
- modalRef.current && modalRef.current.addEventListener('click', handleClickOutside)
133
- modalRef.current && modalRef.current.addEventListener('keyup', handleKeyDown)
127
+ setVisible(visible)
128
+ }, [visible])
129
+
130
+ useEffect(() => {
131
+ document.addEventListener('click', handleClickOutside)
132
+ document.addEventListener('keydown', handleKeyDown)
134
133
 
135
134
  return () => {
136
- modalRef.current && modalRef.current.removeEventListener('click', handleClickOutside)
137
- modalRef.current && modalRef.current.removeEventListener('keyup', handleKeyDown)
135
+ document.removeEventListener('click', handleClickOutside)
136
+ document.removeEventListener('keydown', handleKeyDown)
138
137
  }
139
138
  }, [_visible])
140
139
 
@@ -142,6 +141,7 @@ export const CModal = forwardRef<HTMLDivElement, CModalProps>(
142
141
  if (backdrop === 'static') {
143
142
  return setStaticBackdrop(true)
144
143
  }
144
+
145
145
  return onClose && onClose()
146
146
  }
147
147
 
@@ -150,24 +150,6 @@ export const CModal = forwardRef<HTMLDivElement, CModalProps>(
150
150
  setTimeout(() => setStaticBackdrop(false), duration)
151
151
  }, [staticBackdrop])
152
152
 
153
- const getTransitionClass = (state: string) => {
154
- return state === 'entering'
155
- ? 'd-block'
156
- : state === 'entered'
157
- ? 'show d-block'
158
- : state === 'exiting'
159
- ? 'd-block'
160
- : ''
161
- }
162
- const _className = classNames(
163
- 'modal',
164
- {
165
- 'modal-static': staticBackdrop,
166
- fade: transition,
167
- },
168
- className,
169
- )
170
-
171
153
  // Set focus to modal after open
172
154
  useLayoutEffect(() => {
173
155
  if (_visible) {
@@ -182,7 +164,7 @@ export const CModal = forwardRef<HTMLDivElement, CModalProps>(
182
164
  () => {
183
165
  modalRef.current?.focus()
184
166
  },
185
- !transition ? 0 : duration,
167
+ transition ? duration : 0,
186
168
  )
187
169
  } else {
188
170
  document.body.classList.remove('modal-open')
@@ -210,37 +192,10 @@ export const CModal = forwardRef<HTMLDivElement, CModalProps>(
210
192
  }
211
193
  }
212
194
 
213
- const handleKeyDown = useCallback(
214
- (event: KeyboardEvent) => {
215
- if (event.key === 'Escape' && keyboard) {
216
- return handleDismiss()
217
- }
218
- },
219
- [modalRef, handleDismiss],
220
- )
221
-
222
- const modal = (ref?: React.Ref<HTMLDivElement>, transitionClass?: string) => {
223
- return (
224
- <CModalContext.Provider value={contextValues}>
225
- <div
226
- className={classNames(_className, transitionClass)}
227
- tabIndex={-1}
228
- role="dialog"
229
- ref={ref}
230
- >
231
- <CModalDialog
232
- alignment={alignment}
233
- fullscreen={fullscreen}
234
- scrollable={scrollable}
235
- size={size}
236
- >
237
- <CModalContent {...rest} ref={modalContentRef}>
238
- {children}
239
- </CModalContent>
240
- </CModalDialog>
241
- </div>
242
- </CModalContext.Provider>
243
- )
195
+ const handleKeyDown = (event: KeyboardEvent) => {
196
+ if (event.key === 'Escape' && keyboard) {
197
+ handleDismiss()
198
+ }
244
199
  }
245
200
 
246
201
  return (
@@ -252,18 +207,48 @@ export const CModal = forwardRef<HTMLDivElement, CModalProps>(
252
207
  onEnter={onShow}
253
208
  onExit={onClose}
254
209
  unmountOnExit={unmountOnClose}
255
- timeout={!transition ? 0 : duration}
210
+ timeout={transition ? duration : 0}
256
211
  >
257
- {(state) => {
258
- const transitionClass = getTransitionClass(state)
259
- return typeof window !== 'undefined' && portal
260
- ? createPortal(modal(forkedRef, transitionClass), document.body)
261
- : modal(forkedRef, transitionClass)
262
- }}
212
+ {(state) => (
213
+ <CConditionalPortal portal={portal}>
214
+ <CModalContext.Provider value={contextValues}>
215
+ <div
216
+ className={classNames(
217
+ 'modal',
218
+ {
219
+ 'modal-static': staticBackdrop,
220
+ fade: transition,
221
+ show: state === 'entered',
222
+ },
223
+ className,
224
+ )}
225
+ tabIndex={-1}
226
+ role="dialog"
227
+ style={{
228
+ ...(state !== 'exited' && { display: 'block' }),
229
+ }}
230
+ ref={forkedRef}
231
+ >
232
+ <CModalDialog
233
+ alignment={alignment}
234
+ fullscreen={fullscreen}
235
+ scrollable={scrollable}
236
+ size={size}
237
+ >
238
+ <CModalContent {...rest} ref={modalContentRef}>
239
+ {children}
240
+ </CModalContent>
241
+ </CModalDialog>
242
+ </div>
243
+ </CModalContext.Provider>
244
+ </CConditionalPortal>
245
+ )}
263
246
  </Transition>
264
- {typeof window !== 'undefined' && portal
265
- ? backdrop && createPortal(<CBackdrop visible={_visible} />, document.body)
266
- : backdrop && <CBackdrop visible={_visible} />}
247
+ {backdrop && (
248
+ <CConditionalPortal portal={portal}>
249
+ <CBackdrop visible={_visible} />
250
+ </CConditionalPortal>
251
+ )}
267
252
  </>
268
253
  )
269
254
  },
@@ -11,10 +11,8 @@ export interface CModalBodyProps extends HTMLAttributes<HTMLDivElement> {
11
11
 
12
12
  export const CModalBody = forwardRef<HTMLDivElement, CModalBodyProps>(
13
13
  ({ children, className, ...rest }, ref) => {
14
- const _className = classNames('modal-body', className)
15
-
16
14
  return (
17
- <div className={_className} {...rest} ref={ref}>
15
+ <div className={classNames('modal-body', className)} {...rest} ref={ref}>
18
16
  {children}
19
17
  </div>
20
18
  )
@@ -11,10 +11,8 @@ export interface CModalContentProps extends HTMLAttributes<HTMLDivElement> {
11
11
 
12
12
  export const CModalContent = forwardRef<HTMLDivElement, CModalContentProps>(
13
13
  ({ children, className, ...rest }, ref) => {
14
- const _className = classNames('modal-content', className)
15
-
16
14
  return (
17
- <div className={_className} {...rest} ref={ref}>
15
+ <div className={classNames('modal-content', className)} {...rest} ref={ref}>
18
16
  {children}
19
17
  </div>
20
18
  )
@@ -27,21 +27,23 @@ export interface CModalDialogProps extends HTMLAttributes<HTMLDivElement> {
27
27
 
28
28
  export const CModalDialog = forwardRef<HTMLDivElement, CModalDialogProps>(
29
29
  ({ children, alignment, className, fullscreen, scrollable, size, ...rest }, ref) => {
30
- const _className = classNames(
31
- 'modal-dialog',
32
- {
33
- 'modal-dialog-centered': alignment === 'center',
34
- [typeof fullscreen === 'boolean'
35
- ? 'modal-fullscreen'
36
- : `modal-fullscreen-${fullscreen}-down`]: fullscreen,
37
- 'modal-dialog-scrollable': scrollable,
38
- [`modal-${size}`]: size,
39
- },
40
- className,
41
- )
42
-
43
30
  return (
44
- <div className={_className} {...rest} ref={ref}>
31
+ <div
32
+ className={classNames(
33
+ 'modal-dialog',
34
+ {
35
+ 'modal-dialog-centered': alignment === 'center',
36
+ [typeof fullscreen === 'boolean'
37
+ ? 'modal-fullscreen'
38
+ : `modal-fullscreen-${fullscreen}-down`]: fullscreen,
39
+ 'modal-dialog-scrollable': scrollable,
40
+ [`modal-${size}`]: size,
41
+ },
42
+ className,
43
+ )}
44
+ {...rest}
45
+ ref={ref}
46
+ >
45
47
  {children}
46
48
  </div>
47
49
  )
@@ -11,10 +11,8 @@ export interface CModalFooterProps extends HTMLAttributes<HTMLDivElement> {
11
11
 
12
12
  export const CModalFooter = forwardRef<HTMLDivElement, CModalFooterProps>(
13
13
  ({ children, className, ...rest }, ref) => {
14
- const _className = classNames('modal-footer', className)
15
-
16
14
  return (
17
- <div className={_className} {...rest} ref={ref}>
15
+ <div className={classNames('modal-footer', className)} {...rest} ref={ref}>
18
16
  {children}
19
17
  </div>
20
18
  )
@@ -1,9 +1,10 @@
1
1
  import React, { forwardRef, HTMLAttributes, useContext } from 'react'
2
2
  import PropTypes from 'prop-types'
3
- import { CModalContext } from './CModal'
4
- import { CCloseButton } from '../close-button/CCloseButton'
5
3
  import classNames from 'classnames'
6
4
 
5
+ import { CCloseButton } from '../close-button/CCloseButton'
6
+ import { CModalContext } from './CModal'
7
+
7
8
  export interface CModalHeaderProps extends HTMLAttributes<HTMLDivElement> {
8
9
  /**
9
10
  * A string of all className you want applied to the base component.
@@ -18,10 +19,9 @@ export interface CModalHeaderProps extends HTMLAttributes<HTMLDivElement> {
18
19
  export const CModalHeader = forwardRef<HTMLDivElement, CModalHeaderProps>(
19
20
  ({ children, className, closeButton = true, ...rest }, ref) => {
20
21
  const { setVisible } = useContext(CModalContext)
21
- const _className = classNames('modal-header', className)
22
22
 
23
23
  return (
24
- <div className={_className} {...rest} ref={ref}>
24
+ <div className={classNames('modal-header', className)} {...rest} ref={ref}>
25
25
  {children}
26
26
  {closeButton && <CCloseButton onClick={() => setVisible(false)} />}
27
27
  </div>
@@ -15,10 +15,8 @@ export interface CModalTitleProps extends HTMLAttributes<HTMLHeadingElement> {
15
15
 
16
16
  export const CModalTitle = forwardRef<HTMLHeadElement, CModalTitleProps>(
17
17
  ({ children, component: Component = 'h5', className, ...rest }, ref) => {
18
- const _className = classNames('modal-title', className)
19
-
20
18
  return (
21
- <Component className={_className} {...rest} ref={ref}>
19
+ <Component className={classNames('modal-title', className)} {...rest} ref={ref}>
22
20
  {children}
23
21
  </Component>
24
22
  )
@@ -26,7 +26,6 @@ test('CModal customize', async () => {
26
26
  })
27
27
 
28
28
  test('CModal dialog close on press ESC', async () => {
29
- jest.useFakeTimers()
30
29
  const onClose = jest.fn()
31
30
  render(
32
31
  <CModal onClose={onClose} portal={false} visible>
@@ -36,32 +35,32 @@ test('CModal dialog close on press ESC', async () => {
36
35
  expect(onClose).toHaveBeenCalledTimes(0)
37
36
  const modal = document.querySelector('.modal')
38
37
  if (modal !== null) {
39
- fireEvent.keyUp(modal, {
38
+ fireEvent.keyDown(modal, {
40
39
  key: 'Escape',
41
40
  code: 'Escape',
42
41
  keyCode: 27,
43
42
  charCode: 27,
44
43
  })
45
44
  }
45
+ await new Promise((r) => setTimeout(r, 1000))
46
+ console.log(modal)
47
+ expect(onClose).toHaveBeenCalledTimes(1)
48
+ })
49
+
50
+ test('CModal dialog close on backdrop', async () => {
51
+ jest.useFakeTimers()
52
+ const onClose = jest.fn()
53
+ render(
54
+ <CModal onClose={onClose} portal={false} visible={true}>
55
+ Test
56
+ </CModal>,
57
+ )
58
+ expect(onClose).toHaveBeenCalledTimes(0)
59
+ const backdrop = document.querySelector('.modal-backdrop')
60
+ if (backdrop !== null) {
61
+ fireEvent.click(backdrop)
62
+ }
46
63
  jest.runAllTimers()
47
64
  expect(onClose).toHaveBeenCalledTimes(1)
48
65
  jest.useRealTimers()
49
66
  })
50
-
51
- // test('CModal dialog close on backdrop', async () => {
52
- // jest.useFakeTimers()
53
- // const onClose = jest.fn()
54
- // render(
55
- // <CModal onClose={onClose} portal={false} visible={true}>
56
- // Test
57
- // </CModal>,
58
- // )
59
- // expect(onClose).toHaveBeenCalledTimes(0)
60
- // const backdrop = document.querySelector('.modal-backdrop')
61
- // if (backdrop !== null) {
62
- // fireEvent.click(backdrop)
63
- // }
64
- // jest.runAllTimers()
65
- // expect(onClose).toHaveBeenCalledTimes(1)
66
- // jest.useRealTimers()
67
- // })
@@ -16,11 +16,13 @@ import PropTypes from 'prop-types'
16
16
  import { CFormControlWrapper, CFormControlWrapperProps } from './../form/CFormControlWrapper'
17
17
  import { CPicker } from '../picker/CPicker'
18
18
 
19
- import { CMultiSelectCleaner } from './CMultiSelectCleaner'
20
19
  import { CMultiSelectNativeSelect } from './CMultiSelectNativeSelect'
21
20
  import { CMultiSelectOptions } from './CMultiSelectOptions'
22
21
  import { CMultiSelectSelection } from './CMultiSelectSelection'
23
22
 
23
+ import { filterOptionsList, flattenArray, selectOptions } from './utils'
24
+ import type { Option, SelectedOption } from './types'
25
+
24
26
  export interface CMultiSelectProps
25
27
  extends Omit<CFormControlWrapperProps, 'floatingClassName' | 'floatingLabel'>,
26
28
  Omit<HTMLAttributes<HTMLDivElement>, 'onChange'> {
@@ -113,29 +115,6 @@ export interface CMultiSelectProps
113
115
  visibleItems?: number
114
116
  }
115
117
 
116
- export interface Option {
117
- disabled?: boolean
118
- label?: string
119
- options?: Option[]
120
- selected?: boolean
121
- // TODO: change to label
122
- text?: string
123
- value?: number | string
124
- }
125
-
126
- export interface SelectedOption {
127
- disabled?: boolean
128
- text: string
129
- value: number | string
130
- }
131
-
132
- const flattenArray = (options: Option[]): Option[] =>
133
- options.reduce(
134
- (acc: Option[], val: Option) =>
135
- acc.concat(Array.isArray(val.options) ? flattenArray(val.options) : val),
136
- [],
137
- )
138
-
139
118
  export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
140
119
  (
141
120
  {
@@ -181,30 +160,6 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
181
160
  const [_visible, setVisible] = useState(visible)
182
161
  const [selected, setSelected] = useState<SelectedOption[]>([])
183
162
 
184
- const selectOptions = (options: Option[], deselected?: Option[]) => {
185
- let _selected = [...selected, ...options]
186
-
187
- if (deselected) {
188
- _selected = _selected.filter(
189
- (selectedOption) =>
190
- !deselected.some((deselectedOption) => deselectedOption.value === selectedOption.value),
191
- )
192
- }
193
-
194
- const deduplicated = _selected.reduce((unique: Option[], option) => {
195
- if (!unique.some((obj) => obj.value === option.value)) {
196
- unique.push({
197
- value: option.value,
198
- text: option.text,
199
- ...(option.disabled && { disabled: option.disabled }),
200
- })
201
- }
202
- return unique
203
- }, []) as SelectedOption[]
204
-
205
- setSelected(deduplicated)
206
- }
207
-
208
163
  useMemo(() => {
209
164
  setOptions(options)
210
165
  const _selected =
@@ -227,7 +182,7 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
227
182
  return
228
183
  })
229
184
 
230
- _selected && selectOptions(_selected, deselected)
185
+ _selected && setSelected(selectOptions(_selected, selected, deselected))
231
186
  }, [JSON.stringify(options)])
232
187
 
233
188
  useEffect(() => {
@@ -236,47 +191,10 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
236
191
  nativeSelectRef.current.dispatchEvent(new Event('change', { bubbles: true }))
237
192
  }, [JSON.stringify(selected)])
238
193
 
239
- const _className = classNames(
240
- 'form-multi-select',
241
- {
242
- 'form-multi-select-with-cleaner': cleaner,
243
- [`form-multi-select-${size}`]: size,
244
- show: _visible,
245
- 'form-multi-select-selection-tags': multiple && selectionType === 'tags',
246
- disabled,
247
- 'is-invalid': invalid,
248
- 'is-valid': valid,
249
- },
250
- className,
251
- )
252
-
253
194
  useEffect(() => {
254
195
  isInitialMount.current = false
255
196
  }, [])
256
197
 
257
- const filterOptionsList = (search: string, options: Option[]) => {
258
- return search.length
259
- ? options &&
260
- options.reduce((acc: Option[], val: Option) => {
261
- const options =
262
- val.options &&
263
- val.options.filter(
264
- (element) =>
265
- element.text && element.text.toLowerCase().includes(search.toLowerCase()),
266
- )
267
-
268
- if (
269
- (val.text && val.text.toLowerCase().includes(search.toLowerCase())) ||
270
- (options && options.length)
271
- ) {
272
- acc.push(Object.assign({}, val, options && options.length && { options }))
273
- }
274
-
275
- return acc
276
- }, [])
277
- : _options
278
- }
279
-
280
198
  const handleSearchChange = (event: FormEvent<HTMLInputElement>) => {
281
199
  const target = event.target as HTMLInputElement
282
200
  if (search !== 'external') {
@@ -287,7 +205,9 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
287
205
  }
288
206
 
289
207
  const handleSearchKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
290
- if (_search.length) return
208
+ if (_search.length > 0) {
209
+ return
210
+ }
291
211
 
292
212
  if (event.key === 'Backspace' || event.key === 'Delete') {
293
213
  const last = selected.filter((option: Option) => !option.disabled).pop()
@@ -315,7 +235,12 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
315
235
  }
316
236
 
317
237
  const handleSelectAll = () => {
318
- selectOptions(flattenArray(options).filter((option: Option) => !option.disabled))
238
+ setSelected(
239
+ selectOptions(
240
+ flattenArray(options).filter((option: Option) => !option.disabled),
241
+ selected,
242
+ ),
243
+ )
319
244
  }
320
245
 
321
246
  const handleDeselectAll = () => {
@@ -347,7 +272,19 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
347
272
  ref={nativeSelectRef}
348
273
  />
349
274
  <CPicker
350
- className={_className}
275
+ className={classNames(
276
+ 'form-multi-select',
277
+ {
278
+ 'form-multi-select-with-cleaner': cleaner,
279
+ [`form-multi-select-${size}`]: size,
280
+ show: _visible,
281
+ 'form-multi-select-selection-tags': multiple && selectionType === 'tags',
282
+ disabled,
283
+ 'is-invalid': invalid,
284
+ 'is-valid': valid,
285
+ },
286
+ className,
287
+ )}
351
288
  disabled={disabled}
352
289
  id={id}
353
290
  onHide={() => setVisible(false)}
@@ -366,7 +303,11 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
366
303
  selectionTypeCounterText={selectionTypeCounterText}
367
304
  />
368
305
  {multiple && cleaner && selected.length > 0 && (
369
- <CMultiSelectCleaner onClick={() => handleDeselectAll()} />
306
+ <button
307
+ type="button"
308
+ className="form-multi-select-selection-cleaner"
309
+ onClick={() => handleDeselectAll()}
310
+ ></button>
370
311
  )}
371
312
  {search && (
372
313
  <input
@@ -376,14 +317,14 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
376
317
  onChange={(event) => handleSearchChange(event)}
377
318
  onKeyDown={(event) => handleSearchKeyDown(event)}
378
319
  {...(selected.length === 0 && { placeholder: placeholder })}
379
- {...(selected.length &&
320
+ {...(selected.length > 0 &&
380
321
  selectionType === 'counter' && {
381
322
  placeholder: `${selected.length} ${selectionTypeCounterText}`,
382
323
  })}
383
- {...(selected.length &&
324
+ {...(selected.length > 0 &&
384
325
  !multiple && { placeholder: selected.map((option) => option.text)[0] })}
385
326
  {...(multiple &&
386
- selected.length &&
327
+ selected.length > 0 &&
387
328
  selectionType !== 'counter' && { size: _search.length + 2 })}
388
329
  ref={searchRef}
389
330
  ></input>
@@ -1,7 +1,7 @@
1
1
  import React, { forwardRef, HTMLAttributes } from 'react'
2
2
  import PropTypes from 'prop-types'
3
3
 
4
- import { Option } from './CMultiSelect'
4
+ import type { Option } from './types'
5
5
 
6
6
  export interface CMultiSelectNativeSelectProps
7
7
  extends Omit<HTMLAttributes<HTMLSelectElement>, 'options'> {
@@ -2,10 +2,10 @@ import React, { forwardRef, Fragment, HTMLAttributes, ReactNode } from 'react'
2
2
  import PropTypes from 'prop-types'
3
3
  import classNames from 'classnames'
4
4
 
5
- import { Option } from './CMultiSelect'
6
5
  import { CVirtualScroller } from './../virtual-scroller'
7
6
 
8
- import { getNextSibling, getPreviousSibling } from './../../utils'
7
+ import { getNextSibling, getPreviousSibling } from './utils'
8
+ import type { Option } from './types'
9
9
 
10
10
  export interface CMultiSelectOptionsProps extends HTMLAttributes<HTMLDivElement> {
11
11
  handleOptionOnClick?: (option: Option) => void
@@ -1,7 +1,7 @@
1
1
  import React, { forwardRef, HTMLAttributes } from 'react'
2
2
  import PropTypes from 'prop-types'
3
3
 
4
- import type { SelectedOption } from './CMultiSelect'
4
+ import type { SelectedOption } from './types'
5
5
 
6
6
  export interface CMultiSelectSelectionProps extends HTMLAttributes<HTMLSpanElement> {
7
7
  multiple?: boolean
@@ -0,0 +1,15 @@
1
+ export type Option = {
2
+ disabled?: boolean
3
+ label?: string
4
+ options?: Option[]
5
+ order?: number
6
+ selected?: boolean
7
+ text: string
8
+ value: number | string
9
+ }
10
+
11
+ export type SelectedOption = {
12
+ disabled?: boolean
13
+ text: string
14
+ value: number | string
15
+ }