@coreui/react-pro 4.11.3 → 4.12.0-beta.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/accordion/index.d.ts +1 -1
- package/dist/components/calendar/CCalendar.d.ts +4 -4
- package/dist/components/date-range-picker/CDateRangePicker copy.d.ts +114 -0
- package/dist/components/date-range-picker/CDateRangePicker.d.ts +49 -1
- package/dist/components/multi-select/CMultiSelect.d.ts +34 -3
- package/dist/components/multi-select/CMultiSelectNativeSelect.d.ts +4 -5
- package/dist/components/multi-select/CMultiSelectOptions.d.ts +4 -2
- package/dist/components/multi-select/CMultiSelectSelection.d.ts +1 -0
- package/dist/components/multi-select/types.d.ts +7 -3
- package/dist/components/multi-select/utils.d.ts +5 -5
- package/dist/components/multi-select-new/CMultiSelect.d.ts +133 -0
- package/dist/components/multi-select-new/CMultiSelectNativeSelect.d.ts +7 -0
- package/dist/components/multi-select-new/CMultiSelectNew.d.ts +121 -0
- package/dist/components/multi-select-new/CMultiSelectOptions.d.ts +16 -0
- package/dist/components/multi-select-new/CMultiSelectSelection.d.ts +11 -0
- package/dist/components/multi-select-new/index.d.ts +2 -0
- package/dist/components/multi-select-new/types.d.ts +15 -0
- package/dist/components/multi-select-new/utils.d.ts +11 -0
- package/dist/components/picker/CPicker copy.d.ts +82 -0
- package/dist/components/picker/CPicker.d.ts +14 -56
- package/dist/components/picker copy/CPicker.d.ts +82 -0
- package/dist/components/popover/CPopover.d.ts +1 -1
- package/dist/components/time-picker/CTimePicker.d.ts +41 -1
- package/dist/components/tooltip/CTooltip.d.ts +3 -3
- package/dist/hooks/index.d.ts +2 -1
- package/dist/hooks/useColorModes.d.ts +6 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +2385 -2403
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +2386 -2401
- package/dist/index.js.map +1 -1
- package/dist/props.d.ts +1 -1
- package/dist/utils/index.d.ts +2 -1
- package/dist/utils/isRTL.d.ts +2 -0
- package/package.json +15 -15
- package/src/components/accordion/index.ts +1 -7
- package/src/components/calendar/CCalendar.tsx +49 -64
- package/src/components/calendar/__tests__/__snapshots__/CCalendar.spec.tsx.snap +601 -555
- package/src/components/calendar/utils.ts +1 -1
- package/src/components/date-picker/__tests__/__snapshots__/CDatePicker.spec.tsx.snap +599 -561
- package/src/components/date-range-picker/CDateRangePicker.tsx +208 -107
- package/src/components/date-range-picker/__tests__/__snapshots__/CDateRangePicker.spec.tsx.snap +1197 -1121
- package/src/components/date-range-picker/utils.ts +2 -2
- package/src/components/dropdown/CDropdownMenu.tsx +5 -4
- package/src/components/dropdown/__tests__/__snapshots__/CDropdown.spec.tsx.snap +1 -1
- package/src/components/element-cover/CElementCover.tsx +1 -0
- package/src/components/form/CFormSelect.tsx +3 -1
- package/src/components/modal/CModal.tsx +2 -0
- package/src/components/multi-select/CMultiSelect.tsx +200 -79
- package/src/components/multi-select/CMultiSelectNativeSelect.tsx +34 -24
- package/src/components/multi-select/CMultiSelectOptions.tsx +25 -23
- package/src/components/multi-select/CMultiSelectSelection.tsx +36 -7
- package/src/components/multi-select/__tests__/CMultiSelect.spec.tsx +25 -14
- package/src/components/multi-select/__tests__/CMultiSelectNativeSelect.spec.tsx +1 -1
- package/src/components/multi-select/__tests__/CMultiSelectOptions.spec.tsx +4 -4
- package/src/components/multi-select/__tests__/CMultiSelectSelection.spec.tsx +0 -1
- package/src/components/multi-select/__tests__/__snapshots__/CMultiSelect.spec.tsx.snap +136 -30
- package/src/components/multi-select/__tests__/__snapshots__/CMultiSelectNativeSelect.spec.tsx.snap +2 -0
- package/src/components/multi-select/__tests__/__snapshots__/CMultiSelectOptions.spec.tsx.snap +3 -1
- package/src/components/multi-select/__tests__/__snapshots__/CMultiSelectSelection.spec.tsx.snap +2 -2
- package/src/components/multi-select/types.ts +9 -4
- package/src/components/multi-select/utils.ts +56 -39
- package/src/components/picker/CPicker.tsx +113 -138
- package/src/components/popover/CPopover.tsx +61 -26
- package/src/components/popover/__tests__/__snapshots__/CPopover.spec.tsx.snap +2 -1
- package/src/components/progress/CProgress.tsx +1 -0
- package/src/components/sidebar/CSidebar.tsx +2 -2
- package/src/components/smart-table/CSmartTable.tsx +22 -10
- package/src/components/smart-table/CSmartTableBody.tsx +3 -5
- package/src/components/smart-table/CSmartTableHead.tsx +5 -6
- package/src/components/smart-table/__tests__/CSmartTable.spec.tsx +1 -1
- package/src/components/smart-table/__tests__/__snapshots__/CSmartTable.spec.tsx.snap +580 -0
- package/src/components/spinner/CSpinner.tsx +4 -2
- package/src/components/spinner/__tests__/__snapshots__/CSpinner.spec.tsx.snap +2 -2
- package/src/components/table/CTable.tsx +2 -1
- package/src/components/time-picker/CTimePicker.tsx +182 -82
- package/src/components/time-picker/__tests__/__snapshots__/CTimePicker.spec.tsx.snap +1078 -0
- package/src/components/tooltip/CTooltip.tsx +65 -31
- package/src/components/tooltip/__tests__/CTooltip.spec.tsx +1 -1
- package/src/components/virtual-scroller/CVirtualScroller.tsx +0 -1
- package/src/hooks/index.ts +2 -1
- package/src/hooks/useColorModes.ts +55 -0
- package/src/hooks/useForkedRef.ts +1 -1
- package/src/index.ts +1 -0
- package/src/props.ts +4 -1
- package/src/utils/index.ts +2 -1
- package/src/utils/isRTL.ts +13 -0
- package/src/components/multi-select/__tests__/CMultiSelectCleaner.spec.tsx +0 -15
- package/src/components/multi-select/__tests__/CMultiSelectSearchInput.spec.tsx +0 -17
- package/src/components/multi-select/__tests__/CMultiSelectSelectAllButton.spec.tsx +0 -21
- package/src/components/multi-select/__tests__/__snapshots__/CMultiSelectCleaner.spec.tsx.snap +0 -19
- package/src/components/multi-select/__tests__/__snapshots__/CMultiSelectSearchInput.spec.tsx.snap +0 -20
- package/src/components/multi-select/__tests__/__snapshots__/CMultiSelectSelectAllButton.spec.tsx.snap +0 -24
- package/src/components/smart-table/CSmartTableCleaner.tsx +0 -37
|
@@ -32,9 +32,9 @@ export const getLocalDateFromString = (string: string, locale: string, time?: bo
|
|
|
32
32
|
Number(partials.groups['month']) - 1,
|
|
33
33
|
Number(partials.groups['day']),
|
|
34
34
|
partials.groups['ampm']
|
|
35
|
-
? partials.groups['ampm'] === 'PM'
|
|
35
|
+
? (partials.groups['ampm'] === 'PM'
|
|
36
36
|
? Number(partials.groups['hour']) + 12
|
|
37
|
-
: Number(partials.groups['hour'])
|
|
37
|
+
: Number(partials.groups['hour']))
|
|
38
38
|
: Number(partials.groups['hour']),
|
|
39
39
|
Number(partials.groups['minute']),
|
|
40
40
|
Number(partials.groups['second']),
|
|
@@ -7,6 +7,7 @@ import { Alignments, CDropdownContext } from './CDropdown'
|
|
|
7
7
|
import { CConditionalPortal } from '../conditional-portal'
|
|
8
8
|
|
|
9
9
|
import type { Placements } from '../../types'
|
|
10
|
+
import { isRTL } from '../../utils'
|
|
10
11
|
|
|
11
12
|
export interface CDropdownMenuProps
|
|
12
13
|
extends HTMLAttributes<HTMLDivElement | HTMLUListElement>,
|
|
@@ -109,7 +110,7 @@ export const CDropdownMenu: FC<CDropdownMenuProps> = ({
|
|
|
109
110
|
}
|
|
110
111
|
|
|
111
112
|
if (direction === 'dropup') {
|
|
112
|
-
_placement = 'top-start'
|
|
113
|
+
_placement = isRTL(dropdownMenuRef.current) ? 'top-end' : 'top-start'
|
|
113
114
|
}
|
|
114
115
|
|
|
115
116
|
if (direction === 'dropup-center') {
|
|
@@ -117,15 +118,15 @@ export const CDropdownMenu: FC<CDropdownMenuProps> = ({
|
|
|
117
118
|
}
|
|
118
119
|
|
|
119
120
|
if (direction === 'dropend') {
|
|
120
|
-
_placement = 'right-start'
|
|
121
|
+
_placement = isRTL(dropdownMenuRef.current) ? 'left-start' : 'right-start'
|
|
121
122
|
}
|
|
122
123
|
|
|
123
124
|
if (direction === 'dropstart') {
|
|
124
|
-
_placement = 'left-start'
|
|
125
|
+
_placement = isRTL(dropdownMenuRef.current) ? 'right-start' : 'left-start'
|
|
125
126
|
}
|
|
126
127
|
|
|
127
128
|
if (alignment === 'end') {
|
|
128
|
-
_placement = 'bottom-end'
|
|
129
|
+
_placement = isRTL(dropdownMenuRef.current) ? 'bottom-start' : 'bottom-end'
|
|
129
130
|
}
|
|
130
131
|
|
|
131
132
|
const dropdownMenuComponent = (style?: React.CSSProperties, ref?: React.Ref<HTMLDivElement>) => (
|
|
@@ -9,6 +9,7 @@ type Option = {
|
|
|
9
9
|
label?: string
|
|
10
10
|
value?: string
|
|
11
11
|
}
|
|
12
|
+
|
|
12
13
|
export interface CFormSelectProps
|
|
13
14
|
extends CFormControlWrapperProps,
|
|
14
15
|
Omit<InputHTMLAttributes<HTMLSelectElement>, 'size'> {
|
|
@@ -102,7 +103,8 @@ export const CFormSelect = forwardRef<HTMLSelectElement, CFormSelectProps>(
|
|
|
102
103
|
<option
|
|
103
104
|
{...(typeof option === 'object' &&
|
|
104
105
|
option.disabled && { disabled: option.disabled })}
|
|
105
|
-
{...(typeof option === 'object' &&
|
|
106
|
+
{...(typeof option === 'object' &&
|
|
107
|
+
option.value !== undefined && { value: option.value })}
|
|
106
108
|
key={index}
|
|
107
109
|
>
|
|
108
110
|
{typeof option === 'string' ? option : option.label}
|
|
@@ -142,6 +142,7 @@ export const CModal = forwardRef<HTMLDivElement, CModalProps>(
|
|
|
142
142
|
return setStaticBackdrop(true)
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
+
setVisible(false)
|
|
145
146
|
return onClose && onClose()
|
|
146
147
|
}
|
|
147
148
|
|
|
@@ -174,6 +175,7 @@ export const CModal = forwardRef<HTMLDivElement, CModalProps>(
|
|
|
174
175
|
document.body.style.removeProperty('padding-right')
|
|
175
176
|
}
|
|
176
177
|
}
|
|
178
|
+
|
|
177
179
|
return () => {
|
|
178
180
|
document.body.classList.remove('modal-open')
|
|
179
181
|
if (backdrop) {
|
|
@@ -2,7 +2,6 @@ import React, {
|
|
|
2
2
|
FormEvent,
|
|
3
3
|
forwardRef,
|
|
4
4
|
HTMLAttributes,
|
|
5
|
-
KeyboardEvent,
|
|
6
5
|
ReactNode,
|
|
7
6
|
useEffect,
|
|
8
7
|
useState,
|
|
@@ -12,16 +11,18 @@ import React, {
|
|
|
12
11
|
|
|
13
12
|
import classNames from 'classnames'
|
|
14
13
|
import PropTypes from 'prop-types'
|
|
14
|
+
import { createPopper, Instance } from '@popperjs/core'
|
|
15
15
|
|
|
16
|
-
import { CFormControlWrapper, CFormControlWrapperProps } from '
|
|
17
|
-
import { CPicker } from '../picker/CPicker'
|
|
16
|
+
import { CFormControlWrapper, CFormControlWrapperProps } from '../form/CFormControlWrapper'
|
|
18
17
|
|
|
19
18
|
import { CMultiSelectNativeSelect } from './CMultiSelectNativeSelect'
|
|
20
19
|
import { CMultiSelectOptions } from './CMultiSelectOptions'
|
|
21
20
|
import { CMultiSelectSelection } from './CMultiSelectSelection'
|
|
22
21
|
|
|
23
|
-
import {
|
|
24
|
-
import
|
|
22
|
+
import { useForkedRef } from '../../hooks'
|
|
23
|
+
import { isRTL } from '../../utils'
|
|
24
|
+
import { createOption, filterOptionsList, flattenOptionsArray, selectOptions } from './utils'
|
|
25
|
+
import type { Option, OptionsGroup, SelectedOption } from './types'
|
|
25
26
|
|
|
26
27
|
export interface CMultiSelectProps
|
|
27
28
|
extends Omit<CFormControlWrapperProps, 'floatingClassName' | 'floatingLabel'>,
|
|
@@ -50,6 +51,13 @@ export interface CMultiSelectProps
|
|
|
50
51
|
* Toggle the disabled state for the component.
|
|
51
52
|
*/
|
|
52
53
|
disabled?: boolean
|
|
54
|
+
/**
|
|
55
|
+
* The id global attribute defines an identifier (ID) that must be unique in the whole document.
|
|
56
|
+
*
|
|
57
|
+
* The name and id attributes for the native select element are generated based on the `id` property:
|
|
58
|
+
* - <select id="\{id\}-multi-select" name="\{id\}-multi-select" />
|
|
59
|
+
*/
|
|
60
|
+
id?: string
|
|
53
61
|
/**
|
|
54
62
|
* When set, the options list will have a loading style: loading spinner and reduced opacity.
|
|
55
63
|
*
|
|
@@ -70,10 +78,18 @@ export interface CMultiSelectProps
|
|
|
70
78
|
* @since 4.8.0
|
|
71
79
|
*/
|
|
72
80
|
onFilterChange?: (value: string) => void
|
|
81
|
+
/**
|
|
82
|
+
* The callback is fired when the Multi Select component requests to be hidden.
|
|
83
|
+
*/
|
|
84
|
+
onHide?: () => void
|
|
85
|
+
/**
|
|
86
|
+
* The callback is fired when the Multi Select component requests to be shown.
|
|
87
|
+
*/
|
|
88
|
+
onShow?: () => void
|
|
73
89
|
/**
|
|
74
90
|
* List of option elements.
|
|
75
91
|
*/
|
|
76
|
-
options: Option[]
|
|
92
|
+
options: (Option | OptionsGroup)[]
|
|
77
93
|
/**
|
|
78
94
|
* Sets maxHeight of options list.
|
|
79
95
|
*/
|
|
@@ -82,10 +98,26 @@ export interface CMultiSelectProps
|
|
|
82
98
|
* Sets option style.
|
|
83
99
|
*/
|
|
84
100
|
optionsStyle?: 'checkbox' | 'text'
|
|
101
|
+
/**
|
|
102
|
+
* Custom template for options.
|
|
103
|
+
*
|
|
104
|
+
* @since 4.12.0-beta.0
|
|
105
|
+
*/
|
|
106
|
+
optionsTemplate?: (option: Option) => ReactNode
|
|
107
|
+
/**
|
|
108
|
+
* Custom template for options groups.
|
|
109
|
+
*
|
|
110
|
+
* @since 4.12.0-beta.0
|
|
111
|
+
*/
|
|
112
|
+
optionsGroupsTemplate?: (option: OptionsGroup) => ReactNode
|
|
85
113
|
/**
|
|
86
114
|
* Specifies a short hint that is visible in the search input.
|
|
87
115
|
*/
|
|
88
116
|
placeholder?: string
|
|
117
|
+
/**
|
|
118
|
+
* When it is present, it indicates that the user must choose a value before submitting the form.
|
|
119
|
+
*/
|
|
120
|
+
required?: boolean
|
|
89
121
|
/**
|
|
90
122
|
* Enables search input element.
|
|
91
123
|
*/
|
|
@@ -150,10 +182,15 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
|
|
|
150
182
|
label,
|
|
151
183
|
onChange,
|
|
152
184
|
onFilterChange,
|
|
185
|
+
onHide,
|
|
186
|
+
onShow,
|
|
153
187
|
options,
|
|
154
188
|
optionsMaxHeight = 'auto',
|
|
155
189
|
optionsStyle = 'checkbox',
|
|
190
|
+
optionsTemplate,
|
|
191
|
+
optionsGroupsTemplate,
|
|
156
192
|
placeholder = 'Select...',
|
|
193
|
+
required,
|
|
157
194
|
search = true,
|
|
158
195
|
searchNoResultsLabel = 'No results found',
|
|
159
196
|
selectAll = true,
|
|
@@ -171,11 +208,18 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
|
|
|
171
208
|
},
|
|
172
209
|
ref,
|
|
173
210
|
) => {
|
|
174
|
-
const
|
|
211
|
+
const multiSelectRef = useRef<HTMLDivElement>(null)
|
|
212
|
+
const multiSelectForkedRef = useForkedRef(ref, multiSelectRef)
|
|
213
|
+
|
|
214
|
+
const dropdownRef = useRef<HTMLDivElement>(null)
|
|
175
215
|
const nativeSelectRef = useRef<HTMLSelectElement>(null)
|
|
216
|
+
const togglerRef = useRef<HTMLDivElement>(null)
|
|
176
217
|
const searchRef = useRef<HTMLInputElement>(null)
|
|
177
218
|
|
|
178
|
-
const
|
|
219
|
+
const isInitialMount = useRef(true)
|
|
220
|
+
const popper = useRef<Instance>()
|
|
221
|
+
|
|
222
|
+
const [_options, setOptions] = useState<(Option | OptionsGroup)[]>(options)
|
|
179
223
|
const [_visible, setVisible] = useState(visible)
|
|
180
224
|
const [searchValue, setSearchValue] = useState('')
|
|
181
225
|
const [selected, setSelected] = useState<SelectedOption[]>([])
|
|
@@ -183,47 +227,37 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
|
|
|
183
227
|
|
|
184
228
|
const filteredOptions = useMemo(
|
|
185
229
|
() =>
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
230
|
+
flattenOptionsArray(
|
|
231
|
+
search === 'external'
|
|
232
|
+
? [..._options, ...filterOptionsList(searchValue, userOptions)]
|
|
233
|
+
: filterOptionsList(searchValue, [..._options, ...userOptions]),
|
|
234
|
+
true,
|
|
235
|
+
),
|
|
189
236
|
[_options, searchValue, userOptions],
|
|
190
237
|
)
|
|
191
238
|
|
|
239
|
+
const flattenedOptions = useMemo(() => flattenOptionsArray(options), [JSON.stringify(options)])
|
|
240
|
+
|
|
192
241
|
const userOption = useMemo(() => {
|
|
193
242
|
if (
|
|
194
243
|
allowCreateOptions &&
|
|
195
|
-
filteredOptions.
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
).length > 0
|
|
244
|
+
filteredOptions.some(
|
|
245
|
+
(option) => option.text && option.text.toLowerCase() === searchValue.toLowerCase(),
|
|
246
|
+
)
|
|
199
247
|
) {
|
|
200
248
|
return false
|
|
201
249
|
}
|
|
202
250
|
|
|
203
|
-
return searchRef.current && createOption(String(searchValue),
|
|
251
|
+
return searchRef.current && createOption(String(searchValue), flattenedOptions)
|
|
204
252
|
}, [filteredOptions, searchValue])
|
|
205
253
|
|
|
206
|
-
|
|
254
|
+
useEffect(() => {
|
|
207
255
|
setOptions(options)
|
|
208
|
-
const _selected =
|
|
209
|
-
options &&
|
|
210
|
-
flattenArray(options).filter((option: Option) => {
|
|
211
|
-
if (option.selected) {
|
|
212
|
-
return option
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
return
|
|
216
|
-
})
|
|
217
256
|
|
|
218
|
-
const
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
return option
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
return
|
|
226
|
-
})
|
|
257
|
+
const _selected = flattenedOptions.filter((option: Option) => option.selected === true)
|
|
258
|
+
const deselected = flattenedOptions.filter(
|
|
259
|
+
(option: Option) => option.selected === false,
|
|
260
|
+
) as Option[]
|
|
227
261
|
|
|
228
262
|
_selected && setSelected(selectOptions(_selected, selected, deselected))
|
|
229
263
|
}, [JSON.stringify(options)])
|
|
@@ -233,21 +267,90 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
|
|
|
233
267
|
}, [searchValue])
|
|
234
268
|
|
|
235
269
|
useEffect(() => {
|
|
236
|
-
!isInitialMount.current &&
|
|
237
|
-
nativeSelectRef.current &&
|
|
270
|
+
if (!isInitialMount.current && nativeSelectRef.current) {
|
|
238
271
|
nativeSelectRef.current.dispatchEvent(new Event('change', { bubbles: true }))
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (popper.current) {
|
|
275
|
+
popper.current.update()
|
|
276
|
+
}
|
|
239
277
|
}, [JSON.stringify(selected)])
|
|
240
278
|
|
|
279
|
+
useEffect(() => {
|
|
280
|
+
if (_visible) {
|
|
281
|
+
onShow && onShow()
|
|
282
|
+
|
|
283
|
+
window.addEventListener('mouseup', handleMouseUp)
|
|
284
|
+
window.addEventListener('keyup', handleKeyUp)
|
|
285
|
+
|
|
286
|
+
initPopper()
|
|
287
|
+
searchRef.current && searchRef.current.focus()
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return () => {
|
|
291
|
+
onHide && onHide()
|
|
292
|
+
|
|
293
|
+
window.removeEventListener('mouseup', handleMouseUp)
|
|
294
|
+
window.removeEventListener('keyup', handleKeyUp)
|
|
295
|
+
|
|
296
|
+
destroyPopper()
|
|
297
|
+
}
|
|
298
|
+
}, [_visible])
|
|
299
|
+
|
|
241
300
|
useEffect(() => {
|
|
242
301
|
isInitialMount.current = false
|
|
243
302
|
}, [])
|
|
244
303
|
|
|
304
|
+
const initPopper = () => {
|
|
305
|
+
if (togglerRef.current && dropdownRef.current) {
|
|
306
|
+
popper.current = createPopper(togglerRef.current, dropdownRef.current, {
|
|
307
|
+
placement: isRTL(multiSelectRef.current) ? 'bottom-end' : 'bottom-start',
|
|
308
|
+
modifiers: [
|
|
309
|
+
{
|
|
310
|
+
name: 'preventOverflow',
|
|
311
|
+
options: {
|
|
312
|
+
boundary: 'clippingParents',
|
|
313
|
+
},
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
name: 'offset',
|
|
317
|
+
options: {
|
|
318
|
+
offset: [0, 2],
|
|
319
|
+
},
|
|
320
|
+
},
|
|
321
|
+
],
|
|
322
|
+
})
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const destroyPopper = () => {
|
|
327
|
+
if (popper.current) {
|
|
328
|
+
popper.current.destroy()
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
popper.current = undefined
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const handleKeyUp = (event: KeyboardEvent) => {
|
|
335
|
+
if (event.key === 'Escape') {
|
|
336
|
+
setVisible(false)
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const handleMouseUp = (event: Event) => {
|
|
341
|
+
if (multiSelectRef.current && multiSelectRef.current.contains(event.target as HTMLElement)) {
|
|
342
|
+
return
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
setVisible(false)
|
|
346
|
+
}
|
|
347
|
+
|
|
245
348
|
const handleSearchChange = (event: FormEvent<HTMLInputElement>) => {
|
|
246
349
|
const value = (event.target as HTMLInputElement).value
|
|
247
350
|
setSearchValue(value)
|
|
248
351
|
}
|
|
249
352
|
|
|
250
|
-
const handleSearchKeyDown = (event: KeyboardEvent<HTMLInputElement>) => {
|
|
353
|
+
const handleSearchKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
|
251
354
|
if (event.key === 'Enter' && searchValue && allowCreateOptions) {
|
|
252
355
|
event.preventDefault()
|
|
253
356
|
|
|
@@ -259,9 +362,9 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
|
|
|
259
362
|
if (!userOption) {
|
|
260
363
|
setSelected([
|
|
261
364
|
...selected,
|
|
262
|
-
|
|
365
|
+
filteredOptions.find(
|
|
263
366
|
(option) => String(option.text).toLowerCase() === searchValue.toLowerCase(),
|
|
264
|
-
)
|
|
367
|
+
) as Option,
|
|
265
368
|
])
|
|
266
369
|
}
|
|
267
370
|
|
|
@@ -285,7 +388,7 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
|
|
|
285
388
|
|
|
286
389
|
const handleOptionOnClick = (option: Option) => {
|
|
287
390
|
if (!multiple) {
|
|
288
|
-
setSelected([
|
|
391
|
+
setSelected([option] as SelectedOption[])
|
|
289
392
|
setVisible(false)
|
|
290
393
|
setSearchValue('')
|
|
291
394
|
if (searchRef.current) {
|
|
@@ -310,14 +413,14 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
|
|
|
310
413
|
if (selected.some((_option) => _option.value === option.value)) {
|
|
311
414
|
setSelected(selected.filter((_option) => _option.value !== option.value))
|
|
312
415
|
} else {
|
|
313
|
-
setSelected([...selected,
|
|
416
|
+
setSelected([...selected, option] as SelectedOption[])
|
|
314
417
|
}
|
|
315
418
|
}
|
|
316
419
|
|
|
317
420
|
const handleSelectAll = () => {
|
|
318
421
|
setSelected(
|
|
319
422
|
selectOptions(
|
|
320
|
-
[...
|
|
423
|
+
[...flattenedOptions.filter((option: Option) => !option.disabled), ...userOptions],
|
|
321
424
|
selected,
|
|
322
425
|
),
|
|
323
426
|
)
|
|
@@ -341,8 +444,10 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
|
|
|
341
444
|
valid={valid}
|
|
342
445
|
>
|
|
343
446
|
<CMultiSelectNativeSelect
|
|
447
|
+
id={id}
|
|
344
448
|
multiple={multiple}
|
|
345
449
|
options={selected}
|
|
450
|
+
required={required}
|
|
346
451
|
value={
|
|
347
452
|
multiple
|
|
348
453
|
? selected.map((option: SelectedOption) => option.value.toString())
|
|
@@ -351,51 +456,44 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
|
|
|
351
456
|
onChange={() => onChange && onChange(selected)}
|
|
352
457
|
ref={nativeSelectRef}
|
|
353
458
|
/>
|
|
354
|
-
<
|
|
459
|
+
<div
|
|
355
460
|
className={classNames(
|
|
356
461
|
'form-multi-select',
|
|
357
462
|
{
|
|
358
|
-
'form-multi-select-with-cleaner': cleaner,
|
|
359
463
|
[`form-multi-select-${size}`]: size,
|
|
360
|
-
show: _visible,
|
|
361
|
-
'form-multi-select-selection-tags': multiple && selectionType === 'tags',
|
|
362
464
|
disabled,
|
|
363
465
|
'is-invalid': invalid,
|
|
364
466
|
'is-valid': valid,
|
|
467
|
+
show: _visible,
|
|
365
468
|
},
|
|
366
469
|
className,
|
|
367
470
|
)}
|
|
368
|
-
|
|
471
|
+
aria-expanded={_visible}
|
|
369
472
|
id={id}
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
<button
|
|
387
|
-
type="button"
|
|
388
|
-
className="form-multi-select-selection-cleaner"
|
|
389
|
-
onClick={() => handleDeselectAll()}
|
|
390
|
-
></button>
|
|
391
|
-
)}
|
|
473
|
+
ref={multiSelectForkedRef}
|
|
474
|
+
>
|
|
475
|
+
<div
|
|
476
|
+
className="form-multi-select-input-group"
|
|
477
|
+
onClick={() => setVisible(true)}
|
|
478
|
+
ref={togglerRef}
|
|
479
|
+
>
|
|
480
|
+
<CMultiSelectSelection
|
|
481
|
+
multiple={multiple}
|
|
482
|
+
onRemove={(option) => !disabled && handleOptionOnClick(option)}
|
|
483
|
+
placeholder={placeholder}
|
|
484
|
+
search={search}
|
|
485
|
+
selected={selected}
|
|
486
|
+
selectionType={selectionType}
|
|
487
|
+
selectionTypeCounterText={selectionTypeCounterText}
|
|
488
|
+
>
|
|
392
489
|
{search && (
|
|
393
490
|
<input
|
|
394
491
|
type="text"
|
|
395
492
|
className="form-multi-select-search"
|
|
396
493
|
disabled={disabled}
|
|
397
|
-
|
|
398
|
-
|
|
494
|
+
autoComplete="off"
|
|
495
|
+
onChange={handleSearchChange}
|
|
496
|
+
onKeyDown={handleSearchKeyDown}
|
|
399
497
|
{...(selected.length === 0 && { placeholder: placeholder })}
|
|
400
498
|
{...(selected.length > 0 &&
|
|
401
499
|
selectionType === 'counter' && {
|
|
@@ -409,12 +507,27 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
|
|
|
409
507
|
ref={searchRef}
|
|
410
508
|
></input>
|
|
411
509
|
)}
|
|
510
|
+
</CMultiSelectSelection>
|
|
511
|
+
<div className="form-multi-select-buttons">
|
|
512
|
+
{!disabled && cleaner && selected.length > 0 && (
|
|
513
|
+
<button
|
|
514
|
+
type="button"
|
|
515
|
+
className="form-multi-select-cleaner"
|
|
516
|
+
onClick={() => handleDeselectAll()}
|
|
517
|
+
></button>
|
|
518
|
+
)}
|
|
519
|
+
<button
|
|
520
|
+
type="button"
|
|
521
|
+
className="form-multi-select-indicator"
|
|
522
|
+
onClick={(event) => {
|
|
523
|
+
event.preventDefault()
|
|
524
|
+
event.stopPropagation()
|
|
525
|
+
setVisible(!_visible)
|
|
526
|
+
}}
|
|
527
|
+
></button>
|
|
412
528
|
</div>
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
ref={ref}
|
|
416
|
-
>
|
|
417
|
-
<>
|
|
529
|
+
</div>
|
|
530
|
+
<div className="form-multi-select-dropdown" role="menu" ref={dropdownRef}>
|
|
418
531
|
{multiple && selectAll && (
|
|
419
532
|
<button
|
|
420
533
|
type="button"
|
|
@@ -434,13 +547,15 @@ export const CMultiSelect = forwardRef<HTMLDivElement, CMultiSelectProps>(
|
|
|
434
547
|
}
|
|
435
548
|
optionsMaxHeight={optionsMaxHeight}
|
|
436
549
|
optionsStyle={optionsStyle}
|
|
550
|
+
optionsTemplate={optionsTemplate}
|
|
551
|
+
optionsGroupsTemplate={optionsGroupsTemplate}
|
|
437
552
|
searchNoResultsLabel={searchNoResultsLabel}
|
|
438
553
|
selected={selected}
|
|
439
554
|
virtualScroller={virtualScroller}
|
|
440
555
|
visibleItems={visibleItems}
|
|
441
556
|
/>
|
|
442
|
-
|
|
443
|
-
</
|
|
557
|
+
</div>
|
|
558
|
+
</div>
|
|
444
559
|
</CFormControlWrapper>
|
|
445
560
|
)
|
|
446
561
|
},
|
|
@@ -454,10 +569,16 @@ CMultiSelect.propTypes = {
|
|
|
454
569
|
loading: PropTypes.bool,
|
|
455
570
|
multiple: PropTypes.bool,
|
|
456
571
|
onChange: PropTypes.func,
|
|
572
|
+
onFilterChange: PropTypes.func,
|
|
573
|
+
onHide: PropTypes.func,
|
|
574
|
+
onShow: PropTypes.func,
|
|
457
575
|
options: PropTypes.array.isRequired,
|
|
458
576
|
optionsMaxHeight: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
|
459
577
|
optionsStyle: PropTypes.oneOf(['checkbox', 'text']),
|
|
578
|
+
optionsTemplate: PropTypes.func,
|
|
579
|
+
optionsGroupsTemplate: PropTypes.func,
|
|
460
580
|
placeholder: PropTypes.string,
|
|
581
|
+
required: PropTypes.bool,
|
|
461
582
|
search: PropTypes.oneOfType([PropTypes.bool, PropTypes.oneOf<'external'>(['external'])]),
|
|
462
583
|
searchNoResultsLabel: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
|
|
463
584
|
selectAll: PropTypes.bool,
|
|
@@ -1,44 +1,54 @@
|
|
|
1
|
-
import React, { forwardRef,
|
|
1
|
+
import React, { forwardRef, InputHTMLAttributes } from 'react'
|
|
2
2
|
import PropTypes from 'prop-types'
|
|
3
3
|
|
|
4
4
|
import type { Option } from './types'
|
|
5
5
|
|
|
6
6
|
export interface CMultiSelectNativeSelectProps
|
|
7
|
-
extends Omit<
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
value?: string | number | readonly string[]
|
|
7
|
+
extends Omit<InputHTMLAttributes<HTMLSelectElement>, 'options'> {
|
|
8
|
+
options?: Option[]
|
|
9
|
+
value?: string | number | string[]
|
|
11
10
|
}
|
|
12
11
|
|
|
12
|
+
const createNativeOptions = (options: Option[]) =>
|
|
13
|
+
options &&
|
|
14
|
+
options.map((option: Option, index: number) =>
|
|
15
|
+
option.options ? (
|
|
16
|
+
<optgroup label={option.label} key={index}>
|
|
17
|
+
{createNativeOptions(option.options)}
|
|
18
|
+
</optgroup>
|
|
19
|
+
) : (
|
|
20
|
+
<option value={option.value} key={index} disabled={option.disabled}>
|
|
21
|
+
{option.text}
|
|
22
|
+
</option>
|
|
23
|
+
),
|
|
24
|
+
)
|
|
25
|
+
|
|
13
26
|
export const CMultiSelectNativeSelect = forwardRef<
|
|
14
27
|
HTMLSelectElement,
|
|
15
28
|
CMultiSelectNativeSelectProps
|
|
16
|
-
>(({
|
|
17
|
-
const createNativeOptions = (options: Option[]) => {
|
|
18
|
-
return options.map((option: Option, index: number) => {
|
|
19
|
-
return option.options ? (
|
|
20
|
-
<optgroup label={option.label} key={index}>
|
|
21
|
-
{createNativeOptions(option.options)}
|
|
22
|
-
</optgroup>
|
|
23
|
-
) : (
|
|
24
|
-
<option value={option.value} key={index} disabled={option.disabled}>
|
|
25
|
-
{option.text}
|
|
26
|
-
</option>
|
|
27
|
-
)
|
|
28
|
-
})
|
|
29
|
-
}
|
|
30
|
-
|
|
29
|
+
>(({ id, options, ...rest }, ref) => {
|
|
31
30
|
return (
|
|
32
|
-
<select
|
|
31
|
+
<select
|
|
32
|
+
className="multi-select-new"
|
|
33
|
+
{...(id && { id: `${id}-multi-select` })}
|
|
34
|
+
{...(id && { name: `${id}-multi-select` })}
|
|
35
|
+
tabIndex={-1}
|
|
36
|
+
style={{ display: 'none' }}
|
|
37
|
+
{...rest}
|
|
38
|
+
ref={ref}
|
|
39
|
+
>
|
|
33
40
|
{options && createNativeOptions(options)}
|
|
34
41
|
</select>
|
|
35
42
|
)
|
|
36
43
|
})
|
|
37
44
|
|
|
38
45
|
CMultiSelectNativeSelect.propTypes = {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
46
|
+
options: PropTypes.array,
|
|
47
|
+
value: PropTypes.oneOfType([
|
|
48
|
+
PropTypes.number,
|
|
49
|
+
PropTypes.string,
|
|
50
|
+
PropTypes.arrayOf(PropTypes.string.isRequired),
|
|
51
|
+
]),
|
|
42
52
|
}
|
|
43
53
|
|
|
44
54
|
CMultiSelectNativeSelect.displayName = 'CMultiSelectNativeSelect'
|