@coreui/react-pro 5.17.1 → 5.19.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 (101) hide show
  1. package/README.md +1 -1
  2. package/dist/cjs/components/autocomplete/CAutocomplete.d.ts +243 -0
  3. package/dist/cjs/components/autocomplete/CAutocomplete.js +306 -0
  4. package/dist/cjs/components/autocomplete/CAutocomplete.js.map +1 -0
  5. package/dist/cjs/components/autocomplete/CAutocompleteOptions.d.ts +17 -0
  6. package/dist/cjs/components/autocomplete/CAutocompleteOptions.js +72 -0
  7. package/dist/cjs/components/autocomplete/CAutocompleteOptions.js.map +1 -0
  8. package/dist/cjs/components/autocomplete/index.d.ts +2 -0
  9. package/dist/cjs/components/autocomplete/types.d.ts +18 -0
  10. package/dist/cjs/components/autocomplete/utils.d.ts +11 -0
  11. package/dist/cjs/components/autocomplete/utils.js +107 -0
  12. package/dist/cjs/components/autocomplete/utils.js.map +1 -0
  13. package/dist/cjs/components/calendar/CCalendarPanel.js +6 -7
  14. package/dist/cjs/components/calendar/CCalendarPanel.js.map +1 -1
  15. package/dist/cjs/components/calendar/utils.d.ts +19 -5
  16. package/dist/cjs/components/calendar/utils.js +20 -9
  17. package/dist/cjs/components/calendar/utils.js.map +1 -1
  18. package/dist/cjs/components/date-range-picker/CDateRangePicker.js +10 -8
  19. package/dist/cjs/components/date-range-picker/CDateRangePicker.js.map +1 -1
  20. package/dist/cjs/components/index.d.ts +1 -0
  21. package/dist/cjs/components/multi-select/CMultiSelect.d.ts +6 -0
  22. package/dist/cjs/components/multi-select/CMultiSelect.js +13 -12
  23. package/dist/cjs/components/multi-select/CMultiSelect.js.map +1 -1
  24. package/dist/cjs/components/multi-select/CMultiSelectOptions.js +6 -5
  25. package/dist/cjs/components/multi-select/CMultiSelectOptions.js.map +1 -1
  26. package/dist/cjs/components/multi-select/CMultiSelectSelection.js +1 -1
  27. package/dist/cjs/components/multi-select/CMultiSelectSelection.js.map +1 -1
  28. package/dist/cjs/components/multi-select/utils.d.ts +0 -2
  29. package/dist/cjs/components/multi-select/utils.js +0 -26
  30. package/dist/cjs/components/multi-select/utils.js.map +1 -1
  31. package/dist/cjs/components/time-picker/CTimePicker.js +15 -1
  32. package/dist/cjs/components/time-picker/CTimePicker.js.map +1 -1
  33. package/dist/cjs/index.js +2 -0
  34. package/dist/cjs/index.js.map +1 -1
  35. package/dist/cjs/utils/getNextSibling.d.ts +2 -0
  36. package/dist/cjs/utils/getNextSibling.js +23 -0
  37. package/dist/cjs/utils/getNextSibling.js.map +1 -0
  38. package/dist/cjs/utils/getPreviousSibling.d.ts +2 -0
  39. package/dist/cjs/utils/getPreviousSibling.js +22 -0
  40. package/dist/cjs/utils/getPreviousSibling.js.map +1 -0
  41. package/dist/cjs/utils/index.d.ts +3 -1
  42. package/dist/esm/components/autocomplete/CAutocomplete.d.ts +243 -0
  43. package/dist/esm/components/autocomplete/CAutocomplete.js +304 -0
  44. package/dist/esm/components/autocomplete/CAutocomplete.js.map +1 -0
  45. package/dist/esm/components/autocomplete/CAutocompleteOptions.d.ts +17 -0
  46. package/dist/esm/components/autocomplete/CAutocompleteOptions.js +70 -0
  47. package/dist/esm/components/autocomplete/CAutocompleteOptions.js.map +1 -0
  48. package/dist/esm/components/autocomplete/index.d.ts +2 -0
  49. package/dist/esm/components/autocomplete/types.d.ts +18 -0
  50. package/dist/esm/components/autocomplete/utils.d.ts +11 -0
  51. package/dist/esm/components/autocomplete/utils.js +96 -0
  52. package/dist/esm/components/autocomplete/utils.js.map +1 -0
  53. package/dist/esm/components/calendar/CCalendarPanel.js +7 -8
  54. package/dist/esm/components/calendar/CCalendarPanel.js.map +1 -1
  55. package/dist/esm/components/calendar/utils.d.ts +19 -5
  56. package/dist/esm/components/calendar/utils.js +20 -9
  57. package/dist/esm/components/calendar/utils.js.map +1 -1
  58. package/dist/esm/components/date-range-picker/CDateRangePicker.js +10 -8
  59. package/dist/esm/components/date-range-picker/CDateRangePicker.js.map +1 -1
  60. package/dist/esm/components/index.d.ts +1 -0
  61. package/dist/esm/components/multi-select/CMultiSelect.d.ts +6 -0
  62. package/dist/esm/components/multi-select/CMultiSelect.js +14 -13
  63. package/dist/esm/components/multi-select/CMultiSelect.js.map +1 -1
  64. package/dist/esm/components/multi-select/CMultiSelectOptions.js +4 -3
  65. package/dist/esm/components/multi-select/CMultiSelectOptions.js.map +1 -1
  66. package/dist/esm/components/multi-select/CMultiSelectSelection.js +1 -1
  67. package/dist/esm/components/multi-select/CMultiSelectSelection.js.map +1 -1
  68. package/dist/esm/components/multi-select/utils.d.ts +0 -2
  69. package/dist/esm/components/multi-select/utils.js +1 -25
  70. package/dist/esm/components/multi-select/utils.js.map +1 -1
  71. package/dist/esm/components/time-picker/CTimePicker.js +15 -1
  72. package/dist/esm/components/time-picker/CTimePicker.js.map +1 -1
  73. package/dist/esm/index.js +1 -0
  74. package/dist/esm/index.js.map +1 -1
  75. package/dist/esm/utils/getNextSibling.d.ts +2 -0
  76. package/dist/esm/utils/getNextSibling.js +18 -0
  77. package/dist/esm/utils/getNextSibling.js.map +1 -0
  78. package/dist/esm/utils/getPreviousSibling.d.ts +2 -0
  79. package/dist/esm/utils/getPreviousSibling.js +18 -0
  80. package/dist/esm/utils/getPreviousSibling.js.map +1 -0
  81. package/dist/esm/utils/index.d.ts +3 -1
  82. package/package.json +10 -10
  83. package/src/components/autocomplete/CAutocomplete.tsx +846 -0
  84. package/src/components/autocomplete/CAutocompleteOptions.tsx +165 -0
  85. package/src/components/autocomplete/__tests__/CAutocomplete.spec.tsx +244 -0
  86. package/src/components/autocomplete/__tests__/CAutocompleteOptions.spec.tsx +210 -0
  87. package/src/components/autocomplete/index.ts +3 -0
  88. package/src/components/autocomplete/types.ts +25 -0
  89. package/src/components/autocomplete/utils.ts +125 -0
  90. package/src/components/calendar/CCalendarPanel.tsx +5 -15
  91. package/src/components/calendar/utils.ts +24 -10
  92. package/src/components/date-range-picker/CDateRangePicker.tsx +11 -7
  93. package/src/components/index.ts +1 -0
  94. package/src/components/multi-select/CMultiSelect.tsx +23 -4
  95. package/src/components/multi-select/CMultiSelectOptions.tsx +8 -5
  96. package/src/components/multi-select/CMultiSelectSelection.tsx +3 -3
  97. package/src/components/multi-select/utils.ts +0 -34
  98. package/src/components/time-picker/CTimePicker.tsx +14 -4
  99. package/src/utils/getNextSibling.ts +18 -0
  100. package/src/utils/getPreviousSibling.ts +18 -0
  101. package/src/utils/index.ts +4 -0
