@firecms/ui 3.0.0-beta.12 → 3.0.0-beta.14

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.
@@ -13,20 +13,22 @@ 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,
29
- size?: "small" | "medium" | "large",
30
+ renderValue?: (value: T) => React.ReactNode,
31
+ size?: "smallest" | "small" | "medium" | "large",
30
32
  label?: React.ReactNode | string,
31
33
  disabled?: boolean,
32
34
  error?: boolean,
@@ -36,6 +38,7 @@ export type SelectProps = {
36
38
  padding?: boolean,
37
39
  invisible?: boolean,
38
40
  children?: React.ReactNode;
41
+ dataType?: "string" | "number" | "boolean";
39
42
  };
40
43
 
41
44
  export const Select = forwardRef<HTMLDivElement, SelectProps>(({
@@ -61,6 +64,7 @@ export const Select = forwardRef<HTMLDivElement, SelectProps>(({
61
64
  endAdornment,
62
65
  invisible,
63
66
  children,
67
+ dataType = "string",
64
68
  ...props
65
69
  }, ref) => {
66
70
 
@@ -71,24 +75,35 @@ export const Select = forwardRef<HTMLDivElement, SelectProps>(({
71
75
  }, [open]);
72
76
 
73
77
  const onValueChangeInternal = useCallback((newValue: string) => {
74
- onValueChange?.(newValue);
78
+
79
+ let typedValue: SelectValue = newValue;
80
+ if (dataType === "boolean") {
81
+ if (newValue === "true") typedValue = true;
82
+ else if (newValue === "false") typedValue = false;
83
+ } else if (dataType === "number") {
84
+ if (!isNaN(Number(newValue)) && newValue.trim() !== "") typedValue = Number(newValue);
85
+ }
86
+
87
+ onValueChange?.(typedValue as any);
75
88
  if (onChange) {
76
89
  const event = {
77
90
  target: {
78
91
  name,
79
- value: newValue
92
+ value: typedValue
80
93
  }
81
- } as ChangeEvent<HTMLSelectElement>;
94
+ } as unknown as ChangeEvent<HTMLSelectElement>;
82
95
  onChange(event);
83
96
  }
84
- }, [onChange, value, onValueChange]);
97
+ }, [onChange, onValueChange, name]);
85
98
 
86
99
  const hasValue = Array.isArray(value) ? value.length > 0 : (value != null && value !== "" && value !== undefined);
100
+ // Convert non-string values to strings for Radix UI
101
+ const stringValue = value !== undefined ? String(value) : undefined;
87
102
 
88
103
  return (
89
104
  <SelectPrimitive.Root
90
105
  name={name}
91
- value={value}
106
+ value={stringValue}
92
107
  open={openInternal}
93
108
  disabled={disabled}
94
109
  onValueChange={onValueChangeInternal}
@@ -105,7 +120,8 @@ export const Select = forwardRef<HTMLDivElement, SelectProps>(({
105
120
  "relative flex items-center",
106
121
  className,
107
122
  {
108
- "min-h-[28px]": size === "small",
123
+ "min-h-[28px]": size === "smallest",
124
+ "min-h-[32px]": size === "small",
109
125
  "min-h-[42px]": size === "medium",
110
126
  "min-h-[64px]": size === "large",
111
127
  "w-fit": !fullWidth,
@@ -122,7 +138,7 @@ export const Select = forwardRef<HTMLDivElement, SelectProps>(({
122
138
  padding ? {
123
139
  "px-4": size === "large",
124
140
  "px-3": size === "medium",
125
- "px-2": size === "small"
141
+ "px-2": size === "small" || size === "smallest",
126
142
  } : "",
127
143
  "outline-none focus:outline-none",
128
144
  "select-none rounded-md text-sm",
@@ -131,7 +147,8 @@ export const Select = forwardRef<HTMLDivElement, SelectProps>(({
131
147
  disabled ? "text-surface-accent-600 dark:text-surface-accent-400" : "text-surface-accent-800 dark:text-white",
132
148
  "relative flex flex-row items-center",
133
149
  {
134
- "min-h-[28px]": size === "small",
150
+ "min-h-[28px]": size === "smallest",
151
+ "min-h-[32px]": size === "small",
135
152
  "min-h-[42px]": size === "medium",
136
153
  "min-h-[64px]": size === "large",
137
154
  "w-full": fullWidth,
@@ -145,7 +162,8 @@ export const Select = forwardRef<HTMLDivElement, SelectProps>(({
145
162
  "flex-grow max-w-full flex flex-row gap-2 items-center",
146
163
  "overflow-visible",
147
164
  {
148
- "min-h-[28px]": size === "small",
165
+ "min-h-[28px]": size === "smallest",
166
+ "min-h-[32px]": size === "small",
149
167
  "min-h-[42px]": size === "medium",
150
168
  "min-h-[64px]": size === "large"
151
169
  }
@@ -157,10 +175,9 @@ export const Select = forwardRef<HTMLDivElement, SelectProps>(({
157
175
  }}
158
176
  placeholder={placeholder}
159
177
  className={"w-full"}>
160
- {hasValue && value && renderValue ? renderValue(value) : placeholder}
178
+ {hasValue && value !== undefined && renderValue ? renderValue(value) : placeholder}
161
179
  {/*{hasValue && !renderValue && value}*/}
162
180
  {hasValue && !renderValue && (() => {
163
-
164
181
  // @ts-ignore
165
182
  const childrenProps: SelectItemProps[] = Children.map(children, (child) => {
166
183
  if (React.isValidElement(child)) {
@@ -168,33 +185,33 @@ export const Select = forwardRef<HTMLDivElement, SelectProps>(({
168
185
  }
169
186
  }).filter(Boolean);
170
187
 
171
- const option = childrenProps.find((o) => o.value === value);
188
+ const option = childrenProps.find((o) => String(o.value) === String(value));
172
189
  return option?.children;
173
190
  })()}
174
191
 
175
192
  </SelectPrimitive.Value>
176
193
  </div>
177
194
 
178
- {endAdornment && (
179
- <div
180
- className={cls("h-full flex items-center")}
181
- onClick={(e) => {
182
- e.preventDefault();
183
- e.stopPropagation();
184
- }}>
185
- {endAdornment}
186
- </div>
187
- )}
188
195
  <SelectPrimitive.Icon asChild>
189
- <KeyboardArrowDownIcon size={"medium"}
190
- className={cls("transition", open ? "rotate-180" : "", {
191
- "px-2": size === "large",
192
- "px-1": size === "medium" || size === "small",
193
- })}/>
196
+ <KeyboardArrowDownIcon size={size === "large" ? "medium" : "small"}
197
+ className={cls("transition", open ? "rotate-180" : "", {
198
+ "px-2": size === "large",
199
+ "px-1": size === "medium" || size === "small",
200
+ })}/>
194
201
  </SelectPrimitive.Icon>
195
202
  </div>
196
203
  </SelectPrimitive.Trigger>
197
204
 
205
+ {endAdornment && (
206
+ <div
207
+ className={cls("h-full flex items-center absolute right-0 pr-12",)}
208
+ onClick={(e) => {
209
+ e.preventDefault();
210
+ e.stopPropagation();
211
+ }}>
212
+ {endAdornment}
213
+ </div>
214
+ )}
198
215
  </div>
199
216
  <SelectPrimitive.Portal>
200
217
  <SelectPrimitive.Content position={position}
@@ -211,22 +228,25 @@ export const Select = forwardRef<HTMLDivElement, SelectProps>(({
211
228
 
212
229
  Select.displayName = "Select";
213
230
 
214
- export type SelectItemProps = {
215
- value: string,
231
+ export type SelectItemProps<T extends SelectValue = string> = {
232
+ value: T,
216
233
  children?: React.ReactNode,
217
234
  disabled?: boolean,
218
235
  className?: string,
219
236
  };
220
237
 
221
- export function SelectItem({
222
- value,
223
- children,
224
- disabled,
225
- className
226
- }: SelectItemProps) {
238
+ export function SelectItem<T extends SelectValue = string>({
239
+ value,
240
+ children,
241
+ disabled,
242
+ className
243
+ }: SelectItemProps<T>) {
244
+ // Convert value to string for Radix UI
245
+ const stringValue = String(value);
246
+
227
247
  return <SelectPrimitive.Item
228
- key={value}
229
- value={value}
248
+ key={stringValue}
249
+ value={stringValue}
230
250
  disabled={disabled}
231
251
  className={cls(
232
252
  "w-full",
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import React, { useCallback, useEffect, useRef, forwardRef, ForwardedRef } from "react";
2
+ import React, { ForwardedRef, forwardRef, useEffect, useRef } from "react";
3
3
 
4
4
  import { TextareaAutosize } from "./TextareaAutosize";
5
5
  import {
@@ -33,19 +33,27 @@ export type TextFieldProps<T extends string | number> = {
33
33
  onChange?: (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
34
34
  label?: React.ReactNode;
35
35
  multiline?: boolean;
36
- rows?: number;
37
36
  disabled?: boolean;
38
37
  invisible?: boolean;
39
38
  error?: boolean;
40
39
  endAdornment?: React.ReactNode;
41
40
  autoFocus?: boolean;
42
41
  placeholder?: string;
43
- size?: "small" | "medium" | "large";
42
+ size?: "smallest" | "small" | "medium" | "large";
44
43
  className?: string;
45
44
  style?: React.CSSProperties;
46
45
  inputClassName?: string;
47
46
  inputStyle?: React.CSSProperties;
48
47
  inputRef?: React.ForwardedRef<any>;
48
+ /**
49
+ * Maximum number of rows to display.
50
+ */
51
+ maxRows?: number | string;
52
+ /**
53
+ * Minimum number of rows to display.
54
+ * @default 1
55
+ */
56
+ minRows?: number | string;
49
57
  } & Omit<React.InputHTMLAttributes<HTMLInputElement>, "size">;
50
58
 
51
59
  export const TextField = forwardRef<HTMLDivElement, TextFieldProps<string | number>>(
@@ -57,7 +65,8 @@ export const TextField = forwardRef<HTMLDivElement, TextFieldProps<string | numb
57
65
  type = "text",
58
66
  multiline = false,
59
67
  invisible,
60
- rows,
68
+ maxRows,
69
+ minRows,
61
70
  disabled,
62
71
  error,
63
72
  endAdornment,
@@ -101,7 +110,8 @@ export const TextField = forwardRef<HTMLDivElement, TextFieldProps<string | numb
101
110
  ref={inputRef}
102
111
  placeholder={focused || hasValue || !label ? placeholder : undefined}
103
112
  autoFocus={autoFocus}
104
- rows={rows}
113
+ minRows={minRows}
114
+ maxRows={maxRows}
105
115
  value={value ?? ""}
106
116
  onChange={onChange}
107
117
  style={inputStyle}
@@ -124,11 +134,12 @@ export const TextField = forwardRef<HTMLDivElement, TextFieldProps<string | numb
124
134
  "focused:text-text-primary focused:dark:text-text-primary-dark",
125
135
  invisible ? focusedInvisibleMixin : "",
126
136
  disabled ? fieldBackgroundDisabledMixin : fieldBackgroundHoverMixin,
127
- size === "small"
128
- ? "min-h-[32px]"
129
- : size === "medium"
130
- ? "min-h-[48px]"
131
- : "min-h-[64px]",
137
+ {
138
+ "min-h-[28px]": size === "smallest",
139
+ "min-h-[32px]": size === "small",
140
+ "min-h-[42px]": size === "medium",
141
+ "min-h-[64px]": size === "large",
142
+ },
132
143
  label
133
144
  ? size === "large"
134
145
  ? "pt-8 pb-2"
@@ -158,9 +169,10 @@ export const TextField = forwardRef<HTMLDivElement, TextFieldProps<string | numb
158
169
  disabled ? fieldBackgroundDisabledMixin : fieldBackgroundHoverMixin,
159
170
  error ? "border border-red-500 dark:border-red-600" : "",
160
171
  {
161
- "min-h-[32px]": !invisible && size === "small",
162
- "min-h-[48px]": !invisible && size === "medium",
163
- "min-h-[64px]": !invisible && size === "large",
172
+ "min-h-[28px]": size === "smallest",
173
+ "min-h-[32px]": size === "small",
174
+ "min-h-[42px]": size === "medium",
175
+ "min-h-[64px]": size === "large",
164
176
  },
165
177
  className
166
178
  )}
@@ -193,7 +205,7 @@ export const TextField = forwardRef<HTMLDivElement, TextFieldProps<string | numb
193
205
  {
194
206
  "mr-4": size === "large",
195
207
  "mr-3": size === "medium",
196
- "mr-2": size === "small",
208
+ "mr-2": size === "small" || size === "smallest",
197
209
  }
198
210
  )}
199
211
  >
@@ -54,11 +54,11 @@ export const Icon = React.forwardRef<HTMLSpanElement, IconProps & { iconKey: str
54
54
  ref={ref} // Attach the ref to the span
55
55
  style={{
56
56
  fontSize: `${sizeInPx}px`,
57
+ verticalAlign: "middle",
57
58
  ...style
58
59
  }}
59
60
  className={
60
61
  cls("material-icons",
61
- "block",
62
62
  color ? colorClassesMapping[color] : "",
63
63
  "select-none",
64
64
  className)}
package/src/index.css CHANGED
@@ -71,3 +71,4 @@
71
71
  a {
72
72
  @apply text-blue-600 dark:text-blue-400 dark:hover:text-blue-600 hover:text-blue-800
73
73
  }
74
+
Binary file