# 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
```