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