@firecms/ui 3.0.0-canary.210 → 3.0.0-canary.211
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/components/MultiSelect.d.ts +13 -21
- package/dist/components/Select.d.ts +9 -8
- package/dist/index.es.js +54 -40
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +54 -40
- package/dist/index.umd.js.map +1 -1
- package/package.json +2 -2
- package/src/components/MultiSelect.tsx +38 -46
- package/src/components/Select.tsx +62 -50
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "@firecms/ui",
|
3
3
|
"type": "module",
|
4
|
-
"version": "3.0.0-canary.
|
4
|
+
"version": "3.0.0-canary.211",
|
5
5
|
"description": "Awesome Firebase/Firestore-based headless open-source CMS",
|
6
6
|
"funding": {
|
7
7
|
"url": "https://github.com/sponsors/firecmsco"
|
@@ -115,7 +115,7 @@
|
|
115
115
|
"index.css",
|
116
116
|
"tailwind.config.js"
|
117
117
|
],
|
118
|
-
"gitHead": "
|
118
|
+
"gitHead": "ee9fd6ecade3bb23be5cfa025117be9fe2b6fd7f",
|
119
119
|
"publishConfig": {
|
120
120
|
"access": "public"
|
121
121
|
}
|
@@ -18,39 +18,31 @@ import {
|
|
18
18
|
} from "../styles";
|
19
19
|
import { useInjectStyles } from "../hooks";
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
21
|
+
export type MultiSelectValue = string | number | boolean;
|
22
|
+
|
23
|
+
// Make the context properly generic
|
24
|
+
interface MultiSelectContextProps<T extends MultiSelectValue = string> {
|
25
|
+
fieldValue?: T[];
|
26
|
+
onItemClick: (v: T) => void;
|
24
27
|
}
|
25
28
|
|
26
|
-
|
29
|
+
// Create a proper generic context
|
30
|
+
export const MultiSelectContext = React.createContext<MultiSelectContextProps<any>>({} as any);
|
27
31
|
|
28
32
|
/**
|
29
33
|
* Props for MultiSelect component
|
30
34
|
*/
|
31
|
-
interface MultiSelectProps {
|
32
|
-
|
33
|
-
/**
|
34
|
-
* The modality of the popover. When set to true, interaction with outside elements
|
35
|
-
* will be disabled and only popover content will be visible to screen readers.
|
36
|
-
* Optional, defaults to false.
|
37
|
-
*/
|
35
|
+
interface MultiSelectProps<T extends MultiSelectValue = string> {
|
38
36
|
modalPopover?: boolean;
|
39
|
-
|
40
|
-
/**
|
41
|
-
* Additional class names to apply custom styles to the multi-select component.
|
42
|
-
* Optional, can be used to add custom styles.
|
43
|
-
*/
|
44
37
|
className?: string;
|
45
|
-
|
46
38
|
open?: boolean,
|
47
39
|
name?: string,
|
48
40
|
id?: string,
|
49
41
|
onOpenChange?: (open: boolean) => void,
|
50
|
-
value?:
|
42
|
+
value?: T[],
|
51
43
|
inputClassName?: string,
|
52
44
|
onChange?: React.EventHandler<ChangeEvent<HTMLSelectElement>>,
|
53
|
-
onValueChange?: (updatedValue:
|
45
|
+
onValueChange?: (updatedValue: T[]) => void,
|
54
46
|
placeholder?: React.ReactNode,
|
55
47
|
size?: "small" | "medium",
|
56
48
|
useChips?: boolean,
|
@@ -66,9 +58,10 @@ interface MultiSelectProps {
|
|
66
58
|
padding?: boolean,
|
67
59
|
invisible?: boolean,
|
68
60
|
children: React.ReactNode;
|
69
|
-
renderValues?: (values:
|
61
|
+
renderValues?: (values: T[]) => React.ReactNode;
|
70
62
|
}
|
71
63
|
|
64
|
+
// Use generic type for the forwarded ref
|
72
65
|
export const MultiSelect = React.forwardRef<
|
73
66
|
HTMLButtonElement,
|
74
67
|
MultiSelectProps
|
@@ -95,8 +88,9 @@ export const MultiSelect = React.forwardRef<
|
|
95
88
|
},
|
96
89
|
ref
|
97
90
|
) => {
|
91
|
+
// Properly type the state variables to match the generic props
|
98
92
|
const [isPopoverOpen, setIsPopoverOpen] = React.useState(open ?? false);
|
99
|
-
const [selectedValues, setSelectedValues] = React.useState<
|
93
|
+
const [selectedValues, setSelectedValues] = React.useState<any[]>(value ?? []);
|
100
94
|
|
101
95
|
const onPopoverOpenChange = (open: boolean) => {
|
102
96
|
setIsPopoverOpen(open);
|
@@ -115,24 +109,24 @@ export const MultiSelect = React.forwardRef<
|
|
115
109
|
return child.props.value;
|
116
110
|
}
|
117
111
|
return null;
|
118
|
-
}).filter(Boolean) as
|
112
|
+
}).filter(Boolean) as any[]
|
119
113
|
: [];
|
120
114
|
|
121
115
|
React.useEffect(() => {
|
122
116
|
setSelectedValues(value ?? []);
|
123
117
|
}, [value]);
|
124
118
|
|
125
|
-
function onItemClick(newValue:
|
126
|
-
let newSelectedValues:
|
127
|
-
if (selectedValues.
|
128
|
-
newSelectedValues = selectedValues.filter(
|
119
|
+
function onItemClick(newValue: any) {
|
120
|
+
let newSelectedValues: any[];
|
121
|
+
if (selectedValues.some(v => String(v) === String(newValue))) {
|
122
|
+
newSelectedValues = selectedValues.filter(v => String(v) !== String(newValue));
|
129
123
|
} else {
|
130
124
|
newSelectedValues = [...selectedValues, newValue];
|
131
125
|
}
|
132
126
|
updateValues(newSelectedValues);
|
133
127
|
}
|
134
128
|
|
135
|
-
function updateValues(values:
|
129
|
+
function updateValues(values: any[]) {
|
136
130
|
setSelectedValues(values);
|
137
131
|
onValueChange?.(values);
|
138
132
|
}
|
@@ -149,9 +143,9 @@ export const MultiSelect = React.forwardRef<
|
|
149
143
|
}
|
150
144
|
};
|
151
145
|
|
152
|
-
const toggleOption = (value:
|
153
|
-
const newSelectedValues = selectedValues.
|
154
|
-
? selectedValues.filter(
|
146
|
+
const toggleOption = (value: any) => {
|
147
|
+
const newSelectedValues = selectedValues.some(v => String(v) === String(value))
|
148
|
+
? selectedValues.filter(v => String(v) !== String(value))
|
155
149
|
: [...selectedValues, value];
|
156
150
|
updateValues(newSelectedValues);
|
157
151
|
};
|
@@ -222,14 +216,14 @@ export const MultiSelect = React.forwardRef<
|
|
222
216
|
}
|
223
217
|
}).filter(Boolean);
|
224
218
|
|
225
|
-
const option = childrenProps.find((o) => o.value === value);
|
219
|
+
const option = childrenProps.find((o) => String(o.value) === String(value));
|
226
220
|
if (!useChips) {
|
227
221
|
return option?.children;
|
228
222
|
}
|
229
223
|
return (
|
230
224
|
<Chip
|
231
225
|
size={"medium"}
|
232
|
-
key={value}
|
226
|
+
key={String(value)}
|
233
227
|
className={cls("flex flex-row items-center p-1")}
|
234
228
|
>
|
235
229
|
{option?.children}
|
@@ -255,7 +249,7 @@ export const MultiSelect = React.forwardRef<
|
|
255
249
|
/>}
|
256
250
|
<div className={cls("px-2 h-full flex items-center")}>
|
257
251
|
<KeyboardArrowDownIcon size={"small"}
|
258
|
-
|
252
|
+
className={cls("transition", isPopoverOpen ? "rotate-180" : "")}/>
|
259
253
|
</div>
|
260
254
|
</div>
|
261
255
|
</div>
|
@@ -266,7 +260,7 @@ export const MultiSelect = React.forwardRef<
|
|
266
260
|
</span>
|
267
261
|
<div className={cls("px-2 h-full flex items-center")}>
|
268
262
|
<KeyboardArrowDownIcon size={"small"}
|
269
|
-
|
263
|
+
className={cls("transition", isPopoverOpen ? "rotate-180" : "")}/>
|
270
264
|
</div>
|
271
265
|
</div>
|
272
266
|
)}
|
@@ -319,7 +313,6 @@ export const MultiSelect = React.forwardRef<
|
|
319
313
|
</CommandPrimitive.Item>}
|
320
314
|
{children}
|
321
315
|
</CommandPrimitive.Group>
|
322
|
-
|
323
316
|
</CommandPrimitive.List>
|
324
317
|
</CommandPrimitive>
|
325
318
|
</PopoverPrimitive.Content>
|
@@ -331,18 +324,17 @@ export const MultiSelect = React.forwardRef<
|
|
331
324
|
|
332
325
|
MultiSelect.displayName = "MultiSelect";
|
333
326
|
|
334
|
-
export interface MultiSelectItemProps {
|
335
|
-
value:
|
327
|
+
export interface MultiSelectItemProps<T extends MultiSelectValue = string> {
|
328
|
+
value: T;
|
336
329
|
children?: React.ReactNode,
|
337
330
|
className?: string;
|
338
331
|
}
|
339
332
|
|
340
|
-
export function MultiSelectItem({
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
333
|
+
export function MultiSelectItem<T extends MultiSelectValue = string>({
|
334
|
+
children,
|
335
|
+
value,
|
336
|
+
className
|
337
|
+
}: MultiSelectItemProps<T>) {
|
346
338
|
const context = React.useContext(MultiSelectContext);
|
347
339
|
if (!context) throw new Error("MultiSelectItem must be used inside a MultiSelect");
|
348
340
|
const {
|
@@ -350,9 +342,9 @@ export function MultiSelectItem({
|
|
350
342
|
onItemClick
|
351
343
|
} = context;
|
352
344
|
|
353
|
-
const isSelected = (fieldValue ?? []).
|
345
|
+
const isSelected = (fieldValue ?? []).some(v => String(v) === String(value));
|
346
|
+
|
354
347
|
return <CommandPrimitive.Item
|
355
|
-
// value={value}
|
356
348
|
onMouseDown={(e) => {
|
357
349
|
e.preventDefault();
|
358
350
|
e.stopPropagation();
|
@@ -375,9 +367,9 @@ export function MultiSelectItem({
|
|
375
367
|
<InnerCheckBox checked={isSelected}/>
|
376
368
|
{children}
|
377
369
|
</CommandPrimitive.Item>;
|
378
|
-
|
379
370
|
}
|
380
371
|
|
372
|
+
|
381
373
|
function InnerCheckBox({ checked }: { checked: boolean }) {
|
382
374
|
return <div className={cls(
|
383
375
|
"p-2",
|
@@ -13,19 +13,21 @@ import { CheckIcon, KeyboardArrowDownIcon } from "../icons";
|
|
13
13
|
import { cls } from "../util";
|
14
14
|
import { SelectInputLabel } from "./common/SelectInputLabel";
|
15
15
|
|
16
|
-
export type
|
16
|
+
export type SelectValue = string | number | boolean;
|
17
|
+
|
18
|
+
export type SelectProps<T extends SelectValue = string> = {
|
17
19
|
open?: boolean,
|
18
20
|
name?: string,
|
19
21
|
fullWidth?: boolean,
|
20
22
|
id?: string,
|
21
23
|
onOpenChange?: (open: boolean) => void,
|
22
|
-
value?:
|
24
|
+
value?: T,
|
23
25
|
className?: string,
|
24
26
|
inputClassName?: string,
|
25
27
|
onChange?: React.EventHandler<ChangeEvent<HTMLSelectElement>>,
|
26
|
-
onValueChange?: (updatedValue:
|
28
|
+
onValueChange?: (updatedValue: T) => void,
|
27
29
|
placeholder?: React.ReactNode,
|
28
|
-
renderValue?: (value:
|
30
|
+
renderValue?: (value: T) => React.ReactNode,
|
29
31
|
size?: "small" | "medium" | "large",
|
30
32
|
label?: React.ReactNode | string,
|
31
33
|
disabled?: boolean,
|
@@ -39,30 +41,30 @@ export type SelectProps = {
|
|
39
41
|
};
|
40
42
|
|
41
43
|
export const Select = forwardRef<HTMLDivElement, SelectProps>(({
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
44
|
+
inputRef,
|
45
|
+
open,
|
46
|
+
name,
|
47
|
+
fullWidth = false,
|
48
|
+
id,
|
49
|
+
onOpenChange,
|
50
|
+
value,
|
51
|
+
onChange,
|
52
|
+
onValueChange,
|
53
|
+
className,
|
54
|
+
inputClassName,
|
55
|
+
placeholder,
|
56
|
+
renderValue,
|
57
|
+
label,
|
58
|
+
size = "large",
|
59
|
+
error,
|
60
|
+
disabled,
|
61
|
+
padding = true,
|
62
|
+
position = "item-aligned",
|
63
|
+
endAdornment,
|
64
|
+
invisible,
|
65
|
+
children,
|
66
|
+
...props
|
67
|
+
}, ref) => {
|
66
68
|
|
67
69
|
const [openInternal, setOpenInternal] = useState(open ?? false);
|
68
70
|
|
@@ -71,24 +73,32 @@ export const Select = forwardRef<HTMLDivElement, SelectProps>(({
|
|
71
73
|
}, [open]);
|
72
74
|
|
73
75
|
const onValueChangeInternal = useCallback((newValue: string) => {
|
74
|
-
|
76
|
+
// Convert string value to appropriate type
|
77
|
+
let typedValue: SelectValue = newValue;
|
78
|
+
if (newValue === "true") typedValue = true;
|
79
|
+
else if (newValue === "false") typedValue = false;
|
80
|
+
else if (!isNaN(Number(newValue)) && newValue.trim() !== '') typedValue = Number(newValue);
|
81
|
+
|
82
|
+
onValueChange?.(typedValue as any);
|
75
83
|
if (onChange) {
|
76
84
|
const event = {
|
77
85
|
target: {
|
78
86
|
name,
|
79
|
-
value:
|
87
|
+
value: typedValue
|
80
88
|
}
|
81
|
-
} as ChangeEvent<HTMLSelectElement>;
|
89
|
+
} as unknown as ChangeEvent<HTMLSelectElement>;
|
82
90
|
onChange(event);
|
83
91
|
}
|
84
|
-
}, [onChange,
|
92
|
+
}, [onChange, onValueChange, name]);
|
85
93
|
|
86
94
|
const hasValue = Array.isArray(value) ? value.length > 0 : (value != null && value !== "" && value !== undefined);
|
95
|
+
// Convert non-string values to strings for Radix UI
|
96
|
+
const stringValue = value !== undefined ? String(value) : undefined;
|
87
97
|
|
88
98
|
return (
|
89
99
|
<SelectPrimitive.Root
|
90
100
|
name={name}
|
91
|
-
value={
|
101
|
+
value={stringValue}
|
92
102
|
open={openInternal}
|
93
103
|
disabled={disabled}
|
94
104
|
onValueChange={onValueChangeInternal}
|
@@ -157,10 +167,9 @@ export const Select = forwardRef<HTMLDivElement, SelectProps>(({
|
|
157
167
|
}}
|
158
168
|
placeholder={placeholder}
|
159
169
|
className={"w-full"}>
|
160
|
-
{hasValue && value && renderValue ? renderValue(value) : placeholder}
|
170
|
+
{hasValue && value !== undefined && renderValue ? renderValue(value) : placeholder}
|
161
171
|
{/*{hasValue && !renderValue && value}*/}
|
162
172
|
{hasValue && !renderValue && (() => {
|
163
|
-
|
164
173
|
// @ts-ignore
|
165
174
|
const childrenProps: SelectItemProps[] = Children.map(children, (child) => {
|
166
175
|
if (React.isValidElement(child)) {
|
@@ -168,7 +177,7 @@ export const Select = forwardRef<HTMLDivElement, SelectProps>(({
|
|
168
177
|
}
|
169
178
|
}).filter(Boolean);
|
170
179
|
|
171
|
-
const option = childrenProps.find((o) => o.value === value);
|
180
|
+
const option = childrenProps.find((o) => String(o.value) === String(value));
|
172
181
|
return option?.children;
|
173
182
|
})()}
|
174
183
|
|
@@ -187,10 +196,10 @@ export const Select = forwardRef<HTMLDivElement, SelectProps>(({
|
|
187
196
|
)}
|
188
197
|
<SelectPrimitive.Icon asChild>
|
189
198
|
<KeyboardArrowDownIcon size={"medium"}
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
199
|
+
className={cls("transition", open ? "rotate-180" : "", {
|
200
|
+
"px-2": size === "large",
|
201
|
+
"px-1": size === "medium" || size === "small",
|
202
|
+
})}/>
|
194
203
|
</SelectPrimitive.Icon>
|
195
204
|
</div>
|
196
205
|
</SelectPrimitive.Trigger>
|
@@ -211,22 +220,25 @@ export const Select = forwardRef<HTMLDivElement, SelectProps>(({
|
|
211
220
|
|
212
221
|
Select.displayName = "Select";
|
213
222
|
|
214
|
-
export type SelectItemProps = {
|
215
|
-
value:
|
223
|
+
export type SelectItemProps<T extends SelectValue = string> = {
|
224
|
+
value: T,
|
216
225
|
children?: React.ReactNode,
|
217
226
|
disabled?: boolean,
|
218
227
|
className?: string,
|
219
228
|
};
|
220
229
|
|
221
|
-
export function SelectItem({
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
230
|
+
export function SelectItem<T extends SelectValue = string>({
|
231
|
+
value,
|
232
|
+
children,
|
233
|
+
disabled,
|
234
|
+
className
|
235
|
+
}: SelectItemProps<T>) {
|
236
|
+
// Convert value to string for Radix UI
|
237
|
+
const stringValue = String(value);
|
238
|
+
|
227
239
|
return <SelectPrimitive.Item
|
228
|
-
key={
|
229
|
-
value={
|
240
|
+
key={stringValue}
|
241
|
+
value={stringValue}
|
230
242
|
disabled={disabled}
|
231
243
|
className={cls(
|
232
244
|
"w-full",
|