@geekapps/silo-elements-nextjs 0.1.2 → 0.1.4

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.
@@ -164,6 +164,72 @@ function ProgressBar({ progress, className = "", style }) {
164
164
  }
165
165
  );
166
166
  }
167
+ var FORMATS = [
168
+ { value: "webp", label: "WebP", hint: "Melhor custo-benef\xEDcio" },
169
+ { value: "avif", label: "AVIF", hint: "M\xE1xima compress\xE3o" },
170
+ { value: "jpeg", label: "JPEG", hint: "Compatibilidade universal" },
171
+ { value: "png", label: "PNG", hint: "Sem perda" }
172
+ ];
173
+ function ImageOptions({ value, onChange, style }) {
174
+ const fmt = value.format ?? "webp";
175
+ const optimize = value.optimize ?? true;
176
+ return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 10, ...style }, children: [
177
+ /* @__PURE__ */ jsxs("div", { children: [
178
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 11, fontWeight: 700, color: "var(--silo-text-muted, #64748b)", letterSpacing: "0.05em", textTransform: "uppercase", marginBottom: 6 }, children: "Formato de sa\xEDda" }),
179
+ /* @__PURE__ */ jsx("div", { style: { display: "flex", gap: 6, flexWrap: "wrap" }, children: FORMATS.map((f) => /* @__PURE__ */ jsx(
180
+ "button",
181
+ {
182
+ type: "button",
183
+ onClick: () => onChange({ ...value, format: f.value }),
184
+ title: f.hint,
185
+ style: {
186
+ padding: "4px 12px",
187
+ borderRadius: 6,
188
+ border: `1px solid ${fmt === f.value ? "var(--silo-accent, #6366f1)" : "var(--silo-border, #e2e8f0)"}`,
189
+ background: fmt === f.value ? "var(--silo-accent, #6366f1)" : "transparent",
190
+ color: fmt === f.value ? "#fff" : "var(--silo-text, #0f172a)",
191
+ fontSize: 12,
192
+ fontWeight: 600,
193
+ cursor: "pointer"
194
+ },
195
+ children: f.label
196
+ },
197
+ f.value
198
+ )) })
199
+ ] }),
200
+ /* @__PURE__ */ jsxs("label", { style: { display: "flex", alignItems: "center", gap: 8, cursor: "pointer", userSelect: "none" }, children: [
201
+ /* @__PURE__ */ jsx(
202
+ "span",
203
+ {
204
+ onClick: () => onChange({ ...value, optimize: !optimize }),
205
+ style: {
206
+ width: 36,
207
+ height: 20,
208
+ borderRadius: 10,
209
+ background: optimize ? "var(--silo-accent, #6366f1)" : "var(--silo-border, #e2e8f0)",
210
+ position: "relative",
211
+ flexShrink: 0,
212
+ transition: "background 0.15s",
213
+ cursor: "pointer"
214
+ },
215
+ children: /* @__PURE__ */ jsx("span", { style: {
216
+ position: "absolute",
217
+ top: 2,
218
+ left: optimize ? 18 : 2,
219
+ width: 16,
220
+ height: 16,
221
+ borderRadius: "50%",
222
+ background: "#fff",
223
+ transition: "left 0.15s",
224
+ boxShadow: "0 1px 3px rgba(0,0,0,0.2)"
225
+ } })
226
+ }
227
+ ),
228
+ /* @__PURE__ */ jsx("span", { style: { fontSize: 12, color: "var(--silo-text, #0f172a)", fontWeight: 500 }, children: "Otimizar tamanho" }),
229
+ /* @__PURE__ */ jsx("span", { style: { fontSize: 11, color: "var(--silo-text-muted, #94a3b8)" }, children: optimize ? "Qualidade 85 \u2014 menor tamanho" : "Qualidade m\xE1xima" })
230
+ ] })
231
+ ] });
232
+ }
167
233
 
168
234
  // src/utils/format.ts
