@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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@firecms/ui",
3
3
  "type": "module",
4
- "version": "3.0.0-canary.210",
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": "b8bcb3a2230e8b02644ce5ed0891b273e7e7d630",
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
- interface MultiSelectContextProps {
22
- fieldValue?: string[];
23
- onItemClick: (v: string) => void;
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
- export const MultiSelectContext = React.createContext<MultiSelectContextProps>({} as any);
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?: string[],
42
+ value?: T[],
51
43
  inputClassName?: string,
52
44
  onChange?: React.EventHandler<ChangeEvent<HTMLSelectElement>>,
53
- onValueChange?: (updatedValue: string[]) => void,
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: string[]) => React.ReactNode;
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<string[]>(value ?? []);
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 string[]
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: string) {
126
- let newSelectedValues: string[];
127
- if (selectedValues.includes(newValue)) {
128
- newSelectedValues = selectedValues.filter((v) => v !== newValue);
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: string[]) {
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: string) => {
153
- const newSelectedValues = selectedValues.includes(value)
154
- ? selectedValues.filter((v) => v !== value)
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
- className={cls("transition", isPopoverOpen ? "rotate-180" : "")}/>
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
- className={cls("transition", isPopoverOpen ? "rotate-180" : "")}/>
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: string;
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
- children,
342
- value,
343
- className
344
- }: MultiSelectItemProps) {
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 ?? []).includes(value);
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 SelectProps = {
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?: string,
24
+ value?: T,
23
25
  className?: string,
24
26
  inputClassName?: string,
25
27
  onChange?: React.EventHandler<ChangeEvent<HTMLSelectElement>>,
26
- onValueChange?: (updatedValue: string) => void,
28
+ onValueChange?: (updatedValue: T) => void,
27
29
  placeholder?: React.ReactNode,
28
- renderValue?: (value: string) => React.ReactNode,
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
- inputRef,
43
- open,
44
- name,
45
- fullWidth = false,
46
- id,
47
- onOpenChange,
48
- value,
49
- onChange,
50
- onValueChange,
51
- className,
52
- inputClassName,
53
- placeholder,
54
- renderValue,
55
- label,
56
- size = "large",
57
- error,
58
- disabled,
59
- padding = true,
60
- position = "item-aligned",
61
- endAdornment,
62
- invisible,
63
- children,
64
- ...props
65
- }, ref) => {
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
- onValueChange?.(newValue);
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: newValue
87
+ value: typedValue
80
88
  }
81
- } as ChangeEvent<HTMLSelectElement>;
89
+ } as unknown as ChangeEvent<HTMLSelectElement>;
82
90
  onChange(event);
83
91
  }
84
- }, [onChange, value, onValueChange]);
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={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
- className={cls("transition", open ? "rotate-180" : "", {
191
- "px-2": size === "large",
192
- "px-1": size === "medium" || size === "small",
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: string,
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
- value,
223
- children,
224
- disabled,
225
- className
226
- }: SelectItemProps) {
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={value}
229
- value={value}
240
+ key={stringValue}
241
+ value={stringValue}
230
242
  disabled={disabled}
231
243
  className={cls(
232
244
  "w-full",