113 lines
3.7 KiB
TypeScript
113 lines
3.7 KiB
TypeScript
// * Packages
|
|
import { motion } from "motion/react";
|
|
import { ReactNode, useEffect, useState } from "react";
|
|
import { Head, usePage } from "@inertiajs/react";
|
|
// * Components shadcn
|
|
import { SidebarProvider, useSidebar } from "@/components/ui/sidebar";
|
|
import { Toaster } from "@/components/ui/toaster";
|
|
import { TooltipProvider } from "@/components/ui/tooltip";
|
|
// * Other
|
|
import AppSidebar from "@/back/Components/Layout/AppSidebar";
|
|
import AppHeader from "@/back/Components/Layout/AppHeader";
|
|
import AppFooter from "@/back/Components/Layout/AppFooter";
|
|
import { ThemeProvider } from "@/back/providers/ThemeProvider";
|
|
|
|
type AuthenticatedLayoutProps = {
|
|
title: string;
|
|
beforeNode?: ReactNode;
|
|
afterNode?: ReactNode;
|
|
children: ReactNode;
|
|
};
|
|
|
|
function LayoutContent({
|
|
title,
|
|
beforeNode,
|
|
afterNode,
|
|
children,
|
|
}: Omit<AuthenticatedLayoutProps, "children"> & { children: ReactNode }) {
|
|
const { open } = useSidebar();
|
|
const [isSm, setIsSm] = useState(false);
|
|
|
|
let sidebarVar = "0px";
|
|
if (!isSm) {
|
|
sidebarVar = open
|
|
? "var(--sidebar-width)"
|
|
: "var(--sidebar-width-icon)";
|
|
}
|
|
|
|
useEffect(() => {
|
|
const mediaQuery = window.matchMedia("(max-width: 767px)");
|
|
setIsSm(mediaQuery.matches);
|
|
|
|
const handler = (e: MediaQueryListEvent) => setIsSm(e.matches);
|
|
mediaQuery.addEventListener("change", handler);
|
|
return () => mediaQuery.removeEventListener("change", handler);
|
|
}, []);
|
|
|
|
return (
|
|
<>
|
|
<AppSidebar />
|
|
<motion.main
|
|
initial={{ opacity: 0 }}
|
|
animate={{ opacity: 1 }}
|
|
exit={{ opacity: 0 }}
|
|
transition={{ duration: 0.3 }}
|
|
style={{ maxWidth: `calc(100% - ${sidebarVar})` }}
|
|
className="relative flex flex-col justify-between transition-[max-width] duration-200 ease-linear w-full px-8 pb-4"
|
|
>
|
|
<div className="container flex flex-col justify-between mx-auto">
|
|
<AppHeader />
|
|
<div className="flex justify-between items-center py-4">
|
|
<Head title={title} />
|
|
<div className="flex justify-start items-center">
|
|
{beforeNode}
|
|
<h1
|
|
className={`scroll-m-20 text-4xl font-extrabold tracking-tight lg:text-5xl${
|
|
beforeNode ? " ms-2" : ""
|
|
}`}
|
|
>
|
|
{title}
|
|
</h1>
|
|
</div>
|
|
{afterNode}
|
|
</div>
|
|
{children}
|
|
</div>
|
|
<Toaster />
|
|
<AppFooter />
|
|
</motion.main>
|
|
</>
|
|
);
|
|
}
|
|
|
|
export default function AuthenticatedLayout({
|
|
title,
|
|
beforeNode,
|
|
afterNode,
|
|
children,
|
|
}: AuthenticatedLayoutProps) {
|
|
const isCollapsed =
|
|
typeof window !== "undefined"
|
|
? localStorage.getItem("vite-ui-collapsed")
|
|
: null;
|
|
|
|
const { locale } = usePage().props.appEnv as unknown as {
|
|
locale: string;
|
|
};
|
|
|
|
return (
|
|
<ThemeProvider key={locale}>
|
|
<SidebarProvider defaultOpen={!isCollapsed}>
|
|
<TooltipProvider>
|
|
<LayoutContent
|
|
title={title}
|
|
beforeNode={beforeNode}
|
|
afterNode={afterNode}
|
|
>
|
|
{children}
|
|
</LayoutContent>
|
|
</TooltipProvider>
|
|
</SidebarProvider>
|
|
</ThemeProvider>
|
|
);
|
|
}
|