@@ -0,0 +1,125 @@
1
+ import type { Option, OptionsGroup, Search } from './types'
2
+
3
+ export const filterOptions = (
4
+ options: (Option | OptionsGroup)[],
5
+ search: string
6
+ ): (Option | OptionsGroup)[] => {
7
+ const result: (Option | OptionsGroup)[] = []
8
+
9
+ options.forEach((item: Option | OptionsGroup) => {
10
+ if (typeof item !== 'string' && 'options' in item && Array.isArray(item.options)) {
11
+ const filteredOptions = filterOptions(item.options, search)
12
+ if (filteredOptions.length > 0) {
13
+ result.push({ ...item, options: filteredOptions })
14
+ }
15
+ } else if (getOptionLabel(item).toLowerCase().includes(search.toLowerCase())) {
16
+ result.push(item)
17
+ }
18
+ })
19
+
20
+ return result
21
+ }
22
+
23
+ export const flattenOptionsArray = (
24
+ options: (Option | OptionsGroup)[]
25
+ ): (Option | OptionsGroup)[] => {
26
+ const optionsList: (Option | OptionsGroup)[] = []
27
+
28
+ for (const option of options) {
29
+ if (typeof option !== 'string' && 'options' in option && Array.isArray(option.options)) {
30
+ optionsList.push(...option.options)
31
+ } else {
32
+ optionsList.push(option)
33
+ }
34
+ }
35
+
36
+ return optionsList
37
+ }
38
+
39
+ export const isExternalSearch = (search: Search | undefined): boolean => {
40
+ return (
41
+ (typeof search === 'string' && search === 'external') ||
42
+ (typeof search === 'object' && search.external === true)
43
+ )
44
+ }
45
+
46
+ export const isGlobalSearch = (search: Search | undefined): boolean => {
47
+ return (
48
+ (typeof search === 'string' && search === 'global') ||
49
+ (typeof search === 'object' && search.global === true)
50
+ )
51
+ }
52
+
53
+ export const getOptionLabel = (option: Option) =>
54
+ typeof option === 'string' ? option : option.label
55
+
56
+ export const highlightSubstring = (string: string, query?: string) => {
57
+ if (query) {
58
+ const regex = new RegExp(query, 'gi')
59
+ return string.replace(regex, (string) => `<strong>${string}</strong>`)
60
+ }
61
+
62
+ return string
63
+ }
64
+
65
+ export const isOptionDisabled = (option: Option | OptionsGroup | string) =>
66
+ typeof option === 'string' ? false : option.disabled
67
+
68
+ export const isOptionSelected = (
69
+ option: Option | OptionsGroup | string,
70
+ selected: Option | null
71
+ ) => {
72
+ if (!selected) {
73
+ return false
74
+ }
75
+
76
+ if (typeof option === 'string' && typeof selected === 'string') {
77
+ return selected === option
78
+ }
79
+
80
+ if (typeof option !== 'string' && typeof selected !== 'string') {
81
+ return selected.label === option.label
82
+ }
83
+
84
+ return false
85
+ }
86
+
87
+ export const getFirstOptionByLabel = (
88
+ value: string,
89
+ options: (Option | OptionsGroup)[]
90
+ ): Option | null => {
91
+ for (const option of options) {
92
+ if (typeof option !== 'string' && 'options' in option && Array.isArray(option.options)) {
93
+ const found = getFirstOptionByLabel(value, option.options)
94
+ if (found) {
95
+ return found
96
+ }
97
+ }
98
+
99
+ if (getOptionLabel(option).toLowerCase() === value.toLowerCase()) {
100
+ return option
101
+ }
102
+ }
103
+
104
+ return null
105
+ }
106
+
107
+ export const getFirstOptionByValue = (
108
+ value: number,
109
+ options: (Option | OptionsGroup)[]
110
+ ): Option | null => {
111
+ for (const option of options) {
112
+ if (typeof option !== 'string' && 'options' in option && Array.isArray(option.options)) {
113
+ const found = getFirstOptionByValue(value, option.options)
114
+ if (found) {
115
+ return found
116
+ }
117
+ }
118
+
119
+ if (typeof option !== 'string' && 'value' in option && option.value === value) {
120
+ return option
121
+ }
122
+ }
123
+
124
+ return null
125
+ }
@@ -2,7 +2,6 @@ import React, { forwardRef, KeyboardEvent, useEffect, useState } from 'react'
2
2
  import classNames from 'classnames'