169
235
  function formatBytes(bytes) {
@@ -195,6 +261,7 @@ function ImageUploader({
195
261
  maxSize,
196
262
  accept = "image/*",
197
263
  showPreview = true,
264
+ showImageOptions = false,
198
265
  image,
199
266
  theme,
200
267
  renderIcon,
@@ -205,6 +272,7 @@ function ImageUploader({
205
272
  }) {
206
273
  const { state, upload, pause, resume, abort, reset } = useMultipartUpload(bucket);
207
274
  const [preview, setPreview] = useState(null);
275
+ const [imageOpts, setImageOpts] = useState(image ?? { format: "webp", optimize: true });
208
276
  const t = resolveTheme(theme);
209
277
  const vars = themeToVars(t);
210
278
  const handleFiles = useCallback(async (files) => {
@@ -212,16 +280,17 @@ function ImageUploader({
212
280
  if (!file) return;
213
281
  if (showPreview) setPreview(URL.createObjectURL(file));
214
282
  try {
283
+ const opts = showImageOptions ? imageOpts : image ?? imageOpts;
215
284
  const result = await upload(file, {
216
285
  ...bucket !== void 0 && { bucket },
217
286
  visibility,
218
- ...image && { image }
287
+ image: opts
219
288
  });
220
289
  if (result) onUpload?.(result);
221
290
  } catch (err) {
222
291
  onError?.(err instanceof Error ? err : new Error(String(err)));
223
292
  }
224
- }, [upload, bucket, visibility, image, onUpload, onError, showPreview]);
293
+ }, [upload, bucket, visibility, image, imageOpts, showImageOptions, onUpload, onError, showPreview]);
225
294
  const containerStyle = {
226
295
  ...vars,
227
296
  display: "flex",
@@ -237,6 +306,14 @@ function ImageUploader({
237
306
  if (state.status === "error" && renderError) return /* @__PURE__ */ jsx("div", { style: containerStyle, children: renderError(state.error, reset) });
238
307
  if (state.status === "done" && renderSuccess) return /* @__PURE__ */ jsx("div", { style: containerStyle, children: renderSuccess(state.result) });
239
308
  return /* @__PURE__ */ jsxs("div", { className: `silo-image-uploader${className ? ` ${className}` : ""}`, style: containerStyle, children: [
309
+ showImageOptions && !isUploading && state.status !== "done" && /* @__PURE__ */ jsx(
310
+ ImageOptions,
311
+ {
312
+ value: imageOpts,
313
+ onChange: setImageOpts,
314
+ style: { padding: "12px 14px", borderRadius: 8, border: "1px solid var(--silo-border, #e2e8f0)", background: "var(--silo-bg-hover, #f8fafc)" }
315
+ }
316
+ ),
240
317
  /* @__PURE__ */ jsx(
241
318
  DropZone,
242
319
  {
@@ -303,6 +380,132 @@ function ImageUploader({
303
380
  ] })
304
381
  ] });
305
382
  }
383
+ var CODECS = [
384
+ { value: "h264", label: "H.264", hint: "Compatibilidade m\xE1xima" },
385
+ { value: "h265", label: "H.265", hint: "Melhor compress\xE3o" },
386
+ { value: "av1", label: "AV1", hint: "Futuro \u2014 menor tamanho" },
387
+ { value: "vp9", label: "VP9", hint: "Open source" }
388
+ ];
389
+ var RESOLUTIONS = ["360", "480", "720", "1080", "1440", "2160"];
390
+ var RESOLUTION_LABELS = {
391
+ "360": "360p",
392
+ "480": "480p",
393
+ "720": "720p",
394
+ "1080": "1080p",
395
+ "1440": "1440p",
396
+ "2160": "4K"
397
+ };
398
+ function VideoOptions({ value, onChange, style }) {
399
+ const codec = value.codec ?? "h264";
400
+ const transcoding = value.transcoding ?? "auto";
401
+ const isAuto = transcoding === "auto";
402
+ const selectedRes = isAuto ? [] : transcoding;
403
+ function toggleRes(r) {
404
+ if (isAuto) {
405
+ onChange({ ...value, transcoding: [r] });
406
+ return;
407
+ }
408
+ const next = selectedRes.includes(r) ? selectedRes.filter((x) => x !== r) : [...selectedRes, r];
409
+ onChange({ ...value, transcoding: next.length === 0 ? "auto" : next });
410
+ }
411
+ function toggleFeature(key) {
412
+ onChange({ ...value, [key]: !value[key] });
413
+ }
414
+ const btn = (active) => ({
415
+ padding: "4px 10px",
416
+ borderRadius: 6,
417
+ border: `1px solid ${active ? "var(--silo-accent, #6366f1)" : "var(--silo-border, #e2e8f0)"}`,
418
+ background: active ? "var(--silo-accent, #6366f1)" : "transparent",
419
+ color: active ? "#fff" : "var(--silo-text, #0f172a)",
420
+ fontSize: 12,
421
+ fontWeight: 600,
422
+ cursor: "pointer"
423
+ });
424
+ const toggle = (on) => ({
425
+ display: "inline-flex",
426
+ alignItems: "center",
427
+ gap: 6,
428
+ cursor: "pointer",
429
+ userSelect: "none",
430
+ fontSize: 12,
431
+ color: "var(--silo-text, #0f172a)",
432
+ fontWeight: 500
433
+ });
434
+ function Toggle({ checked, onToggle, label }) {
435
+ return /* @__PURE__ */ jsxs("label", { style: toggle(), onClick: onToggle, children: [
436
+ /* @__PURE__ */ jsx("span", { style: {
437
+ width: 32,
438
+ height: 18,
439
+ borderRadius: 9,
440
+ flexShrink: 0,
441
+ background: checked ? "var(--silo-accent, #6366f1)" : "var(--silo-border, #e2e8f0)",
442
+ position: "relative",
443
+ transition: "background 0.15s",
444
+ cursor: "pointer"
445
+ }, children: /* @__PURE__ */ jsx("span", { style: {
446
+ position: "absolute",
447
+ top: 2,
448
+ left: checked ? 16 : 2,
449
+ width: 14,
450
+ height: 14,
451
+ borderRadius: "50%",
452
+ background: "#fff",
453
+ transition: "left 0.15s",
454
+ boxShadow: "0 1px 3px rgba(0,0,0,0.2)"
455
+ } }) }),
456
+ label
457
+ ] });
458
+ }
459
+ return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 12, ...style }, children: [
460
+ /* @__PURE__ */ jsxs("div", { children: [
461
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 11, fontWeight: 700, color: "var(--silo-text-muted, #64748b)", letterSpacing: "0.05em", textTransform: "uppercase", marginBottom: 6 }, children: "Codec" }),
462
+ /* @__PURE__ */ jsx("div", { style: { display: "flex", gap: 6, flexWrap: "wrap" }, children: CODECS.map((c) => /* @__PURE__ */ jsx(
463
+ "button",
464
+ {
465
+ type: "button",
466
+ title: c.hint,
467
+ onClick: () => onChange({ ...value, codec: c.value }),
468
+ style: btn(codec === c.value),
469
+ children: c.label
470
+ },
471
+ c.value
472
+ )) })
473
+ ] }),
474
+ /* @__PURE__ */ jsxs("div", { children: [
475
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 11, fontWeight: 700, color: "var(--silo-text-muted, #64748b)", letterSpacing: "0.05em", textTransform: "uppercase", marginBottom: 6 }, children: "Resolu\xE7\xF5es" }),
476
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 6, flexWrap: "wrap" }, children: [
477
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => onChange({ ...value, transcoding: "auto" }), style: btn(isAuto), children: "Auto" }),
478
+ RESOLUTIONS.map((r) => /* @__PURE__ */ jsx(
479
+ "button",
480
+ {
481
+ type: "button",
482
+ onClick: () => toggleRes(r),
483
+ style: btn(!isAuto && selectedRes.includes(r)),
484
+ children: RESOLUTION_LABELS[r]
485
+ },
486
+ r
487
+ ))
488
+ ] })
489
+ ] }),
490
+ /* @__PURE__ */ jsxs("div", { children: [
491
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 11, fontWeight: 700, color: "var(--silo-text-muted, #64748b)", letterSpacing: "0.05em", textTransform: "uppercase", marginBottom: 8 }, children: "Recursos" }),
492
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
493
+ /* @__PURE__ */ jsx(Toggle, { checked: value.thumbnails ?? true, onToggle: () => toggleFeature("thumbnails"), label: "Gerar thumbnails" }),
494
+ /* @__PURE__ */ jsx(Toggle, { checked: value.storyboard ?? false, onToggle: () => toggleFeature("storyboard"), label: "Gerar storyboard" }),
495
+ /* @__PURE__ */ jsx(Toggle, { checked: value.autoCaptions ?? false, onToggle: () => toggleFeature("autoCaptions"), label: "Legendas autom\xE1ticas (IA)" }),
496
+ /* @__PURE__ */ jsx(Toggle, { checked: value.separateAudio ?? false, onToggle: () => toggleFeature("separateAudio"), label: "Separar faixa de \xE1udio" })
497
+ ] })
498
+ ] })
499
+ ] });
500
+ }
501
+ var DEFAULT_VIDEO_OPTS = {
502
+ thumbnails: true,
503
+ storyboard: false,
504
+ autoCaptions: false,
505
+ separateAudio: false,
506
+ codec: "h264",
507
+ transcoding: "auto"
508
+ };
306
509
  function VideoUploader({
307
510
  bucket,
308
511
  expiresIn,
@@ -315,6 +518,7 @@ function VideoUploader({
315
518
  maxSize,
316
519
  accept = "video/*",
317
520
  showPreview = true,
521
+ showVideoOptions = false,
318
522
  video,
319
523
  theme,
320
524
  renderIcon,
@@ -325,6 +529,7 @@ function VideoUploader({
325
529
  }) {
326
530
  const { state, upload, pause, resume, abort, reset } = useMultipartUpload(bucket);
327
531
  const [preview, setPreview] = useState(null);
532
+ const [videoOpts, setVideoOpts] = useState(video ?? DEFAULT_VIDEO_OPTS);
328
533
  const t = resolveTheme(theme);
329
534
  const vars = themeToVars(t);
330
535
  const handleFiles = useCallback(async (files) => {
@@ -332,16 +537,17 @@ function VideoUploader({
332
537
  if (!file) return;
333
538
  if (showPreview) setPreview(URL.createObjectURL(file));
334
539
  try {
540
+ const opts = showVideoOptions ? videoOpts : video ?? videoOpts;
335
541
  const result = await upload(file, {
336
542
  ...bucket !== void 0 && { bucket },
337
543
  visibility,
338
- ...video && { video }
544
+ video: opts
339
545
  });
340
546
  if (result) onUpload?.(result);
341
547
  } catch (err) {
342
548
  onError?.(err instanceof Error ? err : new Error(String(err)));
343
549
  }
344
- }, [upload, bucket, visibility, video, onUpload, onError, showPreview]);
550
+ }, [upload, bucket, visibility, video, videoOpts, showVideoOptions, onUpload, onError, showPreview]);
345
551
  const containerStyle = {
346
552
  ...vars,
347
553
  display: "flex",
@@ -357,6 +563,14 @@ function VideoUploader({
357
563
  if (state.status === "error" && renderError) return /* @__PURE__ */ jsx("div", { style: containerStyle, children: renderError(state.error, reset) });
358
564
  if (state.status === "done" && renderSuccess) return /* @__PURE__ */ jsx("div", { style: containerStyle, children: renderSuccess(state.result) });
359
565
  return /* @__PURE__ */ jsxs("div", { className: `silo-video-uploader${className ? ` ${className}` : ""}`, style: containerStyle, children: [
566
+ showVideoOptions && !isUploading && state.status !== "done" && /* @__PURE__ */ jsx(
567
+ VideoOptions,
568
+ {
569
+ value: videoOpts,
570
+ onChange: setVideoOpts,
571
+ style: { padding: "12px 14px", borderRadius: 8, border: "1px solid var(--silo-border, #e2e8f0)", background: "var(--silo-bg-hover, #f8fafc)" }
572
+ }
573
+ ),
360
574
  /* @__PURE__ */ jsx(
361
575
  DropZone,
362
576
  {
@@ -398,7 +612,14 @@ function VideoUploader({
398
612
  /* @__PURE__ */ jsx("span", { style: { fontSize: "12px", color: "var(--silo-text-muted)" }, children: "O processamento inicia ap\xF3s o upload" })
399
613
  ] }) }),
400
614
  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" }),
615
+ /* @__PURE__ */ jsx(
616
+ "button",
617
+ {
618
+ onClick: () => resume({ ...bucket !== void 0 && { bucket }, visibility, video: showVideoOptions ? videoOpts : video ?? videoOpts }),
619
+ style: { flex: 1, padding: "8px", borderRadius: "6px", border: "none", backgroundColor: "#10b981", color: "#fff", fontSize: "13px", fontWeight: 600, cursor: "pointer" },
620
+ children: "Retomar upload"
621
+ }
622
+ ),
402
623
  /* @__PURE__ */ jsx("button", { onClick: () => {
403
624
  abort();
404
625
  setPreview(null);
@@ -421,6 +642,14 @@ function VideoUploader({
421
642
  ] })
422
643
  ] });
423
644
  }
645
+ var DEFAULT_VIDEO_OPTS2 = {
646
+ thumbnails: true,
647
+ storyboard: false,
648
+ autoCaptions: false,
649
+ separateAudio: false,
650
+ codec: "h264",
651
+ transcoding: "auto"
652
+ };
424
653
  function FileUploader({
425
654
  bucket,
426
655
  visibility = "private",
@@ -434,6 +663,8 @@ function FileUploader({
434
663
  accept,
435
664
  multiple = false,
436
665
  allowRename = false,
666
+ showImageOptions = false,
667
+ showVideoOptions = false,
437
668
  image,
438
669
  video,
439
670
  theme,
@@ -456,6 +687,8 @@ function FileUploader({
456
687
  };
457
688
  const single = useMultipartUpload(bucket);
458
689
  const batch = useBatchUpload();
690
+ const [imageOpts, setImageOpts] = useState(image ?? { format: "webp", optimize: true });
691
+ const [videoOpts, setVideoOpts] = useState(video ?? DEFAULT_VIDEO_OPTS2);
459
692
  const [staged, setStaged] = useState(null);
460
693
  const [renames, setRenames] = useState(/* @__PURE__ */ new Map());
461
694
  const [editingIndex, setEditingIndex] = useState(null);
@@ -479,9 +712,11 @@ function FileUploader({
479
712
  setEditingIndex(null);
480
713
  }
481
714
  const doUpload = useCallback(async (files) => {
715
+ const effectiveImage = showImageOptions ? imageOpts : image ?? imageOpts;
716
+ const effectiveVideo = showVideoOptions ? videoOpts : video ?? videoOpts;
482
717
  if (multiple && files.length > 1) {
483
718
  try {
484
- const opts = { ...bucket !== void 0 && { bucket }, visibility, ...image && { image }, ...video && { video } };
719
+ const opts = { ...bucket !== void 0 && { bucket }, visibility, image: effectiveImage, video: effectiveVideo };
485
720
  const results = await batch.upload(files, opts);
486
721
  onBatchUpload?.(results);
487
722
  results.forEach((r) => onUpload?.(r));
@@ -492,7 +727,7 @@ function FileUploader({
492
727
  const file = files[0];
493
728
  if (!file) return;
494
729
  try {
495
- const opts = { ...bucket !== void 0 && { bucket }, visibility, ...image && { image }, ...video && { video } };
730
+ const opts = { ...bucket !== void 0 && { bucket }, visibility, image: effectiveImage, video: effectiveVideo };
496
731
  const result = await single.upload(file, opts);
497
732
  if (result) onUpload?.(result);
498
733
  } catch (err) {
@@ -611,6 +846,22 @@ function FileUploader({
611
846
  }
612
847
  )
613
848
  ] }, i)),
849
+ showImageOptions && staged.some((f) => f.type.startsWith("image/")) && /* @__PURE__ */ jsx(
850
+ ImageOptions,
851
+ {
852
+ value: imageOpts,
853
+ onChange: setImageOpts,
854
+ style: { padding: "12px 14px", borderRadius: 8, border: "1px solid var(--silo-border)", background: "var(--silo-bg-hover, #f8fafc)", marginTop: 4 }
855
+ }
856
+ ),
857
+ showVideoOptions && staged.some((f) => f.type.startsWith("video/")) && /* @__PURE__ */ jsx(
858
+ VideoOptions,
859
+ {
860
+ value: videoOpts,
861
+ onChange: setVideoOpts,
862
+ style: { padding: "12px 14px", borderRadius: 8, border: "1px solid var(--silo-border)", background: "var(--silo-bg-hover, #f8fafc)", marginTop: 4 }
863
+ }
864
+ ),
614
865
  /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "8px", marginTop: "4px" }, children: [
615
866
  /* @__PURE__ */ jsx(
616
867
  "button",