@geekapps/silo-elements-nextjs 0.0.8 → 0.0.9

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.
@@ -1,5 +1,5 @@
1
1
  import { useState, useCallback, useRef } from 'react';
2
- import { useUpload } from '@geekapps/silo-nextjs';
2
+ import { useMultipartUpload, useBatchUpload } from '@geekapps/silo-nextjs';
3
3
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
4
4
 
5
5
  // src/utils/theme.ts
@@ -172,10 +172,21 @@ function formatBytes(bytes) {
172
172
  if (bytes < 1024 ** 3) return `${(bytes / 1024 ** 2).toFixed(1)} MB`;
173
173
  return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
174
174
  }
175
+ function getFileIcon(mimeType) {
176
+ if (mimeType.startsWith("image/")) return "\u{1F5BC}\uFE0F";
177
+ if (mimeType.startsWith("video/")) return "\u{1F3AC}";
178
+ if (mimeType.startsWith("audio/")) return "\u{1F3B5}";
179
+ if (mimeType === "application/pdf") return "\u{1F4C4}";
180
+ if (mimeType.includes("spreadsheet") || mimeType.includes("excel")) return "\u{1F4CA}";
181
+ if (mimeType.includes("presentation") || mimeType.includes("powerpoint")) return "\u{1F4D1}";
182
+ if (mimeType.includes("word") || mimeType.includes("document")) return "\u{1F4DD}";
183
+ if (mimeType.includes("zip") || mimeType.includes("tar") || mimeType.includes("gzip")) return "\u{1F4E6}";
184
+ return "\u{1F4CE}";
185
+ }
175
186
  function ImageUploader({
176
187
  bucket,
177
188
  expiresIn,
178
- private: isPrivate = true,
189
+ visibility = "private",
179
190
  onUpload,
180
191
  onError,
181
192
  className = "",
@@ -184,6 +195,7 @@ function ImageUploader({
184
195
  maxSize,
185
196
  accept = "image/*",
186
197
  showPreview = true,
198
+ image,
187
199
  theme,
188
200
  renderIcon,
189
201
  renderProgress,
@@ -191,28 +203,25 @@ function ImageUploader({
191
203
  renderError,
192
204
  children
193
205
  }) {
194
- const uploadOpts = { private: isPrivate, ...bucket !== void 0 && { bucket }, ...expiresIn !== void 0 && { expiresIn } };
195
- const { state, upload, reset } = useUpload(uploadOpts);
206
+ const { state, upload, pause, resume, abort, reset } = useMultipartUpload(bucket);
196
207
  const [preview, setPreview] = useState(null);
197
208
  const t = resolveTheme(theme);
198
209
  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
- );
210
+ const handleFiles = useCallback(async (files) => {
211
+ const file = files[0];
212
+ if (!file) return;
213
+ if (showPreview) setPreview(URL.createObjectURL(file));
214
+ try {
215
+ const result = await upload(file, {
216
+ ...bucket !== void 0 && { bucket },
217
+ visibility,
218
+ ...image && { image }
219
+ });
220
+ if (result) onUpload?.(result);
221
+ } catch (err) {
222
+ onError?.(err instanceof Error ? err : new Error(String(err)));
223
+ }
224
+ }, [upload, bucket, visibility, image, onUpload, onError, showPreview]);
216
225
  const containerStyle = {
217
226
  ...vars,
218
227
  display: "flex",
@@ -222,12 +231,11 @@ function ImageUploader({
222
231
  fontFamily: "var(--silo-font)",
223
232
  ...style
224
233
  };
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
- }
234
+ const isUploading = state.status === "uploading" || state.status === "preparing" || state.status === "completing";
235
+ const progress = state.status === "uploading" ? state.progress : state.status === "completing" ? 99 : 0;
236
+ const isPaused = state.status === "idle" && preview !== null;
237
+ if (state.status === "error" && renderError) return /* @__PURE__ */ jsx("div", { style: containerStyle, children: renderError(state.error, reset) });
238
+ if (state.status === "done" && renderSuccess) return /* @__PURE__ */ jsx("div", { style: containerStyle, children: renderSuccess(state.result) });
231
239
  return /* @__PURE__ */ jsxs("div", { className: `silo-image-uploader${className ? ` ${className}` : ""}`, style: containerStyle, children: [
232
240
  /* @__PURE__ */ jsx(
233
241
  DropZone,
@@ -236,102 +244,69 @@ function ImageUploader({
236
244
  ...maxSize !== void 0 && { maxSize },
237
245
  ...onError !== void 0 && { onError },
238
246
  ...theme !== void 0 && { theme },
239
- disabled: disabled || state.status === "uploading",
247
+ disabled: disabled || isUploading,
240
248
  onFiles: handleFiles,
241
249
  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" })
250
+ children: preview && !isUploading ? /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: "8px" }, children: [
251
+ /* @__PURE__ */ jsx("img", { src: preview, alt: "Preview", style: { maxWidth: "100%", maxHeight: "200px", borderRadius: "8px", objectFit: "contain" } }),
252
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "12px", color: "var(--silo-text-muted)" }, children: "Clique ou arraste para substituir" })
257
253
  ] }) : /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: "8px" }, children: [
258
254
  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
255
  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" }),
256
+ /* @__PURE__ */ jsx("span", { style: { fontWeight: 600, color: "var(--silo-text)" }, children: "Arraste a imagem aqui" }),
257
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "13px", color: "var(--silo-text-muted)" }, children: "ou clique para selecionar" }),
262
258
  maxSize && /* @__PURE__ */ jsxs("span", { style: { fontSize: "12px", color: "var(--silo-text-muted)" }, children: [
263
- "Max ",
259
+ "M\xE1x ",
264
260
  formatBytes(maxSize)
265
261
  ] })
266
262
  ] })
