react-animated-select 0.2.8 → 0.2.9
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/dist/index.cjs.js +9 -9
- package/dist/index.es.js +481 -473
- package/package.json +1 -1
- package/src/select.jsx +2 -0
- package/src/useSelectLogic.jsx +131 -181
package/package.json
CHANGED
package/src/select.jsx
CHANGED
|
@@ -179,6 +179,8 @@ const Select = forwardRef(({
|
|
|
179
179
|
if (index === highlightedIndex) optionClass += ' rac-highlighted'
|
|
180
180
|
|
|
181
181
|
if (element.disabled) optionClass += ' rac-disabled-option'
|
|
182
|
+
|
|
183
|
+
if (element.isInvalid) optionClass += ' rac-invalid-option'
|
|
182
184
|
|
|
183
185
|
if (typeof element.raw === 'boolean') {
|
|
184
186
|
optionClass += element.raw ? ' rac-true-option' : ' rac-false-option'
|
package/src/useSelectLogic.jsx
CHANGED
|
@@ -1,17 +1,14 @@
|
|
|
1
|
-
import {useState, useMemo, useCallback, useId} from 'react'
|
|
1
|
+
import {useState, useMemo, useCallback, useId, useEffect} from 'react'
|
|
2
2
|
|
|
3
3
|
export function useSelectLogic({
|
|
4
4
|
options = [],
|
|
5
5
|
jsxOptions = [],
|
|
6
|
-
|
|
7
6
|
value,
|
|
8
7
|
defaultValue = undefined,
|
|
9
8
|
onChange,
|
|
10
|
-
|
|
11
9
|
disabled = false,
|
|
12
10
|
loading = false,
|
|
13
11
|
error = false,
|
|
14
|
-
|
|
15
12
|
placeholder = 'Choose option',
|
|
16
13
|
emptyText = 'No options',
|
|
17
14
|
disabledText = 'Disabled',
|
|
@@ -22,247 +19,200 @@ export function useSelectLogic({
|
|
|
22
19
|
invalidOption = 'Invalid option',
|
|
23
20
|
setVisibility
|
|
24
21
|
}) {
|
|
25
|
-
|
|
26
22
|
const stableId = useId()
|
|
27
|
-
|
|
28
|
-
// controlled select check
|
|
29
23
|
const isControlled = value !== undefined
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const [internalValue, setInternalValue] = useState(defaultValue)
|
|
33
|
-
|
|
34
|
-
// value chosenness status
|
|
35
|
-
const selectedValue = isControlled ? value : internalValue
|
|
24
|
+
|
|
25
|
+
const [selectedId, setSelectedId] = useState(null)
|
|
36
26
|
|
|
37
27
|
const isOptionObject = (obj) => {
|
|
38
28
|
if (!obj || typeof obj !== 'object' || Array.isArray(obj)) return false
|
|
39
|
-
return (
|
|
40
|
-
'id' in obj ||
|
|
41
|
-
'value' in obj ||
|
|
42
|
-
'name' in obj ||
|
|
43
|
-
'label' in obj ||
|
|
44
|
-
'disabled' in obj
|
|
45
|
-
)
|
|
29
|
+
return ('id' in obj || 'value' in obj || 'name' in obj || 'label' in obj || 'disabled' in obj)
|
|
46
30
|
}
|
|
47
31
|
|
|
48
|
-
|
|
49
|
-
const normalizedPropOptions = useMemo(() => {
|
|
50
|
-
if (!options) return []
|
|
51
|
-
|
|
32
|
+
const normalizedOptions = useMemo(() => {
|
|
52
33
|
const flat = []
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
if (
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
label: '0',
|
|
66
|
-
original: originalItem
|
|
34
|
+
const push = (key, val, originalItem) => {
|
|
35
|
+
let computedUserId = originalItem?.id ?? originalItem?.value ?? val ?? key
|
|
36
|
+
|
|
37
|
+
if (typeof val === 'function') {
|
|
38
|
+
flat.push({
|
|
39
|
+
key: `invalid-${flat.length}`,
|
|
40
|
+
value: val,
|
|
41
|
+
userId: null,
|
|
42
|
+
disabled: true,
|
|
43
|
+
isInvalid: true,
|
|
44
|
+
label: invalidOption,
|
|
45
|
+
original: originalItem
|
|
67
46
|
})
|
|
68
47
|
return
|
|
69
48
|
}
|
|
70
49
|
|
|
71
|
-
if (
|
|
72
|
-
flat.push({
|
|
73
|
-
key: `
|
|
74
|
-
value,
|
|
75
|
-
userId:
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
original: originalItem
|
|
50
|
+
if (val === '') {
|
|
51
|
+
flat.push({
|
|
52
|
+
key: `empty-str-${flat.length}`,
|
|
53
|
+
value: '',
|
|
54
|
+
userId: null,
|
|
55
|
+
disabled: true,
|
|
56
|
+
label: emptyOption,
|
|
57
|
+
original: originalItem
|
|
79
58
|
})
|
|
80
59
|
return
|
|
81
60
|
}
|
|
82
61
|
|
|
83
|
-
if (
|
|
84
|
-
flat.push({
|
|
85
|
-
key: `empty-${flat.length}`,
|
|
86
|
-
value: null,
|
|
87
|
-
userId:
|
|
88
|
-
disabled: true,
|
|
89
|
-
label: emptyOption,
|
|
90
|
-
original: originalItem
|
|
62
|
+
if (val === null || val === undefined) {
|
|
63
|
+
flat.push({
|
|
64
|
+
key: `empty-${flat.length}`,
|
|
65
|
+
value: null,
|
|
66
|
+
userId: null,
|
|
67
|
+
disabled: true,
|
|
68
|
+
label: emptyOption,
|
|
69
|
+
original: originalItem
|
|
91
70
|
})
|
|
92
71
|
return
|
|
93
72
|
}
|
|
94
73
|
|
|
95
|
-
if (typeof
|
|
96
|
-
flat.push({
|
|
97
|
-
key:
|
|
98
|
-
value:
|
|
99
|
-
userId: computedUserId
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
original: originalItem
|
|
74
|
+
if (typeof val === 'number' || typeof val === 'boolean') {
|
|
75
|
+
flat.push({
|
|
76
|
+
key: `${typeof val}-${val}-${flat.length}`,
|
|
77
|
+
value: val,
|
|
78
|
+
userId: computedUserId,
|
|
79
|
+
label: String(val),
|
|
80
|
+
original: originalItem
|
|
103
81
|
})
|
|
104
82
|
return
|
|
105
83
|
}
|
|
106
84
|
|
|
107
|
-
if (
|
|
85
|
+
if (val && typeof val === 'object' && !Array.isArray(val)) {
|
|
108
86
|
flat.push({
|
|
109
|
-
key:
|
|
110
|
-
value,
|
|
87
|
+
key: val.id ?? val.value ?? val.name ?? key ?? `obj-${flat.length}`,
|
|
88
|
+
value: val,
|
|
111
89
|
userId: computedUserId,
|
|
112
|
-
disabled: !!
|
|
113
|
-
label:
|
|
90
|
+
disabled: !!val.disabled,
|
|
91
|
+
label: val.name ?? val.label ?? String(key),
|
|
114
92
|
original: originalItem
|
|
115
93
|
})
|
|
116
94
|
} else {
|
|
117
|
-
flat.push({
|
|
118
|
-
key
|
|
119
|
-
value,
|
|
120
|
-
userId: computedUserId,
|
|
121
|
-
label: String(
|
|
122
|
-
original: originalItem
|
|
95
|
+
flat.push({
|
|
96
|
+
key: key ?? `opt-${flat.length}`,
|
|
97
|
+
value: val,
|
|
98
|
+
userId: computedUserId,
|
|
99
|
+
label: String(val ?? key),
|
|
100
|
+
original: originalItem
|
|
123
101
|
})
|
|
124
102
|
}
|
|
125
103
|
}
|
|
126
104
|
|
|
127
105
|
if (Array.isArray(options)) {
|
|
128
|
-
|
|
129
|
-
if (
|
|
130
|
-
item
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
Object.keys(item).length == 1 &&
|
|
134
|
-
item.disabled == true
|
|
135
|
-
) {
|
|
106
|
+
options.forEach((item, index) => {
|
|
107
|
+
if (item && typeof item === 'object' && Object.keys(item).length === 1 && item.disabled === true) {
|
|
108
|
+
flat.push({ key: `dis-${index}`, value: null, userId: null, disabled: true, label: disabledOption, original: item })
|
|
109
|
+
} else if (isOptionObject(item)) {
|
|
110
|
+
const stableUserId = item.id ?? (typeof item.value !== 'object' ? item.value : (item.label ?? item.name ?? item.value));
|
|
136
111
|
flat.push({
|
|
137
|
-
key: `
|
|
138
|
-
value:
|
|
139
|
-
userId: null,
|
|
140
|
-
disabled: true,
|
|
141
|
-
label: disabledOption,
|
|
142
|
-
original: item
|
|
143
|
-
})
|
|
144
|
-
continue
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
if (isOptionObject(item)) {
|
|
148
|
-
|
|
149
|
-
const stableUserId = item.id ??
|
|
150
|
-
(typeof item.value !== 'object' ? item.value : (item.label ?? item.name ?? item.value));
|
|
151
|
-
|
|
152
|
-
flat.push({
|
|
153
|
-
key: item.id ?? item.value ?? item.name ?? `opt-${flat.length}`,
|
|
154
|
-
value: item.value ?? item.id ?? item,
|
|
112
|
+
key: item.id ?? item.value ?? item.name ?? `opt-${index}`,
|
|
113
|
+
value: item.value !== undefined ? item.value : (item.id !== undefined ? item.id : item),
|
|
155
114
|
userId: stableUserId,
|
|
156
115
|
disabled: !!item.disabled,
|
|
157
116
|
label: item.name ?? item.label ?? String(item.id ?? item.value),
|
|
158
117
|
original: item
|
|
159
118
|
})
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
push(k, v, v)
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
else {
|
|
119
|
+
} else if (item && typeof item === 'object' && !Array.isArray(item)) {
|
|
120
|
+
Object.entries(item).forEach(([k, v]) => push(k, v, v))
|
|
121
|
+
} else {
|
|
167
122
|
push(item, item, item)
|
|
168
123
|
}
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
for (const [k, v] of Object.entries(options)) {
|
|
173
|
-
push(k, v, v)
|
|
174
|
-
}
|
|
124
|
+
})
|
|
125
|
+
} else if (typeof options === 'object' && options !== null) {
|
|
126
|
+
Object.entries(options).forEach(([k, v]) => push(k, v, v))
|
|
175
127
|
}
|
|
176
128
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
name: opt.label,
|
|
204
|
-
jsx: opt.jsx,
|
|
205
|
-
disabled: opt.disabled,
|
|
206
|
-
className: opt.className,
|
|
207
|
-
type: typeof opt.value === 'boolean' ? 'boolean' : 'normal'
|
|
208
|
-
}
|
|
209
|
-
})
|
|
210
|
-
}, [jsxOptions, stableId])
|
|
211
|
-
|
|
212
|
-
const normalizedOptions = useMemo(() => {
|
|
213
|
-
return [...normalizedPropOptions, ...normalizedJsxOptions]
|
|
214
|
-
}, [normalizedPropOptions, normalizedJsxOptions])
|
|
215
|
-
|
|
216
|
-
const hasOptions = normalizedOptions.length > 0
|
|
217
|
-
|
|
218
|
-
const active = useMemo(() => (
|
|
219
|
-
!error && !loading && !disabled && hasOptions
|
|
220
|
-
), [error, loading, disabled, hasOptions])
|
|
221
|
-
|
|
222
|
-
const controlledId = useMemo(() => {
|
|
223
|
-
if (!isControlled) return null
|
|
129
|
+
const propOpts = flat.map((item, i) => ({
|
|
130
|
+
id: `${stableId}-opt-${i}`,
|
|
131
|
+
userId: item.userId,
|
|
132
|
+
name: String(item.label),
|
|
133
|
+
raw: item.value,
|
|
134
|
+
original: item.original,
|
|
135
|
+
disabled: item.disabled,
|
|
136
|
+
isInvalid: item.isInvalid,
|
|
137
|
+
type: typeof item.value === 'boolean' ? 'boolean' : 'normal'
|
|
138
|
+
}))
|
|
139
|
+
|
|
140
|
+
const jsxOpts = jsxOptions.map((opt, index) => ({
|
|
141
|
+
...opt,
|
|
142
|
+
id: `jsx-${stableId.replace(/:/g, '')}-${opt.id}-${index}`,
|
|
143
|
+
userId: opt.id,
|
|
144
|
+
raw: opt.value,
|
|
145
|
+
original: opt.value,
|
|
146
|
+
name: opt.label,
|
|
147
|
+
type: typeof opt.value === 'boolean' ? 'boolean' : 'normal'
|
|
148
|
+
}))
|
|
149
|
+
|
|
150
|
+
return [...propOpts, ...jsxOpts]
|
|
151
|
+
}, [options, jsxOptions, stableId, emptyOption, disabledOption])
|
|
152
|
+
|
|
153
|
+
const findIdByValue = useCallback((val) => {
|
|
154
|
+
if (val === undefined || val === null) return null
|
|
224
155
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
156
|
+
const refMatch = normalizedOptions.find(o => o.original === val || o.raw === val)
|
|
157
|
+
if (refMatch) return refMatch.id
|
|
158
|
+
|
|
159
|
+
if (typeof val === 'object') {
|
|
160
|
+
try {
|
|
161
|
+
const str = JSON.stringify(val)
|
|
162
|
+
const structMatch = normalizedOptions.find(o =>
|
|
163
|
+
o.original && typeof o.original === 'object' && JSON.stringify(o.original) === str
|
|
164
|
+
)
|
|
165
|
+
if (structMatch) return structMatch.id
|
|
166
|
+
} catch {}
|
|
230
167
|
}
|
|
231
168
|
|
|
232
|
-
return normalizedOptions.find(o => o.userId ===
|
|
233
|
-
}, [
|
|
169
|
+
return normalizedOptions.find(o => o.userId === val)?.id ?? null
|
|
170
|
+
}, [normalizedOptions])
|
|
234
171
|
|
|
235
|
-
|
|
236
|
-
const
|
|
172
|
+
useEffect(() => {
|
|
173
|
+
const effectiveValue = isControlled ? value : defaultValue
|
|
237
174
|
|
|
238
|
-
|
|
175
|
+
const currentSelected = normalizedOptions.find(o => o.id === selectedId)
|
|
176
|
+
const isStillValid = currentSelected && (
|
|
177
|
+
currentSelected.original === effectiveValue ||
|
|
178
|
+
currentSelected.raw === effectiveValue ||
|
|
179
|
+
currentSelected.userId === effectiveValue
|
|
180
|
+
)
|
|
239
181
|
|
|
240
|
-
|
|
182
|
+
if (!isStillValid) {
|
|
183
|
+
setSelectedId(findIdByValue(effectiveValue))
|
|
184
|
+
}
|
|
185
|
+
}, [value, defaultValue, isControlled, normalizedOptions, findIdByValue])
|
|
241
186
|
|
|
242
|
-
|
|
187
|
+
const selected = useMemo(() => {
|
|
188
|
+
return normalizedOptions.find(o => o.id === selectedId) ?? null
|
|
189
|
+
}, [selectedId, normalizedOptions])
|
|
243
190
|
|
|
244
191
|
const selectOption = useCallback((option, e) => {
|
|
245
192
|
if (option.disabled) {
|
|
246
|
-
e
|
|
247
|
-
e
|
|
193
|
+
e?.stopPropagation()
|
|
194
|
+
e?.preventDefault()
|
|
248
195
|
return
|
|
249
196
|
}
|
|
250
197
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
onChange?.(option?.original, option?.userId)
|
|
254
|
-
|
|
198
|
+
setSelectedId(option.id)
|
|
199
|
+
onChange?.(option.original, option.userId)
|
|
255
200
|
setVisibility(false)
|
|
256
201
|
}, [onChange, setVisibility])
|
|
257
202
|
|
|
258
203
|
const clear = useCallback((e) => {
|
|
259
204
|
e.preventDefault()
|
|
260
205
|
e.stopPropagation()
|
|
261
|
-
|
|
262
|
-
setInternalValue(null)
|
|
263
|
-
|
|
206
|
+
setSelectedId(null)
|
|
264
207
|
onChange?.(null, null)
|
|
265
208
|
}, [onChange])
|
|
266
209
|
|
|
267
|
-
return {
|
|
210
|
+
return {
|
|
211
|
+
normalizedOptions, selected, selectOption, clear,
|
|
212
|
+
hasOptions: normalizedOptions.length > 0,
|
|
213
|
+
active: !error && !loading && !disabled && normalizedOptions.length > 0,
|
|
214
|
+
selectedValue: value ?? defaultValue,
|
|
215
|
+
placeholder, emptyText, disabledText, loadingText, errorText,
|
|
216
|
+
disabledOption, emptyOption, invalidOption, disabled, loading, error
|
|
217
|
+
}
|
|
268
218
|
}
|