// ~/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";