@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.
Files changed (38) hide show
  1. package/README.md +1 -1
  2. package/dist/components/calendar/CCalendar.d.ts +12 -0
  3. package/dist/components/date-range-picker/CDateRangePicker.d.ts +6 -0
  4. package/dist/components/multi-select/CMultiSelect.d.ts +18 -1
  5. package/dist/components/multi-select/CMultiSelectOptions.d.ts +1 -0
  6. package/dist/components/multi-select/types.d.ts +1 -0
  7. package/dist/components/multi-select/utils.d.ts +5 -0
  8. package/dist/components/smart-table/CSmartTableInterface.d.ts +1 -1
  9. package/dist/components/time-picker/CTimePicker.d.ts +6 -0
  10. package/dist/index.es.js +126 -61
  11. package/dist/index.es.js.map +1 -1
  12. package/dist/index.js +126 -61
  13. package/dist/index.js.map +1 -1
  14. package/package.json +2 -2
  15. package/src/components/calendar/CCalendar.tsx +79 -44
  16. package/src/components/calendar/utils.ts +1 -0
  17. package/src/components/date-range-picker/CDateRangePicker.tsx +14 -0
  18. package/src/components/dropdown/CDropdown.tsx +33 -30
  19. package/src/components/multi-select/CMultiSelect.tsx +99 -14
  20. package/src/components/multi-select/CMultiSelectOptions.tsx +29 -16
  21. package/src/components/multi-select/types.ts +1 -0
  22. package/src/components/multi-select/utils.ts +42 -25
  23. package/src/components/smart-table/CSmartTableInterface.tsx +1 -1
  24. package/src/components/time-picker/CTimePicker.tsx +9 -0
  25. package/src/components/virtual-scroller/CVirtualScroller.tsx +8 -7
  26. package/dist/components/Types.d.ts +0 -14
  27. package/dist/components/multi-select/CMultiSelectCleaner.d.ts +0 -2
  28. package/dist/components/multi-select/CMultiSelectSearchInput.d.ts +0 -5
  29. package/dist/components/pagination/CSmartPagination.d.ts +0 -80
  30. package/dist/components/time-picker/CTimePicker copy.d.ts +0 -46
  31. package/dist/utils/calendar.d.ts +0 -23
  32. package/dist/utils/getNextSibling.d.ts +0 -2
  33. package/dist/utils/getPreviousSibling.d.ts +0 -2
  34. package/dist/utils/hooks/index.d.ts +0 -3
  35. package/dist/utils/hooks/useForkedRef.d.ts +0 -7
  36. package/dist/utils/hooks/useStateWithCallback.d.ts +0 -2
  37. package/dist/utils/isVisible.d.ts +0 -2
  38. 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.9.0",
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.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
  */
@@ -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
- <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
- ))}
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 = (event: React.KeyboardEvent<HTMLDivElement>, date: Date) => {
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,
@@ -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
 
@@ -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
- <Manager>
179
- <DropdownContent />
180
- </Manager>
181
- ) : (
182
- <DropdownContent />
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 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
+ 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
- setSearch('')
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: _search.length + 2 })}
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
- options={search === 'external' ? _options : filterOptionsList(_search, _options)}
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,