267
263
  ] })
268
264
  }
269
265
  ),
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
- "%"
266
+ isUploading && /* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column", gap: "6px" }, children: renderProgress ? renderProgress(progress) : /* @__PURE__ */ jsxs(Fragment, { children: [
267
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", fontSize: "13px", color: "var(--silo-text-muted)" }, children: [
268
+ /* @__PURE__ */ jsx("span", { children: state.status === "completing" ? "Finalizando\u2026" : state.status === "preparing" ? "Preparando\u2026" : `Parte ${state.part} de ${state.totalParts}` }),
269
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "8px", alignItems: "center" }, children: [
270
+ /* @__PURE__ */ jsxs("span", { children: [
271
+ progress,
272
+ "%"
273
+ ] }),
274
+ /* @__PURE__ */ jsx("button", { onClick: pause, style: { background: "none", border: "none", cursor: "pointer", fontSize: "12px", color: "var(--silo-text-muted)" }, children: "Pausar" })
276
275
  ] })
277
276
  ] }),
278
- /* @__PURE__ */ jsx(ProgressBar, { progress: state.progress })
277
+ /* @__PURE__ */ jsx(ProgressBar, { progress })
279
278
  ] }) }),
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
- )
279
+ isPaused && /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "8px" }, children: [
280
+ /* @__PURE__ */ jsx("button", { onClick: () => resume({ ...bucket !== void 0 && { bucket }, visibility, ...image && { image } }), style: { flex: 1, padding: "8px", borderRadius: "6px", border: "none", backgroundColor: "#10b981", color: "#fff", fontSize: "13px", fontWeight: 600, cursor: "pointer" }, children: "Retomar upload" }),
281
+ /* @__PURE__ */ jsx("button", { onClick: () => {
282
+ abort();
283
+ setPreview(null);
284
+ }, style: { padding: "8px 12px", borderRadius: "6px", border: "1px solid #e2e8f0", background: "#f8fafc", fontSize: "13px", cursor: "pointer", color: "var(--silo-text-muted)" }, children: "Cancelar" })
285
+ ] }),
286
+ 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: [
287
+ /* @__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" }) }),
288
+ /* @__PURE__ */ jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [
289
+ /* @__PURE__ */ jsx("div", { style: { fontWeight: 500, color: "var(--silo-text)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: state.result.key }),
290
+ /* @__PURE__ */ jsx("div", { style: { fontSize: "12px", color: "var(--silo-text-muted)" }, children: formatBytes(state.result.size) })
291
+ ] }),
292
+ /* @__PURE__ */ jsx("button", { onClick: () => {
293
+ reset();
294
+ setPreview(null);
295
+ }, style: { background: "none", border: "none", cursor: "pointer", fontSize: "12px", color: "var(--silo-text-muted)", flexShrink: 0 }, children: "Enviar outro" })
304
296
  ] }),
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" }) }),
297
+ 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: [
316
298
  /* @__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
- )
299
+ /* @__PURE__ */ jsx("button", { onClick: () => {
300
+ reset();
301
+ setPreview(null);
302
+ }, style: { marginLeft: "auto", background: "none", border: "none", cursor: "pointer", fontSize: "12px" }, children: "Tentar novamente" })
328
303
  ] })
