analytica-frontend-lib 1.1.80 → 1.1.82
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/DownloadButton/index.d.mts +46 -0
- package/dist/DownloadButton/index.d.ts +46 -0
- package/dist/DownloadButton/index.js +272 -0
- package/dist/DownloadButton/index.js.map +1 -0
- package/dist/DownloadButton/index.mjs +251 -0
- package/dist/DownloadButton/index.mjs.map +1 -0
- package/dist/VideoPlayer/index.d.mts +12 -1
- package/dist/VideoPlayer/index.d.ts +12 -1
- package/dist/VideoPlayer/index.js +335 -131
- package/dist/VideoPlayer/index.js.map +1 -1
- package/dist/VideoPlayer/index.mjs +307 -103
- package/dist/VideoPlayer/index.mjs.map +1 -1
- package/dist/index.css +13 -0
- package/dist/index.css.map +1 -1
- package/dist/index.d.mts +46 -45
- package/dist/index.d.ts +46 -45
- package/dist/index.js +6682 -6476
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +6806 -6601
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +13 -0
- package/dist/styles.css.map +1 -1
- package/package.json +2 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// src/components/VideoPlayer/VideoPlayer.tsx
|
|
2
2
|
import {
|
|
3
3
|
useRef,
|
|
4
|
-
useState as
|
|
4
|
+
useState as useState3,
|
|
5
5
|
useEffect as useEffect2,
|
|
6
|
-
useCallback
|
|
6
|
+
useCallback as useCallback2
|
|
7
7
|
} from "react";
|
|
8
8
|
import { createPortal } from "react-dom";
|
|
9
9
|
import {
|
|
@@ -208,13 +208,197 @@ var useMobile = () => {
|
|
|
208
208
|
};
|
|
209
209
|
};
|
|
210
210
|
|
|
211
|
+
// src/components/DownloadButton/DownloadButton.tsx
|
|
212
|
+
import { useCallback, useState as useState2 } from "react";
|
|
213
|
+
import { DownloadSimple } from "phosphor-react";
|
|
214
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
215
|
+
var getMimeType = (url) => {
|
|
216
|
+
const extension = getFileExtension(url);
|
|
217
|
+
const mimeTypes = {
|
|
218
|
+
pdf: "application/pdf",
|
|
219
|
+
png: "image/png",
|
|
220
|
+
jpg: "image/jpeg",
|
|
221
|
+
jpeg: "image/jpeg",
|
|
222
|
+
mp3: "audio/mpeg",
|
|
223
|
+
mp4: "video/mp4",
|
|
224
|
+
vtt: "text/vtt"
|
|
225
|
+
};
|
|
226
|
+
return mimeTypes[extension] || "application/octet-stream";
|
|
227
|
+
};
|
|
228
|
+
var triggerDownload = async (url, filename) => {
|
|
229
|
+
try {
|
|
230
|
+
const response = await fetch(url, {
|
|
231
|
+
mode: "cors",
|
|
232
|
+
credentials: "same-origin"
|
|
233
|
+
});
|
|
234
|
+
if (!response.ok) {
|
|
235
|
+
throw new Error(
|
|
236
|
+
`Failed to fetch file: ${response.status} ${response.statusText}`
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
const blob = await response.blob();
|
|
240
|
+
const mimeType = getMimeType(url);
|
|
241
|
+
const typedBlob = new Blob([blob], { type: mimeType });
|
|
242
|
+
const blobUrl = URL.createObjectURL(typedBlob);
|
|
243
|
+
const link = document.createElement("a");
|
|
244
|
+
link.href = blobUrl;
|
|
245
|
+
link.download = filename;
|
|
246
|
+
link.rel = "noopener noreferrer";
|
|
247
|
+
document.body.appendChild(link);
|
|
248
|
+
link.click();
|
|
249
|
+
link.remove();
|
|
250
|
+
setTimeout(() => {
|
|
251
|
+
URL.revokeObjectURL(blobUrl);
|
|
252
|
+
}, 1e3);
|
|
253
|
+
} catch (error) {
|
|
254
|
+
console.warn("Fetch download failed, falling back to direct link:", error);
|
|
255
|
+
const link = document.createElement("a");
|
|
256
|
+
link.href = url;
|
|
257
|
+
link.download = filename;
|
|
258
|
+
link.rel = "noopener noreferrer";
|
|
259
|
+
link.target = "_blank";
|
|
260
|
+
document.body.appendChild(link);
|
|
261
|
+
link.click();
|
|
262
|
+
link.remove();
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
var getFileExtension = (url) => {
|
|
266
|
+
try {
|
|
267
|
+
const u = new URL(url, globalThis.location?.origin || "http://localhost");
|
|
268
|
+
url = u.pathname;
|
|
269
|
+
} catch {
|
|
270
|
+
}
|
|
271
|
+
const path = url.split(/[?#]/)[0];
|
|
272
|
+
const dot = path.lastIndexOf(".");
|
|
273
|
+
return dot > -1 ? path.slice(dot + 1).toLowerCase() : "file";
|
|
274
|
+
};
|
|
275
|
+
var generateFilename = (contentType, url, lessonTitle = "aula") => {
|
|
276
|
+
const sanitizedTitle = lessonTitle.toLowerCase().replaceAll(/[^a-z0-9\s]/g, "").replaceAll(/\s+/g, "-").substring(0, 50);
|
|
277
|
+
const extension = getFileExtension(url);
|
|
278
|
+
return `${sanitizedTitle}-${contentType}.${extension}`;
|
|
279
|
+
};
|
|
280
|
+
var DownloadButton = ({
|
|
281
|
+
content,
|
|
282
|
+
className,
|
|
283
|
+
onDownloadStart,
|
|
284
|
+
onDownloadComplete,
|
|
285
|
+
onDownloadError,
|
|
286
|
+
lessonTitle = "aula",
|
|
287
|
+
disabled = false
|
|
288
|
+
}) => {
|
|
289
|
+
const [isDownloading, setIsDownloading] = useState2(false);
|
|
290
|
+
const isValidUrl = useCallback((url) => {
|
|
291
|
+
return Boolean(
|
|
292
|
+
url && url.trim() !== "" && url !== "undefined" && url !== "null"
|
|
293
|
+
);
|
|
294
|
+
}, []);
|
|
295
|
+
const getAvailableContent = useCallback(() => {
|
|
296
|
+
const downloads = [];
|
|
297
|
+
if (isValidUrl(content.urlDoc)) {
|
|
298
|
+
downloads.push({
|
|
299
|
+
type: "documento",
|
|
300
|
+
url: content.urlDoc,
|
|
301
|
+
label: "Documento"
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
if (isValidUrl(content.urlInitialFrame)) {
|
|
305
|
+
downloads.push({
|
|
306
|
+
type: "quadro-inicial",
|
|
307
|
+
url: content.urlInitialFrame,
|
|
308
|
+
label: "Quadro Inicial"
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
if (isValidUrl(content.urlFinalFrame)) {
|
|
312
|
+
downloads.push({
|
|
313
|
+
type: "quadro-final",
|
|
314
|
+
url: content.urlFinalFrame,
|
|
315
|
+
label: "Quadro Final"
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
if (isValidUrl(content.urlPodcast)) {
|
|
319
|
+
downloads.push({
|
|
320
|
+
type: "podcast",
|
|
321
|
+
url: content.urlPodcast,
|
|
322
|
+
label: "Podcast"
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
if (isValidUrl(content.urlVideo)) {
|
|
326
|
+
downloads.push({ type: "video", url: content.urlVideo, label: "V\xEDdeo" });
|
|
327
|
+
}
|
|
328
|
+
return downloads;
|
|
329
|
+
}, [content, isValidUrl]);
|
|
330
|
+
const handleDownload = useCallback(async () => {
|
|
331
|
+
if (disabled || isDownloading) return;
|
|
332
|
+
const availableContent = getAvailableContent();
|
|
333
|
+
if (availableContent.length === 0) {
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
setIsDownloading(true);
|
|
337
|
+
try {
|
|
338
|
+
for (let i = 0; i < availableContent.length; i++) {
|
|
339
|
+
const item = availableContent[i];
|
|
340
|
+
try {
|
|
341
|
+
onDownloadStart?.(item.type);
|
|
342
|
+
const filename = generateFilename(item.type, item.url, lessonTitle);
|
|
343
|
+
await triggerDownload(item.url, filename);
|
|
344
|
+
onDownloadComplete?.(item.type);
|
|
345
|
+
if (i < availableContent.length - 1) {
|
|
346
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
347
|
+
}
|
|
348
|
+
} catch (error) {
|
|
349
|
+
console.error(`Erro ao baixar ${item.label}:`, error);
|
|
350
|
+
onDownloadError?.(
|
|
351
|
+
item.type,
|
|
352
|
+
error instanceof Error ? error : new Error(`Falha ao baixar ${item.label}`)
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
} finally {
|
|
357
|
+
setIsDownloading(false);
|
|
358
|
+
}
|
|
359
|
+
}, [
|
|
360
|
+
disabled,
|
|
361
|
+
isDownloading,
|
|
362
|
+
getAvailableContent,
|
|
363
|
+
lessonTitle,
|
|
364
|
+
onDownloadStart,
|
|
365
|
+
onDownloadComplete,
|
|
366
|
+
onDownloadError
|
|
367
|
+
]);
|
|
368
|
+
const hasContent = getAvailableContent().length > 0;
|
|
369
|
+
if (!hasContent) {
|
|
370
|
+
return null;
|
|
371
|
+
}
|
|
372
|
+
return /* @__PURE__ */ jsx3("div", { className: cn("flex items-center", className), children: /* @__PURE__ */ jsx3(
|
|
373
|
+
IconButton_default,
|
|
374
|
+
{
|
|
375
|
+
icon: /* @__PURE__ */ jsx3(DownloadSimple, { size: 24 }),
|
|
376
|
+
onClick: handleDownload,
|
|
377
|
+
disabled: disabled || isDownloading,
|
|
378
|
+
"aria-label": (() => {
|
|
379
|
+
if (isDownloading) {
|
|
380
|
+
return "Baixando conte\xFAdo...";
|
|
381
|
+
}
|
|
382
|
+
const contentCount = getAvailableContent().length;
|
|
383
|
+
const suffix = contentCount > 1 ? "s" : "";
|
|
384
|
+
return `Baixar conte\xFAdo da aula (${contentCount} arquivo${suffix})`;
|
|
385
|
+
})(),
|
|
386
|
+
className: cn(
|
|
387
|
+
"!bg-transparent hover:!bg-black/10 transition-colors",
|
|
388
|
+
isDownloading && "opacity-60 cursor-not-allowed"
|
|
389
|
+
)
|
|
390
|
+
}
|
|
391
|
+
) });
|
|
392
|
+
};
|
|
393
|
+
var DownloadButton_default = DownloadButton;
|
|
394
|
+
|
|
211
395
|
// src/components/VideoPlayer/VideoPlayer.tsx
|
|
212
|
-
import { jsx as
|
|
396
|
+
import { jsx as jsx4, jsxs } from "react/jsx-runtime";
|
|
213
397
|
var CONTROLS_HIDE_TIMEOUT = 3e3;
|
|
214
398
|
var LEAVE_HIDE_TIMEOUT = 1e3;
|
|
215
399
|
var INIT_DELAY = 100;
|
|
216
400
|
var formatTime = (seconds) => {
|
|
217
|
-
if (!seconds || isNaN(seconds)) return "0:00";
|
|
401
|
+
if (!seconds || Number.isNaN(seconds)) return "0:00";
|
|
218
402
|
const mins = Math.floor(seconds / 60);
|
|
219
403
|
const secs = Math.floor(seconds % 60);
|
|
220
404
|
return `${mins}:${secs.toString().padStart(2, "0")}`;
|
|
@@ -225,14 +409,14 @@ var ProgressBar = ({
|
|
|
225
409
|
progressPercentage,
|
|
226
410
|
onSeek,
|
|
227
411
|
className = "px-4 pb-2"
|
|
228
|
-
}) => /* @__PURE__ */
|
|
412
|
+
}) => /* @__PURE__ */ jsx4("div", { className, children: /* @__PURE__ */ jsx4(
|
|
229
413
|
"input",
|
|
230
414
|
{
|
|
231
415
|
type: "range",
|
|
232
416
|
min: 0,
|
|
233
417
|
max: duration || 100,
|
|
234
418
|
value: currentTime,
|
|
235
|
-
onChange: (e) => onSeek(parseFloat(e.target.value)),
|
|
419
|
+
onChange: (e) => onSeek(Number.parseFloat(e.target.value)),
|
|
236
420
|
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",
|
|
237
421
|
"aria-label": "Video progress",
|
|
238
422
|
style: {
|
|
@@ -248,23 +432,23 @@ var VolumeControls = ({
|
|
|
248
432
|
iconSize = 24,
|
|
249
433
|
showSlider = true
|
|
250
434
|
}) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
251
|
-
/* @__PURE__ */
|
|
435
|
+
/* @__PURE__ */ jsx4(
|
|
252
436
|
IconButton_default,
|
|
253
437
|
{
|
|
254
|
-
icon: isMuted ? /* @__PURE__ */
|
|
438
|
+
icon: isMuted ? /* @__PURE__ */ jsx4(SpeakerSlash, { size: iconSize }) : /* @__PURE__ */ jsx4(SpeakerHigh, { size: iconSize }),
|
|
255
439
|
onClick: onToggleMute,
|
|
256
440
|
"aria-label": isMuted ? "Unmute" : "Mute",
|
|
257
441
|
className: "!bg-transparent !text-white hover:!bg-white/20"
|
|
258
442
|
}
|
|
259
443
|
),
|
|
260
|
-
showSlider && /* @__PURE__ */
|
|
444
|
+
showSlider && /* @__PURE__ */ jsx4(
|
|
261
445
|
"input",
|
|
262
446
|
{
|
|
263
447
|
type: "range",
|
|
264
448
|
min: 0,
|
|
265
449
|
max: 100,
|
|
266
450
|
value: Math.round(volume * 100),
|
|
267
|
-
onChange: (e) => onVolumeChange(parseInt(e.target.value)),
|
|
451
|
+
onChange: (e) => onVolumeChange(Number.parseInt(e.target.value)),
|
|
268
452
|
className: "w-20 h-1 bg-neutral-600 rounded-full appearance-none cursor-pointer focus:outline-none focus:ring-2 focus:ring-primary-500",
|
|
269
453
|
"aria-label": "Volume control",
|
|
270
454
|
style: {
|
|
@@ -314,17 +498,17 @@ var SpeedMenu = ({
|
|
|
314
498
|
document.removeEventListener("mousedown", handleClickOutside);
|
|
315
499
|
};
|
|
316
500
|
}, [showSpeedMenu, onToggleMenu]);
|
|
317
|
-
const menuContent = /* @__PURE__ */
|
|
501
|
+
const menuContent = /* @__PURE__ */ jsx4(
|
|
318
502
|
"div",
|
|
319
503
|
{
|
|
320
504
|
ref: speedMenuRef,
|
|
321
505
|
role: "menu",
|
|
322
506
|
"aria-label": "Playback speed",
|
|
323
507
|
className: isFullscreen ? "absolute bottom-12 right-0 bg-background border border-border-100 rounded-lg shadow-lg p-2 min-w-24 z-[9999]" : "fixed bg-background border border-border-100 rounded-lg shadow-lg p-2 min-w-24 z-[9999]",
|
|
324
|
-
style:
|
|
508
|
+
style: isFullscreen ? void 0 : {
|
|
325
509
|
top: `${position.top}px`,
|
|
326
510
|
left: `${position.left}px`
|
|
327
|
-
}
|
|
511
|
+
},
|
|
328
512
|
children: [0.5, 0.75, 1, 1.25, 1.5, 2].map((speed) => /* @__PURE__ */ jsxs(
|
|
329
513
|
"button",
|
|
330
514
|
{
|
|
@@ -341,13 +525,13 @@ var SpeedMenu = ({
|
|
|
341
525
|
))
|
|
342
526
|
}
|
|
343
527
|
);
|
|
344
|
-
const portalContent =
|
|
528
|
+
const portalContent = showSpeedMenu && globalThis.window !== void 0 && globalThis.document !== void 0 && !!globalThis.document?.body ? createPortal(menuContent, globalThis.document.body) : null;
|
|
345
529
|
return /* @__PURE__ */ jsxs("div", { className: "relative", ref: speedMenuContainerRef, children: [
|
|
346
|
-
/* @__PURE__ */
|
|
530
|
+
/* @__PURE__ */ jsx4(
|
|
347
531
|
IconButton_default,
|
|
348
532
|
{
|
|
349
533
|
ref: buttonRef,
|
|
350
|
-
icon: /* @__PURE__ */
|
|
534
|
+
icon: /* @__PURE__ */ jsx4(DotsThreeVertical, { size: iconSize }),
|
|
351
535
|
onClick: onToggleMenu,
|
|
352
536
|
"aria-label": "Playback speed",
|
|
353
537
|
"aria-haspopup": "menu",
|
|
@@ -370,29 +554,34 @@ var VideoPlayer = ({
|
|
|
370
554
|
onVideoComplete,
|
|
371
555
|
className,
|
|
372
556
|
autoSave = true,
|
|
373
|
-
storageKey = "video-progress"
|
|
557
|
+
storageKey = "video-progress",
|
|
558
|
+
downloadContent,
|
|
559
|
+
showDownloadButton = false,
|
|
560
|
+
onDownloadStart,
|
|
561
|
+
onDownloadComplete,
|
|
562
|
+
onDownloadError
|
|
374
563
|
}) => {
|
|
375
564
|
const videoRef = useRef(null);
|
|
376
565
|
const { isUltraSmallMobile, isTinyMobile } = useMobile();
|
|
377
|
-
const [isPlaying, setIsPlaying] =
|
|
378
|
-
const [currentTime, setCurrentTime] =
|
|
379
|
-
const [duration, setDuration] =
|
|
380
|
-
const [isMuted, setIsMuted] =
|
|
381
|
-
const [volume, setVolume] =
|
|
382
|
-
const [isFullscreen, setIsFullscreen] =
|
|
383
|
-
const [showControls, setShowControls] =
|
|
384
|
-
const [hasCompleted, setHasCompleted] =
|
|
385
|
-
const [showCaptions, setShowCaptions] =
|
|
566
|
+
const [isPlaying, setIsPlaying] = useState3(false);
|
|
567
|
+
const [currentTime, setCurrentTime] = useState3(0);
|
|
568
|
+
const [duration, setDuration] = useState3(0);
|
|
569
|
+
const [isMuted, setIsMuted] = useState3(false);
|
|
570
|
+
const [volume, setVolume] = useState3(1);
|
|
571
|
+
const [isFullscreen, setIsFullscreen] = useState3(false);
|
|
572
|
+
const [showControls, setShowControls] = useState3(true);
|
|
573
|
+
const [hasCompleted, setHasCompleted] = useState3(false);
|
|
574
|
+
const [showCaptions, setShowCaptions] = useState3(false);
|
|
386
575
|
useEffect2(() => {
|
|
387
576
|
setHasCompleted(false);
|
|
388
577
|
}, [src]);
|
|
389
|
-
const [playbackRate, setPlaybackRate] =
|
|
390
|
-
const [showSpeedMenu, setShowSpeedMenu] =
|
|
578
|
+
const [playbackRate, setPlaybackRate] = useState3(1);
|
|
579
|
+
const [showSpeedMenu, setShowSpeedMenu] = useState3(false);
|
|
391
580
|
const lastSaveTimeRef = useRef(0);
|
|
392
581
|
const trackRef = useRef(null);
|
|
393
582
|
const controlsTimeoutRef = useRef(null);
|
|
394
583
|
const lastMousePositionRef = useRef({ x: 0, y: 0 });
|
|
395
|
-
const isUserInteracting =
|
|
584
|
+
const isUserInteracting = useCallback2(() => {
|
|
396
585
|
if (showSpeedMenu) {
|
|
397
586
|
return true;
|
|
398
587
|
}
|
|
@@ -409,28 +598,28 @@ var VideoPlayer = ({
|
|
|
409
598
|
}
|
|
410
599
|
return false;
|
|
411
600
|
}, [showSpeedMenu]);
|
|
412
|
-
const clearControlsTimeout =
|
|
601
|
+
const clearControlsTimeout = useCallback2(() => {
|
|
413
602
|
if (controlsTimeoutRef.current) {
|
|
414
603
|
clearTimeout(controlsTimeoutRef.current);
|
|
415
604
|
controlsTimeoutRef.current = null;
|
|
416
605
|
}
|
|
417
606
|
}, []);
|
|
418
|
-
const showControlsWithTimer =
|
|
607
|
+
const showControlsWithTimer = useCallback2(() => {
|
|
419
608
|
setShowControls(true);
|
|
420
609
|
clearControlsTimeout();
|
|
421
610
|
if (isFullscreen) {
|
|
422
611
|
if (isPlaying) {
|
|
423
|
-
controlsTimeoutRef.current =
|
|
612
|
+
controlsTimeoutRef.current = globalThis.setTimeout(() => {
|
|
424
613
|
setShowControls(false);
|
|
425
614
|
}, CONTROLS_HIDE_TIMEOUT);
|
|
426
615
|
}
|
|
427
616
|
} else {
|
|
428
|
-
controlsTimeoutRef.current =
|
|
617
|
+
controlsTimeoutRef.current = globalThis.setTimeout(() => {
|
|
429
618
|
setShowControls(false);
|
|
430
619
|
}, CONTROLS_HIDE_TIMEOUT);
|
|
431
620
|
}
|
|
432
621
|
}, [isFullscreen, isPlaying, clearControlsTimeout]);
|
|
433
|
-
const handleMouseMove =
|
|
622
|
+
const handleMouseMove = useCallback2(
|
|
434
623
|
(event) => {
|
|
435
624
|
const currentX = event.clientX;
|
|
436
625
|
const currentY = event.clientY;
|
|
@@ -443,14 +632,14 @@ var VideoPlayer = ({
|
|
|
443
632
|
},
|
|
444
633
|
[showControlsWithTimer]
|
|
445
634
|
);
|
|
446
|
-
const handleMouseEnter =
|
|
635
|
+
const handleMouseEnter = useCallback2(() => {
|
|
447
636
|
showControlsWithTimer();
|
|
448
637
|
}, [showControlsWithTimer]);
|
|
449
|
-
const handleMouseLeave =
|
|
638
|
+
const handleMouseLeave = useCallback2(() => {
|
|
450
639
|
const userInteracting = isUserInteracting();
|
|
451
640
|
clearControlsTimeout();
|
|
452
641
|
if (!isFullscreen && !userInteracting) {
|
|
453
|
-
controlsTimeoutRef.current =
|
|
642
|
+
controlsTimeoutRef.current = globalThis.setTimeout(() => {
|
|
454
643
|
setShowControls(false);
|
|
455
644
|
}, LEAVE_HIDE_TIMEOUT);
|
|
456
645
|
}
|
|
@@ -514,7 +703,12 @@ var VideoPlayer = ({
|
|
|
514
703
|
}
|
|
515
704
|
};
|
|
516
705
|
let raf1 = 0, raf2 = 0, tid;
|
|
517
|
-
if (
|
|
706
|
+
if (globalThis.requestAnimationFrame === void 0) {
|
|
707
|
+
tid = globalThis.setTimeout(init, INIT_DELAY);
|
|
708
|
+
return () => {
|
|
709
|
+
if (tid) clearTimeout(tid);
|
|
710
|
+
};
|
|
711
|
+
} else {
|
|
518
712
|
raf1 = requestAnimationFrame(() => {
|
|
519
713
|
raf2 = requestAnimationFrame(init);
|
|
520
714
|
});
|
|
@@ -522,18 +716,15 @@ var VideoPlayer = ({
|
|
|
522
716
|
cancelAnimationFrame(raf1);
|
|
523
717
|
cancelAnimationFrame(raf2);
|
|
524
718
|
};
|
|
525
|
-
} else {
|
|
526
|
-
tid = window.setTimeout(init, INIT_DELAY);
|
|
527
|
-
return () => {
|
|
528
|
-
if (tid) clearTimeout(tid);
|
|
529
|
-
};
|
|
530
719
|
}
|
|
531
720
|
}, []);
|
|
532
|
-
const getInitialTime =
|
|
721
|
+
const getInitialTime = useCallback2(() => {
|
|
533
722
|
if (!autoSave || !storageKey) {
|
|
534
723
|
return Number.isFinite(initialTime) && initialTime >= 0 ? initialTime : void 0;
|
|
535
724
|
}
|
|
536
|
-
const saved = Number(
|
|
725
|
+
const saved = Number(
|
|
726
|
+
localStorage.getItem(`${storageKey}-${src}`) || Number.NaN
|
|
727
|
+
);
|
|
537
728
|
const hasValidInitial = Number.isFinite(initialTime) && initialTime >= 0;
|
|
538
729
|
const hasValidSaved = Number.isFinite(saved) && saved >= 0;
|
|
539
730
|
if (hasValidInitial) return initialTime;
|
|
@@ -547,7 +738,7 @@ var VideoPlayer = ({
|
|
|
547
738
|
setCurrentTime(start);
|
|
548
739
|
}
|
|
549
740
|
}, [getInitialTime]);
|
|
550
|
-
const saveProgress =
|
|
741
|
+
const saveProgress = useCallback2(
|
|
551
742
|
(time) => {
|
|
552
743
|
if (!autoSave || !storageKey) return;
|
|
553
744
|
const now = Date.now();
|
|
@@ -558,7 +749,7 @@ var VideoPlayer = ({
|
|
|
558
749
|
},
|
|
559
750
|
[autoSave, storageKey, src]
|
|
560
751
|
);
|
|
561
|
-
const togglePlayPause =
|
|
752
|
+
const togglePlayPause = useCallback2(async () => {
|
|
562
753
|
const video = videoRef.current;
|
|
563
754
|
if (!video) return;
|
|
564
755
|
if (!video.paused) {
|
|
@@ -570,7 +761,7 @@ var VideoPlayer = ({
|
|
|
570
761
|
} catch {
|
|
571
762
|
}
|
|
572
763
|
}, []);
|
|
573
|
-
const handleVolumeChange =
|
|
764
|
+
const handleVolumeChange = useCallback2(
|
|
574
765
|
(newVolume) => {
|
|
575
766
|
const video = videoRef.current;
|
|
576
767
|
if (!video) return;
|
|
@@ -589,7 +780,7 @@ var VideoPlayer = ({
|
|
|
589
780
|
},
|
|
590
781
|
[isMuted]
|
|
591
782
|
);
|
|
592
|
-
const toggleMute =
|
|
783
|
+
const toggleMute = useCallback2(() => {
|
|
593
784
|
const video = videoRef.current;
|
|
594
785
|
if (!video) return;
|
|
595
786
|
if (isMuted) {
|
|
@@ -603,13 +794,13 @@ var VideoPlayer = ({
|
|
|
603
794
|
setIsMuted(true);
|
|
604
795
|
}
|
|
605
796
|
}, [isMuted, volume]);
|
|
606
|
-
const handleSeek =
|
|
797
|
+
const handleSeek = useCallback2((newTime) => {
|
|
607
798
|
const video = videoRef.current;
|
|
608
799
|
if (video) {
|
|
609
800
|
video.currentTime = newTime;
|
|
610
801
|
}
|
|
611
802
|
}, []);
|
|
612
|
-
const toggleFullscreen =
|
|
803
|
+
const toggleFullscreen = useCallback2(() => {
|
|
613
804
|
const container = videoRef.current?.parentElement;
|
|
614
805
|
if (!container) return;
|
|
615
806
|
if (!isFullscreen && container.requestFullscreen) {
|
|
@@ -618,23 +809,23 @@ var VideoPlayer = ({
|
|
|
618
809
|
document.exitFullscreen();
|
|
619
810
|
}
|
|
620
811
|
}, [isFullscreen]);
|
|
621
|
-
const handleSpeedChange =
|
|
812
|
+
const handleSpeedChange = useCallback2((speed) => {
|
|
622
813
|
if (videoRef.current) {
|
|
623
814
|
videoRef.current.playbackRate = speed;
|
|
624
815
|
setPlaybackRate(speed);
|
|
625
816
|
setShowSpeedMenu(false);
|
|
626
817
|
}
|
|
627
818
|
}, []);
|
|
628
|
-
const toggleSpeedMenu =
|
|
819
|
+
const toggleSpeedMenu = useCallback2(() => {
|
|
629
820
|
setShowSpeedMenu(!showSpeedMenu);
|
|
630
821
|
}, [showSpeedMenu]);
|
|
631
|
-
const toggleCaptions =
|
|
822
|
+
const toggleCaptions = useCallback2(() => {
|
|
632
823
|
if (!trackRef.current?.track || !subtitles) return;
|
|
633
824
|
const newShowCaptions = !showCaptions;
|
|
634
825
|
setShowCaptions(newShowCaptions);
|
|
635
826
|
trackRef.current.track.mode = newShowCaptions && subtitles ? "showing" : "hidden";
|
|
636
827
|
}, [showCaptions, subtitles]);
|
|
637
|
-
const checkVideoCompletion =
|
|
828
|
+
const checkVideoCompletion = useCallback2(
|
|
638
829
|
(progressPercent) => {
|
|
639
830
|
if (progressPercent >= 95 && !hasCompleted) {
|
|
640
831
|
setHasCompleted(true);
|
|
@@ -643,7 +834,7 @@ var VideoPlayer = ({
|
|
|
643
834
|
},
|
|
644
835
|
[hasCompleted, onVideoComplete]
|
|
645
836
|
);
|
|
646
|
-
const handleTimeUpdate =
|
|
837
|
+
const handleTimeUpdate = useCallback2(() => {
|
|
647
838
|
const video = videoRef.current;
|
|
648
839
|
if (!video) return;
|
|
649
840
|
const current = video.currentTime;
|
|
@@ -656,7 +847,7 @@ var VideoPlayer = ({
|
|
|
656
847
|
checkVideoCompletion(progressPercent);
|
|
657
848
|
}
|
|
658
849
|
}, [duration, saveProgress, onTimeUpdate, onProgress, checkVideoCompletion]);
|
|
659
|
-
const handleLoadedMetadata =
|
|
850
|
+
const handleLoadedMetadata = useCallback2(() => {
|
|
660
851
|
if (videoRef.current) {
|
|
661
852
|
setDuration(videoRef.current.duration);
|
|
662
853
|
}
|
|
@@ -680,46 +871,46 @@ var VideoPlayer = ({
|
|
|
680
871
|
}
|
|
681
872
|
};
|
|
682
873
|
document.addEventListener("visibilitychange", handleVisibilityChange);
|
|
683
|
-
|
|
874
|
+
globalThis.addEventListener("blur", handleBlur);
|
|
684
875
|
return () => {
|
|
685
876
|
document.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
686
|
-
|
|
877
|
+
globalThis.removeEventListener("blur", handleBlur);
|
|
687
878
|
clearControlsTimeout();
|
|
688
879
|
};
|
|
689
880
|
}, [isPlaying, clearControlsTimeout]);
|
|
690
881
|
const progressPercentage = duration > 0 ? currentTime / duration * 100 : 0;
|
|
691
|
-
const getIconSize =
|
|
882
|
+
const getIconSize = useCallback2(() => {
|
|
692
883
|
if (isTinyMobile) return 18;
|
|
693
884
|
if (isUltraSmallMobile) return 20;
|
|
694
885
|
return 24;
|
|
695
886
|
}, [isTinyMobile, isUltraSmallMobile]);
|
|
696
|
-
const getControlsPadding =
|
|
887
|
+
const getControlsPadding = useCallback2(() => {
|
|
697
888
|
if (isTinyMobile) return "px-2 pb-2 pt-1";
|
|
698
889
|
if (isUltraSmallMobile) return "px-3 pb-3 pt-1";
|
|
699
890
|
return "px-4 pb-4";
|
|
700
891
|
}, [isTinyMobile, isUltraSmallMobile]);
|
|
701
|
-
const getControlsGap =
|
|
892
|
+
const getControlsGap = useCallback2(() => {
|
|
702
893
|
if (isTinyMobile) return "gap-1";
|
|
703
894
|
if (isUltraSmallMobile) return "gap-2";
|
|
704
895
|
return "gap-4";
|
|
705
896
|
}, [isTinyMobile, isUltraSmallMobile]);
|
|
706
|
-
const getProgressBarPadding =
|
|
897
|
+
const getProgressBarPadding = useCallback2(() => {
|
|
707
898
|
if (isTinyMobile) return "px-2 pb-1";
|
|
708
899
|
if (isUltraSmallMobile) return "px-3 pb-1";
|
|
709
900
|
return "px-4 pb-2";
|
|
710
901
|
}, [isTinyMobile, isUltraSmallMobile]);
|
|
711
|
-
const getCenterPlayButtonPosition =
|
|
902
|
+
const getCenterPlayButtonPosition = useCallback2(() => {
|
|
712
903
|
if (isTinyMobile) return "items-center justify-center -translate-y-12";
|
|
713
904
|
if (isUltraSmallMobile) return "items-center justify-center -translate-y-8";
|
|
714
905
|
return "items-center justify-center";
|
|
715
906
|
}, [isTinyMobile, isUltraSmallMobile]);
|
|
716
|
-
const getTopControlsOpacity =
|
|
907
|
+
const getTopControlsOpacity = useCallback2(() => {
|
|
717
908
|
return showControls ? "opacity-100" : "opacity-0";
|
|
718
909
|
}, [showControls]);
|
|
719
|
-
const getBottomControlsOpacity =
|
|
910
|
+
const getBottomControlsOpacity = useCallback2(() => {
|
|
720
911
|
return showControls ? "opacity-100" : "opacity-0";
|
|
721
912
|
}, [showControls]);
|
|
722
|
-
const handleVideoKeyDown =
|
|
913
|
+
const handleVideoKeyDown = useCallback2(
|
|
723
914
|
(e) => {
|
|
724
915
|
if (e.key) {
|
|
725
916
|
e.stopPropagation();
|
|
@@ -775,30 +966,43 @@ var VideoPlayer = ({
|
|
|
775
966
|
]
|
|
776
967
|
);
|
|
777
968
|
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col", className), children: [
|
|
778
|
-
(title || subtitleText) && /* @__PURE__ */
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
969
|
+
(title || subtitleText) && /* @__PURE__ */ jsxs("div", { className: "bg-subject-1 px-8 py-4 flex items-end justify-between min-h-20", children: [
|
|
970
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
971
|
+
title && /* @__PURE__ */ jsx4(
|
|
972
|
+
Text_default,
|
|
973
|
+
{
|
|
974
|
+
as: "h2",
|
|
975
|
+
size: "lg",
|
|
976
|
+
weight: "bold",
|
|
977
|
+
color: "text-text-900",
|
|
978
|
+
className: "leading-5 tracking-wide",
|
|
979
|
+
children: title
|
|
980
|
+
}
|
|
981
|
+
),
|
|
982
|
+
subtitleText && /* @__PURE__ */ jsx4(
|
|
983
|
+
Text_default,
|
|
984
|
+
{
|
|
985
|
+
as: "p",
|
|
986
|
+
size: "sm",
|
|
987
|
+
weight: "normal",
|
|
988
|
+
color: "text-text-600",
|
|
989
|
+
className: "leading-5",
|
|
990
|
+
children: subtitleText
|
|
991
|
+
}
|
|
992
|
+
)
|
|
993
|
+
] }),
|
|
994
|
+
showDownloadButton && downloadContent && /* @__PURE__ */ jsx4(
|
|
995
|
+
DownloadButton_default,
|
|
792
996
|
{
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
997
|
+
content: downloadContent,
|
|
998
|
+
lessonTitle: title,
|
|
999
|
+
onDownloadStart,
|
|
1000
|
+
onDownloadComplete,
|
|
1001
|
+
onDownloadError,
|
|
1002
|
+
className: "flex-shrink-0"
|
|
799
1003
|
}
|
|
800
1004
|
)
|
|
801
|
-
] })
|
|
1005
|
+
] }),
|
|
802
1006
|
/* @__PURE__ */ jsxs(
|
|
803
1007
|
"section",
|
|
804
1008
|
{
|
|
@@ -814,7 +1018,7 @@ var VideoPlayer = ({
|
|
|
814
1018
|
onTouchStart: handleMouseEnter,
|
|
815
1019
|
onMouseLeave: handleMouseLeave,
|
|
816
1020
|
children: [
|
|
817
|
-
/* @__PURE__ */
|
|
1021
|
+
/* @__PURE__ */ jsx4(
|
|
818
1022
|
"video",
|
|
819
1023
|
{
|
|
820
1024
|
ref: videoRef,
|
|
@@ -829,7 +1033,7 @@ var VideoPlayer = ({
|
|
|
829
1033
|
onKeyDown: handleVideoKeyDown,
|
|
830
1034
|
tabIndex: 0,
|
|
831
1035
|
"aria-label": title ? `Video: ${title}` : "Video player",
|
|
832
|
-
children: /* @__PURE__ */
|
|
1036
|
+
children: /* @__PURE__ */ jsx4(
|
|
833
1037
|
"track",
|
|
834
1038
|
{
|
|
835
1039
|
ref: trackRef,
|
|
@@ -842,17 +1046,17 @@ var VideoPlayer = ({
|
|
|
842
1046
|
)
|
|
843
1047
|
}
|
|
844
1048
|
),
|
|
845
|
-
!isPlaying && /* @__PURE__ */
|
|
1049
|
+
!isPlaying && /* @__PURE__ */ jsx4(
|
|
846
1050
|
"div",
|
|
847
1051
|
{
|
|
848
1052
|
className: cn(
|
|
849
1053
|
"absolute inset-0 flex bg-black/30 transition-opacity",
|
|
850
1054
|
getCenterPlayButtonPosition()
|
|
851
1055
|
),
|
|
852
|
-
children: /* @__PURE__ */
|
|
1056
|
+
children: /* @__PURE__ */ jsx4(
|
|
853
1057
|
IconButton_default,
|
|
854
1058
|
{
|
|
855
|
-
icon: /* @__PURE__ */
|
|
1059
|
+
icon: /* @__PURE__ */ jsx4(Play, { size: 32, weight: "regular", className: "ml-1" }),
|
|
856
1060
|
onClick: togglePlayPause,
|
|
857
1061
|
"aria-label": "Play video",
|
|
858
1062
|
className: "!bg-transparent !text-white !w-auto !h-auto hover:!bg-transparent hover:!text-gray-200"
|
|
@@ -860,17 +1064,17 @@ var VideoPlayer = ({
|
|
|
860
1064
|
)
|
|
861
1065
|
}
|
|
862
1066
|
),
|
|
863
|
-
/* @__PURE__ */
|
|
1067
|
+
/* @__PURE__ */ jsx4(
|
|
864
1068
|
"div",
|
|
865
1069
|
{
|
|
866
1070
|
className: cn(
|
|
867
1071
|
"absolute top-0 left-0 right-0 p-4 bg-gradient-to-b from-black/70 to-transparent transition-opacity",
|
|
868
1072
|
getTopControlsOpacity()
|
|
869
1073
|
),
|
|
870
|
-
children: /* @__PURE__ */
|
|
1074
|
+
children: /* @__PURE__ */ jsx4("div", { className: "flex justify-start", children: /* @__PURE__ */ jsx4(
|
|
871
1075
|
IconButton_default,
|
|
872
1076
|
{
|
|
873
|
-
icon: isFullscreen ? /* @__PURE__ */
|
|
1077
|
+
icon: isFullscreen ? /* @__PURE__ */ jsx4(ArrowsInSimple, { size: 24 }) : /* @__PURE__ */ jsx4(ArrowsOutSimple, { size: 24 }),
|
|
874
1078
|
onClick: toggleFullscreen,
|
|
875
1079
|
"aria-label": isFullscreen ? "Exit fullscreen" : "Enter fullscreen",
|
|
876
1080
|
className: "!bg-transparent !text-white hover:!bg-white/20"
|
|
@@ -886,7 +1090,7 @@ var VideoPlayer = ({
|
|
|
886
1090
|
getBottomControlsOpacity()
|
|
887
1091
|
),
|
|
888
1092
|
children: [
|
|
889
|
-
/* @__PURE__ */
|
|
1093
|
+
/* @__PURE__ */ jsx4(
|
|
890
1094
|
ProgressBar,
|
|
891
1095
|
{
|
|
892
1096
|
currentTime,
|
|
@@ -905,16 +1109,16 @@ var VideoPlayer = ({
|
|
|
905
1109
|
),
|
|
906
1110
|
children: [
|
|
907
1111
|
/* @__PURE__ */ jsxs("div", { className: cn("flex items-center", getControlsGap()), children: [
|
|
908
|
-
/* @__PURE__ */
|
|
1112
|
+
/* @__PURE__ */ jsx4(
|
|
909
1113
|
IconButton_default,
|
|
910
1114
|
{
|
|
911
|
-
icon: isPlaying ? /* @__PURE__ */
|
|
1115
|
+
icon: isPlaying ? /* @__PURE__ */ jsx4(Pause, { size: getIconSize() }) : /* @__PURE__ */ jsx4(Play, { size: getIconSize() }),
|
|
912
1116
|
onClick: togglePlayPause,
|
|
913
1117
|
"aria-label": isPlaying ? "Pause" : "Play",
|
|
914
1118
|
className: "!bg-transparent !text-white hover:!bg-white/20"
|
|
915
1119
|
}
|
|
916
1120
|
),
|
|
917
|
-
/* @__PURE__ */
|
|
1121
|
+
/* @__PURE__ */ jsx4(
|
|
918
1122
|
VolumeControls,
|
|
919
1123
|
{
|
|
920
1124
|
volume,
|
|
@@ -925,10 +1129,10 @@ var VideoPlayer = ({
|
|
|
925
1129
|
showSlider: !isUltraSmallMobile
|
|
926
1130
|
}
|
|
927
1131
|
),
|
|
928
|
-
subtitles && /* @__PURE__ */
|
|
1132
|
+
subtitles && /* @__PURE__ */ jsx4(
|
|
929
1133
|
IconButton_default,
|
|
930
1134
|
{
|
|
931
|
-
icon: /* @__PURE__ */
|
|
1135
|
+
icon: /* @__PURE__ */ jsx4(ClosedCaptioning, { size: getIconSize() }),
|
|
932
1136
|
onClick: toggleCaptions,
|
|
933
1137
|
"aria-label": showCaptions ? "Hide captions" : "Show captions",
|
|
934
1138
|
className: cn(
|
|
@@ -943,7 +1147,7 @@ var VideoPlayer = ({
|
|
|
943
1147
|
formatTime(duration)
|
|
944
1148
|
] })
|
|
945
1149
|
] }),
|
|
946
|
-
/* @__PURE__ */
|
|
1150
|
+
/* @__PURE__ */ jsx4("div", { className: "flex items-center gap-4", children: /* @__PURE__ */ jsx4(
|
|
947
1151
|
SpeedMenu,
|
|
948
1152
|
{
|
|
949
1153
|
showSpeedMenu,
|