@roy-ui/ui 0.0.12 → 0.0.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.
@@ -0,0 +1,364 @@
1
+ "use client";
2
+ import { TextMorph } from './chunk-MO7UPMW7.js';
3
+ import { Button } from './chunk-B6LXWGX5.js';
4
+ import { forwardRef, useRef, useState, useMemo, useEffect } from 'react';
5
+ import './UploadFiles-VBIEAV7L.css';
6
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
7
+
8
+ var DEFAULT_STATUS_WORDS = [
9
+ "Uploading\u2026",
10
+ "Transferring\u2026",
11
+ "Beaming bytes\u2026",
12
+ "Encrypting\u2026",
13
+ "Securing\u2026",
14
+ "Packing bits\u2026",
15
+ "Syncing\u2026",
16
+ "Crunching data\u2026",
17
+ "Pushing pixels\u2026",
18
+ "Almost there\u2026",
19
+ "Hang tight\u2026",
20
+ "Wrangling chunks\u2026",
21
+ "Verifying\u2026",
22
+ "Finalizing\u2026",
23
+ "Just a sec\u2026"
24
+ ];
25
+ var EXT_META = {
26
+ pdf: { label: "PDF", color: "#f0524b" },
27
+ doc: { label: "DOC", color: "#3b82f6" },
28
+ docx: { label: "DOCX", color: "#3b82f6" },
29
+ fig: { label: "FIG", color: "#a259ff" },
30
+ png: { label: "PNG", color: "#10b981" },
31
+ jpg: { label: "JPG", color: "#10b981" },
32
+ jpeg: { label: "JPG", color: "#10b981" },
33
+ gif: { label: "GIF", color: "#10b981" },
34
+ svg: { label: "SVG", color: "#f59e0b" },
35
+ zip: { label: "ZIP", color: "#f59e0b" },
36
+ mp4: { label: "MP4", color: "#ec4899" },
37
+ csv: { label: "CSV", color: "#22c55e" },
38
+ json: { label: "JSON", color: "#eab308" },
39
+ default: { label: "FILE", color: "#8b8b94" }
40
+ };
41
+ function extMeta(name) {
42
+ const ext = name.split(".").pop()?.toLowerCase() ?? "";
43
+ return EXT_META[ext] ?? EXT_META.default;
44
+ }
45
+ function trimNum(n) {
46
+ return (Math.round(n * 10) / 10).toString();
47
+ }
48
+ function formatBytes(bytes) {
49
+ if (!Number.isFinite(bytes) || bytes < 0) return "0 B";
50
+ if (bytes < 1024) return `${Math.round(bytes)} B`;
51
+ const kb = bytes / 1024;
52
+ if (kb < 1024) return `${trimNum(kb)} KB`;
53
+ const mb = kb / 1024;
54
+ if (mb < 1024) return `${trimNum(mb)} MB`;
55
+ return `${trimNum(mb / 1024)} GB`;
56
+ }
57
+ function shuffle(input) {
58
+ const a = input.slice();
59
+ for (let i = a.length - 1; i > 0; i--) {
60
+ const j = Math.floor(Math.random() * (i + 1));
61
+ [a[i], a[j]] = [a[j], a[i]];
62
+ }
63
+ return a;
64
+ }
65
+ var cn = (...classes) => classes.filter(Boolean).join(" ");
66
+ var UploadGlyph = () => /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", width: "22", height: "22", fill: "none", "aria-hidden": "true", children: [
67
+ /* @__PURE__ */ jsx(
68
+ "path",
69
+ {
70
+ d: "M12 15V4m0 0L7.5 8.5M12 4l4.5 4.5",
71
+ stroke: "currentColor",
72
+ strokeWidth: "1.6",
73
+ strokeLinecap: "round",
74
+ strokeLinejoin: "round"
75
+ }
76
+ ),
77
+ /* @__PURE__ */ jsx(
78
+ "path",
79
+ {
80
+ d: "M4 14v3.5A2.5 2.5 0 0 0 6.5 20h11a2.5 2.5 0 0 0 2.5-2.5V14",
81
+ stroke: "currentColor",
82
+ strokeWidth: "1.6",
83
+ strokeLinecap: "round"
84
+ }
85
+ )
86
+ ] });
87
+ var CloseGlyph = () => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", width: "16", height: "16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx(
88
+ "path",
89
+ {
90
+ d: "M6 6l12 12M18 6L6 18",
91
+ stroke: "currentColor",
92
+ strokeWidth: "1.6",
93
+ strokeLinecap: "round"
94
+ }
95
+ ) });
96
+ var TrashGlyph = () => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", width: "17", height: "17", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx(
97
+ "path",
98
+ {
99
+ d: "M4 7h16M9 7V5.5A1.5 1.5 0 0 1 10.5 4h3A1.5 1.5 0 0 1 15 5.5V7m2 0v11.5A1.5 1.5 0 0 1 15.5 20h-7A1.5 1.5 0 0 1 7 18.5V7m3 3.5v6m4-6v6",
100
+ stroke: "currentColor",
101
+ strokeWidth: "1.5",
102
+ strokeLinecap: "round",
103
+ strokeLinejoin: "round"
104
+ }
105
+ ) });
106
+ var CheckGlyph = () => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", width: "13", height: "13", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx(
107
+ "path",
108
+ {
109
+ d: "M4 12.5l5 5 11-12",
110
+ stroke: "currentColor",
111
+ strokeWidth: "2.2",
112
+ strokeLinecap: "round",
113
+ strokeLinejoin: "round"
114
+ }
115
+ ) });
116
+ var KebabGlyph = () => /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", width: "16", height: "16", fill: "currentColor", "aria-hidden": "true", children: [
117
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "5", r: "1.7" }),
118
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "1.7" }),
119
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "19", r: "1.7" })
120
+ ] });
121
+ function FileRow({
122
+ file,
123
+ word,
124
+ onRemove
125
+ }) {
126
+ const meta = extMeta(file.name);
127
+ const pct = file.progress != null ? Math.max(0, Math.min(100, Math.round(file.progress))) : file.size > 0 ? Math.max(0, Math.min(100, Math.round((file.uploaded ?? 0) / file.size * 100))) : 0;
128
+ const isUploading = file.status === "uploading";
129
+ const isError = file.status === "error";
130
+ return /* @__PURE__ */ jsxs("div", { className: "royui-upload__row", "data-status": file.status, children: [
131
+ /* @__PURE__ */ jsx(
132
+ "span",
133
+ {
134
+ className: "royui-upload__badge",
135
+ style: { ["--royui-upload-badge"]: file.icon ? "transparent" : meta.color },
136
+ "aria-hidden": "true",
137
+ children: file.icon ?? /* @__PURE__ */ jsx("span", { className: "royui-upload__badge-label", children: meta.label })
138
+ }
139
+ ),
140
+ /* @__PURE__ */ jsxs("div", { className: "royui-upload__row-main", children: [
141
+ /* @__PURE__ */ jsxs("div", { className: "royui-upload__row-top", children: [
142
+ /* @__PURE__ */ jsx("span", { className: "royui-upload__name", title: file.name, children: file.name }),
143
+ /* @__PURE__ */ jsx(
144
+ "button",
145
+ {
146
+ type: "button",
147
+ className: "royui-upload__row-action",
148
+ "aria-label": isUploading ? `Cancel ${file.name}` : `Remove ${file.name}`,
149
+ onClick: () => onRemove?.(file.id),
150
+ children: isUploading ? /* @__PURE__ */ jsx(CloseGlyph, {}) : /* @__PURE__ */ jsx(TrashGlyph, {})
151
+ }
152
+ )
153
+ ] }),
154
+ /* @__PURE__ */ jsxs("div", { className: "royui-upload__meta", children: [
155
+ file.status === "complete" && /* @__PURE__ */ jsxs(Fragment, { children: [
156
+ /* @__PURE__ */ jsx("span", { children: formatBytes(file.size) }),
157
+ /* @__PURE__ */ jsx("span", { className: "royui-upload__meta-sep", children: "/" }),
158
+ /* @__PURE__ */ jsxs("span", { className: "royui-upload__complete", children: [
159
+ /* @__PURE__ */ jsx(CheckGlyph, {}),
160
+ "Complete"
161
+ ] })
162
+ ] }),
163
+ isUploading && /* @__PURE__ */ jsxs(Fragment, { children: [
164
+ /* @__PURE__ */ jsxs("span", { children: [
165
+ /* @__PURE__ */ jsx(TextMorph, { value: formatBytes(file.uploaded ?? 0) }),
166
+ " of",
167
+ " ",
168
+ formatBytes(file.size)
169
+ ] }),
170
+ /* @__PURE__ */ jsx("span", { className: "royui-upload__meta-sep", children: "/" }),
171
+ /* @__PURE__ */ jsx(
172
+ TextMorph,
173
+ {
174
+ className: "royui-upload__status",
175
+ value: word,
176
+ renderText: (t) => /* @__PURE__ */ jsx("span", { className: "royui-upload__shimmer", children: t })
177
+ }
178
+ )
179
+ ] }),
180
+ isError && /* @__PURE__ */ jsxs(Fragment, { children: [
181
+ /* @__PURE__ */ jsx("span", { children: formatBytes(file.size) }),
182
+ /* @__PURE__ */ jsx("span", { className: "royui-upload__meta-sep", children: "/" }),
183
+ /* @__PURE__ */ jsx("span", { className: "royui-upload__error", children: "Failed" })
184
+ ] })
185
+ ] }),
186
+ isUploading && /* @__PURE__ */ jsxs("div", { className: "royui-upload__progress", children: [
187
+ /* @__PURE__ */ jsx(
188
+ "div",
189
+ {
190
+ className: "royui-upload__bar",
191
+ role: "progressbar",
192
+ "aria-valuenow": pct,
193
+ "aria-valuemin": 0,
194
+ "aria-valuemax": 100,
195
+ children: /* @__PURE__ */ jsx("div", { className: "royui-upload__bar-fill", style: { width: `${pct}%` } })
196
+ }
197
+ ),
198
+ /* @__PURE__ */ jsxs("span", { className: "royui-upload__pct", children: [
199
+ pct,
200
+ "%"
201
+ ] })
202
+ ] })
203
+ ] })
204
+ ] });
205
+ }
206
+ var UploadFiles = forwardRef(
207
+ function UploadFiles2({
208
+ files,
209
+ title = "Upload files",
210
+ maxSizeLabel = "MAX FILE SIZE: 20 MB",
211
+ accept,
212
+ multiple = true,
213
+ theme = "dark",
214
+ statusWords = DEFAULT_STATUS_WORDS,
215
+ actionLabel,
216
+ onFilesSelected,
217
+ onRemove,
218
+ onRemoveAll,
219
+ onClose,
220
+ onAction,
221
+ className = "",
222
+ ...rest
223
+ }, ref) {
224
+ const inputRef = useRef(null);
225
+ const [isDragging, setDragging] = useState(false);
226
+ const [tick, setTick] = useState(0);
227
+ const anyUploading = files.some((f) => f.status === "uploading");
228
+ const words = useMemo(
229
+ () => statusWords.length ? shuffle(statusWords) : DEFAULT_STATUS_WORDS,
230
+ // eslint-disable-next-line react-hooks/exhaustive-deps
231
+ []
232
+ );
233
+ useEffect(() => {
234
+ if (!anyUploading) return;
235
+ const id = setInterval(() => setTick((t) => t + 1), 1900);
236
+ return () => clearInterval(id);
237
+ }, [anyUploading]);
238
+ const emit = (list) => {
239
+ if (!list || list.length === 0) return;
240
+ onFilesSelected?.(Array.from(list));
241
+ };
242
+ const classes = cn(
243
+ "royui-upload",
244
+ theme === "dark" && "royui-upload--dark",
245
+ theme === "light" && "royui-upload--light",
246
+ theme === "auto" && "royui-upload--auto",
247
+ className
248
+ );
249
+ return /* @__PURE__ */ jsxs("div", { ref, className: classes, ...rest, children: [
250
+ /* @__PURE__ */ jsxs("header", { className: "royui-upload__header", children: [
251
+ /* @__PURE__ */ jsx("h2", { className: "royui-upload__title", children: title }),
252
+ onClose && /* @__PURE__ */ jsx(
253
+ "button",
254
+ {
255
+ type: "button",
256
+ className: "royui-upload__close",
257
+ "aria-label": "Close",
258
+ onClick: onClose,
259
+ children: /* @__PURE__ */ jsx(CloseGlyph, {})
260
+ }
261
+ )
262
+ ] }),
263
+ /* @__PURE__ */ jsxs(
264
+ "div",
265
+ {
266
+ className: cn("royui-upload__dropzone", isDragging && "is-dragging"),
267
+ onDragOver: (e) => {
268
+ e.preventDefault();
269
+ setDragging(true);
270
+ },
271
+ onDragLeave: (e) => {
272
+ e.preventDefault();
273
+ setDragging(false);
274
+ },
275
+ onDrop: (e) => {
276
+ e.preventDefault();
277
+ setDragging(false);
278
+ emit(e.dataTransfer.files);
279
+ },
280
+ children: [
281
+ /* @__PURE__ */ jsx("span", { className: "royui-upload__drop-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx(UploadGlyph, {}) }),
282
+ /* @__PURE__ */ jsxs("p", { className: "royui-upload__drop-text", children: [
283
+ "Drag and drop or",
284
+ " ",
285
+ /* @__PURE__ */ jsx(
286
+ "button",
287
+ {
288
+ type: "button",
289
+ className: "royui-upload__browse",
290
+ onClick: () => inputRef.current?.click(),
291
+ children: "browse files"
292
+ }
293
+ )
294
+ ] }),
295
+ /* @__PURE__ */ jsx("p", { className: "royui-upload__drop-hint", children: maxSizeLabel }),
296
+ /* @__PURE__ */ jsx(
297
+ "input",
298
+ {
299
+ ref: inputRef,
300
+ type: "file",
301
+ accept,
302
+ multiple,
303
+ className: "royui-upload__input",
304
+ onChange: (e) => {
305
+ emit(e.target.files);
306
+ e.target.value = "";
307
+ }
308
+ }
309
+ )
310
+ ]
311
+ }
312
+ ),
313
+ files.length > 0 && /* @__PURE__ */ jsx("div", { className: "royui-upload__list", children: files.map((file, i) => /* @__PURE__ */ jsx(
314
+ FileRow,
315
+ {
316
+ file,
317
+ word: words[(tick + i) % words.length],
318
+ onRemove
319
+ },
320
+ file.id
321
+ )) }),
322
+ files.length > 0 && /* @__PURE__ */ jsxs("footer", { className: "royui-upload__footer", children: [
323
+ /* @__PURE__ */ jsx(
324
+ "button",
325
+ {
326
+ type: "button",
327
+ className: "royui-upload__icon-btn",
328
+ "aria-label": "More options",
329
+ children: /* @__PURE__ */ jsx(KebabGlyph, {})
330
+ }
331
+ ),
332
+ /* @__PURE__ */ jsxs(
333
+ "button",
334
+ {
335
+ type: "button",
336
+ className: "royui-upload__remove-all",
337
+ onClick: onRemoveAll,
338
+ children: [
339
+ /* @__PURE__ */ jsx(TrashGlyph, {}),
340
+ "Remove all"
341
+ ]
342
+ }
343
+ ),
344
+ /* @__PURE__ */ jsx("span", { className: "royui-upload__footer-spacer" }),
345
+ /* @__PURE__ */ jsx(
346
+ Button,
347
+ {
348
+ size: "sm",
349
+ variant: "secondary",
350
+ className: "royui-upload__action",
351
+ disabled: anyUploading,
352
+ onClick: onAction,
353
+ children: actionLabel ?? (anyUploading ? /* @__PURE__ */ jsx(TextMorph, { value: "Uploading\u2026" }) : "Done")
354
+ }
355
+ )
356
+ ] })
357
+ ] });
358
+ }
359
+ );
360
+ UploadFiles.displayName = "UploadFiles";
361
+
362
+ export { UploadFiles };
363
+ //# sourceMappingURL=chunk-6JYEHTQR.js.map
364
+ //# sourceMappingURL=chunk-6JYEHTQR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/upload-files/UploadFiles.tsx"],"names":["UploadFiles"],"mappings":"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","file":"chunk-6JYEHTQR.js","sourcesContent":["'use client';\n\nimport {\n forwardRef,\n useEffect,\n useMemo,\n useRef,\n useState,\n type HTMLAttributes,\n type ReactNode,\n} from 'react';\nimport { TextMorph } from '../text-morph/TextMorph';\nimport { Button } from '../button/Button';\nimport './UploadFiles.css';\n\nexport type UploadStatus = 'uploading' | 'complete' | 'error';\n\nexport interface UploadFile {\n /** Stable id — used as the React key and passed back to onRemove. */\n id: string;\n /** File name including extension; drives the type badge. */\n name: string;\n /** Total size in bytes. */\n size: number;\n /** Bytes transferred so far. Drives the \"X OF Y\" label and, when\n * `progress` is omitted, the percentage. */\n uploaded?: number;\n /** Progress 0–100. Falls back to uploaded / size when omitted. */\n progress?: number;\n /** Lifecycle state. */\n status: UploadStatus;\n /** Optional override for the file-type badge. */\n icon?: ReactNode;\n}\n\nexport type UploadTheme = 'auto' | 'light' | 'dark';\n\nexport interface UploadFilesProps\n extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {\n /** The files to render. The component is controlled — it draws this\n * list and emits events; the consumer owns the upload + progress. */\n files: UploadFile[];\n /** Panel heading. Defaults to \"Upload files\". */\n title?: ReactNode;\n /** Caption under the dropzone. Defaults to \"MAX FILE SIZE: 20 MB\". */\n maxSizeLabel?: string;\n /** Forwarded to the hidden file input's `accept`. */\n accept?: string;\n /** Allow selecting multiple files. Defaults to true. */\n multiple?: boolean;\n /** Color scheme. Dark by default; \"auto\" follows the OS. */\n theme?: UploadTheme;\n /** Words cycled (and morphed) in the per-file status while uploading.\n * Defaults to a long, varied upload vocabulary so the wait stays lively. */\n statusWords?: string[];\n /** Footer action button content. Defaults to a state-derived label\n * (\"Uploading…\" while in flight, \"Done\" otherwise). */\n actionLabel?: ReactNode;\n /** Fired with the dropped / browsed File objects. */\n onFilesSelected?: (files: File[]) => void;\n /** Fired when a single row's trash / cancel control is pressed. */\n onRemove?: (id: string) => void;\n /** Fired by the \"Remove all\" footer control. */\n onRemoveAll?: () => void;\n /** When provided, renders the header close (×) button. */\n onClose?: () => void;\n /** Fired by the footer action button. */\n onAction?: () => void;\n}\n\nconst DEFAULT_STATUS_WORDS = [\n 'Uploading…',\n 'Transferring…',\n 'Beaming bytes…',\n 'Encrypting…',\n 'Securing…',\n 'Packing bits…',\n 'Syncing…',\n 'Crunching data…',\n 'Pushing pixels…',\n 'Almost there…',\n 'Hang tight…',\n 'Wrangling chunks…',\n 'Verifying…',\n 'Finalizing…',\n 'Just a sec…',\n];\n\nconst EXT_META: Record<string, { label: string; color: string }> = {\n pdf: { label: 'PDF', color: '#f0524b' },\n doc: { label: 'DOC', color: '#3b82f6' },\n docx: { label: 'DOCX', color: '#3b82f6' },\n fig: { label: 'FIG', color: '#a259ff' },\n png: { label: 'PNG', color: '#10b981' },\n jpg: { label: 'JPG', color: '#10b981' },\n jpeg: { label: 'JPG', color: '#10b981' },\n gif: { label: 'GIF', color: '#10b981' },\n svg: { label: 'SVG', color: '#f59e0b' },\n zip: { label: 'ZIP', color: '#f59e0b' },\n mp4: { label: 'MP4', color: '#ec4899' },\n csv: { label: 'CSV', color: '#22c55e' },\n json: { label: 'JSON', color: '#eab308' },\n default: { label: 'FILE', color: '#8b8b94' },\n};\n\nfunction extMeta(name: string) {\n const ext = name.split('.').pop()?.toLowerCase() ?? '';\n return EXT_META[ext] ?? EXT_META.default!;\n}\n\nfunction trimNum(n: number): string {\n return (Math.round(n * 10) / 10).toString();\n}\n\n/** Human-readable byte size, e.g. 2.7 MB / 14 MB. */\nfunction formatBytes(bytes: number): string {\n if (!Number.isFinite(bytes) || bytes < 0) return '0 B';\n if (bytes < 1024) return `${Math.round(bytes)} B`;\n const kb = bytes / 1024;\n if (kb < 1024) return `${trimNum(kb)} KB`;\n const mb = kb / 1024;\n if (mb < 1024) return `${trimNum(mb)} MB`;\n return `${trimNum(mb / 1024)} GB`;\n}\n\n/** Deterministic-ish shuffle (Fisher–Yates) so the word order varies per\n * session without two adjacent entries being the same word. */\nfunction shuffle<T>(input: T[]): T[] {\n const a = input.slice();\n for (let i = a.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n [a[i], a[j]] = [a[j]!, a[i]!];\n }\n return a;\n}\n\nconst cn = (...classes: (string | false | undefined)[]) =>\n classes.filter(Boolean).join(' ');\n\n/* ── Icons ──────────────────────────────────────────────────────────────── */\n\nconst UploadGlyph = () => (\n <svg viewBox=\"0 0 24 24\" width=\"22\" height=\"22\" fill=\"none\" aria-hidden=\"true\">\n <path\n d=\"M12 15V4m0 0L7.5 8.5M12 4l4.5 4.5\"\n stroke=\"currentColor\"\n strokeWidth=\"1.6\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n <path\n d=\"M4 14v3.5A2.5 2.5 0 0 0 6.5 20h11a2.5 2.5 0 0 0 2.5-2.5V14\"\n stroke=\"currentColor\"\n strokeWidth=\"1.6\"\n strokeLinecap=\"round\"\n />\n </svg>\n);\n\nconst CloseGlyph = () => (\n <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"none\" aria-hidden=\"true\">\n <path\n d=\"M6 6l12 12M18 6L6 18\"\n stroke=\"currentColor\"\n strokeWidth=\"1.6\"\n strokeLinecap=\"round\"\n />\n </svg>\n);\n\nconst TrashGlyph = () => (\n <svg viewBox=\"0 0 24 24\" width=\"17\" height=\"17\" fill=\"none\" aria-hidden=\"true\">\n <path\n d=\"M4 7h16M9 7V5.5A1.5 1.5 0 0 1 10.5 4h3A1.5 1.5 0 0 1 15 5.5V7m2 0v11.5A1.5 1.5 0 0 1 15.5 20h-7A1.5 1.5 0 0 1 7 18.5V7m3 3.5v6m4-6v6\"\n stroke=\"currentColor\"\n strokeWidth=\"1.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n);\n\nconst CheckGlyph = () => (\n <svg viewBox=\"0 0 24 24\" width=\"13\" height=\"13\" fill=\"none\" aria-hidden=\"true\">\n <path\n d=\"M4 12.5l5 5 11-12\"\n stroke=\"currentColor\"\n strokeWidth=\"2.2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n);\n\nconst KebabGlyph = () => (\n <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"currentColor\" aria-hidden=\"true\">\n <circle cx=\"12\" cy=\"5\" r=\"1.7\" />\n <circle cx=\"12\" cy=\"12\" r=\"1.7\" />\n <circle cx=\"12\" cy=\"19\" r=\"1.7\" />\n </svg>\n);\n\n/* ── File row ───────────────────────────────────────────────────────────── */\n\nfunction FileRow({\n file,\n word,\n onRemove,\n}: {\n file: UploadFile;\n word: string;\n onRemove?: (id: string) => void;\n}) {\n const meta = extMeta(file.name);\n const pct =\n file.progress != null\n ? Math.max(0, Math.min(100, Math.round(file.progress)))\n : file.size > 0\n ? Math.max(0, Math.min(100, Math.round(((file.uploaded ?? 0) / file.size) * 100)))\n : 0;\n\n const isUploading = file.status === 'uploading';\n const isError = file.status === 'error';\n\n return (\n <div className=\"royui-upload__row\" data-status={file.status}>\n <span\n className=\"royui-upload__badge\"\n style={{ ['--royui-upload-badge' as string]: file.icon ? 'transparent' : meta.color }}\n aria-hidden=\"true\"\n >\n {file.icon ?? <span className=\"royui-upload__badge-label\">{meta.label}</span>}\n </span>\n\n <div className=\"royui-upload__row-main\">\n <div className=\"royui-upload__row-top\">\n <span className=\"royui-upload__name\" title={file.name}>\n {file.name}\n </span>\n <button\n type=\"button\"\n className=\"royui-upload__row-action\"\n aria-label={isUploading ? `Cancel ${file.name}` : `Remove ${file.name}`}\n onClick={() => onRemove?.(file.id)}\n >\n {isUploading ? <CloseGlyph /> : <TrashGlyph />}\n </button>\n </div>\n\n <div className=\"royui-upload__meta\">\n {file.status === 'complete' && (\n <>\n <span>{formatBytes(file.size)}</span>\n <span className=\"royui-upload__meta-sep\">/</span>\n <span className=\"royui-upload__complete\">\n <CheckGlyph />\n Complete\n </span>\n </>\n )}\n {isUploading && (\n <>\n <span>\n <TextMorph value={formatBytes(file.uploaded ?? 0)} /> of{' '}\n {formatBytes(file.size)}\n </span>\n <span className=\"royui-upload__meta-sep\">/</span>\n <TextMorph\n className=\"royui-upload__status\"\n value={word}\n renderText={(t) => (\n <span className=\"royui-upload__shimmer\">{t}</span>\n )}\n />\n </>\n )}\n {isError && (\n <>\n <span>{formatBytes(file.size)}</span>\n <span className=\"royui-upload__meta-sep\">/</span>\n <span className=\"royui-upload__error\">Failed</span>\n </>\n )}\n </div>\n\n {isUploading && (\n <div className=\"royui-upload__progress\">\n <div\n className=\"royui-upload__bar\"\n role=\"progressbar\"\n aria-valuenow={pct}\n aria-valuemin={0}\n aria-valuemax={100}\n >\n <div className=\"royui-upload__bar-fill\" style={{ width: `${pct}%` }} />\n </div>\n <span className=\"royui-upload__pct\">{pct}%</span>\n </div>\n )}\n </div>\n </div>\n );\n}\n\n/* ── Component ──────────────────────────────────────────────────────────── */\n\nexport const UploadFiles = forwardRef<HTMLDivElement, UploadFilesProps>(\n function UploadFiles(\n {\n files,\n title = 'Upload files',\n maxSizeLabel = 'MAX FILE SIZE: 20 MB',\n accept,\n multiple = true,\n theme = 'dark',\n statusWords = DEFAULT_STATUS_WORDS,\n actionLabel,\n onFilesSelected,\n onRemove,\n onRemoveAll,\n onClose,\n onAction,\n className = '',\n ...rest\n },\n ref,\n ) {\n const inputRef = useRef<HTMLInputElement>(null);\n const [isDragging, setDragging] = useState(false);\n const [tick, setTick] = useState(0);\n\n const anyUploading = files.some((f) => f.status === 'uploading');\n\n // Shuffle once per mount so the word order varies between sessions.\n const words = useMemo(\n () => (statusWords.length ? shuffle(statusWords) : DEFAULT_STATUS_WORDS),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [],\n );\n\n // Advance the shared cycle only while something is in flight.\n useEffect(() => {\n if (!anyUploading) return;\n const id = setInterval(() => setTick((t) => t + 1), 1900);\n return () => clearInterval(id);\n }, [anyUploading]);\n\n const emit = (list: FileList | null) => {\n if (!list || list.length === 0) return;\n onFilesSelected?.(Array.from(list));\n };\n\n const classes = cn(\n 'royui-upload',\n theme === 'dark' && 'royui-upload--dark',\n theme === 'light' && 'royui-upload--light',\n theme === 'auto' && 'royui-upload--auto',\n className,\n );\n\n return (\n <div ref={ref} className={classes} {...rest}>\n <header className=\"royui-upload__header\">\n <h2 className=\"royui-upload__title\">{title}</h2>\n {onClose && (\n <button\n type=\"button\"\n className=\"royui-upload__close\"\n aria-label=\"Close\"\n onClick={onClose}\n >\n <CloseGlyph />\n </button>\n )}\n </header>\n\n <div\n className={cn('royui-upload__dropzone', isDragging && 'is-dragging')}\n onDragOver={(e) => {\n e.preventDefault();\n setDragging(true);\n }}\n onDragLeave={(e) => {\n e.preventDefault();\n setDragging(false);\n }}\n onDrop={(e) => {\n e.preventDefault();\n setDragging(false);\n emit(e.dataTransfer.files);\n }}\n >\n <span className=\"royui-upload__drop-icon\" aria-hidden=\"true\">\n <UploadGlyph />\n </span>\n <p className=\"royui-upload__drop-text\">\n Drag and drop or{' '}\n <button\n type=\"button\"\n className=\"royui-upload__browse\"\n onClick={() => inputRef.current?.click()}\n >\n browse files\n </button>\n </p>\n <p className=\"royui-upload__drop-hint\">{maxSizeLabel}</p>\n <input\n ref={inputRef}\n type=\"file\"\n accept={accept}\n multiple={multiple}\n className=\"royui-upload__input\"\n onChange={(e) => {\n emit(e.target.files);\n e.target.value = '';\n }}\n />\n </div>\n\n {files.length > 0 && (\n <div className=\"royui-upload__list\">\n {files.map((file, i) => (\n <FileRow\n key={file.id}\n file={file}\n word={words[(tick + i) % words.length]!}\n onRemove={onRemove}\n />\n ))}\n </div>\n )}\n\n {files.length > 0 && (\n <footer className=\"royui-upload__footer\">\n <button\n type=\"button\"\n className=\"royui-upload__icon-btn\"\n aria-label=\"More options\"\n >\n <KebabGlyph />\n </button>\n <button\n type=\"button\"\n className=\"royui-upload__remove-all\"\n onClick={onRemoveAll}\n >\n <TrashGlyph />\n Remove all\n </button>\n <span className=\"royui-upload__footer-spacer\" />\n <Button\n size=\"sm\"\n variant=\"secondary\"\n className=\"royui-upload__action\"\n disabled={anyUploading}\n onClick={onAction}\n >\n {actionLabel ??\n (anyUploading ? (\n <TextMorph value=\"Uploading…\" />\n ) : (\n 'Done'\n ))}\n </Button>\n </footer>\n )}\n </div>\n );\n },\n);\n\nUploadFiles.displayName = 'UploadFiles';\n"]}
@@ -157,5 +157,5 @@ var Button = forwardRef(
157
157
  Button.displayName = "Button";
158
158
 
159
159
  export { Button };
160
- //# sourceMappingURL=chunk-4SGMAZBG.js.map
161
- //# sourceMappingURL=chunk-4SGMAZBG.js.map
160
+ //# sourceMappingURL=chunk-B6LXWGX5.js.map
161
+ //# sourceMappingURL=chunk-B6LXWGX5.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/button/Button.tsx"],"names":[],"mappings":"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","file":"chunk-4SGMAZBG.js","sourcesContent":["'use client';\n\nimport {\n Children,\n cloneElement,\n forwardRef,\n isValidElement,\n type ButtonHTMLAttributes,\n type CSSProperties,\n type ReactElement,\n type ReactNode,\n type Ref,\n} from 'react';\nimport './Button.css';\n\nexport type ButtonSize = 'sm' | 'md' | 'lg';\nexport type ButtonVariant = 'primary' | 'secondary' | 'ghost';\n\nexport interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n /** Visual scale. Defaults to \"md\". */\n size?: ButtonSize;\n /** Visual weight. \"primary\" is the solid depth button; \"secondary\" is a\n * quieter raised surface; \"ghost\" is text-only until hovered. Defaults to \"primary\". */\n variant?: ButtonVariant;\n /**\n * Render the single child element instead of a <button>, merging the\n * button's classes, styles, and props onto it. Use this for a\n * button-styled link with real anchor semantics:\n * `<Button asChild><Link href=\"/x\">Go</Link></Button>`.\n */\n asChild?: boolean;\n /**\n * Base color (hex or rgb()). The whole depth treatment derives from it —\n * a lighter top and darker base for the gradient, a hairline ring that\n * adapts to the tone, and a readable label color picked by luminance.\n * Defaults to the near-black surface. Override individual CSS variables\n * (--royui-btn-top etc.) for finer control.\n */\n color?: string;\n /** Stretch the button to fill its container. Defaults to false. */\n fullWidth?: boolean;\n /** Replaces children with a spinner and disables the button. */\n loading?: boolean;\n /** Optional override for the loading visual. Defaults to a spinner. */\n loadingLabel?: ReactNode;\n children: ReactNode;\n}\n\ntype RGB = { r: number; g: number; b: number };\n\n/** Parse hex (#rgb/#rrggbb/#rrggbbaa) or rgb()/rgba(). Returns null otherwise. */\nfunction parseColor(input: string): RGB | null {\n const s = input.trim();\n const hex = s.replace(/^#/, '');\n if (/^[0-9a-f]{3,4}$/i.test(hex)) {\n return {\n r: parseInt(hex[0]! + hex[0]!, 16),\n g: parseInt(hex[1]! + hex[1]!, 16),\n b: parseInt(hex[2]! + hex[2]!, 16),\n };\n }\n if (/^[0-9a-f]{6}$/i.test(hex) || /^[0-9a-f]{8}$/i.test(hex)) {\n return {\n r: parseInt(hex.slice(0, 2), 16),\n g: parseInt(hex.slice(2, 4), 16),\n b: parseInt(hex.slice(4, 6), 16),\n };\n }\n const m = s.match(/^rgba?\\(([^)]+)\\)$/i);\n if (m) {\n const p = m[1]!.split(/[,\\s/]+/).filter(Boolean).map(Number);\n if (p.length >= 3 && p.slice(0, 3).every((n) => !Number.isNaN(n))) {\n return { r: p[0]!, g: p[1]!, b: p[2]! };\n }\n }\n return null;\n}\n\nconst clamp = (n: number) => Math.max(0, Math.min(255, Math.round(n)));\n/** Mix a channel toward a target (255 = white, 0 = black) by amount 0..1. */\nconst mix = (c: RGB, target: number, amt: number): RGB => ({\n r: clamp(c.r + (target - c.r) * amt),\n g: clamp(c.g + (target - c.g) * amt),\n b: clamp(c.b + (target - c.b) * amt),\n});\nconst rgb = (c: RGB) => `rgb(${c.r}, ${c.g}, ${c.b})`;\n\n/** Derive the full set of depth variables from one base color. */\nfunction tintVars(color: string): CSSProperties {\n const base = parseColor(color);\n // Unparseable (named color, hsl, etc.) — let the raw value drive the\n // surface and keep the default overlays. Depth still reads via the shade.\n if (!base) {\n return {\n ['--royui-btn-top']: color,\n ['--royui-btn-bottom']: color,\n } as CSSProperties;\n }\n const lum = (0.2126 * base.r + 0.7152 * base.g + 0.0722 * base.b) / 255;\n const light = lum > 0.62;\n const shade = mix(base, 0, 0.6);\n return {\n ['--royui-btn-top']: rgb(mix(base, 255, 0.1)),\n ['--royui-btn-bottom']: rgb(mix(base, 0, 0.1)),\n // Light buttons need a darker edge to show on light backgrounds; dark\n // buttons need a lighter one. Either way the ring stays a hairline.\n ['--royui-btn-ring']: rgb(light ? mix(base, 0, 0.16) : mix(base, 255, 0.22)),\n ['--royui-btn-fg']: light ? '#0b0b0d' : '#ffffff',\n // Stronger top sheen on light surfaces (where a faint white is invisible),\n // an in-hue shade at the base so it never looks like grime.\n ['--royui-btn-highlight']: light\n ? 'rgba(255, 255, 255, 0.5)'\n : 'rgba(255, 255, 255, 0.15)',\n ['--royui-btn-shade']: `rgba(${shade.r}, ${shade.g}, ${shade.b}, 0.5)`,\n ['--royui-btn-focus']: light ? 'rgba(0, 0, 0, 0.55)' : 'rgba(255, 255, 255, 0.7)',\n // Press darkens less on light buttons so the dip doesn't look like a smudge.\n ['--royui-btn-active-bright']: light ? '0.93' : '0.86',\n } as CSSProperties;\n}\n\n/** Combine the forwarded ref with the child's own ref (asChild path). */\nfunction mergeRefs<T>(...refs: (Ref<T> | undefined)[]) {\n return (node: T) => {\n for (const ref of refs) {\n if (typeof ref === 'function') ref(node);\n else if (ref && typeof ref === 'object') {\n (ref as { current: T | null }).current = node;\n }\n }\n };\n}\n\nconst DefaultSpinner = () => (\n <span className=\"royui-btn__spinner\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"none\">\n <circle\n cx=\"12\"\n cy=\"12\"\n r=\"9\"\n stroke=\"currentColor\"\n strokeOpacity=\"0.3\"\n strokeWidth=\"2.5\"\n />\n <path\n d=\"M21 12a9 9 0 0 0-9-9\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n />\n </svg>\n </span>\n);\n\nexport const Button = forwardRef<HTMLButtonElement, ButtonProps>(\n (\n {\n size = 'md',\n variant = 'primary',\n asChild = false,\n color,\n fullWidth = false,\n loading = false,\n loadingLabel,\n disabled,\n className = '',\n style,\n children,\n type = 'button',\n ...rest\n },\n ref,\n ) => {\n const classes = [\n 'royui-btn',\n `royui-btn--${size}`,\n `royui-btn--${variant}`,\n fullWidth ? 'royui-btn--full' : '',\n loading ? 'royui-btn--loading' : '',\n className,\n ]\n .filter(Boolean)\n .join(' ');\n\n // Derived vars first so an explicit `style` (or CSS-var override) still wins.\n const mergedStyle = color ? { ...tintVars(color), ...style } : style;\n const spinner = loadingLabel ?? <DefaultSpinner />;\n const isDisabled = disabled || loading;\n\n // Polymorphic path: paint the styles onto the child element (e.g. a link)\n // instead of a <button>, so native anchor semantics survive.\n if (asChild && isValidElement(children)) {\n const child = children as ReactElement<{\n className?: string;\n style?: CSSProperties;\n children?: ReactNode;\n }>;\n const childRef = (child as unknown as { ref?: Ref<unknown> }).ref;\n return cloneElement(\n child,\n {\n ...rest,\n className: [classes, child.props.className].filter(Boolean).join(' '),\n style: { ...mergedStyle, ...child.props.style },\n 'aria-busy': loading || undefined,\n 'aria-disabled': isDisabled || undefined,\n ref: mergeRefs(ref as Ref<unknown>, childRef),\n } as Record<string, unknown>,\n loading ? spinner : child.props.children,\n );\n }\n\n return (\n <button\n ref={ref}\n type={type}\n disabled={isDisabled}\n className={classes}\n style={mergedStyle}\n aria-busy={loading || undefined}\n {...rest}\n >\n {loading ? spinner : children}\n </button>\n );\n },\n);\n\nButton.displayName = 'Button';\n"]}
1
+ {"version":3,"sources":["../src/components/button/Button.tsx"],"names":[],"mappings":"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","file":"chunk-B6LXWGX5.js","sourcesContent":["'use client';\n\nimport {\n Children,\n cloneElement,\n forwardRef,\n isValidElement,\n type ButtonHTMLAttributes,\n type CSSProperties,\n type ReactElement,\n type ReactNode,\n type Ref,\n} from 'react';\nimport './Button.css';\n\nexport type ButtonSize = 'sm' | 'md' | 'lg';\nexport type ButtonVariant = 'primary' | 'secondary' | 'ghost';\n\nexport interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n /** Visual scale. Defaults to \"md\". */\n size?: ButtonSize;\n /** Visual weight. \"primary\" is the solid depth button; \"secondary\" is a\n * quieter raised surface; \"ghost\" is text-only until hovered. Defaults to \"primary\". */\n variant?: ButtonVariant;\n /**\n * Render the single child element instead of a <button>, merging the\n * button's classes, styles, and props onto it. Use this for a\n * button-styled link with real anchor semantics:\n * `<Button asChild><Link href=\"/x\">Go</Link></Button>`.\n */\n asChild?: boolean;\n /**\n * Base color (hex or rgb()). The whole depth treatment derives from it —\n * a lighter top and darker base for the gradient, a hairline ring that\n * adapts to the tone, and a readable label color picked by luminance.\n * Defaults to the near-black surface. Override individual CSS variables\n * (--royui-btn-top etc.) for finer control.\n */\n color?: string;\n /** Stretch the button to fill its container. Defaults to false. */\n fullWidth?: boolean;\n /** Replaces children with a spinner and disables the button. */\n loading?: boolean;\n /** Optional override for the loading visual. Defaults to a spinner. */\n loadingLabel?: ReactNode;\n children: ReactNode;\n}\n\ntype RGB = { r: number; g: number; b: number };\n\n/** Parse hex (#rgb/#rrggbb/#rrggbbaa) or rgb()/rgba(). Returns null otherwise. */\nfunction parseColor(input: string): RGB | null {\n const s = input.trim();\n const hex = s.replace(/^#/, '');\n if (/^[0-9a-f]{3,4}$/i.test(hex)) {\n return {\n r: parseInt(hex[0]! + hex[0]!, 16),\n g: parseInt(hex[1]! + hex[1]!, 16),\n b: parseInt(hex[2]! + hex[2]!, 16),\n };\n }\n if (/^[0-9a-f]{6}$/i.test(hex) || /^[0-9a-f]{8}$/i.test(hex)) {\n return {\n r: parseInt(hex.slice(0, 2), 16),\n g: parseInt(hex.slice(2, 4), 16),\n b: parseInt(hex.slice(4, 6), 16),\n };\n }\n const m = s.match(/^rgba?\\(([^)]+)\\)$/i);\n if (m) {\n const p = m[1]!.split(/[,\\s/]+/).filter(Boolean).map(Number);\n if (p.length >= 3 && p.slice(0, 3).every((n) => !Number.isNaN(n))) {\n return { r: p[0]!, g: p[1]!, b: p[2]! };\n }\n }\n return null;\n}\n\nconst clamp = (n: number) => Math.max(0, Math.min(255, Math.round(n)));\n/** Mix a channel toward a target (255 = white, 0 = black) by amount 0..1. */\nconst mix = (c: RGB, target: number, amt: number): RGB => ({\n r: clamp(c.r + (target - c.r) * amt),\n g: clamp(c.g + (target - c.g) * amt),\n b: clamp(c.b + (target - c.b) * amt),\n});\nconst rgb = (c: RGB) => `rgb(${c.r}, ${c.g}, ${c.b})`;\n\n/** Derive the full set of depth variables from one base color. */\nfunction tintVars(color: string): CSSProperties {\n const base = parseColor(color);\n // Unparseable (named color, hsl, etc.) — let the raw value drive the\n // surface and keep the default overlays. Depth still reads via the shade.\n if (!base) {\n return {\n ['--royui-btn-top']: color,\n ['--royui-btn-bottom']: color,\n } as CSSProperties;\n }\n const lum = (0.2126 * base.r + 0.7152 * base.g + 0.0722 * base.b) / 255;\n const light = lum > 0.62;\n const shade = mix(base, 0, 0.6);\n return {\n ['--royui-btn-top']: rgb(mix(base, 255, 0.1)),\n ['--royui-btn-bottom']: rgb(mix(base, 0, 0.1)),\n // Light buttons need a darker edge to show on light backgrounds; dark\n // buttons need a lighter one. Either way the ring stays a hairline.\n ['--royui-btn-ring']: rgb(light ? mix(base, 0, 0.16) : mix(base, 255, 0.22)),\n ['--royui-btn-fg']: light ? '#0b0b0d' : '#ffffff',\n // Stronger top sheen on light surfaces (where a faint white is invisible),\n // an in-hue shade at the base so it never looks like grime.\n ['--royui-btn-highlight']: light\n ? 'rgba(255, 255, 255, 0.5)'\n : 'rgba(255, 255, 255, 0.15)',\n ['--royui-btn-shade']: `rgba(${shade.r}, ${shade.g}, ${shade.b}, 0.5)`,\n ['--royui-btn-focus']: light ? 'rgba(0, 0, 0, 0.55)' : 'rgba(255, 255, 255, 0.7)',\n // Press darkens less on light buttons so the dip doesn't look like a smudge.\n ['--royui-btn-active-bright']: light ? '0.93' : '0.86',\n } as CSSProperties;\n}\n\n/** Combine the forwarded ref with the child's own ref (asChild path). */\nfunction mergeRefs<T>(...refs: (Ref<T> | undefined)[]) {\n return (node: T) => {\n for (const ref of refs) {\n if (typeof ref === 'function') ref(node);\n else if (ref && typeof ref === 'object') {\n (ref as { current: T | null }).current = node;\n }\n }\n };\n}\n\nconst DefaultSpinner = () => (\n <span className=\"royui-btn__spinner\" aria-hidden=\"true\">\n <svg viewBox=\"0 0 24 24\" width=\"16\" height=\"16\" fill=\"none\">\n <circle\n cx=\"12\"\n cy=\"12\"\n r=\"9\"\n stroke=\"currentColor\"\n strokeOpacity=\"0.3\"\n strokeWidth=\"2.5\"\n />\n <path\n d=\"M21 12a9 9 0 0 0-9-9\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n />\n </svg>\n </span>\n);\n\nexport const Button = forwardRef<HTMLButtonElement, ButtonProps>(\n (\n {\n size = 'md',\n variant = 'primary',\n asChild = false,\n color,\n fullWidth = false,\n loading = false,\n loadingLabel,\n disabled,\n className = '',\n style,\n children,\n type = 'button',\n ...rest\n },\n ref,\n ) => {\n const classes = [\n 'royui-btn',\n `royui-btn--${size}`,\n `royui-btn--${variant}`,\n fullWidth ? 'royui-btn--full' : '',\n loading ? 'royui-btn--loading' : '',\n className,\n ]\n .filter(Boolean)\n .join(' ');\n\n // Derived vars first so an explicit `style` (or CSS-var override) still wins.\n const mergedStyle = color ? { ...tintVars(color), ...style } : style;\n const spinner = loadingLabel ?? <DefaultSpinner />;\n const isDisabled = disabled || loading;\n\n // Polymorphic path: paint the styles onto the child element (e.g. a link)\n // instead of a <button>, so native anchor semantics survive.\n if (asChild && isValidElement(children)) {\n const child = children as ReactElement<{\n className?: string;\n style?: CSSProperties;\n children?: ReactNode;\n }>;\n const childRef = (child as unknown as { ref?: Ref<unknown> }).ref;\n return cloneElement(\n child,\n {\n ...rest,\n className: [classes, child.props.className].filter(Boolean).join(' '),\n style: { ...mergedStyle, ...child.props.style },\n 'aria-busy': loading || undefined,\n 'aria-disabled': isDisabled || undefined,\n ref: mergeRefs(ref as Ref<unknown>, childRef),\n } as Record<string, unknown>,\n loading ? spinner : child.props.children,\n );\n }\n\n return (\n <button\n ref={ref}\n type={type}\n disabled={isDisabled}\n className={classes}\n style={mergedStyle}\n aria-busy={loading || undefined}\n {...rest}\n >\n {loading ? spinner : children}\n </button>\n );\n },\n);\n\nButton.displayName = 'Button';\n"]}
@@ -0,0 +1,4 @@
1
+ "use client";
2
+
3
+ //# sourceMappingURL=chunk-DAA5LXWQ.js.map
4
+ //# sourceMappingURL=chunk-DAA5LXWQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"chunk-DAA5LXWQ.js"}
@@ -1,7 +1,7 @@
1
1
  "use client";
2
- import { Table, TableHeader, TableRow, TableHead, TableBody, TableCell } from './chunk-XERZVDIT.js';
3
2
  import { TableSearch } from './chunk-KSHKVSNK.js';
4
3
  import { TimeRangePicker } from './chunk-QOSMU4DV.js';
4
+ import { Table, TableHeader, TableRow, TableHead, TableBody, TableCell } from './chunk-XERZVDIT.js';
5
5
  import { DateRangePicker, startOfDay, isBetween } from './chunk-SFENGB5N.js';
6
6
  import { Pagination } from './chunk-5CIBIH7R.js';
7
7
  import { useState, useRef, useEffect, useMemo, useCallback } from 'react';
@@ -844,5 +844,5 @@ function DataTable({
844
844
  }
845
845
 
846
846
  export { ColumnMenu, DataTable, downloadString, fromCsv, fromJson, toCsv, toJson };
847
- //# sourceMappingURL=chunk-PDUQROG2.js.map
848
- //# sourceMappingURL=chunk-PDUQROG2.js.map
847
+ //# sourceMappingURL=chunk-LV4JMUGV.js.map
848
+ //# sourceMappingURL=chunk-LV4JMUGV.js.map