@coreui/react-pro 5.0.0-rc.1 → 5.0.0-rc.2

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 (46) hide show
  1. package/README.md +1 -1
  2. package/dist/cjs/components/badge/CBadge.js +1 -1
  3. package/dist/cjs/components/badge/CBadge.js.map +1 -1
  4. package/dist/cjs/components/calendar/CCalendar.d.ts +3 -3
  5. package/dist/cjs/components/date-range-picker/CDateRangePicker.d.ts +3 -3
  6. package/dist/cjs/components/dropdown/CDropdownToggle.d.ts +1 -1
  7. package/dist/cjs/components/nav/CNavGroup.d.ts +1 -1
  8. package/dist/cjs/components/nav/CNavGroupItems.d.ts +1 -1
  9. package/dist/cjs/components/nav/CNavItem.d.ts +1 -1
  10. package/dist/cjs/components/sidebar/CSidebarBrand.d.ts +1 -1
  11. package/dist/cjs/components/sidebar/CSidebarNav.d.ts +1 -1
  12. package/dist/cjs/components/time-picker/CTimePicker.d.ts +20 -2
  13. package/dist/cjs/components/time-picker/CTimePicker.js +30 -22
  14. package/dist/cjs/components/time-picker/CTimePicker.js.map +1 -1
  15. package/dist/cjs/components/time-picker/utils.d.ts +67 -10
  16. package/dist/cjs/components/time-picker/utils.js +114 -27
  17. package/dist/cjs/components/time-picker/utils.js.map +1 -1
  18. package/dist/esm/components/badge/CBadge.js +1 -1
  19. package/dist/esm/components/badge/CBadge.js.map +1 -1
  20. package/dist/esm/components/calendar/CCalendar.d.ts +3 -3
  21. package/dist/esm/components/date-range-picker/CDateRangePicker.d.ts +3 -3
  22. package/dist/esm/components/dropdown/CDropdownToggle.d.ts +1 -1
  23. package/dist/esm/components/nav/CNavGroup.d.ts +1 -1
  24. package/dist/esm/components/nav/CNavGroupItems.d.ts +1 -1
  25. package/dist/esm/components/nav/CNavItem.d.ts +1 -1
  26. package/dist/esm/components/sidebar/CSidebarBrand.d.ts +1 -1
  27. package/dist/esm/components/sidebar/CSidebarNav.d.ts +1 -1
  28. package/dist/esm/components/time-picker/CTimePicker.d.ts +20 -2
  29. package/dist/esm/components/time-picker/CTimePicker.js +31 -23
  30. package/dist/esm/components/time-picker/CTimePicker.js.map +1 -1
  31. package/dist/esm/components/time-picker/utils.d.ts +67 -10
  32. package/dist/esm/components/time-picker/utils.js +114 -28
  33. package/dist/esm/components/time-picker/utils.js.map +1 -1
  34. package/package.json +12 -12
  35. package/src/components/badge/CBadge.tsx +1 -1
  36. package/src/components/calendar/CCalendar.tsx +3 -3
  37. package/src/components/date-range-picker/CDateRangePicker.tsx +3 -3
  38. package/src/components/dropdown/CDropdownToggle.tsx +1 -1
  39. package/src/components/nav/CNavGroup.tsx +1 -1
  40. package/src/components/nav/CNavGroupItems.tsx +1 -1
  41. package/src/components/nav/CNavItem.tsx +1 -1
  42. package/src/components/sidebar/CSidebarBrand.tsx +1 -1
  43. package/src/components/sidebar/CSidebarNav.tsx +1 -1
  44. package/src/components/time-picker/CTimePicker.tsx +73 -30
  45. package/src/components/time-picker/utils.ts +128 -68
  46. package/src/hooks/useDebouncedCallback.ts +1 -1
