@coreui/react-pro 5.18.0 → 5.20.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.
- package/README.md +1 -1
- package/dist/cjs/components/autocomplete/CAutocomplete.d.ts +16 -2
- package/dist/cjs/components/autocomplete/CAutocomplete.js +99 -86
- package/dist/cjs/components/autocomplete/CAutocomplete.js.map +1 -1
- package/dist/cjs/components/calendar/utils.d.ts +0 -11
- package/dist/cjs/components/calendar/utils.js.map +1 -1
- package/dist/cjs/components/date-range-picker/CDateRangePicker.d.ts +6 -0
- package/dist/cjs/components/date-range-picker/CDateRangePicker.js +17 -14
- package/dist/cjs/components/date-range-picker/CDateRangePicker.js.map +1 -1
- package/dist/cjs/components/multi-select/CMultiSelect.d.ts +6 -0
- package/dist/cjs/components/multi-select/CMultiSelect.js +13 -12
- package/dist/cjs/components/multi-select/CMultiSelect.js.map +1 -1
- package/dist/cjs/components/multi-select/CMultiSelectOptions.js +6 -5
- package/dist/cjs/components/multi-select/CMultiSelectOptions.js.map +1 -1
- package/dist/cjs/components/multi-select/CMultiSelectSelection.js +1 -1
- package/dist/cjs/components/multi-select/CMultiSelectSelection.js.map +1 -1
- package/dist/cjs/components/multi-select/utils.d.ts +0 -2
- package/dist/cjs/components/multi-select/utils.js +0 -26
- package/dist/cjs/components/multi-select/utils.js.map +1 -1
- package/dist/cjs/utils/getNextSibling.js +3 -0
- package/dist/cjs/utils/getNextSibling.js.map +1 -1
- package/dist/cjs/utils/getPreviousSibling.js +3 -0
- package/dist/cjs/utils/getPreviousSibling.js.map +1 -1
- package/dist/esm/components/autocomplete/CAutocomplete.d.ts +16 -2
- package/dist/esm/components/autocomplete/CAutocomplete.js +99 -86
- package/dist/esm/components/autocomplete/CAutocomplete.js.map +1 -1
- package/dist/esm/components/calendar/utils.d.ts +0 -11
- package/dist/esm/components/calendar/utils.js.map +1 -1
- package/dist/esm/components/date-range-picker/CDateRangePicker.d.ts +6 -0
- package/dist/esm/components/date-range-picker/CDateRangePicker.js +17 -14
- package/dist/esm/components/date-range-picker/CDateRangePicker.js.map +1 -1
- package/dist/esm/components/multi-select/CMultiSelect.d.ts +6 -0
- package/dist/esm/components/multi-select/CMultiSelect.js +14 -13
- package/dist/esm/components/multi-select/CMultiSelect.js.map +1 -1
- package/dist/esm/components/multi-select/CMultiSelectOptions.js +4 -3
- package/dist/esm/components/multi-select/CMultiSelectOptions.js.map +1 -1
- package/dist/esm/components/multi-select/CMultiSelectSelection.js +1 -1
- package/dist/esm/components/multi-select/CMultiSelectSelection.js.map +1 -1
- package/dist/esm/components/multi-select/utils.d.ts +0 -2
- package/dist/esm/components/multi-select/utils.js +1 -25
- package/dist/esm/components/multi-select/utils.js.map +1 -1
- package/dist/esm/utils/getNextSibling.js +3 -0
- package/dist/esm/utils/getNextSibling.js.map +1 -1
- package/dist/esm/utils/getPreviousSibling.js +3 -0
- package/dist/esm/utils/getPreviousSibling.js.map +1 -1
- package/package.json +7 -7
- package/src/components/autocomplete/CAutocomplete.tsx +190 -146
- package/src/components/calendar/utils.ts +0 -26
- package/src/components/date-range-picker/CDateRangePicker.tsx +12 -0
- package/src/components/multi-select/CMultiSelect.tsx +23 -4
- package/src/components/multi-select/CMultiSelectOptions.tsx +8 -5
- package/src/components/multi-select/CMultiSelectSelection.tsx +3 -3
- package/src/components/multi-select/utils.ts +0 -34
|
@@ -16,8 +16,8 @@ import PropTypes from 'prop-types'
|
|
|
16
16
|
|
|
17
17
|
import type { Placement } from '@popperjs/core'
|
|
18
18
|
|
|
19
|
+
import { CConditionalPortal } from '../conditional-portal'
|
|
19
20
|
import { CFormControlWrapper, CFormControlWrapperProps } from '../form/CFormControlWrapper'
|
|
20
|
-
|
|
21
21
|
import { CAutocompleteOptions } from './CAutocompleteOptions'
|
|
22
22
|
|
|
23
23
|
import { useForkedRef, usePopper } from '../../hooks'
|
|
@@ -73,6 +73,14 @@ export interface CAutocompleteProps
|
|
|
73
73
|
*/
|
|
74
74
|
clearSearchOnSelect?: boolean
|
|
75
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Specifies the DOM element where the dropdown should be rendered when using portal mode.
|
|
78
|
+
* Can be a DOM element reference, DocumentFragment, function that returns an element or null,
|
|
79
|
+
* or null (renders to document body). Works in conjunction with the `portal` prop to control
|
|
80
|
+
* where the portal content is mounted in the DOM tree.
|
|
81
|
+
*/
|
|
82
|
+
container?: DocumentFragment | Element | (() => DocumentFragment | Element | null) | null
|
|
83
|
+
|
|
76
84
|
/**
|
|
77
85
|
* Toggle the disabled state for the component.
|
|
78
86
|
* When `true`, the React.js autocomplete is non-interactive and appears visually disabled.
|
|
@@ -194,6 +202,16 @@ export interface CAutocompleteProps
|
|
|
194
202
|
*/
|
|
195
203
|
placeholder?: string
|
|
196
204
|
|
|
205
|
+
/**
|
|
206
|
+
* Renders the autocomplete dropdown in a React portal, allowing it to break out of its
|
|
207
|
+
* parent container's styling constraints (like overflow, z-index, or positioning).
|
|
208
|
+
* When enabled, the dropdown is rendered at the document root level, ensuring it appears
|
|
209
|
+
* above other page elements and isn't clipped by parent containers.
|
|
210
|
+
*
|
|
211
|
+
* @default false
|
|
212
|
+
*/
|
|
213
|
+
portal?: boolean
|
|
214
|
+
|
|
197
215
|
/**
|
|
198
216
|
* Toggle the readonly state for the component.
|
|
199
217
|
* When `true`, users can view and interact with the dropdown but cannot
|
|
@@ -275,8 +293,6 @@ export interface CAutocompleteProps
|
|
|
275
293
|
* Toggle the visibility of autocomplete dropdown.
|
|
276
294
|
* Controls whether the dropdown is initially visible.
|
|
277
295
|
* The dropdown visibility can still be toggled through user interaction.
|
|
278
|
-
*
|
|
279
|
-
* @default false
|
|
280
296
|
*/
|
|
281
297
|
visible?: boolean
|
|
282
298
|
|
|
@@ -297,6 +313,7 @@ export const CAutocomplete = forwardRef<HTMLDivElement, CAutocompleteProps>(
|
|
|
297
313
|
className,
|
|
298
314
|
cleaner = false,
|
|
299
315
|
clearSearchOnSelect = true,
|
|
316
|
+
container,
|
|
300
317
|
disabled,
|
|
301
318
|
feedback,
|
|
302
319
|
feedbackInvalid,
|
|
@@ -317,6 +334,7 @@ export const CAutocomplete = forwardRef<HTMLDivElement, CAutocompleteProps>(
|
|
|
317
334
|
optionsTemplate,
|
|
318
335
|
optionsGroupsTemplate,
|
|
319
336
|
placeholder,
|
|
337
|
+
portal = false,
|
|
320
338
|
readOnly,
|
|
321
339
|
required,
|
|
322
340
|
resetSelectionOnOptionsChange = false,
|
|
@@ -329,7 +347,7 @@ export const CAutocomplete = forwardRef<HTMLDivElement, CAutocompleteProps>(
|
|
|
329
347
|
valid,
|
|
330
348
|
value,
|
|
331
349
|
virtualScroller,
|
|
332
|
-
visible
|
|
350
|
+
visible,
|
|
333
351
|
visibleItems = 10,
|
|
334
352
|
...rest
|
|
335
353
|
},
|
|
@@ -380,9 +398,10 @@ export const CAutocomplete = forwardRef<HTMLDivElement, CAutocompleteProps>(
|
|
|
380
398
|
useEffect(() => {
|
|
381
399
|
if (resetSelectionOnOptionsChange) {
|
|
382
400
|
handleClear()
|
|
383
|
-
return
|
|
384
401
|
}
|
|
402
|
+
}, [options])
|
|
385
403
|
|
|
404
|
+
useEffect(() => {
|
|
386
405
|
if (value && typeof value === 'string') {
|
|
387
406
|
handleSelect(value)
|
|
388
407
|
return
|
|
@@ -407,48 +426,62 @@ export const CAutocomplete = forwardRef<HTMLDivElement, CAutocompleteProps>(
|
|
|
407
426
|
}, [options, value])
|
|
408
427
|
|
|
409
428
|
useEffect(() => {
|
|
410
|
-
if (
|
|
411
|
-
|
|
412
|
-
? `${searchValue}${getOptionLabel(hint).slice(searchValue.length)}`
|
|
413
|
-
: ''
|
|
429
|
+
if (!showHints) {
|
|
430
|
+
return
|
|
414
431
|
}
|
|
415
|
-
|
|
432
|
+
|
|
433
|
+
const findOption =
|
|
434
|
+
searchValue.length > 0
|
|
435
|
+
? filteredOptions.find((option) =>
|
|
436
|
+
getOptionLabel(option).toLowerCase().startsWith(searchValue.toLowerCase())
|
|
437
|
+
)
|
|
438
|
+
: undefined
|
|
439
|
+
|
|
440
|
+
setHint(findOption)
|
|
441
|
+
}, [filteredOptions, searchValue, showHints])
|
|
416
442
|
|
|
417
443
|
useEffect(() => {
|
|
418
|
-
if (
|
|
419
|
-
|
|
420
|
-
|
|
444
|
+
if (
|
|
445
|
+
!searchNoResultsLabel &&
|
|
446
|
+
searchValue.length > 0 &&
|
|
447
|
+
filteredOptions.length === 0 &&
|
|
448
|
+
_visible
|
|
449
|
+
) {
|
|
421
450
|
handleDropdownHide()
|
|
451
|
+
return
|
|
422
452
|
}
|
|
423
|
-
}, [visible])
|
|
424
453
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
handleDropdownHide()
|
|
454
|
+
if (searchValue.length > 0 && filteredOptions.length > 0 && !_visible) {
|
|
455
|
+
handleDropdownShow()
|
|
428
456
|
}
|
|
457
|
+
}, [filteredOptions])
|
|
429
458
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
) {
|
|
459
|
+
useEffect(() => {
|
|
460
|
+
if (visible === true) {
|
|
461
|
+
handleDropdownShow()
|
|
462
|
+
} else if (visible === false) {
|
|
434
463
|
handleDropdownHide()
|
|
435
464
|
}
|
|
436
|
-
}, [])
|
|
465
|
+
}, [visible])
|
|
437
466
|
|
|
438
|
-
const
|
|
439
|
-
if (
|
|
440
|
-
|
|
441
|
-
autoCompleteRef.current.contains(event.target as HTMLElement)
|
|
442
|
-
) {
|
|
443
|
-
return
|
|
467
|
+
const handleClear = () => {
|
|
468
|
+
if (inputRef.current) {
|
|
469
|
+
inputRef.current.value = ''
|
|
444
470
|
}
|
|
445
471
|
|
|
446
|
-
|
|
447
|
-
|
|
472
|
+
setSearchValue('')
|
|
473
|
+
setSelected(null)
|
|
474
|
+
onChange?.(null)
|
|
475
|
+
}
|
|
448
476
|
|
|
449
|
-
const
|
|
450
|
-
|
|
451
|
-
|
|
477
|
+
const handleGlobalSearch = (event: React.KeyboardEvent<HTMLDivElement>) => {
|
|
478
|
+
if (
|
|
479
|
+
isGlobalSearch(search) &&
|
|
480
|
+
inputRef.current &&
|
|
481
|
+
(event.key.length === 1 || event.key === 'Backspace' || event.key === 'Delete')
|
|
482
|
+
) {
|
|
483
|
+
inputRef.current.focus()
|
|
484
|
+
}
|
|
452
485
|
}
|
|
453
486
|
|
|
454
487
|
const handleInputChange = (event: FormEvent<HTMLInputElement>) => {
|
|
@@ -459,29 +492,6 @@ export const CAutocomplete = forwardRef<HTMLDivElement, CAutocompleteProps>(
|
|
|
459
492
|
onChange?.(null)
|
|
460
493
|
setSelected(null)
|
|
461
494
|
}
|
|
462
|
-
|
|
463
|
-
const filteredOptionsInternal = filterOptions(options, value)
|
|
464
|
-
|
|
465
|
-
if (!searchNoResultsLabel && value !== '' && filteredOptionsInternal.length === 0) {
|
|
466
|
-
if (_visible) {
|
|
467
|
-
handleDropdownHide()
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
return
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
handleDropdownShow(filteredOptionsInternal)
|
|
474
|
-
|
|
475
|
-
if (showHints) {
|
|
476
|
-
const findOption =
|
|
477
|
-
value.length > 0
|
|
478
|
-
? filteredOptionsInternal.find((option) =>
|
|
479
|
-
getOptionLabel(option).toLowerCase().startsWith(value.toLowerCase())
|
|
480
|
-
)
|
|
481
|
-
: undefined
|
|
482
|
-
|
|
483
|
-
setHint(findOption)
|
|
484
|
-
}
|
|
485
495
|
}
|
|
486
496
|
|
|
487
497
|
const handleInputKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
|
@@ -512,6 +522,7 @@ export const CAutocomplete = forwardRef<HTMLDivElement, CAutocompleteProps>(
|
|
|
512
522
|
if (showHints && hint && event.key === 'Tab') {
|
|
513
523
|
event.preventDefault()
|
|
514
524
|
handleSelect(hint)
|
|
525
|
+
handleDropdownHide()
|
|
515
526
|
return
|
|
516
527
|
}
|
|
517
528
|
|
|
@@ -540,80 +551,38 @@ export const CAutocomplete = forwardRef<HTMLDivElement, CAutocompleteProps>(
|
|
|
540
551
|
}
|
|
541
552
|
}
|
|
542
553
|
|
|
543
|
-
const
|
|
544
|
-
if (
|
|
545
|
-
|
|
554
|
+
const handleKeyUp = useCallback((event: KeyboardEvent) => {
|
|
555
|
+
if (event.key === 'Escape') {
|
|
556
|
+
handleDropdownHide()
|
|
546
557
|
}
|
|
547
558
|
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
if (disabled || readOnly || _visible) {
|
|
556
|
-
return
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
if (filtered.length === 0 && searchNoResultsLabel === false) {
|
|
560
|
-
return
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
if (filtered.length === 0 && allowOnlyDefinedOptions === false) {
|
|
564
|
-
return
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
setVisible(true)
|
|
568
|
-
onShow?.()
|
|
569
|
-
|
|
570
|
-
window.addEventListener('mouseup', handleMouseUp)
|
|
571
|
-
window.addEventListener('keyup', handleKeyUp)
|
|
572
|
-
|
|
573
|
-
if (togglerRef.current && dropdownRef.current) {
|
|
574
|
-
initPopper(togglerRef.current, dropdownRef.current, popperConfig)
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
inputRef.current?.focus()
|
|
578
|
-
},
|
|
579
|
-
[
|
|
580
|
-
disabled,
|
|
581
|
-
readOnly,
|
|
582
|
-
_visible,
|
|
583
|
-
filteredOptions.length,
|
|
584
|
-
searchNoResultsLabel,
|
|
585
|
-
allowOnlyDefinedOptions,
|
|
586
|
-
onShow,
|
|
587
|
-
handleMouseUp,
|
|
588
|
-
handleKeyUp,
|
|
589
|
-
initPopper,
|
|
590
|
-
popperConfig,
|
|
591
|
-
]
|
|
592
|
-
)
|
|
593
|
-
|
|
594
|
-
const handleDropdownHide = useCallback(() => {
|
|
595
|
-
setVisible(false)
|
|
596
|
-
onHide?.()
|
|
597
|
-
|
|
598
|
-
window.removeEventListener('mouseup', handleMouseUp)
|
|
599
|
-
window.removeEventListener('keyup', handleKeyUp)
|
|
600
|
-
|
|
601
|
-
destroyPopper()
|
|
602
|
-
inputRef.current?.focus()
|
|
603
|
-
}, [onHide, handleMouseUp, handleKeyUp, destroyPopper])
|
|
559
|
+
if (
|
|
560
|
+
autoCompleteRef.current &&
|
|
561
|
+
!autoCompleteRef.current.contains(event.target as HTMLElement)
|
|
562
|
+
) {
|
|
563
|
+
handleDropdownHide()
|
|
564
|
+
}
|
|
565
|
+
}, [])
|
|
604
566
|
|
|
605
|
-
const
|
|
567
|
+
const handleMouseUp = useCallback((event: Event) => {
|
|
606
568
|
if (
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
(event.key.length === 1 || event.key === 'Backspace' || event.key === 'Delete')
|
|
569
|
+
autoCompleteRef.current &&
|
|
570
|
+
autoCompleteRef.current.contains(event.target as HTMLElement)
|
|
610
571
|
) {
|
|
611
|
-
|
|
572
|
+
return
|
|
612
573
|
}
|
|
613
|
-
|
|
574
|
+
|
|
575
|
+
handleDropdownHide()
|
|
576
|
+
}, [])
|
|
614
577
|
|
|
615
578
|
const handleOptionClick = (option: Option) => {
|
|
616
579
|
handleSelect(option)
|
|
580
|
+
handleDropdownHide()
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
const handleSearch = (search: string) => {
|
|
584
|
+
onInput?.(search)
|
|
585
|
+
setSearchValue(search)
|
|
617
586
|
}
|
|
618
587
|
|
|
619
588
|
const handleSelect = (option?: Option | undefined) => {
|
|
@@ -627,13 +596,73 @@ export const CAutocomplete = forwardRef<HTMLDivElement, CAutocompleteProps>(
|
|
|
627
596
|
|
|
628
597
|
if (clearSearchOnSelect) {
|
|
629
598
|
handleSearch('')
|
|
599
|
+
} else {
|
|
600
|
+
setHint('')
|
|
630
601
|
}
|
|
631
602
|
|
|
632
603
|
setSelected(option ?? null)
|
|
633
604
|
onChange?.(option ?? null)
|
|
634
|
-
handleDropdownHide()
|
|
635
605
|
}
|
|
636
606
|
|
|
607
|
+
const handleDropdownShow = useCallback(() => {
|
|
608
|
+
if (disabled || readOnly || _visible) {
|
|
609
|
+
return
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
if (
|
|
613
|
+
!isExternalSearch(search) &&
|
|
614
|
+
filteredOptions.length === 0 &&
|
|
615
|
+
searchNoResultsLabel === false
|
|
616
|
+
) {
|
|
617
|
+
return
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
if (portal && dropdownRef.current && togglerRef.current) {
|
|
621
|
+
dropdownRef.current.style.minWidth = `${(togglerRef.current as HTMLElement).offsetWidth}px`
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
setVisible(true)
|
|
625
|
+
onShow?.()
|
|
626
|
+
|
|
627
|
+
window.addEventListener('mouseup', handleMouseUp)
|
|
628
|
+
window.addEventListener('keyup', handleKeyUp)
|
|
629
|
+
|
|
630
|
+
if (togglerRef.current && dropdownRef.current) {
|
|
631
|
+
setTimeout(() => {
|
|
632
|
+
initPopper(
|
|
633
|
+
togglerRef.current as HTMLDivElement,
|
|
634
|
+
dropdownRef.current as HTMLDivElement,
|
|
635
|
+
popperConfig
|
|
636
|
+
)
|
|
637
|
+
}, 1) // Allow DOM updates to complete before initializing Popper
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
inputRef.current?.focus()
|
|
641
|
+
}, [
|
|
642
|
+
disabled,
|
|
643
|
+
readOnly,
|
|
644
|
+
_visible,
|
|
645
|
+
filteredOptions.length,
|
|
646
|
+
searchNoResultsLabel,
|
|
647
|
+
allowOnlyDefinedOptions,
|
|
648
|
+
onShow,
|
|
649
|
+
handleMouseUp,
|
|
650
|
+
handleKeyUp,
|
|
651
|
+
initPopper,
|
|
652
|
+
popperConfig,
|
|
653
|
+
])
|
|
654
|
+
|
|
655
|
+
const handleDropdownHide = useCallback(() => {
|
|
656
|
+
setVisible(false)
|
|
657
|
+
onHide?.()
|
|
658
|
+
|
|
659
|
+
window.removeEventListener('mouseup', handleMouseUp)
|
|
660
|
+
window.removeEventListener('keyup', handleKeyUp)
|
|
661
|
+
|
|
662
|
+
destroyPopper()
|
|
663
|
+
inputRef.current?.focus()
|
|
664
|
+
}, [onHide, handleMouseUp, handleKeyUp, destroyPopper])
|
|
665
|
+
|
|
637
666
|
return (
|
|
638
667
|
<CFormControlWrapper
|
|
639
668
|
describedby={rest['aria-describedby']}
|
|
@@ -670,10 +699,14 @@ export const CAutocomplete = forwardRef<HTMLDivElement, CAutocompleteProps>(
|
|
|
670
699
|
{showHints && searchValue !== '' && (
|
|
671
700
|
<input
|
|
672
701
|
className="autocomplete-input autocomplete-input-hint"
|
|
702
|
+
id={id || `autocomplete-hint-${uniqueId}`}
|
|
673
703
|
autoComplete="off"
|
|
674
704
|
readOnly
|
|
675
705
|
tabIndex={-1}
|
|
676
706
|
aria-hidden="true"
|
|
707
|
+
value={
|
|
708
|
+
hint ? `${searchValue}${getOptionLabel(hint).slice(searchValue.length)}` : ''
|
|
709
|
+
}
|
|
677
710
|
ref={inputHintRef}
|
|
678
711
|
/>
|
|
679
712
|
)}
|
|
@@ -698,6 +731,7 @@ export const CAutocomplete = forwardRef<HTMLDivElement, CAutocompleteProps>(
|
|
|
698
731
|
aria-autocomplete="list"
|
|
699
732
|
aria-expanded={_visible}
|
|
700
733
|
aria-haspopup="listbox"
|
|
734
|
+
{...(portal && { 'aria-owns': `autocomplete-listbox-${uniqueId}` })}
|
|
701
735
|
readOnly={readOnly}
|
|
702
736
|
role="combobox"
|
|
703
737
|
ref={inputRef}
|
|
@@ -718,7 +752,10 @@ export const CAutocomplete = forwardRef<HTMLDivElement, CAutocompleteProps>(
|
|
|
718
752
|
<button
|
|
719
753
|
type="button"
|
|
720
754
|
className="autocomplete-indicator"
|
|
721
|
-
disabled={
|
|
755
|
+
disabled={
|
|
756
|
+
!(searchNoResultsLabel || filteredOptions.length > 0) &&
|
|
757
|
+
isExternalSearch(search)
|
|
758
|
+
}
|
|
722
759
|
onClick={(event) => {
|
|
723
760
|
event.preventDefault()
|
|
724
761
|
event.stopPropagation()
|
|
@@ -732,29 +769,34 @@ export const CAutocomplete = forwardRef<HTMLDivElement, CAutocompleteProps>(
|
|
|
732
769
|
</div>
|
|
733
770
|
)}
|
|
734
771
|
</div>
|
|
735
|
-
<
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
772
|
+
<CConditionalPortal container={container} portal={portal}>
|
|
773
|
+
<div
|
|
774
|
+
className={classNames('autocomplete-dropdown', {
|
|
775
|
+
show: portal && _visible,
|
|
776
|
+
})}
|
|
777
|
+
id={`autocomplete-listbox-${uniqueId}`}
|
|
778
|
+
role="listbox"
|
|
779
|
+
aria-labelledby={id || `autocomplete-${uniqueId}`}
|
|
780
|
+
ref={dropdownRef}
|
|
781
|
+
>
|
|
782
|
+
<CAutocompleteOptions
|
|
783
|
+
highlightOptionsOnSearch={highlightOptionsOnSearch}
|
|
784
|
+
loading={loading}
|
|
785
|
+
onOptionClick={(option: Option) =>
|
|
786
|
+
!disabled && !readOnly && handleOptionClick(option)
|
|
787
|
+
}
|
|
788
|
+
options={filteredOptions}
|
|
789
|
+
optionsMaxHeight={optionsMaxHeight}
|
|
790
|
+
optionsTemplate={optionsTemplate}
|
|
791
|
+
optionsGroupsTemplate={optionsGroupsTemplate}
|
|
792
|
+
searchNoResultsLabel={searchNoResultsLabel}
|
|
793
|
+
searchValue={searchValue}
|
|
794
|
+
selected={selected}
|
|
795
|
+
virtualScroller={virtualScroller}
|
|
796
|
+
visibleItems={visibleItems}
|
|
797
|
+
/>
|
|
798
|
+
</div>
|
|
799
|
+
</CConditionalPortal>
|
|
758
800
|
</div>
|
|
759
801
|
</CFormControlWrapper>
|
|
760
802
|
)
|
|
@@ -766,6 +808,7 @@ CAutocomplete.propTypes = {
|
|
|
766
808
|
className: PropTypes.string,
|
|
767
809
|
clearSearchOnSelect: PropTypes.bool,
|
|
768
810
|
cleaner: PropTypes.bool,
|
|
811
|
+
container: PropTypes.any,
|
|
769
812
|
disabled: PropTypes.bool,
|
|
770
813
|
highlightOptionsOnSearch: PropTypes.bool,
|
|
771
814
|
indicator: PropTypes.bool,
|
|
@@ -780,6 +823,7 @@ CAutocomplete.propTypes = {
|
|
|
780
823
|
optionsTemplate: PropTypes.func,
|
|
781
824
|
optionsGroupsTemplate: PropTypes.func,
|
|
782
825
|
placeholder: PropTypes.string,
|
|
826
|
+
portal: PropTypes.bool,
|
|
783
827
|
required: PropTypes.bool,
|
|
784
828
|
resetSelectionOnOptionsChange: PropTypes.bool,
|
|
785
829
|
search: PropTypes.oneOfType([
|
|
@@ -139,32 +139,6 @@ export const getFirstAvailableDateInRange = (
|
|
|
139
139
|
return null
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
-
/**
|
|
143
|
-
* Returns the ISO 8601 week string (e.g., "2026W01") for a given date.
|
|
144
|
-
*
|
|
145
|
-
* The ISO week date system considers:
|
|
146
|
-
* - Weeks starting on Monday
|
|
147
|
-
* - Week 1 is the week with the first Thursday of the year
|
|
148
|
-
*
|
|
149
|
-
* @param {Date} date - The date for which to calculate the ISO week and year.
|
|
150
|
-
* @returns {string} The ISO week string in the format "YYYYWww" (e.g., "2026W01").
|
|
151
|
-
*/
|
|
152
|
-
export const getISOWeekString = (date: Date): string => {
|
|
153
|
-
const tempDate = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()))
|
|
154
|
-
// Set to nearest Thursday: current date + 4 - current day number (0 is Sunday)
|
|
155
|
-
const day = tempDate.getUTCDay()
|
|
156
|
-
const diff = day <= 4 ? 4 - day : 11 - day
|
|
157
|
-
tempDate.setUTCDate(tempDate.getUTCDate() + diff)
|
|
158
|
-
|
|
159
|
-
const yearStart = new Date(Date.UTC(tempDate.getUTCFullYear(), 0, 1))
|
|
160
|
-
const weekNumber = Math.ceil(((tempDate.getTime() - yearStart.getTime()) / 86400000 + 1) / 7)
|
|
161
|
-
|
|
162
|
-
const isoYear = tempDate.getUTCFullYear()
|
|
163
|
-
const weekString = `W${weekNumber.toString().padStart(2, '0')}`
|
|
164
|
-
|
|
165
|
-
return `${isoYear}${weekString}`
|
|
166
|
-
}
|
|
167
|
-
|
|
168
142
|
/**
|
|
169
143
|
* Retrieves an array of month names based on locale and format.
|
|
170
144
|
* @param locale - The locale string (e.g., 'en-US').
|
|
@@ -135,6 +135,12 @@ export interface CDateRangePickerProps
|
|
|
135
135
|
* Specifies short hints that are visible in start date and end date inputs.
|
|
136
136
|
*/
|
|
137
137
|
placeholder?: string | string[]
|
|
138
|
+
/**
|
|
139
|
+
* Enable live preview of dates in input fields when hovering over calendar cells.
|
|
140
|
+
*
|
|
141
|
+
* @since 5.20.0
|
|
142
|
+
*/
|
|
143
|
+
previewDateOnHover?: boolean
|
|
138
144
|
/**
|
|
139
145
|
* @ignore
|
|
140
146
|
*/
|
|
@@ -248,6 +254,7 @@ export const CDateRangePicker = forwardRef<HTMLDivElement | HTMLLIElement, CDate
|
|
|
248
254
|
onShow,
|
|
249
255
|
placeholder = ['Start date', 'End date'],
|
|
250
256
|
portal = true,
|
|
257
|
+
previewDateOnHover = true,
|
|
251
258
|
range = true,
|
|
252
259
|
ranges,
|
|
253
260
|
rangesButtonsColor = 'secondary',
|
|
@@ -390,6 +397,10 @@ export const CDateRangePicker = forwardRef<HTMLDivElement | HTMLLIElement, CDate
|
|
|
390
397
|
}
|
|
391
398
|
|
|
392
399
|
const handleDateHover = (date: Date | string | null) => {
|
|
400
|
+
if (!previewDateOnHover) {
|
|
401
|
+
return
|
|
402
|
+
}
|
|
403
|
+
|
|
393
404
|
if (selectEndDate) {
|
|
394
405
|
setInputEndHoverValue(date)
|
|
395
406
|
} else {
|
|
@@ -764,6 +775,7 @@ CDateRangePicker.propTypes = {
|
|
|
764
775
|
PropTypes.string,
|
|
765
776
|
PropTypes.arrayOf(PropTypes.string.isRequired),
|
|
766
777
|
]),
|
|
778
|
+
previewDateOnHover: PropTypes.bool,
|
|
767
779
|
range: PropTypes.bool,
|
|
768
780
|
ranges: PropTypes.object,
|
|
769
781
|
rangesButtonsColor: CButton.propTypes?.color,
|
|
@@ -6,6 +6,7 @@ import React, {
|
|
|
6
6
|
useState,
|
|
7
7
|
useRef,
|
|
8
8
|
useMemo,
|
|
9
|
+
useId,
|
|
9
10
|
} from 'react'
|
|
10
11
|
|
|
11
12
|
import classNames from 'classnames'
|
|
@@ -46,6 +47,12 @@ export interface CMultiSelectProps
|
|
|
46
47
|
* @since 5.8.0
|
|
47
48
|
*/
|
|
48
49
|
ariaCleanerLabel?: string
|
|
50
|
+
/**
|
|
51
|
+
* A string that provides an accessible label for the indicator button. This label is read by screen readers to describe the action associated with the button.
|
|
52
|
+
*
|
|
53
|
+
* @since 5.19.0
|
|
54
|
+
*/
|
|
55
|
+
ariaIndicatorLabel?: string
|
|
49
56
|
/**
|
|
50
57
|
* A string of all className you want applied to the base component.
|
|
51
58
|
*/
|
|
@@ -217,6 +224,7 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
|
|
|
217
224
|
{
|
|
218
225
|
allowCreateOptions,
|
|
219
226
|
ariaCleanerLabel = 'Clear all selections',
|
|
227
|
+
ariaIndicatorLabel = 'Toggle visibility of options menu',
|
|
220
228
|
className,
|
|
221
229
|
cleaner = true,
|
|
222
230
|
clearSearchOnSelect,
|
|
@@ -274,6 +282,7 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
|
|
|
274
282
|
const nativeSelectRef = useRef<HTMLSelectElement>(null)
|
|
275
283
|
const searchRef = useRef<HTMLInputElement>(null)
|
|
276
284
|
const isInitialMount = useRef(true)
|
|
285
|
+
const uniqueId = useId()
|
|
277
286
|
|
|
278
287
|
const [searchValue, setSearchValue] = useState('')
|
|
279
288
|
const [selected, setSelected] = useState<SelectedOption[]>([])
|
|
@@ -532,7 +541,7 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
|
|
|
532
541
|
feedback={feedback}
|
|
533
542
|
feedbackInvalid={feedbackInvalid}
|
|
534
543
|
feedbackValid={feedbackValid}
|
|
535
|
-
id={id}
|
|
544
|
+
id={id ?? uniqueId}
|
|
536
545
|
invalid={invalid}
|
|
537
546
|
label={label}
|
|
538
547
|
text={text}
|
|
@@ -540,9 +549,9 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
|
|
|
540
549
|
valid={valid}
|
|
541
550
|
>
|
|
542
551
|
<CMultiSelectNativeSelect
|
|
543
|
-
id={id}
|
|
552
|
+
id={id ?? uniqueId}
|
|
544
553
|
multiple={multiple}
|
|
545
|
-
name={name}
|
|
554
|
+
name={name ?? uniqueId}
|
|
546
555
|
options={selected}
|
|
547
556
|
required={required}
|
|
548
557
|
value={
|
|
@@ -574,6 +583,10 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
|
|
|
574
583
|
{...(!search && !disabled && { tabIndex: 0 })}
|
|
575
584
|
onClick={() => !disabled && openDropdown()}
|
|
576
585
|
onKeyDown={handleTogglerKeyDown}
|
|
586
|
+
role="combobox"
|
|
587
|
+
aria-haspopup="listbox"
|
|
588
|
+
aria-expanded={isOpen}
|
|
589
|
+
{...(portal && { 'aria-owns': `multiselect-listbox-${uniqueId}` })}
|
|
577
590
|
ref={dropdownRefElement}
|
|
578
591
|
>
|
|
579
592
|
<CMultiSelectSelection
|
|
@@ -591,6 +604,8 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
|
|
|
591
604
|
type="text"
|
|
592
605
|
className="form-multi-select-search"
|
|
593
606
|
disabled={disabled}
|
|
607
|
+
id={`search${id ?? uniqueId}`}
|
|
608
|
+
name={`search${name ?? uniqueId}`}
|
|
594
609
|
autoComplete="off"
|
|
595
610
|
onChange={handleSearchChange}
|
|
596
611
|
onKeyDown={handleSearchKeyDown}
|
|
@@ -632,6 +647,7 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
|
|
|
632
647
|
}
|
|
633
648
|
}}
|
|
634
649
|
{...(disabled && { tabIndex: -1 })}
|
|
650
|
+
aria-label={ariaIndicatorLabel}
|
|
635
651
|
></button>
|
|
636
652
|
</div>
|
|
637
653
|
</div>
|
|
@@ -640,8 +656,10 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
|
|
|
640
656
|
className={classNames('form-multi-select-dropdown', {
|
|
641
657
|
show: portal && isOpen,
|
|
642
658
|
})}
|
|
659
|
+
id={`multiselect-listbox-${uniqueId}`}
|
|
643
660
|
onKeyDown={handleGlobalSearch}
|
|
644
|
-
role="
|
|
661
|
+
role="listbox"
|
|
662
|
+
aria-multiselectable={multiple}
|
|
645
663
|
ref={dropdownMenuElement}
|
|
646
664
|
>
|
|
647
665
|
{multiple && selectAll && (
|
|
@@ -681,6 +699,7 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
|
|
|
681
699
|
CMultiSelect.propTypes = {
|
|
682
700
|
allowCreateOptions: PropTypes.bool,
|
|
683
701
|
ariaCleanerLabel: PropTypes.string,
|
|
702
|
+
ariaIndicatorLabel: PropTypes.string,
|
|
684
703
|
className: PropTypes.string,
|
|
685
704
|
cleaner: PropTypes.bool,
|
|
686
705
|
clearSearchOnSelect: PropTypes.bool,
|