parent
e81a2f25b8
commit
b033370322
40
src/element/flyout.tsx
Normal file
40
src/element/flyout.tsx
Normal file
@ -0,0 +1,40 @@
|
||||
import { CSSProperties, ReactNode } from "react";
|
||||
import { IconButton } from "./buttons";
|
||||
import { createPortal } from "react-dom";
|
||||
import classNames from "classnames";
|
||||
|
||||
export default function Flyout({
|
||||
show,
|
||||
children,
|
||||
onClose,
|
||||
side,
|
||||
}: {
|
||||
show: boolean;
|
||||
children: ReactNode;
|
||||
onClose: () => void;
|
||||
side: "left" | "right";
|
||||
}) {
|
||||
const styles = {
|
||||
"--flyout-w": "200px",
|
||||
transition: "all 0.2s ease-in-out",
|
||||
transform:
|
||||
side === "right"
|
||||
? `translate(${show ? "0" : "var(--flyout-w)"},0)`
|
||||
: `translate(${show ? "0" : "calc(-1 * var(--flyout-w))"},0)`,
|
||||
} as CSSProperties;
|
||||
|
||||
return createPortal(
|
||||
<div
|
||||
className={classNames("absolute z-20 top-0 overflow-hidden", {
|
||||
"pointer-events-none": !show,
|
||||
"right-0": side == "right",
|
||||
"left-0": side === "left",
|
||||
})}>
|
||||
<div className="bg-layer-2/90 h-[100dvh] px-3 py-4" style={styles}>
|
||||
<IconButton iconName="x" className="rounded-xl w-10 h-10 mb-6" iconSize={16} onClick={onClose} />
|
||||
{children}
|
||||
</div>
|
||||
</div>,
|
||||
document.body,
|
||||
) as React.ReactNode;
|
||||
}
|
@ -142,7 +142,7 @@ export function HeaderNav() {
|
||||
{layoutState.leftNav && (
|
||||
<NavLinkIcon
|
||||
name="hamburger"
|
||||
className="!opacity-100 max-xl:hidden"
|
||||
className="!opacity-100"
|
||||
onClick={() => {
|
||||
layoutState.update(c => {
|
||||
c.leftNavExpand = !c.leftNavExpand;
|
||||
|
@ -1,41 +1,77 @@
|
||||
import { useLayout } from "./context";
|
||||
import { NavLinkIcon } from "./nav-icon";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import { useMediaQuery } from "usehooks-ts";
|
||||
import Flyout from "@/element/flyout";
|
||||
|
||||
export function LeftNav() {
|
||||
const layout = useLayout();
|
||||
const isDesktop = useMediaQuery("(min-width: 1280px)");
|
||||
const expandLabels = !isDesktop || layout.leftNavExpand;
|
||||
|
||||
function hideAfterMobileNav() {
|
||||
if (isDesktop) return;
|
||||
layout.update(c => {
|
||||
c.leftNavExpand = false;
|
||||
return { ...c };
|
||||
});
|
||||
}
|
||||
|
||||
if (layout.leftNav === false) return;
|
||||
return (
|
||||
<div className="flex flex-col gap-4 p-2 max-xl:hidden">
|
||||
<NavLinkIcon name="signal" route="/streams" className="flex gap-2 items-center">
|
||||
{layout.leftNavExpand && (
|
||||
<span className="pr-3">
|
||||
<FormattedMessage defaultMessage="Streams" />
|
||||
</span>
|
||||
)}
|
||||
</NavLinkIcon>
|
||||
<NavLinkIcon name="play-circle" route="/videos" className="flex gap-2 items-center">
|
||||
{layout.leftNavExpand && (
|
||||
<span className="pr-3">
|
||||
<FormattedMessage defaultMessage="Videos" />
|
||||
</span>
|
||||
)}
|
||||
</NavLinkIcon>
|
||||
<NavLinkIcon name="film" route="/shorts" className="flex gap-2 items-center">
|
||||
{layout.leftNavExpand && (
|
||||
<span className="pr-3">
|
||||
<FormattedMessage defaultMessage="Shorts" />
|
||||
</span>
|
||||
)}
|
||||
</NavLinkIcon>
|
||||
<NavLinkIcon name="grid" route="/category" className="flex gap-2 items-center">
|
||||
{layout.leftNavExpand && (
|
||||
<span className="pr-3">
|
||||
<FormattedMessage defaultMessage="Categories" />
|
||||
</span>
|
||||
)}
|
||||
</NavLinkIcon>
|
||||
</div>
|
||||
);
|
||||
function navInner() {
|
||||
return (
|
||||
<div className="flex flex-col gap-4 p-2">
|
||||
<NavLinkIcon name="signal" route="/streams" className="flex gap-2 items-center" onClick={hideAfterMobileNav}>
|
||||
{expandLabels && (
|
||||
<span className="pr-3">
|
||||
<FormattedMessage defaultMessage="Streams" />
|
||||
</span>
|
||||
)}
|
||||
</NavLinkIcon>
|
||||
<NavLinkIcon
|
||||
name="play-circle"
|
||||
route="/videos"
|
||||
className="flex gap-2 items-center"
|
||||
onClick={hideAfterMobileNav}>
|
||||
{expandLabels && (
|
||||
<span className="pr-3">
|
||||
<FormattedMessage defaultMessage="Videos" />
|
||||
</span>
|
||||
)}
|
||||
</NavLinkIcon>
|
||||
<NavLinkIcon name="film" route="/shorts" className="flex gap-2 items-center" onClick={hideAfterMobileNav}>
|
||||
{expandLabels && (
|
||||
<span className="pr-3">
|
||||
<FormattedMessage defaultMessage="Shorts" />
|
||||
</span>
|
||||
)}
|
||||
</NavLinkIcon>
|
||||
<NavLinkIcon name="grid" route="/category" className="flex gap-2 items-center" onClick={hideAfterMobileNav}>
|
||||
{expandLabels && (
|
||||
<span className="pr-3">
|
||||
<FormattedMessage defaultMessage="Categories" />
|
||||
</span>
|
||||
)}
|
||||
</NavLinkIcon>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (isDesktop) {
|
||||
return navInner();
|
||||
} else {
|
||||
return (
|
||||
<Flyout
|
||||
side="left"
|
||||
show={layout.leftNavExpand}
|
||||
onClose={() => {
|
||||
layout.update(c => {
|
||||
c.leftNavExpand = !c.leftNavExpand;
|
||||
return { ...c };
|
||||
});
|
||||
}}>
|
||||
{navInner()}
|
||||
</Flyout>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { NostrLink, TaggedNostrEvent } from "@snort/system";
|
||||
import { Helmet } from "react-helmet";
|
||||
import { Suspense, lazy, useEffect } from "react";
|
||||
import { Suspense, lazy } from "react";
|
||||
import { useMediaQuery } from "usehooks-ts";
|
||||
|
||||
const LiveVideoPlayer = lazy(() => import("@/element/stream/live-video-player"));
|
||||
@ -12,7 +12,6 @@ import { ContentWarningOverlay, useContentWarning } from "@/element/nsfw";
|
||||
import { useCurrentStreamFeed } from "@/hooks/current-stream-feed";
|
||||
import { StreamState } from "@/const";
|
||||
import { StreamInfo } from "@/element/stream/stream-info";
|
||||
import { useLayout } from "./layout/context";
|
||||
import { StreamContextProvider } from "@/element/stream/stream-state";
|
||||
|
||||
export function StreamPage({ link, evPreload }: { evPreload?: TaggedNostrEvent; link: NostrLink }) {
|
||||
@ -33,25 +32,6 @@ export function StreamPage({ link, evPreload }: { evPreload?: TaggedNostrEvent;
|
||||
const goal = useZapGoal(goalTag);
|
||||
const isDesktop = useMediaQuery("(min-width: 1280px)");
|
||||
const isGrownUp = useContentWarning();
|
||||
const layout = useLayout();
|
||||
|
||||
useEffect(() => {
|
||||
if (layout.leftNav) {
|
||||
layout.update(c => {
|
||||
c.leftNav = false;
|
||||
return { ...c };
|
||||
});
|
||||
}
|
||||
}, [layout]);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
layout.update(c => {
|
||||
c.leftNav = true;
|
||||
return { ...c };
|
||||
});
|
||||
};
|
||||
}, []);
|
||||
|
||||
if (contentWarning && !isGrownUp) {
|
||||
return <ContentWarningOverlay />;
|
||||
|
Loading…
x
Reference in New Issue
Block a user