@coreui/react-pro 4.1.4 → 4.2.0-rc.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 (162) hide show
  1. package/README.md +1 -1
  2. package/dist/components/Types.d.ts +1 -1
  3. package/dist/components/accordion/index.d.ts +7 -0
  4. package/dist/components/alert/index.d.ts +4 -0
  5. package/dist/components/avatar/index.d.ts +2 -0
  6. package/dist/components/backdrop/index.d.ts +2 -0
  7. package/dist/components/badge/index.d.ts +2 -0
  8. package/dist/components/breadcrumb/index.d.ts +3 -0
  9. package/dist/components/button/index.d.ts +2 -0
  10. package/dist/components/button-group/index.d.ts +3 -0
  11. package/dist/components/calendar/CCalendar.d.ts +113 -0
  12. package/dist/components/callout/index.d.ts +2 -0
  13. package/dist/components/card/index.d.ts +12 -0
  14. package/dist/components/carousel/index.d.ts +4 -0
  15. package/dist/components/close-button/index.d.ts +2 -0
  16. package/dist/components/collapse/index.d.ts +2 -0
  17. package/dist/components/datepicker/CDatePicker.d.ts +13 -0
  18. package/dist/components/datepicker/index.d.ts +2 -0
  19. package/dist/components/daterangepicker/CDateRangePicker.d.ts +65 -0
  20. package/dist/components/daterangepicker/index.d.ts +2 -0
  21. package/dist/components/dropdown/CDropdown.d.ts +10 -1
  22. package/dist/components/dropdown/CDropdownToggle.d.ts +4 -0
  23. package/dist/components/dropdown/index.d.ts +8 -0
  24. package/dist/components/footer/index.d.ts +2 -0
  25. package/dist/components/form/CForm.d.ts +2 -2
  26. package/dist/components/form/CFormCheck.d.ts +2 -1
  27. package/dist/components/form/CFormControlValidation.d.ts +46 -0
  28. package/dist/components/form/CFormControlWrapper.d.ts +31 -0
  29. package/dist/components/form/CFormInput.d.ts +2 -9
  30. package/dist/components/form/CFormRange.d.ts +7 -1
  31. package/dist/components/form/CFormSelect.d.ts +3 -10
  32. package/dist/components/form/CFormTextarea.d.ts +2 -9
  33. package/dist/components/form/index.d.ts +14 -0
  34. package/dist/components/grid/index.d.ts +4 -0
  35. package/dist/components/header/index.d.ts +7 -0
  36. package/dist/components/image/index.d.ts +2 -0
  37. package/dist/components/index.d.ts +41 -0
  38. package/dist/components/link/index.d.ts +2 -0
  39. package/dist/components/list-group/index.d.ts +3 -0
  40. package/dist/components/loading-button/index.d.ts +2 -0
  41. package/dist/components/modal/CModal.d.ts +3 -0
  42. package/dist/components/modal/index.d.ts +8 -0
  43. package/dist/components/multi-select/index.d.ts +2 -0
  44. package/dist/components/nav/index.d.ts +7 -0
  45. package/dist/components/navbar/index.d.ts +6 -0
  46. package/dist/components/offcanvas/index.d.ts +5 -0
  47. package/dist/components/pagination/index.d.ts +4 -0
  48. package/dist/components/picker/CPicker.d.ts +78 -0
  49. package/dist/components/placeholder/index.d.ts +2 -0
  50. package/dist/components/popover/CPopover.d.ts +2 -2
  51. package/dist/components/popover/index.d.ts +2 -0
  52. package/dist/components/progress/index.d.ts +3 -0
  53. package/dist/components/sidebar/index.d.ts +7 -0
  54. package/dist/components/smart-table/index.d.ts +2 -0
  55. package/dist/components/spinner/index.d.ts +2 -0
  56. package/dist/components/table/index.d.ts +9 -0
  57. package/dist/components/tabs/index.d.ts +3 -0
  58. package/dist/components/timepicker/CTimePicker.d.ts +45 -0
  59. package/dist/components/timepicker/CTimePickerRollCol.d.ts +11 -0
  60. package/dist/components/timepicker/index.d.ts +2 -0
  61. package/dist/components/toast/index.d.ts +6 -0
  62. package/dist/components/tooltip/CTooltip.d.ts +2 -2
  63. package/dist/components/tooltip/index.d.ts +2 -0
  64. package/dist/index.d.ts +0 -125
  65. package/dist/index.es.js +3268 -1339
  66. package/dist/index.es.js.map +1 -1
  67. package/dist/index.js +3513 -1582
  68. package/dist/index.js.map +1 -1
  69. package/dist/utils/calendar.d.ts +23 -0
  70. package/dist/utils/hooks/useStateWithCallback.d.ts +2 -1
  71. package/dist/utils/time.d.ts +21 -0
  72. package/package.json +12 -11
  73. package/src/components/Types.tsx +2 -2
  74. package/src/components/accordion/CAccordionButton.tsx +2 -1
  75. package/src/components/accordion/__tests__/__snapshots__/CAccordionButton.spec.tsx.snap +2 -0
  76. package/src/components/accordion/__tests__/__snapshots__/CAccordionHeader.spec.tsx.snap +2 -0
  77. package/src/components/accordion/index.ts +15 -0
  78. package/src/components/alert/CAlert.tsx +14 -3
  79. package/src/components/alert/index.ts +5 -0
  80. package/src/components/avatar/index.ts +3 -0
  81. package/src/components/backdrop/CBackdrop.tsx +10 -3
  82. package/src/components/backdrop/index.ts +3 -0
  83. package/src/components/badge/index.ts +3 -0
  84. package/src/components/breadcrumb/index.ts +4 -0
  85. package/src/components/button/index.ts +3 -0
  86. package/src/components/button-group/index.ts +4 -0
  87. package/src/components/calendar/CCalendar.tsx +559 -0
  88. package/src/components/calendar/__tests__/CCalendar.spec.tsx +9 -0
  89. package/src/components/calendar/__tests__/__snapshots__/CCalendar.spec.tsx.snap +573 -0
  90. package/src/components/callout/index.ts +3 -0
  91. package/src/components/card/index.ts +25 -0
  92. package/src/components/carousel/index.ts +5 -0
  93. package/src/components/close-button/index.ts +3 -0
  94. package/src/components/collapse/CCollapse.tsx +1 -0
  95. package/src/components/collapse/index.ts +3 -0
  96. package/src/components/datepicker/CDatePicker.tsx +50 -0
  97. package/src/components/datepicker/__tests__/CDatePicker.spec.tsx +9 -0
  98. package/src/components/datepicker/__tests__/__snapshots__/CDatePicker.spec.tsx.snap +617 -0
  99. package/src/components/datepicker/index.ts +3 -0
  100. package/src/components/daterangepicker/CDateRangePicker.tsx +432 -0
  101. package/src/components/daterangepicker/__tests__/CDateRangePicker.spec.tsx +9 -0
  102. package/src/components/daterangepicker/__tests__/__snapshots__/CDateRangePicker.spec.tsx.snap +1201 -0
  103. package/src/components/daterangepicker/index.ts +3 -0
  104. package/src/components/dropdown/CDropdown.tsx +19 -24
  105. package/src/components/dropdown/CDropdownItem.tsx +1 -1
  106. package/src/components/dropdown/CDropdownMenu.tsx +55 -3
  107. package/src/components/dropdown/CDropdownToggle.tsx +28 -5
  108. package/src/components/dropdown/index.ts +17 -0
  109. package/src/components/footer/index.ts +3 -0
  110. package/src/components/form/CForm.tsx +2 -2
  111. package/src/components/form/CFormCheck.tsx +32 -7
  112. package/src/components/form/CFormControlValidation.tsx +97 -0
  113. package/src/components/form/CFormControlWrapper.tsx +85 -0
  114. package/src/components/form/CFormInput.tsx +47 -18
  115. package/src/components/form/CFormRange.tsx +18 -4
  116. package/src/components/form/CFormSelect.tsx +60 -32
  117. package/src/components/form/CFormTextarea.tsx +45 -17
  118. package/src/components/form/index.ts +29 -0
  119. package/src/components/grid/index.ts +5 -0
  120. package/src/components/header/index.ts +8 -0
  121. package/src/components/image/index.ts +3 -0
  122. package/src/components/index.ts +41 -0
  123. package/src/components/link/index.ts +3 -0
  124. package/src/components/list-group/index.ts +4 -0
  125. package/src/components/loading-button/index.ts +3 -0
  126. package/src/components/modal/CModal.tsx +3 -2
  127. package/src/components/modal/index.ts +9 -0
  128. package/src/components/multi-select/CMultiSelect.tsx +45 -38
  129. package/src/components/multi-select/index.ts +3 -0
  130. package/src/components/nav/CNavGroup.tsx +1 -0
  131. package/src/components/nav/index.ts +8 -0
  132. package/src/components/navbar/index.ts +7 -0
  133. package/src/components/offcanvas/COffcanvas.tsx +1 -0
  134. package/src/components/offcanvas/index.ts +6 -0
  135. package/src/components/pagination/index.ts +5 -0
  136. package/src/components/picker/CPicker.tsx +207 -0
  137. package/src/components/picker/__tests__/CPicker.spec.tsx +9 -0
  138. package/src/components/picker/__tests__/__snapshots__/CPicker.spec.tsx.snap +23 -0
  139. package/src/components/placeholder/index.ts +3 -0
  140. package/src/components/popover/CPopover.tsx +78 -70
  141. package/src/components/popover/index.ts +3 -0
  142. package/src/components/progress/index.ts +4 -0
  143. package/src/components/sidebar/index.ts +8 -0
  144. package/src/components/smart-table/index.ts +3 -0
  145. package/src/components/spinner/index.ts +3 -0
  146. package/src/components/table/index.ts +19 -0
  147. package/src/components/tabs/CTabPane.tsx +7 -3
  148. package/src/components/tabs/index.ts +4 -0
  149. package/src/components/timepicker/CTimePicker.tsx +313 -0
  150. package/src/components/timepicker/CTimePickerRollCol.tsx +63 -0
  151. package/src/components/timepicker/__tests__/CTimePicker.spec.tsx +9 -0
  152. package/src/components/timepicker/index.ts +3 -0
  153. package/src/components/toast/CToast.tsx +6 -1
  154. package/src/components/toast/index.ts +7 -0
  155. package/src/components/tooltip/CTooltip.tsx +68 -60
  156. package/src/components/tooltip/index.ts +3 -0
  157. package/src/index.ts +0 -252
  158. package/src/utils/calendar.ts +270 -0
  159. package/src/utils/hooks/useStateWithCallback.ts +6 -3
  160. package/src/utils/time.ts +84 -0
  161. package/dist/components/dropdown/CDropdownToggleCustom.d.ts +0 -15
  162. package/src/components/dropdown/CDropdownToggleCustom.tsx +0 -81