3
3
 
4
4
  import {
5
- convertToDateObject,
6
5
  createGroupsInArray,
7
6
  getMonthDetails,
8
7
  getMonthsNames,
@@ -120,16 +119,11 @@ export const CCalendarPanel = forwardRef<HTMLTableElement, CCalendarPanelProps>(
120
119
  )}
121
120
  <tbody>
122
121
  {view === 'days' &&
123
- monthDetails.map((week, index) => {
124
- const date = convertToDateObject(
125
- week.weekNumber === 0
126
- ? `${calendarDate.getFullYear()}W53`
127
- : `${calendarDate.getFullYear()}W${week.weekNumber}`,
128
- selectionType
129
- )
122
+ monthDetails.map(({ week, days }, index) => {
123
+ const { date } = days[0]
130
124
  const isDisabled = isDateDisabled(date, minDate, maxDate, disabledDates)
131
125
  const isSelected = isDateSelected(date, startDate, endDate)
132
- const current = week.days.some((day) => day.month === 'current')
126
+ const current = days.some((day) => day.month === 'current')
133
127
  return (
134
128
  <tr
135
129
  className={classNames('calendar-row', {
@@ -154,12 +148,8 @@ export const CCalendarPanel = forwardRef<HTMLTableElement, CCalendarPanelProps>(
154
148
  onMouseLeave: () => onCalendarMouseLeave(),
155
149
  })}
156
150
  >
157
- {showWeekNumber && (
158
- <th className="calendar-cell-week-number">
159
- {week.weekNumber === 0 ? 53 : week.weekNumber}
160
- </th>
161
- )}
162
- {week.days.map(({ date, month }: { date: Date; month: string }, idx: number) => {
151
+ {showWeekNumber && <th className="calendar-cell-week-number">{week.number}</th>}
152
+ {days.map(({ date, month }: { date: Date; month: string }, idx: number) => {
163
153
  const isDisabled = isDateDisabled(date, minDate, maxDate, disabledDates)
164
154
  const isSelected = isDateSelected(date, startDate, endDate)
165
155
  return month === 'current' || showAdjacementDays ? (
@@ -87,7 +87,8 @@ export const getDateBySelectionType = (
87
87
  }
88
88
 
89
89
  if (selectionType === 'week') {
90
- return `${date.getFullYear()}W${getWeekNumber(date)}`
90
+ const { year, weekNumber } = getISOWeekNumberAndYear(date)
91
+ return `${year}W${weekNumber.toString().padStart(2, '0')}`
91
92
  }
92
93
 
93
94
  if (selectionType === 'month') {
@@ -254,12 +255,20 @@ const getTrailingDays = (
254
255
  }
255
256
 
256
257
  /**
257
- * Calculates the ISO week number for a given date.
258
- * @param date - The date to calculate the week number for.
259
- * @returns The ISO week number.
258
+ * Calculates the ISO 8601 week number and year for a given date.
259
+ *
260
+ * In the ISO 8601 standard:
261
+ * - Weeks start on Monday.
262
+ * - The first week of the year is the one that contains January 4th.
263
+ * - The year of the week may differ from the calendar year (e.g., Dec 29, 2025 is in ISO year 2026).
264
+ *
265
+ * @param {Date} date - The date for which to calculate the ISO week number and year.
266
+ * @returns {{ weekNumber: number, year: number }} An object containing:
267
+ * - `weekNumber`: the ISO week number (1–53),
268
+ * - `year`: the ISO year (may differ from the calendar year of the date).
260
269
  */
261
- export const getWeekNumber = (date: Date): number => {
262
- const tempDate = new Date(date.getTime())
270
+ export const getISOWeekNumberAndYear = (date: Date): { weekNumber: number; year: number } => {
271
+ const tempDate = new Date(date)
263
272
  tempDate.setHours(0, 0, 0, 0)
264
273
 
265
274
  // Thursday in current week decides the year
@@ -270,7 +279,7 @@ export const getWeekNumber = (date: Date): number => {
270
279
  // Calculate full weeks to the date
271
280
  const weekNumber = 1 + Math.round((tempDate.getTime() - week1.getTime()) / 86_400_000 / 7)
272
281
 
273
- return weekNumber
282
+ return { weekNumber, year: tempDate.getFullYear() }
274
283
  }
275
284
 
276
285
  /**
@@ -284,22 +293,27 @@ export const getMonthDetails = (
284
293
  year: number,
285
294
  month: number,
286
295
  firstDayOfWeek: number
287
- ): { weekNumber?: number; days: { date: Date; month: string }[] }[] => {
296
+ ): { week: { number: number; year: number }; days: { date: Date; month: string }[] }[] => {
288
297
  const daysPrevMonth = getLeadingDays(year, month, firstDayOfWeek)
289
298
  const daysThisMonth = getMonthDays(year, month)
290
299
  const daysNextMonth = getTrailingDays(year, month, daysPrevMonth, daysThisMonth)
291
300
  const days = [...daysPrevMonth, ...daysThisMonth, ...daysNextMonth]
292
- const weeks: { weekNumber?: number; days: { date: Date; month: string }[] }[] = []
301
+ const weeks: {
302
+ week: { number: number; year: number }
303
+ days: { date: Date; month: string }[]
304
+ }[] = []
293
305
 
294
306
  days.forEach((day, index) => {
295
307
  if (index % 7 === 0 || weeks.length === 0) {
296
308
  weeks.push({
309
+ week: { number: 0, year: 0 },
297
310
  days: [],
298
311
  })
299
312
  }
300
313
 
301
314
  if ((index + 1) % 7 === 0) {
302
- weeks[weeks.length - 1].weekNumber = getWeekNumber(day.date)
315
+ const { weekNumber, year } = getISOWeekNumberAndYear(day.date)
316
+ weeks[weeks.length - 1].week = { number: weekNumber, year }
303
317
  }
304
318
 
305
319
  weeks[weeks.length - 1].days.push(day)
@@ -401,6 +401,8 @@ export const CDateRangePicker = forwardRef<HTMLDivElement | HTMLLIElement, CDate
401
401
  event.stopPropagation()
402
402
  setStartDate(null)
403
403
  setEndDate(null)
404
+ onStartDateChange?.(null)
405
+ onEndDateChange?.(null)
404
406
  }
405
407
 
406
408
  const handleEndDateChange = (date: Date | string | null) => {
@@ -447,15 +449,17 @@ export const CDateRangePicker = forwardRef<HTMLDivElement | HTMLLIElement, CDate
447
449
  const date = inputDateParse
448
450
  ? inputDateParse(value)
449
451
  : getLocalDateFromString(value, locale, timepicker)
452
+
450
453
  if (date instanceof Date && date.getTime()) {
451
454
  setCalendarDate(date)
452
- if (input === 'start') {
453
- setStartDate(date)
454
- onStartDateChange?.(date)
455
- } else {
456
- setEndDate(date)
457
- onEndDateChange?.(date)
458
- }
455
+ }
456
+
457
+ if (input === 'start') {
458
+ setStartDate(date ?? null)
459
+ onStartDateChange?.(date ?? null)
460
+ } else {
461
+ setEndDate(date ?? null)
462
+ onEndDateChange?.(date ?? null)
459
463
  }
460
464
  }, inputOnChangeDelay)
461
465
 
@@ -1,5 +1,6 @@
1
1
  export * from './accordion'
2
2
  export * from './alert'
3
+ export * from './autocomplete'
3
4
  export * from './avatar'
4
5
  export * from './backdrop'
5
6
  export * from './badge'
@@ -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="menu"
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,
@@ -5,7 +5,7 @@ import classNames from 'classnames'
5
5
  import { CElementCover } from '../element-cover'
6
6
  import { CVirtualScroller } from '../virtual-scroller'
7
7
 
8
- import { getNextSibling, getPreviousSibling } from './utils'
8
+ import { getNextSibling, getPreviousSibling } from '../../utils'
9
9
  import type { Option, OptionsGroup } from './types'
10
10
 
11
11
  export interface CMultiSelectOptionsProps extends HTMLAttributes<HTMLDivElement> {
@@ -38,12 +38,12 @@ export const CMultiSelectOptions = forwardRef<HTMLDivElement, CMultiSelectOption
38
38
  virtualScroller,
39
39
  visibleItems = 10,
40
40
  },
41
- ref,
41
+ ref
42
42
  ) => {
43
43
  const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>, option: Option) => {
44
44
  if (event.code === 'Space' || event.key === 'Enter') {
45
45
  event.preventDefault()
46
- onOptionOnClick && onOptionOnClick(option)
46
+ onOptionOnClick?.(option)
47
47
  }
48
48
 
49
49
  if (event.key === 'Down' || event.key === 'ArrowDown') {
@@ -77,6 +77,9 @@ export const CMultiSelectOptions = forwardRef<HTMLDivElement, CMultiSelectOption
77
77
  onClick={() => onOptionOnClick && onOptionOnClick(option as Option)}
78
78
  onKeyDown={(event) => handleKeyDown(event, option as Option)}
79
79
  tabIndex={0}
80
+ role="option"
81
+ aria-selected={selected.some((_option) => _option.value === option.value)}
82
+ aria-disabled={Boolean(option.disabled)}
80
83
  >
81
84
  {optionsTemplate ? optionsTemplate(option as Option) : option.label}
82
85
  </div>
@@ -84,7 +87,7 @@ export const CMultiSelectOptions = forwardRef<HTMLDivElement, CMultiSelectOption
84
87
  <div className="form-multi-select-optgroup-label" key={index}>
85
88
  {optionsGroupsTemplate ? optionsGroupsTemplate(option as OptionsGroup) : option.label}
86
89
  </div>
87
- ),
90
+ )
88
91
  )
89
92
  ) : (
90
93
  <div className="form-multi-select-options-empty">{searchNoResultsLabel}</div>
@@ -116,7 +119,7 @@ export const CMultiSelectOptions = forwardRef<HTMLDivElement, CMultiSelectOption
116
119
  {loading && <CElementCover />}
117
120
  </>
118
121
  )
119
- },
122
+ }
120
123
  )
121
124
 
122
125
  CMultiSelectOptions.propTypes = {
@@ -29,7 +29,7 @@ export const CMultiSelectSelection = forwardRef<HTMLSpanElement, CMultiSelectSel
29
29
  selectionType,
30
30
  selectionTypeCounterText,
31
31
  },
32
- ref,
32
+ ref
33
33
  ) => {
34
34
  return (
35
35
  <span
@@ -56,7 +56,7 @@ export const CMultiSelectSelection = forwardRef<HTMLSpanElement, CMultiSelectSel
56
56
  className="form-multi-select-tag-delete"
57
57
  type="button"
58
58
  aria-label="Close"
59
- onClick={() => onRemove && onRemove(option)}
59
+ onClick={() => onRemove?.(option)}
60
60
  />
61
61
  )}
62
62
  </span>
@@ -76,7 +76,7 @@ export const CMultiSelectSelection = forwardRef<HTMLSpanElement, CMultiSelectSel
76
76
  {children}
77
77
  </span>
78
78
  )
79
- },
79
+ }
80
80
  )
81
81
 
82
82
  CMultiSelectSelection.propTypes = {
@@ -66,46 +66,12 @@ export const flattenOptionsArray = (
66
66
  return optionsList
67
67
  }
68
68
 
69
- export const getNextSibling = (elem: HTMLElement, selector?: string) => {
70
- // Get the next sibling element
71
- let sibling = elem.nextElementSibling
72
-
73
- // If there's no selector, return the first sibling
74
- if (!selector) return sibling
75
-
76
- // If the sibling matches our selector, use it
77
- // If not, jump to the next sibling and continue the loop
78
- while (sibling) {
79
- if (sibling.matches(selector)) return sibling
80
- sibling = sibling.nextElementSibling
81
- }
82
-
83
- return
84
- }
85
-
86
69
  export const getOptionsList = (container: HTMLElement): HTMLElement[] => {
87
70
  return Array.from(
88
71
  container.querySelectorAll('.form-multi-select-option:not(.disabled):not(:disabled)'),
89
72
  )
90
73
  }
91
74
 
92
- export const getPreviousSibling = (elem: HTMLElement, selector?: string) => {
93
- // Get the next sibling element
94
- let sibling = elem.previousElementSibling
95
-
96
- // If there's no selector, return the first sibling
97
- if (!selector) return sibling
98
-
99
- // If the sibling matches our selector, use it
100
- // If not, jump to the next sibling and continue the loop
101
- while (sibling) {
102
- if (sibling.matches(selector)) return sibling
103
- sibling = sibling.previousElementSibling
104
- }
105
-
106
- return
107
- }
108
-
109
75
  export const isExternalSearch = (search: Search): boolean => {
110
76
  return (
111
77
  (typeof search === 'string' && search === 'external') ||
@@ -278,10 +278,20 @@ export const CTimePicker = forwardRef<HTMLDivElement | HTMLLIElement, CTimePicke
278
278
  setIsValid(false)
279
279
  }
280
280
 
281
- const handleOnChange = useDebouncedCallback(
282
- (value: string) => isValidTime(value) && setDate(convertTimeToDate(value)),
283
- inputOnChangeDelay
284
- )
281
+ const handleOnChange = useDebouncedCallback((value: string) => {
282
+ if (isValidTime(value)) {
283
+ const date = convertTimeToDate(value)
284
+ setDate(date)
285
+
286
+ if (date) {
287
+ onTimeChange?.(date.toTimeString(), date.toLocaleTimeString(), date)
288
+ } else {
289
+ onTimeChange?.(null)
290
+ }
291
+ } else {
292
+ onTimeChange?.(null)
293
+ }
294
+ }, inputOnChangeDelay)
285
295
 
286
296
  const handleTimeChange = (set: 'hours' | 'minutes' | 'seconds' | 'toggle', value: string) => {
287
297
  const _date = date || new Date('1970-01-01')
@@ -0,0 +1,18 @@
1
+ export const getNextSibling = (elem: HTMLElement, selector?: string) => {
2
+ // Get the next sibling element
3
+ let sibling = elem.nextElementSibling
4
+
5
+ // If there's no selector, return the first sibling
6
+ if (!selector) return sibling
7
+
8
+ // If the sibling matches our selector, use it
9
+ // If not, jump to the next sibling and continue the loop
10
+ while (sibling) {
11
+ if (sibling.matches(selector)) return sibling
12
+ sibling = sibling.nextElementSibling
13
+ }
14
+
15
+ return
16
+ }
17
+
18
+ export default getNextSibling
@@ -0,0 +1,18 @@
1
+ const getPreviousSibling = (elem: HTMLElement, selector?: string) => {
2
+ // Get the next sibling element
3
+ let sibling = elem.previousElementSibling
4
+
5
+ // If there's no selector, return the first sibling
6
+ if (!selector) return sibling
7
+
8
+ // If the sibling matches our selector, use it
9
+ // If not, jump to the next sibling and continue the loop
10
+ while (sibling) {
11
+ if (sibling.matches(selector)) return sibling
12
+ sibling = sibling.previousElementSibling
13
+ }
14
+
15
+ return
16
+ }
17
+
18
+ export default getPreviousSibling
@@ -1,5 +1,7 @@
1
1
  import executeAfterTransition from './executeAfterTransition'
2
2
  import getNextActiveElement from './getNextActiveElement'
3
+ import getNextSibling from './getNextSibling'
4
+ import getPreviousSibling from './getPreviousSibling'
3
5
  import getRTLPlacement from './getRTLPlacement'
4
6
  import getTransitionDurationFromElement from './getTransitionDurationFromElement'
5
7
  import isInViewport from './isInViewport'
@@ -10,6 +12,8 @@ import mergeClassNames from './mergeClassNames'
10
12
  export {
11
13
  executeAfterTransition,
12
14
  getNextActiveElement,
15
+ getNextSibling,
16
+ getPreviousSibling,
13
17
  getRTLPlacement,
14
18
  getTransitionDurationFromElement,
15
19
  isInViewport,