REFACTOR sliders into different files

This commit is contained in:
2025-05-23 11:39:29 +02:00
parent 62af69e45b
commit a25564dd31
5 changed files with 125 additions and 66 deletions

16
package-lock.json generated
View File

@@ -24,6 +24,7 @@
"@vaadin/vaadin-material-styles": "24.7.5", "@vaadin/vaadin-material-styles": "24.7.5",
"@vaadin/vaadin-themable-mixin": "24.7.5", "@vaadin/vaadin-themable-mixin": "24.7.5",
"@vaadin/vaadin-usage-statistics": "2.1.3", "@vaadin/vaadin-usage-statistics": "2.1.3",
"clsx": "^2.1.1",
"construct-style-sheets-polyfill": "3.1.0", "construct-style-sheets-polyfill": "3.1.0",
"date-fns": "2.29.3", "date-fns": "2.29.3",
"lit": "3.3.0", "lit": "3.3.0",
@@ -5560,9 +5561,9 @@
} }
}, },
"node_modules/clsx": { "node_modules/clsx": {
"version": "1.2.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
"integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=6" "node": ">=6"
@@ -8494,6 +8495,15 @@
"core-js": "^3.22.4" "core-js": "^3.22.4"
} }
}, },
"node_modules/react-range-slider-input/node_modules/clsx": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
"integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/react-refresh": { "node_modules/react-refresh": {
"version": "0.17.0", "version": "0.17.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",

View File

@@ -54,6 +54,7 @@
"@vaadin/vaadin-material-styles": "24.7.5", "@vaadin/vaadin-material-styles": "24.7.5",
"@vaadin/vaadin-themable-mixin": "24.7.5", "@vaadin/vaadin-themable-mixin": "24.7.5",
"@vaadin/vaadin-usage-statistics": "2.1.3", "@vaadin/vaadin-usage-statistics": "2.1.3",
"clsx": "^2.1.1",
"construct-style-sheets-polyfill": "3.1.0", "construct-style-sheets-polyfill": "3.1.0",
"date-fns": "2.29.3", "date-fns": "2.29.3",
"lit": "3.3.0", "lit": "3.3.0",

View File

@@ -0,0 +1,40 @@
import {VideoMetadata} from "Frontend/components/Playbar";
import RangeSlider from 'react-range-slider-input';
import 'react-range-slider-input/dist/style.css';
import {useRef} from "react";
import clsx from 'clsx';
type Props = {
videoRef: HTMLVideoElement | null;
videoMetadata: VideoMetadata;
setSliderValue: Function;
className?: string;
};
export default function ClipRangeSlider({videoRef, videoMetadata, setSliderValue, className}: Props) {
const previousRangeSliderInput = useRef<[number, number]>([0, 0]);
const handleRangeSliderInput = (val: [number, number]) => {
if (!videoRef) return;
if (previousRangeSliderInput.current[0] != val[0]) {
videoRef.currentTime = val[0];
setSliderValue(val[0]);
} else if (previousRangeSliderInput.current[1] != val[1]) {
videoRef.currentTime = val[1];
setSliderValue(val[1]);
}
previousRangeSliderInput.current = val;
};
return (
<RangeSlider
min={0}
max={videoMetadata.endPoint}
step={0.1}
onInput={handleRangeSliderInput}
className={clsx(className)}
/>
)
}

View File

@@ -0,0 +1,51 @@
import {VideoMetadata} from "Frontend/components/Playbar";
import {useEffect, useState} from "react";
import clsx from 'clsx';
type Props = {
videoRef: HTMLVideoElement | null;
videoMetadata: VideoMetadata;
sliderValue: number;
setSliderValue: Function;
className?: string;
};
export default function PlaybackSlider({videoRef,
videoMetadata,
sliderValue,
setSliderValue,
className}: Props) {
const updateVideo = (e: React.ChangeEvent<HTMLInputElement>) => {
if (!videoRef) return;
videoRef.currentTime = e.target.valueAsNumber;
setSliderValue(e.target.valueAsNumber);
}
// update slider
useEffect(() => {
if (!videoRef) return;
const updateSlider = () => {
setSliderValue(videoRef.currentTime);
};
videoRef.addEventListener("timeupdate", updateSlider);
return () => {
videoRef.removeEventListener("timeupdate", updateSlider);
};
}, [videoRef]);
return (
<input
type={"range"}
min={0}
max={videoMetadata.endPoint}
value={sliderValue}
onChange={updateVideo}
step={0.1}
className={clsx(className)}
/>
)
}

View File

@@ -1,42 +1,17 @@
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import RangeSlider from 'react-range-slider-input'; import { VideoMetadata } from "Frontend/components/Playbar";
import 'react-range-slider-input/dist/style.css'; import Playbar from "./../../components/Playbar"
import Playbar, {VideoMetadata } from "./../../components/Playbar" import PlaybackSlider from "./../../components/PlaybackSlider"
import ClipRangeSlider from "./../../components/ClipRangeSlider"
export default function VideoId() { export default function VideoId() {
const { id } = useParams(); const { id } = useParams();
const videoRef = useRef<HTMLVideoElement | null>(null); const videoRef = useRef<HTMLVideoElement | null>(null);
const videoUrl = "api/v1/download/input/" + id; const videoUrl = `api/v1/download/input/${id}`
const [metadata, setMetadata] = useState<VideoMetadata | null>(null); const [metadata, setMetadata] = useState<VideoMetadata | null>(null);
const [sliderValue, setSliderValue] = useState(0); const [sliderValue, setSliderValue] = useState(0);
const previousRangeSliderInput = useRef<[number, number]>([0, 0]);
const handleRangeSliderInput = (val: [number, number]) => {
if (!videoRef.current) {
return;
}
if (previousRangeSliderInput.current[0] != val[0]) {
videoRef.current.currentTime = val[0];
setSliderValue(val[0]);
} else if (previousRangeSliderInput.current[1] != val[1]) {
videoRef.current.currentTime = val[1];
setSliderValue(val[1]);
}
previousRangeSliderInput.current = val;
};
const updateVideoTag = (e: React.ChangeEvent<HTMLInputElement>) => {
if (!videoRef.current) {
return;
}
setSliderValue(parseFloat(e.target.value));
videoRef.current.currentTime = parseFloat(e.target.value);
};
useEffect(() => { useEffect(() => {
fetch(`api/v1/metadata/original/${id}`) fetch(`api/v1/metadata/original/${id}`)
@@ -46,30 +21,13 @@ export default function VideoId() {
}) })
.then(setMetadata) .then(setMetadata)
.catch((err) => console.log(err.message)); .catch((err) => console.log(err.message));
}, []); }, [id]);
// update slider
useEffect(() => {
const video = videoRef.current;
if (!video) return;
const updateSlider = () => {
setSliderValue(video.currentTime);
};
video.addEventListener("timeupdate", updateSlider);
return () => {
video.removeEventListener("timeupdate", updateSlider);
};
}, [videoRef]);
return ( return (
<div className={"flex flex-col gap-2 max-w-3xl m-auto align-middle "}> <div className={"flex flex-col gap-2 max-w-3xl m-auto align-middle"}>
<video <video
ref={videoRef} ref={videoRef}
preload="metadata" width={"600"}
width="600"
className={"w-full max-w-3xl rounded-lg shadow-lg border border-gray-300 bg-black m-auto"}> className={"w-full max-w-3xl rounded-lg shadow-lg border border-gray-300 bg-black m-auto"}>
<source src={videoUrl} type="video/mp4" /> <source src={videoUrl} type="video/mp4" />
<source src={videoUrl} type="video/webm" /> <source src={videoUrl} type="video/webm" />
@@ -81,21 +39,20 @@ export default function VideoId() {
{metadata && {metadata &&
<div> <div>
<Playbar video={videoRef.current} videoMetadata={metadata}/> <Playbar video={videoRef.current} videoMetadata={metadata}/>
<input <PlaybackSlider
videoRef={videoRef.current}
videoMetadata={metadata}
sliderValue={sliderValue}
setSliderValue={setSliderValue}
className={"w-full"} className={"w-full"}
type="range"
min={0}
max={metadata.endPoint}
value={sliderValue}
onChange={updateVideoTag}
step={0.1}
/> />
<RangeSlider <ClipRangeSlider
min={0} videoRef={videoRef.current}
max={metadata.endPoint} videoMetadata={metadata}
step={0.1} setSliderValue={setSliderValue}
onInput={handleRangeSliderInput}/> className={"w-full"}
/>
</div> </div>
} }
</div> </div>