@@ -0,0 +1,3 @@
1
+ import { CDateRangePicker } from './CDateRangePicker'
2
+
3
+ export { CDateRangePicker }
@@ -3,6 +3,7 @@ import React, {
3
3
  ElementType,
4
4
  forwardRef,
5
5
  HTMLAttributes,
6
+ RefObject,
6
7
  useEffect,
7
8
  useRef,
8
9
  useState,
@@ -33,6 +34,14 @@ export interface CDropdownProps extends HTMLAttributes<HTMLDivElement | HTMLLIEl
33
34
  * @type 'start' | 'end' | { xs: 'start' | 'end' } | { sm: 'start' | 'end' } | { md: 'start' | 'end' } | { lg: 'start' | 'end' } | { xl: 'start' | 'end'} | { xxl: 'start' | 'end'}
34
35
  */
35
36
  alignment?: Alignments
37
+ /**
38
+ * Configure the auto close behavior of the dropdown:
39
+ * - `true` - the dropdown will be closed by clicking outside or inside the dropdown menu.
40
+ * - `false` - the dropdown will be closed by clicking the toggle button and manually calling hide or toggle method. (Also will not be closed by pressing esc key)
41
+ * - `'inside'` - the dropdown will be closed (only) by clicking inside the dropdown menu.
42
+ * - `'outside'` - the dropdown will be closed (only) by clicking outside the dropdown menu.
43
+ */
44
+ autoClose?: 'inside' | 'outside' | boolean
36
45
  /**
37
46
  * A string of all className you want applied to the base component.
38
47
  */
@@ -80,6 +89,8 @@ export interface CDropdownProps extends HTMLAttributes<HTMLDivElement | HTMLLIEl
80
89
  }
