@coreui/react-pro 4.1.4 → 4.2.0-beta.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 (33) hide show
  1. package/README.md +1 -1
  2. package/dist/components/Types.d.ts +1 -1
  3. package/dist/components/calendar/CCalendar.d.ts +89 -0
  4. package/dist/components/calendar/CCalendar.utils.d.ts +20 -0
  5. package/dist/components/datepicker/CDatePicker.d.ts +13 -0
  6. package/dist/components/daterangepicker/CDateRangePicker.d.ts +50 -0
  7. package/dist/components/dropdown/CDropdown.d.ts +10 -1
  8. package/dist/components/dropdown/CDropdownToggle.d.ts +4 -0
  9. package/dist/components/picker/CPicker.d.ts +37 -0
  10. package/dist/components/timepicker/CTimePicker.d.ts +49 -0
  11. package/dist/components/timepicker/CTimePicker.utils.d.ts +4 -0
  12. package/dist/components/timepicker/CTimePickerRollCol.d.ts +11 -0
  13. package/dist/index.d.ts +5 -2
  14. package/dist/index.es.js +2528 -750
  15. package/dist/index.es.js.map +1 -1
  16. package/dist/index.js +2714 -933
  17. package/dist/index.js.map +1 -1
  18. package/package.json +4 -4
  19. package/src/components/Types.tsx +2 -2
  20. package/src/components/accordion/CAccordionButton.tsx +2 -1
  21. package/src/components/calendar/CCalendar.tsx +482 -0
  22. package/src/components/calendar/CCalendar.utils.ts +183 -0
  23. package/src/components/datepicker/CDatePicker.tsx +49 -0
  24. package/src/components/daterangepicker/CDateRangePicker.tsx +366 -0
  25. package/src/components/dropdown/CDropdown.tsx +19 -24
  26. package/src/components/dropdown/CDropdownMenu.tsx +55 -3
  27. package/src/components/dropdown/CDropdownToggle.tsx +28 -5
  28. package/src/components/picker/CPicker.tsx +142 -0
  29. package/src/components/timepicker/CTimePicker.tsx +341 -0
  30. package/src/components/timepicker/CTimePicker.utils.ts +27 -0
  31. package/src/components/timepicker/CTimePickerRollCol.tsx +57 -0
  32. package/src/index.ts +8 -2
  33. package/src/components/dropdown/CDropdownToggleCustom.tsx +0 -81
@@ -1,5 +1,5 @@
1
1
  import PropTypes from 'prop-types'
2
- import React, { ElementType, FC, HTMLAttributes, useContext } from 'react'
2
+ import React, { ElementType, FC, HTMLAttributes, useContext, useEffect, useRef } from 'react'
3
3
  import classNames from 'classnames'
4
4
  import { Popper, PopperChildrenProps } from 'react-popper'
5
5
 
@@ -35,7 +35,57 @@ 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: Event) => {
63
+ if (autoClose === false) {
64
+ return
65
+ }
66
+ if (!dropdownMenuRef.current?.contains(event.target as HTMLElement)) {
67
+ setVisible(false)
68
+ }
69
+ }
70
+ const handleMouseUp = (event: Event) => {
71
+ if (dropdownToggleRef && dropdownToggleRef.current.contains(event.target as HTMLElement)) {
72
+ return
73
+ }
74
+ if (autoClose === true) {
75
+ setVisible(false)
76
+ return
77
+ }
78
+ if (autoClose === 'inside' && dropdownMenuRef.current?.contains(event.target as HTMLElement)) {
79
+ setVisible(false)
80
+ return
81
+ }
82
+ if (
83
+ autoClose === 'outside' &&
84
+ !dropdownMenuRef.current?.contains(event.target as HTMLElement)
85
+ ) {
86
+ setVisible(false)
87
+ }
88
+ }
39
89
 
40
90
  let _placement: Placements = placement
41
91
 
