diff --git a/packages/ui/src/components/AppShell.tsx b/packages/ui/src/components/AppShell.tsx new file mode 100644 index 00000000..b9399eed --- /dev/null +++ b/packages/ui/src/components/AppShell.tsx @@ -0,0 +1,270 @@ +import * as React from 'react'; +import { clsx } from 'clsx'; +import { Menu, X } from 'lucide-react'; + +type ShellStyle = React.CSSProperties & { + '--bl-app-sidebar-width'?: string; +}; + +export interface AppShellProps extends React.HTMLAttributes { + sidebarWidth?: number; +} + +export function AppShell({ + sidebarWidth = 280, + className, + style, + children, + ...props +}: AppShellProps) { + const shellStyle: ShellStyle = { + '--bl-app-sidebar-width': `${sidebarWidth}px`, + ...style, + }; + + return ( +
+ {children} +
+ ); +} + +export interface AppShellSkipLinkProps extends React.AnchorHTMLAttributes { + label?: string; +} + +export function AppShellSkipLink({ + href = '#main-content', + label = 'Skip to main content', + className, + children, + ...props +}: AppShellSkipLinkProps) { + return ( + + {children ?? label} + + ); +} + +export interface AppShellMobileToggleProps extends React.ButtonHTMLAttributes { + open: boolean; + openLabel?: string; + closeLabel?: string; +} + +export function AppShellMobileToggle({ + open, + openLabel = 'Open menu', + closeLabel = 'Close menu', + className, + children, + ...props +}: AppShellMobileToggleProps) { + return ( + + ); +} + +export interface AppShellOverlayProps extends React.HTMLAttributes { + open: boolean; +} + +export function AppShellOverlay({ open, className, ...props }: AppShellOverlayProps) { + return ( +