@@ -20,6 +20,7 @@ import {
20
20
 
21
21
  import { Colors } from '../../types'
22
22
  import type { LocalizedTimePartials } from './types'
23
+ import { useDebouncedCallback } from '../../hooks'
23
24
 
24
25
  export interface CTimePickerProps
25
26
  extends Omit<CFormControlWrapperProps, 'floatingLabel'>,
@@ -77,10 +78,22 @@ export interface CTimePickerProps
77
78
  * Set the confirm button variant to an outlined button or a ghost button.
78
79
  */
79
80
  confirmButtonVariant?: 'outline' | 'ghost'
81
+ /**
82
+ * Specify a list of available hours using an array, or customize the filtering of hours through a function.
83
+ *
84
+ * @since 5.0.0-rc.2
85
+ */
86
+ hours?: number[] | ((hour: number) => number[])
80
87
  /**
81
88
  * Toggle visibility or set the content of the input indicator.
82
89
  */
83
90
  indicator?: ReactNode | boolean
91
+ /**
92
+ * Defines the delay (in milliseconds) for the input field's onChange event.
93
+ *
94
+ * @since v5.0.0-rc.2
95
+ */
96
+ inputOnChangeDelay?: number
84
97
  /**
85
98
  * Toggle the readonly state for the component.
86
99
  */
@@ -89,6 +102,12 @@ export interface CTimePickerProps
89
102
  * Sets the default locale for components. If not set, it is inherited from the browser.
90
103
  */
91
104
  locale?: string
105
+ /**
106
+ * Toggle the display of minutes, specify a list of available minutes using an array, or customize the filtering of minutes through a function.
107
+ *
108
+ * @since 5.0.0-rc.2
109
+ */
110
+ minutes?: boolean | number[] | ((minute: number) => number[])
92
111
  /**
93
112
  * Callback fired when the time changed.
94
113
  */
@@ -104,11 +123,11 @@ export interface CTimePickerProps
104
123
  */
105
124
  required?: boolean
106
125
  /**
107
- * Show seconds.
126
+ * Toggle the display of seconds, specify a list of available seconds using an array, or customize the filtering of seconds through a function.
108
127
  *
109
128
  * @since 4.8.0
110
129
  */
111
- seconds?: boolean
130
+ seconds?: boolean | number[] | ((second: number) => number[])
112
131
  /**
113
132
  * Size the component small or large.
114
133
  */
@@ -143,12 +162,15 @@ export const CTimePicker = forwardRef<HTMLDivElement | HTMLLIElement, CTimePicke
143
162
  feedbackInvalid,
144
163
  feedbackValid,
145
164
  footer = true,
165
+ hours,
146
166
  id,
147
167
  indicator = true,
168
+ inputOnChangeDelay = 750,
148
169
  inputReadOnly,
149
170
  invalid,
150
171
  label,
151
172
  locale = 'default',
173
+ minutes = true,
152
174
  onTimeChange,
153
175
  onHide,
154
176
  onShow,
@@ -191,13 +213,15 @@ export const CTimePicker = forwardRef<HTMLDivElement | HTMLLIElement, CTimePicke
191
213
  }, [valid, invalid])
192
214
 
193
215
  useEffect(() => {
194
- setLocalizedTimePartials(getLocalizedTimePartials(locale, ampm))
216
+ setLocalizedTimePartials(getLocalizedTimePartials(locale, ampm, hours, minutes, seconds))
195
217
 
196
218
  if (inputRef.current) {
197
219
  inputRef.current.value = date
198
220
  ? date.toLocaleTimeString(locale, {
199
221
  hour12: localizedTimePartials && localizedTimePartials.hour12,
200
- ...(!seconds && { timeStyle: 'short' }),
222
+ hour: 'numeric',
223
+ ...(minutes && { minute: 'numeric' }),
224
+ ...(seconds && { second: 'numeric' }),
201
225
  })
202
226
  : ''
203
227
  }
@@ -239,6 +263,11 @@ export const CTimePicker = forwardRef<HTMLDivElement | HTMLLIElement, CTimePicke
239
263
  setIsValid(false)
240
264
  }
241
265
 
266
+ const handleOnChange = useDebouncedCallback(
267
+ (value: string) => isValidTime(value) && setDate(convertTimeToDate(value)),
268
+ inputOnChangeDelay,
269
+ )
270
+
242
271
  const handleTimeChange = (set: 'hours' | 'minutes' | 'seconds' | 'toggle', value: string) => {
243
272
  const _date = date || new Date('1970-01-01')
244
273
 
@@ -276,11 +305,8 @@ export const CTimePicker = forwardRef<HTMLDivElement | HTMLLIElement, CTimePicke
276
305
  <input
277
306
  autoComplete="off"
278
307
  className="time-picker-input"
279
- // delay={true}
280
308
  disabled={disabled}
281
- onChange={(event) =>
282
- isValidTime(event.target.value) && setDate(convertTimeToDate(event.target.value))
283
- }
309
+ onChange={(event) => handleOnChange(event.target.value)}
284
310
  placeholder={placeholder}
285
311
  readOnly={inputReadOnly}
286
312
  required={required}
@@ -320,22 +346,26 @@ export const CTimePicker = forwardRef<HTMLDivElement | HTMLLIElement, CTimePicke
320
346
  </option>
321
347
  ))}
