softbuilders-react-video-player 1.1.16 → 1.1.18
Sign up to get free protection for your applications and to get access to all the features.
- package/LICENSE +21 -21
- package/README.md +126 -126
- package/dist/components/BufferTracker/index.tsx +19 -19
- package/dist/components/ChapterTooltip/index.tsx +65 -65
- package/dist/components/ChaptersPanal/index.tsx +40 -40
- package/dist/components/CreateNoteMenu/index.tsx +61 -61
- package/dist/components/CurrentTimeLabel/index.tsx +13 -13
- package/dist/components/CurrentTimeTracker/index.tsx +18 -18
- package/dist/components/Menu/index.tsx +49 -49
- package/dist/components/NoteTooltip/index.tsx +46 -46
- package/dist/components/NotesPanal/index.tsx +34 -34
- package/dist/components/TimeSlider/index.js +13 -13
- package/dist/components/TimeSlider/index.tsx +107 -107
- package/dist/components/TimeSliderContainer/index.tsx +35 -35
- package/dist/components/Tooltip/index.tsx +16 -16
- package/dist/components/VideoPlayerComponent/provider.tsx +82 -82
- package/dist/components/VideoPlayerComponent/style/style.css +36 -36
- package/dist/styles/tailwind.css +101 -101
- package/package.json +4 -4
@@ -1,13 +1,13 @@
|
|
1
|
-
"use client";
|
2
|
-
import React, { useEffect } from "react";
|
3
|
-
import { durationFormater } from "../../utils";
|
4
|
-
import { useSoftBuildersVideoPlayerContext } from "../VideoPlayerComponent/provider";
|
5
|
-
type Props = {};
|
6
|
-
|
7
|
-
const CurrentTimeLabel = ({}: Props) => {
|
8
|
-
const { currentTime } = useSoftBuildersVideoPlayerContext();
|
9
|
-
|
10
|
-
return <p>{durationFormater(currentTime)}</p>;
|
11
|
-
};
|
12
|
-
|
13
|
-
export default CurrentTimeLabel;
|
1
|
+
"use client";
|
2
|
+
import React, { useEffect } from "react";
|
3
|
+
import { durationFormater } from "../../utils";
|
4
|
+
import { useSoftBuildersVideoPlayerContext } from "../VideoPlayerComponent/provider";
|
5
|
+
type Props = {};
|
6
|
+
|
7
|
+
const CurrentTimeLabel = ({}: Props) => {
|
8
|
+
const { currentTime } = useSoftBuildersVideoPlayerContext();
|
9
|
+
|
10
|
+
return <p>{durationFormater(currentTime)}</p>;
|
11
|
+
};
|
12
|
+
|
13
|
+
export default CurrentTimeLabel;
|
@@ -1,18 +1,18 @@
|
|
1
|
-
import React, { useEffect } from "react";
|
2
|
-
import { useSoftBuildersVideoPlayerContext } from "../VideoPlayerComponent/provider";
|
3
|
-
|
4
|
-
const CurrentTimeTracker = () => {
|
5
|
-
const { setCurrentTime, player } = useSoftBuildersVideoPlayerContext();
|
6
|
-
|
7
|
-
useEffect(() => {
|
8
|
-
const intervalId = setInterval(() => {
|
9
|
-
setCurrentTime(player?.currentTime() || 0);
|
10
|
-
}, 500);
|
11
|
-
|
12
|
-
// Cleanup function to clear the interval
|
13
|
-
return () => clearInterval(intervalId);
|
14
|
-
}, [player]);
|
15
|
-
return <></>;
|
16
|
-
};
|
17
|
-
|
18
|
-
export default CurrentTimeTracker;
|
1
|
+
import React, { useEffect } from "react";
|
2
|
+
import { useSoftBuildersVideoPlayerContext } from "../VideoPlayerComponent/provider";
|
3
|
+
|
4
|
+
const CurrentTimeTracker = () => {
|
5
|
+
const { setCurrentTime, player } = useSoftBuildersVideoPlayerContext();
|
6
|
+
|
7
|
+
useEffect(() => {
|
8
|
+
const intervalId = setInterval(() => {
|
9
|
+
setCurrentTime(player?.currentTime() || 0);
|
10
|
+
}, 500);
|
11
|
+
|
12
|
+
// Cleanup function to clear the interval
|
13
|
+
return () => clearInterval(intervalId);
|
14
|
+
}, [player]);
|
15
|
+
return <></>;
|
16
|
+
};
|
17
|
+
|
18
|
+
export default CurrentTimeTracker;
|
@@ -1,49 +1,49 @@
|
|
1
|
-
import React from "react";
|
2
|
-
|
3
|
-
type Props = {
|
4
|
-
name: string;
|
5
|
-
};
|
6
|
-
|
7
|
-
const Menu = ({ name }: Props) => {
|
8
|
-
return (
|
9
|
-
<div className="absolute right-0 z-10 w-48 mt-2 origin-top-right bg-white border border-gray-300 rounded-md shadow-lg focus:outline-none">
|
10
|
-
<div
|
11
|
-
className="py-1"
|
12
|
-
role="menu"
|
13
|
-
aria-orientation="vertical"
|
14
|
-
aria-labelledby={`${name}-button`}
|
15
|
-
>
|
16
|
-
<a
|
17
|
-
href="#"
|
18
|
-
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900"
|
19
|
-
role="menuitem"
|
20
|
-
>
|
21
|
-
Dashboard
|
22
|
-
</a>
|
23
|
-
<a
|
24
|
-
href="#"
|
25
|
-
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900"
|
26
|
-
role="menuitem"
|
27
|
-
>
|
28
|
-
Settings
|
29
|
-
</a>
|
30
|
-
<a
|
31
|
-
href="#"
|
32
|
-
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900"
|
33
|
-
role="menuitem"
|
34
|
-
>
|
35
|
-
Profile
|
36
|
-
</a>
|
37
|
-
<a
|
38
|
-
href="#"
|
39
|
-
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900"
|
40
|
-
role="menuitem"
|
41
|
-
>
|
42
|
-
Logout
|
43
|
-
</a>
|
44
|
-
</div>
|
45
|
-
</div>
|
46
|
-
);
|
47
|
-
};
|
48
|
-
|
49
|
-
export default Menu;
|
1
|
+
import React from "react";
|
2
|
+
|
3
|
+
type Props = {
|
4
|
+
name: string;
|
5
|
+
};
|
6
|
+
|
7
|
+
const Menu = ({ name }: Props) => {
|
8
|
+
return (
|
9
|
+
<div className="absolute right-0 z-10 w-48 mt-2 origin-top-right bg-white border border-gray-300 rounded-md shadow-lg focus:outline-none">
|
10
|
+
<div
|
11
|
+
className="py-1"
|
12
|
+
role="menu"
|
13
|
+
aria-orientation="vertical"
|
14
|
+
aria-labelledby={`${name}-button`}
|
15
|
+
>
|
16
|
+
<a
|
17
|
+
href="#"
|
18
|
+
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900"
|
19
|
+
role="menuitem"
|
20
|
+
>
|
21
|
+
Dashboard
|
22
|
+
</a>
|
23
|
+
<a
|
24
|
+
href="#"
|
25
|
+
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900"
|
26
|
+
role="menuitem"
|
27
|
+
>
|
28
|
+
Settings
|
29
|
+
</a>
|
30
|
+
<a
|
31
|
+
href="#"
|
32
|
+
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900"
|
33
|
+
role="menuitem"
|
34
|
+
>
|
35
|
+
Profile
|
36
|
+
</a>
|
37
|
+
<a
|
38
|
+
href="#"
|
39
|
+
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900"
|
40
|
+
role="menuitem"
|
41
|
+
>
|
42
|
+
Logout
|
43
|
+
</a>
|
44
|
+
</div>
|
45
|
+
</div>
|
46
|
+
);
|
47
|
+
};
|
48
|
+
|
49
|
+
export default Menu;
|
@@ -1,46 +1,46 @@
|
|
1
|
-
import React, { useEffect, useState } from "react";
|
2
|
-
import Tooltip from "../Tooltip";
|
3
|
-
import { durationFormater } from "../../utils";
|
4
|
-
import { useSoftBuildersVideoPlayerContext } from "../VideoPlayerComponent/provider";
|
5
|
-
import { SoftBuildersVideoPlayerNote } from "../../types";
|
6
|
-
type Note = SoftBuildersVideoPlayerNote & { percentage: number };
|
7
|
-
|
8
|
-
type Props = {
|
9
|
-
note: Note;
|
10
|
-
};
|
11
|
-
|
12
|
-
const NoteTooltip = ({ note }: Props) => {
|
13
|
-
const [open, setOpen] = useState(false);
|
14
|
-
const { currentTime } = useSoftBuildersVideoPlayerContext();
|
15
|
-
|
16
|
-
useEffect(() => {
|
17
|
-
if (currentTime === Math.floor(note.time)) {
|
18
|
-
setOpen(true);
|
19
|
-
|
20
|
-
setTimeout(() => {
|
21
|
-
setOpen(false);
|
22
|
-
}, 5000);
|
23
|
-
}
|
24
|
-
}, [currentTime, note.time]);
|
25
|
-
return (
|
26
|
-
<div
|
27
|
-
className="sb-w-1 sb-h-1 sb-rounded-full sb-bg-white sb-absolute sb-z-30"
|
28
|
-
style={{ left: `${note.percentage}%` }}
|
29
|
-
onMouseEnter={() => setOpen(true)}
|
30
|
-
onMouseLeave={() => setOpen(false)}
|
31
|
-
>
|
32
|
-
<div className="sb-relative">
|
33
|
-
<Tooltip open={open}>
|
34
|
-
<div className="sb-flex sb-flex-col sb-gap-2 sb-items-center">
|
35
|
-
<p>{note.label}</p>
|
36
|
-
<p className="sb-p-2 sb-bg-[#303030] sb-bg-opacity-50 sb-rounded-md">
|
37
|
-
{durationFormater(note.time)}
|
38
|
-
</p>
|
39
|
-
</div>
|
40
|
-
</Tooltip>
|
41
|
-
</div>
|
42
|
-
</div>
|
43
|
-
);
|
44
|
-
};
|
45
|
-
|
46
|
-
export default NoteTooltip;
|
1
|
+
import React, { useEffect, useState } from "react";
|
2
|
+
import Tooltip from "../Tooltip";
|
3
|
+
import { durationFormater } from "../../utils";
|
4
|
+
import { useSoftBuildersVideoPlayerContext } from "../VideoPlayerComponent/provider";
|
5
|
+
import { SoftBuildersVideoPlayerNote } from "../../types";
|
6
|
+
type Note = SoftBuildersVideoPlayerNote & { percentage: number };
|
7
|
+
|
8
|
+
type Props = {
|
9
|
+
note: Note;
|
10
|
+
};
|
11
|
+
|
12
|
+
const NoteTooltip = ({ note }: Props) => {
|
13
|
+
const [open, setOpen] = useState(false);
|
14
|
+
const { currentTime } = useSoftBuildersVideoPlayerContext();
|
15
|
+
|
16
|
+
useEffect(() => {
|
17
|
+
if (currentTime === Math.floor(note.time)) {
|
18
|
+
setOpen(true);
|
19
|
+
|
20
|
+
setTimeout(() => {
|
21
|
+
setOpen(false);
|
22
|
+
}, 5000);
|
23
|
+
}
|
24
|
+
}, [currentTime, note.time]);
|
25
|
+
return (
|
26
|
+
<div
|
27
|
+
className="sb-w-1 sb-h-1 sb-rounded-full sb-bg-white sb-absolute sb-z-30"
|
28
|
+
style={{ left: `${note.percentage}%` }}
|
29
|
+
onMouseEnter={() => setOpen(true)}
|
30
|
+
onMouseLeave={() => setOpen(false)}
|
31
|
+
>
|
32
|
+
<div className="sb-relative">
|
33
|
+
<Tooltip open={open}>
|
34
|
+
<div className="sb-flex sb-flex-col sb-gap-2 sb-items-center">
|
35
|
+
<p>{note.label}</p>
|
36
|
+
<p className="sb-p-2 sb-bg-[#303030] sb-bg-opacity-50 sb-rounded-md">
|
37
|
+
{durationFormater(note.time)}
|
38
|
+
</p>
|
39
|
+
</div>
|
40
|
+
</Tooltip>
|
41
|
+
</div>
|
42
|
+
</div>
|
43
|
+
);
|
44
|
+
};
|
45
|
+
|
46
|
+
export default NoteTooltip;
|
@@ -1,34 +1,34 @@
|
|
1
|
-
import React, { useEffect, useState } from "react";
|
2
|
-
import NoteTooltip from "../NoteTooltip";
|
3
|
-
import { useSoftBuildersVideoPlayerContext } from "../VideoPlayerComponent/provider";
|
4
|
-
import { SoftBuildersVideoPlayerNote } from "../../types";
|
5
|
-
|
6
|
-
type Note = SoftBuildersVideoPlayerNote & { percentage: number };
|
7
|
-
type Props = {
|
8
|
-
notes: SoftBuildersVideoPlayerNote[];
|
9
|
-
};
|
10
|
-
const NotesPanal = ({ notes }: Props) => {
|
11
|
-
const [ns, setNs] = useState<Note[]>([]);
|
12
|
-
|
13
|
-
const { duration } = useSoftBuildersVideoPlayerContext();
|
14
|
-
|
15
|
-
useEffect(() => {
|
16
|
-
const newNs = notes.map((n) => {
|
17
|
-
const percentage = Math.floor((n.time * 100) / duration);
|
18
|
-
return {
|
19
|
-
...n,
|
20
|
-
percentage,
|
21
|
-
};
|
22
|
-
});
|
23
|
-
setNs(newNs);
|
24
|
-
}, [notes, duration]);
|
25
|
-
return (
|
26
|
-
<div id="notes-panal" className="sb-w-full sb-h-full sb-relative ">
|
27
|
-
{ns.map((n, i) => (
|
28
|
-
<NoteTooltip key={`note-${i}-${n.time}`} note={n} />
|
29
|
-
))}
|
30
|
-
</div>
|
31
|
-
);
|
32
|
-
};
|
33
|
-
|
34
|
-
export default NotesPanal;
|
1
|
+
import React, { useEffect, useState } from "react";
|
2
|
+
import NoteTooltip from "../NoteTooltip";
|
3
|
+
import { useSoftBuildersVideoPlayerContext } from "../VideoPlayerComponent/provider";
|
4
|
+
import { SoftBuildersVideoPlayerNote } from "../../types";
|
5
|
+
|
6
|
+
type Note = SoftBuildersVideoPlayerNote & { percentage: number };
|
7
|
+
type Props = {
|
8
|
+
notes: SoftBuildersVideoPlayerNote[];
|
9
|
+
};
|
10
|
+
const NotesPanal = ({ notes }: Props) => {
|
11
|
+
const [ns, setNs] = useState<Note[]>([]);
|
12
|
+
|
13
|
+
const { duration } = useSoftBuildersVideoPlayerContext();
|
14
|
+
|
15
|
+
useEffect(() => {
|
16
|
+
const newNs = notes.map((n) => {
|
17
|
+
const percentage = Math.floor((n.time * 100) / duration);
|
18
|
+
return {
|
19
|
+
...n,
|
20
|
+
percentage,
|
21
|
+
};
|
22
|
+
});
|
23
|
+
setNs(newNs);
|
24
|
+
}, [notes, duration]);
|
25
|
+
return (
|
26
|
+
<div id="notes-panal" className="sb-w-full sb-h-full sb-relative ">
|
27
|
+
{ns.map((n, i) => (
|
28
|
+
<NoteTooltip key={`note-${i}-${n.time}`} note={n} />
|
29
|
+
))}
|
30
|
+
</div>
|
31
|
+
);
|
32
|
+
};
|
33
|
+
|
34
|
+
export default NotesPanal;
|
@@ -44,20 +44,20 @@ const TimeSlider = ({ chapters }) => {
|
|
44
44
|
return (_jsxs("div", { className: " sb-w-full sb-h-2 sb-flex sb-items-center sb-justify-center", children: [_jsx("div", { className: "sb-absolute sb-top-0 sb-left-0 sb-w-full sb-z-10", children: _jsx(Slider, { value: timeSlider, handleValueChange: handleValueChange, min: MIN, max: MAX, style: {
|
45
45
|
background: "transparent",
|
46
46
|
} }) }), _jsx("div", { className: "sb-absolute sb-top-0 sb-left-0 sb-w-full sb-h-2 sb-bg-slate-400 sb-rounded-md", style: {
|
47
|
-
background: `
|
48
|
-
linear-gradient(to right,
|
49
|
-
#f97316 0%,
|
50
|
-
#f97316 ${timeSlider}%,
|
51
|
-
#f9731640 ${timeSlider}%,
|
52
|
-
#f9731640 ${downloadedBufferPercentage}%,
|
53
|
-
#30303030 ${timeSlider}%,
|
54
|
-
#30303030 100%
|
55
|
-
)
|
47
|
+
background: `
|
48
|
+
linear-gradient(to right,
|
49
|
+
#f97316 0%,
|
50
|
+
#f97316 ${timeSlider}%,
|
51
|
+
#f9731640 ${timeSlider}%,
|
52
|
+
#f9731640 ${downloadedBufferPercentage}%,
|
53
|
+
#30303030 ${timeSlider}%,
|
54
|
+
#30303030 100%
|
55
|
+
)
|
56
56
|
`,
|
57
|
-
maskImage: `
|
58
|
-
linear-gradient(to right,
|
59
|
-
${maskCuttes}
|
60
|
-
)
|
57
|
+
maskImage: `
|
58
|
+
linear-gradient(to right,
|
59
|
+
${maskCuttes}
|
60
|
+
)
|
61
61
|
`,
|
62
62
|
maskSize: "100% 100%",
|
63
63
|
maskRepeat: "no-repeat",
|
@@ -1,107 +1,107 @@
|
|
1
|
-
import React, { useEffect, useState } from "react";
|
2
|
-
import Slider from "../Slider";
|
3
|
-
import { useSoftBuildersVideoPlayerContext } from "../VideoPlayerComponent/provider";
|
4
|
-
import { SoftBuildersVideoPlayerChapter } from "../../types";
|
5
|
-
|
6
|
-
const MIN = 0,
|
7
|
-
MAX = 100;
|
8
|
-
const DEFERENCE = Math.abs(MAX - MIN);
|
9
|
-
const BAR_PERCENTAGE_WIDTH = 0.5;
|
10
|
-
|
11
|
-
type Props = {
|
12
|
-
chapters: SoftBuildersVideoPlayerChapter[];
|
13
|
-
};
|
14
|
-
|
15
|
-
const TimeSlider = ({ chapters }: Props) => {
|
16
|
-
const [timeSlider, setTimeSlider] = useState(0);
|
17
|
-
|
18
|
-
const { player, duration, downloadedBufferPercentage } =
|
19
|
-
useSoftBuildersVideoPlayerContext();
|
20
|
-
|
21
|
-
const handleValueChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
22
|
-
const newTimeSlider = Number(e.target.value);
|
23
|
-
setTimeSlider(newTimeSlider);
|
24
|
-
|
25
|
-
const time = (newTimeSlider * duration) / DEFERENCE;
|
26
|
-
|
27
|
-
player?.currentTime(time);
|
28
|
-
};
|
29
|
-
|
30
|
-
useEffect(() => {
|
31
|
-
const intervalId = setInterval(() => {
|
32
|
-
const currentTime = player?.currentTime() || 0;
|
33
|
-
|
34
|
-
const time = (currentTime * DEFERENCE) / duration;
|
35
|
-
|
36
|
-
setTimeSlider(time);
|
37
|
-
}, 1000);
|
38
|
-
|
39
|
-
// Cleanup function to clear the interval
|
40
|
-
return () => clearInterval(intervalId);
|
41
|
-
}, [player, duration]);
|
42
|
-
|
43
|
-
const [maskCuttes, setMaskCuttes] = useState("");
|
44
|
-
|
45
|
-
useEffect(() => {
|
46
|
-
const arr: string[] = ["black 0%"];
|
47
|
-
chapters.forEach((c) => {
|
48
|
-
const startPercentage = Math.floor((c.startTime * 100) / duration);
|
49
|
-
const endPercentage = Math.floor((c.endTime * 100) / duration);
|
50
|
-
arr.push(`black ${startPercentage}%`);
|
51
|
-
arr.push(`transparent ${startPercentage}%`);
|
52
|
-
arr.push(`transparent ${startPercentage + BAR_PERCENTAGE_WIDTH}%`);
|
53
|
-
|
54
|
-
arr.push(`black ${startPercentage + BAR_PERCENTAGE_WIDTH}%`);
|
55
|
-
arr.push(`black ${endPercentage}%`);
|
56
|
-
|
57
|
-
arr.push(`transparent ${endPercentage}%`);
|
58
|
-
arr.push(`transparent ${endPercentage + BAR_PERCENTAGE_WIDTH}%`);
|
59
|
-
|
60
|
-
arr.push(`black ${endPercentage + BAR_PERCENTAGE_WIDTH}%`);
|
61
|
-
});
|
62
|
-
arr.push(`black 100%`);
|
63
|
-
|
64
|
-
setMaskCuttes(arr.toString());
|
65
|
-
}, [chapters, duration]);
|
66
|
-
|
67
|
-
return (
|
68
|
-
<div className=" sb-w-full sb-h-2 sb-flex sb-items-center sb-justify-center">
|
69
|
-
<div className="sb-absolute sb-top-0 sb-left-0 sb-w-full sb-z-10">
|
70
|
-
<Slider
|
71
|
-
value={timeSlider}
|
72
|
-
handleValueChange={handleValueChange}
|
73
|
-
min={MIN}
|
74
|
-
max={MAX}
|
75
|
-
style={{
|
76
|
-
background: "transparent",
|
77
|
-
}}
|
78
|
-
/>
|
79
|
-
</div>
|
80
|
-
|
81
|
-
<div
|
82
|
-
className="sb-absolute sb-top-0 sb-left-0 sb-w-full sb-h-2 sb-bg-slate-400 sb-rounded-md"
|
83
|
-
style={{
|
84
|
-
background: `
|
85
|
-
linear-gradient(to right,
|
86
|
-
#f97316 0%,
|
87
|
-
#f97316 ${timeSlider}%,
|
88
|
-
#f9731640 ${timeSlider}%,
|
89
|
-
#f9731640 ${downloadedBufferPercentage}%,
|
90
|
-
#30303030 ${timeSlider}%,
|
91
|
-
#30303030 100%
|
92
|
-
)
|
93
|
-
`,
|
94
|
-
maskImage: `
|
95
|
-
linear-gradient(to right,
|
96
|
-
${maskCuttes}
|
97
|
-
)
|
98
|
-
`,
|
99
|
-
maskSize: "100% 100%",
|
100
|
-
maskRepeat: "no-repeat",
|
101
|
-
}}
|
102
|
-
></div>
|
103
|
-
</div>
|
104
|
-
);
|
105
|
-
};
|
106
|
-
|
107
|
-
export default TimeSlider;
|
1
|
+
import React, { useEffect, useState } from "react";
|
2
|
+
import Slider from "../Slider";
|
3
|
+
import { useSoftBuildersVideoPlayerContext } from "../VideoPlayerComponent/provider";
|
4
|
+
import { SoftBuildersVideoPlayerChapter } from "../../types";
|
5
|
+
|
6
|
+
const MIN = 0,
|
7
|
+
MAX = 100;
|
8
|
+
const DEFERENCE = Math.abs(MAX - MIN);
|
9
|
+
const BAR_PERCENTAGE_WIDTH = 0.5;
|
10
|
+
|
11
|
+
type Props = {
|
12
|
+
chapters: SoftBuildersVideoPlayerChapter[];
|
13
|
+
};
|
14
|
+
|
15
|
+
const TimeSlider = ({ chapters }: Props) => {
|
16
|
+
const [timeSlider, setTimeSlider] = useState(0);
|
17
|
+
|
18
|
+
const { player, duration, downloadedBufferPercentage } =
|
19
|
+
useSoftBuildersVideoPlayerContext();
|
20
|
+
|
21
|
+
const handleValueChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
22
|
+
const newTimeSlider = Number(e.target.value);
|
23
|
+
setTimeSlider(newTimeSlider);
|
24
|
+
|
25
|
+
const time = (newTimeSlider * duration) / DEFERENCE;
|
26
|
+
|
27
|
+
player?.currentTime(time);
|
28
|
+
};
|
29
|
+
|
30
|
+
useEffect(() => {
|
31
|
+
const intervalId = setInterval(() => {
|
32
|
+
const currentTime = player?.currentTime() || 0;
|
33
|
+
|
34
|
+
const time = (currentTime * DEFERENCE) / duration;
|
35
|
+
|
36
|
+
setTimeSlider(time);
|
37
|
+
}, 1000);
|
38
|
+
|
39
|
+
// Cleanup function to clear the interval
|
40
|
+
return () => clearInterval(intervalId);
|
41
|
+
}, [player, duration]);
|
42
|
+
|
43
|
+
const [maskCuttes, setMaskCuttes] = useState("");
|
44
|
+
|
45
|
+
useEffect(() => {
|
46
|
+
const arr: string[] = ["black 0%"];
|
47
|
+
chapters.forEach((c) => {
|
48
|
+
const startPercentage = Math.floor((c.startTime * 100) / duration);
|
49
|
+
const endPercentage = Math.floor((c.endTime * 100) / duration);
|
50
|
+
arr.push(`black ${startPercentage}%`);
|
51
|
+
arr.push(`transparent ${startPercentage}%`);
|
52
|
+
arr.push(`transparent ${startPercentage + BAR_PERCENTAGE_WIDTH}%`);
|
53
|
+
|
54
|
+
arr.push(`black ${startPercentage + BAR_PERCENTAGE_WIDTH}%`);
|
55
|
+
arr.push(`black ${endPercentage}%`);
|
56
|
+
|
57
|
+
arr.push(`transparent ${endPercentage}%`);
|
58
|
+
arr.push(`transparent ${endPercentage + BAR_PERCENTAGE_WIDTH}%`);
|
59
|
+
|
60
|
+
arr.push(`black ${endPercentage + BAR_PERCENTAGE_WIDTH}%`);
|
61
|
+
});
|
62
|
+
arr.push(`black 100%`);
|
63
|
+
|
64
|
+
setMaskCuttes(arr.toString());
|
65
|
+
}, [chapters, duration]);
|
66
|
+
|
67
|
+
return (
|
68
|
+
<div className=" sb-w-full sb-h-2 sb-flex sb-items-center sb-justify-center">
|
69
|
+
<div className="sb-absolute sb-top-0 sb-left-0 sb-w-full sb-z-10">
|
70
|
+
<Slider
|
71
|
+
value={timeSlider}
|
72
|
+
handleValueChange={handleValueChange}
|
73
|
+
min={MIN}
|
74
|
+
max={MAX}
|
75
|
+
style={{
|
76
|
+
background: "transparent",
|
77
|
+
}}
|
78
|
+
/>
|
79
|
+
</div>
|
80
|
+
|
81
|
+
<div
|
82
|
+
className="sb-absolute sb-top-0 sb-left-0 sb-w-full sb-h-2 sb-bg-slate-400 sb-rounded-md"
|
83
|
+
style={{
|
84
|
+
background: `
|
85
|
+
linear-gradient(to right,
|
86
|
+
#f97316 0%,
|
87
|
+
#f97316 ${timeSlider}%,
|
88
|
+
#f9731640 ${timeSlider}%,
|
89
|
+
#f9731640 ${downloadedBufferPercentage}%,
|
90
|
+
#30303030 ${timeSlider}%,
|
91
|
+
#30303030 100%
|
92
|
+
)
|
93
|
+
`,
|
94
|
+
maskImage: `
|
95
|
+
linear-gradient(to right,
|
96
|
+
${maskCuttes}
|
97
|
+
)
|
98
|
+
`,
|
99
|
+
maskSize: "100% 100%",
|
100
|
+
maskRepeat: "no-repeat",
|
101
|
+
}}
|
102
|
+
></div>
|
103
|
+
</div>
|
104
|
+
);
|
105
|
+
};
|
106
|
+
|
107
|
+
export default TimeSlider;
|
@@ -1,35 +1,35 @@
|
|
1
|
-
import React from "react";
|
2
|
-
import NotesPanal from "../NotesPanal";
|
3
|
-
import ChaptersPanal from "../ChaptersPanal";
|
4
|
-
import TimeSlider from "../TimeSlider";
|
5
|
-
import {
|
6
|
-
SoftBuildersVideoPlayerChapter,
|
7
|
-
SoftBuildersVideoPlayerNote,
|
8
|
-
} from "../../types";
|
9
|
-
|
10
|
-
type Props = {
|
11
|
-
notes: SoftBuildersVideoPlayerNote[];
|
12
|
-
chapters: SoftBuildersVideoPlayerChapter[];
|
13
|
-
};
|
14
|
-
const TimeSliderContainer = ({ notes, chapters }: Props) => {
|
15
|
-
return (
|
16
|
-
<div
|
17
|
-
id="time-slider-container"
|
18
|
-
className="sb-w-full sb-relative sb-flex sb-items-center sb-justify-center"
|
19
|
-
>
|
20
|
-
<div
|
21
|
-
id="notes-panal"
|
22
|
-
className="sb-absolute sb-w-full sb-h-full sb-top-[27%] sb-left-0"
|
23
|
-
>
|
24
|
-
<NotesPanal notes={notes} />
|
25
|
-
</div>
|
26
|
-
|
27
|
-
<div className="sb-absolute sb-w-full sb-h-full sb-top-0 sb-left-0">
|
28
|
-
<ChaptersPanal chapters={chapters} />
|
29
|
-
</div>
|
30
|
-
<TimeSlider chapters={chapters} />
|
31
|
-
</div>
|
32
|
-
);
|
33
|
-
};
|
34
|
-
|
35
|
-
export default TimeSliderContainer;
|
1
|
+
import React from "react";
|
2
|
+
import NotesPanal from "../NotesPanal";
|
3
|
+
import ChaptersPanal from "../ChaptersPanal";
|
4
|
+
import TimeSlider from "../TimeSlider";
|
5
|
+
import {
|
6
|
+
SoftBuildersVideoPlayerChapter,
|
7
|
+
SoftBuildersVideoPlayerNote,
|
8
|
+
} from "../../types";
|
9
|
+
|
10
|
+
type Props = {
|
11
|
+
notes: SoftBuildersVideoPlayerNote[];
|
12
|
+
chapters: SoftBuildersVideoPlayerChapter[];
|
13
|
+
};
|
14
|
+
const TimeSliderContainer = ({ notes, chapters }: Props) => {
|
15
|
+
return (
|
16
|
+
<div
|
17
|
+
id="time-slider-container"
|
18
|
+
className="sb-w-full sb-relative sb-flex sb-items-center sb-justify-center"
|
19
|
+
>
|
20
|
+
<div
|
21
|
+
id="notes-panal"
|
22
|
+
className="sb-absolute sb-w-full sb-h-full sb-top-[27%] sb-left-0"
|
23
|
+
>
|
24
|
+
<NotesPanal notes={notes} />
|
25
|
+
</div>
|
26
|
+
|
27
|
+
<div className="sb-absolute sb-w-full sb-h-full sb-top-0 sb-left-0">
|
28
|
+
<ChaptersPanal chapters={chapters} />
|
29
|
+
</div>
|
30
|
+
<TimeSlider chapters={chapters} />
|
31
|
+
</div>
|
32
|
+
);
|
33
|
+
};
|
34
|
+
|
35
|
+
export default TimeSliderContainer;
|