@lobehub/ui 5.10.5 → 5.12.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/es/Menu/baseItem.d.mts +32 -4
- package/es/base-ui/ContextMenu/renderItems.mjs +32 -22
- package/es/base-ui/ContextMenu/renderItems.mjs.map +1 -1
- package/es/base-ui/DropdownMenu/renderItems.mjs +29 -18
- package/es/base-ui/DropdownMenu/renderItems.mjs.map +1 -1
- package/es/base-ui/ScrollArea/atoms.d.mts +9 -2
- package/es/base-ui/ScrollArea/atoms.mjs +8 -1
- package/es/base-ui/ScrollArea/atoms.mjs.map +1 -1
- package/es/base-ui/ScrollArea/style.mjs +167 -5
- package/es/base-ui/ScrollArea/style.mjs.map +1 -1
- package/es/base-ui/ScrollArea/type.d.mts +10 -2
- package/es/base-ui/Select/Select.mjs +67 -435
- package/es/base-ui/Select/Select.mjs.map +1 -1
- package/es/base-ui/Select/helpers.mjs +32 -0
- package/es/base-ui/Select/helpers.mjs.map +1 -0
- package/es/base-ui/Select/hooks.mjs +293 -0
- package/es/base-ui/Select/hooks.mjs.map +1 -0
- package/es/base-ui/Select/parts.mjs +136 -0
- package/es/base-ui/Select/parts.mjs.map +1 -0
- package/es/base-ui/Select/renderOptions.mjs +52 -0
- package/es/base-ui/Select/renderOptions.mjs.map +1 -0
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ScrollAreaContentProps, ScrollAreaCornerProps, ScrollAreaRootProps, ScrollAreaScrollbarProps, ScrollAreaThumbProps, ScrollAreaViewportProps } from "./atoms.mjs";
|
|
1
|
+
import { ScrollAreaContentProps, ScrollAreaCornerProps, ScrollAreaFadeOrientation, ScrollAreaRootProps, ScrollAreaScrollbarProps, ScrollAreaThumbProps, ScrollAreaViewportProps } from "./atoms.mjs";
|
|
2
2
|
import { ReactNode } from "react";
|
|
3
3
|
|
|
4
4
|
//#region src/base-ui/ScrollArea/type.d.ts
|
|
@@ -8,7 +8,15 @@ interface ScrollAreaProps extends Omit<ScrollAreaRootProps, 'children'> {
|
|
|
8
8
|
corner?: boolean;
|
|
9
9
|
cornerProps?: ScrollAreaCornerProps;
|
|
10
10
|
scrollbarProps?: Omit<ScrollAreaScrollbarProps, 'children'>;
|
|
11
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Enable gradient scroll fade on the viewport edges.
|
|
13
|
+
*
|
|
14
|
+
* Accepts a boolean (true ≡ vertical) or an explicit orientation:
|
|
15
|
+
* `'vertical' | 'horizontal' | 'both'`.
|
|
16
|
+
*
|
|
17
|
+
* @default false
|
|
18
|
+
*/
|
|
19
|
+
scrollFade?: boolean | ScrollAreaFadeOrientation;
|
|
12
20
|
thumbProps?: ScrollAreaThumbProps;
|
|
13
21
|
viewportProps?: Omit<ScrollAreaViewportProps, 'children' | 'scrollFade'>;
|
|
14
22
|
}
|
|
@@ -1,263 +1,63 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { useAppElement } from "../../ThemeProvider/AppElementContext.mjs";
|
|
3
|
-
import Icon from "../../Icon/Icon.mjs";
|
|
4
2
|
import { styles } from "../../Menu/sharedStyle.mjs";
|
|
5
3
|
import { styles as styles$1, triggerVariants } from "./style.mjs";
|
|
6
|
-
import {
|
|
4
|
+
import { isValueEmpty } from "./helpers.mjs";
|
|
5
|
+
import { usePortalContainer, useSelectOpen, useSelectSearch, useSelectValue, useSelectVirtual } from "./hooks.mjs";
|
|
6
|
+
import { EmptyContent, SelectListSection, SelectSearchInput, SelectTriggerSuffix, createTriggerValueRenderer, resolveIconNode, resolveSuffixIcon } from "./parts.mjs";
|
|
7
|
+
import { renderOptions } from "./renderOptions.mjs";
|
|
8
|
+
import { memo, useCallback, useEffect, useMemo, useState } from "react";
|
|
7
9
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
10
|
import { cx, useThemeMode } from "antd-style";
|
|
9
|
-
import { Check, ChevronDown, Loader2, X } from "lucide-react";
|
|
10
11
|
import { Select } from "@base-ui/react/select";
|
|
11
|
-
import { Virtualizer } from "virtua";
|
|
12
12
|
//#region src/base-ui/Select/Select.tsx
|
|
13
|
-
const isGroupOption = (option) => Boolean(option.options);
|
|
14
|
-
const getOptionSearchText = (option) => {
|
|
15
|
-
if (typeof option.label === "string" || typeof option.label === "number") return String(option.label);
|
|
16
|
-
if (typeof option.value === "string" || typeof option.value === "number") return String(option.value);
|
|
17
|
-
if (option.title) return option.title;
|
|
18
|
-
return "";
|
|
19
|
-
};
|
|
20
|
-
const escapeRegExp = (value) => value.replaceAll(/[$()*+.?[\\\]^{|}]/g, "\\$&");
|
|
21
|
-
const splitBySeparators = (value, separators) => {
|
|
22
|
-
if (!separators || separators.length === 0) return [value];
|
|
23
|
-
const pattern = separators.map(escapeRegExp).join("|");
|
|
24
|
-
return value.split(new RegExp(pattern, "g"));
|
|
25
|
-
};
|
|
26
|
-
const countVirtualItems = (items) => items.reduce((count, item) => {
|
|
27
|
-
if (isGroupOption(item)) return count + item.options.length + 1;
|
|
28
|
-
return count + 1;
|
|
29
|
-
}, 0);
|
|
30
|
-
const isValueEmpty = (value) => value === null || value === void 0 || value === "";
|
|
31
13
|
const Select$1 = memo(({ allowClear, autoFocus, className, classNames, defaultOpen, defaultValue, disabled, id, labelRender, listHeight = 512, listItemHeight, loading, mode, name, onChange, onOpenChange, onSelect, open, optionRender, options, placeholder, popupClassName, popupMatchSelectWidth, prefix, readOnly, required, behaviorVariant = "default", selectedIndicatorVariant = "check", shadow, showSearch, size = "middle", style, suffixIcon, suffixIconProps, tokenSeparators, value, variant, virtual }) => {
|
|
32
14
|
const { isDarkMode } = useThemeMode();
|
|
33
15
|
const resolvedVariant = variant ?? (isDarkMode ? "filled" : "outlined");
|
|
34
16
|
const isMultiple = mode === "multiple" || mode === "tags";
|
|
35
17
|
const isItemAligned = behaviorVariant === "item-aligned";
|
|
36
|
-
const [uncontrolledValue, setUncontrolledValue] = useState(() => {
|
|
37
|
-
if (defaultValue !== void 0) return defaultValue;
|
|
38
|
-
return isMultiple ? [] : null;
|
|
39
|
-
});
|
|
40
|
-
const normalizeValue = useCallback((nextValue) => {
|
|
41
|
-
if (isMultiple) {
|
|
42
|
-
if (Array.isArray(nextValue)) return nextValue;
|
|
43
|
-
if (nextValue === null || nextValue === void 0) return [];
|
|
44
|
-
return [nextValue];
|
|
45
|
-
}
|
|
46
|
-
if (Array.isArray(nextValue)) return nextValue[0] ?? null;
|
|
47
|
-
return nextValue === void 0 ? null : nextValue;
|
|
48
|
-
}, [isMultiple]);
|
|
49
|
-
const mergedValue = value !== void 0 ? value : uncontrolledValue;
|
|
50
|
-
const normalizedValue = useMemo(() => normalizeValue(mergedValue), [mergedValue, normalizeValue]);
|
|
51
|
-
const valueArray = useMemo(() => isMultiple ? normalizedValue : isValueEmpty(normalizedValue) ? [] : [normalizedValue], [isMultiple, normalizedValue]);
|
|
52
18
|
const [extraOptions, setExtraOptions] = useState([]);
|
|
53
19
|
useEffect(() => {
|
|
54
20
|
if (mode !== "tags" && extraOptions.length > 0) setExtraOptions([]);
|
|
55
21
|
}, [mode, extraOptions.length]);
|
|
56
|
-
const { resolvedOptions,
|
|
57
|
-
|
|
58
|
-
const optionValueMap = /* @__PURE__ */ new Map();
|
|
59
|
-
const addOption = (item) => {
|
|
60
|
-
if (!optionValueMap.has(item.value)) optionValueMap.set(item.value, item);
|
|
61
|
-
};
|
|
62
|
-
const walkOptions = (items) => {
|
|
63
|
-
items.forEach((item) => {
|
|
64
|
-
if (isGroupOption(item)) item.options.forEach(addOption);
|
|
65
|
-
else addOption(item);
|
|
66
|
-
});
|
|
67
|
-
};
|
|
68
|
-
walkOptions(baseOptions);
|
|
69
|
-
const filteredExtraOptions = extraOptions.filter((item) => !optionValueMap.has(item.value));
|
|
70
|
-
filteredExtraOptions.forEach(addOption);
|
|
71
|
-
const mergedOptions = [...baseOptions, ...filteredExtraOptions];
|
|
72
|
-
const missingValueOptions = valueArray.filter((val) => !optionValueMap.has(val)).map((val) => ({
|
|
73
|
-
label: String(val),
|
|
74
|
-
value: val
|
|
75
|
-
}));
|
|
76
|
-
missingValueOptions.forEach(addOption);
|
|
77
|
-
return {
|
|
78
|
-
optionMap: optionValueMap,
|
|
79
|
-
resolvedOptions: missingValueOptions.length ? [...mergedOptions, ...missingValueOptions] : mergedOptions
|
|
80
|
-
};
|
|
81
|
-
}, [
|
|
22
|
+
const { appendTagValues, getOption, handleValueChange, normalizedValue, normalizeValue, resolvedOptions, valueArray } = useSelectValue({
|
|
23
|
+
defaultValue,
|
|
82
24
|
extraOptions,
|
|
83
|
-
options,
|
|
84
|
-
valueArray
|
|
85
|
-
]);
|
|
86
|
-
const [uncontrolledOpen, setUncontrolledOpen] = useState(Boolean(defaultOpen));
|
|
87
|
-
useEffect(() => {
|
|
88
|
-
if (open !== void 0) setUncontrolledOpen(open);
|
|
89
|
-
}, [open]);
|
|
90
|
-
const mergedOpen = open ?? uncontrolledOpen;
|
|
91
|
-
const handleOpenChange = useCallback((nextOpen, eventDetails) => {
|
|
92
|
-
onOpenChange?.(nextOpen, eventDetails);
|
|
93
|
-
if (open === void 0) setUncontrolledOpen(nextOpen);
|
|
94
|
-
}, [onOpenChange, open]);
|
|
95
|
-
const [searchValue, setSearchValue] = useState("");
|
|
96
|
-
const shouldShowSearch = Boolean(showSearch || mode === "tags");
|
|
97
|
-
useEffect(() => {
|
|
98
|
-
if (!mergedOpen) setSearchValue("");
|
|
99
|
-
}, [mergedOpen]);
|
|
100
|
-
const getOption = useCallback((optionValue) => {
|
|
101
|
-
const matched = optionMap.get(optionValue);
|
|
102
|
-
if (matched) return matched;
|
|
103
|
-
if (optionValue && typeof optionValue === "object" && "label" in optionValue) return {
|
|
104
|
-
label: optionValue.label,
|
|
105
|
-
value: optionValue
|
|
106
|
-
};
|
|
107
|
-
return {
|
|
108
|
-
label: String(optionValue),
|
|
109
|
-
value: optionValue
|
|
110
|
-
};
|
|
111
|
-
}, [optionMap]);
|
|
112
|
-
const previousValueRef = useRef(normalizedValue);
|
|
113
|
-
useEffect(() => {
|
|
114
|
-
previousValueRef.current = normalizedValue;
|
|
115
|
-
}, [normalizedValue]);
|
|
116
|
-
const handleValueChange = useCallback((nextValue) => {
|
|
117
|
-
const normalizedNextValue = normalizeValue(nextValue);
|
|
118
|
-
const previousValue = previousValueRef.current;
|
|
119
|
-
if (isMultiple) {
|
|
120
|
-
const prevValues = Array.isArray(previousValue) ? previousValue : [];
|
|
121
|
-
const nextValues = Array.isArray(normalizedNextValue) ? normalizedNextValue : [];
|
|
122
|
-
nextValues.filter((val) => !prevValues.some((prev) => Object.is(prev, val))).forEach((val) => {
|
|
123
|
-
onSelect?.(val, getOption(val));
|
|
124
|
-
});
|
|
125
|
-
if (value === void 0) setUncontrolledValue(nextValues);
|
|
126
|
-
onChange?.(nextValues, nextValues.map((val) => getOption(val)));
|
|
127
|
-
} else {
|
|
128
|
-
if (!isValueEmpty(normalizedNextValue) && !Object.is(previousValue, normalizedNextValue)) onSelect?.(normalizedNextValue, getOption(normalizedNextValue));
|
|
129
|
-
if (value === void 0) setUncontrolledValue(normalizedNextValue);
|
|
130
|
-
onChange?.(normalizedNextValue, isValueEmpty(normalizedNextValue) ? void 0 : getOption(normalizedNextValue));
|
|
131
|
-
}
|
|
132
|
-
previousValueRef.current = normalizedNextValue;
|
|
133
|
-
}, [
|
|
134
|
-
getOption,
|
|
135
25
|
isMultiple,
|
|
136
|
-
normalizeValue,
|
|
137
26
|
onChange,
|
|
138
27
|
onSelect,
|
|
28
|
+
options,
|
|
29
|
+
setExtraOptions,
|
|
139
30
|
value
|
|
140
|
-
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
const existingValues = new Set(prev.map((item) => item.value));
|
|
148
|
-
const merged = [...prev];
|
|
149
|
-
newOptionValues.forEach((val) => {
|
|
150
|
-
if (!existingValues.has(val)) merged.push({
|
|
151
|
-
label: val,
|
|
152
|
-
value: val
|
|
153
|
-
});
|
|
154
|
-
});
|
|
155
|
-
return merged;
|
|
156
|
-
});
|
|
157
|
-
valuesToAdd.forEach((val) => {
|
|
158
|
-
if (!nextValues.some((item) => Object.is(item, val))) nextValues.push(val);
|
|
159
|
-
});
|
|
160
|
-
if (nextValues.length !== valueArray.length) handleValueChange(nextValues);
|
|
161
|
-
}, [
|
|
162
|
-
handleValueChange,
|
|
163
|
-
optionMap,
|
|
164
|
-
valueArray
|
|
165
|
-
]);
|
|
166
|
-
const handleSearchChange = useCallback((event) => {
|
|
167
|
-
const nextValue = event.target.value;
|
|
168
|
-
if (mode === "tags") {
|
|
169
|
-
const parts = splitBySeparators(nextValue, tokenSeparators);
|
|
170
|
-
if (parts.length > 1) {
|
|
171
|
-
const pending = parts.pop() ?? "";
|
|
172
|
-
appendTagValues(parts.filter(Boolean));
|
|
173
|
-
setSearchValue(pending);
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
setSearchValue(nextValue);
|
|
178
|
-
}, [
|
|
179
|
-
appendTagValues,
|
|
180
|
-
mode,
|
|
181
|
-
tokenSeparators
|
|
182
|
-
]);
|
|
183
|
-
const handleSearchKeyDown = useCallback((event) => {
|
|
184
|
-
event.stopPropagation();
|
|
185
|
-
if (event.key === "Escape") {
|
|
186
|
-
handleOpenChange(false);
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
189
|
-
if (mode !== "tags") return;
|
|
190
|
-
if (event.key === "Enter") {
|
|
191
|
-
event.preventDefault();
|
|
192
|
-
event.stopPropagation();
|
|
193
|
-
appendTagValues([searchValue]);
|
|
194
|
-
setSearchValue("");
|
|
195
|
-
return;
|
|
196
|
-
}
|
|
197
|
-
if (tokenSeparators?.includes(event.key)) {
|
|
198
|
-
event.preventDefault();
|
|
199
|
-
event.stopPropagation();
|
|
200
|
-
appendTagValues([searchValue]);
|
|
201
|
-
setSearchValue("");
|
|
202
|
-
}
|
|
203
|
-
}, [
|
|
31
|
+
});
|
|
32
|
+
const { handleOpenChange, mergedOpen } = useSelectOpen({
|
|
33
|
+
defaultOpen,
|
|
34
|
+
onOpenChange,
|
|
35
|
+
open
|
|
36
|
+
});
|
|
37
|
+
const { filteredOptions, handleSearchChange, handleSearchKeyDown, searchValue, shouldShowSearch, stopSearchPropagation } = useSelectSearch({
|
|
204
38
|
appendTagValues,
|
|
205
39
|
handleOpenChange,
|
|
40
|
+
mergedOpen,
|
|
206
41
|
mode,
|
|
207
|
-
searchValue,
|
|
208
|
-
tokenSeparators
|
|
209
|
-
]);
|
|
210
|
-
const filteredOptions = useMemo(() => {
|
|
211
|
-
if (!shouldShowSearch || !searchValue.trim()) return resolvedOptions;
|
|
212
|
-
const query = searchValue.trim().toLowerCase();
|
|
213
|
-
const filterItems = (items) => {
|
|
214
|
-
return items.map((item) => {
|
|
215
|
-
if (isGroupOption(item)) {
|
|
216
|
-
const groupItems = item.options.filter((option) => getOptionSearchText(option).toLowerCase().includes(query));
|
|
217
|
-
if (!groupItems.length) return null;
|
|
218
|
-
return {
|
|
219
|
-
...item,
|
|
220
|
-
options: groupItems
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
return getOptionSearchText(item).toLowerCase().includes(query) ? item : null;
|
|
224
|
-
}).filter(Boolean);
|
|
225
|
-
};
|
|
226
|
-
return filterItems(resolvedOptions);
|
|
227
|
-
}, [
|
|
228
42
|
resolvedOptions,
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
className: styles$1.tag,
|
|
248
|
-
children: content
|
|
249
|
-
}, `${String(val)}-${index}`);
|
|
250
|
-
})
|
|
251
|
-
});
|
|
252
|
-
}
|
|
253
|
-
if (isValueEmpty(resolved)) return placeholderNode;
|
|
254
|
-
const option = getOption(resolved);
|
|
255
|
-
const content = labelRender ? labelRender(option) : option.label ?? String(resolved);
|
|
256
|
-
return /* @__PURE__ */ jsx("span", {
|
|
257
|
-
className: styles$1.valueText,
|
|
258
|
-
children: content
|
|
259
|
-
});
|
|
260
|
-
}, [
|
|
43
|
+
showSearch,
|
|
44
|
+
tokenSeparators
|
|
45
|
+
});
|
|
46
|
+
const virtualState = useSelectVirtual({
|
|
47
|
+
filteredOptions,
|
|
48
|
+
listItemHeight,
|
|
49
|
+
size,
|
|
50
|
+
valueArray,
|
|
51
|
+
virtual
|
|
52
|
+
});
|
|
53
|
+
const portalContainer = usePortalContainer();
|
|
54
|
+
const renderValue = useMemo(() => createTriggerValueRenderer({
|
|
55
|
+
getOption,
|
|
56
|
+
isMultiple,
|
|
57
|
+
labelRender,
|
|
58
|
+
normalizeValue,
|
|
59
|
+
placeholder
|
|
60
|
+
}), [
|
|
261
61
|
getOption,
|
|
262
62
|
isMultiple,
|
|
263
63
|
labelRender,
|
|
@@ -271,32 +71,8 @@ const Select$1 = memo(({ allowClear, autoFocus, className, classNames, defaultOp
|
|
|
271
71
|
event.stopPropagation();
|
|
272
72
|
handleValueChange(isMultiple ? [] : null);
|
|
273
73
|
}, [handleValueChange, isMultiple]);
|
|
274
|
-
const prefixNode = useMemo(() =>
|
|
275
|
-
|
|
276
|
-
if (isValidElement(prefix) || typeof prefix === "string" || typeof prefix === "number") return prefix;
|
|
277
|
-
return /* @__PURE__ */ jsx(Icon, {
|
|
278
|
-
icon: prefix,
|
|
279
|
-
size: "small"
|
|
280
|
-
});
|
|
281
|
-
}, [prefix]);
|
|
282
|
-
const suffixIconNode = useMemo(() => {
|
|
283
|
-
if (loading) return /* @__PURE__ */ jsx(Icon, {
|
|
284
|
-
spin: true,
|
|
285
|
-
icon: Loader2,
|
|
286
|
-
size: "small"
|
|
287
|
-
});
|
|
288
|
-
if (suffixIcon === null) return null;
|
|
289
|
-
if (isValidElement(suffixIcon) || typeof suffixIcon === "string" || typeof suffixIcon === "number") return suffixIcon;
|
|
290
|
-
return /* @__PURE__ */ jsx(Icon, {
|
|
291
|
-
icon: suffixIcon || ChevronDown,
|
|
292
|
-
size: "small",
|
|
293
|
-
...suffixIconProps,
|
|
294
|
-
style: {
|
|
295
|
-
pointerEvents: "none",
|
|
296
|
-
...suffixIconProps?.style
|
|
297
|
-
}
|
|
298
|
-
});
|
|
299
|
-
}, [
|
|
74
|
+
const prefixNode = useMemo(() => resolveIconNode(prefix), [prefix]);
|
|
75
|
+
const suffixIconNode = useMemo(() => resolveSuffixIcon(suffixIcon, suffixIconProps, loading), [
|
|
300
76
|
loading,
|
|
301
77
|
suffixIcon,
|
|
302
78
|
suffixIconProps
|
|
@@ -329,133 +105,19 @@ const Select$1 = memo(({ allowClear, autoFocus, className, classNames, defaultOp
|
|
|
329
105
|
size,
|
|
330
106
|
variant: resolvedVariant
|
|
331
107
|
}), className, classNames?.root, classNames?.trigger);
|
|
332
|
-
const
|
|
333
|
-
const
|
|
334
|
-
const
|
|
335
|
-
const
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
if (node) node.scrollIntoView = (...args) => {
|
|
341
|
-
if (!pointerScrollRef.current) HTMLElement.prototype.scrollIntoView.call(node, ...args);
|
|
342
|
-
};
|
|
343
|
-
if (typeof ref === "function") ref(node);
|
|
344
|
-
else if (ref && "current" in ref) ref.current = node;
|
|
345
|
-
}
|
|
346
|
-
});
|
|
347
|
-
}, []);
|
|
348
|
-
const markPointerScroll = useCallback(() => {
|
|
349
|
-
pointerScrollRef.current = true;
|
|
350
|
-
if (pointerScrollTimeoutRef.current) clearTimeout(pointerScrollTimeoutRef.current);
|
|
351
|
-
pointerScrollTimeoutRef.current = setTimeout(() => {
|
|
352
|
-
pointerScrollRef.current = false;
|
|
353
|
-
}, 120);
|
|
354
|
-
}, []);
|
|
355
|
-
const handleListScroll = useCallback(() => {
|
|
356
|
-
if (!virtual || !pointerScrollRef.current) return;
|
|
357
|
-
const listElement = listRef.current;
|
|
358
|
-
const activeElement = document.activeElement;
|
|
359
|
-
if (listElement && activeElement && listElement.contains(activeElement)) listElement.focus({ preventScroll: true });
|
|
360
|
-
}, [virtual]);
|
|
361
|
-
useEffect(() => {
|
|
362
|
-
return () => {
|
|
363
|
-
if (pointerScrollTimeoutRef.current) clearTimeout(pointerScrollTimeoutRef.current);
|
|
364
|
-
};
|
|
365
|
-
}, []);
|
|
366
|
-
const virtualListStyle = useMemo(() => {
|
|
367
|
-
if (!virtual) return void 0;
|
|
368
|
-
const rowCount = countVirtualItems(filteredOptions);
|
|
369
|
-
return { height: `min(${Math.min(Math.max(rowCount, 1), 6) * (listItemHeight ?? (size === "large" ? 40 : size === "small" ? 28 : 32)) + 8}px, var(--lobe-select-available-height, var(--available-height)))` };
|
|
370
|
-
}, [
|
|
371
|
-
filteredOptions,
|
|
108
|
+
const isBoldIndicator = selectedIndicatorVariant === "bold";
|
|
109
|
+
const itemTextClassName = cx(optionRender ? styles.itemContent : styles.label, styles$1.itemText, classNames?.itemText);
|
|
110
|
+
const isEmpty = filteredOptions.length === 0;
|
|
111
|
+
const listContent = isEmpty ? /* @__PURE__ */ jsx(EmptyContent, { classNames }) : renderOptions({
|
|
112
|
+
classNames,
|
|
113
|
+
isBoldIndicator,
|
|
114
|
+
items: filteredOptions,
|
|
115
|
+
itemTextClassName,
|
|
372
116
|
listItemHeight,
|
|
373
|
-
|
|
117
|
+
optionRender,
|
|
118
|
+
renderVirtualItem: virtualState.renderVirtualItem,
|
|
374
119
|
virtual
|
|
375
|
-
]);
|
|
376
|
-
const keepMountedIndices = useMemo(() => {
|
|
377
|
-
if (!virtual || valueArray.length === 0) return void 0;
|
|
378
|
-
const selectedSet = new Set(valueArray);
|
|
379
|
-
const indices = [];
|
|
380
|
-
let index = 0;
|
|
381
|
-
filteredOptions.forEach((item) => {
|
|
382
|
-
if (isGroupOption(item)) {
|
|
383
|
-
if (item.options.some((option) => selectedSet.has(option.value))) indices.push(index);
|
|
384
|
-
index += 1;
|
|
385
|
-
return;
|
|
386
|
-
}
|
|
387
|
-
if (selectedSet.has(item.value)) indices.push(index);
|
|
388
|
-
index += 1;
|
|
389
|
-
});
|
|
390
|
-
return indices.length ? indices : void 0;
|
|
391
|
-
}, [
|
|
392
|
-
filteredOptions,
|
|
393
|
-
valueArray,
|
|
394
|
-
virtual
|
|
395
|
-
]);
|
|
396
|
-
const itemTextClassName = cx(optionRender ? styles.itemContent : styles.label, styles$1.itemText, classNames?.itemText);
|
|
397
|
-
const isBoldIndicator = selectedIndicatorVariant === "bold";
|
|
398
|
-
let optionIndex = 0;
|
|
399
|
-
const renderOptions = (items) => items.map((item, index) => {
|
|
400
|
-
if (isGroupOption(item)) return /* @__PURE__ */ jsxs(Select.Group, {
|
|
401
|
-
className: cx(styles$1.group, classNames?.group),
|
|
402
|
-
children: [/* @__PURE__ */ jsx(Select.GroupLabel, {
|
|
403
|
-
className: cx(styles.groupLabel, styles$1.groupLabel, classNames?.groupLabel),
|
|
404
|
-
children: item.label
|
|
405
|
-
}), item.options.map((option) => {
|
|
406
|
-
const currentIndex = optionIndex++;
|
|
407
|
-
return /* @__PURE__ */ jsxs(Select.Item, {
|
|
408
|
-
disabled: option.disabled,
|
|
409
|
-
label: getOptionSearchText(option),
|
|
410
|
-
render: virtual ? renderVirtualItem : void 0,
|
|
411
|
-
value: option.value,
|
|
412
|
-
className: cx(styles.item, styles$1.item, isBoldIndicator && styles$1.itemBoldSelected, classNames?.item, classNames?.option, option.className),
|
|
413
|
-
style: {
|
|
414
|
-
minHeight: listItemHeight,
|
|
415
|
-
...option.style
|
|
416
|
-
},
|
|
417
|
-
children: [/* @__PURE__ */ jsx(Select.ItemText, {
|
|
418
|
-
className: itemTextClassName,
|
|
419
|
-
children: optionRender ? optionRender(option, { index: currentIndex }) : option.label
|
|
420
|
-
}), !isBoldIndicator && /* @__PURE__ */ jsx(Select.ItemIndicator, {
|
|
421
|
-
className: cx(styles$1.itemIndicator, classNames?.itemIndicator),
|
|
422
|
-
children: /* @__PURE__ */ jsx(Icon, {
|
|
423
|
-
icon: Check,
|
|
424
|
-
size: "small"
|
|
425
|
-
})
|
|
426
|
-
})]
|
|
427
|
-
}, `${String(option.value)}-${currentIndex}`);
|
|
428
|
-
})]
|
|
429
|
-
}, `group-${index}`);
|
|
430
|
-
const currentIndex = optionIndex++;
|
|
431
|
-
return /* @__PURE__ */ jsxs(Select.Item, {
|
|
432
|
-
disabled: item.disabled,
|
|
433
|
-
label: getOptionSearchText(item),
|
|
434
|
-
render: virtual ? renderVirtualItem : void 0,
|
|
435
|
-
value: item.value,
|
|
436
|
-
className: cx(styles.item, styles$1.item, isBoldIndicator && styles$1.itemBoldSelected, classNames?.item, classNames?.option, item.className),
|
|
437
|
-
style: {
|
|
438
|
-
minHeight: listItemHeight,
|
|
439
|
-
...item.style
|
|
440
|
-
},
|
|
441
|
-
children: [/* @__PURE__ */ jsx(Select.ItemText, {
|
|
442
|
-
className: itemTextClassName,
|
|
443
|
-
children: optionRender ? optionRender(item, { index: currentIndex }) : item.label
|
|
444
|
-
}), !isBoldIndicator && /* @__PURE__ */ jsx(Select.ItemIndicator, {
|
|
445
|
-
className: cx(styles$1.itemIndicator, classNames?.itemIndicator),
|
|
446
|
-
children: /* @__PURE__ */ jsx(Icon, {
|
|
447
|
-
icon: Check,
|
|
448
|
-
size: "small"
|
|
449
|
-
})
|
|
450
|
-
})]
|
|
451
|
-
}, `${String(item.value)}-${currentIndex}`);
|
|
452
120
|
});
|
|
453
|
-
const appElement = useAppElement();
|
|
454
|
-
const portalContainer = useMemo(() => {
|
|
455
|
-
if (typeof window === "undefined") return appElement;
|
|
456
|
-
if (!(appElement instanceof HTMLElement)) return void 0;
|
|
457
|
-
return window.getComputedStyle(appElement).display === "contents" ? document.body : appElement;
|
|
458
|
-
}, [appElement]);
|
|
459
121
|
return /* @__PURE__ */ jsxs(Select.Root, {
|
|
460
122
|
disabled,
|
|
461
123
|
id,
|
|
@@ -482,20 +144,11 @@ const Select$1 = memo(({ allowClear, autoFocus, className, classNames, defaultOp
|
|
|
482
144
|
className: cx(styles$1.value, classNames?.value),
|
|
483
145
|
children: renderValue
|
|
484
146
|
}),
|
|
485
|
-
/* @__PURE__ */
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
onClick: handleClear,
|
|
491
|
-
children: /* @__PURE__ */ jsx(Icon, {
|
|
492
|
-
icon: X,
|
|
493
|
-
size: "small"
|
|
494
|
-
})
|
|
495
|
-
}), suffixIconNode !== null && suffixIconNode !== void 0 && /* @__PURE__ */ jsx(Select.Icon, {
|
|
496
|
-
className: cx(styles$1.icon, classNames?.icon),
|
|
497
|
-
children: suffixIconNode
|
|
498
|
-
})]
|
|
147
|
+
/* @__PURE__ */ jsx(SelectTriggerSuffix, {
|
|
148
|
+
classNames,
|
|
149
|
+
showClear,
|
|
150
|
+
suffixIconNode,
|
|
151
|
+
onClear: handleClear
|
|
499
152
|
})
|
|
500
153
|
]
|
|
501
154
|
}), /* @__PURE__ */ jsx(Select.Portal, {
|
|
@@ -509,42 +162,21 @@ const Select$1 = memo(({ allowClear, autoFocus, className, classNames, defaultOp
|
|
|
509
162
|
children: /* @__PURE__ */ jsxs(Select.Popup, {
|
|
510
163
|
style: popupStyle,
|
|
511
164
|
className: cx(styles.popup, styles$1.popup, popupClassName, classNames?.popup, classNames?.dropdown),
|
|
512
|
-
children: [shouldShowSearch && /* @__PURE__ */ jsx(
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
className: cx(styles$1.list, classNames?.list),
|
|
528
|
-
"data-virtual": virtual || void 0,
|
|
529
|
-
children: content
|
|
530
|
-
});
|
|
531
|
-
return /* @__PURE__ */ jsx(Select.List, {
|
|
532
|
-
className: cx(styles$1.list, classNames?.list),
|
|
533
|
-
"data-virtual": virtual || void 0,
|
|
534
|
-
ref: listRef,
|
|
535
|
-
style: virtualListStyle,
|
|
536
|
-
tabIndex: virtual ? -1 : void 0,
|
|
537
|
-
onPointerDown: virtual ? markPointerScroll : void 0,
|
|
538
|
-
onScroll: virtual ? handleListScroll : void 0,
|
|
539
|
-
onTouchMove: virtual ? markPointerScroll : void 0,
|
|
540
|
-
onWheel: virtual ? markPointerScroll : void 0,
|
|
541
|
-
children: /* @__PURE__ */ jsx(Virtualizer, {
|
|
542
|
-
itemSize: listItemHeight,
|
|
543
|
-
keepMounted: keepMountedIndices,
|
|
544
|
-
children: content
|
|
545
|
-
})
|
|
546
|
-
});
|
|
547
|
-
})()]
|
|
165
|
+
children: [shouldShowSearch && /* @__PURE__ */ jsx(SelectSearchInput, {
|
|
166
|
+
classNames,
|
|
167
|
+
placeholder,
|
|
168
|
+
stopPropagation: stopSearchPropagation,
|
|
169
|
+
value: searchValue,
|
|
170
|
+
onChange: handleSearchChange,
|
|
171
|
+
onKeyDown: handleSearchKeyDown
|
|
172
|
+
}), /* @__PURE__ */ jsx(SelectListSection, {
|
|
173
|
+
classNames,
|
|
174
|
+
isEmpty,
|
|
175
|
+
listContent,
|
|
176
|
+
listItemHeight,
|
|
177
|
+
virtual,
|
|
178
|
+
virtualState
|
|
179
|
+
})]
|
|
548
180
|
})
|
|
549
181
|
})
|
|
550
182
|
})]
|