@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.
- package/README.md +1 -1
- package/dist/cjs/components/autocomplete/CAutocomplete.d.ts +243 -0
- package/dist/cjs/components/autocomplete/CAutocomplete.js +306 -0
- package/dist/cjs/components/autocomplete/CAutocomplete.js.map +1 -0
- package/dist/cjs/components/autocomplete/CAutocompleteOptions.d.ts +17 -0
- package/dist/cjs/components/autocomplete/CAutocompleteOptions.js +72 -0
- package/dist/cjs/components/autocomplete/CAutocompleteOptions.js.map +1 -0
- package/dist/cjs/components/autocomplete/index.d.ts +2 -0
- package/dist/cjs/components/autocomplete/types.d.ts +18 -0
- package/dist/cjs/components/autocomplete/utils.d.ts +11 -0
- package/dist/cjs/components/autocomplete/utils.js +107 -0
- package/dist/cjs/components/autocomplete/utils.js.map +1 -0
- package/dist/cjs/components/calendar/CCalendarPanel.js +6 -7
- package/dist/cjs/components/calendar/CCalendarPanel.js.map +1 -1
- package/dist/cjs/components/calendar/utils.d.ts +19 -5
- package/dist/cjs/components/calendar/utils.js +20 -9
- package/dist/cjs/components/calendar/utils.js.map +1 -1
- package/dist/cjs/components/date-range-picker/CDateRangePicker.js +10 -8
- package/dist/cjs/components/date-range-picker/CDateRangePicker.js.map +1 -1
- package/dist/cjs/components/index.d.ts +1 -0
- 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/components/time-picker/CTimePicker.js +15 -1
- package/dist/cjs/components/time-picker/CTimePicker.js.map +1 -1
- package/dist/cjs/index.js +2 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/utils/getNextSibling.d.ts +2 -0
- package/dist/cjs/utils/getNextSibling.js +23 -0
- package/dist/cjs/utils/getNextSibling.js.map +1 -0
- package/dist/cjs/utils/getPreviousSibling.d.ts +2 -0
- package/dist/cjs/utils/getPreviousSibling.js +22 -0
- package/dist/cjs/utils/getPreviousSibling.js.map +1 -0
- package/dist/cjs/utils/index.d.ts +3 -1
- package/dist/esm/components/autocomplete/CAutocomplete.d.ts +243 -0
- package/dist/esm/components/autocomplete/CAutocomplete.js +304 -0
- package/dist/esm/components/autocomplete/CAutocomplete.js.map +1 -0
- package/dist/esm/components/autocomplete/CAutocompleteOptions.d.ts +17 -0
- package/dist/esm/components/autocomplete/CAutocompleteOptions.js +70 -0
- package/dist/esm/components/autocomplete/CAutocompleteOptions.js.map +1 -0
- package/dist/esm/components/autocomplete/index.d.ts +2 -0
- package/dist/esm/components/autocomplete/types.d.ts +18 -0
- package/dist/esm/components/autocomplete/utils.d.ts +11 -0
- package/dist/esm/components/autocomplete/utils.js +96 -0
- package/dist/esm/components/autocomplete/utils.js.map +1 -0
- package/dist/esm/components/calendar/CCalendarPanel.js +7 -8
- package/dist/esm/components/calendar/CCalendarPanel.js.map +1 -1
- package/dist/esm/components/calendar/utils.d.ts +19 -5
- package/dist/esm/components/calendar/utils.js +20 -9
- package/dist/esm/components/calendar/utils.js.map +1 -1
- package/dist/esm/components/date-range-picker/CDateRangePicker.js +10 -8
- package/dist/esm/components/date-range-picker/CDateRangePicker.js.map +1 -1
- package/dist/esm/components/index.d.ts +1 -0
- 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/components/time-picker/CTimePicker.js +15 -1
- package/dist/esm/components/time-picker/CTimePicker.js.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/utils/getNextSibling.d.ts +2 -0
- package/dist/esm/utils/getNextSibling.js +18 -0
- package/dist/esm/utils/getNextSibling.js.map +1 -0
- package/dist/esm/utils/getPreviousSibling.d.ts +2 -0
- package/dist/esm/utils/getPreviousSibling.js +18 -0
- package/dist/esm/utils/getPreviousSibling.js.map +1 -0
- package/dist/esm/utils/index.d.ts +3 -1
- package/package.json +10 -10
- package/src/components/autocomplete/CAutocomplete.tsx +846 -0
- package/src/components/autocomplete/CAutocompleteOptions.tsx +165 -0
- package/src/components/autocomplete/__tests__/CAutocomplete.spec.tsx +244 -0
- package/src/components/autocomplete/__tests__/CAutocompleteOptions.spec.tsx +210 -0
- package/src/components/autocomplete/index.ts +3 -0
- package/src/components/autocomplete/types.ts +25 -0
- package/src/components/autocomplete/utils.ts +125 -0
- package/src/components/calendar/CCalendarPanel.tsx +5 -15
- package/src/components/calendar/utils.ts +24 -10
- package/src/components/date-range-picker/CDateRangePicker.tsx +11 -7
- package/src/components/index.ts +1 -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
- package/src/components/time-picker/CTimePicker.tsx +14 -4
- package/src/utils/getNextSibling.ts +18 -0
- package/src/utils/getPreviousSibling.ts +18 -0
- 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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
259
|
-
*
|
|
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
|
|
262
|
-
const tempDate = new Date(date
|
|
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
|
-
): {
|
|
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: {
|
|
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
|
-
|
|
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
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
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
|
|
package/src/components/index.ts
CHANGED
|
@@ -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,
|
|
@@ -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 '
|
|
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
|
|
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
|
|
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
|
-
(
|
|
283
|
-
|
|
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
|
package/src/utils/index.ts
CHANGED
|
@@ -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,
|