@lobehub/ui 6.0.0-beta.2 → 6.0.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/es/Accordion/style.d.mts +1 -1
  2. package/es/Tabs/style.d.mts +1 -1
  3. package/es/Video/Video.mjs +46 -0
  4. package/es/Video/Video.mjs.map +1 -0
  5. package/es/Video/context.mjs +14 -0
  6. package/es/Video/context.mjs.map +1 -0
  7. package/es/Video/index.d.mts +23 -39
  8. package/es/Video/index.mjs +21 -111
  9. package/es/Video/index.mjs.map +1 -1
  10. package/es/Video/parts/Caption.d.mts +9 -0
  11. package/es/Video/parts/Caption.mjs +34 -0
  12. package/es/Video/parts/Caption.mjs.map +1 -0
  13. package/es/Video/parts/CenterPlay.d.mts +8 -0
  14. package/es/Video/parts/CenterPlay.mjs +34 -0
  15. package/es/Video/parts/CenterPlay.mjs.map +1 -0
  16. package/es/Video/parts/Controls.d.mts +9 -0
  17. package/es/Video/parts/Controls.mjs +123 -0
  18. package/es/Video/parts/Controls.mjs.map +1 -0
  19. package/es/Video/parts/Frame.d.mts +9 -0
  20. package/es/Video/parts/Frame.mjs +94 -0
  21. package/es/Video/parts/Frame.mjs.map +1 -0
  22. package/es/Video/parts/HoverPreview.mjs +93 -0
  23. package/es/Video/parts/HoverPreview.mjs.map +1 -0
  24. package/es/Video/parts/LoadError.d.mts +8 -0
  25. package/es/Video/parts/LoadError.mjs +47 -0
  26. package/es/Video/parts/LoadError.mjs.map +1 -0
  27. package/es/Video/parts/PlayPauseIcon.mjs +48 -0
  28. package/es/Video/parts/PlayPauseIcon.mjs.map +1 -0
  29. package/es/Video/parts/RateMenu.mjs +48 -0
  30. package/es/Video/parts/RateMenu.mjs.map +1 -0
  31. package/es/Video/parts/Root.d.mts +9 -0
  32. package/es/Video/parts/Root.mjs +110 -0
  33. package/es/Video/parts/Root.mjs.map +1 -0
  34. package/es/Video/parts/SectionList.d.mts +9 -0
  35. package/es/Video/parts/SectionList.mjs +32 -0
  36. package/es/Video/parts/SectionList.mjs.map +1 -0
  37. package/es/Video/parts/SectionMenu.mjs +63 -0
  38. package/es/Video/parts/SectionMenu.mjs.map +1 -0
  39. package/es/Video/parts/Timeline.d.mts +9 -0
  40. package/es/Video/parts/Timeline.mjs +169 -0
  41. package/es/Video/parts/Timeline.mjs.map +1 -0
  42. package/es/Video/store.mjs +241 -0
  43. package/es/Video/store.mjs.map +1 -0
  44. package/es/Video/style.mjs +393 -23
  45. package/es/Video/style.mjs.map +1 -1
  46. package/es/Video/time.mjs +60 -0
  47. package/es/Video/time.mjs.map +1 -0
  48. package/es/Video/type.d.mts +125 -0
  49. package/es/Video/useControlsIdle.mjs +39 -0
  50. package/es/Video/useControlsIdle.mjs.map +1 -0
  51. package/es/Video/useFullscreen.mjs +17 -0
  52. package/es/Video/useFullscreen.mjs.map +1 -0
  53. package/es/Video/useKeyboard.mjs +60 -0
  54. package/es/Video/useKeyboard.mjs.map +1 -0
  55. package/es/Video/useVideo.d.mts +9 -0
  56. package/es/Video/useVideo.mjs +17 -0
  57. package/es/Video/useVideo.mjs.map +1 -0
  58. package/es/i18n/resources/en/index.d.mts +3 -2
  59. package/es/i18n/resources/en/index.mjs +4 -2
  60. package/es/i18n/resources/en/index.mjs.map +1 -1
  61. package/es/i18n/resources/en/video.d.mts +23 -0
  62. package/es/i18n/resources/en/video.mjs +24 -0
  63. package/es/i18n/resources/en/video.mjs.map +1 -0
  64. package/es/i18n/resources/zhCn/index.d.mts +3 -2
  65. package/es/i18n/resources/zhCn/index.mjs +4 -2
  66. package/es/i18n/resources/zhCn/index.mjs.map +1 -1
  67. package/es/i18n/resources/zhCn/video.d.mts +23 -0
  68. package/es/i18n/resources/zhCn/video.mjs +24 -0
  69. package/es/i18n/resources/zhCn/video.mjs.map +1 -0
  70. package/es/i18n/types.d.mts +2 -1
  71. package/es/index.d.mts +4 -2
  72. package/es/index.mjs +2 -1
  73. package/es/mdx/mdxComponents/Video.d.mts +2 -1
  74. package/es/mdx/mdxComponents/Video.mjs +5 -3
  75. package/es/mdx/mdxComponents/Video.mjs.map +1 -1
  76. package/es/style.css +263 -23
  77. package/package.json +1 -1
  78. package/es/Video/marker.stylex.mjs +0 -9
  79. package/es/Video/marker.stylex.mjs.map +0 -1