329
304
  ] });
330
305
  }
331
306
  function VideoUploader({
332
307
  bucket,
333
308
  expiresIn,
334
- private: isPrivate = true,
309
+ visibility = "private",
335
310
  onUpload,
336
311
  onError,
337
312
  className = "",
@@ -340,6 +315,7 @@ function VideoUploader({
340
315
  maxSize,
341
316
  accept = "video/*",
342
317
  showPreview = true,
318
+ video,
343
319
  theme,
344
320
  renderIcon,
345
321
  renderProgress,
@@ -347,28 +323,25 @@ function VideoUploader({
347
323
  renderError,
348
324
  children
349
325
  }) {
350
- const uploadOpts = { private: isPrivate, ...bucket !== void 0 && { bucket }, ...expiresIn !== void 0 && { expiresIn } };
351
- const { state, upload, reset } = useUpload(uploadOpts);
326
+ const { state, upload, pause, resume, abort, reset } = useMultipartUpload(bucket);
352
327
  const [preview, setPreview] = useState(null);
353
328
  const t = resolveTheme(theme);
354
329
  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
- );
330
+ const handleFiles = useCallback(async (files) => {
331
+ const file = files[0];
332
+ if (!file) return;
333
+ if (showPreview) setPreview(URL.createObjectURL(file));
334
+ try {
335
+ const result = await upload(file, {
336
+ ...bucket !== void 0 && { bucket },
337
+ visibility,
338
+ ...video && { video }
339
+ });
340
+ if (result) onUpload?.(result);
341
+ } catch (err) {
342
+ onError?.(err instanceof Error ? err : new Error(String(err)));
343
+ }
344
+ }, [upload, bucket, visibility, video, onUpload, onError, showPreview]);
372
345
  const containerStyle = {
373
346
  ...vars,
374
347
  display: "flex",
@@ -378,12 +351,11 @@ function VideoUploader({
378
351
  fontFamily: "var(--silo-font)",
379
352
  ...style
380
353
  };
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
- }
354
+ const isUploading = state.status === "uploading" || state.status === "preparing" || state.status === "completing";
355
+ const progress = state.status === "uploading" ? state.progress : state.status === "completing" ? 99 : 0;
356
+ const isPaused = state.status === "idle" && preview !== null;
357
+ if (state.status === "error" && renderError) return /* @__PURE__ */ jsx("div", { style: containerStyle, children: renderError(state.error, reset) });
358
+ if (state.status === "done" && renderSuccess) return /* @__PURE__ */ jsx("div", { style: containerStyle, children: renderSuccess(state.result) });
387
359
  return /* @__PURE__ */ jsxs("div", { className: `silo-video-uploader${className ? ` ${className}` : ""}`, style: containerStyle, children: [
388
360
  /* @__PURE__ */ jsx(
389
361
  DropZone,
@@ -392,69 +364,68 @@ function VideoUploader({
392
364
  ...maxSize !== void 0 && { maxSize },
393
365
  ...onError !== void 0 && { onError },
394
366
  ...theme !== void 0 && { theme },
395
- disabled: disabled || state.status === "uploading",
367
+ disabled: disabled || isUploading,
396
368
  onFiles: handleFiles,
397
369
  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" })
370
+ children: preview && !isUploading ? /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: "8px" }, children: [
371
+ /* @__PURE__ */ jsx("video", { src: preview, style: { maxWidth: "100%", maxHeight: "180px", borderRadius: "8px" }, controls: false, muted: true, playsInline: true }),
372
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "12px", color: "var(--silo-text-muted)" }, children: "Clique ou arraste para substituir" })
410
373
  ] }) : /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: "8px" }, children: [
411
374
  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
375
  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" }),
376
+ /* @__PURE__ */ jsx("span", { style: { fontWeight: 600, color: "var(--silo-text)" }, children: "Arraste o v\xEDdeo aqui" }),
377
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "13px", color: "var(--silo-text-muted)" }, children: "ou clique para selecionar" }),
415
378
  /* @__PURE__ */ jsxs("span", { style: { fontSize: "12px", color: "var(--silo-text-muted)" }, children: [
416
379
  "MP4, MOV, MKV, WebM",
417
- maxSize ? ` \xB7 Max ${formatBytes(maxSize)}` : ""
380
+ maxSize ? ` \xB7 M\xE1x ${formatBytes(maxSize)}` : ""
418
381
  ] })
419
382
  ] })
