analytica-frontend-lib 1.1.9 → 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/Quiz/index.d.mts +7 -8
- package/dist/Quiz/index.d.ts +7 -8
- package/dist/Quiz/index.js +146 -99
- package/dist/Quiz/index.js.map +1 -1
- package/dist/Quiz/index.mjs +146 -99
- package/dist/Quiz/index.mjs.map +1 -1
- package/dist/Quiz/useQuizStore/index.d.mts +70 -16
- package/dist/Quiz/useQuizStore/index.d.ts +70 -16
- package/dist/Quiz/useQuizStore/index.js +45 -12
- package/dist/Quiz/useQuizStore/index.js.map +1 -1
- package/dist/Quiz/useQuizStore/index.mjs +45 -12
- package/dist/Quiz/useQuizStore/index.mjs.map +1 -1
- package/dist/VideoPlayer/index.js +358 -189
- package/dist/VideoPlayer/index.js.map +1 -1
- package/dist/VideoPlayer/index.mjs +364 -190
- 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 +504 -288
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +510 -289
- 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;
|
|
217
373
|
}
|
|
218
|
-
|
|
374
|
+
try {
|
|
375
|
+
await video.play();
|
|
376
|
+
} catch {
|
|
377
|
+
}
|
|
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) {
|
|
@@ -270,39 +435,43 @@ var VideoPlayer = ({
|
|
|
270
435
|
setShowSpeedMenu(!showSpeedMenu);
|
|
271
436
|
}, [showSpeedMenu]);
|
|
272
437
|
const toggleCaptions = (0, import_react2.useCallback)(() => {
|
|
273
|
-
if (!trackRef.current?.track) return;
|
|
438
|
+
if (!trackRef.current?.track || !subtitles) return;
|
|
274
439
|
const newShowCaptions = !showCaptions;
|
|
275
440
|
setShowCaptions(newShowCaptions);
|
|
276
|
-
trackRef.current.track.mode = newShowCaptions ? "showing" : "hidden";
|
|
277
|
-
}, [showCaptions]);
|
|
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
|
-
}
|
|
441
|
+
trackRef.current.track.mode = newShowCaptions && subtitles ? "showing" : "hidden";
|
|
442
|
+
}, [showCaptions, subtitles]);
|
|
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);
|
|
304
468
|
}
|
|
305
469
|
}, []);
|
|
470
|
+
(0, import_react2.useEffect)(() => {
|
|
471
|
+
if (trackRef.current?.track) {
|
|
472
|
+
trackRef.current.track.mode = showCaptions && subtitles ? "showing" : "hidden";
|
|
473
|
+
}
|
|
474
|
+
}, [subtitles, showCaptions]);
|
|
306
475
|
(0, import_react2.useEffect)(() => {
|
|
307
476
|
const handleVisibilityChange = () => {
|
|
308
477
|
if (document.hidden && isPlaying && videoRef.current) {
|
|
@@ -321,9 +490,78 @@ var VideoPlayer = ({
|
|
|
321
490
|
return () => {
|
|
322
491
|
document.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
323
492
|
window.removeEventListener("blur", handleBlur);
|
|
493
|
+
clearControlsTimeout();
|
|
494
|
+
clearMouseMoveTimeout();
|
|
324
495
|
};
|
|
325
|
-
}, [isPlaying]);
|
|
496
|
+
}, [isPlaying, clearControlsTimeout, clearMouseMoveTimeout]);
|
|
326
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
|
+
);
|
|
327
565
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: cn("flex flex-col", className), children: [
|
|
328
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: [
|
|
329
567
|
title && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
@@ -350,12 +588,17 @@ var VideoPlayer = ({
|
|
|
350
588
|
)
|
|
351
589
|
] }) }),
|
|
352
590
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
353
|
-
"
|
|
591
|
+
"section",
|
|
354
592
|
{
|
|
355
593
|
className: cn(
|
|
356
594
|
"relative w-full bg-background overflow-hidden group",
|
|
357
|
-
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"
|
|
358
598
|
),
|
|
599
|
+
"aria-label": title ? `Video player: ${title}` : "Video player",
|
|
600
|
+
onMouseMove: isFullscreen ? handleMouseMove : showControlsWithTimer,
|
|
601
|
+
onMouseEnter: showControlsWithTimer,
|
|
359
602
|
children: [
|
|
360
603
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
361
604
|
"video",
|
|
@@ -368,39 +611,7 @@ var VideoPlayer = ({
|
|
|
368
611
|
onTimeUpdate: handleTimeUpdate,
|
|
369
612
|
onLoadedMetadata: handleLoadedMetadata,
|
|
370
613
|
onClick: togglePlayPause,
|
|
371
|
-
onKeyDown:
|
|
372
|
-
if (e.key) {
|
|
373
|
-
setShowControls(true);
|
|
374
|
-
}
|
|
375
|
-
if (e.key === " " || e.key === "Enter") {
|
|
376
|
-
e.preventDefault();
|
|
377
|
-
togglePlayPause();
|
|
378
|
-
}
|
|
379
|
-
if (e.key === "ArrowLeft" && videoRef.current) {
|
|
380
|
-
e.preventDefault();
|
|
381
|
-
videoRef.current.currentTime -= 10;
|
|
382
|
-
}
|
|
383
|
-
if (e.key === "ArrowRight" && videoRef.current) {
|
|
384
|
-
e.preventDefault();
|
|
385
|
-
videoRef.current.currentTime += 10;
|
|
386
|
-
}
|
|
387
|
-
if (e.key === "ArrowUp") {
|
|
388
|
-
e.preventDefault();
|
|
389
|
-
handleVolumeChange(Math.min(100, volume * 100 + 10));
|
|
390
|
-
}
|
|
391
|
-
if (e.key === "ArrowDown") {
|
|
392
|
-
e.preventDefault();
|
|
393
|
-
handleVolumeChange(Math.max(0, volume * 100 - 10));
|
|
394
|
-
}
|
|
395
|
-
if (e.key === "m" || e.key === "M") {
|
|
396
|
-
e.preventDefault();
|
|
397
|
-
toggleMute();
|
|
398
|
-
}
|
|
399
|
-
if (e.key === "f" || e.key === "F") {
|
|
400
|
-
e.preventDefault();
|
|
401
|
-
toggleFullscreen();
|
|
402
|
-
}
|
|
403
|
-
},
|
|
614
|
+
onKeyDown: handleVideoKeyDown,
|
|
404
615
|
tabIndex: 0,
|
|
405
616
|
"aria-label": title ? `Video: ${title}` : "Video player",
|
|
406
617
|
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
@@ -408,9 +619,9 @@ var VideoPlayer = ({
|
|
|
408
619
|
{
|
|
409
620
|
ref: trackRef,
|
|
410
621
|
kind: "captions",
|
|
411
|
-
src: subtitles || "data:text/vtt;charset=utf-8,WEBVTT
|
|
412
|
-
srcLang: "
|
|
413
|
-
label: subtitles ? "
|
|
622
|
+
src: subtitles || "data:text/vtt;charset=utf-8,WEBVTT",
|
|
623
|
+
srcLang: "pt-br",
|
|
624
|
+
label: subtitles ? "Legendas em Portugu\xEAs" : "Sem legendas dispon\xEDveis",
|
|
414
625
|
default: false
|
|
415
626
|
}
|
|
416
627
|
)
|
|
@@ -430,9 +641,9 @@ var VideoPlayer = ({
|
|
|
430
641
|
{
|
|
431
642
|
className: cn(
|
|
432
643
|
"absolute top-0 left-0 right-0 p-4 bg-gradient-to-b from-black/70 to-transparent transition-opacity",
|
|
433
|
-
|
|
644
|
+
getTopControlsOpacity()
|
|
434
645
|
),
|
|
435
|
-
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)(
|
|
436
647
|
IconButton_default,
|
|
437
648
|
{
|
|
438
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 }),
|
|
@@ -448,29 +659,18 @@ var VideoPlayer = ({
|
|
|
448
659
|
{
|
|
449
660
|
className: cn(
|
|
450
661
|
"absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/90 to-transparent transition-opacity",
|
|
451
|
-
|
|
662
|
+
getBottomControlsOpacity()
|
|
452
663
|
),
|
|
453
664
|
children: [
|
|
454
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
455
|
-
|
|
665
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
666
|
+
ProgressBar,
|
|
456
667
|
{
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
onChange: (e) => {
|
|
462
|
-
const newTime = parseFloat(e.target.value);
|
|
463
|
-
if (videoRef.current) {
|
|
464
|
-
videoRef.current.currentTime = newTime;
|
|
465
|
-
}
|
|
466
|
-
},
|
|
467
|
-
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",
|
|
468
|
-
"aria-label": "Video progress",
|
|
469
|
-
style: {
|
|
470
|
-
background: `linear-gradient(to right, #2271C4 ${progressPercentage}%, #D5D4D4 ${progressPercentage}%)`
|
|
471
|
-
}
|
|
668
|
+
currentTime,
|
|
669
|
+
duration,
|
|
670
|
+
progressPercentage,
|
|
671
|
+
onSeek: handleSeek
|
|
472
672
|
}
|
|
473
|
-
)
|
|
673
|
+
),
|
|
474
674
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center justify-between px-4 pb-4", children: [
|
|
475
675
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center gap-4", children: [
|
|
476
676
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
@@ -482,32 +682,15 @@ var VideoPlayer = ({
|
|
|
482
682
|
className: "!bg-transparent !text-white hover:!bg-white/20"
|
|
483
683
|
}
|
|
484
684
|
),
|
|
485
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
),
|
|
495
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
496
|
-
"input",
|
|
497
|
-
{
|
|
498
|
-
type: "range",
|
|
499
|
-
min: 0,
|
|
500
|
-
max: 100,
|
|
501
|
-
value: Math.round(volume * 100),
|
|
502
|
-
onChange: (e) => handleVolumeChange(parseInt(e.target.value)),
|
|
503
|
-
className: "w-20 h-1 bg-neutral-600 rounded-full appearance-none cursor-pointer focus:outline-none focus:ring-2 focus:ring-primary-500",
|
|
504
|
-
"aria-label": "Volume control",
|
|
505
|
-
style: {
|
|
506
|
-
background: `linear-gradient(to right, #2271C4 ${volume * 100}%, #D5D4D4 ${volume * 100}%)`
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
)
|
|
510
|
-
] }),
|
|
685
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
686
|
+
VolumeControls,
|
|
687
|
+
{
|
|
688
|
+
volume,
|
|
689
|
+
isMuted,
|
|
690
|
+
onVolumeChange: handleVolumeChange,
|
|
691
|
+
onToggleMute: toggleMute
|
|
692
|
+
}
|
|
693
|
+
),
|
|
511
694
|
subtitles && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
512
695
|
IconButton_default,
|
|
513
696
|
{
|
|
@@ -526,29 +709,15 @@ var VideoPlayer = ({
|
|
|
526
709
|
formatTime(duration)
|
|
527
710
|
] })
|
|
528
711
|
] }),
|
|
529
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "flex items-center gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime3.
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
),
|
|
539
|
-
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)(
|
|
540
|
-
"button",
|
|
541
|
-
{
|
|
542
|
-
onClick: () => handleSpeedChange(speed),
|
|
543
|
-
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"}`,
|
|
544
|
-
children: [
|
|
545
|
-
speed,
|
|
546
|
-
"x"
|
|
547
|
-
]
|
|
548
|
-
},
|
|
549
|
-
speed
|
|
550
|
-
)) })
|
|
551
|
-
] }) })
|
|
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
|
+
) })
|
|
552
721
|
] })
|
|
553
722
|
]
|
|
554
723
|
}
|