@coreui/react-pro 4.13.2 → 4.14.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coreui/react-pro",
3
- "version": "4.13.2",
3
+ "version": "4.14.1",
4
4
  "description": "UI Components Library for React.js",
5
5
  "keywords": [
6
6
  "react",
@@ -11,6 +11,7 @@ import { CFormControlWrapper, CFormControlWrapperProps } from '../form/CFormCont
11
11
  import { CPicker, CPickerProps } from '../picker/CPicker'
12
12
  import { CTimePicker } from '../time-picker/CTimePicker'
13
13
 
14
+ import { useDebouncedCallback } from '../../hooks'
14
15
  import { Colors } from '../../types'
15
16
  import { getLocalDateFromString } from './utils'
16
17
 
@@ -101,6 +102,24 @@ export interface CDateRangePickerProps
101
102
  * Toggle visibility or set the content of the input indicator.
102
103
  */
103
104
  indicator?: ReactNode | boolean
105
+ /**
106
+ * Custom function to format the selected date into a string according to a custom format.
107
+ *
108
+ * @since v4.14.0
109
+ */
110
+ inputDateFormat?: (date: Date) => string
111
+ /**
112
+ * Custom function to parse the input value into a valid Date object.
113
+ *
114
+ * @since v4.14.0
115
+ */
116
+ inputDateParse?: (date: string | Date) => Date
117
+ /**
118
+ * Defines the delay (in milliseconds) for the input field's onChange event.
119
+ *
120
+ * @since v4.14.0
121
+ */
122
+ inputOnChangeDelay?: number
104
123
  /**
105
124
  * Toggle the readonly state for the component.
106
125
  */
@@ -190,7 +209,6 @@ export const CDateRangePicker = forwardRef<HTMLDivElement | HTMLLIElement, CDate
190
209
  confirmButtonColor = 'primary',
191
210
  confirmButtonSize = 'sm',
192
211
  confirmButtonVariant,
193
- indicator = true,
194
212
  dayFormat,
195
213
  disabled,
196
214
  disabledDates,
@@ -202,6 +220,10 @@ export const CDateRangePicker = forwardRef<HTMLDivElement | HTMLLIElement, CDate
202
220
  format,
203
221
  footer,
204
222
  id,
223
+ indicator = true,
224
+ inputDateFormat,
225
+ inputDateParse,
226
+ inputOnChangeDelay = 750,
205
227
  inputReadOnly,
206
228
  invalid,
207
229
  label,
@@ -270,17 +292,13 @@ export const CDateRangePicker = forwardRef<HTMLDivElement | HTMLLIElement, CDate
270
292
  }, [valid, invalid])
271
293
 
272
294
  useEffect(() => {
273
- if (startDate) {
274
- setStartDate(new Date(startDate))
275
- setCalendarDate(new Date(startDate))
276
- }
295
+ setStartDate(startDate ? new Date(startDate) : null)
296
+ setCalendarDate(startDate ? new Date(startDate) : new Date())
277
297
  }, [startDate])
278
298
 
279
299
  useEffect(() => {
280
- if (endDate) {
281
- setEndDate(new Date(endDate))
282
- setCalendarDate(new Date(endDate))
283
- }
300
+ setEndDate(endDate ? new Date(endDate) : null)
301
+ setCalendarDate(endDate ? new Date(endDate) : new Date())
284
302
  }, [endDate])
285
303
 
286
304
  useEffect(() => {
@@ -326,7 +344,9 @@ export const CDateRangePicker = forwardRef<HTMLDivElement | HTMLLIElement, CDate
326
344
  }, [formRef, _startDate, _endDate])
327
345
 
328
346
  const formatDate = (date: Date) => {
329
- return format
347
+ return inputDateFormat
348
+ ? inputDateFormat(date)
349
+ : format
330
350
  ? dateFormat(date, format)
331
351
  : timepicker
332
352
  ? date.toLocaleString(locale)
@@ -402,6 +422,16 @@ export const CDateRangePicker = forwardRef<HTMLDivElement | HTMLLIElement, CDate
402
422
  }
403
423
  }
404
424
 
