analytica-frontend-lib 1.1.10 → 1.1.12
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/VideoPlayer/index.js +367 -183
- package/dist/VideoPlayer/index.js.map +1 -1
- package/dist/VideoPlayer/index.mjs +373 -184
- package/dist/VideoPlayer/index.mjs.map +1 -1
- package/dist/index.css +10 -3
- package/dist/index.css.map +1 -1
- package/dist/index.js +367 -183
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +373 -184
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +10 -3
- package/dist/styles.css.map +1 -1
- package/package.json +1 -1
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
// src/components/VideoPlayer/VideoPlayer.tsx
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
useRef,
|
|
4
|
+
useState,
|
|
5
|
+
useEffect,
|
|
6
|
+
useCallback
|
|
7
|
+
} from "react";
|
|
3
8
|
import {
|
|
4
9
|
Play,
|
|
5
10
|
Pause,
|
|
@@ -124,12 +129,93 @@ var Text_default = Text;
|
|
|
124
129
|
|
|
125
130
|
// src/components/VideoPlayer/VideoPlayer.tsx
|
|
126
131
|
import { jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
132
|
+
var CONTROLS_HIDE_TIMEOUT = 3e3;
|
|
133
|
+
var LEAVE_HIDE_TIMEOUT = 1e3;
|
|
127
134
|
var formatTime = (seconds) => {
|
|
128
135
|
if (!seconds || isNaN(seconds)) return "0:00";
|
|
129
136
|
const mins = Math.floor(seconds / 60);
|
|
130
137
|
const secs = Math.floor(seconds % 60);
|
|
131
138
|
return `${mins}:${secs.toString().padStart(2, "0")}`;
|
|
132
139
|
};
|
|
140
|
+
var ProgressBar = ({
|
|
141
|
+
currentTime,
|
|
142
|
+
duration,
|
|
143
|
+
progressPercentage,
|
|
144
|
+
onSeek
|
|
145
|
+
}) => /* @__PURE__ */ jsx3("div", { className: "px-4 pb-2", children: /* @__PURE__ */ jsx3(
|
|
146
|
+
"input",
|
|
147
|
+
{
|
|
148
|
+
type: "range",
|
|
149
|
+
min: 0,
|
|
150
|
+
max: duration || 100,
|
|
151
|
+
value: currentTime,
|
|
152
|
+
onChange: (e) => onSeek(parseFloat(e.target.value)),
|
|
153
|
+
className: "w-full h-1 bg-neutral-600 rounded-full appearance-none cursor-pointer slider:bg-primary-600 focus:outline-none focus:ring-2 focus:ring-primary-500",
|
|
154
|
+
"aria-label": "Video progress",
|
|
155
|
+
style: {
|
|
156
|
+
background: `linear-gradient(to right, var(--color-primary-700) ${progressPercentage}%, var(--color-secondary-300) ${progressPercentage}%)`
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
) });
|
|
160
|
+
var VolumeControls = ({
|
|
161
|
+
volume,
|
|
162
|
+
isMuted,
|
|
163
|
+
onVolumeChange,
|
|
164
|
+
onToggleMute
|
|
165
|
+
}) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
166
|
+
/* @__PURE__ */ jsx3(
|
|
167
|
+
IconButton_default,
|
|
168
|
+
{
|
|
169
|
+
icon: isMuted ? /* @__PURE__ */ jsx3(SpeakerSlash, { size: 24 }) : /* @__PURE__ */ jsx3(SpeakerHigh, { size: 24 }),
|
|
170
|
+
onClick: onToggleMute,
|
|
171
|
+
"aria-label": isMuted ? "Unmute" : "Mute",
|
|
172
|
+
className: "!bg-transparent !text-white hover:!bg-white/20"
|
|
173
|
+
}
|
|
174
|
+
),
|
|
175
|
+
/* @__PURE__ */ jsx3(
|
|
176
|
+
"input",
|
|
177
|
+
{
|
|
178
|
+
type: "range",
|
|
179
|
+
min: 0,
|
|
180
|
+
max: 100,
|
|
181
|
+
value: Math.round(volume * 100),
|
|
182
|
+
onChange: (e) => onVolumeChange(parseInt(e.target.value)),
|
|
183
|
+
className: "w-20 h-1 bg-neutral-600 rounded-full appearance-none cursor-pointer focus:outline-none focus:ring-2 focus:ring-primary-500",
|
|
184
|
+
"aria-label": "Volume control",
|
|
185
|
+
style: {
|
|
186
|
+
background: `linear-gradient(to right, var(--color-primary-700) ${volume * 100}%, var(--color-secondary-300) ${volume * 100}%)`
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
)
|
|
190
|
+
] });
|
|
191
|
+
var SpeedMenu = ({
|
|
192
|
+
showSpeedMenu,
|
|
193
|
+
playbackRate,
|
|
194
|
+
onToggleMenu,
|
|
195
|
+
onSpeedChange
|
|
196
|
+
}) => /* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
197
|
+
/* @__PURE__ */ jsx3(
|
|
198
|
+
IconButton_default,
|
|
199
|
+
{
|
|
200
|
+
icon: /* @__PURE__ */ jsx3(DotsThreeVertical, { size: 24 }),
|
|
201
|
+
onClick: onToggleMenu,
|
|
202
|
+
"aria-label": "Playback speed",
|
|
203
|
+
className: "!bg-transparent !text-white hover:!bg-white/20"
|
|
204
|
+
}
|
|
205
|
+
),
|
|
206
|
+
showSpeedMenu && /* @__PURE__ */ jsx3("div", { className: "absolute bottom-12 right-0 bg-black/90 rounded-lg p-2 min-w-20", children: [0.5, 0.75, 1, 1.25, 1.5, 2].map((speed) => /* @__PURE__ */ jsxs(
|
|
207
|
+
"button",
|
|
208
|
+
{
|
|
209
|
+
onClick: () => onSpeedChange(speed),
|
|
210
|
+
className: `block w-full text-left px-3 py-1 text-sm rounded hover:bg-white/20 transition-colors ${playbackRate === speed ? "text-primary-400" : "text-white"}`,
|
|
211
|
+
children: [
|
|
212
|
+
speed,
|
|
213
|
+
"x"
|
|
214
|
+
]
|
|
215
|
+
},
|
|
216
|
+
speed
|
|
217
|
+
)) })
|
|
218
|
+
] });
|
|
133
219
|
var VideoPlayer = ({
|
|
134
220
|
src,
|
|
135
221
|
poster,
|
|
@@ -154,10 +240,68 @@ var VideoPlayer = ({
|
|
|
154
240
|
const [showControls, setShowControls] = useState(true);
|
|
155
241
|
const [hasCompleted, setHasCompleted] = useState(false);
|
|
156
242
|
const [showCaptions, setShowCaptions] = useState(false);
|
|
243
|
+
useEffect(() => {
|
|
244
|
+
setHasCompleted(false);
|
|
245
|
+
}, [src]);
|
|
157
246
|
const [playbackRate, setPlaybackRate] = useState(1);
|
|
158
247
|
const [showSpeedMenu, setShowSpeedMenu] = useState(false);
|
|
159
248
|
const lastSaveTimeRef = useRef(0);
|
|
160
249
|
const trackRef = useRef(null);
|
|
250
|
+
const controlsTimeoutRef = useRef(null);
|
|
251
|
+
const lastMousePositionRef = useRef({ x: 0, y: 0 });
|
|
252
|
+
const mouseMoveTimeoutRef = useRef(null);
|
|
253
|
+
const isUserInteracting = useCallback(() => {
|
|
254
|
+
if (showSpeedMenu) return true;
|
|
255
|
+
const activeElement = document.activeElement;
|
|
256
|
+
const videoContainer = videoRef.current?.parentElement;
|
|
257
|
+
if (activeElement && videoContainer?.contains(activeElement)) {
|
|
258
|
+
const isControl = activeElement.matches("button, input, [tabindex]");
|
|
259
|
+
if (isControl) return true;
|
|
260
|
+
}
|
|
261
|
+
return false;
|
|
262
|
+
}, [showSpeedMenu]);
|
|
263
|
+
const clearControlsTimeout = useCallback(() => {
|
|
264
|
+
if (controlsTimeoutRef.current) {
|
|
265
|
+
clearTimeout(controlsTimeoutRef.current);
|
|
266
|
+
controlsTimeoutRef.current = null;
|
|
267
|
+
}
|
|
268
|
+
}, []);
|
|
269
|
+
const clearMouseMoveTimeout = useCallback(() => {
|
|
270
|
+
if (mouseMoveTimeoutRef.current) {
|
|
271
|
+
clearTimeout(mouseMoveTimeoutRef.current);
|
|
272
|
+
mouseMoveTimeoutRef.current = null;
|
|
273
|
+
}
|
|
274
|
+
}, []);
|
|
275
|
+
const showControlsWithTimer = useCallback(() => {
|
|
276
|
+
setShowControls(true);
|
|
277
|
+
clearControlsTimeout();
|
|
278
|
+
if (isPlaying) {
|
|
279
|
+
controlsTimeoutRef.current = window.setTimeout(() => {
|
|
280
|
+
setShowControls(false);
|
|
281
|
+
}, CONTROLS_HIDE_TIMEOUT);
|
|
282
|
+
}
|
|
283
|
+
}, [isPlaying, clearControlsTimeout]);
|
|
284
|
+
const handleMouseMove = useCallback(
|
|
285
|
+
(event) => {
|
|
286
|
+
const currentX = event.clientX;
|
|
287
|
+
const currentY = event.clientY;
|
|
288
|
+
const lastPos = lastMousePositionRef.current;
|
|
289
|
+
const hasMoved = Math.abs(currentX - lastPos.x) > 5 || Math.abs(currentY - lastPos.y) > 5;
|
|
290
|
+
if (hasMoved) {
|
|
291
|
+
lastMousePositionRef.current = { x: currentX, y: currentY };
|
|
292
|
+
showControlsWithTimer();
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
[showControlsWithTimer]
|
|
296
|
+
);
|
|
297
|
+
const handleMouseLeave = useCallback(() => {
|
|
298
|
+
clearControlsTimeout();
|
|
299
|
+
if (isPlaying && !isUserInteracting()) {
|
|
300
|
+
controlsTimeoutRef.current = window.setTimeout(() => {
|
|
301
|
+
setShowControls(false);
|
|
302
|
+
}, LEAVE_HIDE_TIMEOUT);
|
|
303
|
+
}
|
|
304
|
+
}, [isPlaying, clearControlsTimeout, isUserInteracting]);
|
|
161
305
|
useEffect(() => {
|
|
162
306
|
if (videoRef.current) {
|
|
163
307
|
videoRef.current.volume = volume;
|
|
@@ -165,84 +309,129 @@ var VideoPlayer = ({
|
|
|
165
309
|
}
|
|
166
310
|
}, [volume, isMuted]);
|
|
167
311
|
useEffect(() => {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
const
|
|
171
|
-
const
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
312
|
+
const video = videoRef.current;
|
|
313
|
+
if (!video) return;
|
|
314
|
+
const onPlay = () => setIsPlaying(true);
|
|
315
|
+
const onPause = () => setIsPlaying(false);
|
|
316
|
+
const onEnded = () => setIsPlaying(false);
|
|
317
|
+
video.addEventListener("play", onPlay);
|
|
318
|
+
video.addEventListener("pause", onPause);
|
|
319
|
+
video.addEventListener("ended", onEnded);
|
|
320
|
+
return () => {
|
|
321
|
+
video.removeEventListener("play", onPlay);
|
|
322
|
+
video.removeEventListener("pause", onPause);
|
|
323
|
+
video.removeEventListener("ended", onEnded);
|
|
324
|
+
};
|
|
325
|
+
}, []);
|
|
326
|
+
useEffect(() => {
|
|
327
|
+
if (isPlaying) {
|
|
328
|
+
showControlsWithTimer();
|
|
178
329
|
} else {
|
|
179
|
-
|
|
330
|
+
clearControlsTimeout();
|
|
331
|
+
setShowControls(true);
|
|
180
332
|
}
|
|
333
|
+
}, [isPlaying, showControlsWithTimer, clearControlsTimeout]);
|
|
334
|
+
useEffect(() => {
|
|
335
|
+
const handleFullscreenChange = () => {
|
|
336
|
+
const isCurrentlyFullscreen = !!document.fullscreenElement;
|
|
337
|
+
setIsFullscreen(isCurrentlyFullscreen);
|
|
338
|
+
if (isCurrentlyFullscreen) {
|
|
339
|
+
showControlsWithTimer();
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
document.addEventListener("fullscreenchange", handleFullscreenChange);
|
|
343
|
+
return () => {
|
|
344
|
+
document.removeEventListener("fullscreenchange", handleFullscreenChange);
|
|
345
|
+
};
|
|
346
|
+
}, [showControlsWithTimer]);
|
|
347
|
+
const getInitialTime = useCallback(() => {
|
|
348
|
+
if (!autoSave || !storageKey) {
|
|
349
|
+
return Number.isFinite(initialTime) && initialTime >= 0 ? initialTime : void 0;
|
|
350
|
+
}
|
|
351
|
+
const saved = Number(localStorage.getItem(`${storageKey}-${src}`) || NaN);
|
|
352
|
+
const hasValidInitial = Number.isFinite(initialTime) && initialTime >= 0;
|
|
353
|
+
const hasValidSaved = Number.isFinite(saved) && saved >= 0;
|
|
354
|
+
if (hasValidInitial) return initialTime;
|
|
355
|
+
if (hasValidSaved) return saved;
|
|
356
|
+
return void 0;
|
|
357
|
+
}, [autoSave, storageKey, src, initialTime]);
|
|
358
|
+
useEffect(() => {
|
|
359
|
+
const start = getInitialTime();
|
|
181
360
|
if (start !== void 0 && videoRef.current) {
|
|
182
361
|
videoRef.current.currentTime = start;
|
|
183
362
|
setCurrentTime(start);
|
|
184
363
|
}
|
|
185
|
-
}, [
|
|
186
|
-
const saveProgress = useCallback(
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
}, [autoSave, storageKey, src, currentTime]);
|
|
194
|
-
const togglePlayPause = useCallback(() => {
|
|
195
|
-
if (videoRef.current) {
|
|
196
|
-
if (isPlaying) {
|
|
197
|
-
videoRef.current.pause();
|
|
198
|
-
} else {
|
|
199
|
-
videoRef.current.play();
|
|
364
|
+
}, [getInitialTime]);
|
|
365
|
+
const saveProgress = useCallback(
|
|
366
|
+
(time) => {
|
|
367
|
+
if (!autoSave || !storageKey) return;
|
|
368
|
+
const now = Date.now();
|
|
369
|
+
if (now - lastSaveTimeRef.current > 5e3) {
|
|
370
|
+
localStorage.setItem(`${storageKey}-${src}`, time.toString());
|
|
371
|
+
lastSaveTimeRef.current = now;
|
|
200
372
|
}
|
|
201
|
-
|
|
373
|
+
},
|
|
374
|
+
[autoSave, storageKey, src]
|
|
375
|
+
);
|
|
376
|
+
const togglePlayPause = useCallback(async () => {
|
|
377
|
+
const video = videoRef.current;
|
|
378
|
+
if (!video) return;
|
|
379
|
+
if (!video.paused) {
|
|
380
|
+
video.pause();
|
|
381
|
+
return;
|
|
202
382
|
}
|
|
203
|
-
|
|
383
|
+
try {
|
|
384
|
+
await video.play();
|
|
385
|
+
} catch {
|
|
386
|
+
}
|
|
387
|
+
}, []);
|
|
204
388
|
const handleVolumeChange = useCallback(
|
|
205
389
|
(newVolume) => {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
390
|
+
const video = videoRef.current;
|
|
391
|
+
if (!video) return;
|
|
392
|
+
const volumeValue = newVolume / 100;
|
|
393
|
+
video.volume = volumeValue;
|
|
394
|
+
setVolume(volumeValue);
|
|
395
|
+
const shouldMute = volumeValue === 0;
|
|
396
|
+
const shouldUnmute = volumeValue > 0 && isMuted;
|
|
397
|
+
if (shouldMute) {
|
|
398
|
+
video.muted = true;
|
|
399
|
+
setIsMuted(true);
|
|
400
|
+
} else if (shouldUnmute) {
|
|
401
|
+
video.muted = false;
|
|
402
|
+
setIsMuted(false);
|
|
217
403
|
}
|
|
218
404
|
},
|
|
219
405
|
[isMuted]
|
|
220
406
|
);
|
|
221
407
|
const toggleMute = useCallback(() => {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
408
|
+
const video = videoRef.current;
|
|
409
|
+
if (!video) return;
|
|
410
|
+
if (isMuted) {
|
|
411
|
+
const restoreVolume = volume > 0 ? volume : 0.5;
|
|
412
|
+
video.volume = restoreVolume;
|
|
413
|
+
video.muted = false;
|
|
414
|
+
setVolume(restoreVolume);
|
|
415
|
+
setIsMuted(false);
|
|
416
|
+
} else {
|
|
417
|
+
video.muted = true;
|
|
418
|
+
setIsMuted(true);
|
|
233
419
|
}
|
|
234
420
|
}, [isMuted, volume]);
|
|
421
|
+
const handleSeek = useCallback((newTime) => {
|
|
422
|
+
const video = videoRef.current;
|
|
423
|
+
if (video) {
|
|
424
|
+
video.currentTime = newTime;
|
|
425
|
+
}
|
|
426
|
+
}, []);
|
|
235
427
|
const toggleFullscreen = useCallback(() => {
|
|
236
428
|
const container = videoRef.current?.parentElement;
|
|
237
429
|
if (!container) return;
|
|
238
|
-
if (!isFullscreen) {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
}
|
|
242
|
-
} else if (document.exitFullscreen) {
|
|
430
|
+
if (!isFullscreen && container.requestFullscreen) {
|
|
431
|
+
container.requestFullscreen();
|
|
432
|
+
} else if (isFullscreen && document.exitFullscreen) {
|
|
243
433
|
document.exitFullscreen();
|
|
244
434
|
}
|
|
245
|
-
setIsFullscreen(!isFullscreen);
|
|
246
435
|
}, [isFullscreen]);
|
|
247
436
|
const handleSpeedChange = useCallback((speed) => {
|
|
248
437
|
if (videoRef.current) {
|
|
@@ -260,29 +449,28 @@ var VideoPlayer = ({
|
|
|
260
449
|
setShowCaptions(newShowCaptions);
|
|
261
450
|
trackRef.current.track.mode = newShowCaptions && subtitles ? "showing" : "hidden";
|
|
262
451
|
}, [showCaptions, subtitles]);
|
|
263
|
-
const
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
onTimeUpdate?.(current);
|
|
269
|
-
if (duration > 0) {
|
|
270
|
-
const progressPercent = current / duration * 100;
|
|
271
|
-
onProgress?.(progressPercent);
|
|
272
|
-
if (progressPercent >= 95 && !hasCompleted) {
|
|
273
|
-
setHasCompleted(true);
|
|
274
|
-
onVideoComplete?.();
|
|
275
|
-
}
|
|
452
|
+
const checkVideoCompletion = useCallback(
|
|
453
|
+
(progressPercent) => {
|
|
454
|
+
if (progressPercent >= 95 && !hasCompleted) {
|
|
455
|
+
setHasCompleted(true);
|
|
456
|
+
onVideoComplete?.();
|
|
276
457
|
}
|
|
458
|
+
},
|
|
459
|
+
[hasCompleted, onVideoComplete]
|
|
460
|
+
);
|
|
461
|
+
const handleTimeUpdate = useCallback(() => {
|
|
462
|
+
const video = videoRef.current;
|
|
463
|
+
if (!video) return;
|
|
464
|
+
const current = video.currentTime;
|
|
465
|
+
setCurrentTime(current);
|
|
466
|
+
saveProgress(current);
|
|
467
|
+
onTimeUpdate?.(current);
|
|
468
|
+
if (duration > 0) {
|
|
469
|
+
const progressPercent = current / duration * 100;
|
|
470
|
+
onProgress?.(progressPercent);
|
|
471
|
+
checkVideoCompletion(progressPercent);
|
|
277
472
|
}
|
|
278
|
-
}, [
|
|
279
|
-
duration,
|
|
280
|
-
saveProgress,
|
|
281
|
-
onTimeUpdate,
|
|
282
|
-
onProgress,
|
|
283
|
-
onVideoComplete,
|
|
284
|
-
hasCompleted
|
|
285
|
-
]);
|
|
473
|
+
}, [duration, saveProgress, onTimeUpdate, onProgress, checkVideoCompletion]);
|
|
286
474
|
const handleLoadedMetadata = useCallback(() => {
|
|
287
475
|
if (videoRef.current) {
|
|
288
476
|
setDuration(videoRef.current.duration);
|
|
@@ -311,9 +499,78 @@ var VideoPlayer = ({
|
|
|
311
499
|
return () => {
|
|
312
500
|
document.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
313
501
|
window.removeEventListener("blur", handleBlur);
|
|
502
|
+
clearControlsTimeout();
|
|
503
|
+
clearMouseMoveTimeout();
|
|
314
504
|
};
|
|
315
|
-
}, [isPlaying]);
|
|
505
|
+
}, [isPlaying, clearControlsTimeout, clearMouseMoveTimeout]);
|
|
316
506
|
const progressPercentage = duration > 0 ? currentTime / duration * 100 : 0;
|
|
507
|
+
const getTopControlsOpacity = useCallback(() => {
|
|
508
|
+
if (isFullscreen) {
|
|
509
|
+
return showControls ? "opacity-100" : "opacity-0";
|
|
510
|
+
}
|
|
511
|
+
return !isPlaying || showControls ? "opacity-100" : "opacity-0 group-hover:opacity-100";
|
|
512
|
+
}, [isFullscreen, showControls, isPlaying]);
|
|
513
|
+
const getBottomControlsOpacity = useCallback(() => {
|
|
514
|
+
if (isFullscreen) {
|
|
515
|
+
return showControls ? "opacity-100" : "opacity-0";
|
|
516
|
+
}
|
|
517
|
+
return !isPlaying || showControls ? "opacity-100" : "opacity-0 group-hover:opacity-100";
|
|
518
|
+
}, [isFullscreen, showControls, isPlaying]);
|
|
519
|
+
const handleVideoKeyDown = useCallback(
|
|
520
|
+
(e) => {
|
|
521
|
+
if (e.key) {
|
|
522
|
+
e.stopPropagation();
|
|
523
|
+
showControlsWithTimer();
|
|
524
|
+
}
|
|
525
|
+
switch (e.key) {
|
|
526
|
+
case " ":
|
|
527
|
+
case "Enter":
|
|
528
|
+
e.preventDefault();
|
|
529
|
+
togglePlayPause();
|
|
530
|
+
break;
|
|
531
|
+
case "ArrowLeft":
|
|
532
|
+
e.preventDefault();
|
|
533
|
+
if (videoRef.current) {
|
|
534
|
+
videoRef.current.currentTime -= 10;
|
|
535
|
+
}
|
|
536
|
+
break;
|
|
537
|
+
case "ArrowRight":
|
|
538
|
+
e.preventDefault();
|
|
539
|
+
if (videoRef.current) {
|
|
540
|
+
videoRef.current.currentTime += 10;
|
|
541
|
+
}
|
|
542
|
+
break;
|
|
543
|
+
case "ArrowUp":
|
|
544
|
+
e.preventDefault();
|
|
545
|
+
handleVolumeChange(Math.min(100, volume * 100 + 10));
|
|
546
|
+
break;
|
|
547
|
+
case "ArrowDown":
|
|
548
|
+
e.preventDefault();
|
|
549
|
+
handleVolumeChange(Math.max(0, volume * 100 - 10));
|
|
550
|
+
break;
|
|
551
|
+
case "m":
|
|
552
|
+
case "M":
|
|
553
|
+
e.preventDefault();
|
|
554
|
+
toggleMute();
|
|
555
|
+
break;
|
|
556
|
+
case "f":
|
|
557
|
+
case "F":
|
|
558
|
+
e.preventDefault();
|
|
559
|
+
toggleFullscreen();
|
|
560
|
+
break;
|
|
561
|
+
default:
|
|
562
|
+
break;
|
|
563
|
+
}
|
|
564
|
+
},
|
|
565
|
+
[
|
|
566
|
+
showControlsWithTimer,
|
|
567
|
+
togglePlayPause,
|
|
568
|
+
handleVolumeChange,
|
|
569
|
+
volume,
|
|
570
|
+
toggleMute,
|
|
571
|
+
toggleFullscreen
|
|
572
|
+
]
|
|
573
|
+
);
|
|
317
574
|
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col", className), children: [
|
|
318
575
|
(title || subtitleText) && /* @__PURE__ */ jsx3("div", { className: "bg-subject-1 rounded-t-xl px-8 py-4 flex items-end justify-between min-h-20", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
319
576
|
title && /* @__PURE__ */ jsx3(
|
|
@@ -340,12 +597,18 @@ var VideoPlayer = ({
|
|
|
340
597
|
)
|
|
341
598
|
] }) }),
|
|
342
599
|
/* @__PURE__ */ jsxs(
|
|
343
|
-
"
|
|
600
|
+
"section",
|
|
344
601
|
{
|
|
345
602
|
className: cn(
|
|
346
603
|
"relative w-full bg-background overflow-hidden group",
|
|
347
|
-
title || subtitleText ? "rounded-b-xl" : "rounded-xl"
|
|
604
|
+
title || subtitleText ? "rounded-b-xl" : "rounded-xl",
|
|
605
|
+
// Hide cursor when controls are hidden and video is playing
|
|
606
|
+
isPlaying && !showControls ? "cursor-none group-hover:cursor-default" : "cursor-default"
|
|
348
607
|
),
|
|
608
|
+
"aria-label": title ? `Video player: ${title}` : "Video player",
|
|
609
|
+
onMouseMove: handleMouseMove,
|
|
610
|
+
onMouseEnter: showControlsWithTimer,
|
|
611
|
+
onMouseLeave: handleMouseLeave,
|
|
349
612
|
children: [
|
|
350
613
|
/* @__PURE__ */ jsx3(
|
|
351
614
|
"video",
|
|
@@ -358,39 +621,7 @@ var VideoPlayer = ({
|
|
|
358
621
|
onTimeUpdate: handleTimeUpdate,
|
|
359
622
|
onLoadedMetadata: handleLoadedMetadata,
|
|
360
623
|
onClick: togglePlayPause,
|
|
361
|
-
onKeyDown:
|
|
362
|
-
if (e.key) {
|
|
363
|
-
setShowControls(true);
|
|
364
|
-
}
|
|
365
|
-
if (e.key === " " || e.key === "Enter") {
|
|
366
|
-
e.preventDefault();
|
|
367
|
-
togglePlayPause();
|
|
368
|
-
}
|
|
369
|
-
if (e.key === "ArrowLeft" && videoRef.current) {
|
|
370
|
-
e.preventDefault();
|
|
371
|
-
videoRef.current.currentTime -= 10;
|
|
372
|
-
}
|
|
373
|
-
if (e.key === "ArrowRight" && videoRef.current) {
|
|
374
|
-
e.preventDefault();
|
|
375
|
-
videoRef.current.currentTime += 10;
|
|
376
|
-
}
|
|
377
|
-
if (e.key === "ArrowUp") {
|
|
378
|
-
e.preventDefault();
|
|
379
|
-
handleVolumeChange(Math.min(100, volume * 100 + 10));
|
|
380
|
-
}
|
|
381
|
-
if (e.key === "ArrowDown") {
|
|
382
|
-
e.preventDefault();
|
|
383
|
-
handleVolumeChange(Math.max(0, volume * 100 - 10));
|
|
384
|
-
}
|
|
385
|
-
if (e.key === "m" || e.key === "M") {
|
|
386
|
-
e.preventDefault();
|
|
387
|
-
toggleMute();
|
|
388
|
-
}
|
|
389
|
-
if (e.key === "f" || e.key === "F") {
|
|
390
|
-
e.preventDefault();
|
|
391
|
-
toggleFullscreen();
|
|
392
|
-
}
|
|
393
|
-
},
|
|
624
|
+
onKeyDown: handleVideoKeyDown,
|
|
394
625
|
tabIndex: 0,
|
|
395
626
|
"aria-label": title ? `Video: ${title}` : "Video player",
|
|
396
627
|
children: /* @__PURE__ */ jsx3(
|
|
@@ -420,9 +651,9 @@ var VideoPlayer = ({
|
|
|
420
651
|
{
|
|
421
652
|
className: cn(
|
|
422
653
|
"absolute top-0 left-0 right-0 p-4 bg-gradient-to-b from-black/70 to-transparent transition-opacity",
|
|
423
|
-
|
|
654
|
+
getTopControlsOpacity()
|
|
424
655
|
),
|
|
425
|
-
children: /* @__PURE__ */ jsx3("div", { className: "
|
|
656
|
+
children: /* @__PURE__ */ jsx3("div", { className: "flex justify-start", children: /* @__PURE__ */ jsx3(
|
|
426
657
|
IconButton_default,
|
|
427
658
|
{
|
|
428
659
|
icon: isFullscreen ? /* @__PURE__ */ jsx3(ArrowsInSimple, { size: 24 }) : /* @__PURE__ */ jsx3(ArrowsOutSimple, { size: 24 }),
|
|
@@ -438,29 +669,18 @@ var VideoPlayer = ({
|
|
|
438
669
|
{
|
|
439
670
|
className: cn(
|
|
440
671
|
"absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/90 to-transparent transition-opacity",
|
|
441
|
-
|
|
672
|
+
getBottomControlsOpacity()
|
|
442
673
|
),
|
|
443
674
|
children: [
|
|
444
|
-
/* @__PURE__ */ jsx3(
|
|
445
|
-
|
|
675
|
+
/* @__PURE__ */ jsx3(
|
|
676
|
+
ProgressBar,
|
|
446
677
|
{
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
onChange: (e) => {
|
|
452
|
-
const newTime = parseFloat(e.target.value);
|
|
453
|
-
if (videoRef.current) {
|
|
454
|
-
videoRef.current.currentTime = newTime;
|
|
455
|
-
}
|
|
456
|
-
},
|
|
457
|
-
className: "w-full h-1 bg-neutral-600 rounded-full appearance-none cursor-pointer slider:bg-primary-600 focus:outline-none focus:ring-2 focus:ring-primary-500",
|
|
458
|
-
"aria-label": "Video progress",
|
|
459
|
-
style: {
|
|
460
|
-
background: `linear-gradient(to right, #2271C4 ${progressPercentage}%, #D5D4D4 ${progressPercentage}%)`
|
|
461
|
-
}
|
|
678
|
+
currentTime,
|
|
679
|
+
duration,
|
|
680
|
+
progressPercentage,
|
|
681
|
+
onSeek: handleSeek
|
|
462
682
|
}
|
|
463
|
-
)
|
|
683
|
+
),
|
|
464
684
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-4 pb-4", children: [
|
|
465
685
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
|
|
466
686
|
/* @__PURE__ */ jsx3(
|
|
@@ -472,32 +692,15 @@ var VideoPlayer = ({
|
|
|
472
692
|
className: "!bg-transparent !text-white hover:!bg-white/20"
|
|
473
693
|
}
|
|
474
694
|
),
|
|
475
|
-
/* @__PURE__ */
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
),
|
|
485
|
-
/* @__PURE__ */ jsx3(
|
|
486
|
-
"input",
|
|
487
|
-
{
|
|
488
|
-
type: "range",
|
|
489
|
-
min: 0,
|
|
490
|
-
max: 100,
|
|
491
|
-
value: Math.round(volume * 100),
|
|
492
|
-
onChange: (e) => handleVolumeChange(parseInt(e.target.value)),
|
|
493
|
-
className: "w-20 h-1 bg-neutral-600 rounded-full appearance-none cursor-pointer focus:outline-none focus:ring-2 focus:ring-primary-500",
|
|
494
|
-
"aria-label": "Volume control",
|
|
495
|
-
style: {
|
|
496
|
-
background: `linear-gradient(to right, #2271C4 ${volume * 100}%, #D5D4D4 ${volume * 100}%)`
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
)
|
|
500
|
-
] }),
|
|
695
|
+
/* @__PURE__ */ jsx3(
|
|
696
|
+
VolumeControls,
|
|
697
|
+
{
|
|
698
|
+
volume,
|
|
699
|
+
isMuted,
|
|
700
|
+
onVolumeChange: handleVolumeChange,
|
|
701
|
+
onToggleMute: toggleMute
|
|
702
|
+
}
|
|
703
|
+
),
|
|
501
704
|
subtitles && /* @__PURE__ */ jsx3(
|
|
502
705
|
IconButton_default,
|
|
503
706
|
{
|
|
@@ -516,29 +719,15 @@ var VideoPlayer = ({
|
|
|
516
719
|
formatTime(duration)
|
|
517
720
|
] })
|
|
518
721
|
] }),
|
|
519
|
-
/* @__PURE__ */ jsx3("div", { className: "flex items-center gap-4", children: /* @__PURE__ */
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
),
|
|
529
|
-
showSpeedMenu && /* @__PURE__ */ jsx3("div", { className: "absolute bottom-12 right-0 bg-black/90 rounded-lg p-2 min-w-20", children: [0.5, 0.75, 1, 1.25, 1.5, 2].map((speed) => /* @__PURE__ */ jsxs(
|
|
530
|
-
"button",
|
|
531
|
-
{
|
|
532
|
-
onClick: () => handleSpeedChange(speed),
|
|
533
|
-
className: `block w-full text-left px-3 py-1 text-sm rounded hover:bg-white/20 transition-colors ${playbackRate === speed ? "text-primary-400" : "text-white"}`,
|
|
534
|
-
children: [
|
|
535
|
-
speed,
|
|
536
|
-
"x"
|
|
537
|
-
]
|
|
538
|
-
},
|
|
539
|
-
speed
|
|
540
|
-
)) })
|
|
541
|
-
] }) })
|
|
722
|
+
/* @__PURE__ */ jsx3("div", { className: "flex items-center gap-4", children: /* @__PURE__ */ jsx3(
|
|
723
|
+
SpeedMenu,
|
|
724
|
+
{
|
|
725
|
+
showSpeedMenu,
|
|
726
|
+
playbackRate,
|
|
727
|
+
onToggleMenu: toggleSpeedMenu,
|
|
728
|
+
onSpeedChange: handleSpeedChange
|
|
729
|
+
}
|
|
730
|
+
) })
|
|
542
731
|
] })
|
|
543
732
|
]
|
|
544
733
|
}
|