@lobehub/ui 6.0.0-beta.2 → 6.0.0-beta.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.
Files changed (80) hide show
  1. package/es/Accordion/style.d.mts +3 -3
  2. package/es/AutoComplete/style.d.mts +1 -1
  3. package/es/Tabs/style.d.mts +1 -1
  4. package/es/Video/Video.mjs +46 -0
  5. package/es/Video/Video.mjs.map +1 -0
  6. package/es/Video/context.mjs +14 -0
  7. package/es/Video/context.mjs.map +1 -0
  8. package/es/Video/index.d.mts +23 -39
  9. package/es/Video/index.mjs +21 -111
  10. package/es/Video/index.mjs.map +1 -1
  11. package/es/Video/parts/Caption.d.mts +9 -0
  12. package/es/Video/parts/Caption.mjs +37 -0
  13. package/es/Video/parts/Caption.mjs.map +1 -0
  14. package/es/Video/parts/CenterPlay.d.mts +8 -0
  15. package/es/Video/parts/CenterPlay.mjs +34 -0
  16. package/es/Video/parts/CenterPlay.mjs.map +1 -0
  17. package/es/Video/parts/Controls.d.mts +9 -0
  18. package/es/Video/parts/Controls.mjs +128 -0
  19. package/es/Video/parts/Controls.mjs.map +1 -0
  20. package/es/Video/parts/Frame.d.mts +9 -0
  21. package/es/Video/parts/Frame.mjs +101 -0
  22. package/es/Video/parts/Frame.mjs.map +1 -0
  23. package/es/Video/parts/HoverPreview.mjs +93 -0
  24. package/es/Video/parts/HoverPreview.mjs.map +1 -0
  25. package/es/Video/parts/LoadError.d.mts +8 -0
  26. package/es/Video/parts/LoadError.mjs +47 -0
  27. package/es/Video/parts/LoadError.mjs.map +1 -0
  28. package/es/Video/parts/PlayPauseIcon.mjs +48 -0
  29. package/es/Video/parts/PlayPauseIcon.mjs.map +1 -0
  30. package/es/Video/parts/RateMenu.mjs +48 -0
  31. package/es/Video/parts/RateMenu.mjs.map +1 -0
  32. package/es/Video/parts/Root.d.mts +9 -0
  33. package/es/Video/parts/Root.mjs +110 -0
  34. package/es/Video/parts/Root.mjs.map +1 -0
  35. package/es/Video/parts/SectionList.d.mts +9 -0
  36. package/es/Video/parts/SectionList.mjs +32 -0
  37. package/es/Video/parts/SectionList.mjs.map +1 -0
  38. package/es/Video/parts/SectionMenu.mjs +63 -0
  39. package/es/Video/parts/SectionMenu.mjs.map +1 -0
  40. package/es/Video/parts/Timeline.d.mts +9 -0
  41. package/es/Video/parts/Timeline.mjs +169 -0
  42. package/es/Video/parts/Timeline.mjs.map +1 -0
  43. package/es/Video/store.mjs +241 -0
  44. package/es/Video/store.mjs.map +1 -0
  45. package/es/Video/style.mjs +393 -23
  46. package/es/Video/style.mjs.map +1 -1
  47. package/es/Video/time.mjs +60 -0
  48. package/es/Video/time.mjs.map +1 -0
  49. package/es/Video/type.d.mts +125 -0
  50. package/es/Video/useControlsIdle.mjs +39 -0
  51. package/es/Video/useControlsIdle.mjs.map +1 -0
  52. package/es/Video/useFullscreen.mjs +17 -0
  53. package/es/Video/useFullscreen.mjs.map +1 -0
  54. package/es/Video/useKeyboard.mjs +60 -0
  55. package/es/Video/useKeyboard.mjs.map +1 -0
  56. package/es/Video/useVideo.d.mts +9 -0
  57. package/es/Video/useVideo.mjs +17 -0
  58. package/es/Video/useVideo.mjs.map +1 -0
  59. package/es/i18n/resources/en/index.d.mts +3 -2
  60. package/es/i18n/resources/en/index.mjs +4 -2
  61. package/es/i18n/resources/en/index.mjs.map +1 -1
  62. package/es/i18n/resources/en/video.d.mts +23 -0
  63. package/es/i18n/resources/en/video.mjs +24 -0
  64. package/es/i18n/resources/en/video.mjs.map +1 -0
  65. package/es/i18n/resources/zhCn/index.d.mts +3 -2
  66. package/es/i18n/resources/zhCn/index.mjs +4 -2
  67. package/es/i18n/resources/zhCn/index.mjs.map +1 -1
  68. package/es/i18n/resources/zhCn/video.d.mts +23 -0
  69. package/es/i18n/resources/zhCn/video.mjs +24 -0
  70. package/es/i18n/resources/zhCn/video.mjs.map +1 -0
  71. package/es/i18n/types.d.mts +2 -1
  72. package/es/index.d.mts +4 -2
  73. package/es/index.mjs +2 -1
  74. package/es/mdx/mdxComponents/Video.d.mts +2 -1
  75. package/es/mdx/mdxComponents/Video.mjs +5 -3
  76. package/es/mdx/mdxComponents/Video.mjs.map +1 -1
  77. package/es/style.css +263 -23
  78. package/package.json +1 -1
  79. package/es/Video/marker.stylex.mjs +0 -9
  80. package/es/Video/marker.stylex.mjs.map +0 -1
