mirror of
https://github.com/movie-web/movie-web.git
synced 2025-01-13 22:19:10 +01:00
buffer more visible, fix volume mute, rewrote entire overlay router system
Co-authored-by: Jip Frijlink <JipFr@users.noreply.github.com>
This commit is contained in:
parent
4a2a8e89cc
commit
7b3452c535
@ -4,7 +4,7 @@ import {
|
||||
} from "@headlessui/react";
|
||||
import { Fragment, ReactNode } from "react";
|
||||
|
||||
type TransitionAnimations =
|
||||
export type TransitionAnimations =
|
||||
| "slide-down"
|
||||
| "slide-full-left"
|
||||
| "slide-full-right"
|
||||
|
@ -1,8 +1,4 @@
|
||||
import { ReactNode, useEffect, useRef } from "react";
|
||||
|
||||
export function createOverlayAnchorEvent(id: string): string {
|
||||
return `__overlay::anchor::${id}`;
|
||||
}
|
||||
import { ReactNode } from "react";
|
||||
|
||||
interface Props {
|
||||
id: string;
|
||||
@ -10,38 +6,5 @@ interface Props {
|
||||
}
|
||||
|
||||
export function OverlayAnchor(props: Props) {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const old = useRef<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!ref.current) return;
|
||||
|
||||
let cancelled = false;
|
||||
function render() {
|
||||
if (cancelled) return;
|
||||
|
||||
if (ref.current) {
|
||||
const current = old.current;
|
||||
const newer = ref.current.getBoundingClientRect();
|
||||
const newerStr = JSON.stringify(newer);
|
||||
if (current !== newerStr) {
|
||||
old.current = newerStr;
|
||||
const evtStr = createOverlayAnchorEvent(props.id);
|
||||
(window as any)[evtStr] = newer;
|
||||
const evObj = new CustomEvent(createOverlayAnchorEvent(props.id), {
|
||||
detail: newer,
|
||||
});
|
||||
document.dispatchEvent(evObj);
|
||||
}
|
||||
}
|
||||
window.requestAnimationFrame(render);
|
||||
}
|
||||
|
||||
window.requestAnimationFrame(render);
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [props]);
|
||||
|
||||
return <div ref={ref}>{props.children}</div>;
|
||||
return <div id={`__overlayRouter::${props.id}`}>{props.children}</div>;
|
||||
}
|
||||
|
@ -1,29 +1,44 @@
|
||||
import classNames from "classnames";
|
||||
import { ReactNode } from "react";
|
||||
import { ReactNode, useEffect, useMemo } from "react";
|
||||
|
||||
import { Transition } from "@/components/Transition";
|
||||
import { Transition, TransitionAnimations } from "@/components/Transition";
|
||||
import { useIsMobile } from "@/hooks/useIsMobile";
|
||||
import { useInternalOverlayRouter } from "@/hooks/useOverlayRouter";
|
||||
import { useOverlayStore } from "@/stores/overlay/store";
|
||||
|
||||
interface Props {
|
||||
id: string;
|
||||
path: string;
|
||||
children?: ReactNode;
|
||||
className?: string;
|
||||
height?: number;
|
||||
width?: number;
|
||||
height: number;
|
||||
width: number;
|
||||
}
|
||||
|
||||
export function OverlayPage(props: Props) {
|
||||
const router = useInternalOverlayRouter(props.id);
|
||||
const backwards = router.showBackwardsTransition(props.path);
|
||||
const show = router.isCurrentPage(props.path);
|
||||
|
||||
const registerRoute = useOverlayStore((s) => s.registerRoute);
|
||||
const path = useMemo(() => router.makePath(props.path), [props.path, router]);
|
||||
const { isMobile } = useIsMobile();
|
||||
|
||||
useEffect(() => {
|
||||
registerRoute({
|
||||
id: path,
|
||||
width: props.width,
|
||||
height: props.height,
|
||||
});
|
||||
}, [props.height, props.width, path, registerRoute]);
|
||||
|
||||
const width = !isMobile ? `${props.width}px` : "100%";
|
||||
let animation: TransitionAnimations = "none";
|
||||
if (backwards === "yes" || backwards === "no")
|
||||
animation = backwards === "yes" ? "slide-full-left" : "slide-full-right";
|
||||
|
||||
return (
|
||||
<Transition
|
||||
animation={backwards ? "slide-full-left" : "slide-full-right"}
|
||||
animation={animation}
|
||||
className="absolute inset-0"
|
||||
durationClass="duration-[400ms]"
|
||||
show={show}
|
||||
@ -33,7 +48,6 @@ export function OverlayPage(props: Props) {
|
||||
props.className,
|
||||
"grid grid-rows-[auto,minmax(0,1fr)]",
|
||||
])}
|
||||
data-floating-page={show ? "true" : undefined}
|
||||
style={{
|
||||
height: props.height ? `${props.height}px` : undefined,
|
||||
maxHeight: "70vh",
|
||||
|
@ -1,18 +1,90 @@
|
||||
import { ReactNode } from "react";
|
||||
import { a, easings, useSpring } from "@react-spring/web";
|
||||
import { ReactNode, useEffect, useMemo, useRef } from "react";
|
||||
|
||||
import { OverlayAnchorPosition } from "@/components/overlays/positions/OverlayAnchorPosition";
|
||||
import { OverlayMobilePosition } from "@/components/overlays/positions/OverlayMobilePosition";
|
||||
import { useIsMobile } from "@/hooks/useIsMobile";
|
||||
import { useInternalOverlayRouter } from "@/hooks/useOverlayRouter";
|
||||
import { useOverlayStore } from "@/stores/overlay/store";
|
||||
|
||||
interface OverlayRouterProps {
|
||||
children?: ReactNode;
|
||||
id: string;
|
||||
}
|
||||
|
||||
function RouterBase(props: { id: string; children: ReactNode }) {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const { isMobile } = useIsMobile();
|
||||
|
||||
const routes = useOverlayStore((s) => s.routes);
|
||||
const router = useInternalOverlayRouter(props.id);
|
||||
const routeMeta = useMemo(
|
||||
() => routes[router.currentRoute ?? ""],
|
||||
[routes, router]
|
||||
);
|
||||
|
||||
const [dimensions, api] = useSpring(
|
||||
() => ({
|
||||
from: {
|
||||
height: `${routeMeta?.height ?? 0}px`,
|
||||
width: isMobile ? "100%" : `${routeMeta?.width ?? 0}px`,
|
||||
},
|
||||
config: {
|
||||
easing: easings.linear,
|
||||
},
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
||||
const currentState = useRef<null | string>(null);
|
||||
useEffect(() => {
|
||||
const data = {
|
||||
height: routeMeta?.height,
|
||||
width: routeMeta?.width,
|
||||
isMobile,
|
||||
};
|
||||
const dataStr = JSON.stringify(data);
|
||||
if (dataStr !== currentState.current) {
|
||||
const oldData = currentState.current
|
||||
? JSON.parse(currentState.current)
|
||||
: null;
|
||||
currentState.current = dataStr;
|
||||
if (data.isMobile) {
|
||||
api.set({
|
||||
width: "100%",
|
||||
});
|
||||
api({
|
||||
height: `${routeMeta?.height ?? 0}px`,
|
||||
});
|
||||
} else if (oldData?.height === undefined && data.height !== undefined) {
|
||||
api.set({
|
||||
height: `${routeMeta?.height ?? 0}px`,
|
||||
width: `${routeMeta?.width ?? 0}px`,
|
||||
});
|
||||
} else {
|
||||
api({
|
||||
height: `${routeMeta?.height ?? 0}px`,
|
||||
width: `${routeMeta?.width ?? 0}px`,
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [routeMeta?.height, routeMeta?.width, isMobile, api]);
|
||||
|
||||
return (
|
||||
<a.div
|
||||
ref={ref}
|
||||
style={dimensions}
|
||||
className="relative flex items-center justify-center overflow-hidden bg-red-500"
|
||||
>
|
||||
{props.children}
|
||||
</a.div>
|
||||
);
|
||||
}
|
||||
|
||||
export function OverlayRouter(props: OverlayRouterProps) {
|
||||
const { isMobile } = useIsMobile();
|
||||
const content = props.children;
|
||||
const content = <RouterBase id={props.id}>{props.children}</RouterBase>;
|
||||
|
||||
if (isMobile) return <OverlayMobilePosition>{content}</OverlayMobilePosition>;
|
||||
return <OverlayAnchorPosition id={props.id}>{content}</OverlayAnchorPosition>;
|
||||
return <OverlayAnchorPosition>{content}</OverlayAnchorPosition>;
|
||||
}
|
||||
|
@ -1,78 +1,19 @@
|
||||
import classNames from "classnames";
|
||||
import { ReactNode, useCallback, useEffect, useRef, useState } from "react";
|
||||
|
||||
import { createOverlayAnchorEvent } from "@/components/overlays/OverlayAnchor";
|
||||
import { ReactNode } from "react";
|
||||
|
||||
interface AnchorPositionProps {
|
||||
children?: ReactNode;
|
||||
id: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function OverlayAnchorPosition(props: AnchorPositionProps) {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const [left, setLeft] = useState<number>(0);
|
||||
const [top, setTop] = useState<number>(0);
|
||||
const [cardRect, setCardRect] = useState<DOMRect | null>(null);
|
||||
const [anchorRect, setAnchorRect] = useState<DOMRect | null>(null);
|
||||
|
||||
const calculateAndSetCoords = useCallback(
|
||||
(anchor: DOMRect, card: DOMRect) => {
|
||||
const buttonCenter = anchor.left + anchor.width / 2;
|
||||
const bottomReal = window.innerHeight - anchor.bottom;
|
||||
|
||||
setTop(
|
||||
window.innerHeight - bottomReal - anchor.height - card.height - 30
|
||||
);
|
||||
setLeft(
|
||||
Math.min(
|
||||
buttonCenter - card.width / 2,
|
||||
window.innerWidth - card.width - 30
|
||||
)
|
||||
);
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!anchorRect || !cardRect) return;
|
||||
calculateAndSetCoords(anchorRect, cardRect);
|
||||
}, [anchorRect, calculateAndSetCoords, cardRect]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!ref.current) return;
|
||||
function checkBox() {
|
||||
const divRect = ref.current?.getBoundingClientRect();
|
||||
setCardRect(divRect ?? null);
|
||||
}
|
||||
checkBox();
|
||||
const observer = new ResizeObserver(checkBox);
|
||||
observer.observe(ref.current);
|
||||
return () => {
|
||||
observer.disconnect();
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const evtStr = createOverlayAnchorEvent(props.id);
|
||||
if ((window as any)[evtStr]) setAnchorRect((window as any)[evtStr]);
|
||||
function listen(ev: CustomEvent<DOMRect>) {
|
||||
setAnchorRect(ev.detail);
|
||||
}
|
||||
document.addEventListener(evtStr, listen as any);
|
||||
return () => {
|
||||
document.removeEventListener(evtStr, listen as any);
|
||||
};
|
||||
}, [props.id]);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
style={{
|
||||
transform: `translateX(${left}px) translateY(${top}px)`,
|
||||
transform: `translateX(0px) translateY(0px)`,
|
||||
}}
|
||||
className={classNames([
|
||||
"pointer-events-auto z-10 inline-block origin-top-left touch-none overflow-hidden",
|
||||
"pointer-events-auto z-10 inline-block origin-top-left touch-none",
|
||||
props.className,
|
||||
])}
|
||||
>
|
||||
|
@ -10,7 +10,7 @@ export function OverlayMobilePosition(props: MobilePositionProps) {
|
||||
return (
|
||||
<div
|
||||
className={classNames([
|
||||
"pointer-events-auto z-10 inline-block origin-top-left touch-none overflow-hidden",
|
||||
"pointer-events-auto z-10 block origin-top-left touch-none overflow-hidden",
|
||||
props.className,
|
||||
])}
|
||||
>
|
||||
|
@ -46,7 +46,7 @@ export function ProgressBar() {
|
||||
>
|
||||
{/* Pre-loaded content bar */}
|
||||
<div
|
||||
className="absolute top-0 left-0 h-full rounded-full bg-video-progress-preloaded bg-opacity-25 flex justify-end items-center"
|
||||
className="absolute top-0 left-0 h-full rounded-full bg-video-progress-preloaded bg-opacity-50 flex justify-end items-center"
|
||||
style={{
|
||||
width: `${(buffered / duration) * 100}%`,
|
||||
}}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { useCallback, useRef, useState } from "react";
|
||||
import { useCallback, useRef } from "react";
|
||||
|
||||
import { Icon, Icons } from "@/components/Icon";
|
||||
import {
|
||||
@ -24,6 +24,7 @@ export function Volume(props: Props) {
|
||||
|
||||
const commitVolume = useCallback(
|
||||
(percentage) => {
|
||||
console.log("setting", percentage);
|
||||
setVolume(percentage);
|
||||
},
|
||||
[setVolume]
|
||||
|
@ -9,12 +9,12 @@ export function BottomControls(props: {
|
||||
<Transition
|
||||
animation="fade"
|
||||
show={props.show}
|
||||
className="pointer-events-none flex justify-end pt-32 bg-gradient-to-t from-black to-transparent [margin-bottom:env(safe-area-inset-bottom)] transition-opacity duration-200 absolute bottom-0 w-full"
|
||||
className="pointer-events-none flex justify-end pt-32 bg-gradient-to-t from-black to-transparent transition-opacity duration-200 absolute bottom-0 w-full"
|
||||
/>
|
||||
<Transition
|
||||
animation="slide-up"
|
||||
show={props.show}
|
||||
className="pointer-events-auto px-4 pb-3 absolute bottom-0 w-full"
|
||||
className="pointer-events-auto pl-[calc(2rem+env(safe-area-inset-left))] pr-[calc(2rem+env(safe-area-inset-right))] pb-3 mb-[env(safe-area-inset-bottom)] absolute bottom-0 w-full"
|
||||
>
|
||||
{props.children}
|
||||
</Transition>
|
||||
|
@ -79,11 +79,13 @@ export function Container(props: PlayerProps) {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<BaseContainer>
|
||||
<div className="relative">
|
||||
<VideoContainer />
|
||||
<VideoClickTarget />
|
||||
<HeadUpdater />
|
||||
{props.children}
|
||||
</BaseContainer>
|
||||
<BaseContainer>
|
||||
<VideoClickTarget />
|
||||
<HeadUpdater />
|
||||
{props.children}
|
||||
</BaseContainer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ export function TopControls(props: {
|
||||
<Transition
|
||||
animation="slide-down"
|
||||
show={props.show}
|
||||
className="pointer-events-auto px-4 pt-6 absolute top-0 w-full text-white"
|
||||
className="pointer-events-auto pl-[calc(2rem+env(safe-area-inset-left))] pr-[calc(2rem+env(safe-area-inset-right))] pt-6 absolute top-0 w-full text-white"
|
||||
>
|
||||
{props.children}
|
||||
</Transition>
|
||||
|
@ -26,6 +26,7 @@ export function makeVideoElementDisplayInterface(): DisplayInterface {
|
||||
function setSource() {
|
||||
if (!videoElement || !source) return;
|
||||
videoElement.src = source.url;
|
||||
|
||||
videoElement.addEventListener("play", () => {
|
||||
emit("play", undefined);
|
||||
emit("loading", false);
|
||||
@ -35,7 +36,7 @@ export function makeVideoElementDisplayInterface(): DisplayInterface {
|
||||
videoElement.addEventListener("canplay", () => emit("loading", false));
|
||||
videoElement.addEventListener("waiting", () => emit("loading", true));
|
||||
videoElement.addEventListener("volumechange", () =>
|
||||
emit("volumechange", videoElement?.volume ?? 0)
|
||||
emit("volumechange", videoElement?.muted ? 0 : videoElement?.volume ?? 0)
|
||||
);
|
||||
videoElement.addEventListener("timeupdate", () =>
|
||||
emit("time", videoElement?.currentTime ?? 0)
|
||||
@ -118,9 +119,16 @@ export function makeVideoElementDisplayInterface(): DisplayInterface {
|
||||
// clamp time between 0 and 1
|
||||
let volume = Math.min(v, 1);
|
||||
volume = Math.max(0, volume);
|
||||
videoElement.muted = volume === 0; // Muted attribute is always supported
|
||||
|
||||
// update state
|
||||
if (await canChangeVolume()) videoElement.volume = volume;
|
||||
const isChangeable = await canChangeVolume();
|
||||
if (isChangeable) {
|
||||
videoElement.volume = volume;
|
||||
} else {
|
||||
// For browsers where it can't be changed
|
||||
emit("volumechange", volume === 0 ? 0 : 1);
|
||||
}
|
||||
},
|
||||
toggleFullscreen() {
|
||||
if (isFullscreen) {
|
||||
|
@ -37,7 +37,14 @@ function VideoElement() {
|
||||
}
|
||||
}, [display, videoEl]);
|
||||
|
||||
return <video className="w-full h-screen bg-black" autoPlay ref={videoEl} />;
|
||||
return (
|
||||
<video
|
||||
className="absolute inset-0 w-full h-screen bg-black"
|
||||
autoPlay
|
||||
playsInline
|
||||
ref={videoEl}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function VideoContainer() {
|
||||
|
@ -16,8 +16,13 @@ export function useInternalOverlayRouter(id: string) {
|
||||
const [route, setRoute] = useQueryParam("r");
|
||||
const transition = useOverlayStore((s) => s.transition);
|
||||
const setTransition = useOverlayStore((s) => s.setTransition);
|
||||
const setAnchorPoint = useOverlayStore((s) => s.setAnchorPoint);
|
||||
const routerActive = !!route && route.startsWith(`/${id}`);
|
||||
|
||||
function makePath(path: string) {
|
||||
return joinPath(splitPath(path, id));
|
||||
}
|
||||
|
||||
function navigate(path: string) {
|
||||
const oldRoute = route;
|
||||
const newRoute = joinPath(splitPath(path, id));
|
||||
@ -29,17 +34,17 @@ export function useInternalOverlayRouter(id: string) {
|
||||
}
|
||||
|
||||
function showBackwardsTransition(path: string) {
|
||||
if (!transition) return false;
|
||||
if (!transition) return "none";
|
||||
const current = joinPath(splitPath(path, id));
|
||||
|
||||
if (current === transition.to && transition.from.startsWith(transition.to))
|
||||
return true;
|
||||
return "yes";
|
||||
if (
|
||||
current === transition.from &&
|
||||
transition.to.startsWith(transition.from)
|
||||
)
|
||||
return true;
|
||||
return false;
|
||||
return "yes";
|
||||
return "no";
|
||||
}
|
||||
|
||||
function isCurrentPage(path: string) {
|
||||
@ -56,9 +61,22 @@ export function useInternalOverlayRouter(id: string) {
|
||||
}, [setRoute, setTransition]);
|
||||
|
||||
const open = useCallback(() => {
|
||||
const anchor = document.getElementById(`__overlayRouter::${id}`);
|
||||
if (anchor) {
|
||||
const rect = anchor.getBoundingClientRect();
|
||||
setAnchorPoint({
|
||||
h: rect.height,
|
||||
w: rect.width,
|
||||
x: rect.x,
|
||||
y: rect.y,
|
||||
});
|
||||
} else {
|
||||
setAnchorPoint(null);
|
||||
}
|
||||
|
||||
setTransition(null);
|
||||
setRoute(`/${id}`);
|
||||
}, [id, setRoute, setTransition]);
|
||||
}, [id, setRoute, setTransition, setAnchorPoint]);
|
||||
|
||||
return {
|
||||
showBackwardsTransition,
|
||||
@ -67,6 +85,8 @@ export function useInternalOverlayRouter(id: string) {
|
||||
navigate,
|
||||
close,
|
||||
open,
|
||||
makePath,
|
||||
currentRoute: route,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -19,19 +19,11 @@ export function PlayerView() {
|
||||
const meta = useMemo<PlayerMeta>(
|
||||
() => ({
|
||||
type: "show",
|
||||
title: "House",
|
||||
tmdbId: "1408",
|
||||
releaseYear: 2004,
|
||||
episode: {
|
||||
number: 1,
|
||||
title: "Pilot",
|
||||
tmdbId: "63738",
|
||||
},
|
||||
season: {
|
||||
number: 1,
|
||||
tmdbId: "3674",
|
||||
title: "Season 1",
|
||||
},
|
||||
title: "Normal People",
|
||||
releaseYear: 2020,
|
||||
tmdbId: "89905",
|
||||
episode: { number: 12, tmdbId: "2207576", title: "Episode 12" },
|
||||
season: { number: 1, tmdbId: "125160", title: "Season 1" },
|
||||
}),
|
||||
[]
|
||||
);
|
||||
@ -48,10 +40,20 @@ export function PlayerView() {
|
||||
media={scrapeMedia}
|
||||
onGetStream={(out) => {
|
||||
if (out?.stream.type !== "file") return;
|
||||
const qualities = Object.keys(
|
||||
out.stream.qualities
|
||||
console.log(out.stream.qualities);
|
||||
const qualities = Object.keys(out.stream.qualities).sort(
|
||||
(a, b) => Number(b) - Number(a)
|
||||
) as (keyof typeof out.stream.qualities)[];
|
||||
const file = out.stream.qualities[qualities[0]];
|
||||
|
||||
let file;
|
||||
for (const quality of qualities) {
|
||||
if (out.stream.qualities[quality]?.url) {
|
||||
console.log(quality);
|
||||
file = out.stream.qualities[quality];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!file) return;
|
||||
|
||||
playMedia({
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { useEffect, useRef } from "react";
|
||||
|
||||
import { OverlayAnchor } from "@/components/overlays/OverlayAnchor";
|
||||
import { Overlay, OverlayDisplay } from "@/components/overlays/OverlayDisplay";
|
||||
import { OverlayPage } from "@/components/overlays/OverlayPage";
|
||||
@ -20,7 +22,7 @@ export default function TestView() {
|
||||
Open
|
||||
</button>
|
||||
<OverlayAnchor id={router.id}>
|
||||
<div className="h-20 w-20 mt-64 bg-white" />
|
||||
<div className="h-20 w-20 hover:w-24 mt-[50rem] bg-white" />
|
||||
</OverlayAnchor>
|
||||
<Overlay id={router.id}>
|
||||
<OverlayRouter id={router.id}>
|
||||
@ -45,7 +47,7 @@ export default function TestView() {
|
||||
</button>
|
||||
</div>
|
||||
</OverlayPage>
|
||||
<OverlayPage id={router.id} path="/one">
|
||||
<OverlayPage id={router.id} path="/one" width={300} height={300}>
|
||||
<div className="bg-blue-900 p-4">
|
||||
<p>ONE</p>
|
||||
<button
|
||||
@ -58,7 +60,7 @@ export default function TestView() {
|
||||
</button>
|
||||
</div>
|
||||
</OverlayPage>
|
||||
<OverlayPage id={router.id} path="/two">
|
||||
<OverlayPage id={router.id} path="/two" width={200} height={200}>
|
||||
<div className="bg-blue-900 p-4">
|
||||
<p>TWO</p>
|
||||
<button
|
||||
|
@ -65,6 +65,16 @@ body[data-no-select] {
|
||||
height: 60vh;
|
||||
}
|
||||
|
||||
.h-screen {
|
||||
height: 100vh;
|
||||
height: 100dvh;
|
||||
}
|
||||
|
||||
.min-h-screen {
|
||||
min-height: 100vh;
|
||||
min-height: 100dvh;
|
||||
}
|
||||
|
||||
/*generated with Input range slider CSS style generator (version 20211225)
|
||||
https://toughengineer.github.io/demo/slider-styler*/
|
||||
:root {
|
||||
|
@ -6,18 +6,47 @@ export interface OverlayTransition {
|
||||
to: string;
|
||||
}
|
||||
|
||||
export interface OverlayRoute {
|
||||
id: string;
|
||||
height: number;
|
||||
width: number;
|
||||
}
|
||||
|
||||
export interface ActiveAnchorPoint {
|
||||
x: number;
|
||||
y: number;
|
||||
w: number;
|
||||
h: number;
|
||||
}
|
||||
|
||||
interface OverlayStore {
|
||||
transition: null | OverlayTransition;
|
||||
routes: Record<string, OverlayRoute>;
|
||||
anchorPoint: ActiveAnchorPoint | null;
|
||||
setTransition(newTrans: OverlayTransition | null): void;
|
||||
registerRoute(route: OverlayRoute): void;
|
||||
setAnchorPoint(point: ActiveAnchorPoint | null): void;
|
||||
}
|
||||
|
||||
export const useOverlayStore = create(
|
||||
immer<OverlayStore>((set) => ({
|
||||
transition: null,
|
||||
routes: {},
|
||||
anchorPoint: null,
|
||||
setTransition(newTrans) {
|
||||
set((s) => {
|
||||
s.transition = newTrans;
|
||||
});
|
||||
},
|
||||
registerRoute(route) {
|
||||
set((s) => {
|
||||
s.routes[route.id] = route;
|
||||
});
|
||||
},
|
||||
setAnchorPoint(point) {
|
||||
set((s) => {
|
||||
s.anchorPoint = point;
|
||||
});
|
||||
},
|
||||
}))
|
||||
);
|
||||
|
@ -24,7 +24,7 @@ export const createPlayingSlice: MakeSlice<PlayingSlice> = (set) => ({
|
||||
isDragSeeking: false,
|
||||
isFirstLoading: true,
|
||||
hasPlayedOnce: false,
|
||||
volume: 0,
|
||||
volume: 1,
|
||||
playbackSpeed: 1,
|
||||
},
|
||||
play() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user