@coreui/react-pro 4.2.0-beta.0 → 4.2.0-beta.1

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 (35) hide show
  1. package/README.md +1 -1
  2. package/dist/components/calendar/CCalendar.d.ts +27 -10
  3. package/dist/components/calendar/CCalendar.utils.d.ts +2 -1
  4. package/dist/components/daterangepicker/CDateRangePicker.d.ts +26 -11
  5. package/dist/components/picker/CPicker.d.ts +50 -9
  6. package/dist/components/timepicker/CTimePicker.d.ts +9 -13
  7. package/dist/components/timepicker/CTimePicker.utils.d.ts +13 -0
  8. package/dist/index.es.js +243 -206
  9. package/dist/index.es.js.map +1 -1
  10. package/dist/index.js +243 -206
  11. package/dist/index.js.map +1 -1
  12. package/dist/utils/calendar.d.ts +21 -0
  13. package/dist/utils/time.d.ts +17 -0
  14. package/package.json +3 -2
  15. package/src/components/accordion/__tests__/__snapshots__/CAccordionButton.spec.tsx.snap +2 -0
  16. package/src/components/accordion/__tests__/__snapshots__/CAccordionHeader.spec.tsx.snap +2 -0
  17. package/src/components/calendar/CCalendar.tsx +104 -58
  18. package/src/components/calendar/__tests__/CCalendar.spec.tsx +9 -0
  19. package/src/components/calendar/__tests__/__snapshots__/CCalendar.spec.tsx.snap +573 -0
  20. package/src/components/datepicker/CDatePicker.tsx +1 -0
  21. package/src/components/datepicker/__tests__/CDatePicker.spec.tsx +9 -0
  22. package/src/components/datepicker/__tests__/__snapshots__/CDatePicker.spec.tsx.snap +617 -0
  23. package/src/components/daterangepicker/CDateRangePicker.tsx +152 -91
  24. package/src/components/daterangepicker/__tests__/CDateRangePicker.spec.tsx +9 -0
  25. package/src/components/daterangepicker/__tests__/__snapshots__/CDateRangePicker.spec.tsx.snap +1201 -0
  26. package/src/components/dropdown/CDropdownMenu.tsx +5 -3
  27. package/src/components/picker/CPicker.tsx +99 -34
  28. package/src/components/picker/__tests__/CPicker.spec.tsx +9 -0
  29. package/src/components/picker/__tests__/__snapshots__/CPicker.spec.tsx.snap +23 -0
  30. package/src/components/timepicker/CTimePicker.tsx +66 -87
  31. package/src/components/timepicker/CTimePickerRollCol.tsx +7 -1
  32. package/src/components/timepicker/__tests__/CTimePicker.spec.tsx +9 -0
  33. package/src/{components/calendar/CCalendar.utils.ts → utils/calendar.ts} +11 -1
  34. package/src/utils/time.ts +58 -0
  35. package/src/components/timepicker/CTimePicker.utils.ts +0 -27
@@ -1,5 +1,5 @@
1
- import PropTypes from 'prop-types'
2
1
  import React, { ElementType, FC, HTMLAttributes, useContext, useEffect, useRef } from 'react'
2
+ import PropTypes from 'prop-types'
3
3
  import classNames from 'classnames'
4
4
  import { Popper, PopperChildrenProps } from 'react-popper'
5
5
 
@@ -59,14 +59,16 @@ export const CDropdownMenu: FC<CDropdownMenuProps> = ({
59
59
  }
60
60
  }, [visible])
61
61
 