@@ -0,0 +1,101 @@
1
+ "use client";
2
+ import { styleProps } from "../../styles/stylex/props.mjs";
3
+ import { FrameContext, useVideoContext } from "../context.mjs";
4
+ import { styles } from "../style.mjs";
5
+ import { useVideo } from "../useVideo.mjs";
6
+ import { useControlsIdle } from "../useControlsIdle.mjs";
7
+ import { useEffect, useMemo, useRef, useState } from "react";
8
+ import { jsx, jsxs } from "react/jsx-runtime";
9
+ //#region src/Video/parts/Frame.tsx
10
+ const isFocusVisible = (element) => {
11
+ try {
12
+ return element.matches(":focus-visible");
13
+ } catch {
14
+ return true;
15
+ }
16
+ };
17
+ const Frame = ({ children, className, style }) => {
18
+ const { media, store } = useVideoContext();
19
+ const videoRef = useRef(null);
20
+ const pointerTypeRef = useRef("mouse");
21
+ const size = useVideo((state) => state.size);
22
+ const idleForced = useVideo((state) => state.paused || state.status !== "ready");
23
+ const idle = useControlsIdle(idleForced);
24
+ const [overlay, setOverlay] = useState(false);
25
+ useEffect(() => {
26
+ const video = videoRef.current;
27
+ if (!video) return;
28
+ return store.attach(video);
29
+ }, [store, media.src]);
30
+ const frame = useMemo(() => ({
31
+ hold: idle.hold,
32
+ overlay,
33
+ setOverlay,
34
+ visible: idle.visible
35
+ }), [
36
+ idle.hold,
37
+ idle.visible,
38
+ overlay
39
+ ]);
40
+ const aspectRatio = size?.height ? `${size.width} / ${size.height}` : "16 / 9";
41
+ return /* @__PURE__ */ jsx(FrameContext, {
42
+ value: frame,
43
+ children: /* @__PURE__ */ jsxs("div", {
44
+ ...styleProps(styles.frame, className, {
45
+ aspectRatio,
46
+ ...style
47
+ }),
48
+ onBlur: (event) => {
49
+ if (!event.currentTarget.contains(event.relatedTarget)) idle.setFocused(false);
50
+ },
51
+ onFocus: (event) => {
52
+ idle.setFocused(isFocusVisible(event.target));
53
+ },
54
+ onPointerDownCapture: (event) => {
55
+ pointerTypeRef.current = event.pointerType || "mouse";
56
+ },
57
+ onPointerLeave: (event) => {
58
+ if (event.pointerType !== "touch") idle.sleep();
59
+ },
60
+ onPointerMove: (event) => {
61
+ if (event.pointerType !== "touch") idle.wake();
62
+ },
63
+ children: [
64
+ /* @__PURE__ */ jsx("video", {
65
+ playsInline: true,
66
+ autoPlay: media.autoPlay,
67
+ loop: media.loop,
68
+ poster: media.poster,
69
+ preload: media.preload,
70
+ ref: videoRef,
71
+ src: media.src,
72
+ onEnded: media.onEnded,
73
+ onError: media.onError,
74
+ onPause: media.onPause,
75
+ onPlay: media.onPlay,
76
+ onTimeUpdate: media.onTimeUpdate,
77
+ ...styleProps(styles.video)
78
+ }),
79
+ /* @__PURE__ */ jsx("div", {
80
+ "data-video-surface": "",
81
+ ...styleProps(styles.surface),
82
+ onDoubleClick: () => store.toggleFullscreen(),
83
+ onClick: () => {
84
+ if (pointerTypeRef.current === "touch" && !idle.visible) {
85
+ idle.wake();
86
+ return;
87
+ }
88
+ if (pointerTypeRef.current === "touch") idle.wake();
89
+ store.toggle();
90
+ }
91
+ }),
92
+ children
93
+ ]
94
+ })
95
+ });
96
+ };
97
+ Frame.displayName = "VideoFrame";
98
+ //#endregion
99
+ export { Frame as default };
100
+
101
+ //# sourceMappingURL=Frame.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Frame.mjs","names":[],"sources":["../../../src/Video/parts/Frame.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useMemo, useRef, useState } from 'react';\n\nimport { styleProps } from '@/styles/stylex/props';\n\nimport { FrameContext, type FrameContextValue, useVideoContext } from '../context';\nimport { styles } from '../style';\nimport type { VideoFrameProps } from '../type';\nimport { useControlsIdle } from '../useControlsIdle';\nimport { useVideo } from '../useVideo';\n\nconst isFocusVisible = (element: Element) => {\n try {\n return element.matches(':focus-visible');\n } catch {\n return true;\n }\n};\n\nconst Frame = ({ children, className, style }: VideoFrameProps) => {\n const { media, store } = useVideoContext();\n const videoRef = useRef<HTMLVideoElement>(null);\n const pointerTypeRef = useRef('mouse');\n const size = useVideo((state) => state.size);\n const idleForced = useVideo((state) => state.paused || state.status !== 'ready');\n const idle = useControlsIdle(idleForced);\n const [overlay, setOverlay] = useState(false);\n\n useEffect(() => {\n const video = videoRef.current;\n if (!video) return;\n return store.attach(video);\n }, [store, media.src]);\n\n const frame = useMemo<FrameContextValue>(\n () => ({ hold: idle.hold, overlay, setOverlay, visible: idle.visible }),\n [idle.hold, idle.visible, overlay],\n );\n\n const aspectRatio = size?.height ? `${size.width} / ${size.height}` : '16 / 9';\n\n return (\n <FrameContext value={frame}>\n <div\n {...styleProps(styles.frame, className, { aspectRatio, ...style })}\n onBlur={(event) => {\n if (!event.currentTarget.contains(event.relatedTarget as Node | null))\n idle.setFocused(false);\n }}\n onFocus={(event) => {\n idle.setFocused(isFocusVisible(event.target));\n }}\n onPointerDownCapture={(event) => {\n pointerTypeRef.current = event.pointerType || 'mouse';\n }}\n onPointerLeave={(event) => {\n if (event.pointerType !== 'touch') idle.sleep();\n }}\n onPointerMove={(event) => {\n if (event.pointerType !== 'touch') idle.wake();\n }}\n >\n <video\n playsInline\n autoPlay={media.autoPlay}\n loop={media.loop}\n poster={media.poster}\n preload={media.preload}\n ref={videoRef}\n src={media.src}\n onEnded={media.onEnded}\n onError={media.onError}\n onPause={media.onPause}\n onPlay={media.onPlay}\n onTimeUpdate={media.onTimeUpdate}\n {...styleProps(styles.video)}\n />\n <div\n data-video-surface=\"\"\n {...styleProps(styles.surface)}\n onDoubleClick={() => store.toggleFullscreen()}\n onClick={() => {\n if (pointerTypeRef.current === 'touch' && !idle.visible) {\n idle.wake();\n return;\n }\n if (pointerTypeRef.current === 'touch') idle.wake();\n store.toggle();\n }}\n />\n {children}\n </div>\n </FrameContext>\n );\n};\n\nFrame.displayName = 'VideoFrame';\n\nexport default Frame;\n"],"mappings":";;;;;;;;;AAYA,MAAM,kBAAkB,YAAqB;CAC3C,IAAI;EACF,OAAO,QAAQ,QAAQ,gBAAgB;CACzC,QAAQ;EACN,OAAO;CACT;AACF;AAEA,MAAM,SAAS,EAAE,UAAU,WAAW,YAA6B;CACjE,MAAM,EAAE,OAAO,UAAU,gBAAgB;CACzC,MAAM,WAAW,OAAyB,IAAI;CAC9C,MAAM,iBAAiB,OAAO,OAAO;CACrC,MAAM,OAAO,UAAU,UAAU,MAAM,IAAI;CAC3C,MAAM,aAAa,UAAU,UAAU,MAAM,UAAU,MAAM,WAAW,OAAO;CAC/E,MAAM,OAAO,gBAAgB,UAAU;CACvC,MAAM,CAAC,SAAS,cAAc,SAAS,KAAK;CAE5C,gBAAgB;EACd,MAAM,QAAQ,SAAS;EACvB,IAAI,CAAC,OAAO;EACZ,OAAO,MAAM,OAAO,KAAK;CAC3B,GAAG,CAAC,OAAO,MAAM,GAAG,CAAC;CAErB,MAAM,QAAQ,eACL;EAAE,MAAM,KAAK;EAAM;EAAS;EAAY,SAAS,KAAK;CAAQ,IACrE;EAAC,KAAK;EAAM,KAAK;EAAS;CAAO,CACnC;CAEA,MAAM,cAAc,MAAM,SAAS,GAAG,KAAK,MAAM,KAAK,KAAK,WAAW;CAEtE,OACE,oBAAC,cAAD;EAAc,OAAO;EACnB,UAAA,qBAAC,OAAD;GACE,GAAI,WAAW,OAAO,OAAO,WAAW;IAAE;IAAa,GAAG;GAAM,CAAC;GACjE,SAAS,UAAU;IACjB,IAAI,CAAC,MAAM,cAAc,SAAS,MAAM,aAA4B,GAClE,KAAK,WAAW,KAAK;GACzB;GACA,UAAU,UAAU;IAClB,KAAK,WAAW,eAAe,MAAM,MAAM,CAAC;GAC9C;GACA,uBAAuB,UAAU;IAC/B,eAAe,UAAU,MAAM,eAAe;GAChD;GACA,iBAAiB,UAAU;IACzB,IAAI,MAAM,gBAAgB,SAAS,KAAK,MAAM;GAChD;GACA,gBAAgB,UAAU;IACxB,IAAI,MAAM,gBAAgB,SAAS,KAAK,KAAK;GAC/C;GAjBF,UAAA;IAmBE,oBAAC,SAAD;KACE,aAAA;KACA,UAAU,MAAM;KAChB,MAAM,MAAM;KACZ,QAAQ,MAAM;KACd,SAAS,MAAM;KACf,KAAK;KACL,KAAK,MAAM;KACX,SAAS,MAAM;KACf,SAAS,MAAM;KACf,SAAS,MAAM;KACf,QAAQ,MAAM;KACd,cAAc,MAAM;KACpB,GAAI,WAAW,OAAO,KAAK;IAC5B,CAAA;IACD,oBAAC,OAAD;KACE,sBAAmB;KACnB,GAAI,WAAW,OAAO,OAAO;KAC7B,qBAAqB,MAAM,iBAAiB;KAC5C,eAAe;MACb,IAAI,eAAe,YAAY,WAAW,CAAC,KAAK,SAAS;OACvD,KAAK,KAAK;OACV;MACF;MACA,IAAI,eAAe,YAAY,SAAS,KAAK,KAAK;MAClD,MAAM,OAAO;KACf;IACD,CAAA;IACA;GACE;;CACO,CAAA;AAElB;AAEA,MAAM,cAAc"}
@@ -0,0 +1,93 @@
1
+ "use client";
2
+ import { styleProps } from "../../styles/stylex/props.mjs";
3
+ import { markerColor, styles } from "../style.mjs";
4
+ import { formatTime } from "../time.mjs";
5
+ import { useEffect, useRef } from "react";
6
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
7
+ //#region src/Video/parts/HoverPreview.tsx
8
+ const Thumbnail = ({ src, time }) => {
9
+ const canvasRef = useRef(null);
10
+ const probeRef = useRef(null);
11
+ const pendingRef = useRef(null);
12
+ const busyRef = useRef(false);
13
+ useEffect(() => {
14
+ const probe = probeRef.current;
15
+ if (!probe) return;
16
+ const paint = () => {
17
+ const canvas = canvasRef.current;
18
+ try {
19
+ canvas?.getContext("2d")?.drawImage(probe, 0, 0, canvas.width, canvas.height);
20
+ } catch {}
21
+ busyRef.current = false;
22
+ if (pendingRef.current === null) return;
23
+ busyRef.current = true;
24
+ probe.currentTime = pendingRef.current;
25
+ pendingRef.current = null;
26
+ };
27
+ const reset = () => {
28
+ busyRef.current = false;
29
+ };
30
+ probe.addEventListener("seeked", paint);
31
+ probe.addEventListener("error", reset);
32
+ return () => {
33
+ probe.removeEventListener("seeked", paint);
34
+ probe.removeEventListener("error", reset);
35
+ probe.removeAttribute("src");
36
+ probe.load?.();
37
+ };
38
+ }, []);
39
+ useEffect(() => {
40
+ const probe = probeRef.current;
41
+ if (!probe) return;
42
+ if (busyRef.current) {
43
+ pendingRef.current = time;
44
+ return;
45
+ }
46
+ busyRef.current = true;
47
+ probe.currentTime = time;
48
+ }, [time]);
49
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("video", {
50
+ muted: true,
51
+ playsInline: true,
52
+ preload: "auto",
53
+ ref: probeRef,
54
+ src,
55
+ style: { display: "none" }
56
+ }), /* @__PURE__ */ jsx("canvas", {
57
+ height: 99,
58
+ ref: canvasRef,
59
+ width: 176,
60
+ ...styleProps(styles.previewCanvas)
61
+ })] });
62
+ };
63
+ const HoverPreview = ({ marker, section, src, thumbnail, time, visible, x }) => /* @__PURE__ */ jsxs("div", {
64
+ ...styleProps(styles.preview, void 0, {
65
+ display: visible ? void 0 : "none",
66
+ left: x
67
+ }),
68
+ children: [
69
+ thumbnail && /* @__PURE__ */ jsx(Thumbnail, {
70
+ src,
71
+ time
72
+ }),
73
+ (section || marker) && /* @__PURE__ */ jsxs("div", {
74
+ ...styleProps(styles.previewLabel),
75
+ children: [section && /* @__PURE__ */ jsx("div", {
76
+ ...styleProps(styles.previewTitle),
77
+ children: section.title
78
+ }), marker && /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("span", { ...styleProps(styles.dot, void 0, {
79
+ background: markerColor(marker.color),
80
+ display: "inline-block",
81
+ marginInlineEnd: 6
82
+ }) }), marker.content ?? marker.label] })]
83
+ }),
84
+ /* @__PURE__ */ jsx("div", {
85
+ ...styleProps(styles.previewTime),
86
+ children: formatTime(time)
87
+ })
88
+ ]
89
+ });
90
+ //#endregion
91
+ export { HoverPreview as default };
92
+
93
+ //# sourceMappingURL=HoverPreview.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HoverPreview.mjs","names":[],"sources":["../../../src/Video/parts/HoverPreview.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useRef } from 'react';\n\nimport { styleProps } from '@/styles/stylex/props';\n\nimport { markerColor, styles } from '../style';\nimport { formatTime } from '../time';\nimport type { VideoMarker, VideoSection } from '../type';\n\nexport interface HoverPreviewProps {\n marker?: VideoMarker;\n section?: VideoSection;\n src: string;\n thumbnail: boolean;\n time: number;\n visible: boolean;\n x: number;\n}\n\n// The frame comes from a hidden second <video>: seeking the one on screen\n// would move the playhead the viewer is holding. Seeks queue so a fast sweep\n// paints the latest position instead of stalling on stale frames.\nconst Thumbnail = ({ src, time }: { src: string; time: number }) => {\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const probeRef = useRef<HTMLVideoElement>(null);\n const pendingRef = useRef<number | null>(null);\n const busyRef = useRef(false);\n\n useEffect(() => {\n const probe = probeRef.current;\n if (!probe) return;\n const paint = () => {\n const canvas = canvasRef.current;\n try {\n canvas?.getContext('2d')?.drawImage(probe, 0, 0, canvas.width, canvas.height);\n } catch {\n // A frame that cannot be decoded yet leaves the previous paint in place.\n }\n busyRef.current = false;\n if (pendingRef.current === null) return;\n busyRef.current = true;\n probe.currentTime = pendingRef.current;\n pendingRef.current = null;\n };\n const reset = () => {\n busyRef.current = false;\n };\n probe.addEventListener('seeked', paint);\n probe.addEventListener('error', reset);\n return () => {\n probe.removeEventListener('seeked', paint);\n probe.removeEventListener('error', reset);\n // An unmounted element with a src keeps downloading until it is collected.\n probe.removeAttribute('src');\n probe.load?.();\n };\n }, []);\n\n useEffect(() => {\n const probe = probeRef.current;\n if (!probe) return;\n if (busyRef.current) {\n pendingRef.current = time;\n return;\n }\n busyRef.current = true;\n probe.currentTime = time;\n }, [time]);\n\n return (\n <>\n <video\n muted\n playsInline\n preload=\"auto\"\n ref={probeRef}\n src={src}\n style={{ display: 'none' }}\n />\n <canvas height={99} ref={canvasRef} width={176} {...styleProps(styles.previewCanvas)} />\n </>\n );\n};\n\nconst HoverPreview = ({ marker, section, src, thumbnail, time, visible, x }: HoverPreviewProps) => (\n <div\n {...styleProps(styles.preview, undefined, { display: visible ? undefined : 'none', left: x })}\n >\n {thumbnail && <Thumbnail src={src} time={time} />}\n {(section || marker) && (\n <div {...styleProps(styles.previewLabel)}>\n {section && <div {...styleProps(styles.previewTitle)}>{section.title}</div>}\n {marker && (\n <div>\n <span\n {...styleProps(styles.dot, undefined, {\n background: markerColor(marker.color),\n display: 'inline-block',\n marginInlineEnd: 6,\n })}\n />\n {marker.content ?? marker.label}\n </div>\n )}\n </div>\n )}\n <div {...styleProps(styles.previewTime)}>{formatTime(time)}</div>\n </div>\n);\n\nexport default HoverPreview;\n"],"mappings":";;;;;;;AAuBA,MAAM,aAAa,EAAE,KAAK,WAA0C;CAClE,MAAM,YAAY,OAA0B,IAAI;CAChD,MAAM,WAAW,OAAyB,IAAI;CAC9C,MAAM,aAAa,OAAsB,IAAI;CAC7C,MAAM,UAAU,OAAO,KAAK;CAE5B,gBAAgB;EACd,MAAM,QAAQ,SAAS;EACvB,IAAI,CAAC,OAAO;EACZ,MAAM,cAAc;GAClB,MAAM,SAAS,UAAU;GACzB,IAAI;IACF,QAAQ,WAAW,IAAI,CAAC,EAAE,UAAU,OAAO,GAAG,GAAG,OAAO,OAAO,OAAO,MAAM;GAC9E,QAAQ,CAER;GACA,QAAQ,UAAU;GAClB,IAAI,WAAW,YAAY,MAAM;GACjC,QAAQ,UAAU;GAClB,MAAM,cAAc,WAAW;GAC/B,WAAW,UAAU;EACvB;EACA,MAAM,cAAc;GAClB,QAAQ,UAAU;EACpB;EACA,MAAM,iBAAiB,UAAU,KAAK;EACtC,MAAM,iBAAiB,SAAS,KAAK;EACrC,aAAa;GACX,MAAM,oBAAoB,UAAU,KAAK;GACzC,MAAM,oBAAoB,SAAS,KAAK;GAExC,MAAM,gBAAgB,KAAK;GAC3B,MAAM,OAAO;EACf;CACF,GAAG,CAAC,CAAC;CAEL,gBAAgB;EACd,MAAM,QAAQ,SAAS;EACvB,IAAI,CAAC,OAAO;EACZ,IAAI,QAAQ,SAAS;GACnB,WAAW,UAAU;GACrB;EACF;EACA,QAAQ,UAAU;EAClB,MAAM,cAAc;CACtB,GAAG,CAAC,IAAI,CAAC;CAET,OACE,qBAAA,YAAA,EAAA,UAAA,CACE,oBAAC,SAAD;EACE,OAAA;EACA,aAAA;EACA,SAAQ;EACR,KAAK;EACA;EACL,OAAO,EAAE,SAAS,OAAO;CAC1B,CAAA,GACD,oBAAC,UAAD;EAAQ,QAAQ;EAAI,KAAK;EAAW,OAAO;EAAK,GAAI,WAAW,OAAO,aAAa;CAAI,CAAA,CACvF,EAAA,CAAA;AAEN;AAEA,MAAM,gBAAgB,EAAE,QAAQ,SAAS,KAAK,WAAW,MAAM,SAAS,QACtE,qBAAC,OAAD;CACE,GAAI,WAAW,OAAO,SAAS,KAAA,GAAW;EAAE,SAAS,UAAU,KAAA,IAAY;EAAQ,MAAM;CAAE,CAAC;CAD9F,UAAA;EAGG,aAAa,oBAAC,WAAD;GAAgB;GAAW;EAAO,CAAA;GAC9C,WAAW,WACX,qBAAC,OAAD;GAAK,GAAI,WAAW,OAAO,YAAY;GAAvC,UAAA,CACG,WAAW,oBAAC,OAAD;IAAK,GAAI,WAAW,OAAO,YAAY;IAAI,UAAA,QAAQ;GAAW,CAAA,GACzE,UACC,qBAAC,OAAD,EAAA,UAAA,CACE,oBAAC,QAAD,EACE,GAAI,WAAW,OAAO,KAAK,KAAA,GAAW;IACpC,YAAY,YAAY,OAAO,KAAK;IACpC,SAAS;IACT,iBAAiB;GACnB,CAAC,EACF,CAAA,GACA,OAAO,WAAW,OAAO,KACvB,EAAA,CAAA,CAEJ;;EAEP,oBAAC,OAAD;GAAK,GAAI,WAAW,OAAO,WAAW;GAAI,UAAA,WAAW,IAAI;EAAO,CAAA;CAC7D"}
@@ -0,0 +1,8 @@
1
+ //#region src/Video/parts/LoadError.d.ts
2
+ declare const LoadError: {
3
+ (): import("react").JSX.Element | null;
4
+ displayName: string;
5
+ };
6
+ //#endregion
7
+ export { LoadError as default };
8
+ //# sourceMappingURL=LoadError.d.mts.map
@@ -0,0 +1,47 @@
1
+ "use client";
2
+ import { styleProps } from "../../styles/stylex/props.mjs";
3
+ import { useTranslation } from "../../i18n/useTranslation.mjs";
4
+ import { useVideoContext } from "../context.mjs";
5
+ import { styles } from "../style.mjs";
6
+ import { useVideo } from "../useVideo.mjs";
7
+ import video_default from "../../i18n/resources/en/video.mjs";
8
+ import { jsx, jsxs } from "react/jsx-runtime";
9
+ import { AlertTriangle, Download, RefreshCw } from "lucide-react";
10
+ //#region src/Video/parts/LoadError.tsx
11
+ const LoadError = () => {
12
+ const { config, media, store } = useVideoContext();
13
+ const { t } = useTranslation(video_default);
14
+ if (!useVideo((state) => state.status === "error")) return null;
15
+ return /* @__PURE__ */ jsxs("div", {
16
+ role: "alert",
17
+ ...styleProps(styles.error),
18
+ onClick: (event) => event.stopPropagation(),
19
+ children: [
20
+ /* @__PURE__ */ jsx(AlertTriangle, { size: 22 }),
21
+ /* @__PURE__ */ jsx("span", { children: t("video.loadFailed") }),
22
+ /* @__PURE__ */ jsxs("div", {
23
+ ...styleProps(styles.errorActions),
24
+ children: [/* @__PURE__ */ jsxs("button", {
25
+ type: "button",
26
+ ...styleProps(styles.errorButton),
27
+ onClick: async () => {
28
+ await config.onRetry?.();
29
+ store.reload();
30
+ },
31
+ children: [/* @__PURE__ */ jsx(RefreshCw, { size: 14 }), t("video.reload")]
32
+ }), /* @__PURE__ */ jsxs("a", {
33
+ href: media.src,
34
+ rel: "noreferrer",
35
+ target: "_blank",
36
+ ...styleProps(styles.errorButton),
37
+ children: [/* @__PURE__ */ jsx(Download, { size: 14 }), t("video.download")]
38
+ })]
39
+ })
40
+ ]
41
+ });
42
+ };
43
+ LoadError.displayName = "VideoLoadError";
44
+ //#endregion
45
+ export { LoadError as default };
46
+
47
+ //# sourceMappingURL=LoadError.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LoadError.mjs","names":["videoMessages"],"sources":["../../../src/Video/parts/LoadError.tsx"],"sourcesContent":["'use client';\n\nimport { AlertTriangle, Download, RefreshCw } from 'lucide-react';\n\nimport videoMessages from '@/i18n/resources/en/video';\nimport { useTranslation } from '@/i18n/useTranslation';\nimport { styleProps } from '@/styles/stylex/props';\n\nimport { useVideoContext } from '../context';\nimport { styles } from '../style';\nimport { useVideo } from '../useVideo';\n\nconst LoadError = () => {\n const { config, media, store } = useVideoContext();\n const { t } = useTranslation(videoMessages);\n const failed = useVideo((state) => state.status === 'error');\n if (!failed) return null;\n\n return (\n <div role=\"alert\" {...styleProps(styles.error)} onClick={(event) => event.stopPropagation()}>\n <AlertTriangle size={22} />\n <span>{t('video.loadFailed')}</span>\n <div {...styleProps(styles.errorActions)}>\n <button\n type=\"button\"\n {...styleProps(styles.errorButton)}\n onClick={async () => {\n await config.onRetry?.();\n store.reload();\n }}\n >\n <RefreshCw size={14} />\n {t('video.reload')}\n </button>\n <a href={media.src} rel=\"noreferrer\" target=\"_blank\" {...styleProps(styles.errorButton)}>\n <Download size={14} />\n {t('video.download')}\n </a>\n </div>\n </div>\n );\n};\n\nLoadError.displayName = 'VideoLoadError';\n\nexport default LoadError;\n"],"mappings":";;;;;;;;;;AAYA,MAAM,kBAAkB;CACtB,MAAM,EAAE,QAAQ,OAAO,UAAU,gBAAgB;CACjD,MAAM,EAAE,MAAM,eAAeA,aAAa;CAE1C,IAAI,CADW,UAAU,UAAU,MAAM,WAAW,OAC1C,GAAG,OAAO;CAEpB,OACE,qBAAC,OAAD;EAAK,MAAK;EAAQ,GAAI,WAAW,OAAO,KAAK;EAAG,UAAU,UAAU,MAAM,gBAAgB;EAA1F,UAAA;GACE,oBAAC,eAAD,EAAe,MAAM,GAAK,CAAA;GAC1B,oBAAC,QAAD,EAAA,UAAO,EAAE,kBAAkB,EAAQ,CAAA;GACnC,qBAAC,OAAD;IAAK,GAAI,WAAW,OAAO,YAAY;IAAvC,UAAA,CACE,qBAAC,UAAD;KACE,MAAK;KACL,GAAI,WAAW,OAAO,WAAW;KACjC,SAAS,YAAY;MACnB,MAAM,OAAO,UAAU;MACvB,MAAM,OAAO;KACf;KANF,UAAA,CAQE,oBAAC,WAAD,EAAW,MAAM,GAAK,CAAA,GACrB,EAAE,cAAc,CACX;IACR,CAAA,GAAA,qBAAC,KAAD;KAAG,MAAM,MAAM;KAAK,KAAI;KAAa,QAAO;KAAS,GAAI,WAAW,OAAO,WAAW;KAAtF,UAAA,CACE,oBAAC,UAAD,EAAU,MAAM,GAAK,CAAA,GACpB,EAAE,gBAAgB,CAClB;IACA,CAAA,CAAA;;EACF;;AAET;AAEA,UAAU,cAAc"}
@@ -0,0 +1,48 @@
1
+ import { styleProps } from "../../styles/stylex/props.mjs";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ //#region src/Video/parts/PlayPauseIcon.tsx
4
+ const styles = {
5
+ half: {
6
+ kWkggS: "lbtwfq29",
7
+ kpwlN0: "lb10a8y8t",
8
+ kVAEAm: "lb10l6tqk",
9
+ kmkexE: "lbdn3phn lb9kvfbb",
10
+ $$css: true
11
+ },
12
+ leftPause: {
13
+ kz4h6p: "lb54tv3e",
14
+ $$css: true
15
+ },
16
+ leftPlay: {
17
+ kz4h6p: "lbw0i5o0",
18
+ $$css: true
19
+ },
20
+ rightPause: {
21
+ kz4h6p: "lbn4v9xm",
22
+ $$css: true
23
+ },
24
+ rightPlay: {
25
+ kz4h6p: "lb1qujkey",
26
+ $$css: true
27
+ },
28
+ root: {
29
+ k1xSpc: "lb1rg5ohu",
30
+ kUk6DE: "lb1okw0bk",
31
+ kVAEAm: "lb1n2onr6",
32
+ $$css: true
33
+ }
34
+ };
35
+ const PlayPauseIcon = ({ paused, size = 16 }) => /* @__PURE__ */ jsxs("span", {
36
+ "aria-hidden": "true",
37
+ "data-state": paused ? "play" : "pause",
38
+ "data-video-play-icon": "",
39
+ ...styleProps(styles.root, void 0, {
40
+ height: size,
41
+ width: size
42
+ }),
43
+ children: [/* @__PURE__ */ jsx("span", { ...styleProps([styles.half, paused ? styles.leftPlay : styles.leftPause]) }), /* @__PURE__ */ jsx("span", { ...styleProps([styles.half, paused ? styles.rightPlay : styles.rightPause]) })]
44
+ });
45
+ //#endregion
46
+ export { PlayPauseIcon as default };
47
+
48
+ //# sourceMappingURL=PlayPauseIcon.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PlayPauseIcon.mjs","names":[],"sources":["../../../src/Video/parts/PlayPauseIcon.tsx"],"sourcesContent":["import * as stylex from '@stylexjs/stylex';\n\nimport { styleProps } from '@/styles/stylex/props';\n\nconst reducedMotion = '@media (prefers-reduced-motion: reduce)';\n\n// Each half keeps four points in both states (TL, TR, BR, BL), so clip-path\n// interpolates the pause bars into the two halves of the play triangle.\nconst styles = stylex.create({\n half: {\n backgroundColor: 'currentColor',\n inset: 0,\n position: 'absolute',\n transition: {\n default: 'clip-path 200ms cubic-bezier(0.4, 0, 0.2, 1)',\n [reducedMotion]: 'none',\n },\n },\n leftPause: {\n clipPath: 'polygon(19% 12%, 41% 12%, 41% 88%, 19% 88%)',\n },\n leftPlay: {\n clipPath: 'polygon(22% 10%, 55% 30%, 55% 70%, 22% 90%)',\n },\n rightPause: {\n clipPath: 'polygon(59% 12%, 81% 12%, 81% 88%, 59% 88%)',\n },\n rightPlay: {\n clipPath: 'polygon(55% 30%, 88% 50%, 88% 50%, 55% 70%)',\n },\n root: {\n display: 'inline-block',\n flex: 'none',\n position: 'relative',\n },\n});\n\nconst PlayPauseIcon = ({ paused, size = 16 }: { paused: boolean; size?: number }) => (\n <span\n aria-hidden=\"true\"\n data-state={paused ? 'play' : 'pause'}\n data-video-play-icon=\"\"\n {...styleProps(styles.root, undefined, { height: size, width: size })}\n >\n <span {...styleProps([styles.half, paused ? styles.leftPlay : styles.leftPause])} />\n <span {...styleProps([styles.half, paused ? styles.rightPlay : styles.rightPause])} />\n </span>\n);\n\nexport default PlayPauseIcon;\n"],"mappings":";;;AAMA,MAAM,SAAS;CACb,MAAM;EACJ,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,OAAO;CACT;CACA,WAAW;EACT,QAAQ;EACR,OAAO;CACT;CACA,UAAU;EACR,QAAQ;EACR,OAAO;CACT;CACA,YAAY;EACV,QAAQ;EACR,OAAO;CACT;CACA,WAAW;EACT,QAAQ;EACR,OAAO;CACT;CACA,MAAM;EACJ,QAAQ;EACR,QAAQ;EACR,QAAQ;EACR,OAAO;CACT;AACF;AACA,MAAM,iBAAiB,EACrB,QACA,OAAO,SAIH,qBAAC,QAAD;CAAM,eAAY;CAAO,cAAY,SAAS,SAAS;CAAS,wBAAqB;CAAG,GAAI,WAAW,OAAO,MAAM,KAAA,GAAW;EACnI,QAAQ;EACR,OAAO;CACT,CAAC;CAHK,UAAA,CAIF,oBAAC,QAAD,EAAM,GAAI,WAAW,CAAC,OAAO,MAAM,SAAS,OAAO,WAAW,OAAO,SAAS,CAAC,EAAI,CAAA,GACnF,oBAAC,QAAD,EAAM,GAAI,WAAW,CAAC,OAAO,MAAM,SAAS,OAAO,YAAY,OAAO,UAAU,CAAC,EAAI,CAAA,CACjF"}
@@ -0,0 +1,48 @@
1
+ "use client";
2
+ import { styleProps } from "../../styles/stylex/props.mjs";
3
+ import DropdownMenu from "../../DropdownMenu/DropdownMenu.mjs";
4
+ import { useTranslation } from "../../i18n/useTranslation.mjs";
5
+ import { FrameContext, useVideoContext } from "../context.mjs";
6
+ import { styles } from "../style.mjs";
7
+ import { useVideo } from "../useVideo.mjs";
8
+ import video_default from "../../i18n/resources/en/video.mjs";
9
+ import { useFullscreen } from "../useFullscreen.mjs";
10
+ import { DEFAULT_PLAYBACK_RATES } from "../useKeyboard.mjs";
11
+ import { use } from "react";
12
+ import { jsx, jsxs } from "react/jsx-runtime";
13
+ import { Check } from "lucide-react";
14
+ //#region src/Video/parts/RateMenu.tsx
15
+ const RateMenu = () => {
16
+ const { config, store } = useVideoContext();
17
+ const frame = use(FrameContext);
18
+ const fullscreen = useFullscreen(store);
19
+ const { t } = useTranslation(video_default);
20
+ const rate = useVideo((state) => state.rate);
21
+ const rates = config.playbackRates ?? DEFAULT_PLAYBACK_RATES;
22
+ return /* @__PURE__ */ jsx(DropdownMenu, {
23
+ portalProps: fullscreen && store.root ? { container: store.root } : void 0,
24
+ items: rates.map((value) => ({
25
+ extra: value === rate ? /* @__PURE__ */ jsx(Check, { size: 14 }) : void 0,
26
+ key: `${value}`,
27
+ label: `${value}×`,
28
+ onClick: () => store.setRate(value)
29
+ })),
30
+ onOpenChange: (open) => frame?.hold(open),
31
+ children: /* @__PURE__ */ jsxs("button", {
32
+ "aria-label": t("video.speed"),
33
+ title: `${t("video.speed")} (J / L)`,
34
+ type: "button",
35
+ ...styleProps([
36
+ styles.barButton,
37
+ styles.rateButton,
38
+ rate !== 1 && styles.barButtonActive
39
+ ]),
40
+ children: [rate, "×"]
41
+ })
42
+ });
43
+ };
44
+ RateMenu.displayName = "VideoRateMenu";
45
+ //#endregion
46
+ export { RateMenu as default };
47
+
48
+ //# sourceMappingURL=RateMenu.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RateMenu.mjs","names":["videoMessages"],"sources":["../../../src/Video/parts/RateMenu.tsx"],"sourcesContent":["'use client';\n\nimport { Check } from 'lucide-react';\nimport { use } from 'react';\n\nimport DropdownMenu from '@/DropdownMenu';\nimport videoMessages from '@/i18n/resources/en/video';\nimport { useTranslation } from '@/i18n/useTranslation';\nimport { styleProps } from '@/styles/stylex/props';\n\nimport { FrameContext, useVideoContext } from '../context';\nimport { styles } from '../style';\nimport { useFullscreen } from '../useFullscreen';\nimport { DEFAULT_PLAYBACK_RATES } from '../useKeyboard';\nimport { useVideo } from '../useVideo';\n\nconst RateMenu = () => {\n const { config, store } = useVideoContext();\n const frame = use(FrameContext);\n const fullscreen = useFullscreen(store);\n const { t } = useTranslation(videoMessages);\n const rate = useVideo((state) => state.rate);\n const rates = config.playbackRates ?? DEFAULT_PLAYBACK_RATES;\n\n return (\n <DropdownMenu\n portalProps={fullscreen && store.root ? { container: store.root } : undefined}\n items={rates.map((value) => ({\n extra: value === rate ? <Check size={14} /> : undefined,\n key: `${value}`,\n label: `${value}×`,\n onClick: () => store.setRate(value),\n }))}\n onOpenChange={(open) => frame?.hold(open)}\n >\n <button\n aria-label={t('video.speed')}\n title={`${t('video.speed')} (J / L)`}\n type=\"button\"\n {...styleProps([styles.barButton, styles.rateButton, rate !== 1 && styles.barButtonActive])}\n >\n {rate}×\n </button>\n </DropdownMenu>\n );\n};\n\nRateMenu.displayName = 'VideoRateMenu';\n\nexport default RateMenu;\n"],"mappings":";;;;;;;;;;;;;;AAgBA,MAAM,iBAAiB;CACrB,MAAM,EAAE,QAAQ,UAAU,gBAAgB;CAC1C,MAAM,QAAQ,IAAI,YAAY;CAC9B,MAAM,aAAa,cAAc,KAAK;CACtC,MAAM,EAAE,MAAM,eAAeA,aAAa;CAC1C,MAAM,OAAO,UAAU,UAAU,MAAM,IAAI;CAC3C,MAAM,QAAQ,OAAO,iBAAiB;CAEtC,OACE,oBAAC,cAAD;EACE,aAAa,cAAc,MAAM,OAAO,EAAE,WAAW,MAAM,KAAK,IAAI,KAAA;EACpE,OAAO,MAAM,KAAK,WAAW;GAC3B,OAAO,UAAU,OAAO,oBAAC,OAAD,EAAO,MAAM,GAAK,CAAA,IAAI,KAAA;GAC9C,KAAK,GAAG;GACR,OAAO,GAAG,MAAM;GAChB,eAAe,MAAM,QAAQ,KAAK;EACpC,EAAE;EACF,eAAe,SAAS,OAAO,KAAK,IAAI;EAExC,UAAA,qBAAC,UAAD;GACE,cAAY,EAAE,aAAa;GAC3B,OAAO,GAAG,EAAE,aAAa,EAAE;GAC3B,MAAK;GACL,GAAI,WAAW;IAAC,OAAO;IAAW,OAAO;IAAY,SAAS,KAAK,OAAO;GAAe,CAAC;GAJ5F,UAAA,CAMG,MAAK,GACA;;CACI,CAAA;AAElB;AAEA,SAAS,cAAc"}
@@ -0,0 +1,9 @@
1
+ import { VideoRootProps } from "../type.mjs";
2
+ //#region src/Video/parts/Root.d.ts
3
+ declare const Root: {
4
+ ({ ref, player, src, poster, autoPlay, loop, preload, onPlay, onPause, onEnded, onError, onTimeUpdate, sections, markers, fps, playbackRates, defaultPlaybackRate, muted, onMutedChange, onRateChange, onMarkerClick, onRetry, captions, width, height, maxHeight, className, style, children }: VideoRootProps): import("react").JSX.Element;
5
+ displayName: string;
6
+ };
7
+ //#endregion
8
+ export { Root as default };
9
+ //# sourceMappingURL=Root.d.mts.map
@@ -0,0 +1,110 @@
1
+ "use client";
2
+ import { styleProps } from "../../styles/stylex/props.mjs";
3
+ import { VideoContext } from "../context.mjs";
4
+ import { styles } from "../style.mjs";
5
+ import { sortSections } from "../time.mjs";
6
+ import { createVideoStore } from "../store.mjs";
7
+ import { useVideoState } from "../useVideo.mjs";
8
+ import { handleVideoKey } from "../useKeyboard.mjs";
9
+ import { useImperativeHandle, useLayoutEffect, useMemo, useState } from "react";
10
+ import { jsx } from "react/jsx-runtime";
11
+ //#region src/Video/parts/Root.tsx
12
+ const NO_MARKERS = [];
13
+ const NO_SECTIONS = [];
14
+ const Root = ({ ref, player, src, poster, autoPlay, loop, preload = "metadata", onPlay, onPause, onEnded, onError, onTimeUpdate, sections = NO_SECTIONS, markers = NO_MARKERS, fps = 30, playbackRates, defaultPlaybackRate, muted, onMutedChange, onRateChange, onMarkerClick, onRetry, captions = true, width, height, maxHeight, className, style, children }) => {
15
+ const [ownStore] = useState(createVideoStore);
16
+ const store = player ?? ownStore;
17
+ store.setOptions({
18
+ defaultPlaybackRate,
19
+ muted,
20
+ onMutedChange,
21
+ onRateChange
22
+ });
23
+ useImperativeHandle(ref, () => store, [store]);
24
+ const sorted = useMemo(() => sortSections(sections), [sections]);
25
+ useLayoutEffect(() => {
26
+ store.setData({
27
+ fps,
28
+ markers,
29
+ sections: sorted
30
+ });
31
+ }, [
32
+ store,
33
+ fps,
34
+ markers,
35
+ sorted
36
+ ]);
37
+ useLayoutEffect(() => {
38
+ if (!captions) store.setCaptions(false);
39
+ }, [store]);
40
+ const size = useVideoState(store, (state) => state.size);
41
+ const aspect = size?.height ? size.width / size.height : 16 / 9;
42
+ const sizing = maxHeight === void 0 ? {
43
+ height,
44
+ width
45
+ } : {
46
+ maxWidth: `${Math.round(maxHeight * aspect)}px`,
47
+ width: "100%"
48
+ };
49
+ const value = useMemo(() => ({
50
+ config: {
51
+ fps,
52
+ markers,
53
+ onMarkerClick,
54
+ onRetry,
55
+ playbackRates,
56
+ sections: sorted
57
+ },
58
+ media: {
59
+ autoPlay,
60
+ loop,
61
+ onEnded,
62
+ onError,
63
+ onPause,
64
+ onPlay,
65
+ onTimeUpdate,
66
+ poster,
67
+ preload,
68
+ src
69
+ },
70
+ store
71
+ }), [
72
+ autoPlay,
73
+ fps,
74
+ loop,
75
+ markers,
76
+ onEnded,
77
+ onError,
78
+ onMarkerClick,
79
+ onPause,
80
+ onPlay,
81
+ onRetry,
82
+ onTimeUpdate,
83
+ playbackRates,
84
+ poster,
85
+ preload,
86
+ sorted,
87
+ src,
88
+ store
89
+ ]);
90
+ return /* @__PURE__ */ jsx(VideoContext, {
91
+ value,
92
+ children: /* @__PURE__ */ jsx("div", {
93
+ ref: store.attachRoot,
94
+ tabIndex: 0,
95
+ onKeyDown: (event) => {
96
+ if (handleVideoKey(event, store, playbackRates)) event.preventDefault();
97
+ },
98
+ ...styleProps(styles.root, className, {
99
+ ...sizing,
100
+ ...style
101
+ }),
102
+ children
103
+ })
104
+ });
105
+ };
106
+ Root.displayName = "VideoRoot";
107
+ //#endregion
108
+ export { Root as default };
109
+
110
+ //# sourceMappingURL=Root.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Root.mjs","names":[],"sources":["../../../src/Video/parts/Root.tsx"],"sourcesContent":["'use client';\n\nimport { useImperativeHandle, useLayoutEffect, useMemo, useState } from 'react';\n\nimport { styleProps } from '@/styles/stylex/props';\n\nimport { VideoContext, type VideoContextValue } from '../context';\nimport { createVideoStore, type VideoStore } from '../store';\nimport { styles } from '../style';\nimport { sortSections } from '../time';\nimport type { VideoMarker, VideoRootProps, VideoSection } from '../type';\nimport { handleVideoKey } from '../useKeyboard';\nimport { useVideoState } from '../useVideo';\n\nconst NO_MARKERS: VideoMarker[] = [];\nconst NO_SECTIONS: VideoSection[] = [];\n\nconst Root = ({\n ref,\n player,\n src,\n poster,\n autoPlay,\n loop,\n preload = 'metadata',\n onPlay,\n onPause,\n onEnded,\n onError,\n onTimeUpdate,\n sections = NO_SECTIONS,\n markers = NO_MARKERS,\n fps = 30,\n playbackRates,\n defaultPlaybackRate,\n muted,\n onMutedChange,\n onRateChange,\n onMarkerClick,\n onRetry,\n captions = true,\n width,\n height,\n maxHeight,\n className,\n style,\n children,\n}: VideoRootProps) => {\n const [ownStore] = useState(createVideoStore);\n const store = (player as VideoStore | undefined) ?? ownStore;\n\n // Assigned during render so the store already has them when a child Frame\n // attaches its <video> in an effect, which runs before this component's own.\n store.setOptions({ defaultPlaybackRate, muted, onMutedChange, onRateChange });\n\n useImperativeHandle(ref, () => store, [store]);\n\n const sorted = useMemo(() => sortSections(sections), [sections]);\n\n useLayoutEffect(() => {\n store.setData({ fps, markers, sections: sorted });\n }, [store, fps, markers, sorted]);\n\n useLayoutEffect(() => {\n if (!captions) store.setCaptions(false);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [store]);\n\n const size = useVideoState(store, (state) => state.size);\n const aspect = size?.height ? size.width / size.height : 16 / 9;\n const sizing =\n maxHeight === undefined\n ? { height, width }\n : { maxWidth: `${Math.round(maxHeight * aspect)}px`, width: '100%' };\n\n const value = useMemo<VideoContextValue>(\n () => ({\n config: { fps, markers, onMarkerClick, onRetry, playbackRates, sections: sorted },\n media: {\n autoPlay,\n loop,\n onEnded,\n onError,\n onPause,\n onPlay,\n onTimeUpdate,\n poster,\n preload,\n src,\n },\n store,\n }),\n [\n autoPlay,\n fps,\n loop,\n markers,\n onEnded,\n onError,\n onMarkerClick,\n onPause,\n onPlay,\n onRetry,\n onTimeUpdate,\n playbackRates,\n poster,\n preload,\n sorted,\n src,\n store,\n ],\n );\n\n return (\n <VideoContext value={value}>\n <div\n ref={store.attachRoot}\n tabIndex={0}\n onKeyDown={(event) => {\n if (handleVideoKey(event, store, playbackRates)) event.preventDefault();\n }}\n {...styleProps(styles.root, className, { ...sizing, ...style })}\n >\n {children}\n </div>\n </VideoContext>\n );\n};\n\nRoot.displayName = 'VideoRoot';\n\nexport default Root;\n"],"mappings":";;;;;;;;;;;AAcA,MAAM,aAA4B,CAAC;AACnC,MAAM,cAA8B,CAAC;AAErC,MAAM,QAAQ,EACZ,KACA,QACA,KACA,QACA,UACA,MACA,UAAU,YACV,QACA,SACA,SACA,SACA,cACA,WAAW,aACX,UAAU,YACV,MAAM,IACN,eACA,qBACA,OACA,eACA,cACA,eACA,SACA,WAAW,MACX,OACA,QACA,WACA,WACA,OACA,eACoB;CACpB,MAAM,CAAC,YAAY,SAAS,gBAAgB;CAC5C,MAAM,QAAS,UAAqC;CAIpD,MAAM,WAAW;EAAE;EAAqB;EAAO;EAAe;CAAa,CAAC;CAE5E,oBAAoB,WAAW,OAAO,CAAC,KAAK,CAAC;CAE7C,MAAM,SAAS,cAAc,aAAa,QAAQ,GAAG,CAAC,QAAQ,CAAC;CAE/D,sBAAsB;EACpB,MAAM,QAAQ;GAAE;GAAK;GAAS,UAAU;EAAO,CAAC;CAClD,GAAG;EAAC;EAAO;EAAK;EAAS;CAAM,CAAC;CAEhC,sBAAsB;EACpB,IAAI,CAAC,UAAU,MAAM,YAAY,KAAK;CAExC,GAAG,CAAC,KAAK,CAAC;CAEV,MAAM,OAAO,cAAc,QAAQ,UAAU,MAAM,IAAI;CACvD,MAAM,SAAS,MAAM,SAAS,KAAK,QAAQ,KAAK,SAAS,KAAK;CAC9D,MAAM,SACJ,cAAc,KAAA,IACV;EAAE;EAAQ;CAAM,IAChB;EAAE,UAAU,GAAG,KAAK,MAAM,YAAY,MAAM,EAAE;EAAK,OAAO;CAAO;CAEvE,MAAM,QAAQ,eACL;EACL,QAAQ;GAAE;GAAK;GAAS;GAAe;GAAS;GAAe,UAAU;EAAO;EAChF,OAAO;GACL;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;EACF;EACA;CACF,IACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CACF;CAEA,OACE,oBAAC,cAAD;EAAqB;EACnB,UAAA,oBAAC,OAAD;GACE,KAAK,MAAM;GACX,UAAU;GACV,YAAY,UAAU;IACpB,IAAI,eAAe,OAAO,OAAO,aAAa,GAAG,MAAM,eAAe;GACxE;GACA,GAAI,WAAW,OAAO,MAAM,WAAW;IAAE,GAAG;IAAQ,GAAG;GAAM,CAAC;GAE7D;EACE,CAAA;CACO,CAAA;AAElB;AAEA,KAAK,cAAc"}
@@ -0,0 +1,9 @@
1
+ import { VideoSectionListProps } from "../type.mjs";
2
+ //#region src/Video/parts/SectionList.d.ts
3
+ declare const SectionList: {
4
+ ({ className, style }: VideoSectionListProps): import("react").JSX.Element | null;
5
+ displayName: string;
6
+ };
7
+ //#endregion
8
+ export { SectionList as default };
9
+ //# sourceMappingURL=SectionList.d.mts.map
@@ -0,0 +1,32 @@
1
+ "use client";
2
+ import { styleProps } from "../../styles/stylex/props.mjs";
3
+ import { useVideoContext } from "../context.mjs";
4
+ import { styles } from "../style.mjs";
5
+ import { formatTime } from "../time.mjs";
6
+ import { useVideo } from "../useVideo.mjs";
7
+ import { jsx, jsxs } from "react/jsx-runtime";
8
+ //#region src/Video/parts/SectionList.tsx
9
+ const SectionList = ({ className, style }) => {
10
+ const { config, store } = useVideoContext();
11
+ const active = useVideo((state) => state.activeSection);
12
+ if (config.sections.length === 0) return null;
13
+ return /* @__PURE__ */ jsx("div", {
14
+ ...styleProps(styles.sectionList, className, style),
15
+ children: config.sections.map((section) => /* @__PURE__ */ jsxs("button", {
16
+ "aria-current": section === active ? "true" : void 0,
17
+ "data-video-section": "",
18
+ type: "button",
19
+ ...styleProps([styles.sectionChip, section === active && styles.sectionChipActive]),
20
+ onClick: () => store.seek(section.start),
21
+ children: [/* @__PURE__ */ jsx("span", {
22
+ ...styleProps(styles.sectionChipTime),
23
+ children: formatTime(section.start)
24
+ }), section.title]
25
+ }, section.start))
26
+ });
27
+ };
28
+ SectionList.displayName = "VideoSectionList";
29
+ //#endregion
30
+ export { SectionList as default };
31
+
32
+ //# sourceMappingURL=SectionList.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SectionList.mjs","names":[],"sources":["../../../src/Video/parts/SectionList.tsx"],"sourcesContent":["'use client';\n\nimport { styleProps } from '@/styles/stylex/props';\n\nimport { useVideoContext } from '../context';\nimport { styles } from '../style';\nimport { formatTime } from '../time';\nimport type { VideoSectionListProps } from '../type';\nimport { useVideo } from '../useVideo';\n\nconst SectionList = ({ className, style }: VideoSectionListProps) => {\n const { config, store } = useVideoContext();\n const active = useVideo((state) => state.activeSection);\n if (config.sections.length === 0) return null;\n\n return (\n <div {...styleProps(styles.sectionList, className, style)}>\n {config.sections.map((section) => (\n <button\n aria-current={section === active ? 'true' : undefined}\n data-video-section=\"\"\n key={section.start}\n type=\"button\"\n {...styleProps([styles.sectionChip, section === active && styles.sectionChipActive])}\n onClick={() => store.seek(section.start)}\n >\n <span {...styleProps(styles.sectionChipTime)}>{formatTime(section.start)}</span>\n {section.title}\n </button>\n ))}\n </div>\n );\n};\n\nSectionList.displayName = 'VideoSectionList';\n\nexport default SectionList;\n"],"mappings":";;;;;;;;AAUA,MAAM,eAAe,EAAE,WAAW,YAAmC;CACnE,MAAM,EAAE,QAAQ,UAAU,gBAAgB;CAC1C,MAAM,SAAS,UAAU,UAAU,MAAM,aAAa;CACtD,IAAI,OAAO,SAAS,WAAW,GAAG,OAAO;CAEzC,OACE,oBAAC,OAAD;EAAK,GAAI,WAAW,OAAO,aAAa,WAAW,KAAK;EACrD,UAAA,OAAO,SAAS,KAAK,YACpB,qBAAC,UAAD;GACE,gBAAc,YAAY,SAAS,SAAS,KAAA;GAC5C,sBAAmB;GAEnB,MAAK;GACL,GAAI,WAAW,CAAC,OAAO,aAAa,YAAY,UAAU,OAAO,iBAAiB,CAAC;GACnF,eAAe,MAAM,KAAK,QAAQ,KAAK;GANzC,UAAA,CAQE,oBAAC,QAAD;IAAM,GAAI,WAAW,OAAO,eAAe;IAAI,UAAA,WAAW,QAAQ,KAAK;GAAQ,CAAA,GAC9E,QAAQ,KACH;EAPD,GAAA,QAAQ,KAOP,CACT;CACE,CAAA;AAET;AAEA,YAAY,cAAc"}
@@ -0,0 +1,63 @@
1
+ "use client";
2
+ import { styleProps } from "../../styles/stylex/props.mjs";
3
+ import DropdownMenu from "../../DropdownMenu/DropdownMenu.mjs";
4
+ import { useTranslation } from "../../i18n/useTranslation.mjs";
5
+ import { FrameContext, useVideoContext } from "../context.mjs";
6
+ import { styles } from "../style.mjs";
7
+ import { formatTime } from "../time.mjs";
8
+ import { useVideo } from "../useVideo.mjs";
9
+ import video_default from "../../i18n/resources/en/video.mjs";
10
+ import { useFullscreen } from "../useFullscreen.mjs";
11
+ import { use, useState } from "react";
12
+ import { jsx, jsxs } from "react/jsx-runtime";
13
+ import { Check, ChevronRight } from "lucide-react";
14
+ //#region src/Video/parts/SectionMenu.tsx
15
+ const SectionMenu = () => {
16
+ const { config, store } = useVideoContext();
17
+ const frame = use(FrameContext);
18
+ const fullscreen = useFullscreen(store);
19
+ const { t } = useTranslation(video_default);
20
+ const active = useVideo((state) => state.activeSection);
21
+ const [shown, setShown] = useState({
22
+ direction: "none",
23
+ section: active
24
+ });
25
+ if (shown.section !== active) {
26
+ const from = shown.section ? config.sections.indexOf(shown.section) : -1;
27
+ setShown({
28
+ direction: (active ? config.sections.indexOf(active) : -1) >= from ? "forward" : "backward",
29
+ section: active
30
+ });
31
+ }
32
+ if (config.sections.length === 0) return null;
33
+ return /* @__PURE__ */ jsx(DropdownMenu, {
34
+ portalProps: fullscreen && store.root ? { container: store.root } : void 0,
35
+ items: config.sections.map((section) => ({
36
+ extra: section === active ? /* @__PURE__ */ jsx(Check, { size: 14 }) : formatTime(section.start),
37
+ key: `${section.start}`,
38
+ label: section.title,
39
+ onClick: () => store.seek(section.start)
40
+ })),
41
+ onOpenChange: (open) => frame?.hold(open),
42
+ children: /* @__PURE__ */ jsxs("button", {
43
+ title: t("video.sections"),
44
+ type: "button",
45
+ ...styleProps(styles.chip),
46
+ children: [/* @__PURE__ */ jsx("span", {
47
+ "data-direction": shown.direction,
48
+ "data-video-section-label": "",
49
+ ...styleProps([
50
+ styles.chipText,
51
+ shown.direction === "forward" && styles.chipLabelForward,
52
+ shown.direction === "backward" && styles.chipLabelBackward
53
+ ]),
54
+ children: active?.title ?? t("video.sections")
55
+ }, active?.start ?? "none"), /* @__PURE__ */ jsx(ChevronRight, { size: 14 })]
56
+ })
57
+ });
58
+ };
59
+ SectionMenu.displayName = "VideoSectionMenu";
60
+ //#endregion
61
+ export { SectionMenu as default };
62
+
63
+ //# sourceMappingURL=SectionMenu.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SectionMenu.mjs","names":["videoMessages"],"sources":["../../../src/Video/parts/SectionMenu.tsx"],"sourcesContent":["'use client';\n\nimport { Check, ChevronRight } from 'lucide-react';\nimport { use, useState } from 'react';\n\nimport DropdownMenu from '@/DropdownMenu';\nimport videoMessages from '@/i18n/resources/en/video';\nimport { useTranslation } from '@/i18n/useTranslation';\nimport { styleProps } from '@/styles/stylex/props';\n\nimport { FrameContext, useVideoContext } from '../context';\nimport { styles } from '../style';\nimport { formatTime } from '../time';\nimport { useFullscreen } from '../useFullscreen';\nimport { useVideo } from '../useVideo';\n\nconst SectionMenu = () => {\n const { config, store } = useVideoContext();\n const frame = use(FrameContext);\n const fullscreen = useFullscreen(store);\n const { t } = useTranslation(videoMessages);\n const active = useVideo((state) => state.activeSection);\n const [shown, setShown] = useState({ direction: 'none', section: active });\n if (shown.section !== active) {\n const from = shown.section ? config.sections.indexOf(shown.section) : -1;\n const to = active ? config.sections.indexOf(active) : -1;\n setShown({ direction: to >= from ? 'forward' : 'backward', section: active });\n }\n if (config.sections.length === 0) return null;\n\n return (\n <DropdownMenu\n portalProps={fullscreen && store.root ? { container: store.root } : undefined}\n items={config.sections.map((section) => ({\n extra: section === active ? <Check size={14} /> : formatTime(section.start),\n key: `${section.start}`,\n label: section.title,\n onClick: () => store.seek(section.start),\n }))}\n onOpenChange={(open) => frame?.hold(open)}\n >\n <button title={t('video.sections')} type=\"button\" {...styleProps(styles.chip)}>\n <span\n data-direction={shown.direction}\n data-video-section-label=\"\"\n key={active?.start ?? 'none'}\n {...styleProps([\n styles.chipText,\n shown.direction === 'forward' && styles.chipLabelForward,\n shown.direction === 'backward' && styles.chipLabelBackward,\n ])}\n >\n {active?.title ?? t('video.sections')}\n </span>\n <ChevronRight size={14} />\n </button>\n </DropdownMenu>\n );\n};\n\nSectionMenu.displayName = 'VideoSectionMenu';\n\nexport default SectionMenu;\n"],"mappings":";;;;;;;;;;;;;;AAgBA,MAAM,oBAAoB;CACxB,MAAM,EAAE,QAAQ,UAAU,gBAAgB;CAC1C,MAAM,QAAQ,IAAI,YAAY;CAC9B,MAAM,aAAa,cAAc,KAAK;CACtC,MAAM,EAAE,MAAM,eAAeA,aAAa;CAC1C,MAAM,SAAS,UAAU,UAAU,MAAM,aAAa;CACtD,MAAM,CAAC,OAAO,YAAY,SAAS;EAAE,WAAW;EAAQ,SAAS;CAAO,CAAC;CACzE,IAAI,MAAM,YAAY,QAAQ;EAC5B,MAAM,OAAO,MAAM,UAAU,OAAO,SAAS,QAAQ,MAAM,OAAO,IAAI;EAEtE,SAAS;GAAE,YADA,SAAS,OAAO,SAAS,QAAQ,MAAM,IAAI,OAC1B,OAAO,YAAY;GAAY,SAAS;EAAO,CAAC;CAC9E;CACA,IAAI,OAAO,SAAS,WAAW,GAAG,OAAO;CAEzC,OACE,oBAAC,cAAD;EACE,aAAa,cAAc,MAAM,OAAO,EAAE,WAAW,MAAM,KAAK,IAAI,KAAA;EACpE,OAAO,OAAO,SAAS,KAAK,aAAa;GACvC,OAAO,YAAY,SAAS,oBAAC,OAAD,EAAO,MAAM,GAAK,CAAA,IAAI,WAAW,QAAQ,KAAK;GAC1E,KAAK,GAAG,QAAQ;GAChB,OAAO,QAAQ;GACf,eAAe,MAAM,KAAK,QAAQ,KAAK;EACzC,EAAE;EACF,eAAe,SAAS,OAAO,KAAK,IAAI;EAExC,UAAA,qBAAC,UAAD;GAAQ,OAAO,EAAE,gBAAgB;GAAG,MAAK;GAAS,GAAI,WAAW,OAAO,IAAI;GAA5E,UAAA,CACE,oBAAC,QAAD;IACE,kBAAgB,MAAM;IACtB,4BAAyB;IAEzB,GAAI,WAAW;KACb,OAAO;KACP,MAAM,cAAc,aAAa,OAAO;KACxC,MAAM,cAAc,cAAc,OAAO;IAC3C,CAAC;IAEA,UAAA,QAAQ,SAAS,EAAE,gBAAgB;GAChC,GARC,QAAQ,SAAS,MAQlB,GACN,oBAAC,cAAD,EAAc,MAAM,GAAK,CAAA,CACnB;;CACI,CAAA;AAElB;AAEA,YAAY,cAAc"}