// ~/src/siteplane/components/dashboard-shell/dashboard-shell.tsx "use client"; import * as React from "react"; import { cn } from "../../system/utils/cn"; import { slotClass } from "../../system/utils/slot-class"; import { Sidebar, SidebarHeader, SidebarInset, SidebarProvider, SidebarTrigger, } from "../sidebar"; export type DashboardShellLayout = "inset" | "push"; export interface DashboardShellProps extends React.ComponentProps< typeof SidebarProvider > { layout?: DashboardShellLayout; /** * Keeps DashboardShellHeader in view while the content scrolls. Default * true. From the Sidebar's mobile breakpoint up, the shell is the * viewport's height and only DashboardShellMain scrolls. Below it the page * scrolls and the header sticks to the top of the viewport. Set false for a * page that scrolls as a whole: the shell is then at least the viewport's * height, and the header scrolls away with the page. */ stickyHeader?: boolean; } export interface DashboardShellHeaderProps extends React.ComponentProps<"header"> { divider?: boolean; } export interface DashboardShellMainProps extends React.ComponentProps<"div"> { /** * Runs the content to the edges of the main area, without its padding, for * a view that lays out its own panes, such as a list beside a detail pane. */ flush?: boolean; } // The parts that follow the shell's settings read them here. Outside a // DashboardShell no sticky rule applies, so a part there keeps the Sidebar's // own behaviour. const DashboardShellContext: React.Context<{ stickyHeader: boolean }> = React.createContext<{ stickyHeader: boolean }>({ stickyHeader: false }); export function DashboardShell({ children, className, layout = "push", mobileBreakpoint = "md", stickyHeader = true, ...props }: DashboardShellProps): React.ReactElement { const context = React.useMemo(() => ({ stickyHeader }), [stickyHeader]); return ( {children} ); } export function DashboardShellSidebar({ className, collapsible = "icon", ...props }: React.ComponentProps): React.ReactElement { return ( ); } export function DashboardShellSidebarHeader({ className, ...props }: React.ComponentProps): React.ReactElement { return ( ); } export function DashboardShellInset({ className, ...props }: React.ComponentProps): React.ReactElement { const { stickyHeader } = React.useContext(DashboardShellContext); return ( ); } export function DashboardShellHeader({ className, divider = true, ...props }: DashboardShellHeaderProps): React.ReactElement { // The header sits on the main surface (B1), never on the page canvas: it has // no fill of its own, so the sidebar and the content never share one grey. // Its inline inset is the main area's 16 px in both layouts and at every // width, so a title in it starts on the content's line; a leading sidebar // trigger puts its glyph on that line (design-rework.css, "Dashboard Shell // header"). In the push layout SidebarInset moves the header clear of the // expanded sidebar with the rest of the content. With the shell's // stickyHeader, the default, it sticks to the top below the breakpoint, // filled with the surface it sits on there, so the content scrolls under it // unseen. return (
); } export function DashboardShellMain({ className, flush = false, ...props }: DashboardShellMainProps): React.ReactElement { return (
); } export function DashboardShellPrimary({ className, ...props }: React.ComponentProps<"section">): React.ReactElement { return (
); } export { SidebarTrigger as DashboardShellSidebarTrigger }; // ~/src/siteplane/components/dashboard-shell/index.ts export * from "./dashboard-shell";