@coreui/react-pro 4.9.0 → 4.11.0-next.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/components/calendar/CCalendar.d.ts +12 -0
- package/dist/components/date-range-picker/CDateRangePicker.d.ts +6 -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/components/time-picker/CTimePicker.d.ts +6 -0
- package/dist/index.es.js +126 -61
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +126 -61
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/components/calendar/CCalendar.tsx +79 -44
- package/src/components/calendar/utils.ts +1 -0
- package/src/components/date-range-picker/CDateRangePicker.tsx +14 -0
- package/src/components/dropdown/CDropdown.tsx +33 -30
- package/src/components/multi-select/CMultiSelect.tsx +99 -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/time-picker/CTimePicker.tsx +9 -0
- package/src/components/virtual-scroller/CVirtualScroller.tsx +8 -7
- package/dist/components/Types.d.ts +0 -14
- package/dist/components/multi-select/CMultiSelectCleaner.d.ts +0 -2
- package/dist/components/multi-select/CMultiSelectSearchInput.d.ts +0 -5
- package/dist/components/pagination/CSmartPagination.d.ts +0 -80
- package/dist/components/time-picker/CTimePicker copy.d.ts +0 -46
- package/dist/utils/calendar.d.ts +0 -23
- package/dist/utils/getNextSibling.d.ts +0 -2
- package/dist/utils/getPreviousSibling.d.ts +0 -2
- package/dist/utils/hooks/index.d.ts +0 -3
- package/dist/utils/hooks/useForkedRef.d.ts +0 -7
- package/dist/utils/hooks/useStateWithCallback.d.ts +0 -2
- package/dist/utils/isVisible.d.ts +0 -2
- package/dist/utils/time.d.ts +0 -21
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.0",
|
|
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
|
*/
|
|
@@ -164,7 +176,9 @@ const Calendar = (props: {
|
|
|
164
176
|
onCellMouseLeave: () => void
|
|
165
177
|
onMonthClick: (month: number, view: string) => void
|
|
166
178
|
onYearClick: (date: Date, view: string) => void
|
|
179
|
+
selectAdjacementDays: boolean
|
|
167
180
|
selectEndDate: boolean | undefined
|
|
181
|
+
showAdjacementDays: boolean
|
|
168
182
|
startDate: Date | null
|
|
169
183
|
view: string
|
|
170
184
|
weekdayFormat: number | 'long' | 'narrow' | 'short' | ((date: Date) => string | number)
|
|
@@ -186,7 +200,9 @@ const Calendar = (props: {
|
|
|
186
200
|
onCellMouseLeave,
|
|
187
201
|
onMonthClick,
|
|
188
202
|
onYearClick,
|
|
203
|
+
selectAdjacementDays,
|
|
189
204
|
selectEndDate,
|
|
205
|
+
showAdjacementDays,
|
|
190
206
|
startDate,
|
|
191
207
|
view,
|
|
192
208
|
weekdayFormat,
|
|
@@ -235,45 +251,50 @@ const Calendar = (props: {
|
|
|
235
251
|
monthDetails.map((week, index) => {
|
|
236
252
|
return (
|
|
237
253
|
<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
|
-
|
|
254
|
+
{week.map(({ date, month }: { date: Date; month: string }, idx: number) =>
|
|
255
|
+
month === 'current' || showAdjacementDays ? (
|
|
256
|
+
<td
|
|
257
|
+
className={classNames('calendar-cell', {
|
|
258
|
+
today: month === 'current' && isToday(date),
|
|
259
|
+
disabled: isDateDisabled(date, minDate, maxDate, disabledDates),
|
|
260
|
+
[month]: true,
|
|
261
|
+
last: isLastDayOfMonth(date),
|
|
262
|
+
'range-hover':
|
|
263
|
+
month === 'current' &&
|
|
264
|
+
(hoverDate && selectEndDate
|
|
265
|
+
? isDateInRange(date, startDate, hoverDate)
|
|
266
|
+
: isDateInRange(date, hoverDate, endDate)),
|
|
267
|
+
range: month === 'current' && isDateInRange(date, startDate, endDate),
|
|
268
|
+
selected: isDateSelected(date, startDate, endDate),
|
|
269
|
+
start: isStartDate(date, startDate, endDate),
|
|
270
|
+
end: isEndDate(date, startDate, endDate),
|
|
271
|
+
})}
|
|
272
|
+
key={idx}
|
|
273
|
+
tabIndex={0}
|
|
274
|
+
title={date.toLocaleDateString(locale)}
|
|
275
|
+
{...((month === 'current' || selectAdjacementDays) && {
|
|
276
|
+
onBlur: () => onCellMouseLeave(),
|
|
277
|
+
onClick: () => onCellClick(date),
|
|
278
|
+
onFocus: () => onCellMouseEnter(date),
|
|
279
|
+
onKeyDown: (event) => onCellKeyDown(event, date),
|
|
280
|
+
onMouseEnter: () => onCellMouseEnter(date),
|
|
281
|
+
onMouseLeave: () => onCellMouseLeave(),
|
|
282
|
+
})}
|
|
283
|
+
{...(month !== 'current' &&
|
|
284
|
+
!selectAdjacementDays && {
|
|
285
|
+
onMouseEnter: () => onCellMouseLeave(),
|
|
286
|
+
})}
|
|
287
|
+
>
|
|
288
|
+
<div className="calendar-cell-inner">
|
|
289
|
+
{typeof dayFormat === 'function'
|
|
290
|
+
? dayFormat(date)
|
|
291
|
+
: date.toLocaleDateString(locale, { day: dayFormat })}
|
|
292
|
+
</div>
|
|
293
|
+
</td>
|
|
294
|
+
) : (
|
|
295
|
+
<td key={idx}></td>
|
|
296
|
+
),
|
|
297
|
+
)}
|
|
277
298
|
</tr>
|
|
278
299
|
)
|
|
279
300
|
})}
|
|
@@ -451,7 +472,9 @@ export const CCalendar = forwardRef<HTMLDivElement, CCalendarProps>(
|
|
|
451
472
|
navPrevDoubleIcon,
|
|
452
473
|
navYearFirst,
|
|
453
474
|
range,
|
|
475
|
+
selectAdjacementDays = false,
|
|
454
476
|
selectEndDate,
|
|
477
|
+
showAdjacementDays = true,
|
|
455
478
|
weekdayFormat = 2,
|
|
456
479
|
onCalendarCellHover,
|
|
457
480
|
onCalendarDateChange,
|
|
@@ -551,11 +574,14 @@ export const CCalendar = forwardRef<HTMLDivElement, CCalendarProps>(
|
|
|
551
574
|
onCalendarDateChange && onCalendarDateChange(d)
|
|
552
575
|
}
|
|
553
576
|
|
|
554
|
-
const handleCellOnClick = (date: Date) => {
|
|
577
|
+
const handleCellOnClick = (date: Date, index?: number) => {
|
|
555
578
|
if (isDateDisabled(date, _minDate, _maxDate, disabledDates)) {
|
|
556
579
|
return
|
|
557
580
|
}
|
|
558
581
|
|
|
582
|
+
const _date = new Date(date)
|
|
583
|
+
setCalendarDate(index ? new Date(_date.setMonth(_date.getMonth() - index)) : _date)
|
|
584
|
+
|
|
559
585
|
if (range) {
|
|
560
586
|
if (_selectEndDate) {
|
|
561
587
|
setSelectEndDate(false)
|
|
@@ -600,6 +626,7 @@ export const CCalendar = forwardRef<HTMLDivElement, CCalendarProps>(
|
|
|
600
626
|
if (isDateDisabled(date, _minDate, _maxDate, disabledDates)) {
|
|
601
627
|
return
|
|
602
628
|
}
|
|
629
|
+
|
|
603
630
|
setHoverDate(date)
|
|
604
631
|
onCalendarCellHover && onCalendarCellHover(date)
|
|
605
632
|
}
|
|
@@ -609,10 +636,14 @@ export const CCalendar = forwardRef<HTMLDivElement, CCalendarProps>(
|
|
|
609
636
|
onCalendarCellHover && onCalendarCellHover(null)
|
|
610
637
|
}
|
|
611
638
|
|
|
612
|
-
const handleCellKeyDown = (
|
|
639
|
+
const handleCellKeyDown = (
|
|
640
|
+
event: React.KeyboardEvent<HTMLDivElement>,
|
|
641
|
+
date: Date,
|
|
642
|
+
index?: number,
|
|
643
|
+
) => {
|
|
613
644
|
if (event.code === 'Space' || event.key === 'Enter') {
|
|
614
645
|
event.preventDefault()
|
|
615
|
-
handleCellOnClick && handleCellOnClick(date)
|
|
646
|
+
handleCellOnClick && handleCellOnClick(date, index)
|
|
616
647
|
return
|
|
617
648
|
}
|
|
618
649
|
}
|
|
@@ -679,8 +710,8 @@ export const CCalendar = forwardRef<HTMLDivElement, CCalendarProps>(
|
|
|
679
710
|
locale={locale}
|
|
680
711
|
maxDate={_maxDate}
|
|
681
712
|
minDate={_minDate}
|
|
682
|
-
onCellClick={handleCellOnClick}
|
|
683
|
-
onCellKeyDown={handleCellKeyDown}
|
|
713
|
+
onCellClick={(date) => handleCellOnClick(date, index)}
|
|
714
|
+
onCellKeyDown={(event, date) => handleCellKeyDown(event, date, index)}
|
|
684
715
|
onCellMouseEnter={handleCellMouseEnter}
|
|
685
716
|
onCellMouseLeave={handleCellMouseLeave}
|
|
686
717
|
onMonthClick={(month, view) => {
|
|
@@ -692,6 +723,8 @@ export const CCalendar = forwardRef<HTMLDivElement, CCalendarProps>(
|
|
|
692
723
|
setView(view)
|
|
693
724
|
}}
|
|
694
725
|
selectEndDate={_selectEndDate}
|
|
726
|
+
selectAdjacementDays={selectAdjacementDays}
|
|
727
|
+
showAdjacementDays={showAdjacementDays}
|
|
695
728
|
startDate={_startDate}
|
|
696
729
|
view={view}
|
|
697
730
|
weekdayFormat={weekdayFormat}
|
|
@@ -724,7 +757,9 @@ CCalendar.propTypes = {
|
|
|
724
757
|
navPrevDoubleIcon: PropTypes.node,
|
|
725
758
|
navYearFirst: PropTypes.bool,
|
|
726
759
|
range: PropTypes.bool,
|
|
760
|
+
selectAdjacementDays: PropTypes.bool,
|
|
727
761
|
selectEndDate: PropTypes.bool,
|
|
762
|
+
showAdjacementDays: PropTypes.bool,
|
|
728
763
|
startDate: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.string]),
|
|
729
764
|
weekdayFormat: PropTypes.oneOfType([
|
|
730
765
|
PropTypes.func,
|
|
@@ -84,6 +84,12 @@ export interface CDateRangePickerProps
|
|
|
84
84
|
* Set the ranges button variant to an outlined button or a ghost button.
|
|
85
85
|
*/
|
|
86
86
|
rangesButtonsVariant?: 'outline' | 'ghost'
|
|
87
|
+
/**
|
|
88
|
+
* When present, it specifies that date must be filled out before submitting the form.
|
|
89
|
+
*
|
|
90
|
+
* @since 4.10.0
|
|
91
|
+
*/
|
|
92
|
+
required?: boolean
|
|
87
93
|
/**
|
|
88
94
|
* Default icon or character character that separates two dates.
|
|
89
95
|
*/
|
|
@@ -159,7 +165,10 @@ export const CDateRangePicker = forwardRef<HTMLDivElement | HTMLLIElement, CDate
|
|
|
159
165
|
rangesButtonsColor = 'secondary',
|
|
160
166
|
rangesButtonsSize,
|
|
161
167
|
rangesButtonsVariant = 'ghost',
|
|
168
|
+
required,
|
|
162
169
|
separator = true,
|
|
170
|
+
selectAdjacementDays,
|
|
171
|
+
showAdjacementDays,
|
|
163
172
|
size,
|
|
164
173
|
startDate,
|
|
165
174
|
text,
|
|
@@ -316,6 +325,7 @@ export const CDateRangePicker = forwardRef<HTMLDivElement | HTMLLIElement, CDate
|
|
|
316
325
|
{...(id && { name: range ? `${id}-start-date` : `${id}-date` })}
|
|
317
326
|
placeholder={Array.isArray(placeholder) ? placeholder[0] : placeholder}
|
|
318
327
|
readOnly={inputReadOnly || typeof format === 'string'}
|
|
328
|
+
required={required}
|
|
319
329
|
onChange={(event) => {
|
|
320
330
|
const date = getLocalDateFromString(event.target.value, locale, timepicker)
|
|
321
331
|
if (date instanceof Date && date.getTime()) {
|
|
@@ -346,6 +356,7 @@ export const CDateRangePicker = forwardRef<HTMLDivElement | HTMLLIElement, CDate
|
|
|
346
356
|
{...(id && { name: `${id}-end-date` })}
|
|
347
357
|
placeholder={placeholder[1]}
|
|
348
358
|
readOnly={inputReadOnly || typeof format === 'string'}
|
|
359
|
+
required={required}
|
|
349
360
|
onChange={(event) => {
|
|
350
361
|
const date = getLocalDateFromString(event.target.value, locale, timepicker)
|
|
351
362
|
if (date instanceof Date && date.getTime()) {
|
|
@@ -482,7 +493,9 @@ export const CDateRangePicker = forwardRef<HTMLDivElement | HTMLLIElement, CDate
|
|
|
482
493
|
navigation={navigation}
|
|
483
494
|
navYearFirst={navYearFirst}
|
|
484
495
|
range={range}
|
|
496
|
+
selectAdjacementDays={selectAdjacementDays}
|
|
485
497
|
selectEndDate={selectEndDate}
|
|
498
|
+
showAdjacementDays={showAdjacementDays}
|
|
486
499
|
startDate={_startDate}
|
|
487
500
|
onCalendarCellHover={(date) => handleCalendarCellHover(date)}
|
|
488
501
|
onCalendarDateChange={(date) => handleCalendarDateChange(date)}
|
|
@@ -565,6 +578,7 @@ CDateRangePicker.propTypes = {
|
|
|
565
578
|
rangesButtonsColor: CButton.propTypes?.color,
|
|
566
579
|
rangesButtonsSize: CButton.propTypes?.size,
|
|
567
580
|
rangesButtonsVariant: CButton.propTypes?.variant,
|
|
581
|
+
required: PropTypes.bool,
|
|
568
582
|
separator: PropTypes.oneOfType([PropTypes.bool, PropTypes.node]),
|
|
569
583
|
size: PropTypes.oneOf(['sm', 'lg']),
|
|
570
584
|
timepicker: PropTypes.bool,
|
|
@@ -3,6 +3,7 @@ import React, {
|
|
|
3
3
|
ElementType,
|
|
4
4
|
forwardRef,
|
|
5
5
|
HTMLAttributes,
|
|
6
|
+
ReactNode,
|
|
6
7
|
RefObject,
|
|
7
8
|
useEffect,
|
|
8
9
|
useRef,
|
|
@@ -87,6 +88,16 @@ export interface CDropdownProps extends HTMLAttributes<HTMLDivElement | HTMLLIEl
|
|
|
87
88
|
visible?: boolean
|
|
88
89
|
}
|
|
89
90
|
|
|
91
|
+
const PopperManagerWrapper = ({
|
|
92
|
+
children,
|
|
93
|
+
popper,
|
|
94
|
+
}: {
|
|
95
|
+
children: ReactNode
|
|
96
|
+
popper: boolean
|
|
97
|
+
}) => {
|
|
98
|
+
return popper ? <Manager>{children}</Manager> : <>{children}</>
|
|
99
|
+
}
|
|
100
|
+
|
|
90
101
|
interface ContextProps extends CDropdownProps {
|
|
91
102
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
92
103
|
dropdownToggleRef: RefObject<any> | undefined
|
|
@@ -149,38 +160,30 @@ export const CDropdown = forwardRef<HTMLDivElement | HTMLLIElement, CDropdownPro
|
|
|
149
160
|
!_visible && onHide && onHide()
|
|
150
161
|
}, [_visible])
|
|
151
162
|
|
|
152
|
-
const DropdownContent = () => {
|
|
153
|
-
return variant === 'input-group' ? (
|
|
154
|
-
<>{children}</>
|
|
155
|
-
) : (
|
|
156
|
-
<Component
|
|
157
|
-
className={classNames(
|
|
158
|
-
variant === 'nav-item' ? 'nav-item dropdown' : variant,
|
|
159
|
-
{
|
|
160
|
-
'dropdown-center': direction === 'center',
|
|
161
|
-
'dropup dropup-center': direction === 'dropup-center',
|
|
162
|
-
direction: direction && direction !== 'center' && direction !== 'dropup-center',
|
|
163
|
-
show: _visible,
|
|
164
|
-
},
|
|
165
|
-
className,
|
|
166
|
-
)}
|
|
167
|
-
{...rest}
|
|
168
|
-
ref={forkedRef}
|
|
169
|
-
>
|
|
170
|
-
{children}
|
|
171
|
-
</Component>
|
|
172
|
-
)
|
|
173
|
-
}
|
|
174
|
-
|
|
175
163
|
return (
|
|
176
164
|
<CDropdownContext.Provider value={contextValues}>
|
|
177
|
-
{popper
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
165
|
+
<PopperManagerWrapper popper={popper}>
|
|
166
|
+
{variant === 'input-group' ? (
|
|
167
|
+
<>{children}</>
|
|
168
|
+
) : (
|
|
169
|
+
<Component
|
|
170
|
+
className={classNames(
|
|
171
|
+
variant === 'nav-item' ? 'nav-item dropdown' : variant,
|
|
172
|
+
{
|
|
173
|
+
'dropdown-center': direction === 'center',
|
|
174
|
+
'dropup dropup-center': direction === 'dropup-center',
|
|
175
|
+
direction: direction && direction !== 'center' && direction !== 'dropup-center',
|
|
176
|
+
show: _visible,
|
|
177
|
+
},
|
|
178
|
+
className,
|
|
179
|
+
)}
|
|
180
|
+
{...rest}
|
|
181
|
+
ref={forkedRef}
|
|
182
|
+
>
|
|
183
|
+
{children}
|
|
184
|
+
</Component>
|
|
185
|
+
)}
|
|
186
|
+
</PopperManagerWrapper>
|
|
184
187
|
</CDropdownContext.Provider>
|
|
185
188
|
)
|
|
186
189
|
},
|
|
@@ -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,35 @@ 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
|
+
if (userOption) {
|
|
253
|
+
setSelected([...selected, ...userOption])
|
|
254
|
+
setUserOptions([...userOptions, ...userOption])
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (!userOption) {
|
|
258
|
+
setSelected([
|
|
259
|
+
...selected,
|
|
260
|
+
flattenArray(filteredOptions).filter(
|
|
261
|
+
(option) => String(option.text).toLowerCase() === searchValue.toLowerCase(),
|
|
262
|
+
)[0],
|
|
263
|
+
])
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
setSearchValue('')
|
|
267
|
+
if (searchRef.current) {
|
|
268
|
+
searchRef.current.value = ''
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (searchValue.length > 0) {
|
|
209
275
|
return
|
|
210
276
|
}
|
|
211
277
|
|
|
@@ -219,7 +285,7 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
|
|
|
219
285
|
if (!multiple) {
|
|
220
286
|
setSelected([{ value: option.value, text: option.text }] as SelectedOption[])
|
|
221
287
|
setVisible(false)
|
|
222
|
-
|
|
288
|
+
setSearchValue('')
|
|
223
289
|
if (searchRef.current) {
|
|
224
290
|
searchRef.current.value = ''
|
|
225
291
|
}
|
|
@@ -227,6 +293,18 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
|
|
|
227
293
|
return
|
|
228
294
|
}
|
|
229
295
|
|
|
296
|
+
if (option.custom && !userOptions.some((_option) => _option.value === option.value)) {
|
|
297
|
+
setUserOptions([...userOptions, option])
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (clearSearchOnSelect || option.custom) {
|
|
301
|
+
setSearchValue('')
|
|
302
|
+
if (searchRef.current) {
|
|
303
|
+
searchRef.current.value = ''
|
|
304
|
+
searchRef.current.focus()
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
230
308
|
if (selected.some((_option) => _option.value === option.value)) {
|
|
231
309
|
setSelected(selected.filter((_option) => _option.value !== option.value))
|
|
232
310
|
} else {
|
|
@@ -237,7 +315,7 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
|
|
|
237
315
|
const handleSelectAll = () => {
|
|
238
316
|
setSelected(
|
|
239
317
|
selectOptions(
|
|
240
|
-
flattenArray(options).filter((option: Option) => !option.disabled),
|
|
318
|
+
[...flattenArray(options).filter((option: Option) => !option.disabled), ...userOptions],
|
|
241
319
|
selected,
|
|
242
320
|
),
|
|
243
321
|
)
|
|
@@ -325,7 +403,7 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
|
|
|
325
403
|
!multiple && { placeholder: selected.map((option) => option.text)[0] })}
|
|
326
404
|
{...(multiple &&
|
|
327
405
|
selected.length > 0 &&
|
|
328
|
-
selectionType !== 'counter' && { size:
|
|
406
|
+
selectionType !== 'counter' && { size: searchValue.length + 2 })}
|
|
329
407
|
ref={searchRef}
|
|
330
408
|
></input>
|
|
331
409
|
)}
|
|
@@ -346,7 +424,12 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
|
|
|
346
424
|
)}
|
|
347
425
|
<CMultiSelectOptions
|
|
348
426
|
handleOptionOnClick={(option) => !disabled && handleOptionOnClick(option)}
|
|
349
|
-
|
|
427
|
+
loading={loading}
|
|
428
|
+
options={
|
|
429
|
+
filteredOptions.length === 0 && allowCreateOptions
|
|
430
|
+
? userOption || []
|
|
431
|
+
: filteredOptions
|
|
432
|
+
}
|
|
350
433
|
optionsMaxHeight={optionsMaxHeight}
|
|
351
434
|
optionsStyle={optionsStyle}
|
|
352
435
|
searchNoResultsLabel={searchNoResultsLabel}
|
|
@@ -364,7 +447,9 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
|
|
|
364
447
|
CMultiSelect.propTypes = {
|
|
365
448
|
className: PropTypes.string,
|
|
366
449
|
cleaner: PropTypes.bool,
|
|
450
|
+
clearSearchOnSelect: PropTypes.bool,
|
|
367
451
|
disabled: PropTypes.bool,
|
|
452
|
+
loading: PropTypes.bool,
|
|
368
453
|
multiple: PropTypes.bool,
|
|
369
454
|
onChange: PropTypes.func,
|
|
370
455
|
options: PropTypes.array.isRequired,
|