@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coreui/react-pro",
3
- "version": "4.10.0",
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.0",
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, view: string) => void
166
- onYearClick: (date: Date, view: string) => void
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
- <td
240
- className={classNames('calendar-cell', {
241
- today: month === 'current' && isToday(date),
242
- disabled: isDateDisabled(date, minDate, maxDate, disabledDates),
243
- [month]: true,
244
- last: isLastDayOfMonth(date),
245
- 'range-hover':
246
- month === 'current' &&
247
- (hoverDate && selectEndDate
248
- ? isDateInRange(date, startDate, hoverDate)
249
- : isDateInRange(date, hoverDate, endDate)),
250
- range: month === 'current' && isDateInRange(date, startDate, endDate),
251
- selected: isDateSelected(date, startDate, endDate),
252
- start: isStartDate(date, startDate, endDate),
253
- end: isEndDate(date, startDate, endDate),
254
- })}
255
- key={idx}
256
- tabIndex={0}
257
- title={date.toLocaleDateString(locale)}
258
- {...(month === 'current' && {
259
- onBlur: () => onCellMouseLeave(),
260
- onClick: () => onCellClick(date),
261
- onFocus: () => onCellMouseEnter(date),
262
- onKeyDown: (event) => onCellKeyDown(event, date),
263
- onMouseEnter: () => onCellMouseEnter(date),
264
- onMouseLeave: () => onCellMouseLeave(),
265
- })}
266
- {...(month !== 'current' && {
267
- onMouseEnter: () => onCellMouseLeave(),
268
- })}
269
- >
270
- <div className="calendar-cell-inner">
271
- {typeof dayFormat === 'function'
272
- ? dayFormat(date)
273
- : date.toLocaleDateString(locale, { day: dayFormat })}
274
- </div>
275
- </td>
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
- onClick={() => onMonthClick(index * 3 + idx - addMonths, 'days')}
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 = (event: React.KeyboardEvent<HTMLDivElement>, date: Date) => {
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={(month, view) => {
687
- setCalendarPage(0, 0, month)
688
- setView(view)
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,
@@ -164,6 +164,7 @@ export const isDateDisabled = (
164
164
  if (max && date > max) {
165
165
  disabled = true
166
166
  }
167
+
167
168
  return disabled
168
169
  }
169
170
 
@@ -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 target = event.target as HTMLInputElement
200
- if (search !== 'external') {
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 (_search.length > 0) {
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
- setSearch('')
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: _search.length + 2 })}
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
- options={search === 'external' ? _options : filterOptionsList(_search, _options)}
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 virtualScroller ? (
90
- <CVirtualScroller className="form-multi-select-options" visibleItems={visibleItems} ref={ref}>
91
- {createOptions(options)}
92
- </CVirtualScroller>
93
- ) : (
94
- <div
95
- className="form-multi-select-options"
96
- {...(optionsMaxHeight !== 'auto' && {
97
- style: { maxHeight: optionsMaxHeight, overflow: 'scroll' },
98
- })}
99
- ref={ref}
100
- >
101
- {createOptions(options)}
102
- </div>
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']),
@@ -1,4 +1,5 @@
1
1
  export type Option = {
2
+ custom?: boolean
2
3
  disabled?: boolean
3
4
  label?: string
4
5
  options?: Option[]