@meta-1/design 0.0.179 → 0.0.181
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
|
@@ -16,8 +16,10 @@ import { cn } from "@meta-1/design/lib";
|
|
|
16
16
|
import type { ButtonProps } from "../button";
|
|
17
17
|
import { Checkbox } from "../checkbox";
|
|
18
18
|
|
|
19
|
+
export type ComboSelectValueType = string | number;
|
|
20
|
+
|
|
19
21
|
export interface ComboSelectOptionProps<Data> {
|
|
20
|
-
value:
|
|
22
|
+
value: ComboSelectValueType;
|
|
21
23
|
label: ReactNode;
|
|
22
24
|
raw?: Data;
|
|
23
25
|
}
|
|
@@ -30,8 +32,8 @@ export interface ComboSelectProps<Data = unknown> {
|
|
|
30
32
|
searchPlaceholder?: string;
|
|
31
33
|
empty?: ReactNode;
|
|
32
34
|
className?: string;
|
|
33
|
-
onChange?: (value:
|
|
34
|
-
value?:
|
|
35
|
+
onChange?: (value: ComboSelectValueType | ComboSelectValueType[]) => void;
|
|
36
|
+
value?: ComboSelectValueType | ComboSelectValueType[];
|
|
35
37
|
loading?: boolean;
|
|
36
38
|
filter?: (value: string, search: string, option?: ComboSelectOptionProps<Data>) => boolean;
|
|
37
39
|
multiple?: boolean;
|
|
@@ -58,7 +60,7 @@ function SelectedLabels({
|
|
|
58
60
|
<>
|
|
59
61
|
{selectedValues.map((v) => {
|
|
60
62
|
// biome-ignore lint/suspicious/noExplicitAny: <options>
|
|
61
|
-
const label = options.find((option: any) => option.value === v)?.label || v;
|
|
63
|
+
const label = options.find((option: any) => String(option.value) === v)?.label || v;
|
|
62
64
|
return label ? (
|
|
63
65
|
<div className="my-0.5 mr-1 rounded bg-secondary px-2 py-[3px] text-sm" key={v}>
|
|
64
66
|
{label}
|
|
@@ -76,7 +78,7 @@ function SelectedLabels({
|
|
|
76
78
|
);
|
|
77
79
|
}
|
|
78
80
|
|
|
79
|
-
function handleSelect(
|
|
81
|
+
function handleSelect<Data>(
|
|
80
82
|
optionValue: string,
|
|
81
83
|
multiple: boolean,
|
|
82
84
|
isSelected: boolean,
|
|
@@ -84,8 +86,9 @@ function handleSelect(
|
|
|
84
86
|
limit: number,
|
|
85
87
|
setSelectedValues: (v: string[]) => void,
|
|
86
88
|
setValueState: (v: string) => void,
|
|
87
|
-
onChange: (v:
|
|
89
|
+
onChange: (v: ComboSelectValueType | ComboSelectValueType[]) => void,
|
|
88
90
|
setOpen: (v: boolean) => void,
|
|
91
|
+
options: ComboSelectOptionProps<Data>[],
|
|
89
92
|
) {
|
|
90
93
|
if (multiple) {
|
|
91
94
|
if (isSelected) {
|
|
@@ -96,13 +99,20 @@ function handleSelect(
|
|
|
96
99
|
}
|
|
97
100
|
selectedValues.push(optionValue);
|
|
98
101
|
}
|
|
99
|
-
const
|
|
100
|
-
setSelectedValues(
|
|
101
|
-
|
|
102
|
+
const stringValues = cloneDeep(selectedValues);
|
|
103
|
+
setSelectedValues(stringValues);
|
|
104
|
+
// 转换回原始类型
|
|
105
|
+
const originalValues = stringValues
|
|
106
|
+
.map((v) => options.find((opt) => String(opt.value) === v)?.value)
|
|
107
|
+
.filter((v) => v !== undefined) as ComboSelectValueType[];
|
|
108
|
+
onChange?.(originalValues);
|
|
102
109
|
} else {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
110
|
+
setValueState(optionValue);
|
|
111
|
+
// 转换回原始类型
|
|
112
|
+
const option = options.find((opt) => String(opt.value) === optionValue);
|
|
113
|
+
if (option) {
|
|
114
|
+
onChange?.(option.value);
|
|
115
|
+
}
|
|
106
116
|
setOpen(false);
|
|
107
117
|
}
|
|
108
118
|
}
|
|
@@ -126,7 +136,7 @@ function ComboSelectCommandList<Data>({
|
|
|
126
136
|
limit: number;
|
|
127
137
|
setSelectedValues: (v: string[]) => void;
|
|
128
138
|
setValueState: (v: string) => void;
|
|
129
|
-
onChange?: (v:
|
|
139
|
+
onChange?: (v: ComboSelectValueType | ComboSelectValueType[]) => void;
|
|
130
140
|
onSearch?: (v: string) => void;
|
|
131
141
|
value: string | string[];
|
|
132
142
|
setOpen: (v: boolean) => void;
|
|
@@ -135,15 +145,15 @@ function ComboSelectCommandList<Data>({
|
|
|
135
145
|
<CommandList>
|
|
136
146
|
{loading
|
|
137
147
|
? null
|
|
138
|
-
:
|
|
139
|
-
|
|
140
|
-
const isSelected = selectedValues.includes(
|
|
148
|
+
: options.map((option) => {
|
|
149
|
+
const optionValueStr = String(option.value);
|
|
150
|
+
const isSelected = selectedValues.includes(optionValueStr);
|
|
141
151
|
return (
|
|
142
152
|
<CommandItem
|
|
143
153
|
key={option.value}
|
|
144
154
|
onSelect={() =>
|
|
145
155
|
handleSelect(
|
|
146
|
-
|
|
156
|
+
optionValueStr,
|
|
147
157
|
multiple,
|
|
148
158
|
isSelected,
|
|
149
159
|
selectedValues,
|
|
@@ -152,14 +162,17 @@ function ComboSelectCommandList<Data>({
|
|
|
152
162
|
setValueState,
|
|
153
163
|
onChange!,
|
|
154
164
|
setOpen,
|
|
165
|
+
options,
|
|
155
166
|
)
|
|
156
167
|
}
|
|
157
|
-
value={
|
|
168
|
+
value={optionValueStr}
|
|
158
169
|
>
|
|
159
170
|
{multiple ? <Checkbox checked={isSelected} /> : null}
|
|
160
171
|
{option.label}
|
|
161
172
|
{multiple ? null : (
|
|
162
|
-
<CheckIcon
|
|
173
|
+
<CheckIcon
|
|
174
|
+
className={cn("ml-auto h-4 w-4", value === optionValueStr ? "opacity-100" : "opacity-0")}
|
|
175
|
+
/>
|
|
163
176
|
)}
|
|
164
177
|
</CommandItem>
|
|
165
178
|
);
|
|
@@ -174,12 +187,12 @@ type ComboSelectButtonProps<Data> = {
|
|
|
174
187
|
className?: string;
|
|
175
188
|
placeholderDom: ReactNode;
|
|
176
189
|
options: ComboSelectOptionProps<Data>[];
|
|
177
|
-
valueState?: string
|
|
190
|
+
valueState?: string;
|
|
178
191
|
loading: boolean;
|
|
179
192
|
showClear: boolean;
|
|
180
193
|
setSelectedValues: (v: string[]) => void;
|
|
181
194
|
setValueState: (v: string) => void;
|
|
182
|
-
onChange?: (v:
|
|
195
|
+
onChange?: (v: ComboSelectValueType | ComboSelectValueType[]) => void;
|
|
183
196
|
onSearch?: (v: string) => void;
|
|
184
197
|
initLoading: boolean;
|
|
185
198
|
} & Omit<ButtonProps, "onChange">;
|
|
@@ -224,7 +237,9 @@ const ComboSelectButton = forwardRef(
|
|
|
224
237
|
<SelectedLabels options={options} placeholderDom={placeholderDom} selectedValues={selectedValues} />
|
|
225
238
|
) : (
|
|
226
239
|
<span>
|
|
227
|
-
{valueState
|
|
240
|
+
{valueState
|
|
241
|
+
? options.find((option) => String(option.value) === valueState)?.label || valueState
|
|
242
|
+
: placeholderDom}
|
|
228
243
|
</span>
|
|
229
244
|
)}
|
|
230
245
|
</div>
|
|
@@ -279,19 +294,26 @@ export function ComboSelect<Data = unknown>(props: ComboSelectProps<Data>) {
|
|
|
279
294
|
|
|
280
295
|
const [open, setOpen] = useState(false);
|
|
281
296
|
const containerRef = useRef(null);
|
|
282
|
-
|
|
297
|
+
// 内部统一使用 string 类型
|
|
298
|
+
const [valueState, setValueState] = useState<string | undefined>(
|
|
299
|
+
value !== undefined && !Array.isArray(value) ? String(value) : undefined,
|
|
300
|
+
);
|
|
283
301
|
const [selectedValues, setSelectedValues] = useState<string[]>(
|
|
284
|
-
multiple
|
|
302
|
+
multiple && Array.isArray(value)
|
|
303
|
+
? value.map((v) => String(v))
|
|
304
|
+
: !multiple && value !== undefined
|
|
305
|
+
? [String(value)]
|
|
306
|
+
: [],
|
|
285
307
|
);
|
|
286
308
|
const size = useSize(containerRef);
|
|
287
309
|
|
|
288
310
|
useEffect(() => {
|
|
289
|
-
if (multiple) {
|
|
290
|
-
setSelectedValues(
|
|
291
|
-
|
|
292
|
-
);
|
|
293
|
-
} else {
|
|
294
|
-
setValueState(
|
|
311
|
+
if (multiple && Array.isArray(value)) {
|
|
312
|
+
setSelectedValues(value.map((v) => String(v)));
|
|
313
|
+
} else if (!multiple && value !== undefined && !Array.isArray(value)) {
|
|
314
|
+
setValueState(String(value));
|
|
315
|
+
} else if (!multiple && (value === undefined || value === null)) {
|
|
316
|
+
setValueState(undefined);
|
|
295
317
|
}
|
|
296
318
|
}, [value, multiple]);
|
|
297
319
|
|
|
@@ -328,7 +350,7 @@ export function ComboSelect<Data = unknown>(props: ComboSelectProps<Data>) {
|
|
|
328
350
|
filter(
|
|
329
351
|
value,
|
|
330
352
|
search,
|
|
331
|
-
options.find((option) => option.value === value),
|
|
353
|
+
options.find((option) => String(option.value) === value),
|
|
332
354
|
)
|
|
333
355
|
? 1
|
|
334
356
|
: 0
|
|
@@ -355,7 +377,7 @@ export function ComboSelect<Data = unknown>(props: ComboSelectProps<Data>) {
|
|
|
355
377
|
setOpen={setOpen}
|
|
356
378
|
setSelectedValues={setSelectedValues}
|
|
357
379
|
setValueState={setValueState}
|
|
358
|
-
value={
|
|
380
|
+
value={valueState || selectedValues}
|
|
359
381
|
/>
|
|
360
382
|
</Command>
|
|
361
383
|
</PopoverContent>
|
|
@@ -5,17 +5,19 @@ import { Empty } from "@meta-1/design/components/uix/empty";
|
|
|
5
5
|
import { cn } from "@meta-1/design/lib";
|
|
6
6
|
import { SelectContent, SelectItem, SelectTrigger, SelectValue, Select as UISelect } from "../../ui/select";
|
|
7
7
|
|
|
8
|
+
export type SelectValueType = string | number;
|
|
9
|
+
|
|
8
10
|
export interface SelectOptionProps {
|
|
9
|
-
value:
|
|
11
|
+
value: SelectValueType;
|
|
10
12
|
label: ReactNode;
|
|
11
13
|
disabled?: boolean;
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
export interface SelectProps {
|
|
15
17
|
options: SelectOptionProps[];
|
|
16
|
-
value?:
|
|
18
|
+
value?: SelectValueType;
|
|
17
19
|
defaultValue?: string;
|
|
18
|
-
onChange?: (value:
|
|
20
|
+
onChange?: (value: SelectValueType) => void;
|
|
19
21
|
placeholder?: string;
|
|
20
22
|
className?: string;
|
|
21
23
|
side?: "top" | "right" | "bottom" | "left";
|
|
@@ -51,7 +53,11 @@ export const Select = forwardRef<HTMLSelectElement, SelectProps>((props, _ref) =
|
|
|
51
53
|
|
|
52
54
|
const handleChange = (val: string) => {
|
|
53
55
|
if (onChange) {
|
|
54
|
-
|
|
56
|
+
// 尝试找到原始选项,返回原始类型的 value
|
|
57
|
+
const option = options.find((opt) => String(opt.value) === val);
|
|
58
|
+
if (option) {
|
|
59
|
+
onChange(option.value);
|
|
60
|
+
}
|
|
55
61
|
}
|
|
56
62
|
if (!isControlled) {
|
|
57
63
|
setInnerValue(val);
|
|
@@ -70,7 +76,7 @@ export const Select = forwardRef<HTMLSelectElement, SelectProps>((props, _ref) =
|
|
|
70
76
|
onOpenChange={onOpenChange}
|
|
71
77
|
onValueChange={handleChange}
|
|
72
78
|
open={open}
|
|
73
|
-
value={currentValue}
|
|
79
|
+
value={currentValue !== undefined ? String(currentValue) : undefined}
|
|
74
80
|
>
|
|
75
81
|
<div className={cn("relative inline-block", allowClear && currentValue ? "group" : "", className)}>
|
|
76
82
|
<SelectTrigger className={cn("flex w-full", triggerClassName)}>
|
|
@@ -93,7 +99,7 @@ export const Select = forwardRef<HTMLSelectElement, SelectProps>((props, _ref) =
|
|
|
93
99
|
>
|
|
94
100
|
{options?.length
|
|
95
101
|
? options.map((option) => (
|
|
96
|
-
<SelectItem disabled={option.disabled} key={option.value} value={option.value}>
|
|
102
|
+
<SelectItem disabled={option.disabled} key={option.value} value={String(option.value)}>
|
|
97
103
|
{option.label}
|
|
98
104
|
</SelectItem>
|
|
99
105
|
))
|
|
@@ -45,13 +45,14 @@ export const TagsInput = forwardRef<HTMLDivElement, TagsInputProps>((props, ref)
|
|
|
45
45
|
const inputRef = useRef<HTMLInputElement>(null);
|
|
46
46
|
const [inputValue, setInputValue] = useState("");
|
|
47
47
|
const [tags, setTags] = useState<string[]>(value || defaultValue);
|
|
48
|
+
const [isComposing, setIsComposing] = useState(false);
|
|
48
49
|
|
|
49
50
|
const isControlled = value !== undefined;
|
|
50
|
-
const currentTags = isControlled ? value : tags;
|
|
51
|
+
const currentTags = isControlled ? value || [] : tags;
|
|
51
52
|
|
|
52
53
|
useEffect(() => {
|
|
53
54
|
if (isControlled) {
|
|
54
|
-
setTags(value);
|
|
55
|
+
setTags(value || []);
|
|
55
56
|
}
|
|
56
57
|
}, [value, isControlled]);
|
|
57
58
|
|
|
@@ -93,7 +94,8 @@ export const TagsInput = forwardRef<HTMLDivElement, TagsInputProps>((props, ref)
|
|
|
93
94
|
const target = e.target as HTMLInputElement;
|
|
94
95
|
const value = target.value;
|
|
95
96
|
|
|
96
|
-
|
|
97
|
+
if (isComposing) return;
|
|
98
|
+
|
|
97
99
|
if (e.key === "Enter" || (typeof separator === "string" && e.key === separator)) {
|
|
98
100
|
e.preventDefault();
|
|
99
101
|
if (value) {
|
|
@@ -102,7 +104,6 @@ export const TagsInput = forwardRef<HTMLDivElement, TagsInputProps>((props, ref)
|
|
|
102
104
|
return;
|
|
103
105
|
}
|
|
104
106
|
|
|
105
|
-
// Backspace 删除最后一个标签
|
|
106
107
|
if (e.key === "Backspace" && !value && currentTags.length > 0) {
|
|
107
108
|
e.preventDefault();
|
|
108
109
|
removeTag(currentTags.length - 1);
|
|
@@ -199,6 +200,8 @@ export const TagsInput = forwardRef<HTMLDivElement, TagsInputProps>((props, ref)
|
|
|
199
200
|
disabled={disabled || (maxTags !== undefined && currentTags.length >= maxTags)}
|
|
200
201
|
onBlur={handleBlur}
|
|
201
202
|
onChange={handleInputChange}
|
|
203
|
+
onCompositionEnd={() => setIsComposing(false)}
|
|
204
|
+
onCompositionStart={() => setIsComposing(true)}
|
|
202
205
|
onKeyDown={handleKeyDown}
|
|
203
206
|
placeholder={currentTags.length === 0 ? placeholder : undefined}
|
|
204
207
|
ref={inputRef}
|