@coreui/react-pro 4.10.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/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.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
 
@@ -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,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,
@@ -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[]
@@ -1,33 +1,50 @@
1
1
  import type { Option, SelectedOption } from './types'
2
2
 
3
- export const filterOptionsList = (search: string, _options: Option[]) => {
4
- return search.length > 0
3
+ export const createOption = (text: string, options: Option[]) => {
4
+ const value = text.toLowerCase().replace(/\s/g, '-')
5
+ let uniqueValue = value
6
+ let i = 1
7
+
8
+ while (options.find((option) => String(option.value) === uniqueValue)) {
9
+ uniqueValue = `${value}-${i}`
10
+ i++
11
+ }
12
+
13
+ return [
14
+ {
15
+ value: uniqueValue,
16
+ text,
17
+ custom: true,
18
+ },
19
+ ]
20
+ }
21
+
22
+ export const filterOptionsList = (search: string, _options: Option[]) =>
23
+ search.length > 0
5
24
  ? _options &&
6
- _options.reduce((acc: Option[], val: Option) => {
7
- const options =
8
- val.options &&
9
- val.options.filter(
10
- (element) =>
11
- element.text && element.text.toLowerCase().includes(search.toLowerCase()),
12
- )
13
-
14
- if (
15
- (val.text && val.text.toLowerCase().includes(search.toLowerCase())) ||
16
- (options && options.length > 0)
17
- ) {
18
- acc.push(Object.assign({}, val, options && options.length > 0 && { options }))
19
- }
20
-
21
- return acc
22
- }, [])
25
+ _options.reduce((acc: Option[], val: Option) => {
26
+ const options =
27
+ val.options &&
28
+ val.options.filter(
29
+ (element) => element.text && element.text.toLowerCase().includes(search.toLowerCase()),
30
+ )
31
+ if (
32
+ (val.text && val.text.toLowerCase().includes(search.toLowerCase())) ||
33
+ (options && options.length > 0)
34
+ ) {
35
+ acc.push(Object.assign({}, val, options && options.length > 0 && { options }))
36
+ }
37
+
38
+ return acc
39
+ }, [])
23
40
  : _options
24
- }
25
41
 
26
- export const flattenArray = (options: Option[]): Option[] => {
27
- return options.reduce((acc: Option[], val: Option) => {
28
- return acc.concat(Array.isArray(val.options) ? flattenArray(val.options) : val)
29
- }, [])
30
- }
42
+ export const flattenArray = (options: Option[]): Option[] =>
43
+ options.reduce(
44
+ (acc: Option[], val: Option) =>
45
+ acc.concat(Array.isArray(val.options) ? flattenArray(val.options) : val),
46
+ [],
47
+ )
31
48
 
32
49
  export const getNextSibling = (elem: HTMLElement, selector?: string) => {
33
50
  // Get the next sibling element
@@ -121,7 +121,7 @@ export interface CSmartTableProps extends HTMLAttributes<HTMLDivElement> {
121
121
  */
122
122
  itemsPerPageSelect?: boolean | ItemsPerPageSelect
123
123
  /**
124
- * When set, table will have loading style: loading spinner and reduced opacity. When 'small' prop is enabled spinner will be also smaller.
124
+ * When set, table will have loading style: loading spinner and reduced opacity.
125
125
  */
126
126
  loading?: boolean
127
127
  /**