322
348
  </select>
323
- <>:</>
324
- <select
325
- className="time-picker-inline-select"
326
- disabled={disabled}
327
- onChange={(event: React.ChangeEvent<HTMLSelectElement>) =>
328
- handleTimeChange('minutes', event.target.value)
329
- }
330
- value={getSelectedMinutes(date)}
331
- >
332
- {localizedTimePartials &&
333
- localizedTimePartials.listOfMinutes.map((option, index) => (
334
- <option value={option.value.toString()} key={index}>
335
- {option.label}
336
- </option>
337
- ))}
338
- </select>
349
+ {minutes && (
350
+ <>
351
+ <>:</>
352
+ <select
353
+ className="time-picker-inline-select"
354
+ disabled={disabled}
355
+ onChange={(event: React.ChangeEvent<HTMLSelectElement>) =>
356
+ handleTimeChange('minutes', event.target.value)
357
+ }
358
+ value={getSelectedMinutes(date)}
359
+ >
360
+ {localizedTimePartials &&
361
+ localizedTimePartials.listOfMinutes.map((option, index) => (
362
+ <option value={option.value.toString()} key={index}>
363
+ {option.label}
364
+ </option>
365
+ ))}
366
+ </select>
367
+ </>
368
+ )}
339
369
  {seconds && (
340
370
  <>
341
371
  <>:</>
@@ -459,11 +489,13 @@ export const CTimePicker = forwardRef<HTMLDivElement | HTMLLIElement, CTimePicke
459
489
  onClick={(index: number) => handleTimeChange('hours', index.toString())}
460
490
  selected={getSelectedHour(date, locale, ampm)}
461
491
  />
462
- <CTimePickerRollCol
463
- elements={localizedTimePartials && localizedTimePartials.listOfMinutes}
464
- onClick={(index: number) => handleTimeChange('minutes', index.toString())}
465
- selected={getSelectedMinutes(date)}
466
- />
492
+ {minutes && (
493
+ <CTimePickerRollCol
494
+ elements={localizedTimePartials && localizedTimePartials.listOfMinutes}
495
+ onClick={(index: number) => handleTimeChange('minutes', index.toString())}
496
+ selected={getSelectedMinutes(date)}
497
+ />
498
+ )}
467
499
  {seconds && (
468
500
  <CTimePickerRollCol
469
501
  elements={localizedTimePartials && localizedTimePartials.listOfSeconds}
@@ -503,10 +535,21 @@ CTimePicker.propTypes = {
503
535
  confirmButtonColor: CButton.propTypes?.color,
504
536
  confirmButtonSize: CButton.propTypes?.size,
505
537
  confirmButtonVariant: CButton.propTypes?.variant,
538
+ hours: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.number.isRequired), PropTypes.func]),
539
+ inputOnChangeDelay: PropTypes.number,
506
540
  locale: PropTypes.string,
541
+ minutes: PropTypes.oneOfType([
542
+ PropTypes.bool,
543
+ PropTypes.arrayOf(PropTypes.number.isRequired),
544
+ PropTypes.func,
545
+ ]),
507
546
  onTimeChange: PropTypes.func,
508
547
  required: PropTypes.bool,
509
- seconds: PropTypes.bool,
548
+ seconds: PropTypes.oneOfType([
549
+ PropTypes.bool,
550
+ PropTypes.arrayOf(PropTypes.number.isRequired),
551
+ PropTypes.func,
552
+ ]),
510
553
  time: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.string]),
511
554
  variant: PropTypes.oneOf(['roll', 'select']),