420
383
  ] })
421
384
  }
422
385
  ),
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
- "%"
386
+ isUploading && /* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column", gap: "6px" }, children: renderProgress ? renderProgress(progress) : /* @__PURE__ */ jsxs(Fragment, { children: [
387
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", fontSize: "13px", color: "var(--silo-text-muted)" }, children: [
388
+ /* @__PURE__ */ jsx("span", { children: state.status === "completing" ? "Finalizando\u2026" : state.status === "preparing" ? "Preparando\u2026" : `Parte ${state.part} de ${state.totalParts}` }),
389
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "8px", alignItems: "center" }, children: [
390
+ /* @__PURE__ */ jsxs("span", { children: [
391
+ progress,
392
+ "%"
393
+ ] }),
394
+ /* @__PURE__ */ jsx("button", { onClick: pause, style: { background: "none", border: "none", cursor: "pointer", fontSize: "12px", color: "var(--silo-text-muted)" }, children: "Pausar" })
429
395
  ] })
430
396
  ] }),
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" })
397
+ /* @__PURE__ */ jsx(ProgressBar, { progress }),
398
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "12px", color: "var(--silo-text-muted)" }, children: "O processamento inicia ap\xF3s o upload" })
433
399
  ] }) }),
400
+ isPaused && /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "8px" }, children: [
401
+ /* @__PURE__ */ jsx("button", { onClick: () => resume({ ...bucket !== void 0 && { bucket }, visibility, ...video && { video } }), style: { flex: 1, padding: "8px", borderRadius: "6px", border: "none", backgroundColor: "#10b981", color: "#fff", fontSize: "13px", fontWeight: 600, cursor: "pointer" }, children: "Retomar upload" }),
402
+ /* @__PURE__ */ jsx("button", { onClick: () => {
403
+ abort();
404
+ setPreview(null);
405
+ }, style: { padding: "8px 12px", borderRadius: "6px", border: "1px solid #e2e8f0", background: "#f8fafc", fontSize: "13px", cursor: "pointer", color: "var(--silo-text-muted)" }, children: "Cancelar" })
406
+ ] }),
434
407
  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
408
  /* @__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();
409
+ /* @__PURE__ */ jsx("span", { children: "V\xEDdeo enviado \u2014 processando em segundo plano" }),
410
+ /* @__PURE__ */ jsx("button", { onClick: () => {
439
411
  reset();
440
412
  setPreview(null);
441
- }, style: { marginLeft: "auto", background: "none", border: "none", cursor: "pointer", fontSize: "12px", color: "var(--silo-text-muted)" }, children: "Upload another" })
413
+ }, style: { marginLeft: "auto", background: "none", border: "none", cursor: "pointer", fontSize: "12px", color: "var(--silo-text-muted)" }, children: "Enviar outro" })
442
414
  ] }),
443
415
  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
416
  /* @__PURE__ */ jsx("span", { children: state.error.message }),
