xiod-ui 1.0.0
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/LICENSE +73 -0
- package/README.md +167 -0
- package/dist/components/accordion.d.ts +9 -0
- package/dist/components/accordion.js +46 -0
- package/dist/components/agent-steps.d.ts +47 -0
- package/dist/components/agent-steps.js +223 -0
- package/dist/components/alert-dialog.d.ts +21 -0
- package/dist/components/alert-dialog.js +74 -0
- package/dist/components/alert.d.ts +12 -0
- package/dist/components/alert.js +45 -0
- package/dist/components/aspect-ratio.d.ts +9 -0
- package/dist/components/aspect-ratio.js +22 -0
- package/dist/components/autocomplete.d.ts +35 -0
- package/dist/components/autocomplete.js +160 -0
- package/dist/components/avatar.d.ts +17 -0
- package/dist/components/avatar.js +65 -0
- package/dist/components/badge.d.ts +15 -0
- package/dist/components/badge.js +46 -0
- package/dist/components/breadcrumb.d.ts +12 -0
- package/dist/components/breadcrumb.js +75 -0
- package/dist/components/button-group.d.ts +14 -0
- package/dist/components/button-group.js +45 -0
- package/dist/components/button-split.d.ts +19 -0
- package/dist/components/button-split.js +73 -0
- package/dist/components/button.d.ts +15 -0
- package/dist/components/button.js +54 -0
- package/dist/components/calendar.d.ts +63 -0
- package/dist/components/calendar.js +425 -0
- package/dist/components/card.d.ts +17 -0
- package/dist/components/card.js +139 -0
- package/dist/components/carousel.d.ts +59 -0
- package/dist/components/carousel.js +503 -0
- package/dist/components/checkbox.d.ts +16 -0
- package/dist/components/checkbox.js +86 -0
- package/dist/components/circular-progress.d.ts +28 -0
- package/dist/components/circular-progress.js +127 -0
- package/dist/components/collapsible.d.ts +8 -0
- package/dist/components/collapsible.js +27 -0
- package/dist/components/color-picker.d.ts +159 -0
- package/dist/components/color-picker.js +1589 -0
- package/dist/components/combobox.d.ts +46 -0
- package/dist/components/combobox.js +237 -0
- package/dist/components/command.d.ts +22 -0
- package/dist/components/command.js +129 -0
- package/dist/components/context-menu.d.ts +38 -0
- package/dist/components/context-menu.js +164 -0
- package/dist/components/copy-to-clipboard.d.ts +32 -0
- package/dist/components/copy-to-clipboard.js +83 -0
- package/dist/components/corner-badge.d.ts +22 -0
- package/dist/components/corner-badge.js +84 -0
- package/dist/components/dashboard-grid.d.ts +83 -0
- package/dist/components/dashboard-grid.js +486 -0
- package/dist/components/date-picker.d.ts +29 -0
- package/dist/components/date-picker.js +261 -0
- package/dist/components/dialog.d.ts +26 -0
- package/dist/components/dialog.js +97 -0
- package/dist/components/dot-matrix.d.ts +32 -0
- package/dist/components/dot-matrix.js +707 -0
- package/dist/components/draggable.d.ts +42 -0
- package/dist/components/draggable.js +346 -0
- package/dist/components/drawer.d.ts +66 -0
- package/dist/components/drawer.js +304 -0
- package/dist/components/empty.d.ts +14 -0
- package/dist/components/empty.js +75 -0
- package/dist/components/field.d.ts +12 -0
- package/dist/components/field.js +44 -0
- package/dist/components/fieldset.d.ts +7 -0
- package/dist/components/fieldset.js +21 -0
- package/dist/components/file-upload.d.ts +61 -0
- package/dist/components/file-upload.js +482 -0
- package/dist/components/form.d.ts +6 -0
- package/dist/components/form.js +14 -0
- package/dist/components/frame.d.ts +10 -0
- package/dist/components/frame.js +47 -0
- package/dist/components/gauge.d.ts +27 -0
- package/dist/components/gauge.js +74 -0
- package/dist/components/grid.d.ts +26 -0
- package/dist/components/grid.js +204 -0
- package/dist/components/group.d.ts +19 -0
- package/dist/components/group.js +45 -0
- package/dist/components/input-group.d.ts +15 -0
- package/dist/components/input-group.js +61 -0
- package/dist/components/input-otp.d.ts +24 -0
- package/dist/components/input-otp.js +50 -0
- package/dist/components/input-payment.d.ts +131 -0
- package/dist/components/input-payment.js +1140 -0
- package/dist/components/input-phone.d.ts +70 -0
- package/dist/components/input-phone.js +657 -0
- package/dist/components/input-sensitive.d.ts +12 -0
- package/dist/components/input-sensitive.js +169 -0
- package/dist/components/input.d.ts +13 -0
- package/dist/components/input.js +26 -0
- package/dist/components/kbd.d.ts +6 -0
- package/dist/components/kbd.js +19 -0
- package/dist/components/kinetic-click.d.ts +48 -0
- package/dist/components/kinetic-click.js +1184 -0
- package/dist/components/label.d.ts +6 -0
- package/dist/components/label.js +18 -0
- package/dist/components/list-box.d.ts +20 -0
- package/dist/components/list-box.js +148 -0
- package/dist/components/loader.d.ts +13 -0
- package/dist/components/loader.js +430 -0
- package/dist/components/marker.d.ts +15 -0
- package/dist/components/marker.js +59 -0
- package/dist/components/menu.d.ts +42 -0
- package/dist/components/menu.js +186 -0
- package/dist/components/menubar.d.ts +40 -0
- package/dist/components/menubar.js +107 -0
- package/dist/components/message.d.ts +33 -0
- package/dist/components/message.js +112 -0
- package/dist/components/meter.d.ts +10 -0
- package/dist/components/meter.js +42 -0
- package/dist/components/morphic-toast.d.ts +61 -0
- package/dist/components/morphic-toast.js +916 -0
- package/dist/components/navigation-menu.d.ts +26 -0
- package/dist/components/navigation-menu.js +116 -0
- package/dist/components/number-field.d.ts +15 -0
- package/dist/components/number-field.js +86 -0
- package/dist/components/option-picker.d.ts +18 -0
- package/dist/components/option-picker.js +89 -0
- package/dist/components/orb.d.ts +38 -0
- package/dist/components/orb.js +1413 -0
- package/dist/components/pagination.d.ts +25 -0
- package/dist/components/pagination.js +147 -0
- package/dist/components/popover.d.ts +22 -0
- package/dist/components/popover.js +79 -0
- package/dist/components/preview-card.d.ts +16 -0
- package/dist/components/preview-card.js +60 -0
- package/dist/components/progress.d.ts +21 -0
- package/dist/components/progress.js +139 -0
- package/dist/components/radio.d.ts +16 -0
- package/dist/components/radio.js +68 -0
- package/dist/components/resizable.d.ts +58 -0
- package/dist/components/resizable.js +964 -0
- package/dist/components/ruler-picker.d.ts +27 -0
- package/dist/components/ruler-picker.js +253 -0
- package/dist/components/scroll-area.d.ts +13 -0
- package/dist/components/scroll-area.js +34 -0
- package/dist/components/scroll-bar.d.ts +13 -0
- package/dist/components/scroll-bar.js +47 -0
- package/dist/components/select.d.ts +29 -0
- package/dist/components/select.js +140 -0
- package/dist/components/separator.d.ts +6 -0
- package/dist/components/separator.js +15 -0
- package/dist/components/sidebar.d.ts +65 -0
- package/dist/components/sidebar.js +408 -0
- package/dist/components/skeleton.d.ts +5 -0
- package/dist/components/skeleton.js +12 -0
- package/dist/components/slider.d.ts +12 -0
- package/dist/components/slider.js +96 -0
- package/dist/components/sortable.d.ts +44 -0
- package/dist/components/sortable.js +327 -0
- package/dist/components/switch.d.ts +15 -0
- package/dist/components/switch.js +63 -0
- package/dist/components/table.d.ts +12 -0
- package/dist/components/table.js +65 -0
- package/dist/components/tabs.d.ts +12 -0
- package/dist/components/tabs.js +39 -0
- package/dist/components/text.d.ts +14 -0
- package/dist/components/text.js +69 -0
- package/dist/components/textarea.d.ts +9 -0
- package/dist/components/textarea.js +20 -0
- package/dist/components/theme-provider.d.ts +52 -0
- package/dist/components/theme-provider.js +148 -0
- package/dist/components/timeline.d.ts +34 -0
- package/dist/components/timeline.js +147 -0
- package/dist/components/toast.d.ts +17 -0
- package/dist/components/toast.js +161 -0
- package/dist/components/toggle-group.d.ts +14 -0
- package/dist/components/toggle-group.js +53 -0
- package/dist/components/toggle.d.ts +11 -0
- package/dist/components/toggle.js +36 -0
- package/dist/components/toolbar.d.ts +11 -0
- package/dist/components/toolbar.js +49 -0
- package/dist/components/tooltip.d.ts +17 -0
- package/dist/components/tooltip.js +56 -0
- package/dist/components/waveform.d.ts +61 -0
- package/dist/components/waveform.js +500 -0
- package/dist/components/wheel-picker.d.ts +44 -0
- package/dist/components/wheel-picker.js +771 -0
- package/dist/hooks/use-copy-to-clipboard.d.ts +9 -0
- package/dist/hooks/use-copy-to-clipboard.js +36 -0
- package/dist/hooks/use-media-query.d.ts +21 -0
- package/dist/hooks/use-media-query.js +58 -0
- package/dist/styles.css +202 -0
- package/dist/themes/afterglow.css +89 -0
- package/dist/themes/afterglow.scoped.css +90 -0
- package/dist/themes/blacklight.css +89 -0
- package/dist/themes/blacklight.scoped.css +90 -0
- package/dist/themes/cathode.css +89 -0
- package/dist/themes/cathode.scoped.css +90 -0
- package/dist/themes/cel.css +89 -0
- package/dist/themes/cel.scoped.css +90 -0
- package/dist/themes/cinder.css +89 -0
- package/dist/themes/cinder.scoped.css +90 -0
- package/dist/themes/clay.css +89 -0
- package/dist/themes/clay.scoped.css +90 -0
- package/dist/themes/cobalt.css +89 -0
- package/dist/themes/cobalt.scoped.css +90 -0
- package/dist/themes/crimson.css +89 -0
- package/dist/themes/crimson.scoped.css +90 -0
- package/dist/themes/ion.css +89 -0
- package/dist/themes/ion.scoped.css +90 -0
- package/dist/themes/iris.css +89 -0
- package/dist/themes/iris.scoped.css +90 -0
- package/dist/themes/lagoon.css +89 -0
- package/dist/themes/lagoon.scoped.css +90 -0
- package/dist/themes/mochi.css +89 -0
- package/dist/themes/mochi.scoped.css +90 -0
- package/dist/themes/riso.css +89 -0
- package/dist/themes/riso.scoped.css +90 -0
- package/dist/themes/roast.css +89 -0
- package/dist/themes/roast.scoped.css +90 -0
- package/dist/themes/sage.css +89 -0
- package/dist/themes/sage.scoped.css +90 -0
- package/dist/themes/sepia.css +89 -0
- package/dist/themes/sepia.scoped.css +90 -0
- package/dist/themes/sorbet.css +89 -0
- package/dist/themes/sorbet.scoped.css +90 -0
- package/dist/themes/taffy.css +89 -0
- package/dist/themes/taffy.scoped.css +90 -0
- package/dist/themes/toxin.css +89 -0
- package/dist/themes/toxin.scoped.css +90 -0
- package/package.json +140 -0
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Button } from "./button.js";
|
|
3
|
+
import { cn } from "cn";
|
|
4
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { cva } from "class-variance-authority";
|
|
6
|
+
import * as React$1 from "react";
|
|
7
|
+
import { mergeProps } from "@base-ui/react/merge-props";
|
|
8
|
+
import { useRender } from "@base-ui/react/use-render";
|
|
9
|
+
import { AlertCircle } from "xiod-icons/icons/AlertCircle";
|
|
10
|
+
import { CheckmarkCircle } from "xiod-icons/icons/CheckmarkCircle";
|
|
11
|
+
import { Delete } from "xiod-icons/icons/Delete";
|
|
12
|
+
import { File } from "xiod-icons/icons/File";
|
|
13
|
+
import { FileArchive } from "xiod-icons/icons/FileArchive";
|
|
14
|
+
import { FileAudio } from "xiod-icons/icons/FileAudio";
|
|
15
|
+
import { FileCode } from "xiod-icons/icons/FileCode";
|
|
16
|
+
import { FileEmpty } from "xiod-icons/icons/FileEmpty";
|
|
17
|
+
import { FileImage } from "xiod-icons/icons/FileImage";
|
|
18
|
+
import { FileVideo } from "xiod-icons/icons/FileVideo";
|
|
19
|
+
//#region src/components/file-upload.tsx
|
|
20
|
+
const FileUploadContext = React$1.createContext(null);
|
|
21
|
+
function useFileUploadContext() {
|
|
22
|
+
const context = React$1.useContext(FileUploadContext);
|
|
23
|
+
if (!context) throw new Error("FileUpload subcomponents must be rendered within a <FileUpload> provider component.");
|
|
24
|
+
return context;
|
|
25
|
+
}
|
|
26
|
+
function isAccepted(file, acceptString) {
|
|
27
|
+
if (!acceptString) return true;
|
|
28
|
+
const acceptTypes = acceptString.split(",").map((t) => t.trim().toLowerCase());
|
|
29
|
+
const fileType = file.type.toLowerCase();
|
|
30
|
+
const fileName = file.name.toLowerCase();
|
|
31
|
+
return acceptTypes.some((type) => {
|
|
32
|
+
if (type.startsWith(".")) return fileName.endsWith(type);
|
|
33
|
+
if (type.endsWith("/*")) {
|
|
34
|
+
const baseType = type.slice(0, -2);
|
|
35
|
+
return fileType.startsWith(baseType);
|
|
36
|
+
}
|
|
37
|
+
return fileType === type;
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
function formatFileSize(bytes) {
|
|
41
|
+
if (bytes === 0) return "0 Bytes";
|
|
42
|
+
const k = 1024;
|
|
43
|
+
const sizes = [
|
|
44
|
+
"Bytes",
|
|
45
|
+
"KB",
|
|
46
|
+
"MB",
|
|
47
|
+
"GB"
|
|
48
|
+
];
|
|
49
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
50
|
+
return `${parseFloat((bytes / k ** i).toFixed(2))} ${sizes[i]}`;
|
|
51
|
+
}
|
|
52
|
+
function FileUpload({ files: filesProp, onFilesChange, onFilesAdded, onFileRemove, maxFiles = 5, maxSizeMB = 10, accept, disabled = false, children, ...props }) {
|
|
53
|
+
const [internalFiles, setInternalFiles] = React$1.useState([]);
|
|
54
|
+
const [isDragging, setIsDragging] = React$1.useState(false);
|
|
55
|
+
const fileInputRef = React$1.useRef(null);
|
|
56
|
+
const files = filesProp ?? internalFiles;
|
|
57
|
+
const setFiles = React$1.useCallback((update) => {
|
|
58
|
+
if (filesProp === void 0) setInternalFiles(update);
|
|
59
|
+
if (onFilesChange) onFilesChange(typeof update === "function" ? update(files) : update);
|
|
60
|
+
}, [
|
|
61
|
+
filesProp,
|
|
62
|
+
onFilesChange,
|
|
63
|
+
files
|
|
64
|
+
]);
|
|
65
|
+
const removeFile = React$1.useCallback((id) => {
|
|
66
|
+
if (disabled) return;
|
|
67
|
+
const updatedFiles = files.filter((f) => f.id !== id);
|
|
68
|
+
setFiles(updatedFiles);
|
|
69
|
+
if (onFileRemove) onFileRemove(id);
|
|
70
|
+
}, [
|
|
71
|
+
files,
|
|
72
|
+
disabled,
|
|
73
|
+
onFileRemove,
|
|
74
|
+
setFiles
|
|
75
|
+
]);
|
|
76
|
+
const addFiles = React$1.useCallback((newFiles) => {
|
|
77
|
+
if (disabled) return;
|
|
78
|
+
const validatedItems = [];
|
|
79
|
+
const currentFileCount = files.length;
|
|
80
|
+
let addedCount = 0;
|
|
81
|
+
for (const file of newFiles) {
|
|
82
|
+
if (maxFiles && currentFileCount + addedCount >= maxFiles) break;
|
|
83
|
+
let errorMessage;
|
|
84
|
+
let status = "idle";
|
|
85
|
+
if (maxSizeMB && file.size > maxSizeMB * 1024 * 1024) {
|
|
86
|
+
errorMessage = `File exceeds max size of ${maxSizeMB}MB`;
|
|
87
|
+
status = "error";
|
|
88
|
+
} else if (accept && !isAccepted(file, accept)) {
|
|
89
|
+
errorMessage = "Invalid file type";
|
|
90
|
+
status = "error";
|
|
91
|
+
}
|
|
92
|
+
validatedItems.push({
|
|
93
|
+
id: Math.random().toString(36).substring(2, 9),
|
|
94
|
+
file,
|
|
95
|
+
progress: 0,
|
|
96
|
+
status,
|
|
97
|
+
errorMessage
|
|
98
|
+
});
|
|
99
|
+
addedCount++;
|
|
100
|
+
}
|
|
101
|
+
if (validatedItems.length > 0) {
|
|
102
|
+
const updatedFiles = [...files, ...validatedItems];
|
|
103
|
+
setFiles(updatedFiles);
|
|
104
|
+
if (onFilesAdded) onFilesAdded(validatedItems.map((item) => item.file));
|
|
105
|
+
}
|
|
106
|
+
}, [
|
|
107
|
+
files,
|
|
108
|
+
maxFiles,
|
|
109
|
+
maxSizeMB,
|
|
110
|
+
accept,
|
|
111
|
+
disabled,
|
|
112
|
+
onFilesAdded,
|
|
113
|
+
setFiles
|
|
114
|
+
]);
|
|
115
|
+
const contextValue = React$1.useMemo(() => ({
|
|
116
|
+
files,
|
|
117
|
+
isDragging,
|
|
118
|
+
setIsDragging,
|
|
119
|
+
maxFiles,
|
|
120
|
+
maxSizeMB,
|
|
121
|
+
accept,
|
|
122
|
+
disabled,
|
|
123
|
+
removeFile,
|
|
124
|
+
addFiles,
|
|
125
|
+
fileInputRef
|
|
126
|
+
}), [
|
|
127
|
+
files,
|
|
128
|
+
isDragging,
|
|
129
|
+
maxFiles,
|
|
130
|
+
maxSizeMB,
|
|
131
|
+
accept,
|
|
132
|
+
disabled,
|
|
133
|
+
removeFile,
|
|
134
|
+
addFiles
|
|
135
|
+
]);
|
|
136
|
+
return /* @__PURE__ */ jsx(FileUploadContext.Provider, {
|
|
137
|
+
value: contextValue,
|
|
138
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
139
|
+
className: "contents",
|
|
140
|
+
"data-slot": "file-upload",
|
|
141
|
+
...props,
|
|
142
|
+
children
|
|
143
|
+
})
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
function FileUploadTrigger({ className, render, ...props }) {
|
|
147
|
+
const { fileInputRef, isDragging, setIsDragging, disabled, addFiles } = useFileUploadContext();
|
|
148
|
+
const handleDragOver = (e) => {
|
|
149
|
+
if (disabled) return;
|
|
150
|
+
e.preventDefault();
|
|
151
|
+
e.stopPropagation();
|
|
152
|
+
setIsDragging(true);
|
|
153
|
+
};
|
|
154
|
+
const handleDragLeave = (e) => {
|
|
155
|
+
if (disabled) return;
|
|
156
|
+
e.preventDefault();
|
|
157
|
+
e.stopPropagation();
|
|
158
|
+
setIsDragging(false);
|
|
159
|
+
};
|
|
160
|
+
const handleDrop = (e) => {
|
|
161
|
+
if (disabled) return;
|
|
162
|
+
e.preventDefault();
|
|
163
|
+
e.stopPropagation();
|
|
164
|
+
setIsDragging(false);
|
|
165
|
+
if (e.dataTransfer.files && e.dataTransfer.files.length > 0) addFiles(Array.from(e.dataTransfer.files));
|
|
166
|
+
};
|
|
167
|
+
const handleClick = () => {
|
|
168
|
+
if (disabled) return;
|
|
169
|
+
fileInputRef.current?.click();
|
|
170
|
+
};
|
|
171
|
+
const handleKeyDown = (e) => {
|
|
172
|
+
if (disabled) return;
|
|
173
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
174
|
+
e.preventDefault();
|
|
175
|
+
fileInputRef.current?.click();
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
const defaultProps = {
|
|
179
|
+
role: "button",
|
|
180
|
+
tabIndex: disabled ? -1 : 0,
|
|
181
|
+
className: cn("relative flex flex-col items-center justify-center rounded-2xl border border-dashed border-input bg-muted/20 hover:bg-muted/40 p-[calc(--spacing(8)-1px)] text-center transition-[color,background-color,border-color,transform,box-shadow,opacity] duration-200 outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-64 cursor-pointer select-none", "before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-2xl)-1px)] before:shadow-[0_1px_--theme(--color-black/4%)] dark:before:shadow-[0_-1px_--theme(--color-white/6%)]", isDragging && "scale-[1.01] border-primary bg-primary/4 dark:bg-primary/8 border-solid", disabled && "cursor-not-allowed opacity-64", className),
|
|
182
|
+
"data-dragging": isDragging ? "true" : void 0,
|
|
183
|
+
"data-disabled": disabled ? "true" : void 0,
|
|
184
|
+
"data-slot": "file-upload-trigger",
|
|
185
|
+
onDragOver: handleDragOver,
|
|
186
|
+
onDragLeave: handleDragLeave,
|
|
187
|
+
onDrop: handleDrop,
|
|
188
|
+
onClick: handleClick,
|
|
189
|
+
onKeyDown: handleKeyDown
|
|
190
|
+
};
|
|
191
|
+
return useRender({
|
|
192
|
+
defaultTagName: "div",
|
|
193
|
+
props: mergeProps(defaultProps, props),
|
|
194
|
+
render
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
function FileUploadInput({ className, ...props }) {
|
|
198
|
+
const { fileInputRef, maxFiles, accept, disabled, addFiles } = useFileUploadContext();
|
|
199
|
+
const handleChange = (e) => {
|
|
200
|
+
if (e.target.files && e.target.files.length > 0) addFiles(Array.from(e.target.files));
|
|
201
|
+
e.target.value = "";
|
|
202
|
+
};
|
|
203
|
+
return /* @__PURE__ */ jsx("input", {
|
|
204
|
+
type: "file",
|
|
205
|
+
ref: fileInputRef,
|
|
206
|
+
className: cn("hidden", className),
|
|
207
|
+
multiple: maxFiles > 1,
|
|
208
|
+
accept,
|
|
209
|
+
disabled,
|
|
210
|
+
onChange: handleChange,
|
|
211
|
+
"data-slot": "file-upload-input",
|
|
212
|
+
...props
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
function FileUploadList({ className, render, ...props }) {
|
|
216
|
+
const defaultProps = {
|
|
217
|
+
className: cn("grid gap-3 w-full mt-4", className),
|
|
218
|
+
"data-slot": "file-upload-list"
|
|
219
|
+
};
|
|
220
|
+
return useRender({
|
|
221
|
+
defaultTagName: "div",
|
|
222
|
+
props: mergeProps(defaultProps, props),
|
|
223
|
+
render
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
const FileItemContext = React$1.createContext(null);
|
|
227
|
+
function useFileItemContext() {
|
|
228
|
+
const context = React$1.useContext(FileItemContext);
|
|
229
|
+
if (!context) throw new Error("FileUploadItem subcomponents must be rendered within a <FileUploadItem> parent component.");
|
|
230
|
+
return context;
|
|
231
|
+
}
|
|
232
|
+
function FileUploadItem({ className, render, fileItem, ...props }) {
|
|
233
|
+
const defaultProps = {
|
|
234
|
+
className: cn("group relative flex items-center gap-4 overflow-hidden rounded-xl border border-border/80 bg-background/50 p-[calc(--spacing(3)-1px)] shadow-xs/5 not-dark:bg-clip-padding before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-xl)-1px)] before:shadow-[0_1px_--theme(--color-black/4%)] dark:before:shadow-[0_-1px_--theme(--color-white/6%)] transition-[color,background-color,border-color,box-shadow,opacity]", fileItem.status === "uploading" && "pb-4.5", className),
|
|
235
|
+
"data-status": fileItem.status,
|
|
236
|
+
"data-slot": "file-upload-item"
|
|
237
|
+
};
|
|
238
|
+
return /* @__PURE__ */ jsx(FileItemContext.Provider, {
|
|
239
|
+
value: fileItem,
|
|
240
|
+
children: useRender({
|
|
241
|
+
defaultTagName: "div",
|
|
242
|
+
props: mergeProps(defaultProps, props),
|
|
243
|
+
render
|
|
244
|
+
})
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
function FileUploadItemIcon({ className, render, ...props }) {
|
|
248
|
+
const { file } = useFileItemContext();
|
|
249
|
+
const defaultProps = {
|
|
250
|
+
className: cn("flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-muted text-muted-foreground", className),
|
|
251
|
+
"data-slot": "file-upload-item-icon"
|
|
252
|
+
};
|
|
253
|
+
const getIcon = () => {
|
|
254
|
+
const type = file.type.toLowerCase();
|
|
255
|
+
const name = file.name.toLowerCase();
|
|
256
|
+
if (type.startsWith("image/")) return /* @__PURE__ */ jsx(FileImage, { className: "size-5 text-primary" });
|
|
257
|
+
if (type.startsWith("video/")) return /* @__PURE__ */ jsx(FileVideo, { className: "size-5 text-primary" });
|
|
258
|
+
if (type.startsWith("audio/")) return /* @__PURE__ */ jsx(FileAudio, { className: "size-5 text-warning" });
|
|
259
|
+
if (type.includes("pdf") || name.endsWith(".pdf")) return /* @__PURE__ */ jsx(File, { className: "size-5 text-destructive" });
|
|
260
|
+
if (type.includes("zip") || type.includes("tar") || type.includes("gzip") || type.includes("rar") || name.endsWith(".zip") || name.endsWith(".rar") || name.endsWith(".gz")) return /* @__PURE__ */ jsx(FileArchive, { className: "size-5 text-success" });
|
|
261
|
+
if (type.includes("json") || type.includes("javascript") || type.includes("typescript") || type.includes("html") || type.includes("css") || name.endsWith(".json") || name.endsWith(".js") || name.endsWith(".ts") || name.endsWith(".tsx") || name.endsWith(".html") || name.endsWith(".css")) return /* @__PURE__ */ jsx(FileCode, { className: "size-5 text-info" });
|
|
262
|
+
return /* @__PURE__ */ jsx(FileEmpty, { className: "size-5 text-muted-foreground" });
|
|
263
|
+
};
|
|
264
|
+
const defaultContent = getIcon();
|
|
265
|
+
const mergedProps = mergeProps(defaultProps, props);
|
|
266
|
+
if (mergedProps.children === void 0) mergedProps.children = defaultContent;
|
|
267
|
+
return useRender({
|
|
268
|
+
defaultTagName: "div",
|
|
269
|
+
props: mergedProps,
|
|
270
|
+
render
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
function FileUploadItemName({ className, render, ...props }) {
|
|
274
|
+
const { file } = useFileItemContext();
|
|
275
|
+
const defaultProps = {
|
|
276
|
+
className: cn("truncate text-sm font-medium text-foreground pr-2", className),
|
|
277
|
+
"data-slot": "file-upload-item-name"
|
|
278
|
+
};
|
|
279
|
+
const mergedProps = mergeProps(defaultProps, props);
|
|
280
|
+
if (mergedProps.children === void 0) mergedProps.children = file.name;
|
|
281
|
+
return useRender({
|
|
282
|
+
defaultTagName: "p",
|
|
283
|
+
props: mergedProps,
|
|
284
|
+
render
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
function FileUploadItemSize({ className, render, ...props }) {
|
|
288
|
+
const { file } = useFileItemContext();
|
|
289
|
+
const defaultProps = {
|
|
290
|
+
className: cn("text-xs text-muted-foreground", className),
|
|
291
|
+
"data-slot": "file-upload-item-size"
|
|
292
|
+
};
|
|
293
|
+
const mergedProps = mergeProps(defaultProps, props);
|
|
294
|
+
if (mergedProps.children === void 0) mergedProps.children = formatFileSize(file.size);
|
|
295
|
+
return useRender({
|
|
296
|
+
defaultTagName: "span",
|
|
297
|
+
props: mergedProps,
|
|
298
|
+
render
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
function FileUploadItemProgress({ className, render, ...props }) {
|
|
302
|
+
const { progress, status } = useFileItemContext();
|
|
303
|
+
const defaultProps = {
|
|
304
|
+
className: cn("absolute bottom-0 left-0 h-1 w-full bg-muted/60", className),
|
|
305
|
+
"data-slot": "file-upload-item-progress"
|
|
306
|
+
};
|
|
307
|
+
const mergedProps = mergeProps(defaultProps, props);
|
|
308
|
+
if (mergedProps.children === void 0) mergedProps.children = /* @__PURE__ */ jsx("div", {
|
|
309
|
+
className: "h-full bg-primary transition-[width] duration-300 ease-in-out",
|
|
310
|
+
style: { width: `${progress}%` },
|
|
311
|
+
role: "progressbar",
|
|
312
|
+
"aria-valuenow": progress,
|
|
313
|
+
"aria-valuemin": 0,
|
|
314
|
+
"aria-valuemax": 100
|
|
315
|
+
});
|
|
316
|
+
const renderResult = useRender({
|
|
317
|
+
defaultTagName: "div",
|
|
318
|
+
props: mergedProps,
|
|
319
|
+
render
|
|
320
|
+
});
|
|
321
|
+
if (status !== "uploading") return null;
|
|
322
|
+
return renderResult;
|
|
323
|
+
}
|
|
324
|
+
function FileUploadItemStatus({ className, render, ...props }) {
|
|
325
|
+
const { status, progress, errorMessage } = useFileItemContext();
|
|
326
|
+
const defaultProps = {
|
|
327
|
+
className: cn("flex items-center gap-1.5 shrink-0", className),
|
|
328
|
+
"data-slot": "file-upload-item-status"
|
|
329
|
+
};
|
|
330
|
+
const getStatusContent = () => {
|
|
331
|
+
if (status === "success") return /* @__PURE__ */ jsxs("span", {
|
|
332
|
+
className: "inline-flex items-center gap-1 rounded-full bg-success/10 px-2 py-0.5 text-xs font-medium text-success dark:bg-success/20",
|
|
333
|
+
children: [/* @__PURE__ */ jsx(CheckmarkCircle, { className: "size-3" }), " Done"]
|
|
334
|
+
});
|
|
335
|
+
if (status === "error") return /* @__PURE__ */ jsxs("span", {
|
|
336
|
+
className: "inline-flex items-center gap-1 rounded-full bg-destructive/10 px-2 py-0.5 text-xs font-medium text-destructive dark:bg-destructive/20",
|
|
337
|
+
title: errorMessage,
|
|
338
|
+
children: [/* @__PURE__ */ jsx(AlertCircle, { className: "size-3" }), " Failed"]
|
|
339
|
+
});
|
|
340
|
+
if (status === "uploading") return /* @__PURE__ */ jsxs("span", {
|
|
341
|
+
className: "text-xs font-medium text-muted-foreground",
|
|
342
|
+
children: [Math.round(progress), "%"]
|
|
343
|
+
});
|
|
344
|
+
return null;
|
|
345
|
+
};
|
|
346
|
+
const defaultContent = getStatusContent();
|
|
347
|
+
const mergedProps = mergeProps(defaultProps, props);
|
|
348
|
+
if (mergedProps.children === void 0) mergedProps.children = defaultContent;
|
|
349
|
+
const renderResult = useRender({
|
|
350
|
+
defaultTagName: "div",
|
|
351
|
+
props: mergedProps,
|
|
352
|
+
render
|
|
353
|
+
});
|
|
354
|
+
if (!defaultContent) return null;
|
|
355
|
+
return renderResult;
|
|
356
|
+
}
|
|
357
|
+
function FileUploadItemRemove({ className, render, ...props }) {
|
|
358
|
+
const { id } = useFileItemContext();
|
|
359
|
+
const { removeFile, disabled } = useFileUploadContext();
|
|
360
|
+
const defaultProps = {
|
|
361
|
+
type: "button",
|
|
362
|
+
disabled,
|
|
363
|
+
onClick: (e) => {
|
|
364
|
+
e.stopPropagation();
|
|
365
|
+
removeFile(id);
|
|
366
|
+
},
|
|
367
|
+
className: cn("relative flex items-center justify-center rounded-lg text-muted-foreground hover:bg-destructive/10 hover:text-destructive transition-colors shrink-0 outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-64 size-8 pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11", className),
|
|
368
|
+
"data-slot": "file-upload-item-remove"
|
|
369
|
+
};
|
|
370
|
+
const mergedProps = mergeProps(defaultProps, props);
|
|
371
|
+
if (mergedProps.children === void 0) mergedProps.children = /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Delete, { className: "size-4" }), /* @__PURE__ */ jsx("span", {
|
|
372
|
+
className: "sr-only",
|
|
373
|
+
children: "Remove file"
|
|
374
|
+
})] });
|
|
375
|
+
return useRender({
|
|
376
|
+
defaultTagName: "button",
|
|
377
|
+
props: mergedProps,
|
|
378
|
+
render
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
const attachmentVariants = cva("group/attachment relative flex w-fit max-w-full min-w-0 shrink-0 flex-wrap rounded-2xl border bg-card text-card-foreground focus-within:ring-1 focus-within:ring-ring/30 not-dark:bg-clip-padding transition-[color,background-color,border-color,box-shadow,opacity] duration-200 outline-none", {
|
|
382
|
+
variants: {
|
|
383
|
+
size: {
|
|
384
|
+
default: "gap-2 text-sm has-data-[slot=attachment-content]:px-2.5 has-data-[slot=attachment-content]:py-2 has-data-[slot=attachment-media]:p-2",
|
|
385
|
+
sm: "gap-2.5 text-xs has-data-[slot=attachment-content]:px-2 has-data-[slot=attachment-content]:py-1.5 has-data-[slot=attachment-media]:p-1.5",
|
|
386
|
+
xs: "gap-1.5 rounded-xl text-xs has-data-[slot=attachment-content]:px-1.5 has-data-[slot=attachment-content]:py-1 has-data-[slot=attachment-media]:p-1"
|
|
387
|
+
},
|
|
388
|
+
orientation: {
|
|
389
|
+
horizontal: "min-w-40 items-center",
|
|
390
|
+
vertical: "w-24 flex-col has-data-[slot=attachment-content]:w-30"
|
|
391
|
+
}
|
|
392
|
+
},
|
|
393
|
+
defaultVariants: {
|
|
394
|
+
size: "default",
|
|
395
|
+
orientation: "horizontal"
|
|
396
|
+
}
|
|
397
|
+
});
|
|
398
|
+
function Attachment({ className, state = "done", size = "default", orientation = "horizontal", ...props }) {
|
|
399
|
+
return /* @__PURE__ */ jsx("div", {
|
|
400
|
+
"data-slot": "attachment",
|
|
401
|
+
"data-state": state,
|
|
402
|
+
"data-size": size,
|
|
403
|
+
"data-orientation": orientation,
|
|
404
|
+
className: cn(attachmentVariants({
|
|
405
|
+
size,
|
|
406
|
+
orientation
|
|
407
|
+
}), state === "error" ? "border-destructive/30 bg-destructive/4" : "border-border/80 bg-background/50", state === "idle" && "border-dashed", "before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-2xl)-1px)] group-data-[size=xs]/attachment:before:rounded-[calc(var(--radius-xl)-1px)] before:shadow-[0_1px_--theme(--color-black/4%)] dark:before:shadow-[0_-1px_--theme(--color-white/6%)]", className),
|
|
408
|
+
...props
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
const attachmentMediaVariants = cva("relative flex aspect-square w-10 shrink-0 items-center justify-center overflow-hidden rounded-lg bg-muted text-foreground group-data-[orientation=vertical]/attachment:w-full group-data-[size=sm]/attachment:w-8 group-data-[size=xs]/attachment:w-7 group-data-[size=xs]/attachment:rounded-md group-data-[state=error]/attachment:bg-destructive/10 group-data-[state=error]/attachment:text-destructive group-data-[orientation=vertical]/attachment:*:data-[slot=spinner]:size-6! [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 group-data-[orientation=vertical]/attachment:[&_svg:not([class*='size-'])]:size-6 group-data-[size=xs]/attachment:[&_svg:not([class*='size-'])]:size-3.5", {
|
|
412
|
+
variants: { variant: {
|
|
413
|
+
icon: "",
|
|
414
|
+
image: "opacity-60 group-data-[state=done]/attachment:opacity-100 group-data-[state=idle]/attachment:opacity-100 *:[img]:aspect-square *:[img]:w-full *:[img]:object-cover"
|
|
415
|
+
} },
|
|
416
|
+
defaultVariants: { variant: "icon" }
|
|
417
|
+
});
|
|
418
|
+
function AttachmentMedia({ className, variant = "icon", ...props }) {
|
|
419
|
+
return /* @__PURE__ */ jsx("div", {
|
|
420
|
+
"data-slot": "attachment-media",
|
|
421
|
+
"data-variant": variant,
|
|
422
|
+
className: cn(attachmentMediaVariants({ variant }), className),
|
|
423
|
+
...props
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
function AttachmentContent({ className, ...props }) {
|
|
427
|
+
return /* @__PURE__ */ jsx("div", {
|
|
428
|
+
"data-slot": "attachment-content",
|
|
429
|
+
className: cn("max-w-full min-w-0 flex-1 leading-tight group-data-[orientation=vertical]/attachment:px-1", className),
|
|
430
|
+
...props
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
function AttachmentTitle({ className, ...props }) {
|
|
434
|
+
return /* @__PURE__ */ jsx("span", {
|
|
435
|
+
"data-slot": "attachment-title",
|
|
436
|
+
className: cn("block max-w-full min-w-0 truncate font-medium text-foreground group-data-[state=processing]/attachment:animate-pulse group-data-[state=uploading]/attachment:animate-pulse", className),
|
|
437
|
+
...props
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
function AttachmentDescription({ className, ...props }) {
|
|
441
|
+
return /* @__PURE__ */ jsx("span", {
|
|
442
|
+
"data-slot": "attachment-description",
|
|
443
|
+
className: cn("mt-0.5 block min-w-0 truncate text-xs text-muted-foreground group-data-[state=error]/attachment:text-destructive/80 max-w-full", className),
|
|
444
|
+
...props
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
function AttachmentActions({ className, ...props }) {
|
|
448
|
+
return /* @__PURE__ */ jsx("div", {
|
|
449
|
+
"data-slot": "attachment-actions",
|
|
450
|
+
className: cn("relative z-20 flex shrink-0 items-center group-data-[orientation=vertical]/attachment:absolute group-data-[orientation=vertical]/attachment:top-3 group-data-[orientation=vertical]/attachment:right-3 group-data-[orientation=vertical]/attachment:gap-1", className),
|
|
451
|
+
...props
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
function AttachmentAction({ className, variant, size = "icon-xs", ...props }) {
|
|
455
|
+
return /* @__PURE__ */ jsx(Button, {
|
|
456
|
+
"data-slot": "attachment-action",
|
|
457
|
+
variant: variant ?? "ghost",
|
|
458
|
+
size,
|
|
459
|
+
className: cn("pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11", className),
|
|
460
|
+
...props
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
function AttachmentTrigger({ className, render, type, ...props }) {
|
|
464
|
+
return useRender({
|
|
465
|
+
defaultTagName: "button",
|
|
466
|
+
props: mergeProps({
|
|
467
|
+
type: render ? type : type ?? "button",
|
|
468
|
+
className: cn("absolute inset-0 z-10 outline-none cursor-pointer", className)
|
|
469
|
+
}, props),
|
|
470
|
+
render,
|
|
471
|
+
state: { slot: "attachment-trigger" }
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
function AttachmentGroup({ className, ...props }) {
|
|
475
|
+
return /* @__PURE__ */ jsx("div", {
|
|
476
|
+
"data-slot": "attachment-group",
|
|
477
|
+
className: cn("flex min-w-0 snap-x snap-mandatory scrollbar-none gap-3 overflow-x-auto overscroll-x-contain py-1 *:data-[slot=attachment]:flex-none *:data-[slot=attachment]:snap-start", className),
|
|
478
|
+
...props
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
//#endregion
|
|
482
|
+
export { Attachment, AttachmentAction, AttachmentActions, AttachmentContent, AttachmentDescription, AttachmentGroup, AttachmentMedia, AttachmentTitle, AttachmentTrigger, FileUpload, FileUploadInput, FileUploadItem, FileUploadItemIcon, FileUploadItemName, FileUploadItemProgress, FileUploadItemRemove, FileUploadItemSize, FileUploadItemStatus, FileUploadList, FileUploadTrigger, attachmentMediaVariants, attachmentVariants, formatFileSize };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "cn";
|
|
3
|
+
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
import { Form as Form$1 } from "@base-ui/react/form";
|
|
5
|
+
//#region src/components/form.tsx
|
|
6
|
+
function Form({ className, ...props }) {
|
|
7
|
+
return /* @__PURE__ */ jsx(Form$1, {
|
|
8
|
+
className: cn("flex w-full flex-col gap-4", className),
|
|
9
|
+
"data-slot": "form",
|
|
10
|
+
...props
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
export { Form };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as React$1 from "react";
|
|
2
|
+
//#region src/components/frame.d.ts
|
|
3
|
+
declare function Frame({ className, ...props }: React$1.ComponentProps<"div">): React$1.JSX.Element;
|
|
4
|
+
declare function FramePanel({ className, ...props }: React$1.ComponentProps<"div">): React$1.JSX.Element;
|
|
5
|
+
declare function FrameHeader({ className, ...props }: React$1.ComponentProps<"header">): React$1.JSX.Element;
|
|
6
|
+
declare function FrameTitle({ className, ...props }: React$1.ComponentProps<"div">): React$1.JSX.Element;
|
|
7
|
+
declare function FrameDescription({ className, ...props }: React$1.ComponentProps<"div">): React$1.JSX.Element;
|
|
8
|
+
declare function FrameFooter({ className, ...props }: React$1.ComponentProps<"footer">): React$1.JSX.Element;
|
|
9
|
+
//#endregion
|
|
10
|
+
export { Frame, FrameDescription, FrameFooter, FrameHeader, FramePanel, FrameTitle };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { cn } from "cn";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
//#region src/components/frame.tsx
|
|
4
|
+
function Frame({ className, ...props }) {
|
|
5
|
+
return /* @__PURE__ */ jsx("div", {
|
|
6
|
+
className: cn("relative flex flex-col rounded-2xl bg-muted/72 p-1", "*:[[data-slot=frame-panel]+[data-slot=frame-panel]]:mt-1", className),
|
|
7
|
+
"data-slot": "frame",
|
|
8
|
+
...props
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
function FramePanel({ className, ...props }) {
|
|
12
|
+
return /* @__PURE__ */ jsx("div", {
|
|
13
|
+
className: cn("relative rounded-xl border bg-background bg-clip-padding p-[calc(--spacing(5)-1px)] shadow-xs/5 before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-xl)-1px)] before:shadow-[0_1px_--theme(--color-black/4%)] dark:before:shadow-[0_-1px_--theme(--color-white/6%)]", className),
|
|
14
|
+
"data-slot": "frame-panel",
|
|
15
|
+
...props
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
function FrameHeader({ className, ...props }) {
|
|
19
|
+
return /* @__PURE__ */ jsx("header", {
|
|
20
|
+
className: cn("flex flex-col px-5 py-4", className),
|
|
21
|
+
"data-slot": "frame-panel-header",
|
|
22
|
+
...props
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
function FrameTitle({ className, ...props }) {
|
|
26
|
+
return /* @__PURE__ */ jsx("div", {
|
|
27
|
+
className: cn("font-semibold text-sm", className),
|
|
28
|
+
"data-slot": "frame-panel-title",
|
|
29
|
+
...props
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
function FrameDescription({ className, ...props }) {
|
|
33
|
+
return /* @__PURE__ */ jsx("div", {
|
|
34
|
+
className: cn("text-muted-foreground text-sm", className),
|
|
35
|
+
"data-slot": "frame-panel-description",
|
|
36
|
+
...props
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
function FrameFooter({ className, ...props }) {
|
|
40
|
+
return /* @__PURE__ */ jsx("footer", {
|
|
41
|
+
className: cn("px-5 py-4", className),
|
|
42
|
+
"data-slot": "frame-panel-footer",
|
|
43
|
+
...props
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
//#endregion
|
|
47
|
+
export { Frame, FrameDescription, FrameFooter, FrameHeader, FramePanel, FrameTitle };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as React$1 from "react";
|
|
2
|
+
import { useRender } from "@base-ui/react/use-render";
|
|
3
|
+
//#region src/components/gauge.d.ts
|
|
4
|
+
export interface GaugeProps extends useRender.ComponentProps<"div"> {
|
|
5
|
+
/**
|
|
6
|
+
* The progress value (0 to max).
|
|
7
|
+
*/
|
|
8
|
+
value?: number;
|
|
9
|
+
/**
|
|
10
|
+
* The maximum progress value. Defaults to 100.
|
|
11
|
+
*/
|
|
12
|
+
max?: number;
|
|
13
|
+
/**
|
|
14
|
+
* The size of the gauge (diameter) in pixels. Defaults to 120.
|
|
15
|
+
*/
|
|
16
|
+
size?: number;
|
|
17
|
+
/**
|
|
18
|
+
* The stroke width of the SVG circle. Defaults to 12.
|
|
19
|
+
*/
|
|
20
|
+
strokeWidth?: number;
|
|
21
|
+
/**
|
|
22
|
+
* Whether to display the value text in the center. Defaults to true.
|
|
23
|
+
*/
|
|
24
|
+
showValue?: boolean;
|
|
25
|
+
}
|
|
26
|
+
export declare function Gauge({ render, value, max, size, strokeWidth, showValue, className, children, ...props }: GaugeProps): React$1.ReactElement;
|
|
27
|
+
//#endregion
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn } from "cn";
|
|
3
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
import { mergeProps } from "@base-ui/react/merge-props";
|
|
5
|
+
import { useRender } from "@base-ui/react/use-render";
|
|
6
|
+
//#region src/components/gauge.tsx
|
|
7
|
+
function Gauge({ render, value = 0, max = 100, size = 120, strokeWidth = 12, showValue = true, className, children, ...props }) {
|
|
8
|
+
const safeMax = Number.isFinite(max) && max > 0 ? max : 100;
|
|
9
|
+
const safeValue = Math.min(Math.max(Number.isFinite(value) ? value : 0, 0), safeMax);
|
|
10
|
+
const safeSize = Number.isFinite(size) && size > 0 ? size : 120;
|
|
11
|
+
const safeStrokeWidth = Number.isFinite(strokeWidth) && strokeWidth > 0 ? Math.min(strokeWidth, safeSize) : Math.min(12, safeSize);
|
|
12
|
+
const percentage = safeValue / safeMax * 100;
|
|
13
|
+
const center = safeSize / 2;
|
|
14
|
+
const radius = Math.max(0, center - safeStrokeWidth / 2);
|
|
15
|
+
const circumference = 2 * Math.PI * radius;
|
|
16
|
+
const semiCircumference = circumference / 2;
|
|
17
|
+
const trackDashoffset = semiCircumference;
|
|
18
|
+
const indicatorDashoffset = circumference - semiCircumference * (percentage / 100);
|
|
19
|
+
const defaultProps = {
|
|
20
|
+
className: cn("relative inline-flex flex-col items-center justify-end shrink-0", className),
|
|
21
|
+
role: "meter",
|
|
22
|
+
"aria-valuemin": 0,
|
|
23
|
+
"aria-valuemax": safeMax,
|
|
24
|
+
"aria-valuenow": safeValue,
|
|
25
|
+
"data-state": percentage === 100 ? "complete" : "loading",
|
|
26
|
+
"data-slot": "gauge",
|
|
27
|
+
style: {
|
|
28
|
+
width: safeSize,
|
|
29
|
+
height: safeSize / 2 + safeStrokeWidth / 2
|
|
30
|
+
},
|
|
31
|
+
children: /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("svg", {
|
|
32
|
+
className: "absolute top-0 left-0 overflow-visible pointer-events-none",
|
|
33
|
+
style: {
|
|
34
|
+
width: safeSize,
|
|
35
|
+
height: safeSize
|
|
36
|
+
},
|
|
37
|
+
viewBox: `0 0 ${safeSize} ${safeSize}`,
|
|
38
|
+
children: [/* @__PURE__ */ jsx("circle", {
|
|
39
|
+
className: "stroke-muted fill-transparent",
|
|
40
|
+
cx: center,
|
|
41
|
+
cy: center,
|
|
42
|
+
r: radius,
|
|
43
|
+
strokeWidth: safeStrokeWidth,
|
|
44
|
+
strokeDasharray: circumference,
|
|
45
|
+
strokeDashoffset: trackDashoffset,
|
|
46
|
+
strokeLinecap: "round",
|
|
47
|
+
transform: `rotate(180 ${center} ${center})`
|
|
48
|
+
}), /* @__PURE__ */ jsx("circle", {
|
|
49
|
+
className: "stroke-primary fill-transparent transition-[stroke-dashoffset] duration-500 ease-in-out",
|
|
50
|
+
cx: center,
|
|
51
|
+
cy: center,
|
|
52
|
+
r: radius,
|
|
53
|
+
strokeWidth: safeStrokeWidth,
|
|
54
|
+
strokeDasharray: circumference,
|
|
55
|
+
strokeDashoffset: indicatorDashoffset,
|
|
56
|
+
strokeLinecap: "round",
|
|
57
|
+
transform: `rotate(180 ${center} ${center})`
|
|
58
|
+
})]
|
|
59
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
60
|
+
className: "flex flex-col items-center justify-end z-10 w-full h-full pb-1",
|
|
61
|
+
children: children ? children : showValue ? /* @__PURE__ */ jsx("span", {
|
|
62
|
+
className: "text-2xl font-bold tracking-tighter text-foreground leading-none",
|
|
63
|
+
children: safeValue
|
|
64
|
+
}) : null
|
|
65
|
+
})] })
|
|
66
|
+
};
|
|
67
|
+
return useRender({
|
|
68
|
+
defaultTagName: "div",
|
|
69
|
+
render,
|
|
70
|
+
props: mergeProps(defaultProps, props)
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
//#endregion
|
|
74
|
+
export { Gauge };
|