import { useState } from "react"; import { AppShell, Flex, PageContainer, Sidebar, Topbar } from "@godxjp/ui/layout"; import type { SidebarSectionProp } from "@godxjp/ui/layout"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@godxjp/ui/data-display"; import { Button, Text } from "@godxjp/ui/general"; import { Input } from "@godxjp/ui/data-entry"; import { Bell, Building2, FileText, LayoutDashboard, MessageSquare, Plus, Receipt, Users, } from "lucide-react"; /** * AppShell · grid states, one per URL. * * The shell's grid is keyed on four attributes — `data-sidebar`, `data-nav-rail(-position)`, * `data-collapsed` and `data-topbar-span` — and every combination is a real product shape. This * frame renders ONE AppShell in whichever state the query names, so a browser gate can walk all of * them (`scripts/check-app-shell-narrow-grid.mjs`) without stacking several `main` landmarks on a * single page: * * ?topbarSpan=content|full ?navRail=start|end|top|bottom ?collapsed=1 ?sidebar=none * ?responsive=docked ?measure=narrow|medium ?footer=1|row * * `measure` and `footer` feed `scripts/check-app-shell-page-width.mjs` (gh#672): the page is fluid * in the main column at every sidebar state, and a sticky footer band always spans `.app-main`, * even when a service caps content with `--app-shell-page-max-width` or the page sets `measure`. * The footer's CONTENT sits in the same column as the body (gh#682), so the two shapes a footer * takes are both rendered: `footer=1` an end-aligned action group, `footer=row` a full-row composer. * * With no query it is `topbarSpan="full"` without a rail — the state gh#474 found collapsing * `main` into a 170px implicit track below 900px, with the page actions laid out past the edge. */ const SECTIONS: SidebarSectionProp[] = [ { label: "会計", items: [ { id: "dashboard", label: "ダッシュボード", icon: LayoutDashboard }, { id: "journal", label: "仕訳", icon: FileText }, { id: "invoices", label: "請求書", icon: Receipt }, ], }, { label: "管理", items: [ { id: "partners", label: "取引先", icon: Building2 }, { id: "users", label: "ユーザー", icon: Users }, ], }, ]; type RailPosition = "start" | "end" | "top" | "bottom"; function readState() { const params = new URLSearchParams(window.location.search); const rail = params.get("navRail"); return { topbarSpan: params.get("topbarSpan") === "content" ? ("content" as const) : ("full" as const), navRail: (["start", "end", "top", "bottom"] as const).find((p) => p === rail) as RailPosition | undefined, collapsed: params.get("collapsed") === "1", hasSidebar: params.get("sidebar") !== "none", responsiveNavigation: params.get("responsive") === "docked" ? ("docked" as const) : ("drawer" as const), measure: (["narrow", "medium"] as const).find((m) => m === params.get("measure")), footer: (["1", "row"] as const).find((f) => f === params.get("footer")), }; } export default function Demo() { const [{ topbarSpan, navRail, collapsed, hasSidebar, responsiveNavigation, measure, footer }] = useState(readState); const [activeId, setActiveId] = useState("dashboard"); const strip = navRail === "top" || navRail === "bottom"; return ( ) : undefined } navRail={ navRail ? ( ) : undefined } navRailPosition={navRail} topbar={ CoreBooks} end={ } /> } > ) : footer === "row" ? ( ) : undefined } stickyFooter={footer !== undefined} extra={ } > 今月の請求 未送信 3 件 · 入金待ち 12 件 状態はクエリで切り替えます。どの状態でも 900px 以下では 1 列になり、main は画面幅いっぱいに広がります。 ); }