'use client'; import { cva, VariantProps } from 'class-variance-authority'; import * as React from 'react'; import { AppLayoutAnatomy } from '.'; import { cn, ComponentAnatomy, defineStyleAnatomy } from '../core/styling'; import { Drawer, DrawerProps } from '../drawer'; /* ------------------------------------------------------------------------------------------------- * Context * -----------------------------------------------------------------------------------------------*/ export const __AppSidebarContext = React.createContext<{ open: boolean; setOpen: (open: boolean) => void; size: VariantProps['sidebarSize']; setSize: ( size: VariantProps['sidebarSize'] ) => void; isBelowBreakpoint: boolean; }>({ open: false, setOpen: () => {}, setSize: () => {}, size: 'md', isBelowBreakpoint: false, }); export function useAppSidebarContext() { const ctx = React.useContext(__AppSidebarContext); if (!ctx) throw new Error( 'useAppSidebarContext must be used within a AppSidebarProvider' ); return ctx; } /* ------------------------------------------------------------------------------------------------- * Anatomy * -----------------------------------------------------------------------------------------------*/ export const AppSidebarAnatomy = defineStyleAnatomy({ sidebar: cva([ 'UI-AppSidebar__sidebar', 'flex flex-grow flex-col overflow-y-auto border-r border-transparent bg-[--background]', ]), }); export const AppSidebarTriggerAnatomy = defineStyleAnatomy({ trigger: cva([ 'UI-AppSidebarTrigger__trigger', 'block lg:hidden', 'items-center justify-center rounded-[--radius] p-2 text-[--muted] hover:bg-[--subtle] hover:text-[--foreground] transition-colors', 'focus:outline-none focus:ring-2 focus:ring-inset focus:ring-[--ring]', ]), }); /* ------------------------------------------------------------------------------------------------- * AppSidebar * -----------------------------------------------------------------------------------------------*/ export type AppSidebarProps = React.ComponentPropsWithoutRef<'div'> & ComponentAnatomy & { mobileDrawerProps?: Partial; }; export const AppSidebar = React.forwardRef( (props, ref) => { const { children, className, ...rest } = props; const ctx = React.useContext(__AppSidebarContext); return ( <>
{children}
ctx.setOpen(v)} side="left" > {children} ); } ); AppSidebar.displayName = 'AppSidebar'; /* ------------------------------------------------------------------------------------------------- * AppSidebarTrigger * -----------------------------------------------------------------------------------------------*/ export type AppSidebarTriggerProps = React.ComponentPropsWithoutRef<'button'> & ComponentAnatomy; export const AppSidebarTrigger = React.forwardRef< HTMLButtonElement, AppSidebarTriggerProps >((props, ref) => { const { children, className, ...rest } = props; const ctx = React.useContext(__AppSidebarContext); return ( ); }); AppSidebarTrigger.displayName = 'AppSidebarTrigger'; /* ------------------------------------------------------------------------------------------------- * AppSidebarProvider * -----------------------------------------------------------------------------------------------*/ export type AppSidebarProviderProps = { children?: React.ReactNode; open?: boolean; onOpenChange?: (open: boolean) => void; onSizeChange?: ( size: VariantProps['sidebarSize'] ) => void; }; export const AppSidebarProvider: React.FC = ({ children, onOpenChange, onSizeChange, }) => { const [open, setOpen] = React.useState(false); const [size, setSize] = React.useState['sidebarSize']>( undefined ); const [isBelowBreakpoint, setIsBelowBreakpoint] = React.useState(false); React.useEffect(() => { const handleResize = () => setIsBelowBreakpoint(window.innerWidth < 1024); // lg breakpoint handleResize(); window.addEventListener('resize', handleResize); return () => window.removeEventListener('resize', handleResize); }, [isBelowBreakpoint]); return ( <__AppSidebarContext.Provider value={{ open, setOpen: (open: boolean) => { onOpenChange?.(open); setOpen(open); }, setSize: ( size: VariantProps['sidebarSize'] ) => { onSizeChange?.(size); setSize(size); }, size: size, isBelowBreakpoint, }} > {children} ); }; AppSidebarProvider.displayName = 'AppSidebarProvider';