512
555
  }
@@ -1,134 +1,194 @@
1
1
  import type { LocalizedTimePartials } from './types'
2
2
 
3
+ /**
4
+ * Converts a 12-hour time format to a 24-hour time format.
5
+ * @param {('am' | 'pm')} abbr The abbreviation indicating AM or PM.
6
+ * @param {number} hour The hour to be converted.
7
+ * @returns {number} The hour in 24-hour format.
8
+ */
3
9
  export const convert12hTo24h = (abbr: 'am' | 'pm', hour: number) => {
4
10
  if (abbr === 'am' && hour === 12) {
5
11
  return 0
6
12
  }
13
+
7
14
  if (abbr === 'am') {
8
15
  return hour
9
16
  }
17
+
10
18
  if (abbr === 'pm' && hour === 12) {
11
19
  return 12
12
20
  }
21
+
13
22
  return hour + 12
14
23
  }
15
24
 
25
+ /**
26
+ * Converts a 24-hour time format to a 12-hour format.
27
+ * @param {number} hour The hour to be converted.
28
+ * @returns {number} The hour in 12-hour format.
29
+ */
16
30
  export const convert24hTo12h = (hour: number) => hour % 12 || 12
17
31
 
32
+ /**
33
+ * Converts a time input into a Date object.
34
+ * @param {Date | string | null | undefined} time The time input to be converted.
35
+ * @returns {Date | null} The converted Date object or null if the input is falsy.
36
+ */
18
37
  export const convertTimeToDate = (time: Date | string | null | undefined) =>
19
- time
20
- ? time instanceof Date
21
- ? time
22
- : new Date(`1970-01-01 ${time}`)
23
- : null
38
+ time ? (time instanceof Date ? time : new Date(`1970-01-01 ${time}`)) : null
24
39
 
40
+ /**
41
+ * Retrieves the AM/PM part of the specified date according to the given locale.
42
+ * @param {Date} date The date from which to extract the AM/PM part.
43
+ * @param {string} locale The locale to use for formatting.
44
+ * @returns {string} 'am' or 'pm' based on the given date and locale.
45
+ */
25
46
  export const getAmPm = (date: Date, locale: string) => {
26
47
  if (date.toLocaleTimeString(locale).includes('AM')) {
27
48
  return 'am'
28
49
  }
50
+
29
51
  if (date.toLocaleTimeString(locale).includes('PM')) {
30
52
  return 'pm'
31
53
  }
54
+
32
55
  return date.getHours() >= 12 ? 'pm' : 'am'
33
56
  }
34
57
 