@@ -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,142 @@
1
+ import React, { forwardRef, ReactNode, useEffect, useState } from 'react'
2
+ import PropTypes from 'prop-types'
3
+ import classNames from 'classnames'
4
+
5
+ import { CButton } from '../button/CButton'
6
+ import { CDropdown, CDropdownProps } from '../dropdown/CDropdown'
7
+ import { CDropdownMenu } from '../dropdown/CDropdownMenu'
8
+ import { CDropdownToggle } from '../dropdown/CDropdownToggle'
9
+
10
+ export interface CPickerProps extends Omit<CDropdownProps, 'variant'> {
11
+ /**
12
+ * Cancel button text.
13
+ */
14
+ cancelButtonText?: ReactNode
15
+ /**
16
+ * Set container type for the component.
17
+ */
18
+ container?: 'dropdown' | 'inline'
19
+ /**
20
+ * Toggle the disabled state for the component.
21
+ */
22
+ disabled?: boolean
23
+ /**
24
+ * Toggle visibility of footer element or set the content of footer.
25
+ */
26
+ footer?: boolean | ReactNode
27
+ /**
28
+ * Ok button text
29
+ */
30
+ okButtonText?: ReactNode
31
+ /**
32
+ * Callback fired when the cancel button has been clicked.
33
+ */
34
+ onCancelClick?: () => void
35
+ /**
36
+ * Callback fired when the ok button has been clicked.
37
+ */
38
+ onOkClick?: () => void
39
+ /**
40
+ * The content of toggler.
41
+ */
42
+ toggler?: ReactNode
43
+ }
44
+
45
+ export const CPicker = forwardRef<HTMLDivElement | HTMLLIElement, CPickerProps>(
46
+ (
47
+ {
48
+ children,
49
+ cancelButtonText = 'Cancel',
50
+ className,
51
+ container = 'dropdown',
52
+ disabled,
53
+ footer,
54
+ okButtonText = 'OK',
55
+ onCancelClick,
56
+ onOkClick,
57
+ onHide,
58
+ onShow,
59
+ toggler: Toggler,
60
+ visible,
61
+ ...rest
62
+ },
63
+ ref,
64
+ ) => {
65
+ const [_visible, setVisible] = useState(visible)
66
+
67
+ useEffect(() => {
68
+ setVisible(visible)
69
+ }, [visible])
70
+
71
+ const Footer = () => (
72
+ <>
73
+ <CButton
74
+ color="primary"
75
+ size="sm"
76
+ variant="ghost"
77
+ onClick={() => onCancelClick && onCancelClick()}
78
+ >
79
+ {cancelButtonText}
80
+ </CButton>
81
+ <CButton
82
+ color="primary"
83
+ size="sm"
84
+ onClick={() => {
85
+ onOkClick && onOkClick()
86
+ setVisible(false)
87
+ }}
88
+ >
89
+ {okButtonText}
90
+ </CButton>
91
+ </>
92
+ )
93
+
94
+ const _className = classNames('picker', className)
95
+
96
+ switch (container) {
97
+ case 'inline':
98
+ return <div className={_className}>{children}</div>
99
+ default:
100
+ return (
101
+ <CDropdown
102
+ autoClose="outside"
103
+ className={_className}
104
+ onHide={() => {
105
+ setVisible(false)
106
+ onHide && onHide()
107
+ }}
108
+ onShow={() => {
109
+ setVisible(true)
110
+ onShow && onShow()
111
+ }}
112
+ variant="dropdown"
113
+ visible={_visible}
114
+ {...rest}
115
+ ref={ref}
116
+ >
117
+ <CDropdownToggle custom disabled={_visible || disabled}>
118
+ {Toggler}
119
+ </CDropdownToggle>
120
+ <CDropdownMenu component="div">
121
+ {children}
122
+ {footer && (
123
+ <div className="picker-footer">
124
+ {typeof footer === 'boolean' ? Footer() : footer}
125
+ </div>
126
+ )}
127
+ </CDropdownMenu>
128
+ </CDropdown>
129
+ )
130
+ }
131
+ },
132
+ )
133
+
134
+ CPicker.displayName = 'CPicker'
135
+
136
+ CPicker.propTypes = {
137
+ ...CDropdown.propTypes,
138
+ container: PropTypes.oneOf(['dropdown', 'inline']),
139
+ disabled: PropTypes.bool,
140
+ onCancelClick: PropTypes.func,
141
+ onOkClick: PropTypes.func,
142
+ }
@@ -0,0 +1,341 @@
1
+ /* eslint-disable react/prop-types */
2
+ import React, { forwardRef, ReactNode, useEffect, useRef, useState } from 'react'
3
+ import PropTypes from 'prop-types'
4
+ import classNames from 'classnames'
5
+
6
+ import { CFormInput } from '../form/CFormInput'
7
+ import { CFormSelect } from '../form/CFormSelect'
8
+ import { CInputGroup } from '../form/CInputGroup'
9
+ import { CInputGroupText } from '../form/CInputGroupText'
10
+
11
+ import { CTimePickerRollCol } from './CTimePickerRollCol'
12
+ import { CPicker, CPickerProps } from './../picker/CPicker'
13
+ import { convert12hTo24h, convert24hTo12h, getAmPm, isAmPm } from './CTimePicker.utils'
14
+
15
+ export interface CTimePickerProps
16
+ extends Omit<
17
+ CPickerProps,
18
+ 'autoClose' | 'component' | 'dark' | 'placeholder' | 'variant' | 'onChange'
19
+ > {
20
+ /**
21
+ * A string of all className you want applied to the component.
22
+ */
23
+ className?: string
24
+ /**
25
+ * Enables selection cleaner element.
26
+ */
27
+ cleaner?: boolean
28
+ /**
29
+ * Cleaner icon.
30
+ */
31
+ cleanerIcon?: ReactNode | boolean
32
+ /**
33
+ * Default input icon.
34
+ */
35
+ defaultIcon?: ReactNode | boolean
36
+ /**
37
+ * Toggle the readonly state for the component.
38
+ */
39
+ inputReadOnly?: boolean
40
+ /**
41
+ * Sets the default locale for components. If not set, it is inherited from the navigator.language.
42
+ */
43
+ locale?: string
44
+ /**
45
+ * Callback fired when the user hovers over the calendar cell.
46
+ */
47
+ onChange?: (timeString: string, localeTimeString: string, date: Date) => void
48
+ /**
49
+ * Specifies a short hint that is visible in the input.
50
+ */
51
+ placeholder?: string
52
+ /**
53
+ * Size the component small or large.
54
+ */
55
+ size?: 'sm' | 'lg'
56
+ /**
57
+ * Default value of the component
58
+ */
59
+ value?: Date | string | null
60
+ /**
61
+ * Set the time picker variant to a roll or select.
62
+ */
63
+ variant?: 'roll' | 'select'
64
+ }
65
+
66
+ export const CTimePicker = forwardRef<HTMLDivElement | HTMLLIElement, CTimePickerProps>(
67
+ (
68
+ {
69
+ className,
70
+ cleaner = true,
71
+ cleanerIcon,
72
+ container = 'dropdown',
73
+ defaultIcon,
74
+ disabled,
75
+ footer = true,
76
+ inputReadOnly,
77
+ locale = typeof navigator === 'undefined' ? 'en-US' : navigator.language,
78
+ onChange,
79
+ placeholder = 'Select time',
80
+ size,
81
+ value,
82
+ variant = 'roll',
83
+ ...rest
84
+ },
85
+ ref,
86
+ ) => {
87
+ const inputRef = useRef<HTMLInputElement>(null)
88
+ const [date, setDate] = useState<Date | null>(
89
+ value ? (value instanceof Date ? new Date(value) : new Date(`1970-01-01 ${value}`)) : null,
90
+ )
91
+ const [_ampm, setAmPm] = useState<'am' | 'pm'>(date ? getAmPm(new Date(date), locale) : 'am')
92
+
93
+ useEffect(() => {
94
+ setDate(
95
+ value ? (value instanceof Date ? new Date(value) : new Date(`1970-01-01 ${value}`)) : null,
96
+ )
97
+ }, [value])
98
+
99
+ useEffect(() => {
100
+ if (inputRef.current) {
101
+ inputRef.current.value = date ? date.toLocaleTimeString(locale) : ''
102
+ }
103
+ }, [date])
104
+
105
+ const getHours = (locale: string) =>
106
+ Array.from(new Array(isAmPm(locale) ? 12 : 24), (_, i) => {
107
+ return {
108
+ value: isAmPm(locale) ? i + 1 : i,
109
+ label: (isAmPm(locale) ? i + 1 : i).toLocaleString(locale),
110
+ }
111
+ })
112
+
113
+ const getMinutesOrSeconds = (locale: string) =>
114
+ Array.from(new Array(60), (_, i) => {
115
+ return {
116
+ value: i,
117
+ label: i.toLocaleString(locale).padStart(2, (0).toLocaleString(locale)),
118
+ }
119
+ })
120
+
121
+ const selectedHour = date
122
+ ? isAmPm(locale)
123
+ ? convert24hTo12h(date.getHours())
124
+ : date.getHours()
125
+ : null
126
+
127
+ const selectedMinutes = date ? date.getMinutes() : null
128
+
129
+ const selectedSeconds = date ? date.getSeconds() : null
130
+
131
+ const handleClear = (event: React.MouseEvent<HTMLElement>) => {
132
+ event.stopPropagation()
133
+ setDate(null)
134
+ }
135
+
136
+ const handleTimeChange = (set: 'hours' | 'minutes' | 'seconds' | 'toggle', value: string) => {
137
+ const _date = date || new Date('1970-01-01')
138
+
139
+ if (set === 'toggle') {
140
+ if (value === 'am') {
141
+ setAmPm('am')
142
+ _date.setHours(_date.getHours() - 12)
143
+ }
144
+ if (value === 'pm') {
145
+ setAmPm('pm')
146
+ _date.setHours(_date.getHours() + 12)
147
+ }
148
+ }
149
+
150
+ if (set === 'hours') {
151
+ if (isAmPm(locale)) {
152
+ _date.setHours(convert12hTo24h(_ampm, parseInt(value)))
153
+ } else {
154
+ _date.setHours(parseInt(value))
155
+ }
156
+ }
157
+
158
+ if (set === 'minutes') {
159
+ _date.setMinutes(parseInt(value))
160
+ }
161
+
162
+ if (set === 'seconds') {
163
+ _date.setSeconds(parseInt(value))
164
+ }
165
+
166
+ setDate(new Date(_date))
167
+ onChange && onChange(_date.toTimeString(), _date.toLocaleTimeString(), _date)
168
+ }
169
+
170
+ const InputGroup = () => (
171
+ <CInputGroup className="picker-input-group" size={size}>
172
+ <CFormInput
173
+ disabled={disabled}
174
+ placeholder={placeholder}
175
+ readOnly={inputReadOnly}
176
+ ref={inputRef}
177
+ />
178
+ {(defaultIcon !== false || cleaner) && (
179
+ <CInputGroupText>
180
+ {defaultIcon !== false && (
181
+ <span className="picker-input-group-indicator">
182
+ {defaultIcon ? (
183
+ defaultIcon
184
+ ) : (
185
+ <span className="picker-input-group-icon time-picker-input-icon" />
186
+ )}
187
+ </span>
188
+ )}
189
+ {cleaner && date && (
190
+ <span
191
+ className="picker-input-group-cleaner"
192
+ role="button"
193
+ onClick={(event) => handleClear(event)}
194
+ >
195
+ {cleanerIcon ? (
196
+ cleanerIcon
197
+ ) : (
198
+ <span className="picker-input-group-icon time-picker-cleaner-icon" />
199
+ )}
200
+ </span>
201
+ )}
202
+ </CInputGroupText>
203
+ )}
204
+ </CInputGroup>
205
+ )
206
+
207
+ const TimePickerSelect = () => {
208
+ return (
209
+ <>
210
+ <span className="time-picker-inline-icon" />
211
+ <CFormSelect
212
+ disabled={disabled}
213
+ options={getHours(locale).map((option) => {
214
+ return {
215
+ value: option.value.toString(),
216
+ label: option.label,
217
+ }
218
+ })}
219
+ onChange={(event: React.ChangeEvent<HTMLSelectElement>) =>
220
+ handleTimeChange('hours', event.target.value)
221
+ }
222
+ value={selectedHour ? selectedHour : '0'}
223
+ />
224
+ <span className="px-1">:</span>
225
+ <CFormSelect
226
+ disabled={disabled}
227
+ options={Array.from(new Array(60), (_, i) => {
228
+ return {
229
+ value: i.toString(),
230
+ label: i.toLocaleString(locale).padStart(2, (0).toLocaleString(locale)),
231
+ }
232
+ })}
233
+ onChange={(event: React.ChangeEvent<HTMLSelectElement>) =>
234
+ handleTimeChange('minutes', event.target.value)
235
+ }
236
+ value={selectedMinutes ? selectedMinutes : '0'}
237
+ />
238
+ <span className="px-1">:</span>
239
+ <CFormSelect
240
+ disabled={disabled}
241
+ options={Array.from(new Array(60), (_, i) => {
242
+ return {
243
+ value: i.toString(),
244
+ label: i.toLocaleString(locale).padStart(2, (0).toLocaleString(locale)),
245
+ }
246
+ })}
247
+ onChange={(event: React.ChangeEvent<HTMLSelectElement>) =>
248
+ handleTimeChange('seconds', event.target.value)
249
+ }
250
+ value={selectedSeconds ? selectedSeconds : '0'}
251
+ />
252
+ {isAmPm(locale) && (
253
+ <CFormSelect
254
+ disabled={disabled}
255
+ options={[
256
+ { value: 'am', label: 'AM' },
257
+ { value: 'pm', label: 'PM' },
258
+ ]}
259
+ onChange={(event: React.ChangeEvent<HTMLSelectElement>) =>
260
+ handleTimeChange('toggle', event.target.value)
261
+ }
262
+ value={_ampm}
263
+ />
264
+ )}
265
+ </>
266
+ )
267
+ }
268
+
269
+ const TimePickerRoll = () => (
270
+ <>
271
+ <CTimePickerRollCol
272
+ elements={getHours(locale)}
273
+ onClick={(index: number) => handleTimeChange('hours', index.toString())}
274
+ selected={selectedHour}
275
+ />
276
+ <CTimePickerRollCol
277
+ elements={getMinutesOrSeconds(locale)}
278
+ selected={selectedMinutes}
279
+ onClick={(index: number) => handleTimeChange('minutes', index.toString())}
280
+ />
281
+ <CTimePickerRollCol
282
+ elements={getMinutesOrSeconds(locale)}
283
+ selected={selectedSeconds}
284
+ onClick={(index: number) => handleTimeChange('seconds', index.toString())}
285
+ />
286
+ {isAmPm(locale) && (
287
+ <CTimePickerRollCol
288
+ elements={[
289
+ { value: 'am', label: 'AM' },
290
+ { value: 'pm', label: 'PM' },
291
+ ]}
292
+ selected={_ampm}
293
+ onClick={(value: string) => handleTimeChange('toggle', value)}
294
+ />
295
+ )}
296
+ </>
297
+ )
298
+
299
+ const _className = classNames('time-picker', className)
300
+
301
+ return (
302
+ <CPicker
303
+ className={_className}
304
+ container={container}
305
+ disabled={disabled}
306
+ footer={footer}
307
+ onCancelClick={() => {
308
+ setDate(
309
+ value
310
+ ? value instanceof Date
311
+ ? new Date(value)
312
+ : new Date(`1970-01-01 ${value}`)
313
+ : new Date(),
314
+ )
315
+ }}
316
+ toggler={InputGroup()}
317
+ {...rest}
318
+ ref={ref}
319
+ >
320
+ <div
321
+ className={classNames('time-picker-body', {
322
+ ['time-picker-roll']: variant === 'roll',
323
+ })}
324
+ >
325
+ {variant === 'select' ? <TimePickerSelect /> : TimePickerRoll()}
326
+ </div>
327
+ </CPicker>
328
+ )
329
+ },
330
+ )
331
+
332
+ CTimePicker.propTypes = {
333
+ ...CPicker.propTypes,
334
+ className: PropTypes.string,
335
+ locale: PropTypes.string,
336
+ onChange: PropTypes.func,
337
+ value: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.string]),
338
+ variant: PropTypes.oneOf(['roll', 'select']),
339
+ }
340
+
341
+ CTimePicker.displayName = 'CTimePicker'
@@ -0,0 +1,27 @@
1
+ export const convert12hTo24h = (abbr: 'am' | 'pm', hour: number) => {
2
+ if (abbr === 'am' && hour === 12) {
3
+ return 0
4
+ }
5
+ if (abbr === 'am') {
6
+ return hour
7
+ }
8
+ if (abbr === 'pm' && hour === 12) {
9
+ return 12
10
+ }
11
+ return hour + 12
12
+ }
13
+
14
+ export const convert24hTo12h = (hour: number) => hour % 12 || 12
15
+
16
+ export const getAmPm = (date: Date, locale: string) => {
17
+ if (date.toLocaleTimeString(locale).includes('AM')) {
18
+ return 'am'
19
+ }
20
+ if (date.toLocaleTimeString(locale).includes('PM')) {
21
+ return 'pm'
22
+ }
23
+ return date.getHours() >= 12 ? 'pm' : 'am'
24
+ }
25
+
26
+ export const isAmPm = (locale: string) =>
27
+ ['am', 'AM', 'pm', 'PM'].some((el) => new Date().toLocaleString(locale).includes(el))