@coreui/react-pro 4.10.0 → 4.11.0-next.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/README.md +1 -1
- package/dist/components/calendar/CCalendar.d.ts +12 -0
- package/dist/components/multi-select/CMultiSelect.d.ts +18 -1
- package/dist/components/multi-select/CMultiSelectOptions.d.ts +1 -0
- package/dist/components/multi-select/types.d.ts +1 -0
- package/dist/components/multi-select/utils.d.ts +5 -0
- package/dist/components/smart-table/CSmartTableInterface.d.ts +1 -1
- package/dist/index.es.js +143 -58
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +143 -58
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/components/calendar/CCalendar.tsx +130 -59
- package/src/components/calendar/utils.ts +1 -0
- package/src/components/date-range-picker/CDateRangePicker.tsx +4 -0
- package/src/components/multi-select/CMultiSelect.tsx +101 -14
- package/src/components/multi-select/CMultiSelectOptions.tsx +29 -16
- package/src/components/multi-select/types.ts +1 -0
- package/src/components/multi-select/utils.ts +42 -25
- package/src/components/smart-table/CSmartTableInterface.tsx +1 -1
- package/src/components/virtual-scroller/CVirtualScroller.tsx +8 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coreui/react-pro",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.11.0-next.1",
|
|
4
4
|
"description": "UI Components Library for React.js",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"typescript": "^4.9.5"
|
|
65
65
|
},
|
|
66
66
|
"peerDependencies": {
|
|
67
|
-
"@coreui/coreui-pro": ">=4.4.
|
|
67
|
+
"@coreui/coreui-pro": ">=4.4.4",
|
|
68
68
|
"react": ">=17",
|
|
69
69
|
"react-dom": ">=17"
|
|
70
70
|
}
|
|
@@ -103,6 +103,18 @@ export interface CCalendarProps {
|
|
|
103
103
|
* @since 4.3.0
|
|
104
104
|
*/
|
|
105
105
|
navYearFirst?: boolean
|
|
106
|
+
/**
|
|
107
|
+
* Set whether days in adjacent months shown before or after the current month are selectable. This only applies if the `showAdjacementDays` option is set to true.
|
|
108
|
+
*
|
|
109
|
+
* @since 4.11.0-next.0
|
|
110
|
+
*/
|
|
111
|
+
selectAdjacementDays?: boolean
|
|
112
|
+
/**
|
|
113
|
+
* Set whether to display dates in adjacent months (non-selectable) at the start and end of the current month.
|
|
114
|
+
*
|
|
115
|
+
* @since 4.11.0-next.0
|
|
116
|
+
*/
|
|
117
|
+
showAdjacementDays?: boolean
|
|
106
118
|
/**
|
|
107
119
|
* Allow range selection.
|
|
108
120
|
*/
|
|
@@ -162,9 +174,13 @@ const Calendar = (props: {
|
|
|
162
174
|
onCellKeyDown: (event: KeyboardEvent<HTMLDivElement>, date: Date) => void
|
|
163
175
|
onCellMouseEnter: (date: Date) => void
|
|
164
176
|
onCellMouseLeave: () => void
|
|
165
|
-
onMonthClick: (month: number
|
|
166
|
-
|
|
177
|
+
onMonthClick: (month: number) => void
|
|
178
|
+
onMonthKeyDown: (event: KeyboardEvent<HTMLDivElement>, month: number) => void
|
|
179
|
+
onYearClick: (date: Date) => void
|
|
180
|
+
onYearKeyDown: (event: KeyboardEvent<HTMLDivElement>, date: Date) => void
|
|
181
|
+
selectAdjacementDays: boolean
|
|
167
182
|
selectEndDate: boolean | undefined
|
|
183
|
+
showAdjacementDays: boolean
|
|
168
184
|
startDate: Date | null
|
|
169
185
|
view: string
|
|
170
186
|
weekdayFormat: number | 'long' | 'narrow' | 'short' | ((date: Date) => string | number)
|
|
@@ -185,8 +201,12 @@ const Calendar = (props: {
|
|
|
185
201
|
onCellMouseEnter,
|
|
186
202
|
onCellMouseLeave,
|
|
187
203
|
onMonthClick,
|
|
204
|
+
onMonthKeyDown,
|
|
188
205
|
onYearClick,
|
|
206
|
+
onYearKeyDown,
|
|
207
|
+
selectAdjacementDays,
|
|
189
208
|
selectEndDate,
|
|
209
|
+
showAdjacementDays,
|
|
190
210
|
startDate,
|
|
191
211
|
view,
|
|
192
212
|
weekdayFormat,
|
|
@@ -235,45 +255,56 @@ const Calendar = (props: {
|
|
|
235
255
|
monthDetails.map((week, index) => {
|
|
236
256
|
return (
|
|
237
257
|
<tr key={index}>
|
|
238
|
-
{week.map(({ date, month }: { date: Date; month: string }, idx: number) =>
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
258
|
+
{week.map(({ date, month }: { date: Date; month: string }, idx: number) =>
|
|
259
|
+
month === 'current' || showAdjacementDays ? (
|
|
260
|
+
<td
|
|
261
|
+
className={classNames('calendar-cell', {
|
|
262
|
+
today: month === 'current' && isToday(date),
|
|
263
|
+
disabled: isDateDisabled(date, minDate, maxDate, disabledDates),
|
|
264
|
+
[month]: true,
|
|
265
|
+
clickable: month !== 'current' && selectAdjacementDays,
|
|
266
|
+
last: isLastDayOfMonth(date),
|
|
267
|
+
'range-hover':
|
|
268
|
+
month === 'current' &&
|
|
269
|
+
(hoverDate && selectEndDate
|
|
270
|
+
? isDateInRange(date, startDate, hoverDate)
|
|
271
|
+
: isDateInRange(date, hoverDate, endDate)),
|
|
272
|
+
range: month === 'current' && isDateInRange(date, startDate, endDate),
|
|
273
|
+
selected: isDateSelected(date, startDate, endDate),
|
|
274
|
+
start: isStartDate(date, startDate, endDate),
|
|
275
|
+
end: isEndDate(date, startDate, endDate),
|
|
276
|
+
})}
|
|
277
|
+
key={idx}
|
|
278
|
+
tabIndex={
|
|
279
|
+
(month === 'current' || selectAdjacementDays) &&
|
|
280
|
+
!isDateDisabled(date, minDate, maxDate, disabledDates)
|
|
281
|
+
? 0
|
|
282
|
+
: -1
|
|
283
|
+
}
|
|
284
|
+
title={date.toLocaleDateString(locale)}
|
|
285
|
+
{...((month === 'current' || selectAdjacementDays) && {
|
|
286
|
+
onBlur: () => onCellMouseLeave(),
|
|
287
|
+
onClick: () => onCellClick(date),
|
|
288
|
+
onFocus: () => onCellMouseEnter(date),
|
|
289
|
+
onKeyDown: (event) => onCellKeyDown(event, date),
|
|
290
|
+
onMouseEnter: () => onCellMouseEnter(date),
|
|
291
|
+
onMouseLeave: () => onCellMouseLeave(),
|
|
292
|
+
})}
|
|
293
|
+
{...(month !== 'current' &&
|
|
294
|
+
!selectAdjacementDays && {
|
|
295
|
+
onMouseEnter: () => onCellMouseLeave(),
|
|
296
|
+
})}
|
|
297
|
+
>
|
|
298
|
+
<div className="calendar-cell-inner">
|
|
299
|
+
{typeof dayFormat === 'function'
|
|
300
|
+
? dayFormat(date)
|
|
301
|
+
: date.toLocaleDateString(locale, { day: dayFormat })}
|
|
302
|
+
</div>
|
|
303
|
+
</td>
|
|
304
|
+
) : (
|
|
305
|
+
<td key={idx}></td>
|
|
306
|
+
),
|
|
307
|
+
)}
|
|
277
308
|
</tr>
|
|
278
309
|
)
|
|
279
310
|
})}
|
|
@@ -284,9 +315,11 @@ const Calendar = (props: {
|
|
|
284
315
|
{row.map((month, idx) => {
|
|
285
316
|
return (
|
|
286
317
|
<td
|
|
287
|
-
key={idx}
|
|
288
318
|
className="calendar-cell month"
|
|
289
|
-
|
|
319
|
+
key={idx}
|
|
320
|
+
onClick={() => onMonthClick(index * 3 + idx - addMonths)}
|
|
321
|
+
onKeyDown={(event) => onMonthKeyDown(event, index * 3 + idx - addMonths)}
|
|
322
|
+
tabIndex={0}
|
|
290
323
|
>
|
|
291
324
|
<div className="calendar-cell-inner">{month}</div>
|
|
292
325
|
</td>
|
|
@@ -302,14 +335,18 @@ const Calendar = (props: {
|
|
|
302
335
|
{row.map((year, idx) => {
|
|
303
336
|
return (
|
|
304
337
|
<td
|
|
305
|
-
key={idx}
|
|
306
338
|
className="calendar-cell year"
|
|
339
|
+
key={idx}
|
|
307
340
|
onClick={() =>
|
|
308
|
-
onYearClick(
|
|
341
|
+
onYearClick(new Date(year, date.getMonth() - addMonths, date.getDate()))
|
|
342
|
+
}
|
|
343
|
+
onKeyDown={(event) =>
|
|
344
|
+
onYearKeyDown(
|
|
345
|
+
event,
|
|
309
346
|
new Date(year, date.getMonth() - addMonths, date.getDate()),
|
|
310
|
-
'months',
|
|
311
347
|
)
|
|
312
348
|
}
|
|
349
|
+
tabIndex={0}
|
|
313
350
|
>
|
|
314
351
|
<div className="calendar-cell-inner">
|
|
315
352
|
{new Date(year, 0, 1).toLocaleDateString(locale, { year: 'numeric' })}
|
|
@@ -451,7 +488,9 @@ export const CCalendar = forwardRef<HTMLDivElement, CCalendarProps>(
|
|
|
451
488
|
navPrevDoubleIcon,
|
|
452
489
|
navYearFirst,
|
|
453
490
|
range,
|
|
491
|
+
selectAdjacementDays = false,
|
|
454
492
|
selectEndDate,
|
|
493
|
+
showAdjacementDays = true,
|
|
455
494
|
weekdayFormat = 2,
|
|
456
495
|
onCalendarCellHover,
|
|
457
496
|
onCalendarDateChange,
|
|
@@ -551,11 +590,14 @@ export const CCalendar = forwardRef<HTMLDivElement, CCalendarProps>(
|
|
|
551
590
|
onCalendarDateChange && onCalendarDateChange(d)
|
|
552
591
|
}
|
|
553
592
|
|
|
554
|
-
const handleCellOnClick = (date: Date) => {
|
|
593
|
+
const handleCellOnClick = (date: Date, index?: number) => {
|
|
555
594
|
if (isDateDisabled(date, _minDate, _maxDate, disabledDates)) {
|
|
556
595
|
return
|
|
557
596
|
}
|
|
558
597
|
|
|
598
|
+
const _date = new Date(date)
|
|
599
|
+
setCalendarDate(index ? new Date(_date.setMonth(_date.getMonth() - index)) : _date)
|
|
600
|
+
|
|
559
601
|
if (range) {
|
|
560
602
|
if (_selectEndDate) {
|
|
561
603
|
setSelectEndDate(false)
|
|
@@ -600,6 +642,7 @@ export const CCalendar = forwardRef<HTMLDivElement, CCalendarProps>(
|
|
|
600
642
|
if (isDateDisabled(date, _minDate, _maxDate, disabledDates)) {
|
|
601
643
|
return
|
|
602
644
|
}
|
|
645
|
+
|
|
603
646
|
setHoverDate(date)
|
|
604
647
|
onCalendarCellHover && onCalendarCellHover(date)
|
|
605
648
|
}
|
|
@@ -609,14 +652,30 @@ export const CCalendar = forwardRef<HTMLDivElement, CCalendarProps>(
|
|
|
609
652
|
onCalendarCellHover && onCalendarCellHover(null)
|
|
610
653
|
}
|
|
611
654
|
|
|
612
|
-
const handleCellKeyDown = (
|
|
655
|
+
const handleCellKeyDown = (
|
|
656
|
+
event: React.KeyboardEvent<HTMLDivElement>,
|
|
657
|
+
date: Date,
|
|
658
|
+
index?: number,
|
|
659
|
+
) => {
|
|
613
660
|
if (event.code === 'Space' || event.key === 'Enter') {
|
|
614
661
|
event.preventDefault()
|
|
615
|
-
handleCellOnClick && handleCellOnClick(date)
|
|
662
|
+
handleCellOnClick && handleCellOnClick(date, index)
|
|
616
663
|
return
|
|
617
664
|
}
|
|
618
665
|
}
|
|
619
666
|
|
|
667
|
+
const handleMonthClick = (month: number) => {
|
|
668
|
+
setCalendarPage(0, 0, month)
|
|
669
|
+
setView('days')
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
const handleMonthKeyDown = (event: React.KeyboardEvent<HTMLDivElement>, month: number) => {
|
|
673
|
+
if (event.code === 'Space' || event.key === 'Enter') {
|
|
674
|
+
setCalendarPage(0, 0, month)
|
|
675
|
+
setView('days')
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
620
679
|
const handleNavigationOnClick = (direction: string, double = false) => {
|
|
621
680
|
if (direction === 'prev') {
|
|
622
681
|
if (double) {
|
|
@@ -648,6 +707,18 @@ export const CCalendar = forwardRef<HTMLDivElement, CCalendarProps>(
|
|
|
648
707
|
}
|
|
649
708
|
}
|
|
650
709
|
|
|
710
|
+
const handleYearClick = (date: Date) => {
|
|
711
|
+
setCalendarDate(date)
|
|
712
|
+
setView('months')
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
const handleYearKeyDown = (event: React.KeyboardEvent<HTMLDivElement>, date: Date) => {
|
|
716
|
+
if (event.code === 'Space' || event.key === 'Enter') {
|
|
717
|
+
setCalendarDate(date)
|
|
718
|
+
setView('months')
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
|
|
651
722
|
return (
|
|
652
723
|
<div className={classNames('calendars', className)}>
|
|
653
724
|
{_calendarDate &&
|
|
@@ -679,19 +750,17 @@ export const CCalendar = forwardRef<HTMLDivElement, CCalendarProps>(
|
|
|
679
750
|
locale={locale}
|
|
680
751
|
maxDate={_maxDate}
|
|
681
752
|
minDate={_minDate}
|
|
682
|
-
onCellClick={handleCellOnClick}
|
|
683
|
-
onCellKeyDown={handleCellKeyDown}
|
|
753
|
+
onCellClick={(date) => handleCellOnClick(date, index)}
|
|
754
|
+
onCellKeyDown={(event, date) => handleCellKeyDown(event, date, index)}
|
|
684
755
|
onCellMouseEnter={handleCellMouseEnter}
|
|
685
756
|
onCellMouseLeave={handleCellMouseLeave}
|
|
686
|
-
onMonthClick={
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
}
|
|
690
|
-
onYearClick={(date, view) => {
|
|
691
|
-
setCalendarDate(date)
|
|
692
|
-
setView(view)
|
|
693
|
-
}}
|
|
757
|
+
onMonthClick={handleMonthClick}
|
|
758
|
+
onMonthKeyDown={handleMonthKeyDown}
|
|
759
|
+
onYearClick={handleYearClick}
|
|
760
|
+
onYearKeyDown={handleYearKeyDown}
|
|
694
761
|
selectEndDate={_selectEndDate}
|
|
762
|
+
selectAdjacementDays={selectAdjacementDays}
|
|
763
|
+
showAdjacementDays={showAdjacementDays}
|
|
695
764
|
startDate={_startDate}
|
|
696
765
|
view={view}
|
|
697
766
|
weekdayFormat={weekdayFormat}
|
|
@@ -724,7 +793,9 @@ CCalendar.propTypes = {
|
|
|
724
793
|
navPrevDoubleIcon: PropTypes.node,
|
|
725
794
|
navYearFirst: PropTypes.bool,
|
|
726
795
|
range: PropTypes.bool,
|
|
796
|
+
selectAdjacementDays: PropTypes.bool,
|
|
727
797
|
selectEndDate: PropTypes.bool,
|
|
798
|
+
showAdjacementDays: PropTypes.bool,
|
|
728
799
|
startDate: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.string]),
|
|
729
800
|
weekdayFormat: PropTypes.oneOfType([
|
|
730
801
|
PropTypes.func,
|
|
@@ -167,6 +167,8 @@ export const CDateRangePicker = forwardRef<HTMLDivElement | HTMLLIElement, CDate
|
|
|
167
167
|
rangesButtonsVariant = 'ghost',
|
|
168
168
|
required,
|
|
169
169
|
separator = true,
|
|
170
|
+
selectAdjacementDays,
|
|
171
|
+
showAdjacementDays,
|
|
170
172
|
size,
|
|
171
173
|
startDate,
|
|
172
174
|
text,
|
|
@@ -491,7 +493,9 @@ export const CDateRangePicker = forwardRef<HTMLDivElement | HTMLLIElement, CDate
|
|
|
491
493
|
navigation={navigation}
|
|
492
494
|
navYearFirst={navYearFirst}
|
|
493
495
|
range={range}
|
|
496
|
+
selectAdjacementDays={selectAdjacementDays}
|
|
494
497
|
selectEndDate={selectEndDate}
|
|
498
|
+
showAdjacementDays={showAdjacementDays}
|
|
495
499
|
startDate={_startDate}
|
|
496
500
|
onCalendarCellHover={(date) => handleCalendarCellHover(date)}
|
|
497
501
|
onCalendarDateChange={(date) => handleCalendarDateChange(date)}
|
|
@@ -20,12 +20,18 @@ import { CMultiSelectNativeSelect } from './CMultiSelectNativeSelect'
|
|
|
20
20
|
import { CMultiSelectOptions } from './CMultiSelectOptions'
|
|
21
21
|
import { CMultiSelectSelection } from './CMultiSelectSelection'
|
|
22
22
|
|
|
23
|
-
import { filterOptionsList, flattenArray, selectOptions } from './utils'
|
|
23
|
+
import { createOption, filterOptionsList, flattenArray, selectOptions } from './utils'
|
|
24
24
|
import type { Option, SelectedOption } from './types'
|
|
25
25
|
|
|
26
26
|
export interface CMultiSelectProps
|
|
27
27
|
extends Omit<CFormControlWrapperProps, 'floatingClassName' | 'floatingLabel'>,
|
|
28
28
|
Omit<HTMLAttributes<HTMLDivElement>, 'onChange'> {
|
|
29
|
+
/**
|
|
30
|
+
* Allow users to create options if they are not in the list of options.
|
|
31
|
+
*
|
|
32
|
+
* @since 4.11.0-next.0
|
|
33
|
+
*/
|
|
34
|
+
allowCreateOptions?: boolean
|
|
29
35
|
/**
|
|
30
36
|
* A string of all className you want applied to the base component.
|
|
31
37
|
*/
|
|
@@ -34,10 +40,22 @@ export interface CMultiSelectProps
|
|
|
34
40
|
* Enables selection cleaner element.
|
|
35
41
|
*/
|
|
36
42
|
cleaner?: boolean
|
|
43
|
+
/**
|
|
44
|
+
* Clear current search on selecting an item.
|
|
45
|
+
*
|
|
46
|
+
* @since 4.11.0-next.0
|
|
47
|
+
*/
|
|
48
|
+
clearSearchOnSelect?: boolean
|
|
37
49
|
/**
|
|
38
50
|
* Toggle the disabled state for the component.
|
|
39
51
|
*/
|
|
40
52
|
disabled?: boolean
|
|
53
|
+
/**
|
|
54
|
+
* When set, the options list will have a loading style: loading spinner and reduced opacity.
|
|
55
|
+
*
|
|
56
|
+
* @since 4.11.0-next.0
|
|
57
|
+
*/
|
|
58
|
+
loading?: boolean
|
|
41
59
|
/**
|
|
42
60
|
* It specifies that multiple options can be selected at once.
|
|
43
61
|
*/
|
|
@@ -107,7 +125,6 @@ export interface CMultiSelectProps
|
|
|
107
125
|
*/
|
|
108
126
|
visible?: boolean
|
|
109
127
|
/**
|
|
110
|
-
*
|
|
111
128
|
* Amount of visible items when virtualScroller is set to `true`.
|
|
112
129
|
*
|
|
113
130
|
* @since 4.8.0
|
|
@@ -118,12 +135,15 @@ export interface CMultiSelectProps
|
|
|
118
135
|
export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
|
|
119
136
|
(
|
|
120
137
|
{
|
|
138
|
+
allowCreateOptions,
|
|
121
139
|
className,
|
|
122
140
|
cleaner = true,
|
|
141
|
+
clearSearchOnSelect,
|
|
123
142
|
disabled,
|
|
124
143
|
feedback,
|
|
125
144
|
feedbackInvalid,
|
|
126
145
|
feedbackValid,
|
|
146
|
+
loading,
|
|
127
147
|
multiple = true,
|
|
128
148
|
id,
|
|
129
149
|
invalid,
|
|
@@ -156,9 +176,32 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
|
|
|
156
176
|
const searchRef = useRef<HTMLInputElement>(null)
|
|
157
177
|
|
|
158
178
|
const [_options, setOptions] = useState<Option[]>(options)
|
|
159
|
-
const [_search, setSearch] = useState('')
|
|
160
179
|
const [_visible, setVisible] = useState(visible)
|
|
180
|
+
const [searchValue, setSearchValue] = useState('')
|
|
161
181
|
const [selected, setSelected] = useState<SelectedOption[]>([])
|
|
182
|
+
const [userOptions, setUserOptions] = useState<Option[]>([])
|
|
183
|
+
|
|
184
|
+
const filteredOptions = useMemo(
|
|
185
|
+
() =>
|
|
186
|
+
search === 'external'
|
|
187
|
+
? [..._options, ...filterOptionsList(searchValue, userOptions)]
|
|
188
|
+
: filterOptionsList(searchValue, [..._options, ...userOptions]),
|
|
189
|
+
[_options, searchValue, userOptions],
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
const userOption = useMemo(() => {
|
|
193
|
+
if (
|
|
194
|
+
allowCreateOptions &&
|
|
195
|
+
filteredOptions.length > 0 &&
|
|
196
|
+
flattenArray(filteredOptions).filter(
|
|
197
|
+
(option) => option.text.toLowerCase() === searchValue.toLowerCase(),
|
|
198
|
+
).length > 0
|
|
199
|
+
) {
|
|
200
|
+
return false
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return searchRef.current && createOption(String(searchValue), flattenArray(_options))
|
|
204
|
+
}, [filteredOptions, searchValue])
|
|
162
205
|
|
|
163
206
|
useMemo(() => {
|
|
164
207
|
setOptions(options)
|
|
@@ -185,6 +228,10 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
|
|
|
185
228
|
_selected && setSelected(selectOptions(_selected, selected, deselected))
|
|
186
229
|
}, [JSON.stringify(options)])
|
|
187
230
|
|
|
231
|
+
useEffect(() => {
|
|
232
|
+
!isInitialMount.current && onFilterChange && onFilterChange(searchValue)
|
|
233
|
+
}, [searchValue])
|
|
234
|
+
|
|
188
235
|
useEffect(() => {
|
|
189
236
|
!isInitialMount.current &&
|
|
190
237
|
nativeSelectRef.current &&
|
|
@@ -196,16 +243,37 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
|
|
|
196
243
|
}, [])
|
|
197
244
|
|
|
198
245
|
const handleSearchChange = (event: FormEvent<HTMLInputElement>) => {
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
setSearch(target.value.toLowerCase())
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
onFilterChange && onFilterChange(target.value)
|
|
246
|
+
const value = (event.target as HTMLInputElement).value
|
|
247
|
+
setSearchValue(value)
|
|
205
248
|
}
|
|
206
249
|
|
|
207
250
|
const handleSearchKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
|
|
208
|
-
if (
|
|
251
|
+
if (event.key === 'Enter' && searchValue && allowCreateOptions) {
|
|
252
|
+
event.preventDefault()
|
|
253
|
+
|
|
254
|
+
if (userOption) {
|
|
255
|
+
setSelected([...selected, ...userOption])
|
|
256
|
+
setUserOptions([...userOptions, ...userOption])
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (!userOption) {
|
|
260
|
+
setSelected([
|
|
261
|
+
...selected,
|
|
262
|
+
flattenArray(filteredOptions).filter(
|
|
263
|
+
(option) => String(option.text).toLowerCase() === searchValue.toLowerCase(),
|
|
264
|
+
)[0],
|
|
265
|
+
])
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
setSearchValue('')
|
|
269
|
+
if (searchRef.current) {
|
|
270
|
+
searchRef.current.value = ''
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (searchValue.length > 0) {
|
|
209
277
|
return
|
|
210
278
|
}
|
|
211
279
|
|
|
@@ -219,7 +287,7 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
|
|
|
219
287
|
if (!multiple) {
|
|
220
288
|
setSelected([{ value: option.value, text: option.text }] as SelectedOption[])
|
|
221
289
|
setVisible(false)
|
|
222
|
-
|
|
290
|
+
setSearchValue('')
|
|
223
291
|
if (searchRef.current) {
|
|
224
292
|
searchRef.current.value = ''
|
|
225
293
|
}
|
|
@@ -227,6 +295,18 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
|
|
|
227
295
|
return
|
|
228
296
|
}
|
|
229
297
|
|
|
298
|
+
if (option.custom && !userOptions.some((_option) => _option.value === option.value)) {
|
|
299
|
+
setUserOptions([...userOptions, option])
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (clearSearchOnSelect || option.custom) {
|
|
303
|
+
setSearchValue('')
|
|
304
|
+
if (searchRef.current) {
|
|
305
|
+
searchRef.current.value = ''
|
|
306
|
+
searchRef.current.focus()
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
230
310
|
if (selected.some((_option) => _option.value === option.value)) {
|
|
231
311
|
setSelected(selected.filter((_option) => _option.value !== option.value))
|
|
232
312
|
} else {
|
|
@@ -237,7 +317,7 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
|
|
|
237
317
|
const handleSelectAll = () => {
|
|
238
318
|
setSelected(
|
|
239
319
|
selectOptions(
|
|
240
|
-
flattenArray(options).filter((option: Option) => !option.disabled),
|
|
320
|
+
[...flattenArray(options).filter((option: Option) => !option.disabled), ...userOptions],
|
|
241
321
|
selected,
|
|
242
322
|
),
|
|
243
323
|
)
|
|
@@ -325,7 +405,7 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
|
|
|
325
405
|
!multiple && { placeholder: selected.map((option) => option.text)[0] })}
|
|
326
406
|
{...(multiple &&
|
|
327
407
|
selected.length > 0 &&
|
|
328
|
-
selectionType !== 'counter' && { size:
|
|
408
|
+
selectionType !== 'counter' && { size: searchValue.length + 2 })}
|
|
329
409
|
ref={searchRef}
|
|
330
410
|
></input>
|
|
331
411
|
)}
|
|
@@ -346,7 +426,12 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
|
|
|
346
426
|
)}
|
|
347
427
|
<CMultiSelectOptions
|
|
348
428
|
handleOptionOnClick={(option) => !disabled && handleOptionOnClick(option)}
|
|
349
|
-
|
|
429
|
+
loading={loading}
|
|
430
|
+
options={
|
|
431
|
+
filteredOptions.length === 0 && allowCreateOptions
|
|
432
|
+
? userOption || []
|
|
433
|
+
: filteredOptions
|
|
434
|
+
}
|
|
350
435
|
optionsMaxHeight={optionsMaxHeight}
|
|
351
436
|
optionsStyle={optionsStyle}
|
|
352
437
|
searchNoResultsLabel={searchNoResultsLabel}
|
|
@@ -364,7 +449,9 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
|
|
|
364
449
|
CMultiSelect.propTypes = {
|
|
365
450
|
className: PropTypes.string,
|
|
366
451
|
cleaner: PropTypes.bool,
|
|
452
|
+
clearSearchOnSelect: PropTypes.bool,
|
|
367
453
|
disabled: PropTypes.bool,
|
|
454
|
+
loading: PropTypes.bool,
|
|
368
455
|
multiple: PropTypes.bool,
|
|
369
456
|
onChange: PropTypes.func,
|
|
370
457
|
options: PropTypes.array.isRequired,
|
|
@@ -6,9 +6,11 @@ import { CVirtualScroller } from './../virtual-scroller'
|
|
|
6
6
|
|
|
7
7
|
import { getNextSibling, getPreviousSibling } from './utils'
|
|
8
8
|
import type { Option } from './types'
|
|
9
|
+
import { CElementCover } from '../element-cover'
|
|
9
10
|
|
|
10
11
|
export interface CMultiSelectOptionsProps extends HTMLAttributes<HTMLDivElement> {
|
|
11
12
|
handleOptionOnClick?: (option: Option) => void
|
|
13
|
+
loading?: boolean
|
|
12
14
|
options: Option[]
|
|
13
15
|
optionsMaxHeight?: number | string
|
|
14
16
|
optionsStyle?: 'checkbox' | 'text'
|
|
@@ -22,6 +24,7 @@ export const CMultiSelectOptions = forwardRef<HTMLDivElement, CMultiSelectOption
|
|
|
22
24
|
(
|
|
23
25
|
{
|
|
24
26
|
handleOptionOnClick,
|
|
27
|
+
loading,
|
|
25
28
|
options,
|
|
26
29
|
optionsMaxHeight,
|
|
27
30
|
optionsStyle,
|
|
@@ -44,7 +47,7 @@ export const CMultiSelectOptions = forwardRef<HTMLDivElement, CMultiSelectOption
|
|
|
44
47
|
const target = event.target as HTMLElement
|
|
45
48
|
const next = getNextSibling(target, '.form-multi-select-option')
|
|
46
49
|
if (next) {
|
|
47
|
-
(next as HTMLElement).focus() // eslint-disable-line prettier/prettier
|
|
50
|
+
;(next as HTMLElement).focus() // eslint-disable-line prettier/prettier
|
|
48
51
|
}
|
|
49
52
|
}
|
|
50
53
|
|
|
@@ -53,7 +56,7 @@ export const CMultiSelectOptions = forwardRef<HTMLDivElement, CMultiSelectOption
|
|
|
53
56
|
const target = event.target as HTMLElement
|
|
54
57
|
const prev = getPreviousSibling(target, '.form-multi-select-option')
|
|
55
58
|
if (prev) {
|
|
56
|
-
(prev as HTMLElement).focus() // eslint-disable-line prettier/prettier
|
|
59
|
+
;(prev as HTMLElement).focus() // eslint-disable-line prettier/prettier
|
|
57
60
|
}
|
|
58
61
|
}
|
|
59
62
|
}
|
|
@@ -86,26 +89,36 @@ export const CMultiSelectOptions = forwardRef<HTMLDivElement, CMultiSelectOption
|
|
|
86
89
|
<div className="form-multi-select-options-empty">{searchNoResultsLabel}</div>
|
|
87
90
|
)
|
|
88
91
|
|
|
89
|
-
return
|
|
90
|
-
|
|
91
|
-
{
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
92
|
+
return (
|
|
93
|
+
<>
|
|
94
|
+
{virtualScroller ? (
|
|
95
|
+
<CVirtualScroller
|
|
96
|
+
className="form-multi-select-options"
|
|
97
|
+
visibleItems={visibleItems}
|
|
98
|
+
ref={ref}
|
|
99
|
+
>
|
|
100
|
+
{createOptions(options)}
|
|
101
|
+
</CVirtualScroller>
|
|
102
|
+
) : (
|
|
103
|
+
<div
|
|
104
|
+
className="form-multi-select-options"
|
|
105
|
+
{...(optionsMaxHeight !== 'auto' && {
|
|
106
|
+
style: { maxHeight: optionsMaxHeight, overflow: 'scroll' },
|
|
107
|
+
})}
|
|
108
|
+
ref={ref}
|
|
109
|
+
>
|
|
110
|
+
{createOptions(options)}
|
|
111
|
+
</div>
|
|
112
|
+
)}
|
|
113
|
+
{loading && <CElementCover />}
|
|
114
|
+
</>
|
|
103
115
|
)
|
|
104
116
|
},
|
|
105
117
|
)
|
|
106
118
|
|
|
107
119
|
CMultiSelectOptions.propTypes = {
|
|
108
120
|
handleOptionOnClick: PropTypes.func,
|
|
121
|
+
loading: PropTypes.bool,
|
|
109
122
|
options: PropTypes.array.isRequired,
|
|
110
123
|
optionsMaxHeight: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
|
111
124
|
optionsStyle: PropTypes.oneOf(['checkbox', 'text']),
|