11 changed files with 172 additions and 32 deletions
@ -0,0 +1,46 @@
@@ -0,0 +1,46 @@
|
||||
import { useVideoPlayerState } from "../VideoContext"; |
||||
|
||||
function durationExceedsHour(secs: number): boolean { |
||||
return secs > 60 * 60; |
||||
} |
||||
|
||||
function formatSeconds(secs: number, showHours = false): string { |
||||
if (Number.isNaN(secs)) { |
||||
if (showHours) return "0:00:00"; |
||||
return "0:00"; |
||||
} |
||||
|
||||
let time = secs; |
||||
const seconds = Math.floor(time % 60); |
||||
|
||||
time /= 60; |
||||
const minutes = Math.floor(time % 60); |
||||
|
||||
time /= 60; |
||||
const hours = Math.floor(time); |
||||
|
||||
const paddedSecs = seconds.toString().padStart(2, "0"); |
||||
const paddedMins = minutes.toString().padStart(2, "0"); |
||||
|
||||
if (!showHours) return [minutes, paddedSecs].join(":"); |
||||
return [hours, paddedMins, paddedSecs].join(":"); |
||||
} |
||||
|
||||
interface Props { |
||||
className?: string; |
||||
} |
||||
|
||||
export function SkipTime(props: Props) { |
||||
const { videoState } = useVideoPlayerState(); |
||||
const hasHours = durationExceedsHour(videoState.duration); |
||||
const time = formatSeconds(videoState.time, hasHours); |
||||
const duration = formatSeconds(videoState.duration, hasHours); |
||||
|
||||
return ( |
||||
<div className={props.className}> |
||||
<p className="select-none text-white"> |
||||
{time} / {duration} |
||||
</p> |
||||
</div> |
||||
); |
||||
} |
@ -0,0 +1,54 @@
@@ -0,0 +1,54 @@
|
||||
import { useEffect, useRef } from "react"; |
||||
import { useVideoPlayerState } from "../VideoContext"; |
||||
|
||||
interface Props { |
||||
children?: React.ReactNode; |
||||
id?: string; |
||||
className?: string; |
||||
} |
||||
|
||||
export function VideoPopout(props: Props) { |
||||
const { videoState } = useVideoPlayerState(); |
||||
const popoutRef = useRef<HTMLDivElement>(null); |
||||
const isOpen = videoState.popout === props.id; |
||||
|
||||
useEffect(() => { |
||||
if (!isOpen) return; |
||||
const popoutEl = popoutRef.current; |
||||
let hasTriggered = false; |
||||
function windowClick() { |
||||
setTimeout(() => { |
||||
if (hasTriggered) return; |
||||
videoState.closePopout(); |
||||
hasTriggered = false; |
||||
}, 10); |
||||
} |
||||
function popoutClick() { |
||||
hasTriggered = true; |
||||
setTimeout(() => { |
||||
hasTriggered = false; |
||||
}, 100); |
||||
} |
||||
window.addEventListener("click", windowClick); |
||||
popoutEl?.addEventListener("click", popoutClick); |
||||
return () => { |
||||
window.removeEventListener("click", windowClick); |
||||
popoutEl?.removeEventListener("click", popoutClick); |
||||
}; |
||||
}, [isOpen, videoState]); |
||||
|
||||
if (!isOpen) return null; |
||||
|
||||
return ( |
||||
<div className="is-popout absolute inset-x-0 h-0"> |
||||
<div className="absolute bottom-10 right-0 h-96 w-72 rounded-lg bg-denim-400"> |
||||
<div |
||||
ref={popoutRef} |
||||
className={["h-full w-full", props.className].join(" ")} |
||||
> |
||||
{props.children} |
||||
</div> |
||||
</div> |
||||
</div> |
||||
); |
||||
} |
Loading…
Reference in new issue