81
90
 
82
91
  interface ContextProps extends CDropdownProps {
92
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
93
+ dropdownToggleRef: RefObject<any> | undefined
83
94
  setVisible: React.Dispatch<React.SetStateAction<boolean | undefined>>
84
95
  }
85
96
 
@@ -90,6 +101,7 @@ export const CDropdown = forwardRef<HTMLDivElement | HTMLLIElement, CDropdownPro
90
101
  {
91
102
  children,
92
103
  alignment,
104
+ autoClose = true,
93
105
  className,
94
106
  dark,
95
107
  direction,
@@ -106,6 +118,7 @@ export const CDropdown = forwardRef<HTMLDivElement | HTMLLIElement, CDropdownPro
106
118
  ) => {
107
119
  const [_visible, setVisible] = useState(visible)
108
120
  const dropdownRef = useRef<HTMLDivElement>(null)
121
+ const dropdownToggleRef = useRef(null)
109
122
  const forkedRef = useForkedRef(ref, dropdownRef)
110
123
 
111
124
  const Component = variant === 'nav-item' ? 'li' : component
@@ -117,8 +130,10 @@ export const CDropdown = forwardRef<HTMLDivElement | HTMLLIElement, CDropdownPro
117
130
 
118
131
  const contextValues = {
119
132
  alignment,
133
+ autoClose,
120
134
  dark,
121
135
  direction: direction,
136
+ dropdownToggleRef,
122
137
  placement: placement,
123
138
  popper,
124
139
  variant,
@@ -135,19 +150,6 @@ export const CDropdown = forwardRef<HTMLDivElement | HTMLLIElement, CDropdownPro
135
150
  className,
136
151
  )
137
152
 
138
- useEffect(() => {
139
- _visible &&
140
- setTimeout(() => {
141
- window.addEventListener('mouseup', handleClickOutside)
142
- window.addEventListener('keyup', handleKeyup)
143
- })
144
-
145
- return () => {
146
- window.removeEventListener('mouseup', handleClickOutside)
147
- window.removeEventListener('keyup', handleKeyup)
148
- }
149
- }, [_visible])
150
-
151
153
  useEffect(() => {
152
154
  setVisible(visible)
153
155
  }, [visible])
@@ -157,17 +159,6 @@ export const CDropdown = forwardRef<HTMLDivElement | HTMLLIElement, CDropdownPro
157
159
  !_visible && onHide && onHide()
158
160
  }, [_visible])
