@xhub-short/ui 0.1.0-beta.1 → 0.1.0-beta.11
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.
- package/dist/CommentSheet.css-DyEc3Sro.d.ts +217 -0
- package/dist/VideoSlotPlayIndicator-DPs8Xt5C.d.ts +51 -0
- package/dist/chunk-3OB3OVYR.js +349 -0
- package/dist/{chunk-WKX2WBVO.js → chunk-3XPJHUYL.js} +1 -39
- package/dist/chunk-4RIMQOBR.js +58 -0
- package/dist/chunk-4TUBNA2X.js +180 -0
- package/dist/{chunk-4YDIRPIN.js → chunk-ANCP53F3.js} +3 -3
- package/dist/{chunk-UXMA4KJZ.js → chunk-CAWE42LH.js} +5 -3
- package/dist/{chunk-ANGBSV7L.js → chunk-CIIZ3IHV.js} +10 -5
- package/dist/chunk-DR7KR7OT.js +103 -0
- package/dist/chunk-DXLCQ4FH.js +102 -0
- package/dist/chunk-EDWS2IPH.js +1 -0
- package/dist/chunk-FR7UQSZP.js +570 -0
- package/dist/chunk-IWSBYOSS.js +91 -0
- package/dist/chunk-JEY6R4KJ.js +334 -0
- package/dist/chunk-KMJ3PQ7M.js +1262 -0
- package/dist/chunk-MFJS65C5.js +368 -0
- package/dist/{chunk-HW4LXTFT.js → chunk-OM4L7RE5.js} +18 -6
- package/dist/chunk-PBIH2F2Q.js +344 -0
- package/dist/chunk-PJ4NMVMY.js +326 -0
- package/dist/chunk-Q6MG7AVG.js +531 -0
- package/dist/chunk-QCKVF2DR.js +713 -0
- package/dist/chunk-QCRRF76W.js +75 -0
- package/dist/chunk-QUEJHA24.js +508 -0
- package/dist/chunk-VXW7AOGM.js +285 -0
- package/dist/chunk-YB7AXTX7.js +430 -0
- package/dist/chunk-ZGWSJ6Z5.js +601 -0
- package/dist/components/ActionBar/index.js +1 -1
- package/dist/components/AdvanceMenu/index.d.ts +78 -0
- package/dist/components/AdvanceMenu/index.js +1 -0
- package/dist/components/AuthorInfo/index.d.ts +5 -1
- package/dist/components/AuthorInfo/index.js +1 -1
- package/dist/components/BottomSheet/index.d.ts +82 -0
- package/dist/components/BottomSheet/index.js +1 -0
- package/dist/components/CleanModeOverlay/index.d.ts +60 -0
- package/dist/components/CleanModeOverlay/index.js +1 -0
- package/dist/components/CommentSheet/index.d.ts +164 -0
- package/dist/components/CommentSheet/index.js +1 -0
- package/dist/components/DetailView/index.d.ts +311 -0
- package/dist/components/DetailView/index.js +1 -0
- package/dist/components/ErrorBoundary/index.js +1 -1
- package/dist/components/ImageCarousel/index.d.ts +50 -0
- package/dist/components/ImageCarousel/index.js +1 -0
- package/dist/components/ImagePostSlot/index.d.ts +207 -0
- package/dist/components/ImagePostSlot/index.js +1 -0
- package/dist/components/ProgressBar/index.d.ts +30 -2
- package/dist/components/ProgressBar/index.js +1 -1
- package/dist/components/QualityPicker/index.d.ts +35 -0
- package/dist/components/QualityPicker/index.js +1 -0
- package/dist/components/ReportSheet/index.d.ts +68 -0
- package/dist/components/ReportSheet/index.js +1 -0
- package/dist/components/Skeleton/index.js +1 -1
- package/dist/components/SpeedPicker/index.d.ts +32 -0
- package/dist/components/SpeedPicker/index.js +1 -0
- package/dist/components/VideoFeed/index.d.ts +12 -1
- package/dist/components/VideoFeed/index.js +1 -1
- package/dist/components/VideoInfo/index.d.ts +4 -2
- package/dist/components/VideoInfo/index.js +1 -1
- package/dist/components/VideoPlayer/index.d.ts +14 -41
- package/dist/components/VideoPlayer/index.js +1 -1
- package/dist/components/VideoSlot/index.d.ts +84 -65
- package/dist/components/VideoSlot/index.js +2 -1
- package/dist/components/VirtualSlider/index.d.ts +339 -0
- package/dist/components/VirtualSlider/index.js +1 -0
- package/dist/components/icons/index.js +1 -1
- package/dist/index.d.ts +107 -95
- package/dist/index.js +84 -27
- package/package.json +51 -7
- package/dist/chunk-2PTMP65P.js +0 -738
- package/dist/chunk-4MN72OZH.js +0 -148
- package/dist/chunk-DHQJBXQW.js +0 -562
- package/dist/chunk-SSJDO24Q.js +0 -204
- package/dist/chunk-XAOEHLOX.js +0 -1326
- package/dist/chunk-YW23IBKF.js +0 -530
- package/dist/chunk-ZZDQKP4R.js +0 -418
- package/dist/use-gesture-react.esm-3SV4QLEJ.js +0 -1893
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
import { PauseIcon, PlayIcon } from './chunk-ANCP53F3.js';
|
|
2
|
+
import { createContext, memo, useState, useRef, useEffect, useCallback, useContext } from 'react';
|
|
3
|
+
import { jsx } from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
var DEFAULT_DOUBLE_TAP_DELAY = 300;
|
|
6
|
+
var DEFAULT_LONG_PRESS_DELAY = 500;
|
|
7
|
+
var TAP_DEBOUNCE_MS = 50;
|
|
8
|
+
var DEFAULT_MOVEMENT_THRESHOLD = 10;
|
|
9
|
+
function useDoubleTap({
|
|
10
|
+
containerRef,
|
|
11
|
+
onSingleTap,
|
|
12
|
+
onDoubleTap,
|
|
13
|
+
onLongPress,
|
|
14
|
+
doubleTapDelay = DEFAULT_DOUBLE_TAP_DELAY,
|
|
15
|
+
longPressDelay = DEFAULT_LONG_PRESS_DELAY,
|
|
16
|
+
disabled = false,
|
|
17
|
+
movementThreshold = DEFAULT_MOVEMENT_THRESHOLD
|
|
18
|
+
}) {
|
|
19
|
+
const lastTapTimeRef = useRef(0);
|
|
20
|
+
const lastDoubleTapTimeRef = useRef(0);
|
|
21
|
+
const tapTimeoutRef = useRef(null);
|
|
22
|
+
const longPressTimeoutRef = useRef(null);
|
|
23
|
+
const longPressTriggeredRef = useRef(false);
|
|
24
|
+
const lastPositionRef = useRef({ x: 50, y: 50 });
|
|
25
|
+
const lastExecutionTimeRef = useRef(0);
|
|
26
|
+
const pointerStartRef = useRef(null);
|
|
27
|
+
const cumulativeMovementRef = useRef(0);
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
return () => {
|
|
30
|
+
if (tapTimeoutRef.current) {
|
|
31
|
+
clearTimeout(tapTimeoutRef.current);
|
|
32
|
+
tapTimeoutRef.current = null;
|
|
33
|
+
}
|
|
34
|
+
if (longPressTimeoutRef.current) {
|
|
35
|
+
clearTimeout(longPressTimeoutRef.current);
|
|
36
|
+
longPressTimeoutRef.current = null;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}, []);
|
|
40
|
+
const calculatePosition = useCallback(
|
|
41
|
+
(clientX, clientY) => {
|
|
42
|
+
const container = containerRef.current;
|
|
43
|
+
if (!container) return { x: 50, y: 50 };
|
|
44
|
+
const rect = container.getBoundingClientRect();
|
|
45
|
+
const x = (clientX - rect.left) / rect.width * 100;
|
|
46
|
+
const y = (clientY - rect.top) / rect.height * 100;
|
|
47
|
+
return {
|
|
48
|
+
x: Math.max(0, Math.min(100, x)),
|
|
49
|
+
y: Math.max(0, Math.min(100, y))
|
|
50
|
+
};
|
|
51
|
+
},
|
|
52
|
+
[containerRef]
|
|
53
|
+
);
|
|
54
|
+
const handleTapCore = useCallback(() => {
|
|
55
|
+
if (disabled) return;
|
|
56
|
+
const now = Date.now();
|
|
57
|
+
const timeSinceLastTap = now - lastTapTimeRef.current;
|
|
58
|
+
if (tapTimeoutRef.current) {
|
|
59
|
+
clearTimeout(tapTimeoutRef.current);
|
|
60
|
+
tapTimeoutRef.current = null;
|
|
61
|
+
}
|
|
62
|
+
if (timeSinceLastTap < doubleTapDelay) {
|
|
63
|
+
lastDoubleTapTimeRef.current = now;
|
|
64
|
+
lastTapTimeRef.current = 0;
|
|
65
|
+
onDoubleTap?.(lastPositionRef.current);
|
|
66
|
+
} else {
|
|
67
|
+
lastTapTimeRef.current = now;
|
|
68
|
+
tapTimeoutRef.current = setTimeout(() => {
|
|
69
|
+
tapTimeoutRef.current = null;
|
|
70
|
+
const timeSinceDoubleTap = Date.now() - lastDoubleTapTimeRef.current;
|
|
71
|
+
if (lastDoubleTapTimeRef.current > 0 && timeSinceDoubleTap < doubleTapDelay * 1.5) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (lastTapTimeRef.current !== now) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
onSingleTap?.();
|
|
78
|
+
}, doubleTapDelay);
|
|
79
|
+
}
|
|
80
|
+
}, [disabled, doubleTapDelay, onSingleTap, onDoubleTap]);
|
|
81
|
+
const isSwipe = useCallback(
|
|
82
|
+
(clientX, clientY) => {
|
|
83
|
+
if (cumulativeMovementRef.current > movementThreshold * 2) {
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
const start = pointerStartRef.current;
|
|
87
|
+
if (!start) return false;
|
|
88
|
+
const dx = Math.abs(clientX - start.x);
|
|
89
|
+
const dy = Math.abs(clientY - start.y);
|
|
90
|
+
return dx > movementThreshold || dy > movementThreshold;
|
|
91
|
+
},
|
|
92
|
+
[movementThreshold]
|
|
93
|
+
);
|
|
94
|
+
const executeTapWithDebounce = useCallback(
|
|
95
|
+
(clientX, clientY) => {
|
|
96
|
+
if (disabled) return;
|
|
97
|
+
if (isSwipe(clientX, clientY)) {
|
|
98
|
+
pointerStartRef.current = null;
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const now = Date.now();
|
|
102
|
+
if (now - lastExecutionTimeRef.current < TAP_DEBOUNCE_MS) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
lastExecutionTimeRef.current = now;
|
|
106
|
+
lastPositionRef.current = calculatePosition(clientX, clientY);
|
|
107
|
+
handleTapCore();
|
|
108
|
+
pointerStartRef.current = null;
|
|
109
|
+
},
|
|
110
|
+
[disabled, calculatePosition, handleTapCore, isSwipe]
|
|
111
|
+
);
|
|
112
|
+
const cancelLongPress = useCallback(() => {
|
|
113
|
+
if (longPressTimeoutRef.current) {
|
|
114
|
+
clearTimeout(longPressTimeoutRef.current);
|
|
115
|
+
longPressTimeoutRef.current = null;
|
|
116
|
+
}
|
|
117
|
+
}, []);
|
|
118
|
+
const handlePointerDown = useCallback(
|
|
119
|
+
(e) => {
|
|
120
|
+
if (e.button !== 0) return;
|
|
121
|
+
pointerStartRef.current = { x: e.clientX, y: e.clientY };
|
|
122
|
+
cumulativeMovementRef.current = 0;
|
|
123
|
+
longPressTriggeredRef.current = false;
|
|
124
|
+
if (onLongPress && !disabled) {
|
|
125
|
+
cancelLongPress();
|
|
126
|
+
const position = calculatePosition(e.clientX, e.clientY);
|
|
127
|
+
longPressTimeoutRef.current = setTimeout(() => {
|
|
128
|
+
longPressTimeoutRef.current = null;
|
|
129
|
+
if (cumulativeMovementRef.current <= movementThreshold * 2) {
|
|
130
|
+
longPressTriggeredRef.current = true;
|
|
131
|
+
onLongPress(position);
|
|
132
|
+
}
|
|
133
|
+
}, longPressDelay);
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
[onLongPress, disabled, longPressDelay, movementThreshold, calculatePosition, cancelLongPress]
|
|
137
|
+
);
|
|
138
|
+
const handlePointerMove = useCallback(
|
|
139
|
+
(e) => {
|
|
140
|
+
if (!pointerStartRef.current) return;
|
|
141
|
+
cumulativeMovementRef.current += Math.abs(e.movementX) + Math.abs(e.movementY);
|
|
142
|
+
if (cumulativeMovementRef.current > movementThreshold * 2) {
|
|
143
|
+
cancelLongPress();
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
[movementThreshold, cancelLongPress]
|
|
147
|
+
);
|
|
148
|
+
const handlePointerUp = useCallback(
|
|
149
|
+
(e) => {
|
|
150
|
+
if (e.button !== 0) return;
|
|
151
|
+
cancelLongPress();
|
|
152
|
+
if (longPressTriggeredRef.current) {
|
|
153
|
+
longPressTriggeredRef.current = false;
|
|
154
|
+
pointerStartRef.current = null;
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
executeTapWithDebounce(e.clientX, e.clientY);
|
|
158
|
+
},
|
|
159
|
+
[executeTapWithDebounce, cancelLongPress]
|
|
160
|
+
);
|
|
161
|
+
const handleClick = useCallback(
|
|
162
|
+
(e) => {
|
|
163
|
+
if (e.detail > 0) {
|
|
164
|
+
executeTapWithDebounce(e.clientX, e.clientY);
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
[executeTapWithDebounce]
|
|
168
|
+
);
|
|
169
|
+
return {
|
|
170
|
+
handlers: {
|
|
171
|
+
onPointerDown: handlePointerDown,
|
|
172
|
+
onPointerMove: handlePointerMove,
|
|
173
|
+
onPointerUp: handlePointerUp,
|
|
174
|
+
onClick: handleClick
|
|
175
|
+
},
|
|
176
|
+
lastPosition: lastPositionRef
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
function invariantContextError(hookName, componentName) {
|
|
180
|
+
const baseMessage = `${hookName} must be used within a VideoSlot component.`;
|
|
181
|
+
if (process.env.NODE_ENV !== "production") {
|
|
182
|
+
const debugInfo = [
|
|
183
|
+
"",
|
|
184
|
+
"\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550",
|
|
185
|
+
"\u274C VideoSlot Context Error",
|
|
186
|
+
"\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550",
|
|
187
|
+
"",
|
|
188
|
+
`Hook: ${hookName}`,
|
|
189
|
+
"",
|
|
190
|
+
"",
|
|
191
|
+
"Possible causes:",
|
|
192
|
+
" 1. You used a VideoSlot compound component outside of <VideoSlot>",
|
|
193
|
+
" 2. You used the headless component without SDK wiring",
|
|
194
|
+
" 3. The component tree was rendered in isolation (e.g., Storybook)",
|
|
195
|
+
"",
|
|
196
|
+
"Solutions:",
|
|
197
|
+
" 1. Wrap with <VideoSlot> or <VideoSlotHeadless>",
|
|
198
|
+
" 2. Use useOptionalVideoSlotContext() if context is optional",
|
|
199
|
+
" 3. Provide mock context in tests via <VideoSlotContext.Provider>",
|
|
200
|
+
"",
|
|
201
|
+
"Example:",
|
|
202
|
+
" // Correct usage",
|
|
203
|
+
" <VideoSlot video={video}>",
|
|
204
|
+
" <VideoSlotPoster /> \u2190 These need context",
|
|
205
|
+
" <VideoSlotOverlay />",
|
|
206
|
+
" </VideoSlot>",
|
|
207
|
+
"",
|
|
208
|
+
"\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"
|
|
209
|
+
].filter(Boolean).join("\n");
|
|
210
|
+
throw new Error(baseMessage + debugInfo);
|
|
211
|
+
}
|
|
212
|
+
throw new Error(baseMessage);
|
|
213
|
+
}
|
|
214
|
+
var VideoSlotContext = createContext(null);
|
|
215
|
+
VideoSlotContext.displayName = "VideoSlotContext";
|
|
216
|
+
function useVideoSlotContext() {
|
|
217
|
+
const context = useContext(VideoSlotContext);
|
|
218
|
+
if (!context) {
|
|
219
|
+
invariantContextError("useVideoSlotContext");
|
|
220
|
+
}
|
|
221
|
+
return context;
|
|
222
|
+
}
|
|
223
|
+
function useOptionalVideoSlotContext() {
|
|
224
|
+
return useContext(VideoSlotContext);
|
|
225
|
+
}
|
|
226
|
+
function useVideoSlotVideo() {
|
|
227
|
+
const { video } = useVideoSlotContext();
|
|
228
|
+
return video;
|
|
229
|
+
}
|
|
230
|
+
function useVideoSlotPlayer() {
|
|
231
|
+
const { playerState, playerControls } = useVideoSlotContext();
|
|
232
|
+
return { state: playerState, controls: playerControls };
|
|
233
|
+
}
|
|
234
|
+
function useVideoSlotResource() {
|
|
235
|
+
const { resourceState } = useVideoSlotContext();
|
|
236
|
+
return resourceState;
|
|
237
|
+
}
|
|
238
|
+
function useVideoSlotIsActive() {
|
|
239
|
+
const { resourceState } = useVideoSlotContext();
|
|
240
|
+
return resourceState.isActive;
|
|
241
|
+
}
|
|
242
|
+
function useVideoSlotIsPlaying() {
|
|
243
|
+
const { playerState } = useVideoSlotContext();
|
|
244
|
+
return playerState.isPlaying;
|
|
245
|
+
}
|
|
246
|
+
function DefaultPlayIcon({ size }) {
|
|
247
|
+
return /* @__PURE__ */ jsx(PlayIcon, { size });
|
|
248
|
+
}
|
|
249
|
+
function DefaultPauseIcon({ size }) {
|
|
250
|
+
return /* @__PURE__ */ jsx(PauseIcon, { size });
|
|
251
|
+
}
|
|
252
|
+
var VideoSlotPlayIndicatorInner = memo(function VideoSlotPlayIndicatorInner2({
|
|
253
|
+
playIcon,
|
|
254
|
+
pauseIcon,
|
|
255
|
+
size = 48,
|
|
256
|
+
duration = 600,
|
|
257
|
+
persistWhenPaused = true,
|
|
258
|
+
className,
|
|
259
|
+
testId,
|
|
260
|
+
lastUserToggleTime,
|
|
261
|
+
isPlaying
|
|
262
|
+
}) {
|
|
263
|
+
const [showIndicator, setShowIndicator] = useState(false);
|
|
264
|
+
const [isPersisting, setIsPersisting] = useState(false);
|
|
265
|
+
const [isAnimatingOut, setIsAnimatingOut] = useState(false);
|
|
266
|
+
const [iconType, setIconType] = useState("play");
|
|
267
|
+
const hideTimeoutRef = useRef(null);
|
|
268
|
+
const lastHandledToggleTimeRef = useRef(0);
|
|
269
|
+
const isPlayingRef = useRef(isPlaying);
|
|
270
|
+
isPlayingRef.current = isPlaying;
|
|
271
|
+
const persistWhenPausedRef = useRef(persistWhenPaused);
|
|
272
|
+
persistWhenPausedRef.current = persistWhenPaused;
|
|
273
|
+
const prevIsPlayingRef = useRef(isPlaying);
|
|
274
|
+
const clearHideTimeout = () => {
|
|
275
|
+
if (hideTimeoutRef.current) {
|
|
276
|
+
clearTimeout(hideTimeoutRef.current);
|
|
277
|
+
hideTimeoutRef.current = null;
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
useEffect(() => {
|
|
281
|
+
const wasPlaying = prevIsPlayingRef.current;
|
|
282
|
+
prevIsPlayingRef.current = isPlaying;
|
|
283
|
+
if (!wasPlaying && isPlaying && isPersisting) {
|
|
284
|
+
const timeSinceLastToggle = Date.now() - lastUserToggleTime;
|
|
285
|
+
const isAutoResume = timeSinceLastToggle > 100;
|
|
286
|
+
if (isAutoResume) {
|
|
287
|
+
if (hideTimeoutRef.current) {
|
|
288
|
+
clearTimeout(hideTimeoutRef.current);
|
|
289
|
+
hideTimeoutRef.current = null;
|
|
290
|
+
}
|
|
291
|
+
setShowIndicator(false);
|
|
292
|
+
setIsPersisting(false);
|
|
293
|
+
setIsAnimatingOut(false);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}, [isPlaying, isPersisting, lastUserToggleTime]);
|
|
297
|
+
useEffect(() => {
|
|
298
|
+
if (lastUserToggleTime === 0 || lastUserToggleTime === lastHandledToggleTimeRef.current) {
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
lastHandledToggleTimeRef.current = lastUserToggleTime;
|
|
302
|
+
if (isPersisting && isPlaying) {
|
|
303
|
+
setIconType("pause");
|
|
304
|
+
setIsPersisting(false);
|
|
305
|
+
setIsAnimatingOut(true);
|
|
306
|
+
clearHideTimeout();
|
|
307
|
+
hideTimeoutRef.current = setTimeout(() => {
|
|
308
|
+
setShowIndicator(false);
|
|
309
|
+
setIsAnimatingOut(false);
|
|
310
|
+
hideTimeoutRef.current = null;
|
|
311
|
+
}, duration);
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
setShowIndicator(true);
|
|
315
|
+
setIsPersisting(false);
|
|
316
|
+
setIsAnimatingOut(false);
|
|
317
|
+
setIconType("play");
|
|
318
|
+
clearHideTimeout();
|
|
319
|
+
hideTimeoutRef.current = setTimeout(() => {
|
|
320
|
+
const currentIsPlaying = isPlayingRef.current;
|
|
321
|
+
const shouldPersist = persistWhenPausedRef.current;
|
|
322
|
+
if (shouldPersist && !currentIsPlaying) {
|
|
323
|
+
setIsPersisting(true);
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
setShowIndicator(false);
|
|
327
|
+
hideTimeoutRef.current = null;
|
|
328
|
+
}, duration);
|
|
329
|
+
return clearHideTimeout;
|
|
330
|
+
}, [lastUserToggleTime, duration, isPersisting, isPlaying]);
|
|
331
|
+
if (!showIndicator && !isPersisting) return null;
|
|
332
|
+
const iconToShow = iconType === "pause" ? pauseIcon ?? /* @__PURE__ */ jsx(DefaultPauseIcon, { size }) : playIcon ?? /* @__PURE__ */ jsx(DefaultPlayIcon, { size });
|
|
333
|
+
const indicatorClass = [
|
|
334
|
+
"sv-video-slot__play-indicator",
|
|
335
|
+
showIndicator && "sv-video-slot__play-indicator--visible",
|
|
336
|
+
isPersisting && "sv-video-slot__play-indicator--persist",
|
|
337
|
+
isAnimatingOut && "sv-video-slot__play-indicator--animating-out",
|
|
338
|
+
className
|
|
339
|
+
].filter(Boolean).join(" ");
|
|
340
|
+
return /* @__PURE__ */ jsx(
|
|
341
|
+
"div",
|
|
342
|
+
{
|
|
343
|
+
className: indicatorClass,
|
|
344
|
+
style: { "--sv-indicator-duration": `${duration}ms` },
|
|
345
|
+
"aria-hidden": "true",
|
|
346
|
+
"data-testid": testId,
|
|
347
|
+
children: /* @__PURE__ */ jsx("span", { className: "sv-video-slot__play-indicator-icon", children: iconToShow })
|
|
348
|
+
}
|
|
349
|
+
);
|
|
350
|
+
});
|
|
351
|
+
VideoSlotPlayIndicatorInner.displayName = "VideoSlotPlayIndicatorInner";
|
|
352
|
+
function VideoSlotPlayIndicator(props) {
|
|
353
|
+
const context = useOptionalVideoSlotContext();
|
|
354
|
+
if (!context) return null;
|
|
355
|
+
const lastUserToggleTime = context.lastUserToggleTime ?? 0;
|
|
356
|
+
const isPlaying = context.playerState?.isPlaying ?? false;
|
|
357
|
+
return /* @__PURE__ */ jsx(
|
|
358
|
+
VideoSlotPlayIndicatorInner,
|
|
359
|
+
{
|
|
360
|
+
...props,
|
|
361
|
+
lastUserToggleTime,
|
|
362
|
+
isPlaying
|
|
363
|
+
}
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
VideoSlotPlayIndicator.displayName = "VideoSlotPlayIndicator";
|
|
367
|
+
|
|
368
|
+
export { VideoSlotContext, VideoSlotPlayIndicator, VideoSlotPlayIndicatorInner, useDoubleTap, useOptionalVideoSlotContext, useVideoSlotContext, useVideoSlotIsActive, useVideoSlotIsPlaying, useVideoSlotPlayer, useVideoSlotResource, useVideoSlotVideo };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { injectComponentCSS } from './chunk-
|
|
3
|
-
import {
|
|
1
|
+
import { clsx2 } from './chunk-EDWS2IPH.js';
|
|
2
|
+
import { injectComponentCSS } from './chunk-CAWE42LH.js';
|
|
3
|
+
import { memo, useInsertionEffect, useCallback, useMemo, useRef, useState, useEffect } from 'react';
|
|
4
4
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
5
5
|
|
|
6
6
|
// src/components/VideoPlayer/VideoPlayer.css.ts
|
|
@@ -35,6 +35,7 @@ var VIDEO_PLAYER_CSS = (
|
|
|
35
35
|
align-items: center;
|
|
36
36
|
justify-content: center;
|
|
37
37
|
z-index: var(--sv-player-video-z);
|
|
38
|
+
background: var(--sv-bg-primary, #000);
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
|
|
@@ -653,6 +654,7 @@ function useVideoElement(config) {
|
|
|
653
654
|
} = config;
|
|
654
655
|
const videoRef = useRef(null);
|
|
655
656
|
const hlsRef = useRef(null);
|
|
657
|
+
const prevTimeRef = useRef(0);
|
|
656
658
|
const [isLoading, setIsLoading] = useState(true);
|
|
657
659
|
const [isBuffering, setIsBuffering] = useState(false);
|
|
658
660
|
const [isReady, setIsReady] = useState(false);
|
|
@@ -752,7 +754,15 @@ function useVideoElement(config) {
|
|
|
752
754
|
onEnded?.();
|
|
753
755
|
};
|
|
754
756
|
const handleTimeUpdate = () => {
|
|
755
|
-
|
|
757
|
+
const currentTime = video.currentTime;
|
|
758
|
+
const duration = video.duration;
|
|
759
|
+
const prevTime = prevTimeRef.current;
|
|
760
|
+
if (loop && duration > 0 && prevTime > duration * 0.9 && // Was near end (>90%)
|
|
761
|
+
currentTime < duration * 0.1) {
|
|
762
|
+
onEnded?.();
|
|
763
|
+
}
|
|
764
|
+
prevTimeRef.current = currentTime;
|
|
765
|
+
onTimeUpdate?.(currentTime);
|
|
756
766
|
};
|
|
757
767
|
const handleDurationChange = () => {
|
|
758
768
|
onDurationChange?.(video.duration);
|
|
@@ -819,6 +829,7 @@ function useVideoElement(config) {
|
|
|
819
829
|
};
|
|
820
830
|
}, [
|
|
821
831
|
autoPlay,
|
|
832
|
+
loop,
|
|
822
833
|
onCanPlay,
|
|
823
834
|
onPlay,
|
|
824
835
|
onPlaying,
|
|
@@ -920,7 +931,7 @@ function getMediaErrorMessage(code) {
|
|
|
920
931
|
return "Unknown video error";
|
|
921
932
|
}
|
|
922
933
|
}
|
|
923
|
-
function
|
|
934
|
+
function VideoPlayerHeadlessBase({
|
|
924
935
|
src,
|
|
925
936
|
type,
|
|
926
937
|
poster,
|
|
@@ -1021,7 +1032,7 @@ function VideoPlayerHeadless({
|
|
|
1021
1032
|
return /* @__PURE__ */ jsxs(
|
|
1022
1033
|
"div",
|
|
1023
1034
|
{
|
|
1024
|
-
className:
|
|
1035
|
+
className: clsx2(PLAYER_CLASS, stateClasses, className),
|
|
1025
1036
|
...{ [VIDEO_TYPE_ATTR]: type },
|
|
1026
1037
|
...{ [PLAYBACK_STATE_ATTR]: playbackState },
|
|
1027
1038
|
style: {
|
|
@@ -1118,6 +1129,7 @@ function getUserFriendlyErrorMessage(message) {
|
|
|
1118
1129
|
}
|
|
1119
1130
|
return "Failed to load video. Please try again.";
|
|
1120
1131
|
}
|
|
1132
|
+
var VideoPlayerHeadless = memo(VideoPlayerHeadlessBase);
|
|
1121
1133
|
VideoPlayerHeadless.displayName = "VideoPlayerHeadless";
|
|
1122
1134
|
|
|
1123
1135
|
export { BUFFERING_STATE_CLASS, DEFAULT_OBJECT_FIT, DEFAULT_PRELOAD, ENDED_CLASS, ERROR_CLASS, ERROR_STATE_CLASS, FIRST_FRAME_MAX_WIDTH, FIRST_FRAME_QUALITY, LOADING_CLASS, LOADING_STATE_ATTR, LOADING_STATE_CLASS, PAUSED_CLASS, PLAYBACK_STATE_ATTR, PLAYER_CLASS, PLAYING_CLASS, POSTER_CLASS, READY_CLASS, VIDEO_CLASS, VIDEO_PLAYER_CSS, VIDEO_TYPE_ATTR, VIDEO_WRAPPER_CLASS, VideoElementError, VideoPlayerHeadless, Z_INDEX, Z_INDEX_CSS_VARS, createFirstFrameCache, firstFrameCache, useAutoFirstFrameCapture, useFirstFrameCapture, useVideoElement };
|