35
- // TODO: clean-up
36
- export const getListOfHours = (locale: string, ampm: 'auto' | boolean = 'auto') =>
37
- Array.from({ length: (ampm === 'auto' && isAmPm(locale)) || ampm === true ? 12 : 24 }, (_, i) => {
38
- return {
39
- value: (ampm === 'auto' && isAmPm(locale)) || ampm === true ? i + 1 : i,
40
- label: ((ampm === 'auto' && isAmPm(locale)) || ampm === true ? i + 1 : i).toLocaleString(
41
- locale,
42
- ),
43
- }
58
+ /**
59
+ * Formats an array of time values (hours, minutes, or seconds) according to the specified locale and partial.
60
+ * @param {number[]} values An array of time values to format.
61
+ * @param {string} locale The locale to use for formatting.
62
+ * @param {('hour' | 'minute' | 'second')} partial The type of time value to format.
63
+ * @returns {Array} An array of objects with the original value and its localized label.
64
+ */
65
+ export const formatTimePartials = (
66
+ values: number[],
67
+ locale: string,
68
+ partial: 'hour' | 'minute' | 'second',
69
+ ) => {
70
+ const date = new Date()
71
+
72
+ const formatter = new Intl.DateTimeFormat(locale, {
73
+ hour: 'numeric',
74
+ minute: '2-digit',
75
+ second: '2-digit',
44
76
  })
45
77
 
46
- // TODO: clean-up
47
- export const getListOfMinutes = (locale: string, valueAsString = false) =>
48
- Array.from({ length: 60 }, (_, i) => {
49
- const d = new Date()
50
- d.setMinutes(i)
51
- return {
52
- value: valueAsString ? i.toString() : i,
53
- label: d
54
- .toLocaleTimeString(locale, {
55
- minute: '2-digit',
56
- second: '2-digit',
57
- })
58
- .split(/[^A-Za-z0-9\u06F0-\u06F90-9]/)[0],
78
+ return values.map((value) => {
79
+ if (partial === 'hour') {
80
+ date.setHours(value)
81
+ }
82
+
83
+ if (partial === 'minute') {
84
+ date.setMinutes(value)
85
+ }
86
+
87
+ if (partial === 'second') {
88
+ date.setSeconds(value)
59
89
  }
60
- })
61
90
 
62
- // TODO: clean-up
63
- export const getListOfSeconds = (locale: string, valueAsString = false) =>
64
- Array.from({ length: 60 }, (_, i) => {
65
- const d = new Date()
66
- d.setSeconds(i)
67
91
  return {
68
- value: valueAsString ? i.toString() : i,
69
- label: d
70
- .toLocaleTimeString(locale, {
71
- minute: '2-digit',
72
- second: '2-digit',
73
- })
74
- .split(/[^A-Za-z0-9\u06F0-\u06F90-9]/)[0],
92
+ value,
93
+ label: formatter.formatToParts(date).find((part) => part.type === partial)?.value || '',
75
94
  }
76
95
  })
96
+ }
77
97
 
98
+ /**
99
+ * Generates localized time partials (hours, minutes, seconds) based on the given parameters.
100
+ * @param {string} locale The locale to use for generating localized time partials.
101
+ * @param {'auto' | boolean} ampm Determines whether to use 12-hour or 24-hour format. 'auto' decides based on locale.
102
+ * @param {boolean | number[] | Function} hours An array of hours, a boolean, or a function to generate hours.
103
+ * @param {boolean | number[] | Function} minutes An array of minutes, a boolean, or a function to generate minutes.
104
+ * @param {boolean | number[] | Function} seconds An array of seconds, a boolean, or a function to generate seconds.
105
+ * @returns {LocalizedTimePartials} An object containing arrays of localized time partials and a boolean indicating if 12-hour format is used.
106
+ */
78
107
  export const getLocalizedTimePartials = (
79
108
  locale: string,
80
109
  ampm: 'auto' | boolean = 'auto',
110
+ hours: boolean | number[] | ((hour: number) => number[]) = [],
111
+ minutes: boolean | number[] | ((minute: number) => number[]) = [],
112
+ seconds: boolean | number[] | ((second: number) => number[]) = [],
81
113
  ): LocalizedTimePartials => {
82
- const date = new Date()
83
- const hour12 = ['am', 'AM', 'pm', 'PM'].some((el) => date.toLocaleString(locale).includes(el))
84
- const listOfHours = Array.from(
85
- { length: (ampm === 'auto' && hour12) || ampm === true ? 12 : 24 },
86
- (_, i) => {
87
- return {
88
- value: (ampm === 'auto' && hour12) || ampm === true ? i + 1 : i,
89
- label: ((ampm === 'auto' && hour12) || ampm === true ? i + 1 : i).toLocaleString(locale),
90
- }
91
- },
92
- )
93
- const listOfMinutesSeconds = Array.from({ length: 60 }, (_, i) => {
94
- date.setMinutes(i)
95
- return {
96
- value: i,
97
- label: date
98
- .toLocaleTimeString(locale, {
99
- minute: '2-digit',
100
- second: '2-digit',
101
- })
102
- .split(/[^A-Za-z0-9\u06F0-\u06F90-9]/)[0],
103
- }
104
- })
114
+ const hour12 = (ampm === 'auto' && isAmPm(locale)) || ampm === true
115
+
116
+ const listOfHours =
117
+ Array.isArray(hours) && hours.length > 0
118
+ ? hours
119
+ : (typeof hours === 'function'
120
+ ? Array.from({ length: hour12 ? 12 : 24 }, (_, i) => (hour12 ? i + 1 : i)).filter((hour) =>
121
+ hours(hour),
122
+ )
123
+ : Array.from({ length: hour12 ? 12 : 24 }, (_, i) => (hour12 ? i + 1 : i)))
124
+
125
+ const listOfMinutes =
126
+ Array.isArray(minutes) && minutes.length > 0
127
+ ? minutes
128
+ : (typeof minutes === 'function'
129
+ ? Array.from({ length: 60 }, (_, i) => i).filter((minute) => minutes(minute))
130
+ : Array.from({ length: 60 }, (_, i) => i))
131
+
132
+ const listOfSeconds =
133
+ Array.isArray(seconds) && seconds.length > 0
134
+ ? seconds
135
+ : (typeof seconds === 'function'
136
+ ? Array.from({ length: 60 }, (_, i) => i).filter((second) => seconds(second))
137
+ : Array.from({ length: 60 }, (_, i) => i))
105
138
 
106
139
  return {
107
- listOfHours,
108
- listOfMinutes: listOfMinutesSeconds,
109
- listOfSeconds: listOfMinutesSeconds,
140
+ listOfHours: formatTimePartials(listOfHours, locale, 'hour'),
141
+ listOfMinutes: formatTimePartials(listOfMinutes, locale, 'minute'),
142
+ listOfSeconds: formatTimePartials(listOfSeconds, locale, 'second'),
110
143
  hour12,
111
144
  }
112
145
  }
113
146
 
147
+ /**
148
+ * Gets the selected hour from a date object in either 12-hour or 24-hour format based on locale and preference.
149
+ * @param {Date | null} date The date object from which to extract the hour. If null, the function returns an empty string.
150
+ * @param {string} locale The locale to use when determining whether to return in 12-hour or 24-hour format.
151
+ * @param {'auto' | boolean} ampm Determines the format of the hour returned. 'auto' decides based on locale, true forces 12-hour format, and false forces 24-hour format.
152
+ * @returns {string | number} The hour in the specified format or an empty string if the date is null.
153
+ */
114
154
  export const getSelectedHour = (
115
155
  date: Date | null,
116
156
  locale: string,
117
157
  ampm: 'auto' | boolean = 'auto',
118
158
  ) =>
119
159
  date
120
- ? (ampm === 'auto' && isAmPm(locale)) || ampm === true
160
+ ? ((ampm === 'auto' && isAmPm(locale)) || ampm === true
121
161
  ? convert24hTo12h(date.getHours())
122
- : date.getHours()
162
+ : date.getHours())
123
163
  : ''
124
164
 
165
+ /**
166
+ * Gets the selected minutes from a date object.
167
+ * @param {Date | null} date The date object from which to extract the minutes. If null, the function returns an empty string.
168
+ * @returns {string | number} The minutes from the date or an empty string if the date is null.
169
+ */
125
170
  export const getSelectedMinutes = (date: Date | null) => (date ? date.getMinutes() : '')
126
171
 
172
+ /**
173
+ * Gets the selected seconds from a date object.
174
+ * @param {Date | null} date The date object from which to extract the seconds. If null, the function returns an empty string.
175
+ * @returns {string | number} The seconds from the date or an empty string if the date is null.
176
+ */
127
177
  export const getSelectedSeconds = (date: Date | null) => (date ? date.getSeconds() : '')
128
178
 
179
+ /**
180
+ * Determines if the given locale uses AM/PM format.
181
+ * @param {string} locale The locale to check.
182
+ * @returns {boolean} True if the locale uses AM/PM format, otherwise false.
183
+ */
129
184
  export const isAmPm = (locale: string) =>
130
185
  ['am', 'AM', 'pm', 'PM'].some((el) => new Date().toLocaleString(locale).includes(el))
131
186
 
187
+ /**
188
+ * Validates if the given string represents a valid time.
189
+ * @param {string} time The time string to validate.
190
+ * @returns {boolean} True if the string is a valid time, otherwise false.
191
+ */
132
192
  export const isValidTime = (time: string) => {
133
193
  const d = new Date(`1970-01-01 ${time}`)
134
194
  return d instanceof Date && d.getTime()
@@ -1,4 +1,4 @@
1
- import { useRef, useCallback, } from 'react'
1
+ import { useRef, useCallback } from 'react'
2
2
 
3
3
  export const useDebouncedCallback = <F extends Function>(callback: F, delay: number) => {
4
4
  const timeout = useRef<ReturnType<typeof setTimeout>>()