scrubber is now an HOC

This commit is contained in:
wil committed 2024-03-06 13:22:00 -05:00
1 parent cda1302466
commit bfeb6d391f
4 files changed
+121 -96

No files matched your search

@@ -1,101 +1,36 @@
import React, {
useRef,
useState,
PointerEvent
} from 'react';
import React from 'react';
import {
faGripDots
} from "@fortawesome/pro-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { QuickTrimData } from './types';
import {withScrubbing, withScrubbingPropsI} from './withScrubbing';
export default function TrimScrubber({
trimStart,
trimDuration,
duration,
width,
boundingWidth,
onChange
}:{
interface TrimScrubberPropsI extends withScrubbingPropsI {
trimStart:number;
trimDuration: number;
duration: number;
boundingWidth: number;
width:number;
onChange:(val:QuickTrimData)=>void
}){
const [{
left,
leftOffset,
pointerStart
}, setState] = useState<{
left: number,
leftOffset:number
pointerStart:number
}>({
left: trimStart/duration*boundingWidth,
leftOffset: trimStart/duration*boundingWidth,
pointerStart: -1
});
const ref = useRef<HTMLDivElement | null>(null);
if(ref.current){
ref.current.style.cursor = pointerStart >= 0
? 'grabbing' : 'grab';
}
function handleScrubStart(e: PointerEvent<HTMLDivElement>){
e.persist();
// console.log(`start: ${pointerStart}` );
setState((curr)=>({
...curr,
pointerStart: e.clientX
}));
}
function handleScrubEnd(e: PointerEvent<HTMLDivElement>){
e.persist();
// console.log(`end` );
setState((curr)=>({
...curr,
pointerStart: -1,
leftOffset: left,
}));
console.log(`${duration} * ${left} / ${boundingWidth} = ${duration * left / boundingWidth}`)
onChange(({
trimStartSeconds: duration * left / boundingWidth,
trimEndSeconds: duration * left / boundingWidth + trimDuration,
}))
}
function handleScrubMove(e: PointerEvent<HTMLDivElement>){
e.persist();
if(pointerStart >= 0 && pointerStart!==null && ref.current){
// console.log(`${e.clientX} - ${pointerStart} = ${e.clientX-pointerStart}`)
let newLeft = leftOffset + e.clientX - pointerStart;
// console.log(`${newLeft} + ${boundingWidth * widthPercent/100} > ${boundingWidth}`)
if (newLeft + width > boundingWidth) {
newLeft = boundingWidth - width;
}else if(newLeft < 0) {
newLeft = 0;
}
setState((curr)=>({
...curr,
left: newLeft
}));
}
}
return(
<div className="trim-scrubber"
ref={ref}
style={{
width: width + "px",
left: left,
}}
onPointerDown={handleScrubStart}
onPointerUp={handleScrubEnd}
onPointerLeave={handleScrubEnd}
onPointerMove={handleScrubMove}
>
<FontAwesomeIcon icon={faGripDots} />
</div>
);
}
export const TrimScrubber = (props:TrimScrubberPropsI)=>{
const TrimScrubberWithScrubbing =
withScrubbing<TrimScrubberPropsI>(() => {
return(
<div className="trim-scrubber">
<FontAwesomeIcon icon={faGripDots} />
</div>
);
});
return (
<TrimScrubberWithScrubbing
styleOverride={{
top: '-1rem',
}}
{...props}
/>
)
}
@@ -18,7 +18,7 @@ import { VideoFakeyouProps } from "../VideoFakeyou/VideoFakeyou";
import { QuickTrimData, TrimStates, PlaybarStates } from "./types";
import { formatSecondsToHHMMSSCS } from "./helpers";
import TrimScrbber from "./TrimScrubber";
import {TrimScrubber} from "./TrimScrubber";
import './styles.scss'
interface VideoQuickTrimProps extends VideoFakeyouProps {
@@ -225,10 +225,10 @@ export default memo(function VideoQuickTrim({
{trimScrubberWidth > 0 &&
playbarWidth > 0 &&
maxDuration > 0 &&
<TrimScrbber
<TrimScrubber
key={trimReset.toString()}
boundingWidth={playbarWidth}
width={trimScrubberWidth}
scrubberWidth={trimScrubberWidth}
trimStart={trimStart}
trimDuration={trimDuration}
duration={maxDuration}
@@ -82,15 +82,12 @@ $playbar-height: 2rem;
border-radius: 0 0 0.5rem 0.5rem;
}
.trim-scrubber{
position: absolute;
top: -1rem;
height: 3rem;
background-color: #e66462;
opacity: 0.8;
outline: 0.125rem $white solid;
border-top: 1rem $white solid;
border-radius: 0.25rem 0.25rem 0.125rem 0.125rem;
cursor: grab;
display: flex;
justify-content: center;
@@ -0,0 +1,93 @@
import React,{
useRef,
useState,
PointerEvent,
} from 'react';
export interface withScrubbingPropsI {
boundingWidth: number;
scrubberWidth: number;
styleOverride?: {[key: string]: string|number };
onScrubEnds?: ()=>number; //return scrubber location as %
onScrubChanges?: ()=>number; //return scrubber location as %
}
type withSrcubbingStates = {
currLeftOffset: number,
prevLeftOffset: number;
pointerStartPos: number;
}
export const withScrubbing = <P extends withScrubbingPropsI>(Component: React.ComponentType<P>) => ({
boundingWidth,
scrubberWidth,
styleOverride = {},
onScrubEnds,
onScrubChanges,
...rest
}: withScrubbingPropsI) => {
// return (props: Omit<P, keyof withScrubbingPropsI>)=>{
const ref = useRef<HTMLDivElement | null>(null)
const [{
currLeftOffset, prevLeftOffset, pointerStartPos
}, setStates] = useState<withSrcubbingStates>({
currLeftOffset: 0, // in pixels
prevLeftOffset: 0, //in pixels
pointerStartPos: -1 // negative denotes pointer not engaged
});
function handleScrubStart(e: PointerEvent<HTMLDivElement>){
e.persist();
console.log(`start: ${pointerStartPos} -> ${e.clientX}` );
setStates((curr)=>({
...curr,
pointerStartPos: e.clientX
}));
}
function handleScrubEnd(e: PointerEvent<HTMLDivElement>){
e.persist();
console.log(`end` );
setStates((curr)=>({
...curr,
pointerStartPos: -1,
prevLeftOffset: currLeftOffset,
}));
}
function handleScrubMove(e: PointerEvent<HTMLDivElement>){
e.persist();
if(pointerStartPos >= 0 && pointerStartPos!==null){
let newLeftOffset = prevLeftOffset + e.clientX - pointerStartPos;
if (newLeftOffset + scrubberWidth > boundingWidth) {
newLeftOffset = boundingWidth - scrubberWidth;
}else if(newLeftOffset < 0) {
newLeftOffset = 0;
}
setStates((curr)=>({
...curr,
currLeftOffset: newLeftOffset
}));
}
}
return(
<div
ref={ref}
className="scrubber-wrapper"
style={{
position: 'absolute',
top:0,
width: scrubberWidth + 'px',
left: currLeftOffset,
cursor: pointerStartPos >=0 ? 'grabbing': 'grab',
...styleOverride
}}
onPointerDown={handleScrubStart}
onPointerUp={handleScrubEnd}
onPointerLeave={handleScrubEnd}
onPointerMove={handleScrubMove}
>
<Component
{...rest as P}
/>
</div>
);
// }
};