159
161
 
160
- const handleKeyup = (event: Event) => {
161
- if (!dropdownRef.current?.contains(event.target as HTMLElement)) {
162
- setVisible(false)
163
- }
164
- }
165
- const handleClickOutside = (event: Event) => {
166
- if (!dropdownRef.current?.contains(event.target as HTMLElement)) {
167
- setVisible(false)
168
- }
169
- }
170
-
171
162
  const dropdownContent = () => {
172
163
  return variant === 'input-group' ? (
173
164
  <>{children}</>
@@ -203,6 +194,10 @@ CDropdown.propTypes = {
203
194
  PropTypes.shape({ xl: alignmentDirection }),
204
195
  PropTypes.shape({ xxl: alignmentDirection }),
205
196
  ]),
197
+ autoClose: PropTypes.oneOfType([
198
+ PropTypes.bool,
199
+ PropTypes.oneOf<'inside' | 'outside'>(['inside', 'outside']),
200
+ ]),
206
201
  children: PropTypes.node,
207
202
  className: PropTypes.string,
208
203
  component: PropTypes.elementType,
@@ -21,7 +21,7 @@ export const CDropdownItem = forwardRef<HTMLButtonElement | HTMLAnchorElement, C
21
21
  const _className = classNames('dropdown-item', className)
22
22
 
23
23
  return (
24
- <CLink component={component} {...rest} className={_className} ref={ref}>
24
+ <CLink className={_className} component={component} {...rest} ref={ref}>
25
25
  {children}
26
26
  </CLink>
27
27
  )
@@ -1,5 +1,5 @@
1
+ import React, { ElementType, FC, HTMLAttributes, useContext, useEffect, useRef } from 'react'
1
2
  import PropTypes from 'prop-types'
2
- import React, { ElementType, FC, HTMLAttributes, useContext } from 'react'
3
3
  import classNames from 'classnames'
4
4
  import { Popper, PopperChildrenProps } from 'react-popper'
5
5
 
@@ -35,19 +35,69 @@ export const CDropdownMenu: FC<CDropdownMenuProps> = ({
35
35
  component: Component = 'ul',
36
36
  ...rest
37
37
  }) => {
38
- const { alignment, dark, direction, placement, popper, visible } = useContext(CDropdownContext)
38
+ const {
39
+ alignment,
40
+ autoClose,
41
+ dark,
42
+ direction,
43
+ dropdownToggleRef,
44
+ placement,
45
+ popper,
46
+ visible,
47
+ setVisible,
48
+ } = useContext(CDropdownContext)
49
+
50
+ const dropdownMenuRef = useRef<HTMLDivElement>(null)
51
+
52
+ useEffect(() => {
53
+ visible && window.addEventListener('mouseup', handleMouseUp)
54
+ visible && window.addEventListener('keyup', handleKeyup)
55
+
56
+ return () => {
57
+ window.removeEventListener('mouseup', handleMouseUp)
58
+ window.removeEventListener('keyup', handleKeyup)
59
+ }
60
+ }, [visible])
61
+
62
+ const handleKeyup = (event: KeyboardEvent) => {
63
+ if (autoClose === false) {
64
+ return
65
+ }
66
+
67
+ if (event.key === 'Escape') {
68
+ setVisible(false)
69
+ }
70
+ }
71
+
72
+ const handleMouseUp = (event: Event) => {
73
+ if (dropdownToggleRef && dropdownToggleRef.current.contains(event.target as HTMLElement)) {
74
+ return
75
+ }
76
+
77
+ if (
78
+ autoClose === true ||
79
+ (autoClose === 'inside' && dropdownMenuRef.current?.contains(event.target as HTMLElement)) ||
80
+ (autoClose === 'outside' && !dropdownMenuRef.current?.contains(event.target as HTMLElement))
81
+ ) {
82
+ setTimeout(() => setVisible(false), 1)
83
+ return
84
+ }
85
+ }
39
86
 
40
87
  let _placement: Placements = placement
41
88
 
42
89
  if (direction === 'dropup') {
43
90
  _placement = 'top-start'
44
91
  }
92
+
45
93
  if (direction === 'dropend') {
46
94
  _placement = 'right-start'
47
95
  }
96
+
48
97
  if (direction === 'dropstart') {
49
98
  _placement = 'left-start'
50
99
  }
100
+
51
101
  if (alignment === 'end') {
52
102
  _placement = 'bottom-end'
53
103
  }
@@ -101,7 +151,9 @@ export const CDropdownMenu: FC<CDropdownMenuProps> = ({
101
151
  }
102
152
 
103
153
  return popper && visible ? (
104
- <Popper placement={_placement}>{({ ref, style }) => dropdownMenuComponent(style, ref)}</Popper>
154
+ <Popper innerRef={dropdownMenuRef} placement={_placement}>
155
+ {({ ref, style }) => dropdownMenuComponent(style, ref)}
156
+ </Popper>
105
157
  ) : (
106
158
  dropdownMenuComponent()
107
159
  )
@@ -3,6 +3,8 @@ import PropTypes from 'prop-types'
3
3
  import classNames from 'classnames'
4
4
  import { Reference } from 'react-popper'
5
5
 
6
+ import { useForkedRef } from '../../utils/hooks'
7
+
6
8
  import { Triggers, triggerPropType } from '../Types'
7
9
 
8
10
  import { CButton, CButtonProps } from '../button/CButton'
@@ -13,6 +15,10 @@ export interface CDropdownToggleProps extends Omit<CButtonProps, 'type'> {
13
15
  * Enables pseudo element caret on toggler.
14
16
  */
15
17
  caret?: boolean
18
+ /**
19
+ * Create a custom toggler which accepts any content.
20
+ */
21
+ custom?: boolean
16
22
  /**
17
23
  * Similarly, create split button dropdowns with virtually the same markup as single button dropdowns, but with the addition of `.dropdown-toggle-split` className for proper spacing around the dropdown caret.
18
24
  */
@@ -28,12 +34,13 @@ export interface CDropdownToggleProps extends Omit<CButtonProps, 'type'> {
28
34
  export const CDropdownToggle: FC<CDropdownToggleProps> = ({
29
35
  children,
30
36
  caret = true,
37
+ custom,
31
38
  className,
32
39
  split,
33
40
  trigger = 'click',
34
41
  ...rest
35
42
  }) => {
36
- const { popper, variant, visible, setVisible } = useContext(CDropdownContext)
43
+ const { dropdownToggleRef, popper, variant, visible, setVisible } = useContext(CDropdownContext)
37
44
  const _className = classNames(
38
45
  {
39
46
  'dropdown-toggle': caret,
@@ -59,31 +66,47 @@ export const CDropdownToggle: FC<CDropdownToggleProps> = ({
59
66
  const togglerProps = {
60
67
  className: _className,
61
68
  'aria-expanded': visible,
69
+ ...(!rest.disabled && { ...triggers }),
62
70
  ...triggers,
63
71
  }
64
72
 
65
73
  // We use any because Toggler can be `a` as well as `button`.
66
74
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
67
75
  const Toggler = (ref?: React.Ref<any>) => {
68
- return variant === 'nav-item' ? (
69
- <a href="#" {...togglerProps} ref={ref}>
76
+ return custom && React.isValidElement(children) ? (
77
+ <>
78
+ {React.cloneElement(children, {
79
+ 'aria-expanded': visible,
80
+ ...(!rest.disabled && { ...triggers }),
81
+ ref: useForkedRef(ref, dropdownToggleRef),
82
+ })}
83
+ </>
84
+ ) : variant === 'nav-item' ? (
85
+ <a href="#" {...togglerProps} ref={useForkedRef(ref, dropdownToggleRef)}>
70
86
  {children}
71
87
  </a>
72
88
  ) : (
73
- <CButton type="button" {...togglerProps} tabIndex={0} {...rest} ref={ref}>
89
+ <CButton
90
+ type="button"
91
+ {...togglerProps}
92
+ tabIndex={0}
93
+ {...rest}
94
+ ref={useForkedRef(ref, dropdownToggleRef)}
95
+ >
74
96
  {children}
75
97
  {split && <span className="visually-hidden">Toggle Dropdown</span>}
76
98
  </CButton>
77
99
  )
78
100
  }
79
101
 
80
- return popper ? <Reference>{({ ref }) => Toggler(ref)}</Reference> : Toggler()
102
+ return popper ? <Reference>{({ ref }) => Toggler(ref)}</Reference> : Toggler(dropdownToggleRef)
81
103
  }
82
104
 
83
105
  CDropdownToggle.propTypes = {
84
106
  caret: PropTypes.bool,
85
107
  children: PropTypes.node,
86
108
  className: PropTypes.string,
109
+ custom: PropTypes.bool,
87
110
  split: PropTypes.bool,
88
111
  trigger: triggerPropType,
89
112
  }
@@ -0,0 +1,17 @@
1
+ import { CDropdown } from './CDropdown'
2
+ import { CDropdownDivider } from './CDropdownDivider'
3
+ import { CDropdownHeader } from './CDropdownHeader'
4
+ import { CDropdownItem } from './CDropdownItem'
5
+ import { CDropdownItemPlain } from './CDropdownItemPlain'
6
+ import { CDropdownMenu } from './CDropdownMenu'
7
+ import { CDropdownToggle } from './CDropdownToggle'
8
+
9
+ export {
10
+ CDropdown,
11
+ CDropdownDivider,
12
+ CDropdownHeader,
13
+ CDropdownItem,
14
+ CDropdownItemPlain,
15
+ CDropdownMenu,
16
+ CDropdownToggle,
17
+ }
@@ -0,0 +1,3 @@
1
+ import { CFooter } from './CFooter'
2
+
3
+ export { CFooter }
@@ -1,8 +1,8 @@
1
- import React, { forwardRef, HTMLAttributes } from 'react'
1
+ import React, { forwardRef, FormHTMLAttributes } from 'react'
2
2
  import PropTypes from 'prop-types'
3
3
  import classNames from 'classnames'
4
4
 
5
- export interface CFormProps extends HTMLAttributes<HTMLFormElement> {
5
+ export interface CFormProps extends FormHTMLAttributes<HTMLFormElement> {
6
6
  /**
7
7
  * A string of all className you want applied to the component.
8
8
  */
@@ -5,6 +5,7 @@ import classNames from 'classnames'
5
5
  import { useForkedRef } from '../../utils/hooks'
6
6
  import { Colors, Shapes } from '../Types'
7
7
 
8
+ import { CFormControlValidation, CFormControlValidationProps } from './CFormControlValidation'
8
9
  import { CFormLabel } from './CFormLabel'
9
10
 
10
11
  export type ButtonObject = {
@@ -30,7 +31,9 @@ export type ButtonObject = {
30
31
  variant?: 'outline' | 'ghost'
31
32
  }
32
33
 
33
- export interface CFormCheckProps extends InputHTMLAttributes<HTMLInputElement> {
34
+ export interface CFormCheckProps
35
+ extends CFormControlValidationProps,
36
+ InputHTMLAttributes<HTMLInputElement> {
34
37
  /**
35
38
  * Create button-like checkboxes and radio buttons.
36
39
  */
@@ -78,6 +81,11 @@ export const CFormCheck = forwardRef<HTMLInputElement, CFormCheckProps>(
78
81
  {
79
82
  className,
80
83
  button,
84
+ feedback,
85
+ feedbackInvalid,
86
+ feedbackValid,
87
+ floatingLabel,
88
+ tooltipFeedback,
81
89
  hitArea,
82
90
  id,
83
91
  indeterminate,
@@ -131,6 +139,19 @@ export const CFormCheck = forwardRef<HTMLInputElement, CFormCheckProps>(
131
139
  return <input type={type} className={inputClassName} id={id} {...rest} ref={forkedRef} />
132
140
  }
133
141
 
142
+ const formValidation = () => (
143
+ <CFormControlValidation
144
+ describedby={rest['aria-describedby']}
145
+ feedback={feedback}
146
+ feedbackInvalid={feedbackInvalid}
147
+ feedbackValid={feedbackValid}
148
+ floatingLabel={floatingLabel}
149
+ invalid={invalid}
150
+ tooltipFeedback={tooltipFeedback}
151
+ valid={valid}
152
+ />
153
+ )
154
+
134
155
  const formLabel = () => {
135
156
  return (
136
157
  <CFormLabel customClassName={labelClassName} {...(id && { htmlFor: id })}>
@@ -143,17 +164,22 @@ export const CFormCheck = forwardRef<HTMLInputElement, CFormCheckProps>(
143
164
  <>
144
165
  {formControl()}
145
166
  {label && formLabel()}
167
+ {formValidation()}
146
168
  </>
147
169
  ) : label ? (
148
170
  hitArea ? (
149
- <CFormLabel customClassName={className} {...(id && { htmlFor: id })}>
150
- {formControl()}
151
- {label}
152
- </CFormLabel>
171
+ <>
172
+ <CFormLabel customClassName={className} {...(id && { htmlFor: id })}>
173
+ {formControl()}
174
+ {label}
175
+ </CFormLabel>
176
+ {formValidation()}
177
+ </>
153
178
  ) : (
154
179
  <div className={_className}>
155
180
  {formControl()}
156
181
  {formLabel()}
182
+ {formValidation()}
157
183
  </div>
158
184
  )
159
185
  ) : (
@@ -169,10 +195,9 @@ CFormCheck.propTypes = {
169
195
  id: PropTypes.string,
170
196
  indeterminate: PropTypes.bool,
171
197
  inline: PropTypes.bool,
172
- invalid: PropTypes.bool,
173
198
  label: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
174
199
  type: PropTypes.oneOf(['checkbox', 'radio']),
175
- valid: PropTypes.bool,
200
+ ...CFormControlValidation.propTypes,
176
201
  }
177
202
 
178
203
  CFormCheck.displayName = 'CFormCheck'
@@ -0,0 +1,97 @@
1
+ import React, { FC, ReactNode } from 'react'
2
+
3
+ import PropTypes from 'prop-types'
4
+
5
+ import { CFormFeedback } from './CFormFeedback'
6
+
7
+ export interface CFormControlValidationProps {
8
+ /**
9
+ * @ignore
10
+ */
11
+ describedby?: string
12
+ /**
13
+ * Provide valuable, actionable feedback.
14
+ *
15
+ * @since 4.2.0
16
+ */
17
+ feedback?: ReactNode | string
18
+ /**
19
+ * Provide valuable, actionable feedback.
20
+ *
21
+ * @since 4.2.0
22
+ */
23
+ feedbackInvalid?: ReactNode | string
24
+ /**
25
+ * Provide valuable, actionable invalid feedback when using standard HTML form validation which applied two CSS pseudo-classes, `:invalid` and `:valid`.
26
+ *
27
+ * @since 4.2.0
28
+ */
29
+ feedbackValid?: ReactNode | string
30
+ /**
31
+ * Provide valuable, actionable valid feedback when using standard HTML form validation which applied two CSS pseudo-classes, `:invalid` and `:valid`.
32
+ *
33
+ * @since 4.2.0
34
+ */
35
+ floatingLabel?: ReactNode | string
36
+ /**
37
+ * Set component validation state to invalid.
38
+ */
39
+ invalid?: boolean
40
+ /**
41
+ * Display validation feedback in a styled tooltip.
42
+ *
43
+ * @since 4.2.0
44
+ */
45
+ tooltipFeedback?: boolean
46
+ /**
47
+ * Set component validation state to valid.
48
+ */
49
+ valid?: boolean
50
+ }
51
+
52
+ export const CFormControlValidation: FC<CFormControlValidationProps> = ({
53
+ describedby,
54
+ feedback,
55
+ feedbackInvalid,
56
+ feedbackValid,
57
+ invalid,
58
+ tooltipFeedback,
59
+ valid,
60
+ }) => {
61
+ return (
62
+ <>
63
+ {feedback && (valid || invalid) && (
64
+ <CFormFeedback
65
+ {...(invalid && { id: describedby })}
66
+ invalid={invalid}
67
+ tooltip={tooltipFeedback}
68
+ valid={valid}
69
+ >
70
+ {feedback}
71
+ </CFormFeedback>
72
+ )}
73
+ {feedbackInvalid && (
74
+ <CFormFeedback id={describedby} invalid tooltip={tooltipFeedback}>
75
+ {feedbackInvalid}
76
+ </CFormFeedback>
77
+ )}
78
+ {feedbackValid && (
79
+ <CFormFeedback valid tooltip={tooltipFeedback}>
80
+ {feedbackValid}
81
+ </CFormFeedback>
82
+ )}
83
+ </>
84
+ )
85
+ }
86
+
87
+ CFormControlValidation.propTypes = {
88
+ describedby: PropTypes.string,
89
+ feedback: PropTypes.oneOfType([PropTypes.node, PropTypes.string]),
90
+ feedbackValid: PropTypes.oneOfType([PropTypes.node, PropTypes.string]),
91
+ feedbackInvalid: PropTypes.oneOfType([PropTypes.node, PropTypes.string]),
92
+ invalid: PropTypes.bool,
93
+ tooltipFeedback: PropTypes.bool,
94
+ valid: PropTypes.bool,
95
+ }
96
+
97
+ CFormControlValidation.displayName = 'CFormControlValidation'
@@ -0,0 +1,85 @@
1
+ import React, { FC, ReactNode } from 'react'
2
+
3
+ import PropTypes from 'prop-types'
4
+
5
+ import { CFormControlValidation, CFormControlValidationProps } from './CFormControlValidation'
6
+ import { CFormFloating } from './CFormFloating'
7
+ import { CFormLabel } from './CFormLabel'
8
+ import { CFormText } from './CFormText'
9
+
10
+ export interface CFormControlWrapperProps extends CFormControlValidationProps {
11
+ /**
12
+ * @ignore
13
+ */
14
+ children?: ReactNode
15
+ /**
16
+ * Provide valuable, actionable valid feedback when using standard HTML form validation which applied two CSS pseudo-classes, `:invalid` and `:valid`.
17
+ *
18
+ * @since 4.2.0
19
+ */
20
+ floatingLabel?: ReactNode | string
21
+ /**
22
+ * @ignore
23
+ */
24
+ id?: string
25
+ /**
26
+ * Add a caption for a component.
27
+ *
28
+ * @since 4.2.0
29
+ */
30
+ label?: ReactNode | string
31
+ /**
32
+ * Add helper text to the component.
33
+ *
34
+ * @since 4.2.0
35
+ */
36
+ text?: ReactNode | string
37
+ }
38
+
39
+ export const CFormControlWrapper: FC<CFormControlWrapperProps> = ({
40
+ children,
41
+ describedby,
42
+ feedback,
43
+ feedbackInvalid,
44
+ feedbackValid,
45
+ floatingLabel,
46
+ id,
47
+ invalid,
48
+ label,
49
+ text,
50
+ tooltipFeedback,
51
+ valid,
52
+ }) => {
53
+ return floatingLabel ? (
54
+ <CFormFloating>
55
+ {children}
56
+ <CFormLabel htmlFor={id}>{label || floatingLabel}</CFormLabel>
57
+ </CFormFloating>
58
+ ) : (
59
+ <>
60
+ {label && <CFormLabel htmlFor={id}>{label}</CFormLabel>}
61
+ {children}
62
+ {text && <CFormText id={describedby}>{text}</CFormText>}
63
+ <CFormControlValidation
64
+ describedby={describedby}
65
+ feedback={feedback}
66
+ feedbackInvalid={feedbackInvalid}
67
+ feedbackValid={feedbackValid}
68
+ floatingLabel={floatingLabel}
69
+ invalid={invalid}
70
+ tooltipFeedback={tooltipFeedback}
71
+ valid={valid}
72
+ />
73
+ </>
74
+ )
75
+ }
76
+
77
+ CFormControlWrapper.propTypes = {
78
+ children: PropTypes.node,
79
+ floatingLabel: PropTypes.oneOfType([PropTypes.node, PropTypes.string]),
80
+ label: PropTypes.oneOfType([PropTypes.node, PropTypes.string]),
81
+ text: PropTypes.oneOfType([PropTypes.node, PropTypes.string]),
82
+ ...CFormControlValidation.propTypes,
83
+ }
84
+
85
+ CFormControlWrapper.displayName = 'CFormControlWrapper'