# Drawer Layer A global drawer management system that allows you to open drawers from anywhere in your application without mounting multiple instances. ## Features - ✅ Stack-based system - Drawers can open other drawers - ✅ Reactive config updates - Drawer config stays in sync with component state - ✅ Two usage patterns - Simple API and Composition Components - ✅ Smooth animations - Built-in open/close transitions - ✅ Flexible - Use simple props or build custom layouts - ✅ Uses design-system components - All content uses Drawer composition components from design-system ## File Structure ``` src/components/layers/Drawers/ ├── constants.ts # Drawer constants (z-index, border radius) ├── hooks.ts # useDrawerLayer, useDrawerLayerItem hooks ├── utils.ts # Type guards and helper functions ├── types.ts # TypeScript type definitions ├── index.tsx # DrawerLayerProvider and Drawer exports ├── stories.tsx # Storybook stories └── README.md # This file ``` ## Setup Wrap your application with the `DrawerLayerProvider`: ```tsx import { DrawerLayerProvider } from '@material-hu/layers/Drawers'; function App() { return ( ); } ``` ## Usage **Props:** Use `wrapperProps` for the MUI Drawer wrapper (e.g. `PaperProps`, `SlideProps`, `anchor`). Content config (title, children, or content) stays at top level. ### 1. Simple API Use the simple API for common drawer patterns. This uses the design-system Drawer component internally. ```tsx import { useDrawerLayer } from '@material-hu/layers/Drawers'; function MyComponent() { const { openDrawer, closeDrawer } = useDrawerLayer(); const handleEdit = () => { openDrawer({ title: 'Edit Item', size: 'medium', children: , primaryButtonProps: { children: 'Save', onClick: () => { saveChanges(); closeDrawer(); }, }, secondaryButtonProps: { children: 'Cancel', onClick: closeDrawer, }, footer: , wrapperProps: { anchor: 'right', PaperProps: { sx: { maxWidth: 480 } }, }, }); }; return ; } ``` #### Simple API Props | Prop | Type | Description | |------|------|-------------| | `title` | `string` | Drawer title | | `size` | `'medium' \| 'large'` | Drawer width | | `children` | `ReactNode` | Main content | | `onClose` | `(reason?: DrawerCloseReason) => void` | Close handler (handled by layer) | | `onBack` | `() => void` | Back button handler | | `hasBackButton` | `boolean` | Show back button | | `primaryButtonProps` | `LoadingButtonProps` | Primary action button | | `secondaryButtonProps` | `LoadingButtonProps` | Secondary action button | | `footer` | `ReactNode` | Extra footer content | | `primaryContent` | `ReactNode` | Left side content (double layout) | | `secondaryContent` | `ReactNode` | Right side content (double layout) | | `slotProps` | `object` | Styling props for internal sections | | `wrapperProps` | `DrawerWrapperProps` | Optional. Props for the MUI Drawer wrapper (`PaperProps`, `SlideProps`, `anchor`, `sx`, etc.). | ### 2. useDrawerLayerItem Hook Use this hook when you need a drawer with **reactive config updates**. The drawer configuration automatically stays in sync with your component's state. ```tsx import { useDrawerLayerItem } from '@material-hu/layers/Drawers'; function MyComponent() { const [isLoading, setIsLoading] = useState(false); const [formData, setFormData] = useState({ name: '' }); const drawer = useDrawerLayerItem('edit-profile', { title: 'Edit Profile', children: ( setFormData({ name: e.target.value })} /> ), primaryButtonProps: { children: 'Save', loading: isLoading, // Auto-updates when isLoading changes onClick: async () => { setIsLoading(true); await saveProfile(formData); setIsLoading(false); drawer.closeDrawer(); }, }, secondaryButtonProps: { children: 'Cancel', onClick: () => drawer.closeDrawer(), }, }); return ; } ``` #### useDrawerLayerItem API | Parameter | Type | Description | |-----------|------|-------------| | `id` | `string` | Unique identifier for the drawer | | `config` | `OpenDrawerArgs` | Drawer configuration (same props as `openDrawer`) | **Returns:** | Method | Type | Description | |--------|------|-------------| | `openDrawer` | `(config?: OpenDrawerArgs) => void` | Opens the drawer (optionally with different config) | | `closeDrawer` | `(immediate?: boolean) => void` | Closes the drawer | | `updateDrawerConfig` | `(setter: (current) => new) => void` | Manually update drawer config | #### When to use each hook | Use case | Hook | |----------|------| | Simple one-off drawers | `useDrawerLayer` | | Drawers with reactive state (loading, form data) | `useDrawerLayerItem` | | Programmatic config updates | `useDrawerLayerItem` | ### 3. Composition Components API Use composition components for complete flexibility in layout and content. Pass **`content`** (your composed JSX) and optionally **`wrapperProps`** for the MUI Drawer wrapper. ```tsx import { useDrawerLayer, Drawer } from '@material-hu/layers/Drawers'; function MyComponent() { const { openDrawer, closeDrawer } = useDrawerLayer(); const handleCustomDrawer = () => { openDrawer({ content: ( <> ), wrapperProps: { anchor: 'right', PaperProps: { sx: { maxWidth: 480 } }, }, }); }; return ; } ``` #### Composition API props | Prop | Type | Description | |------|------|-------------| | `content` | `ReactNode` | **Required.** Your composed layout (e.g. `Drawer.Header` + `Drawer.Body` + …). | | `onClose` | `(reason?: DrawerCloseReason) => void` | Close handler (handled by layer). | | `wrapperProps` | `DrawerWrapperProps` | Optional. Props for the MUI Drawer wrapper (`PaperProps`, `SlideProps`, `anchor`, `sx`, etc.). | #### Available Composition Components ##### `Drawer.Header` Header section with title, optional back button, and close button. ```tsx {/* Optional custom content */} ``` ##### `Drawer.Body` Content section with automatic scroll support. ```tsx ``` ##### `Drawer.DoubleLayout` Two-column layout for side-by-side content. ```tsx } secondaryContent={} slotProps={{ // Optional styling layout?: StackProps; primaryContent?: StackProps; secondaryContent?: StackProps; }} /> ``` ##### `Drawer.Footer` Extra footer section for additional content. ```tsx ``` ##### `Drawer.Actions` Action buttons section. ```tsx {/* Or provide custom children */} ``` ## Examples ### Simple Form Drawer ```tsx const handleEdit = () => { openDrawer({ title: 'Edit Profile', size: 'medium', children: , primaryButtonProps: { children: 'Save', onClick: () => { saveProfile(); closeDrawer(); } }, secondaryButtonProps: { children: 'Cancel', onClick: closeDrawer } }); }; ``` ### Drawer with Custom Layout ```tsx const handleCustom = () => { openDrawer({ content: ( <> ) }); }; ``` ### Double Layout Drawer ```tsx const handleDoubleLayout = () => { openDrawer({ title: 'Compare', size: 'large', primaryContent: , secondaryContent: , primaryButtonProps: { children: 'Apply', onClick: closeDrawer } }); }; ``` ## Behavior Notes - **Multiple Instances**: Multiple instances of Drawers can be used and stacked on top of the other one. - **Session Management**: Each drawer has a unique session ID to prevent conflicts during transitions. - **Immediate Close**: Pass `true` to `closeDrawer(true)` to close without animation (useful before navigation). - **Auto Cleanup**: Drawer state is automatically cleaned up after close animations complete. - **Size**: Automatically adjusts to `large` when using double layout (`primaryContent` + `secondaryContent`). ## TypeScript Support Full TypeScript support with exported types: ```tsx import type { OpenDrawerArgs, SimpleDrawerProps, CompositionDrawerProps, DrawerWrapperProps, DrawerLayerContextValue, DrawerLevel, DrawerCloseReason, } from '@material-hu/layers/Drawers/types'; ``` - **`OpenDrawerArgs`** – Argument for `openDrawer` / `useDrawerLayerItem` config. Simple (design-system props) or Composition (`content`). Optional `wrapperProps` for the MUI Drawer wrapper. - **`DrawerWrapperProps`** – MUI Drawer props (excluding `open`, `onClose`). Pass via `wrapperProps` in `OpenDrawerArgs`. - **`DrawerCloseReason`** – `'backdropClick' | 'escapeKeyDown' | 'dismissButton' | 'cancelButton' | 'backButton' | 'submitButton'`. ### Available Exports ```tsx // Main exports import { DrawerLayerProvider, // Context provider useDrawerLayer, // Basic drawer hook useDrawerLayerItem, // Reactive drawer hook Drawer, // Composition components } from '@material-hu/layers/Drawers'; // Composition components Drawer.Header Drawer.Body Drawer.DoubleLayout Drawer.Footer Drawer.Actions Drawer.Content ```