caption clear + popout math

Co-authored-by: Jip Frijlink <JipFr@users.noreply.github.com>
This commit is contained in:
Jelle van Snik 2023-02-09 20:42:51 +01:00
parent d89bbaef97
commit 056f837dcb
5 changed files with 62 additions and 45 deletions

View File

@ -13,7 +13,6 @@ function makeCaptionId(caption: MWCaption, isLinked: boolean): string {
return isLinked ? `linked-${caption.langIso}` : `external-${caption.langIso}`; return isLinked ? `linked-${caption.langIso}` : `external-${caption.langIso}`;
} }
// TODO add option to clear captions
export function CaptionSelectionPopout() { export function CaptionSelectionPopout() {
const descriptor = useVideoPlayerDescriptor(); const descriptor = useVideoPlayerDescriptor();
const meta = useMeta(descriptor); const meta = useMeta(descriptor);
@ -42,10 +41,23 @@ export function CaptionSelectionPopout() {
<div>Captions</div> <div>Captions</div>
</PopoutSection> </PopoutSection>
<div className="relative overflow-y-auto"> <div className="relative overflow-y-auto">
<PopoutSection>
<PopoutListEntry
active={!currentCaption}
onClick={() => {
controls.clearCaption();
controls.closePopout();
}}
>
No captions
</PopoutListEntry>
</PopoutSection>
<p className="sticky top-0 z-10 flex items-center space-x-1 bg-ash-200 px-5 py-3 text-sm font-bold uppercase"> <p className="sticky top-0 z-10 flex items-center space-x-1 bg-ash-200 px-5 py-3 text-sm font-bold uppercase">
<Icon className="text-base" icon={Icons.LINK} /> <Icon className="text-base" icon={Icons.LINK} />
<span>Linked captions</span> <span>Linked captions</span>
</p> </p>
<PopoutSection className="pt-0"> <PopoutSection className="pt-0">
<div> <div>
{linkedCaptions.map((link) => ( {linkedCaptions.map((link) => (

View File

@ -18,8 +18,10 @@ export function PopoutAnchor(props: Props) {
if (state.interface.popout !== props.for) return; if (state.interface.popout !== props.for) return;
let handle = -1; let cancelled = false;
function render() { function render() {
if (cancelled) return;
if (ref.current) { if (ref.current) {
const current = JSON.stringify(state.interface.popoutBounds); const current = JSON.stringify(state.interface.popoutBounds);
const newer = ref.current.getBoundingClientRect(); const newer = ref.current.getBoundingClientRect();
@ -28,12 +30,12 @@ export function PopoutAnchor(props: Props) {
updateInterface(descriptor, state); updateInterface(descriptor, state);
} }
} }
handle = window.requestAnimationFrame(render); window.requestAnimationFrame(render);
} }
handle = window.requestAnimationFrame(render); window.requestAnimationFrame(render);
return () => { return () => {
window.cancelAnimationFrame(handle); cancelled = true;
}; };
}, [descriptor, props]); }, [descriptor, props]);

View File

@ -4,8 +4,11 @@ import { EpisodeSelectionPopout } from "@/video/components/popouts/EpisodeSelect
import { CaptionSelectionPopout } from "@/video/components/popouts/CaptionSelectionPopout"; import { CaptionSelectionPopout } from "@/video/components/popouts/CaptionSelectionPopout";
import { useVideoPlayerDescriptor } from "@/video/state/hooks"; import { useVideoPlayerDescriptor } from "@/video/state/hooks";
import { useControls } from "@/video/state/logic/controls"; import { useControls } from "@/video/state/logic/controls";
import { useInterface } from "@/video/state/logic/interface"; import {
import { useCallback, useEffect, useMemo, useRef, useState } from "react"; useInterface,
VideoInterfaceEvent,
} from "@/video/state/logic/interface";
import { useCallback, useEffect, useRef, useState } from "react";
import "./Popouts.css"; import "./Popouts.css";
@ -22,9 +25,44 @@ function ShowPopout(props: { popoutId: string | null }) {
return null; return null;
} }
// TODO bug: coords are sometimes completely broken function PopoutContainer(props: { videoInterface: VideoInterfaceEvent }) {
export function PopoutProviderAction() {
const ref = useRef<HTMLDivElement>(null); const ref = useRef<HTMLDivElement>(null);
const [right, setRight] = useState<number>(0);
const [bottom, setBottom] = useState<number>(0);
const [width, setWidth] = useState<number>(0);
const calculateAndSetCoords = useCallback((rect: DOMRect, w: number) => {
const buttonCenter = rect.left + rect.width / 2;
setBottom(rect ? rect.height + 30 : 30);
setRight(Math.max(window.innerWidth - buttonCenter - w / 2, 30));
}, []);
useEffect(() => {
if (!props.videoInterface.popoutBounds) return;
calculateAndSetCoords(props.videoInterface.popoutBounds, width);
}, [props.videoInterface.popoutBounds, calculateAndSetCoords, width]);
useEffect(() => {
const rect = ref.current?.getBoundingClientRect();
setWidth(rect?.width ?? 0);
}, []);
return (
<div
ref={ref}
className="absolute z-10 grid h-[500px] w-80 grid-rows-[auto,minmax(0,1fr)] overflow-hidden rounded-lg bg-ash-200"
style={{
right: `${right}px`,
bottom: `${bottom}px`,
}}
>
<ShowPopout popoutId={props.videoInterface.popout} />
</div>
);
}
export function PopoutProviderAction() {
const descriptor = useVideoPlayerDescriptor(); const descriptor = useVideoPlayerDescriptor();
const videoInterface = useInterface(descriptor); const videoInterface = useInterface(descriptor);
const controls = useControls(descriptor); const controls = useControls(descriptor);
@ -34,26 +72,6 @@ export function PopoutProviderAction() {
controls.closePopout(); controls.closePopout();
}, [controls]); }, [controls]);
const distanceFromRight = useMemo(() => {
if (!videoInterface.popoutBounds) return 30;
const buttonCenter =
videoInterface.popoutBounds.left + videoInterface.popoutBounds.width / 2;
return Math.max(
window.innerWidth -
buttonCenter -
(ref.current?.getBoundingClientRect().width ?? 0) / 2,
30
);
}, [videoInterface.popoutBounds]);
const distanceFromBottom = useMemo(() => {
return videoInterface.popoutBounds
? videoInterface.popoutBounds.height + 30
: 30;
}, [videoInterface.popoutBounds]);
return ( return (
<Transition <Transition
show={!!videoInterface.popout} show={!!videoInterface.popout}
@ -62,16 +80,7 @@ export function PopoutProviderAction() {
> >
<div className="popout-wrapper pointer-events-auto absolute inset-0"> <div className="popout-wrapper pointer-events-auto absolute inset-0">
<div onClick={handleClick} className="absolute inset-0" /> <div onClick={handleClick} className="absolute inset-0" />
<div <PopoutContainer videoInterface={videoInterface} />
ref={ref}
className="absolute z-10 grid h-[500px] w-80 grid-rows-[auto,minmax(0,1fr)] overflow-hidden rounded-lg bg-ash-200"
style={{
right: `${distanceFromRight}px`,
bottom: `${distanceFromBottom}px`,
}}
>
<ShowPopout popoutId={videoInterface.popout} />
</div>
</div> </div>
</Transition> </Transition>
); );

View File

@ -45,11 +45,6 @@ export function ScrollToActive(props: ScrollToActiveProps) {
wrapper?.querySelector(".active"); wrapper?.querySelector(".active");
if (wrapper && active) { if (wrapper && active) {
active.scrollIntoView({
block: "nearest",
inline: "nearest",
});
let activeYPositionCentered = 0; let activeYPositionCentered = 0;
const setActiveYPositionCentered = () => { const setActiveYPositionCentered = () => {
activeYPositionCentered = activeYPositionCentered =

View File

@ -74,7 +74,6 @@ export function useControls(
}, },
closePopout() { closePopout() {
state.interface.popout = null; state.interface.popout = null;
state.interface.popoutBounds = null;
updateInterface(descriptor, state); updateInterface(descriptor, state);
}, },
setFocused(focused) { setFocused(focused) {