@@ -0,0 +1,9 @@
1
+ import { VideoTimelineProps } from "../type.mjs";
2
+ //#region src/Video/parts/Timeline.d.ts
3
+ declare const Timeline: {
4
+ ({ children, className, style, thumbnails }: VideoTimelineProps): import("react").JSX.Element;
5
+ displayName: string;
6
+ };
7
+ //#endregion
8
+ export { Timeline as default };
9
+ //# sourceMappingURL=Timeline.d.mts.map
@@ -0,0 +1,169 @@
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 { markerColor, styles } from "../style.mjs";
6
+ import { formatTime, mergeSegments, sectionAt } from "../time.mjs";
7
+ import { useVideo } from "../useVideo.mjs";
8
+ import video_default from "../../i18n/resources/en/video.mjs";
9
+ import HoverPreview from "./HoverPreview.mjs";
10
+ import { useLayoutEffect, useMemo, useRef, useState } from "react";
11
+ import { jsx, jsxs } from "react/jsx-runtime";
12
+ //#region src/Video/parts/Timeline.tsx
13
+ const SNAP_RATIO = .012;
14
+ const GAP_PX = 3;
15
+ const PREVIEW_HALF = 88;
16
+ const canHover = () => typeof window === "undefined" || !window.matchMedia?.("(hover: none)").matches;
17
+ const markerName = (marker) => [
18
+ formatTime(marker.time),
19
+ marker.label,
20
+ marker.content
21
+ ].filter((part) => typeof part === "string").join(" ");
22
+ const Timeline = ({ children, className, style, thumbnails = true }) => {
23
+ const { config, media, store } = useVideoContext();
24
+ const { t } = useTranslation(video_default);
25
+ const duration = useVideo((state) => Number.isFinite(state.duration) ? state.duration : 0);
26
+ const trackRef = useRef(null);
27
+ const [width, setWidth] = useState(0);
28
+ const [hover, setHover] = useState(null);
29
+ const [hovering, setHovering] = useState(false);
30
+ useLayoutEffect(() => {
31
+ const track = trackRef.current;
32
+ if (!track) return;
33
+ setWidth(track.getBoundingClientRect().width);
34
+ if (typeof ResizeObserver === "undefined") return;
35
+ const observer = new ResizeObserver(([entry]) => setWidth(entry.contentRect.width));
36
+ observer.observe(track);
37
+ return () => observer.disconnect();
38
+ }, []);
39
+ useLayoutEffect(() => {
40
+ const track = trackRef.current;
41
+ if (!track) return;
42
+ const setOnChange = (name, value) => {
43
+ if (track.getAttribute(name) !== value) track.setAttribute(name, value);
44
+ };
45
+ const paint = (state) => {
46
+ track.style.setProperty("--video-time", String(state.time));
47
+ setOnChange("aria-valuenow", String(Math.round(state.time)));
48
+ setOnChange("aria-valuetext", [`${formatTime(state.time)} / ${formatTime(state.duration)}`, state.activeSection?.title].filter(Boolean).join(", "));
49
+ };
50
+ paint(store.getState());
51
+ return store.subscribe(paint);
52
+ }, [store]);
53
+ const { hairlines, segments } = useMemo(() => mergeSegments(config.sections, duration, width), [
54
+ config.sections,
55
+ duration,
56
+ width
57
+ ]);
58
+ const visibleMarkers = useMemo(() => config.markers.filter((marker) => marker.time >= 0 && marker.time <= duration), [config.markers, duration]);
59
+ const pct = (seconds) => duration > 0 ? seconds / duration * 100 : 0;
60
+ const timeAt = (element, clientX) => {
61
+ const box = element.getBoundingClientRect();
62
+ if (!box.width) return 0;
63
+ return Math.min(Math.max((clientX - box.left) / box.width, 0), 1) * duration;
64
+ };
65
+ const onPointerDown = (event) => {
66
+ if (event.button !== 0 || !duration) return;
67
+ const element = event.currentTarget;
68
+ store.seek(timeAt(element, event.clientX));
69
+ element.setPointerCapture?.(event.pointerId);
70
+ const move = (next) => store.seek(timeAt(element, next.clientX));
71
+ const up = () => {
72
+ element.removeEventListener("pointermove", move);
73
+ element.removeEventListener("pointerup", up);
74
+ element.removeEventListener("pointercancel", up);
75
+ };
76
+ element.addEventListener("pointermove", move);
77
+ element.addEventListener("pointerup", up);
78
+ element.addEventListener("pointercancel", up);
79
+ };
80
+ const onPointerMove = (event) => {
81
+ if (event.pointerType === "touch" || !duration || !canHover()) return;
82
+ const element = event.currentTarget;
83
+ const box = element.getBoundingClientRect();
84
+ const seconds = timeAt(element, event.clientX);
85
+ const snap = duration * SNAP_RATIO;
86
+ const marker = visibleMarkers.find((item) => Math.abs(item.time - seconds) < snap);
87
+ setHovering(true);
88
+ setHover({
89
+ marker,
90
+ time: marker?.time ?? seconds,
91
+ x: Math.min(Math.max(event.clientX - box.left, PREVIEW_HALF), Math.max(box.width - PREVIEW_HALF, PREVIEW_HALF))
92
+ });
93
+ };
94
+ return /* @__PURE__ */ jsxs("div", {
95
+ ...styleProps(void 0, className, {
96
+ position: "relative",
97
+ ...style
98
+ }),
99
+ children: [
100
+ hover && /* @__PURE__ */ jsx(HoverPreview, {
101
+ marker: hover.marker,
102
+ section: sectionAt(config.sections, hover.time),
103
+ src: media.src,
104
+ thumbnail: thumbnails,
105
+ time: hover.time,
106
+ visible: hovering,
107
+ x: hover.x
108
+ }),
109
+ /* @__PURE__ */ jsxs("div", {
110
+ "aria-label": t("video.seek"),
111
+ "aria-valuemax": Math.round(duration),
112
+ "aria-valuemin": 0,
113
+ ref: trackRef,
114
+ role: "slider",
115
+ ...styleProps(styles.track),
116
+ onPointerDown,
117
+ onPointerLeave: () => setHovering(false),
118
+ onPointerMove,
119
+ children: [
120
+ segments.map((segment, index) => {
121
+ const span = segment.end - segment.start;
122
+ const hovered = hovering && hover !== null && hover.time >= segment.start && hover.time < segment.end;
123
+ const hoverFill = hovering && hover ? Math.min(Math.max((hover.time - segment.start) / span, 0), 1) : 0;
124
+ const leftInset = index === 0 ? 0 : GAP_PX / 2;
125
+ const rightInset = index === segments.length - 1 ? 0 : GAP_PX / 2;
126
+ return /* @__PURE__ */ jsxs("div", {
127
+ "data-video-segment": "",
128
+ ...styleProps([styles.segment, hovered && styles.segmentHovered], void 0, {
129
+ left: `calc(${pct(segment.start)}% + ${leftInset}px)`,
130
+ width: `calc(${pct(span)}% - ${leftInset + rightInset}px)`
131
+ }),
132
+ children: [hoverFill > 0 && /* @__PURE__ */ jsx("div", { ...styleProps(styles.segmentFill, void 0, {
133
+ background: "rgb(255 255 255 / 32%)",
134
+ width: `${hoverFill * 100}%`
135
+ }) }), /* @__PURE__ */ jsx("div", { ...styleProps(styles.segmentFill, void 0, {
136
+ background: "#fff",
137
+ width: `calc(clamp(0, (var(--video-time, 0) - ${segment.start}) / ${span}, 1) * 100%)`
138
+ }) })]
139
+ }, segment.start);
140
+ }),
141
+ hairlines.map((at) => /* @__PURE__ */ jsx("div", { ...styleProps(styles.hairline, void 0, { left: `${pct(at)}%` }) }, at)),
142
+ /* @__PURE__ */ jsx("div", { ...styleProps(styles.head, void 0, { left: duration > 0 ? `calc(var(--video-time, 0) / ${duration} * 100%)` : 0 }) }),
143
+ visibleMarkers.map((marker, index) => /* @__PURE__ */ jsx("button", {
144
+ "aria-label": markerName(marker),
145
+ type: "button",
146
+ ...styleProps(styles.marker, void 0, {
147
+ background: markerColor(marker.color),
148
+ left: `${pct(marker.time)}%`
149
+ }),
150
+ onPointerDown: (event) => event.stopPropagation(),
151
+ onClick: (event) => {
152
+ event.stopPropagation();
153
+ store.pause();
154
+ store.seek(marker.time);
155
+ store.setCaptions(true);
156
+ config.onMarkerClick?.(marker);
157
+ }
158
+ }, marker.key ?? `${marker.time}-${index}`))
159
+ ]
160
+ }),
161
+ children
162
+ ]
163
+ });
164
+ };
165
+ Timeline.displayName = "VideoTimeline";
166
+ //#endregion
167
+ export { Timeline as default };
168
+
169
+ //# sourceMappingURL=Timeline.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Timeline.mjs","names":["videoMessages"],"sources":["../../../src/Video/parts/Timeline.tsx"],"sourcesContent":["'use client';\n\nimport {\n type PointerEvent as ReactPointerEvent,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from '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 { markerColor, styles } from '../style';\nimport { formatTime, mergeSegments, sectionAt } from '../time';\nimport type { VideoMarker, VideoState, VideoTimelineProps } from '../type';\nimport { useVideo } from '../useVideo';\nimport HoverPreview from './HoverPreview';\n\nconst SNAP_RATIO = 0.012;\nconst GAP_PX = 3;\nconst PREVIEW_HALF = 88;\n\ninterface Hover {\n marker?: VideoMarker;\n time: number;\n x: number;\n}\n\nconst canHover = () =>\n typeof window === 'undefined' || !window.matchMedia?.('(hover: none)').matches;\n\nconst markerName = (marker: VideoMarker) =>\n [formatTime(marker.time), marker.label, marker.content]\n .filter((part) => typeof part === 'string')\n .join(' ');\n\nconst Timeline = ({ children, className, style, thumbnails = true }: VideoTimelineProps) => {\n const { config, media, store } = useVideoContext();\n const { t } = useTranslation(videoMessages);\n const duration = useVideo((state) => (Number.isFinite(state.duration) ? state.duration : 0));\n const trackRef = useRef<HTMLDivElement>(null);\n const [width, setWidth] = useState(0);\n const [hover, setHover] = useState<Hover | null>(null);\n const [hovering, setHovering] = useState(false);\n\n useLayoutEffect(() => {\n const track = trackRef.current;\n if (!track) return;\n setWidth(track.getBoundingClientRect().width);\n if (typeof ResizeObserver === 'undefined') return;\n const observer = new ResizeObserver(([entry]) => setWidth(entry.contentRect.width));\n observer.observe(track);\n return () => observer.disconnect();\n }, []);\n\n // Playback must not re-render the timeline: the playhead and the played\n // fills read --video-time, written straight to the track on every frame.\n useLayoutEffect(() => {\n const track = trackRef.current;\n if (!track) return;\n const setOnChange = (name: string, value: string) => {\n if (track.getAttribute(name) !== value) track.setAttribute(name, value);\n };\n const paint = (state: VideoState) => {\n track.style.setProperty('--video-time', String(state.time));\n setOnChange('aria-valuenow', String(Math.round(state.time)));\n setOnChange(\n 'aria-valuetext',\n [`${formatTime(state.time)} / ${formatTime(state.duration)}`, state.activeSection?.title]\n .filter(Boolean)\n .join(', '),\n );\n };\n paint(store.getState());\n return store.subscribe(paint);\n }, [store]);\n\n const { hairlines, segments } = useMemo(\n () => mergeSegments(config.sections, duration, width),\n [config.sections, duration, width],\n );\n\n const visibleMarkers = useMemo(\n () => config.markers.filter((marker) => marker.time >= 0 && marker.time <= duration),\n [config.markers, duration],\n );\n\n const pct = (seconds: number) => (duration > 0 ? (seconds / duration) * 100 : 0);\n const timeAt = (element: HTMLElement, clientX: number) => {\n const box = element.getBoundingClientRect();\n if (!box.width) return 0;\n return Math.min(Math.max((clientX - box.left) / box.width, 0), 1) * duration;\n };\n\n const onPointerDown = (event: ReactPointerEvent<HTMLDivElement>) => {\n if (event.button !== 0 || !duration) return;\n const element = event.currentTarget;\n store.seek(timeAt(element, event.clientX));\n element.setPointerCapture?.(event.pointerId);\n const move = (next: PointerEvent) => store.seek(timeAt(element, next.clientX));\n const up = () => {\n element.removeEventListener('pointermove', move);\n element.removeEventListener('pointerup', up);\n element.removeEventListener('pointercancel', up);\n };\n element.addEventListener('pointermove', move);\n element.addEventListener('pointerup', up);\n element.addEventListener('pointercancel', up);\n };\n\n const onPointerMove = (event: ReactPointerEvent<HTMLDivElement>) => {\n if (event.pointerType === 'touch' || !duration || !canHover()) return;\n const element = event.currentTarget;\n const box = element.getBoundingClientRect();\n const seconds = timeAt(element, event.clientX);\n const snap = duration * SNAP_RATIO;\n const marker = visibleMarkers.find((item) => Math.abs(item.time - seconds) < snap);\n setHovering(true);\n setHover({\n marker,\n time: marker?.time ?? seconds,\n x: Math.min(\n Math.max(event.clientX - box.left, PREVIEW_HALF),\n Math.max(box.width - PREVIEW_HALF, PREVIEW_HALF),\n ),\n });\n };\n\n return (\n <div {...styleProps(undefined, className, { position: 'relative', ...style })}>\n {hover && (\n <HoverPreview\n marker={hover.marker}\n section={sectionAt(config.sections, hover.time)}\n src={media.src}\n thumbnail={thumbnails}\n time={hover.time}\n visible={hovering}\n x={hover.x}\n />\n )}\n <div\n aria-label={t('video.seek')}\n aria-valuemax={Math.round(duration)}\n aria-valuemin={0}\n ref={trackRef}\n role=\"slider\"\n {...styleProps(styles.track)}\n onPointerDown={onPointerDown}\n onPointerLeave={() => setHovering(false)}\n onPointerMove={onPointerMove}\n >\n {segments.map((segment, index) => {\n const span = segment.end - segment.start;\n const hovered =\n hovering && hover !== null && hover.time >= segment.start && hover.time < segment.end;\n const hoverFill =\n hovering && hover ? Math.min(Math.max((hover.time - segment.start) / span, 0), 1) : 0;\n const leftInset = index === 0 ? 0 : GAP_PX / 2;\n const rightInset = index === segments.length - 1 ? 0 : GAP_PX / 2;\n return (\n <div\n data-video-segment=\"\"\n key={segment.start}\n {...styleProps([styles.segment, hovered && styles.segmentHovered], undefined, {\n left: `calc(${pct(segment.start)}% + ${leftInset}px)`,\n width: `calc(${pct(span)}% - ${leftInset + rightInset}px)`,\n })}\n >\n {hoverFill > 0 && (\n <div\n {...styleProps(styles.segmentFill, undefined, {\n background: 'rgb(255 255 255 / 32%)',\n width: `${hoverFill * 100}%`,\n })}\n />\n )}\n <div\n {...styleProps(styles.segmentFill, undefined, {\n background: '#fff',\n width: `calc(clamp(0, (var(--video-time, 0) - ${segment.start}) / ${span}, 1) * 100%)`,\n })}\n />\n </div>\n );\n })}\n {hairlines.map((at) => (\n <div key={at} {...styleProps(styles.hairline, undefined, { left: `${pct(at)}%` })} />\n ))}\n <div\n {...styleProps(styles.head, undefined, {\n left: duration > 0 ? `calc(var(--video-time, 0) / ${duration} * 100%)` : 0,\n })}\n />\n {visibleMarkers.map((marker, index) => (\n <button\n aria-label={markerName(marker)}\n key={marker.key ?? `${marker.time}-${index}`}\n type=\"button\"\n {...styleProps(styles.marker, undefined, {\n background: markerColor(marker.color),\n left: `${pct(marker.time)}%`,\n })}\n onPointerDown={(event) => event.stopPropagation()}\n onClick={(event) => {\n event.stopPropagation();\n store.pause();\n store.seek(marker.time);\n store.setCaptions(true);\n config.onMarkerClick?.(marker);\n }}\n />\n ))}\n </div>\n {children}\n </div>\n );\n};\n\nTimeline.displayName = 'VideoTimeline';\n\nexport default Timeline;\n"],"mappings":"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"}
@@ -0,0 +1,241 @@
1
+ import { markersAt, sectionAt, sortSections, steppedTime } from "./time.mjs";
2
+ //#region src/Video/store.ts
3
+ const EMPTY_MARKERS = [];
4
+ const sameMarkers = (a, b) => a.length === b.length && a.every((marker, index) => marker === b[index]);
5
+ const createVideoStore = () => {
6
+ let data = {
7
+ fps: 30,
8
+ markers: [],
9
+ sections: []
10
+ };
11
+ let options = {};
12
+ let video = null;
13
+ let root = null;
14
+ let painted;
15
+ let state = {
16
+ activeMarkers: EMPTY_MARKERS,
17
+ activeSection: void 0,
18
+ captions: true,
19
+ duration: 0,
20
+ muted: false,
21
+ paused: true,
22
+ rate: 1,
23
+ size: void 0,
24
+ status: "loading",
25
+ time: 0
26
+ };
27
+ const listeners = /* @__PURE__ */ new Set();
28
+ const derive = (next) => {
29
+ const activeSection = sectionAt(data.sections, next.time);
30
+ const found = markersAt(data.markers, next.time, data.fps);
31
+ const activeMarkers = found.length === 0 ? EMPTY_MARKERS : sameMarkers(found, state.activeMarkers) ? state.activeMarkers : found;
32
+ return {
33
+ ...next,
34
+ activeMarkers,
35
+ activeSection
36
+ };
37
+ };
38
+ const commit = (patch) => {
39
+ const next = derive({
40
+ ...state,
41
+ ...patch
42
+ });
43
+ if (!Object.keys(next).some((key) => next[key] !== state[key])) return;
44
+ state = next;
45
+ for (const listener of listeners) listener(state);
46
+ };
47
+ const frameTime = () => {
48
+ if (!video) return 0;
49
+ return video.paused ? video.currentTime : painted ?? video.currentTime;
50
+ };
51
+ const clampToMedia = (seconds) => {
52
+ const lower = Math.max(seconds, 0);
53
+ return video && Number.isFinite(video.duration) ? Math.min(lower, video.duration) : lower;
54
+ };
55
+ const atEnd = () => Boolean(video) && (video.ended || Number.isFinite(video.duration) && video.currentTime >= video.duration - .5 / data.fps);
56
+ const seek = (seconds) => {
57
+ if (!video || !Number.isFinite(seconds)) return;
58
+ painted = void 0;
59
+ video.currentTime = clampToMedia(seconds);
60
+ commit({ time: video.currentTime });
61
+ };
62
+ let attachedOnce = false;
63
+ return {
64
+ attach: (element) => {
65
+ const target = element;
66
+ video = target;
67
+ let handle = 0;
68
+ let running = false;
69
+ const cancel = () => {
70
+ if (!running) return;
71
+ running = false;
72
+ if (target.cancelVideoFrameCallback) target.cancelVideoFrameCallback(handle);
73
+ else cancelAnimationFrame(handle);
74
+ };
75
+ const schedule = () => {
76
+ handle = target.requestVideoFrameCallback ? target.requestVideoFrameCallback(tick) : requestAnimationFrame(() => tick());
77
+ };
78
+ function tick(_now, frame) {
79
+ if (!running) return;
80
+ if (!target.paused) painted = frame?.mediaTime;
81
+ commit({ time: target.paused ? target.currentTime : painted ?? target.currentTime });
82
+ schedule();
83
+ }
84
+ const start = () => {
85
+ if (running) return;
86
+ running = true;
87
+ schedule();
88
+ };
89
+ const onMeta = () => commit({
90
+ duration: target.duration,
91
+ time: target.currentTime,
92
+ size: {
93
+ height: target.videoHeight,
94
+ width: target.videoWidth
95
+ },
96
+ status: "ready"
97
+ });
98
+ const onPlay = () => {
99
+ commit({ paused: false });
100
+ start();
101
+ };
102
+ const onPause = () => {
103
+ cancel();
104
+ const held = painted;
105
+ painted = void 0;
106
+ if (!atEnd() && held !== void 0 && Math.abs(target.currentTime - held) > .001) target.currentTime = held;
107
+ commit({
108
+ paused: true,
109
+ time: target.currentTime
110
+ });
111
+ };
112
+ const onSeeked = () => {
113
+ painted = void 0;
114
+ commit({ time: target.currentTime });
115
+ };
116
+ const onRate = () => {
117
+ if (target.playbackRate === state.rate) return;
118
+ commit({ rate: target.playbackRate });
119
+ options.onRateChange?.(target.playbackRate);
120
+ };
121
+ const onVolume = () => {
122
+ if (target.muted === state.muted) return;
123
+ commit({ muted: target.muted });
124
+ options.onMutedChange?.(target.muted);
125
+ };
126
+ const onError = () => commit({ status: "error" });
127
+ const muted = attachedOnce ? state.muted : options.muted ?? false;
128
+ const rate = attachedOnce ? state.rate : options.defaultPlaybackRate ?? 1;
129
+ attachedOnce = true;
130
+ commit({
131
+ duration: Number.isFinite(target.duration) ? target.duration : 0,
132
+ muted,
133
+ paused: target.paused,
134
+ rate,
135
+ size: void 0,
136
+ status: "loading",
137
+ time: target.currentTime
138
+ });
139
+ if (target.muted !== muted) target.muted = muted;
140
+ if (target.playbackRate !== rate) target.playbackRate = rate;
141
+ const events = [
142
+ ["loadedmetadata", onMeta],
143
+ ["play", onPlay],
144
+ ["pause", onPause],
145
+ ["ended", onPause],
146
+ ["seeked", onSeeked],
147
+ ["ratechange", onRate],
148
+ ["volumechange", onVolume],
149
+ ["error", onError]
150
+ ];
151
+ for (const [type, handler] of events) target.addEventListener(type, handler);
152
+ if (target.readyState >= 1) onMeta();
153
+ if (!target.paused) start();
154
+ return () => {
155
+ cancel();
156
+ for (const [type, handler] of events) target.removeEventListener(type, handler);
157
+ if (video === target) video = null;
158
+ };
159
+ },
160
+ attachRoot: (element) => {
161
+ root = element;
162
+ },
163
+ getData: () => data,
164
+ getState: () => state,
165
+ pause: () => video?.pause(),
166
+ play: async () => {
167
+ await video?.play();
168
+ },
169
+ reload: () => {
170
+ if (!video) return;
171
+ commit({ status: "loading" });
172
+ video.load();
173
+ },
174
+ requestFullscreen: async () => {
175
+ await root?.requestFullscreen?.();
176
+ },
177
+ get root() {
178
+ return root;
179
+ },
180
+ seek,
181
+ setCaptions: (captions) => commit({ captions }),
182
+ setData: (next) => {
183
+ data = {
184
+ ...data,
185
+ ...next,
186
+ sections: next.sections ? sortSections(next.sections) : data.sections
187
+ };
188
+ commit({});
189
+ },
190
+ setMuted: (muted) => {
191
+ if (video) video.muted = muted;
192
+ },
193
+ setOptions: (next) => {
194
+ options = {
195
+ ...options,
196
+ ...next
197
+ };
198
+ },
199
+ setRate: (rate) => {
200
+ if (video) video.playbackRate = rate;
201
+ },
202
+ step: (frames) => {
203
+ if (!video) return;
204
+ const from = frameTime();
205
+ painted = void 0;
206
+ video.pause();
207
+ const limit = Number.isFinite(video.duration) ? video.duration : Number.POSITIVE_INFINITY;
208
+ video.currentTime = steppedTime(from, frames, limit, data.fps);
209
+ commit({ time: video.currentTime });
210
+ },
211
+ subscribe: (listener) => {
212
+ listeners.add(listener);
213
+ return () => {
214
+ listeners.delete(listener);
215
+ };
216
+ },
217
+ toggle: () => {
218
+ if (!video) return;
219
+ if (!video.paused) return video.pause();
220
+ if (atEnd()) video.currentTime = 0;
221
+ video.play();
222
+ },
223
+ toggleFullscreen: () => {
224
+ if (typeof document !== "undefined" && document.fullscreenElement) {
225
+ document.exitFullscreen?.();
226
+ return;
227
+ }
228
+ root?.requestFullscreen?.();
229
+ },
230
+ toggleMute: () => {
231
+ if (video) video.muted = !video.muted;
232
+ },
233
+ get video() {
234
+ return video;
235
+ }
236
+ };
237
+ };
238
+ //#endregion
239
+ export { createVideoStore };
240
+
241
+ //# sourceMappingURL=store.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.mjs","names":[],"sources":["../../src/Video/store.ts"],"sourcesContent":["import { markersAt, sectionAt, sortSections, steppedTime } from './time';\nimport type { VideoMarker, VideoPlayer, VideoSection, VideoState } from './type';\n\nexport interface VideoStoreOptions {\n defaultPlaybackRate?: number;\n muted?: boolean;\n onMutedChange?: (muted: boolean) => void;\n onRateChange?: (rate: number) => void;\n}\n\nexport interface VideoStoreData {\n fps: number;\n markers: VideoMarker[];\n sections: VideoSection[];\n}\n\nexport interface VideoStore extends VideoPlayer {\n attach: (video: HTMLVideoElement) => () => void;\n attachRoot: (root: HTMLDivElement | null) => void;\n getData: () => VideoStoreData;\n reload: () => void;\n setData: (data: Partial<VideoStoreData>) => void;\n setOptions: (options: VideoStoreOptions) => void;\n toggleFullscreen: () => void;\n}\n\nconst EMPTY_MARKERS: VideoMarker[] = [];\n\nconst sameMarkers = (a: VideoMarker[], b: VideoMarker[]) =>\n a.length === b.length && a.every((marker, index) => marker === b[index]);\n\ntype FrameVideo = HTMLVideoElement & {\n cancelVideoFrameCallback?: (handle: number) => void;\n requestVideoFrameCallback?: (\n callback: (now: number, frame: { mediaTime: number }) => void,\n ) => number;\n};\n\nexport const createVideoStore = (): VideoStore => {\n let data: VideoStoreData = { fps: 30, markers: [], sections: [] };\n let options: VideoStoreOptions = {};\n let video: FrameVideo | null = null;\n let root: HTMLDivElement | null = null;\n // The frame actually on screen while playing. `currentTime` runs ahead of\n // the painted frame, and pausing would otherwise land a frame past it.\n let painted: number | undefined;\n let state: VideoState = {\n activeMarkers: EMPTY_MARKERS,\n activeSection: undefined,\n captions: true,\n duration: 0,\n muted: false,\n paused: true,\n rate: 1,\n size: undefined,\n status: 'loading',\n time: 0,\n };\n const listeners = new Set<(state: VideoState) => void>();\n\n const derive = (next: VideoState): VideoState => {\n const activeSection = sectionAt(data.sections, next.time);\n const found = markersAt(data.markers, next.time, data.fps);\n const activeMarkers =\n found.length === 0\n ? EMPTY_MARKERS\n : sameMarkers(found, state.activeMarkers)\n ? state.activeMarkers\n : found;\n return { ...next, activeMarkers, activeSection };\n };\n\n const commit = (patch: Partial<VideoState>) => {\n const next = derive({ ...state, ...patch });\n const changed = (Object.keys(next) as (keyof VideoState)[]).some(\n (key) => next[key] !== state[key],\n );\n if (!changed) return;\n state = next;\n for (const listener of listeners) listener(state);\n };\n\n const frameTime = () => {\n if (!video) return 0;\n return video.paused ? video.currentTime : (painted ?? video.currentTime);\n };\n\n // Before metadata the duration is NaN: only the lower bound applies, and the\n // element keeps the value as its start position.\n const clampToMedia = (seconds: number) => {\n const lower = Math.max(seconds, 0);\n return video && Number.isFinite(video.duration) ? Math.min(lower, video.duration) : lower;\n };\n\n const atEnd = () =>\n Boolean(video) &&\n (video!.ended ||\n (Number.isFinite(video!.duration) && video!.currentTime >= video!.duration - 0.5 / data.fps));\n\n const seek = (seconds: number) => {\n if (!video || !Number.isFinite(seconds)) return;\n painted = undefined;\n video.currentTime = clampToMedia(seconds);\n commit({ time: video.currentTime });\n };\n\n let attachedOnce = false;\n\n const store: VideoStore = {\n attach: (element) => {\n const target = element as FrameVideo;\n video = target;\n let handle = 0;\n let running = false;\n\n const cancel = () => {\n if (!running) return;\n running = false;\n if (target.cancelVideoFrameCallback) target.cancelVideoFrameCallback(handle);\n else cancelAnimationFrame(handle);\n };\n const schedule = () => {\n handle = target.requestVideoFrameCallback\n ? target.requestVideoFrameCallback(tick)\n : requestAnimationFrame(() => tick());\n };\n function tick(_now?: number, frame?: { mediaTime: number }) {\n if (!running) return;\n if (!target.paused) painted = frame?.mediaTime;\n commit({ time: target.paused ? target.currentTime : (painted ?? target.currentTime) });\n schedule();\n }\n const start = () => {\n if (running) return;\n running = true;\n schedule();\n };\n\n const onMeta = () =>\n commit({\n duration: target.duration,\n time: target.currentTime,\n size: { height: target.videoHeight, width: target.videoWidth },\n status: 'ready',\n });\n const onPlay = () => {\n commit({ paused: false });\n start();\n };\n const onPause = () => {\n cancel();\n const held = painted;\n painted = undefined;\n // At the end the element must stay ended, or play() would resume on\n // the last frame instead of restarting.\n if (!atEnd() && held !== undefined && Math.abs(target.currentTime - held) > 1e-3)\n target.currentTime = held;\n commit({ paused: true, time: target.currentTime });\n };\n const onSeeked = () => {\n painted = undefined;\n commit({ time: target.currentTime });\n };\n const onRate = () => {\n if (target.playbackRate === state.rate) return;\n commit({ rate: target.playbackRate });\n options.onRateChange?.(target.playbackRate);\n };\n const onVolume = () => {\n if (target.muted === state.muted) return;\n commit({ muted: target.muted });\n options.onMutedChange?.(target.muted);\n };\n const onError = () => commit({ status: 'error' });\n\n // Initial props apply once; a later video (a new src) keeps the viewer's choices.\n const muted = attachedOnce ? state.muted : (options.muted ?? false);\n const rate = attachedOnce ? state.rate : (options.defaultPlaybackRate ?? 1);\n attachedOnce = true;\n commit({\n duration: Number.isFinite(target.duration) ? target.duration : 0,\n muted,\n paused: target.paused,\n rate,\n size: undefined,\n status: 'loading',\n time: target.currentTime,\n });\n if (target.muted !== muted) target.muted = muted;\n if (target.playbackRate !== rate) target.playbackRate = rate;\n\n const events: [string, () => void][] = [\n ['loadedmetadata', onMeta],\n ['play', onPlay],\n ['pause', onPause],\n ['ended', onPause],\n ['seeked', onSeeked],\n ['ratechange', onRate],\n ['volumechange', onVolume],\n ['error', onError],\n ];\n for (const [type, handler] of events) target.addEventListener(type, handler);\n if (target.readyState >= 1) onMeta();\n if (!target.paused) start();\n\n return () => {\n cancel();\n for (const [type, handler] of events) target.removeEventListener(type, handler);\n if (video === target) video = null;\n };\n },\n attachRoot: (element) => {\n root = element;\n },\n getData: () => data,\n getState: () => state,\n pause: () => video?.pause(),\n play: async () => {\n await video?.play();\n },\n reload: () => {\n if (!video) return;\n commit({ status: 'loading' });\n video.load();\n },\n requestFullscreen: async () => {\n await root?.requestFullscreen?.();\n },\n get root() {\n return root;\n },\n seek,\n setCaptions: (captions) => commit({ captions }),\n setData: (next) => {\n data = {\n ...data,\n ...next,\n sections: next.sections ? sortSections(next.sections) : data.sections,\n };\n commit({});\n },\n setMuted: (muted) => {\n if (video) video.muted = muted;\n },\n setOptions: (next) => {\n options = { ...options, ...next };\n },\n setRate: (rate) => {\n if (video) video.playbackRate = rate;\n },\n step: (frames) => {\n if (!video) return;\n const from = frameTime();\n painted = undefined;\n video.pause();\n const limit = Number.isFinite(video.duration) ? video.duration : Number.POSITIVE_INFINITY;\n video.currentTime = steppedTime(from, frames, limit, data.fps);\n commit({ time: video.currentTime });\n },\n subscribe: (listener) => {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n toggle: () => {\n if (!video) return;\n if (!video.paused) return video.pause();\n if (atEnd()) video.currentTime = 0;\n void video.play();\n },\n toggleFullscreen: () => {\n if (typeof document !== 'undefined' && document.fullscreenElement) {\n void document.exitFullscreen?.();\n return;\n }\n void root?.requestFullscreen?.();\n },\n toggleMute: () => {\n if (video) video.muted = !video.muted;\n },\n get video() {\n return video;\n },\n };\n\n return store;\n};\n"],"mappings":"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"}