@geekapps/silo-elements-nextjs 0.2.42 → 0.2.44

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.
@@ -8,17 +8,11 @@ type SourceProps = {
8
8
  type?: VideoSourceType;
9
9
  default?: boolean;
10
10
  };
11
- type SourcesProps = {
12
- children: ReactNode;
13
- };
14
- type SubtitleProps = {
11
+ type CaptionsProps = {
15
12
  src: string;
16
- srclang: string;
17
- label: string;
18
- default?: boolean;
19
13
  };
20
- type SubtitlesProps = {
21
- children: ReactNode;
14
+ type ThumbnailProps = {
15
+ src: string;
22
16
  };
23
17
  type StoryboardFrameProps = {
24
18
  start: number;
@@ -39,7 +33,6 @@ type StoryboardProps = {
39
33
  type VideoProps = {
40
34
  title?: string;
41
35
  description?: string;
42
- poster?: string;
43
36
  children: ReactNode;
44
37
  className?: string;
45
38
  autoHideControls?: boolean;
@@ -48,20 +41,16 @@ type VideoProps = {
48
41
  /** Fixed height — disables aspect-ratio so the player stays at exactly this height regardless of video dimensions */
49
42
  fixedHeight?: string | number;
50
43
  };
51
- declare function Sources(_props: SourcesProps): null;
52
- declare namespace Sources {
53
- var displayName: string;
54
- }
55
44
  declare function Source(_props: SourceProps): null;
56
45
  declare namespace Source {
57
46
  var displayName: string;
58
47
  }
59
- declare function Subtitles(_props: SubtitlesProps): null;
60
- declare namespace Subtitles {
48
+ declare function Captions(_props: CaptionsProps): null;
49
+ declare namespace Captions {
61
50
  var displayName: string;
62
51
  }
63
- declare function Subtitle(_props: SubtitleProps): null;
64
- declare namespace Subtitle {
52
+ declare function VideoThumbnail(_props: ThumbnailProps): null;
53
+ declare namespace VideoThumbnail {
65
54
  var displayName: string;
66
55
  }
67
56
  declare function Storyboard(_props: StoryboardProps): null;
@@ -72,7 +61,7 @@ declare function StoryboardFrame(_props: StoryboardFrameProps): null;
72
61
  declare namespace StoryboardFrame {
73
62
  var displayName: string;
74
63
  }
75
- declare function Video({ title, description, poster, children, className, autoHideControls, defaultVolume, maxHeight, fixedHeight, }: VideoProps): react__default.JSX.Element;
64
+ declare function Video({ title, description, children, className, autoHideControls, defaultVolume, maxHeight, fixedHeight, }: VideoProps): react__default.JSX.Element;
76
65
  declare const VideoPlayer: typeof Video;
77
66
 
78
- export { Source, type SourceProps, Sources, type SourcesProps, Storyboard, StoryboardFrame, type StoryboardFrameProps, type StoryboardProps, Subtitle, type SubtitleProps, Subtitles, type SubtitlesProps, Video, VideoPlayer, type VideoSourceType };
67
+ export { Captions, type CaptionsProps, Source, type SourceProps, Storyboard, StoryboardFrame, type StoryboardFrameProps, type StoryboardProps, type ThumbnailProps, Video, VideoPlayer, type VideoSourceType, VideoThumbnail };
@@ -1,6 +1,6 @@
1
- import React, { useMemo, useRef, useState, useCallback, useEffect } from 'react';
1
+ import React, { useMemo, useState, useEffect, useRef, useCallback } from 'react';
2
2
  import gsap from 'gsap';
3
- import { Pause, Play, VolumeX, Volume2, Captions, Settings, Minimize, Maximize } from 'lucide-react';
3
+ import { Pause, Play, VolumeX, Volume2, Captions as Captions$1, Settings, Minimize, Maximize } from 'lucide-react';
4
4
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
5
5
 
6
6
  var AUTO_QUALITY = {
@@ -9,22 +9,18 @@ var AUTO_QUALITY = {
9
9
  type: "auto"
10
10
  };
11
11
  var PLAYBACK_SPEEDS = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 2];
12
- function Sources(_props) {
13
- return null;
14
- }
15
- Sources.displayName = "SiloSources";
16
12
  function Source(_props) {
17
13
  return null;
18
14
  }
19
15
  Source.displayName = "SiloSource";
20
- function Subtitles(_props) {
16
+ function Captions(_props) {
21
17
  return null;
22
18
  }
23
- Subtitles.displayName = "SiloSubtitles";
24
- function Subtitle(_props) {
19
+ Captions.displayName = "SiloCaptions";
20
+ function VideoThumbnail(_props) {
25
21
  return null;
26
22
  }
27
- Subtitle.displayName = "SiloSubtitle";
23
+ VideoThumbnail.displayName = "SiloVideoThumbnail";
28
24
  function Storyboard(_props) {
29
25
  return null;
30
26
  }
@@ -36,7 +32,6 @@ StoryboardFrame.displayName = "SiloStoryboardFrame";
36
32
  function Video({
37
33
  title,
38
34
  description,
39
- poster,
40
35
  children,
41
36
  className,
42
37
  autoHideControls = true,
@@ -45,14 +40,40 @@ function Video({
45
40
  fixedHeight
46
41
  }) {
47
42
  const parsed = useMemo(() => parseVideoChildren(children), [children]);
43
+ const [captions, setCaptions] = useState([]);
44
+ const [poster, setPoster] = useState(void 0);
45
+ useEffect(() => {
46
+ if (!parsed.captionsSrc) {
47
+ setCaptions([]);
48
+ return;
49
+ }
50
+ let cancelled = false;
51
+ fetch(parsed.captionsSrc, { cache: "no-store" }).then((r) => r.json()).then((data) => {
52
+ if (!cancelled && Array.isArray(data)) {
53
+ setCaptions(data.map((c, i) => ({ ...c, default: i === 0 })));
54
+ }
55
+ }).catch(() => {
56
+ if (!cancelled) setCaptions([]);
57
+ });
58
+ return () => {
59
+ cancelled = true;
60
+ };
61
+ }, [parsed.captionsSrc]);
62
+ useEffect(() => {
63
+ if (!parsed.thumbnailSrc) {
64
+ setPoster(void 0);
65
+ return;
66
+ }
67
+ setPoster(parsed.thumbnailSrc);
68
+ }, [parsed.thumbnailSrc]);
48
69
  const initialSourceIndex = useMemo(() => {
49
70
  const index = parsed.sources.findIndex((source) => source.default);
50
71
  return index >= 0 ? index : 0;
51
72
  }, [parsed.sources]);
52
73
  const initialSubtitleMode = useMemo(() => {
53
- const track = parsed.subtitles.find((subtitle) => subtitle.default);
74
+ const track = captions.find((c) => c.default);
54
75
  return track?.srclang ?? "off";
55
- }, [parsed.subtitles]);
76
+ }, [captions]);
56
77
  const containerRef = useRef(null);
57
78
  const chromeRef = useRef(null);
58
79
  const playerRef = useRef(null);
@@ -97,7 +118,6 @@ function Video({
97
118
  const [isFullscreen, setIsFullscreen] = useState(false);
98
119
  const [playerHeight, setPlayerHeight] = useState(0);
99
120
  const [playerWidth, setPlayerWidth] = useState(0);
100
- const [error, setError] = useState(null);
101
121
  const activeSource = parsed.sources[sourceIndex] ?? parsed.sources[0] ?? null;
102
122
  const progressPercent = duration ? currentTime / duration * 100 : 0;
103
123
  const bufferedPercent = duration ? bufferedTime / duration * 100 : 0;
@@ -158,10 +178,10 @@ function Video({
158
178
  }
159
179
  }, [sourceIndex, parsed.sources.length, initialSourceIndex]);
160
180
  useEffect(() => {
161
- if (subtitleMode !== "off" && !parsed.subtitles.some((subtitle) => subtitle.srclang === subtitleMode)) {
181
+ if (subtitleMode !== "off" && !captions.some((subtitle) => subtitle.srclang === subtitleMode)) {
162
182
  setSubtitleMode(initialSubtitleMode);
163
183
  }
164
- }, [subtitleMode, parsed.subtitles, initialSubtitleMode]);
184
+ }, [subtitleMode, captions, initialSubtitleMode]);
165
185
  useEffect(() => {
166
186
  const video = videoRef.current;
167
187
  if (!video) return;
@@ -368,7 +388,6 @@ function Video({
368
388
  return;
369
389
  }
370
390
  destroyMediaEngines();
371
- setError(null);
372
391
  setIsLoading(true);
373
392
  setCurrentTime(0);
374
393
  setBufferedTime(0);
@@ -413,13 +432,13 @@ function Video({
413
432
  setIsLoading(false);
414
433
  });
415
434
  dash.on(dashjs.MediaPlayer.events.ERROR, () => {
416
- setError("N\xE3o foi poss\xEDvel reproduzir o stream MPEG-DASH.");
435
+ console.error("[Silo] MPEG-DASH playback failed");
417
436
  setIsLoading(false);
418
437
  });
419
438
  dash.initialize(video, activeSource.src, false);
420
439
  } catch {
421
440
  if (!cancelled) {
422
- setError("N\xE3o foi poss\xEDvel carregar o player MPEG-DASH.");
441
+ console.error("[Silo] MPEG-DASH player load failed");
423
442
  setIsLoading(false);
424
443
  }
425
444
  }
@@ -485,7 +504,7 @@ function Video({
485
504
  hls.recoverMediaError();
486
505
  return;
487
506
  }
488
- setError("N\xE3o foi poss\xEDvel reproduzir o stream HLS.");
507
+ console.error("[Silo] HLS playback failed");
489
508
  setIsLoading(false);
490
509
  });
491
510
  return;
@@ -496,11 +515,11 @@ function Video({
496
515
  setIsLoading(false);
497
516
  return;
498
517
  }
499
- setError("Este navegador n\xE3o suporta HLS.");
518
+ console.error("[Silo] HLS not supported in this browser");
500
519
  setIsLoading(false);
501
520
  } catch {
502
521
  if (!cancelled) {
503
- setError("N\xE3o foi poss\xEDvel carregar o player HLS.");
522
+ console.error("[Silo] HLS player load failed");
504
523
  setIsLoading(false);
505
524
  }
506
525
  }
@@ -522,9 +541,18 @@ function Video({
522
541
  const togglePlay = useCallback(async () => {
523
542
  const video = videoRef.current;
524
543
  if (!video) return;
544
+ const wasPaused = video.paused;
525
545
  try {
526
- const wasPaused = video.paused;
527
546
  if (wasPaused) {
547
+ if (video.readyState < HTMLMediaElement.HAVE_FUTURE_DATA) {
548
+ await new Promise((resolve) => {
549
+ const onReady = () => {
550
+ video.removeEventListener("canplay", onReady);
551
+ resolve();
552
+ };
553
+ video.addEventListener("canplay", onReady);
554
+ });
555
+ }
528
556
  await video.play();
529
557
  } else {
530
558
  video.pause();
@@ -537,7 +565,6 @@ function Video({
537
565
  600
538
566
  );
539
567
  } catch {
540
- setError("O navegador bloqueou a reprodu\xE7\xE3o autom\xE1tica.");
541
568
  }
542
569
  }, []);
543
570
  const seekRelative = useCallback((seconds) => {
@@ -591,7 +618,7 @@ function Video({
591
618
  }
592
619
  throw new Error("Fullscreen unavailable");
593
620
  } catch {
594
- setError("N\xE3o foi poss\xEDvel alterar o modo fullscreen.");
621
+ console.error("[Silo] Fullscreen toggle failed");
595
622
  }
596
623
  }, []);
597
624
  const changeAudio = useCallback((trackId) => {
@@ -778,7 +805,7 @@ function Video({
778
805
  preload: "metadata",
779
806
  crossOrigin: "anonymous",
780
807
  children: [
781
- parsed.subtitles.map((subtitle) => /* @__PURE__ */ jsx(
808
+ captions.map((subtitle) => /* @__PURE__ */ jsx(
782
809
  "track",
783
810
  {
784
811
  kind: "subtitles",
@@ -826,7 +853,6 @@ function Video({
826
853
  }
827
854
  ),
828
855
  isLoading && /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-0 z-20 grid place-items-center bg-black/10", children: /* @__PURE__ */ jsx("div", { className: "size-9 animate-spin rounded-full border-2 border-white/25 border-t-white" }) }),
829
- error && /* @__PURE__ */ jsx("div", { className: "absolute inset-x-8 top-1/2 z-40 -translate-y-1/2 rounded-2xl border border-white/10 bg-black/75 p-5 text-center text-sm text-white shadow-2xl backdrop-blur-xl", children: error }),
830
856
  /* @__PURE__ */ jsxs(
831
857
  "div",
832
858
  {
@@ -886,11 +912,11 @@ function Video({
886
912
  label: "Qualidade",
887
913
  value: qualities.find((q) => q.id === selectedQuality)?.label ?? "Auto"
888
914
  },
889
- ...parsed.subtitles.length > 0 ? [
915
+ ...captions.length > 0 ? [
890
916
  {
891
917
  id: "subtitles",
892
918
  label: "Legendas",
893
- value: subtitleStyle.track === "off" ? "Desligado" : parsed.subtitles.find(
919
+ value: subtitleStyle.track === "off" ? "Desligado" : captions.find(
894
920
  (s) => s.srclang === subtitleStyle.track
895
921
  )?.label ?? subtitleStyle.track
896
922
  }
@@ -1047,7 +1073,7 @@ function Video({
1047
1073
  ),
1048
1074
  /* @__PURE__ */ jsx("div", { className: "py-1.5", children: [
1049
1075
  { srclang: "off", label: "Desligado" },
1050
- ...parsed.subtitles
1076
+ ...captions
1051
1077
  ].map((s) => /* @__PURE__ */ jsxs(
1052
1078
  "button",
1053
1079
  {
@@ -1447,16 +1473,16 @@ function Video({
1447
1473
  ) })
1448
1474
  ] }),
1449
1475
  /* @__PURE__ */ jsx("div", { className: "mx-0.5 h-4 w-px bg-white/20 @md:mx-1" }),
1450
- parsed.subtitles.length > 0 && /* @__PURE__ */ jsx(
1476
+ captions.length > 0 && /* @__PURE__ */ jsx(
1451
1477
  "button",
1452
1478
  {
1453
1479
  type: "button",
1454
1480
  onClick: () => setSubtitleMode(
1455
- (m) => m === "off" ? parsed.subtitles[0]?.srclang ?? "off" : "off"
1481
+ (m) => m === "off" ? captions[0]?.srclang ?? "off" : "off"
1456
1482
  ),
1457
1483
  className: `grid size-8 place-items-center rounded transition hover:text-white/80 @sm:size-10${subtitleMode !== "off" ? "text-white" : "text-white/60"}`,
1458
1484
  "aria-label": "Captions",
1459
- children: /* @__PURE__ */ jsx(Captions, { className: "size-4 @sm:size-5" })
1485
+ children: /* @__PURE__ */ jsx(Captions$1, { className: "size-4 @sm:size-5" })
1460
1486
  }
1461
1487
  ),
1462
1488
  /* @__PURE__ */ jsx("div", { className: "relative", children: /* @__PURE__ */ jsx(
@@ -1584,8 +1610,7 @@ function Video({
1584
1610
  var VideoPlayer = Video;
1585
1611
  function parseVideoChildren(children) {
1586
1612
  const parsed = {
1587
- sources: [],
1588
- subtitles: []
1613
+ sources: []
1589
1614
  };
1590
1615
  function dn(child) {
1591
1616
  return child.type?.displayName ?? child.type?.name ?? "";
@@ -1593,25 +1618,17 @@ function parseVideoChildren(children) {
1593
1618
  React.Children.forEach(children, (child) => {
1594
1619
  if (!React.isValidElement(child)) return;
1595
1620
  const name = dn(child);
1596
- if (child.type === Sources || name === "SiloSources") {
1621
+ if (child.type === Source || name === "SiloSource") {
1597
1622
  const element = child;
1598
- React.Children.forEach(element.props.children, (sourceChild) => {
1599
- if (!React.isValidElement(sourceChild)) return;
1600
- const sn = dn(sourceChild);
1601
- if (sourceChild.type !== Source && sn !== "SiloSource") return;
1602
- const sourceElement = sourceChild;
1603
- parsed.sources.push(sourceElement.props);
1604
- });
1623
+ parsed.sources.push(element.props);
1605
1624
  }
1606
- if (child.type === Subtitles || name === "SiloSubtitles") {
1625
+ if (child.type === Captions || name === "SiloCaptions") {
1607
1626
  const element = child;
1608
- React.Children.forEach(element.props.children, (subtitleChild) => {
1609
- if (!React.isValidElement(subtitleChild)) return;
1610
- const sn = dn(subtitleChild);
1611
- if (subtitleChild.type !== Subtitle && sn !== "SiloSubtitle") return;
1612
- const subtitleElement = subtitleChild;
1613
- parsed.subtitles.push(subtitleElement.props);
1614
- });
1627
+ parsed.captionsSrc = element.props.src;
1628
+ }
1629
+ if (child.type === VideoThumbnail || name === "SiloVideoThumbnail") {
1630
+ const element = child;
1631
+ parsed.thumbnailSrc = element.props.src;
1615
1632
  }
1616
1633
  if (child.type === Storyboard || name === "SiloStoryboard") {
1617
1634
  const element = child;
@@ -1711,6 +1728,6 @@ function formatTime(seconds) {
1711
1728
  return `${minutes}:${String(secs).padStart(2, "0")}`;
1712
1729
  }
1713
1730
 
1714
- export { Source, Sources, Storyboard, StoryboardFrame, Subtitle, Subtitles, Video, VideoPlayer };
1731
+ export { Captions, Source, Storyboard, StoryboardFrame, Video, VideoPlayer, VideoThumbnail };
1715
1732
  //# sourceMappingURL=VideoPlayer.js.map
1716
1733
  //# sourceMappingURL=VideoPlayer.js.map