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