425
+ const handleOnChange = useDebouncedCallback((value: string, input: string) => {
426
+ const date = inputDateParse
427
+ ? inputDateParse(value)
428
+ : getLocalDateFromString(value, locale, timepicker)
429
+ if (date instanceof Date && date.getTime()) {
430
+ setCalendarDate(date)
431
+ input === 'start' ? setStartDate(date) : setEndDate(date)
432
+ }
433
+ }, inputOnChangeDelay)
434
+
405
435
  const InputGroup = () => (
406
436
  <div
407
437
  className={classNames('input-group', 'picker-input-group', {
@@ -419,11 +449,7 @@ export const CDateRangePicker = forwardRef<HTMLDivElement | HTMLLIElement, CDate
419
449
  readOnly={inputReadOnly || typeof format === 'string'}
420
450
  required={required}
421
451
  onChange={(event) => {
422
- const date = getLocalDateFromString(event.target.value, locale, timepicker)
423
- if (date instanceof Date && date.getTime()) {
424
- setCalendarDate(date)
425
- setStartDate(date)
426
- }
452
+ handleOnChange(event.target.value, 'start')
427
453
  }}
428
454
  onClick={() => setSelectEndDate(false)}
429
455
  ref={inputStartRef}
@@ -439,18 +465,13 @@ export const CDateRangePicker = forwardRef<HTMLDivElement | HTMLLIElement, CDate
439
465
  className={classNames('form-control date-picker-input', {
440
466
  hover: inputEndHoverValue,
441
467
  })}
442
-
443
468
  disabled={disabled}
444
469
  {...(id && { name: `${id}-end-date` })}
445
470
  placeholder={placeholder[1]}
446
471
  readOnly={inputReadOnly || typeof format === 'string'}
447
472
  required={required}
448
473
  onChange={(event) => {
449
- const date = getLocalDateFromString(event.target.value, locale, timepicker)
450
- if (date instanceof Date && date.getTime()) {
451
- setCalendarDate(date)
452
- setEndDate(date)
453
- }
474
+ handleOnChange(event.target.value, 'end')
454
475
  }}
455
476
  onClick={() => setSelectEndDate(true)}
456
477
  ref={inputEndRef}
@@ -692,6 +713,9 @@ CDateRangePicker.propTypes = {
692
713
  confirmButtonVariant: CButton.propTypes?.variant,
693
714
  id: PropTypes.string,
694
715
  indicator: PropTypes.oneOfType([PropTypes.bool, PropTypes.node]),
716
+ inputDateFormat: PropTypes.func,
717
+ inputDateParse: PropTypes.func,
718
+ inputOnChangeDelay: PropTypes.number,
695
719
  inputReadOnly: PropTypes.bool,
696
720
  placeholder: PropTypes.oneOfType([
697
721
  PropTypes.string,
@@ -467,7 +467,7 @@ export const CSmartTable = forwardRef<HTMLDivElement, CSmartTableProps>(
467
467
  {(pagination || itemsPerPageSelect) && (
468
468
  <div className="row">
469
469
  <div className="col">
470
- {((pagination && numberOfPages > 1) || paginationProps) && (
470
+ {((pagination && numberOfPages > 1) || (paginationProps && paginationProps.pages > 1)) && (
471
471
  <CSmartPagination
472
472
  activePage={_activePage}
473
473
  onActivePageChange={(page) => {
@@ -183,7 +183,7 @@ export const CTimePicker = forwardRef<HTMLDivElement | HTMLLIElement, CTimePicke
183
183
  })
184
184
 
185
185
  useEffect(() => {
186
- setDate(convertTimeToDate(time))
186
+ setDate(time ? convertTimeToDate(time) : null)
187
187
  }, [time])
188
188
 
189
189
  useEffect(() => {
@@ -390,38 +390,6 @@ export const CTimePicker = forwardRef<HTMLDivElement | HTMLLIElement, CTimePicke
390
390
  )
391
391
  }
392
392
 
393
- const TimePickerRoll = () => (
394
- <>
395
- <CTimePickerRollCol
396
- elements={localizedTimePartials && localizedTimePartials.listOfHours}
397
- onClick={(index: number) => handleTimeChange('hours', index.toString())}
398
- selected={getSelectedHour(date, locale, ampm)}
399
- />
400
- <CTimePickerRollCol
401
- elements={localizedTimePartials && localizedTimePartials.listOfMinutes}
402
- onClick={(index: number) => handleTimeChange('minutes', index.toString())}
403
- selected={getSelectedMinutes(date)}
404
- />
405
- {seconds && (
406
- <CTimePickerRollCol
407
- elements={localizedTimePartials && localizedTimePartials.listOfSeconds}
408
- onClick={(index: number) => handleTimeChange('seconds', index.toString())}
409
- selected={getSelectedSeconds(date)}
410
- />
411
- )}
412
- {localizedTimePartials && localizedTimePartials.hour12 && (
413
- <CTimePickerRollCol
414
- elements={[
415
- { value: 'am', label: 'AM' },
416
- { value: 'pm', label: 'PM' },
417
- ]}
418
- onClick={(value: string) => handleTimeChange('toggle', value)}
419
- selected={_ampm}
420
- />
421
- )}
422
- </>
423
- )
424
-
425
393
  return (
426
394
  <CFormControlWrapper
427
395
  describedby={rest['aria-describedby']}
@@ -499,7 +467,39 @@ export const CTimePicker = forwardRef<HTMLDivElement | HTMLLIElement, CTimePicke
499
467
  ['time-picker-roll']: variant === 'roll',
500
468
  })}
501
469
  >
502
- {variant === 'select' ? <TimePickerSelect /> : TimePickerRoll()}
470
+ {variant === 'select' ? (
471
+ <TimePickerSelect />
472
+ ) : (
473
+ <>
474
+ <CTimePickerRollCol
475
+ elements={localizedTimePartials && localizedTimePartials.listOfHours}
476
+ onClick={(index: number) => handleTimeChange('hours', index.toString())}
477
+ selected={getSelectedHour(date, locale, ampm)}
478
+ />
479
+ <CTimePickerRollCol
480
+ elements={localizedTimePartials && localizedTimePartials.listOfMinutes}
481
+ onClick={(index: number) => handleTimeChange('minutes', index.toString())}
482
+ selected={getSelectedMinutes(date)}
483
+ />
484
+ {seconds && (
485
+ <CTimePickerRollCol
486
+ elements={localizedTimePartials && localizedTimePartials.listOfSeconds}
487
+ onClick={(index: number) => handleTimeChange('seconds', index.toString())}
488
+ selected={getSelectedSeconds(date)}
489
+ />
490
+ )}
491
+ {localizedTimePartials && localizedTimePartials.hour12 && (
492
+ <CTimePickerRollCol
493
+ elements={[
494
+ { value: 'am', label: 'AM' },
495
+ { value: 'pm', label: 'PM' },
496
+ ]}
497
+ onClick={(value: string) => handleTimeChange('toggle', value)}
498
+ selected={_ampm}
499
+ />
500
+ )}
501
+ </>
502
+ )}
503
503
  </div>
504
504
  </CPicker>
505
505
  </CFormControlWrapper>
@@ -2,7 +2,7 @@ import React, { forwardRef, useEffect, useRef } from 'react'
2
2
  import PropTypes from 'prop-types'
3
3
  import classNames from 'classnames'
4
4
 
5
- import { useForkedRef } from '../../hooks'
5
+ import { useForkedRef, useIsVisible } from '../../hooks'
6
6
 
7
7
  export interface Element {
8
8
  value: number | string
@@ -20,18 +20,21 @@ export const CTimePickerRollCol = forwardRef<HTMLDivElement, CTimePickerRollColP
20
20
  const init = useRef(true)
21
21
  const colRef = useRef<HTMLDivElement>(null)
22
22
  const forkedRef = useForkedRef(ref, colRef)
23
+ const isVisible = useIsVisible(colRef)
23
24
 
24
25
  useEffect(() => {
25
26
  const nodeEl = colRef.current?.querySelector('.selected')
26
- if (nodeEl && nodeEl instanceof HTMLElement) {
27
+ if (isVisible && nodeEl && nodeEl instanceof HTMLElement) {
27
28
  colRef.current?.scrollTo({
28
29
  top: nodeEl.offsetTop,
29
30
  behavior: init.current ? 'auto' : 'smooth',
30
31
  })
31
32
  }
32
33
 
33
- init.current = false
34
- }, [selected])
34
+ if (isVisible) {
35
+ init.current = false
36
+ }
37
+ }, [isVisible, selected])
35
38
 
36
39
  const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>, value: number | string) => {
37
40
  if (event.code === 'Space' || event.key === 'Enter') {
@@ -1,5 +1,7 @@
1
+ import { useDebouncedCallback } from './useDebouncedCallback'
1
2
  import { useForkedRef } from './useForkedRef'
3
+ import { useIsVisible } from './useIsVisible'
2
4
  import { usePopper } from './usePopper'
3
5
  import { useStateWithCallback } from './useStateWithCallback'
4
6
 
5
- export { useForkedRef, usePopper, useStateWithCallback }
7
+ export { useDebouncedCallback, useForkedRef, useIsVisible, usePopper, useStateWithCallback }
@@ -0,0 +1,18 @@
1
+ import { useRef, useCallback, } from 'react'
2
+
3
+ export const useDebouncedCallback = <F extends Function>(callback: F, delay: number) => {
4
+ const timeout = useRef<ReturnType<typeof setTimeout>>()
5
+
6
+ return useCallback(
7
+ (...args: any[]) => {
8
+ const handler = () => {
9
+ clearTimeout(timeout.current)
10
+ callback(...args)
11
+ }
12
+
13
+ clearTimeout(timeout.current)
14
+ timeout.current = setTimeout(handler, delay)
15
+ },
16
+ [callback, delay],
17
+ )
18
+ }
@@ -0,0 +1,14 @@
1
+ import { RefObject, useEffect, useState } from 'react'
2
+
3
+ export const useIsVisible = (ref: RefObject<HTMLElement>) => {
4
+ const [isIntersecting, setIntersecting] = useState(false)
5
+
6
+ useEffect(() => {
7
+ const observer = new IntersectionObserver(([entry]) => setIntersecting(entry.isIntersecting))
8
+
9
+ ref.current && observer.observe(ref.current)
10
+ return () => observer.disconnect()
11
+ }, [])
12
+
13
+ return isIntersecting
14
+ }