446
- /* @__PURE__ */ jsx("button", { onClick: (e) => {
447
- e.stopPropagation();
417
+ /* @__PURE__ */ jsx("button", { onClick: () => {
448
418
  reset();
449
- }, style: { marginLeft: "auto", background: "none", border: "none", cursor: "pointer", fontSize: "12px", color: "var(--silo-text-muted)" }, children: "Retry" })
419
+ setPreview(null);
420
+ }, style: { marginLeft: "auto", background: "none", border: "none", cursor: "pointer", fontSize: "12px" }, children: "Tentar novamente" })
450
421
  ] })
451
422
  ] });
452
423
  }
453
424
  function FileUploader({
454
425
  bucket,
455
- expiresIn,
456
- private: isPrivate = true,
426
+ visibility = "private",
457
427
  onUpload,
428
+ onBatchUpload,
458
429
  onError,
459
430
  className = "",
460
431
  style,
@@ -462,6 +433,8 @@ function FileUploader({
462
433
  maxSize,
463
434
  accept,
464
435
  multiple = false,
436
+ image,
437
+ video,
465
438
  theme,
466
439
  renderIcon,
467
440
  renderProgress,
@@ -469,23 +442,8 @@ function FileUploader({
469
442
  renderError,
470
443
  children
471
444
  }) {
472
- const uploadOpts = { private: isPrivate, ...bucket !== void 0 && { bucket }, ...expiresIn !== void 0 && { expiresIn } };
473
- const { state, upload, reset } = useUpload(uploadOpts);
474
445
  const t = resolveTheme(theme);
475
446
  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
447
  const containerStyle = {
490
448
  ...vars,
491
449
  display: "flex",
@@ -495,14 +453,39 @@ function FileUploader({
495
453
  fontFamily: "var(--silo-font)",
496
454
  ...style
497
455
  };
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
- }
456
+ const single = useMultipartUpload(bucket);
457
+ const batch = useBatchUpload();
458
+ const handleFiles = useCallback(async (files) => {
459
+ if (multiple && files.length > 1) {
460
+ try {
461
+ const opts = { ...bucket !== void 0 && { bucket }, visibility, ...image && { image }, ...video && { video } };
462
+ const results = await batch.upload(files, opts);
463
+ onBatchUpload?.(results);
464
+ results.forEach((r) => onUpload?.(r));
465
+ } catch (err) {
466
+ onError?.(err instanceof Error ? err : new Error(String(err)));
467
+ }
468
+ } else {
469
+ const file = files[0];
470
+ if (!file) return;
471
+ try {
472
+ const opts = { ...bucket !== void 0 && { bucket }, visibility, ...image && { image }, ...video && { video } };
473
+ const result = await single.upload(file, opts);
474
+ if (result) onUpload?.(result);
475
+ } catch (err) {
476
+ onError?.(err instanceof Error ? err : new Error(String(err)));
477
+ }
478
+ }
479
+ }, [single, batch, multiple, bucket, visibility, image, video, onUpload, onBatchUpload, onError]);
480
+ const isBatch = batch.state.files.length > 0;
481
+ const isSingleUploading = single.state.status === "uploading" || single.state.status === "preparing" || single.state.status === "completing";
482
+ const isBatchUploading = batch.state.status === "uploading" || batch.state.status === "preparing";
483
+ const isUploading = isSingleUploading || isBatchUploading;
484
+ const singleProgress = single.state.status === "uploading" ? single.state.progress : single.state.status === "completing" ? 99 : 0;
485
+ if (single.state.status === "error" && renderError) return /* @__PURE__ */ jsx("div", { style: containerStyle, children: renderError(single.state.error, single.reset) });
486
+ if (single.state.status === "done" && renderSuccess) return /* @__PURE__ */ jsx("div", { style: containerStyle, children: renderSuccess(single.state.result) });
504
487
  return /* @__PURE__ */ jsxs("div", { className: `silo-file-uploader${className ? ` ${className}` : ""}`, style: containerStyle, children: [
505
- /* @__PURE__ */ jsx(
488
+ !isBatch && /* @__PURE__ */ jsx(
506
489
  DropZone,
507
490
  {
508
491
  ...accept !== void 0 && { accept },
@@ -510,56 +493,89 @@ function FileUploader({
510
493
  ...onError !== void 0 && { onError },
511
494
  ...theme !== void 0 && { theme },
512
495
  multiple,
513
- disabled: disabled || state.status === "uploading",
496
+ disabled: disabled || isUploading,
514
497
  onFiles: handleFiles,
515
498
  style: { padding: "28px 24px", textAlign: "center" },
516
499
  children: /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: "8px" }, children: [
517
500
  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
501
  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" }),
502
+ /* @__PURE__ */ jsx("span", { style: { fontWeight: 600, color: "var(--silo-text)" }, children: multiple ? "Arraste arquivos aqui" : "Arraste o arquivo aqui" }),
503
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "13px", color: "var(--silo-text-muted)" }, children: "ou clique para selecionar" }),
521
504
  maxSize && /* @__PURE__ */ jsxs("span", { style: { fontSize: "12px", color: "var(--silo-text-muted)" }, children: [
522
- "Max ",
505
+ "M\xE1x ",
523
506
  formatBytes(maxSize)
524
507
  ] })
525
508
  ] })
526
509
  ] })
527
510
  }
528
511
  ),
529
- state.status === "uploading" && /* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column", gap: "6px" }, children: renderProgress ? renderProgress(state.progress) : /* @__PURE__ */ jsxs(Fragment, { children: [
512
+ isSingleUploading && !isBatch && /* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column", gap: "6px" }, children: renderProgress ? renderProgress(singleProgress) : /* @__PURE__ */ jsxs(Fragment, { children: [
530
513
  /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", fontSize: "13px", color: "var(--silo-text-muted)" }, children: [
531
- /* @__PURE__ */ jsx("span", { children: "Uploading\u2026" }),
514
+ /* @__PURE__ */ jsx("span", { children: single.state.status === "completing" ? "Finalizando\u2026" : single.state.status === "preparing" ? "Preparando\u2026" : `Parte ${single.state.part} de ${single.state.totalParts}` }),
532
515
  /* @__PURE__ */ jsxs("span", { children: [
533
- state.progress,
516
+ singleProgress,
534
517
  "%"
535
518
  ] })
536
519
  ] }),
537
- /* @__PURE__ */ jsx(ProgressBar, { progress: state.progress })
520
+ /* @__PURE__ */ jsx(ProgressBar, { progress: singleProgress })
538
521
  ] }) }),
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: [
522
+ isBatch && /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "8px" }, children: [
523
+ batch.state.files.map((f, i) => {
524
+ const st = f.status;
525
+ const p = st.status === "uploading" ? st.progress : st.status === "paused" ? st.progress : st.status === "done" ? 100 : st.status === "completing" ? 99 : 0;
526
+ const isPaused = st.status === "paused";
527
+ const isDone = st.status === "done";
528
+ const isErr = st.status === "error";
529
+ return /* @__PURE__ */ jsxs("div", { style: { padding: "10px 12px", backgroundColor: "var(--silo-bg)", borderRadius: "8px", border: "1px solid var(--silo-border)" }, children: [
530
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "8px", marginBottom: isDone || isErr ? 0 : "6px" }, children: [
531
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "16px" }, children: getFileIcon(f.file.type) }),
532
+ /* @__PURE__ */ jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [
533
+ /* @__PURE__ */ jsx("div", { style: { fontSize: "13px", fontWeight: 500, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: f.file.name }),
534
+ /* @__PURE__ */ jsx("div", { style: { fontSize: "11px", color: "var(--silo-text-muted)" }, children: formatBytes(f.file.size) })
535
+ ] }),
536
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "11px", fontWeight: 600, color: isDone ? "#16a34a" : isErr ? "#ef4444" : isPaused ? "#f59e0b" : "var(--silo-accent)", flexShrink: 0 }, children: isDone ? "\u2713" : isErr ? "Erro" : isPaused ? `Pausado ${p}%` : `${p}%` }),
537
+ !isDone && !isErr && f.fileId && (isPaused ? /* @__PURE__ */ jsx("button", { onClick: () => batch.resumeFile(f.fileId), style: { fontSize: "11px", padding: "2px 8px", borderRadius: "4px", border: "none", backgroundColor: "#10b981", color: "#fff", cursor: "pointer" }, children: "Retomar" }) : st.status === "uploading" ? /* @__PURE__ */ jsx("button", { onClick: () => batch.pauseFile(f.fileId), style: { fontSize: "11px", padding: "2px 8px", borderRadius: "4px", border: "none", backgroundColor: "#f59e0b", color: "#fff", cursor: "pointer" }, children: "Pausar" }) : null)
538
+ ] }),
539
+ !isDone && !isErr && /* @__PURE__ */ jsx(ProgressBar, { progress: p })
540
+ ] }, i);
541
+ }),
542
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "8px", marginTop: "4px" }, children: [
543
+ batch.state.status !== "done" && /* @__PURE__ */ jsx(
544
+ "button",
545
+ {
546
+ onClick: isBatchUploading ? batch.pauseAll : batch.resumeAll,
547
+ style: { flex: 1, padding: "8px", borderRadius: "6px", border: "none", backgroundColor: isBatchUploading ? "#f59e0b" : "#10b981", color: "#fff", fontSize: "13px", fontWeight: 600, cursor: "pointer" },
548
+ children: isBatchUploading ? "Pausar tudo" : "Retomar tudo"
549
+ }
550
+ ),
551
+ /* @__PURE__ */ jsx(
552
+ "button",
553
+ {
554
+ onClick: batch.state.status === "done" ? batch.reset : batch.abortAll,
555
+ style: { padding: "8px 14px", borderRadius: "6px", border: "none", backgroundColor: batch.state.status === "done" ? "var(--silo-accent)" : "rgba(239,68,68,0.1)", color: batch.state.status === "done" ? "#fff" : "#ef4444", fontSize: "13px", fontWeight: 600, cursor: "pointer" },
556
+ children: batch.state.status === "done" ? "Novo upload" : "Cancelar"
557
+ }
558
+ )
559
+ ] })
560
+ ] }),
561
+ single.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
562
  /* @__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
563
  /* @__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) })
564
+ /* @__PURE__ */ jsx("div", { style: { fontWeight: 500, color: "var(--silo-text)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: single.state.result.key }),
565
+ /* @__PURE__ */ jsx("div", { style: { fontSize: "12px", color: "var(--silo-text-muted)" }, children: formatBytes(single.state.result.size) })
544
566
  ] }),
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" })
567
+ /* @__PURE__ */ jsx("button", { onClick: single.reset, style: { background: "none", border: "none", cursor: "pointer", fontSize: "12px", color: "var(--silo-text-muted)", flexShrink: 0 }, children: "Enviar outro" })
549
568
  ] }),
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" })
569
+ single.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: [
570
+ /* @__PURE__ */ jsx("span", { children: single.state.error.message }),
571
+ /* @__PURE__ */ jsx("button", { onClick: single.reset, style: { marginLeft: "auto", background: "none", border: "none", cursor: "pointer", fontSize: "12px" }, children: "Tentar novamente" })
556
572
  ] })
557
573
  ] });
558
574
  }
559
575
  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}" }
576
+ image: { label: "Imagem", icon: "\u{1F5BC}\uFE0F" },
577
+ video: { label: "V\xEDdeo", icon: "\u{1F3AC}" },
578
+ file: { label: "Arquivo", icon: "\u{1F4CE}" }
563
579
  };
564
580
  function MediaUploader({
565
581
  tabs = ["image", "video", "file"],
@@ -571,6 +587,7 @@ function MediaUploader({
571
587
  style,
572
588
  theme,
573
589
  onUpload,
590
+ onBatchUpload,
574
591
  onError,
575
592
  ...shared
576
593
  }) {
@@ -644,8 +661,10 @@ function MediaUploader({
644
661
  {
645
662
  ...shared,
646
663
  ...fileProps,
664
+ multiple: true,
647
665
  ...theme !== void 0 && { theme },
648
666
  ...onUpload !== void 0 && { onUpload },
667
+ ...onBatchUpload !== void 0 && { onBatchUpload },
649
668
  ...onError !== void 0 && { onError }
650
669
  }
651
670
  )