70 lines
2.1 KiB
TypeScript
70 lines
2.1 KiB
TypeScript
"use client";
|
|
|
|
import { type ReactNode, useState, useCallback, useEffect } from "react";
|
|
import { usePathname } from "next/navigation";
|
|
import { Sidebar } from "@/components/Sidebar";
|
|
|
|
export function AppShell({
|
|
title,
|
|
description,
|
|
actions,
|
|
children,
|
|
}: {
|
|
title: string;
|
|
description: string;
|
|
actions?: ReactNode;
|
|
children: ReactNode;
|
|
}) {
|
|
const [sidebarOpen, setSidebarOpen] = useState(false);
|
|
const pathname = usePathname();
|
|
|
|
useEffect(() => {
|
|
setSidebarOpen(false);
|
|
}, [pathname]);
|
|
|
|
const toggle = useCallback(() => setSidebarOpen((o) => !o), []);
|
|
const close = useCallback(() => setSidebarOpen(false), []);
|
|
|
|
return (
|
|
<div className="app-shell">
|
|
<a href="#main-content" className="skip-link">
|
|
Skip to main content
|
|
</a>
|
|
<button
|
|
className="sidebar-toggle"
|
|
onClick={toggle}
|
|
aria-label={sidebarOpen ? "Close menu" : "Open menu"}
|
|
type="button"
|
|
>
|
|
{sidebarOpen ? "\u2715" : "\u2630"}
|
|
</button>
|
|
<div
|
|
className={`sidebar-overlay${sidebarOpen ? " open" : ""}`}
|
|
onClick={close}
|
|
aria-hidden="true"
|
|
/>
|
|
<Sidebar open={sidebarOpen} />
|
|
<main id="main-content" className="main-panel" tabIndex={-1} aria-labelledby="page-title">
|
|
<div className="page-grid">
|
|
<header
|
|
className="surface-card"
|
|
style={{ padding: "var(--nl-space-6)", display: "flex", justifyContent: "space-between", gap: "var(--nl-space-4)", alignItems: "start", flexWrap: "wrap" }}
|
|
>
|
|
<div style={{ display: "grid", gap: "var(--nl-space-2)" }}>
|
|
<h1
|
|
id="page-title"
|
|
style={{ margin: 0, fontFamily: "var(--nl-font-display)", fontSize: "var(--nl-fs-2xl)", fontWeight: 700 }}
|
|
>
|
|
{title}
|
|
</h1>
|
|
<div style={{ color: "var(--nl-text-secondary)", maxWidth: 720 }}>{description}</div>
|
|
</div>
|
|
{actions ? <div aria-label="Page actions">{actions}</div> : null}
|
|
</header>
|
|
{children}
|
|
</div>
|
|
</main>
|
|
</div>
|
|
);
|
|
}
|