62
- const handleKeyup = (event: Event) => {
62
+ const handleKeyup = (event: KeyboardEvent) => {
63
63
  if (autoClose === false) {
64
64
  return
65
65
  }
66
- if (!dropdownMenuRef.current?.contains(event.target as HTMLElement)) {
66
+
67
+ if (event.key === 'Escape') {
67
68
  setVisible(false)
68
69
  }
69
70
  }
71
+
70
72
  const handleMouseUp = (event: Event) => {
71
73
  if (dropdownToggleRef && dropdownToggleRef.current.contains(event.target as HTMLElement)) {
72
74
  return
@@ -7,11 +7,57 @@ import { CDropdown, CDropdownProps } from '../dropdown/CDropdown'
7
7
  import { CDropdownMenu } from '../dropdown/CDropdownMenu'
8
8
  import { CDropdownToggle } from '../dropdown/CDropdownToggle'
9
9
 
10
+ import { Colors } from '../Types'
11
+
10
12
  export interface CPickerProps extends Omit<CDropdownProps, 'variant'> {
11
13
  /**
12
- * Cancel button text.
14
+ * Toggle visibility or set the content of cancel button.
15
+ *
16
+ * @default 'Cancel'
17
+ */
18
+ cancelButton?: boolean | ReactNode
19
+ /**
20
+ * Sets the color context of the cancel button to one of CoreUI’s themed colors.
21
+ *
22
+ * @type 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'info' | 'dark' | 'light' | string
23
+ * @default 'primary'
24
+ */
25
+ cancelButtonColor?: Colors
26
+ /**
27
+ * Size the cancel button small or large.
28
+ *
29
+ * @default 'sm'
30
+ */
31
+ cancelButtonSize?: 'sm' | 'lg'
32
+ /**
33
+ * Set the cancel button variant to an outlined button or a ghost button.
34
+ *
35
+ * @default 'ghost'
36
+ */
37
+ cancelButtonVariant?: 'outline' | 'ghost'
38
+ /**
39
+ * Toggle visibility or set the content of confirm button.
40
+ *
41
+ * @default 'OK'
13
42
  */
14
- cancelButtonText?: ReactNode
43
+ confirmButton?: boolean | ReactNode
44
+ /**
45
+ * Sets the color context of the confirm button to one of CoreUI’s themed colors.
46
+ *
47
+ * @type 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'info' | 'dark' | 'light' | string
48
+ * @default 'primary'
49
+ */
50
+ confirmButtonColor?: Colors
51
+ /**
52
+ * Size the confirm button small or large.
53
+ *
54
+ * @default 'sm'
55
+ */
56
+ confirmButtonSize?: 'sm' | 'lg'
57
+ /**
58
+ * Set the confirm button variant to an outlined button or a ghost button.
59
+ */
60
+ confirmButtonVariant?: 'outline' | 'ghost'
15
61
  /**
16
62
  * Set container type for the component.
17
63
  */
@@ -24,18 +70,14 @@ export interface CPickerProps extends Omit<CDropdownProps, 'variant'> {
24
70
  * Toggle visibility of footer element or set the content of footer.
25
71
  */
26
72
  footer?: boolean | ReactNode
27
- /**
28
- * Ok button text
29
- */
30
- okButtonText?: ReactNode
31
73
  /**
32
74
  * Callback fired when the cancel button has been clicked.
33
75
  */
34
- onCancelClick?: () => void
76
+ onCancel?: () => void
35
77
  /**
36
- * Callback fired when the ok button has been clicked.
78
+ * Callback fired when the confirm button has been clicked.
37
79
  */
38
- onOkClick?: () => void
80
+ onConfirm?: () => void
39
81
  /**
40
82
  * The content of toggler.
41
83
  */
@@ -46,14 +88,20 @@ export const CPicker = forwardRef<HTMLDivElement | HTMLLIElement, CPickerProps>(
46
88
  (
47
89
  {
48
90
  children,
49
- cancelButtonText = 'Cancel',
91
+ cancelButton = 'Cancel',
92
+ cancelButtonColor = 'primary',
93
+ cancelButtonSize = 'sm',
94
+ cancelButtonVariant = 'ghost',
95
+ confirmButton = 'OK',
96
+ confirmButtonColor = 'primary',
97
+ confirmButtonSize = 'sm',
98
+ confirmButtonVariant,
50
99
  className,
51
100
  container = 'dropdown',
52
101
  disabled,
53
102
  footer,
54
- okButtonText = 'OK',
55
- onCancelClick,
56
- onOkClick,
103
+ onCancel,
104
+ onConfirm,
57
105
  onHide,
58
106
  onShow,
59
107
  toggler: Toggler,
@@ -70,24 +118,29 @@ export const CPicker = forwardRef<HTMLDivElement | HTMLLIElement, CPickerProps>(
70
118
 
71
119
  const Footer = () => (
72
120
  <>
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>
121
+ {cancelButton && (
122
+ <CButton
123
+ color={cancelButtonColor}
124
+ size={cancelButtonSize}
125
+ variant={cancelButtonVariant}
126
+ onClick={() => onCancel && onCancel()}
127
+ >
128
+ {cancelButton}
129
+ </CButton>
130
+ )}
131
+ {confirmButton && (
132
+ <CButton
133
+ color={confirmButtonColor}
134
+ size={confirmButtonSize}
135
+ variant={confirmButtonVariant}
136
+ onClick={() => {
137
+ onConfirm && onConfirm()
138
+ setVisible(false)
139
+ }}
140
+ >
141
+ {confirmButton}
142
+ </CButton>
143
+ )}
91
144
  </>
92
145
  )
93
146
 
@@ -134,9 +187,21 @@ export const CPicker = forwardRef<HTMLDivElement | HTMLLIElement, CPickerProps>(
134
187
  CPicker.displayName = 'CPicker'
135
188
 
136
189
  CPicker.propTypes = {
137
- ...CDropdown.propTypes,
190
+ cancelButton: PropTypes.oneOfType([PropTypes.bool, PropTypes.node]),
191
+ cancelButtonColor: CButton.propTypes?.color,
192
+ cancelButtonSize: CButton.propTypes?.size,
193
+ cancelButtonVariant: CButton.propTypes?.variant,
194
+ confirmButton: PropTypes.oneOfType([PropTypes.bool, PropTypes.node]),
195
+ confirmButtonColor: CButton.propTypes?.color,
196
+ confirmButtonSize: CButton.propTypes?.size,
197
+ confirmButtonVariant: CButton.propTypes?.variant,
198
+ children: PropTypes.node,
199
+ className: PropTypes.string,
138
200
  container: PropTypes.oneOf(['dropdown', 'inline']),
139
201
  disabled: PropTypes.bool,
140
- onCancelClick: PropTypes.func,
141
- onOkClick: PropTypes.func,
202
+ footer: PropTypes.oneOfType([PropTypes.bool, PropTypes.node]),
203
+ onCancel: PropTypes.func,
204
+ onConfirm: PropTypes.func,
205
+ toggler: PropTypes.node,
206
+ ...CDropdown.propTypes,
142
207
  }
@@ -0,0 +1,9 @@
1
+ import * as React from 'react'
2
+ import { render } from '@testing-library/react'
3
+ import '@testing-library/jest-dom/extend-expect'
4
+ import { CPicker } from '../CPicker'
5
+
6
+ test('loads and displays CPicker component', async () => {
7
+ const { container } = render(<CPicker>Test</CPicker>)
8
+ expect(container).toMatchSnapshot()
9
+ })
@@ -0,0 +1,23 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`loads and displays CPicker component 1`] = `
4
+ <div>
5
+ <div
6
+ class="dropdown picker"
7
+ >
8
+ <button
9
+ aria-expanded="false"
10
+ class="btn btn-primary dropdown-toggle"
11
+ tabindex="0"
12
+ type="button"
13
+ />
14
+ <div
15
+ aria-hidden="true"
16
+ class="dropdown-menu"
17
+ role="menu"
18
+ >
19
+ Test
20
+ </div>
21
+ </div>
22
+ </div>
23
+ `;
@@ -10,7 +10,18 @@ import { CInputGroupText } from '../form/CInputGroupText'
10
10
 
11
11
  import { CTimePickerRollCol } from './CTimePickerRollCol'
12
12
  import { CPicker, CPickerProps } from './../picker/CPicker'
13
- import { convert12hTo24h, convert24hTo12h, getAmPm, isAmPm } from './CTimePicker.utils'
13
+ import {
14
+ convert12hTo24h,
15
+ convertTimeToDate,
16
+ getAmPm,
17
+ getListOfHours,
18
+ getMinutesOrSeconds,
19
+ getSelectedHour,
20
+ getSelectedMinutes,
21
+ getSelectedSeconds,
22
+ isAmPm,
23
+ isValidTime,
24
+ } from '../../utils/time'
14
25
 
15
26
  export interface CTimePickerProps
16
27
  extends Omit<
@@ -22,29 +33,25 @@ export interface CTimePickerProps
22
33
  */
23
34
  className?: string
24
35
  /**
25
- * Enables selection cleaner element.
36
+ * Toggle visibility or set the content of the cleaner button.
26
37
  */
27
- cleaner?: boolean
38
+ cleaner?: ReactNode | boolean
28
39
  /**
29
- * Cleaner icon.
40
+ * Toggle visibility or set the content of the input indicator.
30
41
  */
31
- cleanerIcon?: ReactNode | boolean
32
- /**
33
- * Default input icon.
34
- */
35
- defaultIcon?: ReactNode | boolean
42
+ indicator?: ReactNode | boolean
36
43
  /**
37
44
  * Toggle the readonly state for the component.
38
45
  */
39
46
  inputReadOnly?: boolean
40
47
  /**
41
- * Sets the default locale for components. If not set, it is inherited from the navigator.language.
48
+ * Sets the default locale for components. If not set, it is inherited from the browser.
42
49
  */
43
50
  locale?: string
44
51
  /**
45
- * Callback fired when the user hovers over the calendar cell.
52
+ * Callback fired when the time changed.
46
53
  */
47
- onChange?: (timeString: string, localeTimeString: string, date: Date) => void
54
+ onTimeChange?: (timeString: string, localeTimeString: string, date: Date) => void
48
55
  /**
49
56
  * Specifies a short hint that is visible in the input.
50
57
  */
@@ -54,9 +61,9 @@ export interface CTimePickerProps
54
61
  */
55
62
  size?: 'sm' | 'lg'
56
63
  /**
57
- * Default value of the component
64
+ * Initial selected time.
58
65
  */
59
- value?: Date | string | null
66
+ time?: Date | string | null
60
67
  /**
61
68
  * Set the time picker variant to a roll or select.
62
69
  */
@@ -68,65 +75,39 @@ export const CTimePicker = forwardRef<HTMLDivElement | HTMLLIElement, CTimePicke
68
75
  {
69
76
  className,
70
77
  cleaner = true,
71
- cleanerIcon,
72
78
  container = 'dropdown',
73
- defaultIcon,
74
79
  disabled,
75
80
  footer = true,
81
+ indicator = true,
76
82
  inputReadOnly,
77
- locale = typeof navigator === 'undefined' ? 'en-US' : navigator.language,
78
- onChange,
83
+ locale = 'default',
84
+ onTimeChange,
85
+ onHide,
86
+ onShow,
79
87
  placeholder = 'Select time',
80
88
  size,
81
- value,
89
+ time,
82
90
  variant = 'roll',
83
91
  ...rest
84
92
  },
85
93
  ref,
86
94
  ) => {
87
95
  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
- )
96
+ const [date, setDate] = useState<Date | null>(convertTimeToDate(time))
97
+ const [initialDate, setInitialDate] = useState<Date | null>(null)
91
98
  const [_ampm, setAmPm] = useState<'am' | 'pm'>(date ? getAmPm(new Date(date), locale) : 'am')
92
99
 
93
100
  useEffect(() => {
94
- setDate(
95
- value ? (value instanceof Date ? new Date(value) : new Date(`1970-01-01 ${value}`)) : null,
96
- )
97
- }, [value])
101
+ setDate(convertTimeToDate(time))
102
+ }, [time])
98
103
 
99
104
  useEffect(() => {
100
105
  if (inputRef.current) {
101
106
  inputRef.current.value = date ? date.toLocaleTimeString(locale) : ''
102
107
  }
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
108
 
127
- const selectedMinutes = date ? date.getMinutes() : null
128
-
129
- const selectedSeconds = date ? date.getSeconds() : null
109
+ date && setAmPm(getAmPm(new Date(date), locale))
110
+ }, [date])
130
111
 
131
112
  const handleClear = (event: React.MouseEvent<HTMLElement>) => {
132
113
  event.stopPropagation()
@@ -138,11 +119,9 @@ export const CTimePicker = forwardRef<HTMLDivElement | HTMLLIElement, CTimePicke
138
119
 
139
120
  if (set === 'toggle') {
140
121
  if (value === 'am') {
141
- setAmPm('am')
142
122
  _date.setHours(_date.getHours() - 12)
143
123
  }
144
124
  if (value === 'pm') {
145
- setAmPm('pm')
146
125
  _date.setHours(_date.getHours() + 12)
147
126
  }
148
127
  }
@@ -164,38 +143,41 @@ export const CTimePicker = forwardRef<HTMLDivElement | HTMLLIElement, CTimePicke
164
143
  }
165
144
 
166
145
  setDate(new Date(_date))
167
- onChange && onChange(_date.toTimeString(), _date.toLocaleTimeString(), _date)
146
+ onTimeChange && onTimeChange(_date.toTimeString(), _date.toLocaleTimeString(), _date)
168
147
  }
169
148
 
170
149
  const InputGroup = () => (
171
150
  <CInputGroup className="picker-input-group" size={size}>
172
151
  <CFormInput
173
152
  disabled={disabled}
153
+ onChange={(event) =>
154
+ isValidTime(event.target.value) && setDate(convertTimeToDate(event.target.value))
155
+ }
174
156
  placeholder={placeholder}
175
157
  readOnly={inputReadOnly}
176
158
  ref={inputRef}
177
159
  />
178
- {(defaultIcon !== false || cleaner) && (
160
+ {(indicator || cleaner) && (
179
161
  <CInputGroupText>
180
- {defaultIcon !== false && (
162
+ {indicator && (
181
163
  <span className="picker-input-group-indicator">
182
- {defaultIcon ? (
183
- defaultIcon
184
- ) : (
164
+ {typeof indicator === 'boolean' ? (
185
165
  <span className="picker-input-group-icon time-picker-input-icon" />
166
+ ) : (
167
+ indicator
186
168
  )}
187
169
  </span>
188
170
  )}
189
171
  {cleaner && date && (
190
172
  <span
191
173
  className="picker-input-group-cleaner"
192
- role="button"
193
174
  onClick={(event) => handleClear(event)}
175
+ role="button"
194
176
  >
195
- {cleanerIcon ? (
196
- cleanerIcon
197
- ) : (
177
+ {typeof cleaner === 'boolean' ? (
198
178
  <span className="picker-input-group-icon time-picker-cleaner-icon" />
179
+ ) : (
180
+ cleaner
199
181
  )}
200
182
  </span>
201
183
  )}
@@ -210,7 +192,7 @@ export const CTimePicker = forwardRef<HTMLDivElement | HTMLLIElement, CTimePicke
210
192
  <span className="time-picker-inline-icon" />
211
193
  <CFormSelect
212
194
  disabled={disabled}
213
- options={getHours(locale).map((option) => {
195
+ options={getListOfHours(locale).map((option) => {
214
196
  return {
215
197
  value: option.value.toString(),
216
198
  label: option.label,
@@ -219,12 +201,12 @@ export const CTimePicker = forwardRef<HTMLDivElement | HTMLLIElement, CTimePicke
219
201
  onChange={(event: React.ChangeEvent<HTMLSelectElement>) =>
220
202
  handleTimeChange('hours', event.target.value)
221
203
  }
222
- value={selectedHour ? selectedHour : '0'}
204
+ value={getSelectedHour(date, locale)}
223
205
  />
224
- <span className="px-1">:</span>
206
+ <>:</>
225
207
  <CFormSelect
226
208
  disabled={disabled}
227
- options={Array.from(new Array(60), (_, i) => {
209
+ options={Array.from({ length: 60 }, (_, i) => {
228
210
  return {
229
211
  value: i.toString(),
230
212
  label: i.toLocaleString(locale).padStart(2, (0).toLocaleString(locale)),
@@ -233,12 +215,12 @@ export const CTimePicker = forwardRef<HTMLDivElement | HTMLLIElement, CTimePicke
233
215
  onChange={(event: React.ChangeEvent<HTMLSelectElement>) =>
234
216
  handleTimeChange('minutes', event.target.value)
235
217
  }
236
- value={selectedMinutes ? selectedMinutes : '0'}
218
+ value={getSelectedMinutes(date)}
237
219
  />
238
- <span className="px-1">:</span>
220
+ <>:</>
239
221
  <CFormSelect
240
222
  disabled={disabled}
241
- options={Array.from(new Array(60), (_, i) => {
223
+ options={Array.from({ length: 60 }, (_, i) => {
242
224
  return {
243
225
  value: i.toString(),
244
226
  label: i.toLocaleString(locale).padStart(2, (0).toLocaleString(locale)),
@@ -247,7 +229,7 @@ export const CTimePicker = forwardRef<HTMLDivElement | HTMLLIElement, CTimePicke
247
229
  onChange={(event: React.ChangeEvent<HTMLSelectElement>) =>
248
230
  handleTimeChange('seconds', event.target.value)
249
231
  }
250
- value={selectedSeconds ? selectedSeconds : '0'}
232
+ value={getSelectedSeconds(date)}
251
233
  />
252
234
  {isAmPm(locale) && (
253
235
  <CFormSelect
@@ -269,19 +251,19 @@ export const CTimePicker = forwardRef<HTMLDivElement | HTMLLIElement, CTimePicke
269
251
  const TimePickerRoll = () => (
270
252
  <>
271
253
  <CTimePickerRollCol
272
- elements={getHours(locale)}
254
+ elements={getListOfHours(locale)}
273
255
  onClick={(index: number) => handleTimeChange('hours', index.toString())}
274
- selected={selectedHour}
256
+ selected={getSelectedHour(date, locale)}
275
257
  />
276
258
  <CTimePickerRollCol
277
259
  elements={getMinutesOrSeconds(locale)}
278
- selected={selectedMinutes}
279
260
  onClick={(index: number) => handleTimeChange('minutes', index.toString())}
261
+ selected={getSelectedMinutes(date)}
280
262
  />
281
263
  <CTimePickerRollCol
282
264
  elements={getMinutesOrSeconds(locale)}
283
- selected={selectedSeconds}
284
265
  onClick={(index: number) => handleTimeChange('seconds', index.toString())}
266
+ selected={getSelectedSeconds(date)}
285
267
  />
286
268
  {isAmPm(locale) && (
287
269
  <CTimePickerRollCol
@@ -289,8 +271,8 @@ export const CTimePicker = forwardRef<HTMLDivElement | HTMLLIElement, CTimePicke
289
271
  { value: 'am', label: 'AM' },
290
272
  { value: 'pm', label: 'PM' },
291
273
  ]}
292
- selected={_ampm}
293
274
  onClick={(value: string) => handleTimeChange('toggle', value)}
275
+ selected={_ampm}
294
276
  />
295
277
  )}
296
278
  </>
@@ -304,14 +286,11 @@ export const CTimePicker = forwardRef<HTMLDivElement | HTMLLIElement, CTimePicke
304
286
  container={container}
305
287
  disabled={disabled}
306
288
  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
- )
289
+ onCancel={() => initialDate && setDate(new Date(initialDate))}
290
+ onHide={() => onHide && onHide()}
291
+ onShow={() => {
292
+ date && setInitialDate(new Date(date))
293
+ onShow && onShow()
315
294
  }}
316
295
  toggler={InputGroup()}
317
296
  {...rest}
@@ -333,8 +312,8 @@ CTimePicker.propTypes = {
333
312
  ...CPicker.propTypes,
334
313
  className: PropTypes.string,
335
314
  locale: PropTypes.string,
336
- onChange: PropTypes.func,
337
- value: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.string]),
315
+ onTimeChange: PropTypes.func,
316
+ time: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.string]),
338
317
  variant: PropTypes.oneOf(['roll', 'select']),
339
318
  }
340
319
 
@@ -17,14 +17,20 @@ export interface CTimePickerRollColProps {
17
17
 
18
18
  export const CTimePickerRollCol = forwardRef<HTMLDivElement, CTimePickerRollColProps>(
19
19
  ({ elements, onClick, selected }, ref) => {
20
+ const init = useRef(true)
20
21
  const colRef = useRef<HTMLDivElement>(null)
21
22
  const forkedRef = useForkedRef(ref, colRef)
22
23
 
23
24
  useEffect(() => {
24
25
  const nodeEl = colRef.current?.querySelector('.selected')
25
26
  if (nodeEl && nodeEl instanceof HTMLElement) {
26
- colRef.current?.scrollTo({ top: nodeEl.offsetTop, behavior: 'smooth' })
27
+ colRef.current?.scrollTo({
28
+ top: nodeEl.offsetTop,
29
+ behavior: init.current ? 'auto' : 'smooth',
30
+ })
27
31
  }
32
+
33
+ init.current = false
28
34
  }, [selected])
29
35
 
30
36
  return (
@@ -0,0 +1,9 @@
1
+ import * as React from 'react'
2
+ import { render } from '@testing-library/react'
3
+ import '@testing-library/jest-dom/extend-expect'
4
+ import { CTimePicker } from '../CTimePicker'
5
+
6
+ test('loads and displays CTimePicker component', async () => {
7
+ const { container } = render(<CTimePicker />)
8
+ expect(container).toMatchSnapshot()
9
+ })
@@ -115,7 +115,12 @@ export const getMonthDetails = (year: number, month: number, firstDayOfWeek: num
115
115
  return weeks
116
116
  }
117
117
 
118
- export const isDisabled = (date: Date, min?: Date, max?: Date, dates?: Date[] | Date[][]) => {
118
+ export const isDateDisabled = (
119
+ date: Date,
120
+ min?: Date | null,
121
+ max?: Date | null,
122
+ dates?: Date[] | Date[][],
123
+ ) => {
119
124
  let disabled
120
125
  if (dates) {
121
126
  dates.forEach((_date: Date | Date[]) => {
@@ -181,3 +186,8 @@ export const isToday = (date: Date) => {
181
186
  date.getFullYear() == today.getFullYear()
182
187
  )
183
188
  }
189
+
190
+ export const isValidDate = (date: string) => {
191
+ const d = new Date(date)
192
+ return d instanceof Date && d.getTime()
193
+ }
@@ -0,0 +1,58 @@
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 convertTimeToDate = (time: Date | string | null | undefined) =>
17
+ time ? (time instanceof Date ? new Date(time) : new Date(`1970-01-01 ${time}`)) : null
18
+
19
+ export const getAmPm = (date: Date, locale: string) => {
20
+ if (date.toLocaleTimeString(locale).includes('AM')) {
21
+ return 'am'
22
+ }
23
+ if (date.toLocaleTimeString(locale).includes('PM')) {
24
+ return 'pm'
25
+ }
26
+ return date.getHours() >= 12 ? 'pm' : 'am'
27
+ }
28
+
29
+ export const getListOfHours = (locale: string) =>
30
+ Array.from({ length: isAmPm(locale) ? 12 : 24 }, (_, i) => {
31
+ return {
32
+ value: isAmPm(locale) ? i + 1 : i,
33
+ label: (isAmPm(locale) ? i + 1 : i).toLocaleString(locale),
34
+ }
35
+ })
36
+
37
+ export const getMinutesOrSeconds = (locale: string) =>
38
+ Array.from({ length: 60 }, (_, i) => {
39
+ return {
40
+ value: i,
41
+ label: i.toLocaleString(locale).padStart(2, (0).toLocaleString(locale)),
42
+ }
43
+ })
44
+
45
+ export const getSelectedHour = (date: Date | null, locale: string) =>
46
+ date ? (isAmPm(locale) ? convert24hTo12h(date.getHours()) : date.getHours()) : ''
47
+
48
+ export const getSelectedMinutes = (date: Date | null) => (date ? date.getMinutes() : '')
49
+
50
+ export const getSelectedSeconds = (date: Date | null) => (date ? date.getSeconds() : '')
51
+
52
+ export const isAmPm = (locale: string) =>
53
+ ['am', 'AM', 'pm', 'PM'].some((el) => new Date().toLocaleString(locale).includes(el))
54
+
55
+ export const isValidTime = (time: string) => {
56
+ const d = new Date(`1970-01-01 ${time}`)
57
+ return d instanceof Date && d.getTime()
58
+ }