@firecms/ui 3.0.1 → 3.1.0-canary.7d91b7c
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/README.md +9 -7
- package/dist/components/Card.d.ts +1 -1
- package/dist/components/ColorPicker.d.ts +30 -0
- package/dist/components/DateTimeField.d.ts +7 -0
- package/dist/components/Dialog.d.ts +2 -1
- package/dist/components/FileUpload.d.ts +1 -1
- package/dist/components/Menu.d.ts +2 -1
- package/dist/components/Menubar.d.ts +2 -1
- package/dist/components/MultiSelect.d.ts +2 -1
- package/dist/components/SearchBar.d.ts +11 -1
- package/dist/components/Select.d.ts +2 -1
- package/dist/components/Sheet.d.ts +1 -0
- package/dist/components/ToggleButtonGroup.d.ts +30 -0
- package/dist/components/index.d.ts +2 -0
- package/dist/hooks/PortalContainerContext.d.ts +31 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/useOutsideAlerter.d.ts +1 -1
- package/dist/index.css +57 -6
- package/dist/index.es.js +1604 -936
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +1604 -936
- package/dist/index.umd.js.map +1 -1
- package/dist/styles.d.ts +11 -11
- package/package.json +7 -7
- package/src/components/BooleanSwitch.tsx +3 -3
- package/src/components/Button.tsx +5 -5
- package/src/components/Card.tsx +7 -7
- package/src/components/Checkbox.tsx +1 -1
- package/src/components/ColorPicker.tsx +134 -0
- package/src/components/DateTimeField.tsx +123 -34
- package/src/components/DebouncedTextField.tsx +3 -3
- package/src/components/Dialog.tsx +25 -16
- package/src/components/DialogActions.tsx +1 -1
- package/src/components/ExpandablePanel.tsx +1 -1
- package/src/components/FileUpload.tsx +25 -24
- package/src/components/IconButton.tsx +3 -2
- package/src/components/Menu.tsx +44 -30
- package/src/components/Menubar.tsx +14 -3
- package/src/components/MultiSelect.tsx +91 -74
- package/src/components/Popover.tsx +11 -3
- package/src/components/SearchBar.tsx +37 -19
- package/src/components/Select.tsx +86 -73
- package/src/components/Separator.tsx +2 -2
- package/src/components/Sheet.tsx +12 -3
- package/src/components/Slider.tsx +4 -4
- package/src/components/Table.tsx +1 -1
- package/src/components/Tabs.tsx +14 -17
- package/src/components/TextField.tsx +19 -8
- package/src/components/ToggleButtonGroup.tsx +67 -0
- package/src/components/Tooltip.tsx +9 -2
- package/src/components/index.tsx +2 -0
- package/src/hooks/PortalContainerContext.tsx +48 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useInjectStyles.tsx +12 -3
- package/src/hooks/useOutsideAlerter.tsx +1 -1
- package/src/index.css +57 -6
- package/src/styles.ts +11 -11
- package/src/util/cls.ts +1 -1
- package/tailwind.config.js +2 -3
|
@@ -12,6 +12,16 @@ interface SearchBarProps {
|
|
|
12
12
|
onTextSearch?: (searchString?: string) => void;
|
|
13
13
|
placeholder?: string;
|
|
14
14
|
expandable?: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Size of the search bar.
|
|
17
|
+
* - "small": 32px height (matches TextField small)
|
|
18
|
+
* - "medium": 44px height (matches TextField medium)
|
|
19
|
+
* @default "medium"
|
|
20
|
+
*/
|
|
21
|
+
size?: "small" | "medium";
|
|
22
|
+
/**
|
|
23
|
+
* @deprecated Use size="medium" or size="small" instead. This prop will be removed in a future version.
|
|
24
|
+
*/
|
|
15
25
|
large?: boolean;
|
|
16
26
|
innerClassName?: string;
|
|
17
27
|
className?: string;
|
|
@@ -22,18 +32,19 @@ interface SearchBarProps {
|
|
|
22
32
|
}
|
|
23
33
|
|
|
24
34
|
export function SearchBar({
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
onClick,
|
|
36
|
+
onTextSearch,
|
|
37
|
+
placeholder = "Search",
|
|
38
|
+
expandable = false,
|
|
39
|
+
size = "medium",
|
|
40
|
+
large,
|
|
41
|
+
innerClassName,
|
|
42
|
+
className,
|
|
43
|
+
autoFocus,
|
|
44
|
+
disabled,
|
|
45
|
+
loading,
|
|
46
|
+
inputRef
|
|
47
|
+
}: SearchBarProps) {
|
|
37
48
|
|
|
38
49
|
const [searchText, setSearchText] = useState<string>("");
|
|
39
50
|
const [active, setActive] = useState<boolean>(false);
|
|
@@ -58,18 +69,23 @@ export function SearchBar({
|
|
|
58
69
|
onTextSearch(undefined);
|
|
59
70
|
}, [onTextSearch]);
|
|
60
71
|
|
|
72
|
+
// Height classes matching TextField sizes
|
|
73
|
+
const heightClass = size === "small" ? "h-[36px]" : "h-[44px]";
|
|
74
|
+
const iconPaddingClass = size === "small" ? "px-2" : "px-4";
|
|
75
|
+
const inputPaddingClass = size === "small" ? "pl-8" : "pl-12";
|
|
76
|
+
|
|
61
77
|
return (
|
|
62
78
|
<div
|
|
63
79
|
onClick={onClick}
|
|
64
80
|
className={cls("relative",
|
|
65
|
-
|
|
81
|
+
heightClass,
|
|
66
82
|
"bg-surface-accent-50 dark:bg-surface-800 border",
|
|
67
83
|
defaultBorderMixin,
|
|
68
84
|
"rounded-lg",
|
|
69
85
|
className)}>
|
|
70
86
|
<div
|
|
71
|
-
className="absolute p-0
|
|
72
|
-
{loading ? <CircularProgress size={"smallest"}/> : <SearchIcon className={"text-text-disabled dark:text-text-disabled-dark"}/>}
|
|
87
|
+
className={cls("absolute p-0 h-full pointer-events-none flex items-center justify-center top-0", iconPaddingClass)}>
|
|
88
|
+
{loading ? <CircularProgress size={"smallest"} /> : <SearchIcon className={"text-text-disabled dark:text-text-disabled-dark"} size={size === "small" ? "small" : "medium"} />}
|
|
73
89
|
</div>
|
|
74
90
|
<input
|
|
75
91
|
value={searchText ?? ""}
|
|
@@ -89,18 +105,20 @@ export function SearchBar({
|
|
|
89
105
|
(disabled || loading) && "pointer-events-none",
|
|
90
106
|
"placeholder-text-disabled dark:placeholder-text-disabled-dark",
|
|
91
107
|
"relative flex items-center rounded-lg transition-all bg-transparent outline-none appearance-none border-none",
|
|
92
|
-
"
|
|
108
|
+
inputPaddingClass, "h-full text-current",
|
|
109
|
+
size === "small" ? "text-sm" : "",
|
|
93
110
|
expandable ? (active ? "w-[220px]" : "w-[180px]") : "",
|
|
94
111
|
innerClassName
|
|
95
112
|
)}
|
|
96
113
|
/>
|
|
97
114
|
{searchText
|
|
98
115
|
? <IconButton
|
|
99
|
-
className={`${
|
|
116
|
+
className={`${size === "small" ? "mr-0 top-0" : "mr-1 top-0"} absolute right-0 z-10`}
|
|
117
|
+
size={"small"}
|
|
100
118
|
onClick={clearText}>
|
|
101
|
-
<CloseIcon size={"
|
|
119
|
+
<CloseIcon size={"smallest"} />
|
|
102
120
|
</IconButton>
|
|
103
|
-
: <div style={{ width: 26 }}/>
|
|
121
|
+
: <div style={{ width: 26 }} />
|
|
104
122
|
}
|
|
105
123
|
</div>
|
|
106
124
|
);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import React, { ChangeEvent, Children, forwardRef, useCallback, useEffect, useState } from "react";
|
|
2
|
+
import React, { ChangeEvent, Children, forwardRef, useCallback, useEffect, useMemo, useState } from "react";
|
|
3
3
|
import * as SelectPrimitive from "@radix-ui/react-select";
|
|
4
4
|
import {
|
|
5
5
|
defaultBorderMixin,
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
import { CheckIcon, KeyboardArrowDownIcon } from "../icons";
|
|
13
13
|
import { cls } from "../util";
|
|
14
14
|
import { SelectInputLabel } from "./common/SelectInputLabel";
|
|
15
|
+
import { usePortalContainer } from "../hooks/PortalContainerContext";
|
|
15
16
|
|
|
16
17
|
export type SelectValue = string | number | boolean;
|
|
17
18
|
|
|
@@ -35,40 +36,42 @@ export type SelectProps<T extends SelectValue = string> = {
|
|
|
35
36
|
error?: boolean,
|
|
36
37
|
position?: "item-aligned" | "popper",
|
|
37
38
|
endAdornment?: React.ReactNode,
|
|
38
|
-
inputRef?: React.RefObject<HTMLButtonElement>,
|
|
39
|
+
inputRef?: React.RefObject<HTMLButtonElement | null>,
|
|
39
40
|
padding?: boolean,
|
|
40
41
|
invisible?: boolean,
|
|
41
42
|
children?: React.ReactNode;
|
|
42
43
|
dataType?: "string" | "number" | "boolean";
|
|
44
|
+
portalContainer?: HTMLElement | null; // Explicitly added to props type if missing
|
|
43
45
|
};
|
|
44
46
|
|
|
45
47
|
export const Select = forwardRef<HTMLDivElement, SelectProps>(({
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
48
|
+
inputRef,
|
|
49
|
+
open,
|
|
50
|
+
name,
|
|
51
|
+
fullWidth = false,
|
|
52
|
+
id,
|
|
53
|
+
onOpenChange,
|
|
54
|
+
value,
|
|
55
|
+
onChange,
|
|
56
|
+
onValueChange,
|
|
57
|
+
className,
|
|
58
|
+
inputClassName,
|
|
59
|
+
viewportClassName,
|
|
60
|
+
placeholder,
|
|
61
|
+
renderValue,
|
|
62
|
+
label,
|
|
63
|
+
size = "large",
|
|
64
|
+
error,
|
|
65
|
+
disabled,
|
|
66
|
+
padding = true,
|
|
67
|
+
position = "item-aligned",
|
|
68
|
+
endAdornment,
|
|
69
|
+
invisible,
|
|
70
|
+
children,
|
|
71
|
+
dataType = "string",
|
|
72
|
+
portalContainer: manualContainer, // Rename to avoid confusion
|
|
73
|
+
...props
|
|
74
|
+
}, ref) => {
|
|
72
75
|
|
|
73
76
|
const [openInternal, setOpenInternal] = useState(open ?? false);
|
|
74
77
|
|
|
@@ -76,8 +79,13 @@ export const Select = forwardRef<HTMLDivElement, SelectProps>(({
|
|
|
76
79
|
setOpenInternal(open ?? false);
|
|
77
80
|
}, [open]);
|
|
78
81
|
|
|
79
|
-
|
|
82
|
+
// Get the portal container from context
|
|
83
|
+
const contextContainer = usePortalContainer();
|
|
84
|
+
|
|
85
|
+
// Resolve final container (Manual Prop > Context Container > Undefined/Body)
|
|
86
|
+
const finalContainer = (manualContainer ?? contextContainer ?? undefined) as HTMLElement | undefined;
|
|
80
87
|
|
|
88
|
+
const onValueChangeInternal = useCallback((newValue: string) => {
|
|
81
89
|
let typedValue: SelectValue = newValue;
|
|
82
90
|
if (dataType === "boolean") {
|
|
83
91
|
if (newValue === "true") typedValue = true;
|
|
@@ -96,23 +104,22 @@ export const Select = forwardRef<HTMLDivElement, SelectProps>(({
|
|
|
96
104
|
} as unknown as ChangeEvent<HTMLSelectElement>;
|
|
97
105
|
onChange(event);
|
|
98
106
|
}
|
|
99
|
-
}, [onChange, onValueChange, name]);
|
|
107
|
+
}, [onChange, onValueChange, name, dataType]);
|
|
100
108
|
|
|
101
109
|
const hasValue = Array.isArray(value) ? value.length > 0 : (value != null && value !== "" && value !== undefined);
|
|
102
|
-
// Convert non-string values to strings for Radix UI
|
|
103
110
|
const stringValue = value !== undefined ? String(value) : undefined;
|
|
104
111
|
|
|
105
|
-
const
|
|
112
|
+
const displayChildren = useMemo(() => {
|
|
106
113
|
if (!hasValue || renderValue) return null;
|
|
107
|
-
// @ts-ignore
|
|
108
|
-
const childrenProps: SelectItemProps[] = Children.map(children, (child) => {
|
|
109
|
-
if (React.isValidElement(child)) {
|
|
110
|
-
return child.props;
|
|
111
|
-
}
|
|
112
|
-
}).filter(Boolean);
|
|
113
114
|
|
|
114
|
-
|
|
115
|
-
|
|
115
|
+
// Find the child that matches the current value to display its content
|
|
116
|
+
let found: React.ReactNode = null;
|
|
117
|
+
Children.forEach(children, (child) => {
|
|
118
|
+
if (React.isValidElement<SelectItemProps>(child) && String(child.props.value) === String(value)) {
|
|
119
|
+
found = child.props.children;
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
return found;
|
|
116
123
|
}, [children, hasValue, renderValue, value]);
|
|
117
124
|
|
|
118
125
|
return (
|
|
@@ -155,6 +162,7 @@ export const Select = forwardRef<HTMLDivElement, SelectProps>(({
|
|
|
155
162
|
"px-3": size === "medium",
|
|
156
163
|
"px-2": size === "small" || size === "smallest",
|
|
157
164
|
} : "",
|
|
165
|
+
"outline-hidden focus:outline-hidden",
|
|
158
166
|
"outline-none focus:outline-none",
|
|
159
167
|
"select-none rounded-md text-sm",
|
|
160
168
|
error ? "text-red-500 dark:text-red-600" : "focus:text-text-primary dark:focus:text-text-primary-dark",
|
|
@@ -184,28 +192,30 @@ export const Select = forwardRef<HTMLDivElement, SelectProps>(({
|
|
|
184
192
|
"min-h-[64px]": size === "large"
|
|
185
193
|
}
|
|
186
194
|
)}>
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
{hasValue && value !== undefined && renderValue ? renderValue(value) : placeholder}
|
|
195
|
-
{/*{hasValue && !renderValue && value}*/}
|
|
196
|
-
{hasValue && !renderValue && selectedChild}
|
|
195
|
+
<SelectPrimitive.Value
|
|
196
|
+
onClick={(e) => {
|
|
197
|
+
e.preventDefault();
|
|
198
|
+
e.stopPropagation();
|
|
199
|
+
}}
|
|
200
|
+
placeholder={placeholder}
|
|
201
|
+
className={"w-full"}>
|
|
197
202
|
|
|
198
|
-
|
|
199
|
-
|
|
203
|
+
{hasValue && value !== undefined && renderValue
|
|
204
|
+
? renderValue(value)
|
|
205
|
+
: (displayChildren || placeholder)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
</SelectPrimitive.Value>
|
|
209
|
+
</div>
|
|
200
210
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
211
|
+
<SelectPrimitive.Icon asChild>
|
|
212
|
+
<KeyboardArrowDownIcon size={size === "large" ? "medium" : "small"}
|
|
213
|
+
className={cls("transition", open ? "rotate-180" : "", {
|
|
214
|
+
"px-2": size === "large",
|
|
215
|
+
"px-1": size === "medium" || size === "small",
|
|
216
|
+
})} />
|
|
217
|
+
</SelectPrimitive.Icon>
|
|
218
|
+
</SelectPrimitive.Trigger>
|
|
209
219
|
|
|
210
220
|
{endAdornment && (
|
|
211
221
|
<div
|
|
@@ -218,11 +228,13 @@ export const Select = forwardRef<HTMLDivElement, SelectProps>(({
|
|
|
218
228
|
</div>
|
|
219
229
|
)}
|
|
220
230
|
</div>
|
|
221
|
-
|
|
231
|
+
|
|
232
|
+
{/* Pass the calculated finalContainer */}
|
|
233
|
+
<SelectPrimitive.Portal container={finalContainer}>
|
|
222
234
|
<SelectPrimitive.Content position={position}
|
|
223
|
-
|
|
235
|
+
className={cls(focusedDisabled, "z-50 relative overflow-hidden border bg-white dark:bg-surface-900 p-2 rounded-lg", defaultBorderMixin)}>
|
|
224
236
|
<SelectPrimitive.Viewport className={cls("p-1", viewportClassName)}
|
|
225
|
-
|
|
237
|
+
style={{ maxHeight: "var(--radix-select-content-available-height)" }}>
|
|
226
238
|
{children}
|
|
227
239
|
</SelectPrimitive.Viewport>
|
|
228
240
|
</SelectPrimitive.Content>
|
|
@@ -233,6 +245,7 @@ export const Select = forwardRef<HTMLDivElement, SelectProps>(({
|
|
|
233
245
|
|
|
234
246
|
Select.displayName = "Select";
|
|
235
247
|
|
|
248
|
+
// ... (SelectItem and SelectGroup remain unchanged)
|
|
236
249
|
export type SelectItemProps<T extends SelectValue = string> = {
|
|
237
250
|
value: T,
|
|
238
251
|
children?: React.ReactNode,
|
|
@@ -241,11 +254,11 @@ export type SelectItemProps<T extends SelectValue = string> = {
|
|
|
241
254
|
};
|
|
242
255
|
|
|
243
256
|
export const SelectItem = React.memo(function SelectItem<T extends SelectValue = string>({
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
257
|
+
value,
|
|
258
|
+
children,
|
|
259
|
+
disabled,
|
|
260
|
+
className
|
|
261
|
+
}: SelectItemProps<T>) {
|
|
249
262
|
// Convert value to string for Radix UI
|
|
250
263
|
const stringValue = String(value);
|
|
251
264
|
|
|
@@ -267,7 +280,7 @@ export const SelectItem = React.memo(function SelectItem<T extends SelectValue =
|
|
|
267
280
|
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
|
268
281
|
<div
|
|
269
282
|
className="absolute left-1 data-[state=checked]:block hidden">
|
|
270
|
-
<CheckIcon size={16}/>
|
|
283
|
+
<CheckIcon size={16} />
|
|
271
284
|
</div>
|
|
272
285
|
</SelectPrimitive.Item>;
|
|
273
286
|
});
|
|
@@ -279,10 +292,10 @@ export type SelectGroupProps = {
|
|
|
279
292
|
};
|
|
280
293
|
|
|
281
294
|
export const SelectGroup = React.memo(function SelectGroup({
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
295
|
+
label,
|
|
296
|
+
children,
|
|
297
|
+
className
|
|
298
|
+
}: SelectGroupProps) {
|
|
286
299
|
return <>
|
|
287
300
|
<SelectPrimitive.Group
|
|
288
301
|
className={cls(
|
|
@@ -15,12 +15,12 @@ export function Separator({
|
|
|
15
15
|
<SeparatorPrimitive.Root
|
|
16
16
|
decorative={decorative}
|
|
17
17
|
orientation="horizontal"
|
|
18
|
-
className={cls("dark:bg-opacity-80 dark:bg-surface-800 bg-surface-100 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px my-4", className)}/>
|
|
18
|
+
className={cls("dark:bg-opacity-80 dark:bg-surface-800 dark:bg-surface-800/80 bg-surface-100 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px my-4", className)}/>
|
|
19
19
|
);
|
|
20
20
|
else
|
|
21
21
|
return (
|
|
22
22
|
<SeparatorPrimitive.Root
|
|
23
|
-
className={cls("dark:bg-opacity-80 dark:bg-surface-800 bg-surface-100 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px mx-4", className)}
|
|
23
|
+
className={cls("dark:bg-opacity-80 dark:bg-surface-800 dark:bg-surface-800/80 bg-surface-100 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px mx-4", className)}
|
|
24
24
|
decorative={decorative}
|
|
25
25
|
orientation="vertical"
|
|
26
26
|
/>
|
package/src/components/Sheet.tsx
CHANGED
|
@@ -3,6 +3,7 @@ import React, { useEffect, useState } from "react";
|
|
|
3
3
|
import { cls } from "../util";
|
|
4
4
|
import { defaultBorderMixin } from "../styles";
|
|
5
5
|
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
|
6
|
+
import { usePortalContainer } from "../hooks/PortalContainerContext";
|
|
6
7
|
|
|
7
8
|
interface SheetProps {
|
|
8
9
|
children: React.ReactNode;
|
|
@@ -18,6 +19,7 @@ interface SheetProps {
|
|
|
18
19
|
style?: React.CSSProperties;
|
|
19
20
|
overlayClassName?: string;
|
|
20
21
|
overlayStyle?: React.CSSProperties;
|
|
22
|
+
portalContainer?: HTMLElement | null;
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
export const Sheet: React.FC<SheetProps> = ({
|
|
@@ -33,10 +35,17 @@ export const Sheet: React.FC<SheetProps> = ({
|
|
|
33
35
|
style,
|
|
34
36
|
overlayClassName,
|
|
35
37
|
overlayStyle,
|
|
38
|
+
portalContainer,
|
|
36
39
|
...props
|
|
37
40
|
}) => {
|
|
38
41
|
const [displayed, setDisplayed] = useState(false);
|
|
39
42
|
|
|
43
|
+
// Get the portal container from context
|
|
44
|
+
const contextContainer = usePortalContainer();
|
|
45
|
+
|
|
46
|
+
// Prioritize manual prop, fallback to context container
|
|
47
|
+
const finalContainer = (portalContainer ?? contextContainer ?? undefined) as HTMLElement | undefined;
|
|
48
|
+
|
|
40
49
|
useEffect(() => {
|
|
41
50
|
const timeout = setTimeout(() => {
|
|
42
51
|
setDisplayed(open);
|
|
@@ -62,7 +71,7 @@ export const Sheet: React.FC<SheetProps> = ({
|
|
|
62
71
|
<DialogPrimitive.Root open={displayed || open}
|
|
63
72
|
modal={modal}
|
|
64
73
|
onOpenChange={onOpenChange}>
|
|
65
|
-
<DialogPrimitive.Portal>
|
|
74
|
+
<DialogPrimitive.Portal container={finalContainer}>
|
|
66
75
|
<DialogPrimitive.Title autoFocus tabIndex={0}>
|
|
67
76
|
{title ?? "Sheet"}
|
|
68
77
|
</DialogPrimitive.Title>
|
|
@@ -70,8 +79,8 @@ export const Sheet: React.FC<SheetProps> = ({
|
|
|
70
79
|
className={cls(
|
|
71
80
|
"outline-none",
|
|
72
81
|
"fixed inset-0 transition-opacity z-20 ease-in-out duration-100 backdrop-blur-sm",
|
|
73
|
-
"bg-black bg-opacity-50",
|
|
74
|
-
"dark:bg-surface-900 dark:bg-opacity-60",
|
|
82
|
+
"bg-black bg-opacity-50 bg-black/50",
|
|
83
|
+
"dark:bg-surface-900 dark:bg-opacity-60 dark:bg-surface-900/60",
|
|
75
84
|
displayed && open ? "opacity-100" : "opacity-0",
|
|
76
85
|
overlayClassName
|
|
77
86
|
)}
|
|
@@ -37,8 +37,8 @@ function SliderThumb(props: {
|
|
|
37
37
|
"border-surface-accent-300 bg-surface-accent-300 dark:border-surface-700 dark:bg-surface-700": props.props.disabled
|
|
38
38
|
},
|
|
39
39
|
props.classes,
|
|
40
|
-
"focus-visible:ring-4 focus-visible:ring-primary focus-visible:ring-opacity-50",
|
|
41
|
-
"hover:ring-4 hover:ring-primary hover:ring-opacity-25",
|
|
40
|
+
"focus-visible:ring-4 focus-visible:ring-primary focus-visible:ring-opacity-50 focus-visible:ring-primary/50",
|
|
41
|
+
"hover:ring-4 hover:ring-primary hover:ring-opacity-25 hover:ring-primary/25",
|
|
42
42
|
"block rounded-full transition-colors focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50")}
|
|
43
43
|
|
|
44
44
|
/>
|
|
@@ -48,7 +48,7 @@ function SliderThumb(props: {
|
|
|
48
48
|
className={cls(
|
|
49
49
|
"TooltipContent",
|
|
50
50
|
"max-w-lg leading-relaxed",
|
|
51
|
-
"z-50 rounded px-3 py-2 text-xs leading-none bg-surface-accent-700 dark:bg-surface-accent-800 bg-opacity-90 font-medium text-surface-accent-50 shadow-2xl select-none duration-400 ease-in transform opacity-100",
|
|
51
|
+
"z-50 rounded px-3 py-2 text-xs leading-none bg-surface-accent-700 dark:bg-surface-accent-800 bg-opacity-90 bg-surface-accent-700/90 dark:bg-surface-accent-800/90 font-medium text-surface-accent-50 shadow-2xl select-none duration-400 ease-in transform opacity-100",
|
|
52
52
|
)}>
|
|
53
53
|
{props.props.value?.[props.index]}
|
|
54
54
|
</TooltipPrimitive.Content>
|
|
@@ -84,7 +84,7 @@ const Slider = React.forwardRef<
|
|
|
84
84
|
>
|
|
85
85
|
<SliderPrimitive.Track
|
|
86
86
|
style={{ height: size === "small" ? 4 : 8 }}
|
|
87
|
-
className={"relative w-full grow overflow-hidden rounded-full bg-surface-accent-300 bg-opacity-40 dark:bg-surface-700 dark:bg-opacity-40"}>
|
|
87
|
+
className={"relative w-full grow overflow-hidden rounded-full bg-surface-accent-300 bg-opacity-40 bg-surface-accent-300/40 dark:bg-surface-700 dark:bg-opacity-40 dark:bg-surface-700/40"}>
|
|
88
88
|
|
|
89
89
|
<SliderPrimitive.Range
|
|
90
90
|
className={cls("absolute h-full", {
|
package/src/components/Table.tsx
CHANGED
|
@@ -36,7 +36,7 @@ export const TableBody = React.memo(({
|
|
|
36
36
|
...rest
|
|
37
37
|
}: TableBodyProps) => (
|
|
38
38
|
<tbody
|
|
39
|
-
className={cls("bg-white dark:bg-surface-950 text-sm divide-y divide-surface-100 dark:divide-surface-700 dark:divide-opacity-70", className)}
|
|
39
|
+
className={cls("bg-white dark:bg-surface-950 text-sm divide-y divide-surface-100 dark:divide-surface-700 dark:divide-opacity-70 dark:divide-surface-700/70", className)}
|
|
40
40
|
{...rest}
|
|
41
41
|
>
|
|
42
42
|
{children}
|
package/src/components/Tabs.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import * as TabsPrimitive from "@radix-ui/react-tabs";
|
|
3
3
|
import { cls } from "../util";
|
|
4
|
+
import { defaultBorderMixin } from "../styles";
|
|
4
5
|
|
|
5
6
|
export type TabsProps = {
|
|
6
7
|
value: string,
|
|
@@ -17,11 +18,12 @@ export function Tabs({
|
|
|
17
18
|
innerClassName,
|
|
18
19
|
children
|
|
19
20
|
}: TabsProps) {
|
|
20
|
-
|
|
21
21
|
return <TabsPrimitive.Root value={value} onValueChange={onValueChange} className={className}>
|
|
22
22
|
<TabsPrimitive.List className={cls(
|
|
23
|
-
"
|
|
24
|
-
|
|
23
|
+
"border",
|
|
24
|
+
defaultBorderMixin,
|
|
25
|
+
"gap-2",
|
|
26
|
+
"inline-flex h-10 items-center justify-center rounded-md bg-surface-50 p-1 text-surface-600 dark:bg-surface-900 dark:text-surface-400",
|
|
25
27
|
innerClassName)
|
|
26
28
|
}>
|
|
27
29
|
{children}
|
|
@@ -47,19 +49,14 @@ export function Tab({
|
|
|
47
49
|
return <TabsPrimitive.Trigger value={value}
|
|
48
50
|
disabled={disabled}
|
|
49
51
|
className={cls(
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
disabled
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
"uppercase inline-block p-2 px-4 rounded",
|
|
60
|
-
"hover:bg-surface-accent-200 hover:bg-opacity-75 dark:hover:bg-surface-accent-800",
|
|
61
|
-
innerClassName)}>
|
|
62
|
-
{children}
|
|
63
|
-
</div>
|
|
52
|
+
"inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-white transition-all",
|
|
53
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-surface-400 focus-visible:ring-offset-2",
|
|
54
|
+
"disabled:pointer-events-none disabled:opacity-50",
|
|
55
|
+
"data-[state=active]:bg-white data-[state=active]:text-surface-900 dark:data-[state=active]:bg-surface-950 dark:data-[state=active]:text-surface-50",
|
|
56
|
+
// "data-[state=active]:border",
|
|
57
|
+
// defaultBorderMixin,
|
|
58
|
+
className,
|
|
59
|
+
innerClassName)}>
|
|
60
|
+
{children}
|
|
64
61
|
</TabsPrimitive.Trigger>;
|
|
65
62
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import React, { ForwardedRef, forwardRef, useEffect, useRef } from "react";
|
|
3
3
|
|
|
4
|
-
import { TextareaAutosize } from "./TextareaAutosize";
|
|
5
4
|
import {
|
|
6
5
|
fieldBackgroundDisabledMixin,
|
|
7
6
|
fieldBackgroundHoverMixin,
|
|
@@ -85,10 +84,16 @@ export const TextField = forwardRef<HTMLDivElement, TextFieldProps<string | numb
|
|
|
85
84
|
|
|
86
85
|
const inputRef = inputRefProp ?? useRef(null);
|
|
87
86
|
|
|
88
|
-
|
|
89
|
-
const [focused, setFocused] = React.useState(document.activeElement === inputRef.current);
|
|
87
|
+
const [focused, setFocused] = React.useState(false);
|
|
90
88
|
const hasValue = value !== undefined && value !== null && value !== "";
|
|
91
89
|
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
// @ts-ignore
|
|
92
|
+
if (inputRef.current && document.activeElement === inputRef.current) {
|
|
93
|
+
setFocused(true);
|
|
94
|
+
}
|
|
95
|
+
}, []);
|
|
96
|
+
|
|
92
97
|
useEffect(() => {
|
|
93
98
|
if (type !== "number") return;
|
|
94
99
|
const handleWheel = (event: any) => {
|
|
@@ -105,19 +110,21 @@ export const TextField = forwardRef<HTMLDivElement, TextFieldProps<string | numb
|
|
|
105
110
|
}, [inputRef, type]);
|
|
106
111
|
|
|
107
112
|
const input = multiline ? (
|
|
108
|
-
<
|
|
113
|
+
<textarea
|
|
109
114
|
{...(inputProps as any)}
|
|
110
115
|
ref={inputRef}
|
|
111
116
|
placeholder={focused || hasValue || !label ? placeholder : undefined}
|
|
112
117
|
autoFocus={autoFocus}
|
|
113
|
-
|
|
114
|
-
maxRows={maxRows}
|
|
118
|
+
rows={typeof minRows === "string" ? parseInt(minRows) : (minRows ?? 3)}
|
|
115
119
|
value={value ?? ""}
|
|
116
120
|
onChange={onChange}
|
|
121
|
+
onFocus={() => setFocused(true)}
|
|
122
|
+
onBlur={() => setFocused(false)}
|
|
117
123
|
style={inputStyle}
|
|
118
124
|
className={cls(
|
|
119
125
|
invisible ? focusedInvisibleMixin : "",
|
|
120
|
-
"rounded-md resize-none w-full outline-none
|
|
126
|
+
"rounded-md resize-none w-full outline-none text-base bg-transparent min-h-[64px] px-3",
|
|
127
|
+
label ? "pt-8 pb-2" : "py-2",
|
|
121
128
|
disabled && "outline-none opacity-50 text-surface-accent-600 dark:text-surface-accent-500",
|
|
122
129
|
inputClassName
|
|
123
130
|
)}
|
|
@@ -144,7 +151,11 @@ export const TextField = forwardRef<HTMLDivElement, TextFieldProps<string | numb
|
|
|
144
151
|
? size === "large"
|
|
145
152
|
? "pt-8 pb-2"
|
|
146
153
|
: "pt-4 pb-2"
|
|
147
|
-
: "
|
|
154
|
+
: size === "smallest"
|
|
155
|
+
? "py-0.5"
|
|
156
|
+
: size === "small"
|
|
157
|
+
? "py-1"
|
|
158
|
+
: "py-2",
|
|
148
159
|
endAdornment ? "pr-12" : "pr-3",
|
|
149
160
|
disabled &&
|
|
150
161
|
"outline-none opacity-65 dark:opacity-60 text-surface-accent-800 dark:text-white",
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { cls } from "../util";
|
|
3
|
+
|
|
4
|
+
export type ToggleButtonOption<T extends string = string> = {
|
|
5
|
+
value: T;
|
|
6
|
+
label: string;
|
|
7
|
+
icon?: React.ReactNode;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type ToggleButtonGroupProps<T extends string = string> = {
|
|
12
|
+
/**
|
|
13
|
+
* Currently selected value
|
|
14
|
+
*/
|
|
15
|
+
value: T;
|
|
16
|
+
/**
|
|
17
|
+
* Callback when value changes
|
|
18
|
+
*/
|
|
19
|
+
onValueChange: (value: T) => void;
|
|
20
|
+
/**
|
|
21
|
+
* Options to display
|
|
22
|
+
*/
|
|
23
|
+
options: ToggleButtonOption<T>[];
|
|
24
|
+
/**
|
|
25
|
+
* Additional class names for the container
|
|
26
|
+
*/
|
|
27
|
+
className?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* A toggle button group component for selecting one option from a set.
|
|
32
|
+
* Displays options as buttons in a horizontal row with active state styling.
|
|
33
|
+
*/
|
|
34
|
+
export function ToggleButtonGroup<T extends string = string>({
|
|
35
|
+
value,
|
|
36
|
+
onValueChange,
|
|
37
|
+
options,
|
|
38
|
+
className
|
|
39
|
+
}: ToggleButtonGroupProps<T>) {
|
|
40
|
+
return (
|
|
41
|
+
<div className={cls("inline-flex flex-row bg-surface-100 dark:bg-surface-800 rounded-lg p-1 gap-1", className)}>
|
|
42
|
+
{options.map((option) => (
|
|
43
|
+
<button
|
|
44
|
+
key={option.value}
|
|
45
|
+
type="button"
|
|
46
|
+
onClick={(e) => {
|
|
47
|
+
e.stopPropagation();
|
|
48
|
+
if (!option.disabled) {
|
|
49
|
+
onValueChange(option.value);
|
|
50
|
+
}
|
|
51
|
+
}}
|
|
52
|
+
disabled={option.disabled}
|
|
53
|
+
className={cls(
|
|
54
|
+
"flex flex-row items-center justify-center gap-2 py-3 px-4 rounded-md transition-colors",
|
|
55
|
+
value === option.value
|
|
56
|
+
? "bg-white dark:bg-surface-950 text-primary dark:text-primary-300"
|
|
57
|
+
: "text-surface-500 dark:text-surface-400 hover:bg-surface-100 dark:hover:bg-surface-700",
|
|
58
|
+
option.disabled && "opacity-50 cursor-not-allowed"
|
|
59
|
+
)}
|
|
60
|
+
>
|
|
61
|
+
{option.icon}
|
|
62
|
+
<span className="text-sm font-medium">{option.label}</span>
|
|
63
|
+
</button>
|
|
64
|
+
))}
|
|
65
|
+
</div>
|
|
66
|
+
);
|
|
67
|
+
}
|