@geekapps/silo-elements-nextjs 0.0.1 → 0.0.3

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.
Files changed (45) hide show
  1. package/dist/FileUploader.d.ts +7 -3
  2. package/dist/FileUploader.js +279 -38
  3. package/dist/FileUploader.js.map +1 -1
  4. package/dist/ImageUploader.d.ts +7 -3
  5. package/dist/ImageUploader.js +329 -66
  6. package/dist/ImageUploader.js.map +1 -1
  7. package/dist/MediaUploader.d.ts +7 -3
  8. package/dist/MediaUploader.js +654 -43
  9. package/dist/MediaUploader.js.map +1 -1
  10. package/dist/VideoPlayer.d.ts +20 -18
  11. package/dist/VideoPlayer.js +980 -675
  12. package/dist/VideoPlayer.js.map +1 -1
  13. package/dist/VideoUploader.d.ts +7 -3
  14. package/dist/VideoUploader.js +295 -43
  15. package/dist/VideoUploader.js.map +1 -1
  16. package/dist/components/DropZone.d.ts +8 -5
  17. package/dist/components/DropZone.js +134 -49
  18. package/dist/components/DropZone.js.map +1 -1
  19. package/dist/components/ProgressBar.d.ts +6 -4
  20. package/dist/components/ProgressBar.js +35 -15
  21. package/dist/components/ProgressBar.js.map +1 -1
  22. package/dist/index.d.ts +12 -12
  23. package/dist/index.js +1696 -10
  24. package/dist/index.js.map +1 -1
  25. package/dist/types.d.ts +12 -10
  26. package/dist/types.js +2 -1
  27. package/dist/types.js.map +1 -1
  28. package/dist/utils/format.d.ts +4 -3
  29. package/dist/utils/format.js +19 -26
  30. package/dist/utils/format.js.map +1 -1
  31. package/dist/utils/theme.d.ts +8 -5
  32. package/dist/utils/theme.js +34 -30
  33. package/dist/utils/theme.js.map +1 -1
  34. package/package.json +4 -3
  35. package/dist/FileUploader.d.ts.map +0 -1
  36. package/dist/ImageUploader.d.ts.map +0 -1
  37. package/dist/MediaUploader.d.ts.map +0 -1
  38. package/dist/VideoPlayer.d.ts.map +0 -1
  39. package/dist/VideoUploader.d.ts.map +0 -1
  40. package/dist/components/DropZone.d.ts.map +0 -1
  41. package/dist/components/ProgressBar.d.ts.map +0 -1
  42. package/dist/index.d.ts.map +0 -1
  43. package/dist/types.d.ts.map +0 -1
  44. package/dist/utils/format.d.ts.map +0 -1
  45. package/dist/utils/theme.d.ts.map +0 -1
@@ -1,47 +1,658 @@
1
- "use client";
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useState } from "react";
4
- import { ImageUploader } from "./ImageUploader.js";
5
- import { VideoUploader } from "./VideoUploader.js";
6
- import { FileUploader } from "./FileUploader.js";
7
- import { resolveTheme, themeToVars } from "./utils/theme.js";
8
- const TAB_LABELS = {
9
- image: { label: "Image", icon: "🖼️" },
10
- video: { label: "Video", icon: "🎬" },
11
- file: { label: "File", icon: "📎" },
1
+ import { useState, useCallback, useRef } from 'react';
2
+ import { useUpload } from '@geekapps/silo-nextjs';
3
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
4
+
5
+ // src/utils/theme.ts
6
+ var defaultTheme = {
7
+ borderColor: "#e2e8f0",
8
+ borderColorActive: "#6366f1",
9
+ backgroundColor: "#f8fafc",
10
+ backgroundColorHover: "#f1f5f9",
11
+ textColor: "#0f172a",
12
+ textColorMuted: "#64748b",
13
+ accentColor: "#6366f1",
14
+ accentColorHover: "#4f46e5",
15
+ errorColor: "#ef4444",
16
+ successColor: "#22c55e",
17
+ borderRadius: "12px",
18
+ fontFamily: "inherit"
12
19
  };
13
- export function MediaUploader({ tabs = ["image", "video", "file"], defaultTab, imageProps, videoProps, fileProps, className = "", style, theme, onUpload, onError, ...shared }) {
14
- const [activeTab, setActiveTab] = useState(defaultTab ?? tabs[0] ?? "image");
15
- const t = resolveTheme(theme);
16
- const vars = themeToVars(t);
17
- const containerStyle = {
18
- ...vars,
19
- display: "flex",
20
- flexDirection: "column",
21
- gap: "0",
22
- width: "100%",
23
- fontFamily: "var(--silo-font)",
24
- border: "1px solid var(--silo-border)",
25
- borderRadius: "var(--silo-radius)",
20
+ function resolveTheme(theme) {
21
+ return { ...defaultTheme, ...theme };
22
+ }
23
+ function themeToVars(theme) {
24
+ return {
25
+ "--silo-border": theme.borderColor,
26
+ "--silo-border-active": theme.borderColorActive,
27
+ "--silo-bg": theme.backgroundColor,
28
+ "--silo-bg-hover": theme.backgroundColorHover,
29
+ "--silo-text": theme.textColor,
30
+ "--silo-text-muted": theme.textColorMuted,
31
+ "--silo-accent": theme.accentColor,
32
+ "--silo-accent-hover": theme.accentColorHover,
33
+ "--silo-error": theme.errorColor,
34
+ "--silo-success": theme.successColor,
35
+ "--silo-radius": theme.borderRadius,
36
+ "--silo-font": theme.fontFamily
37
+ };
38
+ }
39
+ function DropZone({
40
+ accept,
41
+ multiple = false,
42
+ disabled = false,
43
+ maxSize,
44
+ onFiles,
45
+ onError,
46
+ className = "",
47
+ style,
48
+ theme,
49
+ children
50
+ }) {
51
+ const [dragging, setDragging] = useState(false);
52
+ const inputRef = useRef(null);
53
+ const t = resolveTheme(theme);
54
+ const vars = themeToVars(t);
55
+ const validate = useCallback(
56
+ (files) => {
57
+ return files.filter((f) => {
58
+ if (maxSize && f.size > maxSize) {
59
+ onError?.(new Error(`File "${f.name}" exceeds max size of ${maxSize} bytes`));
60
+ return false;
61
+ }
62
+ return true;
63
+ });
64
+ },
65
+ [maxSize, onError]
66
+ );
67
+ const handleDrop = useCallback(
68
+ (e) => {
69
+ e.preventDefault();
70
+ setDragging(false);
71
+ if (disabled) return;
72
+ const files = Array.from(e.dataTransfer.files);
73
+ const valid = validate(files);
74
+ if (valid.length) onFiles(valid);
75
+ },
76
+ [disabled, validate, onFiles]
77
+ );
78
+ const handleChange = useCallback(
79
+ (e) => {
80
+ const files = Array.from(e.target.files ?? []);
81
+ const valid = validate(files);
82
+ if (valid.length) onFiles(valid);
83
+ e.target.value = "";
84
+ },
85
+ [validate, onFiles]
86
+ );
87
+ const rootStyle = {
88
+ ...vars,
89
+ fontFamily: "var(--silo-font)",
90
+ border: `2px dashed ${dragging ? "var(--silo-border-active)" : "var(--silo-border)"}`,
91
+ borderRadius: "var(--silo-radius)",
92
+ backgroundColor: dragging ? "var(--silo-bg-hover)" : "var(--silo-bg)",
93
+ color: "var(--silo-text)",
94
+ cursor: disabled ? "not-allowed" : "pointer",
95
+ transition: "border-color 0.15s, background-color 0.15s",
96
+ opacity: disabled ? 0.5 : 1,
97
+ ...style
98
+ };
99
+ return /* @__PURE__ */ jsxs(
100
+ "div",
101
+ {
102
+ className: `silo-dropzone${className ? ` ${className}` : ""}`,
103
+ style: rootStyle,
104
+ onDragOver: (e) => {
105
+ e.preventDefault();
106
+ if (!disabled) setDragging(true);
107
+ },
108
+ onDragLeave: () => setDragging(false),
109
+ onDrop: handleDrop,
110
+ onClick: () => !disabled && inputRef.current?.click(),
111
+ role: "button",
112
+ tabIndex: disabled ? -1 : 0,
113
+ onKeyDown: (e) => {
114
+ if (e.key === "Enter" || e.key === " ") inputRef.current?.click();
115
+ },
116
+ "aria-label": "Upload area",
117
+ children: [
118
+ /* @__PURE__ */ jsx(
119
+ "input",
120
+ {
121
+ ref: inputRef,
122
+ type: "file",
123
+ accept,
124
+ multiple,
125
+ style: { display: "none" },
126
+ onChange: handleChange,
127
+ disabled
128
+ }
129
+ ),
130
+ children
131
+ ]
132
+ }
133
+ );
134
+ }
135
+ function ProgressBar({ progress, className = "", style }) {
136
+ return /* @__PURE__ */ jsx(
137
+ "div",
138
+ {
139
+ className: `silo-progress-track${className ? ` ${className}` : ""}`,
140
+ style: {
141
+ height: "6px",
142
+ borderRadius: "3px",
143
+ backgroundColor: "rgba(99,102,241,0.15)",
26
144
  overflow: "hidden",
27
- backgroundColor: "var(--silo-bg)",
28
- ...style,
29
- };
30
- return (_jsxs("div", { className: `silo-media-uploader${className ? ` ${className}` : ""}`, style: containerStyle, children: [_jsx("div", { style: { display: "flex", borderBottom: "1px solid var(--silo-border)" }, children: tabs.map((tab) => (_jsxs("button", { onClick: () => setActiveTab(tab), style: {
31
- flex: 1,
32
- padding: "10px 8px",
33
- background: "none",
34
- border: "none",
35
- borderBottom: `2px solid ${activeTab === tab ? "var(--silo-accent)" : "transparent"}`,
36
- cursor: "pointer",
37
- fontSize: "13px",
38
- fontWeight: activeTab === tab ? 600 : 400,
39
- color: activeTab === tab ? "var(--silo-accent)" : "var(--silo-text-muted)",
40
- transition: "color 0.15s, border-color 0.15s",
41
- display: "flex",
42
- alignItems: "center",
43
- justifyContent: "center",
44
- gap: "6px",
45
- }, children: [_jsx("span", { children: TAB_LABELS[tab].icon }), _jsx("span", { children: TAB_LABELS[tab].label })] }, tab))) }), _jsxs("div", { style: { padding: "16px" }, children: [activeTab === "image" && (_jsx(ImageUploader, { ...shared, ...imageProps, ...(theme !== undefined && { theme }), ...(onUpload !== undefined && { onUpload }), ...(onError !== undefined && { onError }) })), activeTab === "video" && (_jsx(VideoUploader, { ...shared, ...videoProps, ...(theme !== undefined && { theme }), ...(onUpload !== undefined && { onUpload }), ...(onError !== undefined && { onError }) })), activeTab === "file" && (_jsx(FileUploader, { ...shared, ...fileProps, ...(theme !== undefined && { theme }), ...(onUpload !== undefined && { onUpload }), ...(onError !== undefined && { onError }) }))] })] }));
145
+ ...style
146
+ },
147
+ role: "progressbar",
148
+ "aria-valuenow": progress,
149
+ "aria-valuemin": 0,
150
+ "aria-valuemax": 100,
151
+ children: /* @__PURE__ */ jsx(
152
+ "div",
153
+ {
154
+ className: "silo-progress-fill",
155
+ style: {
156
+ height: "100%",
157
+ width: `${progress}%`,
158
+ backgroundColor: "var(--silo-accent, #6366f1)",
159
+ borderRadius: "3px",
160
+ transition: "width 0.2s ease"
161
+ }
162
+ }
163
+ )
164
+ }
165
+ );
166
+ }
167
+
168
+ // src/utils/format.ts
169
+ function formatBytes(bytes) {
170
+ if (bytes < 1024) return `${bytes} B`;
171
+ if (bytes < 1024 ** 2) return `${(bytes / 1024).toFixed(1)} KB`;
172
+ if (bytes < 1024 ** 3) return `${(bytes / 1024 ** 2).toFixed(1)} MB`;
173
+ return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
174
+ }
175
+ function ImageUploader({
176
+ bucket,
177
+ expiresIn,
178
+ private: isPrivate = true,
179
+ onUpload,
180
+ onError,
181
+ className = "",
182
+ style,
183
+ disabled = false,
184
+ maxSize,
185
+ accept = "image/*",
186
+ showPreview = true,
187
+ theme,
188
+ renderIcon,
189
+ renderProgress,
190
+ renderSuccess,
191
+ renderError,
192
+ children
193
+ }) {
194
+ const uploadOpts = { private: isPrivate, ...bucket !== void 0 && { bucket }, ...expiresIn !== void 0 && { expiresIn } };
195
+ const { state, upload, reset } = useUpload(uploadOpts);
196
+ const [preview, setPreview] = useState(null);
197
+ const t = resolveTheme(theme);
198
+ const vars = themeToVars(t);
199
+ const handleFiles = useCallback(
200
+ async (files) => {
201
+ const file = files[0];
202
+ if (!file) return;
203
+ if (showPreview) {
204
+ const url = URL.createObjectURL(file);
205
+ setPreview(url);
206
+ }
207
+ try {
208
+ const result = await upload(file);
209
+ onUpload?.(result);
210
+ } catch (err) {
211
+ onError?.(err instanceof Error ? err : new Error(String(err)));
212
+ }
213
+ },
214
+ [upload, onUpload, onError, showPreview]
215
+ );
216
+ const containerStyle = {
217
+ ...vars,
218
+ display: "flex",
219
+ flexDirection: "column",
220
+ gap: "12px",
221
+ width: "100%",
222
+ fontFamily: "var(--silo-font)",
223
+ ...style
224
+ };
225
+ if (state.status === "error" && renderError) {
226
+ return /* @__PURE__ */ jsx("div", { style: containerStyle, children: renderError(state.error, reset) });
227
+ }
228
+ if (state.status === "done" && renderSuccess) {
229
+ return /* @__PURE__ */ jsx("div", { style: containerStyle, children: renderSuccess(state.result) });
230
+ }
231
+ return /* @__PURE__ */ jsxs("div", { className: `silo-image-uploader${className ? ` ${className}` : ""}`, style: containerStyle, children: [
232
+ /* @__PURE__ */ jsx(
233
+ DropZone,
234
+ {
235
+ ...accept !== void 0 && { accept },
236
+ ...maxSize !== void 0 && { maxSize },
237
+ ...onError !== void 0 && { onError },
238
+ ...theme !== void 0 && { theme },
239
+ disabled: disabled || state.status === "uploading",
240
+ onFiles: handleFiles,
241
+ style: { padding: "32px 24px", textAlign: "center" },
242
+ children: preview && state.status !== "uploading" ? /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: "8px" }, children: [
243
+ /* @__PURE__ */ jsx(
244
+ "img",
245
+ {
246
+ src: preview,
247
+ alt: "Preview",
248
+ style: {
249
+ maxWidth: "100%",
250
+ maxHeight: "200px",
251
+ borderRadius: "8px",
252
+ objectFit: "contain"
253
+ }
254
+ }
255
+ ),
256
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "12px", color: "var(--silo-text-muted)" }, children: "Click or drag to replace" })
257
+ ] }) : /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: "8px" }, children: [
258
+ renderIcon ? renderIcon() : /* @__PURE__ */ jsx("svg", { width: "40", height: "40", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", style: { color: "var(--silo-text-muted)", opacity: 0.6 }, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" }) }),
259
+ children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
260
+ /* @__PURE__ */ jsx("span", { style: { fontWeight: 600, color: "var(--silo-text)" }, children: "Drop image here" }),
261
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "13px", color: "var(--silo-text-muted)" }, children: "or click to browse" }),
262
+ maxSize && /* @__PURE__ */ jsxs("span", { style: { fontSize: "12px", color: "var(--silo-text-muted)" }, children: [
263
+ "Max ",
264
+ formatBytes(maxSize)
265
+ ] })
266
+ ] })
267
+ ] })
268
+ }
269
+ ),
270
+ state.status === "uploading" && /* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column", gap: "6px" }, children: renderProgress ? renderProgress(state.progress) : /* @__PURE__ */ jsxs(Fragment, { children: [
271
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", fontSize: "13px", color: "var(--silo-text-muted)" }, children: [
272
+ /* @__PURE__ */ jsx("span", { children: "Uploading\u2026" }),
273
+ /* @__PURE__ */ jsxs("span", { children: [
274
+ state.progress,
275
+ "%"
276
+ ] })
277
+ ] }),
278
+ /* @__PURE__ */ jsx(ProgressBar, { progress: state.progress })
279
+ ] }) }),
280
+ state.status === "done" && !renderSuccess && /* @__PURE__ */ jsxs("div", { style: {
281
+ display: "flex",
282
+ alignItems: "center",
283
+ gap: "8px",
284
+ padding: "10px 14px",
285
+ borderRadius: "8px",
286
+ backgroundColor: "rgba(34,197,94,0.1)",
287
+ color: "var(--silo-success, #22c55e)",
288
+ fontSize: "14px"
289
+ }, children: [
290
+ /* @__PURE__ */ jsx("svg", { width: "18", height: "18", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M5 13l4 4L19 7" }) }),
291
+ /* @__PURE__ */ jsx("span", { children: "Upload complete" }),
292
+ /* @__PURE__ */ jsx(
293
+ "button",
294
+ {
295
+ onClick: (e) => {
296
+ e.stopPropagation();
297
+ reset();
298
+ setPreview(null);
299
+ },
300
+ style: { marginLeft: "auto", background: "none", border: "none", cursor: "pointer", fontSize: "12px", color: "var(--silo-text-muted)" },
301
+ children: "Upload another"
302
+ }
303
+ )
304
+ ] }),
305
+ state.status === "error" && !renderError && /* @__PURE__ */ jsxs("div", { style: {
306
+ display: "flex",
307
+ alignItems: "center",
308
+ gap: "8px",
309
+ padding: "10px 14px",
310
+ borderRadius: "8px",
311
+ backgroundColor: "rgba(239,68,68,0.1)",
312
+ color: "var(--silo-error, #ef4444)",
313
+ fontSize: "14px"
314
+ }, children: [
315
+ /* @__PURE__ */ jsx("svg", { width: "18", height: "18", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" }) }),
316
+ /* @__PURE__ */ jsx("span", { children: state.error.message }),
317
+ /* @__PURE__ */ jsx(
318
+ "button",
319
+ {
320
+ onClick: (e) => {
321
+ e.stopPropagation();
322
+ reset();
323
+ },
324
+ style: { marginLeft: "auto", background: "none", border: "none", cursor: "pointer", fontSize: "12px", color: "var(--silo-text-muted)" },
325
+ children: "Retry"
326
+ }
327
+ )
328
+ ] })
329
+ ] });
330
+ }
331
+ function VideoUploader({
332
+ bucket,
333
+ expiresIn,
334
+ private: isPrivate = true,
335
+ onUpload,
336
+ onError,
337
+ className = "",
338
+ style,
339
+ disabled = false,
340
+ maxSize,
341
+ accept = "video/*",
342
+ showPreview = true,
343
+ theme,
344
+ renderIcon,
345
+ renderProgress,
346
+ renderSuccess,
347
+ renderError,
348
+ children
349
+ }) {
350
+ const uploadOpts = { private: isPrivate, ...bucket !== void 0 && { bucket }, ...expiresIn !== void 0 && { expiresIn } };
351
+ const { state, upload, reset } = useUpload(uploadOpts);
352
+ const [preview, setPreview] = useState(null);
353
+ const t = resolveTheme(theme);
354
+ const vars = themeToVars(t);
355
+ const handleFiles = useCallback(
356
+ async (files) => {
357
+ const file = files[0];
358
+ if (!file) return;
359
+ if (showPreview) {
360
+ const url = URL.createObjectURL(file);
361
+ setPreview(url);
362
+ }
363
+ try {
364
+ const result = await upload(file);
365
+ onUpload?.(result);
366
+ } catch (err) {
367
+ onError?.(err instanceof Error ? err : new Error(String(err)));
368
+ }
369
+ },
370
+ [upload, onUpload, onError, showPreview]
371
+ );
372
+ const containerStyle = {
373
+ ...vars,
374
+ display: "flex",
375
+ flexDirection: "column",
376
+ gap: "12px",
377
+ width: "100%",
378
+ fontFamily: "var(--silo-font)",
379
+ ...style
380
+ };
381
+ if (state.status === "error" && renderError) {
382
+ return /* @__PURE__ */ jsx("div", { style: containerStyle, children: renderError(state.error, reset) });
383
+ }
384
+ if (state.status === "done" && renderSuccess) {
385
+ return /* @__PURE__ */ jsx("div", { style: containerStyle, children: renderSuccess(state.result) });
386
+ }
387
+ return /* @__PURE__ */ jsxs("div", { className: `silo-video-uploader${className ? ` ${className}` : ""}`, style: containerStyle, children: [
388
+ /* @__PURE__ */ jsx(
389
+ DropZone,
390
+ {
391
+ ...accept !== void 0 && { accept },
392
+ ...maxSize !== void 0 && { maxSize },
393
+ ...onError !== void 0 && { onError },
394
+ ...theme !== void 0 && { theme },
395
+ disabled: disabled || state.status === "uploading",
396
+ onFiles: handleFiles,
397
+ style: { padding: "32px 24px", textAlign: "center" },
398
+ children: preview && state.status !== "uploading" ? /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: "8px" }, children: [
399
+ /* @__PURE__ */ jsx(
400
+ "video",
401
+ {
402
+ src: preview,
403
+ style: { maxWidth: "100%", maxHeight: "180px", borderRadius: "8px" },
404
+ controls: false,
405
+ muted: true,
406
+ playsInline: true
407
+ }
408
+ ),
409
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "12px", color: "var(--silo-text-muted)" }, children: "Click or drag to replace" })
410
+ ] }) : /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: "8px" }, children: [
411
+ renderIcon ? renderIcon() : /* @__PURE__ */ jsx("svg", { width: "40", height: "40", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", style: { color: "var(--silo-text-muted)", opacity: 0.6 }, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M15 10l4.553-2.069A1 1 0 0121 8.878v6.244a1 1 0 01-1.447.894L15 14M3 8a2 2 0 012-2h8a2 2 0 012 2v8a2 2 0 01-2 2H5a2 2 0 01-2-2V8z" }) }),
412
+ children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
413
+ /* @__PURE__ */ jsx("span", { style: { fontWeight: 600, color: "var(--silo-text)" }, children: "Drop video here" }),
414
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "13px", color: "var(--silo-text-muted)" }, children: "or click to browse" }),
415
+ /* @__PURE__ */ jsxs("span", { style: { fontSize: "12px", color: "var(--silo-text-muted)" }, children: [
416
+ "MP4, MOV, MKV, WebM",
417
+ maxSize ? ` \xB7 Max ${formatBytes(maxSize)}` : ""
418
+ ] })
419
+ ] })
420
+ ] })
421
+ }
422
+ ),
423
+ state.status === "uploading" && /* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column", gap: "6px" }, children: renderProgress ? renderProgress(state.progress) : /* @__PURE__ */ jsxs(Fragment, { children: [
424
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", fontSize: "13px", color: "var(--silo-text-muted)" }, children: [
425
+ /* @__PURE__ */ jsx("span", { children: "Uploading video\u2026" }),
426
+ /* @__PURE__ */ jsxs("span", { children: [
427
+ state.progress,
428
+ "%"
429
+ ] })
430
+ ] }),
431
+ /* @__PURE__ */ jsx(ProgressBar, { progress: state.progress }),
432
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "12px", color: "var(--silo-text-muted)" }, children: "Processing will start after upload completes" })
433
+ ] }) }),
434
+ state.status === "done" && !renderSuccess && /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "8px", padding: "10px 14px", borderRadius: "8px", backgroundColor: "rgba(34,197,94,0.1)", color: "var(--silo-success, #22c55e)", fontSize: "14px" }, children: [
435
+ /* @__PURE__ */ jsx("svg", { width: "18", height: "18", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M5 13l4 4L19 7" }) }),
436
+ /* @__PURE__ */ jsx("span", { children: "Video uploaded \u2014 processing in background" }),
437
+ /* @__PURE__ */ jsx("button", { onClick: (e) => {
438
+ e.stopPropagation();
439
+ reset();
440
+ setPreview(null);
441
+ }, style: { marginLeft: "auto", background: "none", border: "none", cursor: "pointer", fontSize: "12px", color: "var(--silo-text-muted)" }, children: "Upload another" })
442
+ ] }),
443
+ state.status === "error" && !renderError && /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "8px", padding: "10px 14px", borderRadius: "8px", backgroundColor: "rgba(239,68,68,0.1)", color: "var(--silo-error, #ef4444)", fontSize: "14px" }, children: [
444
+ /* @__PURE__ */ jsx("svg", { width: "18", height: "18", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" }) }),
445
+ /* @__PURE__ */ jsx("span", { children: state.error.message }),
446
+ /* @__PURE__ */ jsx("button", { onClick: (e) => {
447
+ e.stopPropagation();
448
+ reset();
449
+ }, style: { marginLeft: "auto", background: "none", border: "none", cursor: "pointer", fontSize: "12px", color: "var(--silo-text-muted)" }, children: "Retry" })
450
+ ] })
451
+ ] });
452
+ }
453
+ function FileUploader({
454
+ bucket,
455
+ expiresIn,
456
+ private: isPrivate = true,
457
+ onUpload,
458
+ onError,
459
+ className = "",
460
+ style,
461
+ disabled = false,
462
+ maxSize,
463
+ accept,
464
+ multiple = false,
465
+ theme,
466
+ renderIcon,
467
+ renderProgress,
468
+ renderSuccess,
469
+ renderError,
470
+ children
471
+ }) {
472
+ const uploadOpts = { private: isPrivate, ...bucket !== void 0 && { bucket }, ...expiresIn !== void 0 && { expiresIn } };
473
+ const { state, upload, reset } = useUpload(uploadOpts);
474
+ const t = resolveTheme(theme);
475
+ const vars = themeToVars(t);
476
+ const handleFiles = useCallback(
477
+ async (files) => {
478
+ const file = files[0];
479
+ if (!file) return;
480
+ try {
481
+ const result = await upload(file);
482
+ onUpload?.(result);
483
+ } catch (err) {
484
+ onError?.(err instanceof Error ? err : new Error(String(err)));
485
+ }
486
+ },
487
+ [upload, onUpload, onError]
488
+ );
489
+ const containerStyle = {
490
+ ...vars,
491
+ display: "flex",
492
+ flexDirection: "column",
493
+ gap: "12px",
494
+ width: "100%",
495
+ fontFamily: "var(--silo-font)",
496
+ ...style
497
+ };
498
+ if (state.status === "error" && renderError) {
499
+ return /* @__PURE__ */ jsx("div", { style: containerStyle, children: renderError(state.error, reset) });
500
+ }
501
+ if (state.status === "done" && renderSuccess) {
502
+ return /* @__PURE__ */ jsx("div", { style: containerStyle, children: renderSuccess(state.result) });
503
+ }
504
+ return /* @__PURE__ */ jsxs("div", { className: `silo-file-uploader${className ? ` ${className}` : ""}`, style: containerStyle, children: [
505
+ /* @__PURE__ */ jsx(
506
+ DropZone,
507
+ {
508
+ ...accept !== void 0 && { accept },
509
+ ...maxSize !== void 0 && { maxSize },
510
+ ...onError !== void 0 && { onError },
511
+ ...theme !== void 0 && { theme },
512
+ multiple,
513
+ disabled: disabled || state.status === "uploading",
514
+ onFiles: handleFiles,
515
+ style: { padding: "28px 24px", textAlign: "center" },
516
+ children: /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: "8px" }, children: [
517
+ renderIcon ? renderIcon() : /* @__PURE__ */ jsx("svg", { width: "40", height: "40", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", style: { color: "var(--silo-text-muted)", opacity: 0.6 }, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M9 13h6m-3-3v6m5 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" }) }),
518
+ children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
519
+ /* @__PURE__ */ jsx("span", { style: { fontWeight: 600, color: "var(--silo-text)" }, children: multiple ? "Drop files here" : "Drop a file here" }),
520
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "13px", color: "var(--silo-text-muted)" }, children: "or click to browse" }),
521
+ maxSize && /* @__PURE__ */ jsxs("span", { style: { fontSize: "12px", color: "var(--silo-text-muted)" }, children: [
522
+ "Max ",
523
+ formatBytes(maxSize)
524
+ ] })
525
+ ] })
526
+ ] })
527
+ }
528
+ ),
529
+ state.status === "uploading" && /* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column", gap: "6px" }, children: renderProgress ? renderProgress(state.progress) : /* @__PURE__ */ jsxs(Fragment, { children: [
530
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", fontSize: "13px", color: "var(--silo-text-muted)" }, children: [
531
+ /* @__PURE__ */ jsx("span", { children: "Uploading\u2026" }),
532
+ /* @__PURE__ */ jsxs("span", { children: [
533
+ state.progress,
534
+ "%"
535
+ ] })
536
+ ] }),
537
+ /* @__PURE__ */ jsx(ProgressBar, { progress: state.progress })
538
+ ] }) }),
539
+ state.status === "done" && !renderSuccess && /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "10px", padding: "10px 14px", borderRadius: "8px", backgroundColor: "rgba(34,197,94,0.08)", border: "1px solid rgba(34,197,94,0.2)", fontSize: "14px" }, children: [
540
+ /* @__PURE__ */ jsx("svg", { width: "18", height: "18", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", style: { color: "#22c55e", flexShrink: 0 }, children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M5 13l4 4L19 7" }) }),
541
+ /* @__PURE__ */ jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [
542
+ /* @__PURE__ */ jsx("div", { style: { fontWeight: 500, color: "var(--silo-text)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: state.result.key }),
543
+ /* @__PURE__ */ jsx("div", { style: { fontSize: "12px", color: "var(--silo-text-muted)" }, children: formatBytes(state.result.size) })
544
+ ] }),
545
+ /* @__PURE__ */ jsx("button", { onClick: (e) => {
546
+ e.stopPropagation();
547
+ reset();
548
+ }, style: { background: "none", border: "none", cursor: "pointer", fontSize: "12px", color: "var(--silo-text-muted)", flexShrink: 0 }, children: "Replace" })
549
+ ] }),
550
+ state.status === "error" && !renderError && /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "8px", padding: "10px 14px", borderRadius: "8px", backgroundColor: "rgba(239,68,68,0.1)", color: "var(--silo-error, #ef4444)", fontSize: "14px" }, children: [
551
+ /* @__PURE__ */ jsx("span", { children: state.error.message }),
552
+ /* @__PURE__ */ jsx("button", { onClick: (e) => {
553
+ e.stopPropagation();
554
+ reset();
555
+ }, style: { marginLeft: "auto", background: "none", border: "none", cursor: "pointer", fontSize: "12px" }, children: "Retry" })
556
+ ] })
557
+ ] });
558
+ }
559
+ var TAB_LABELS = {
560
+ image: { label: "Image", icon: "\u{1F5BC}\uFE0F" },
561
+ video: { label: "Video", icon: "\u{1F3AC}" },
562
+ file: { label: "File", icon: "\u{1F4CE}" }
563
+ };
564
+ function MediaUploader({
565
+ tabs = ["image", "video", "file"],
566
+ defaultTab,
567
+ imageProps,
568
+ videoProps,
569
+ fileProps,
570
+ className = "",
571
+ style,
572
+ theme,
573
+ onUpload,
574
+ onError,
575
+ ...shared
576
+ }) {
577
+ const [activeTab, setActiveTab] = useState(defaultTab ?? tabs[0] ?? "image");
578
+ const t = resolveTheme(theme);
579
+ const vars = themeToVars(t);
580
+ const containerStyle = {
581
+ ...vars,
582
+ display: "flex",
583
+ flexDirection: "column",
584
+ gap: "0",
585
+ width: "100%",
586
+ fontFamily: "var(--silo-font)",
587
+ border: "1px solid var(--silo-border)",
588
+ borderRadius: "var(--silo-radius)",
589
+ overflow: "hidden",
590
+ backgroundColor: "var(--silo-bg)",
591
+ ...style
592
+ };
593
+ return /* @__PURE__ */ jsxs("div", { className: `silo-media-uploader${className ? ` ${className}` : ""}`, style: containerStyle, children: [
594
+ /* @__PURE__ */ jsx("div", { style: { display: "flex", borderBottom: "1px solid var(--silo-border)" }, children: tabs.map((tab) => /* @__PURE__ */ jsxs(
595
+ "button",
596
+ {
597
+ onClick: () => setActiveTab(tab),
598
+ style: {
599
+ flex: 1,
600
+ padding: "10px 8px",
601
+ background: "none",
602
+ border: "none",
603
+ borderBottom: `2px solid ${activeTab === tab ? "var(--silo-accent)" : "transparent"}`,
604
+ cursor: "pointer",
605
+ fontSize: "13px",
606
+ fontWeight: activeTab === tab ? 600 : 400,
607
+ color: activeTab === tab ? "var(--silo-accent)" : "var(--silo-text-muted)",
608
+ transition: "color 0.15s, border-color 0.15s",
609
+ display: "flex",
610
+ alignItems: "center",
611
+ justifyContent: "center",
612
+ gap: "6px"
613
+ },
614
+ children: [
615
+ /* @__PURE__ */ jsx("span", { children: TAB_LABELS[tab].icon }),
616
+ /* @__PURE__ */ jsx("span", { children: TAB_LABELS[tab].label })
617
+ ]
618
+ },
619
+ tab
620
+ )) }),
621
+ /* @__PURE__ */ jsxs("div", { style: { padding: "16px" }, children: [
622
+ activeTab === "image" && /* @__PURE__ */ jsx(
623
+ ImageUploader,
624
+ {
625
+ ...shared,
626
+ ...imageProps,
627
+ ...theme !== void 0 && { theme },
628
+ ...onUpload !== void 0 && { onUpload },
629
+ ...onError !== void 0 && { onError }
630
+ }
631
+ ),
632
+ activeTab === "video" && /* @__PURE__ */ jsx(
633
+ VideoUploader,
634
+ {
635
+ ...shared,
636
+ ...videoProps,
637
+ ...theme !== void 0 && { theme },
638
+ ...onUpload !== void 0 && { onUpload },
639
+ ...onError !== void 0 && { onError }
640
+ }
641
+ ),
642
+ activeTab === "file" && /* @__PURE__ */ jsx(
643
+ FileUploader,
644
+ {
645
+ ...shared,
646
+ ...fileProps,
647
+ ...theme !== void 0 && { theme },
648
+ ...onUpload !== void 0 && { onUpload },
649
+ ...onError !== void 0 && { onError }
650
+ }
651
+ )
652
+ ] })
653
+ ] });
46
654
  }
655
+
656
+ export { MediaUploader };
657
+ //# sourceMappingURL=MediaUploader.js.map
47
658
  //# sourceMappingURL=MediaUploader.js.map