# Drawer

> Side panel that slides in from any edge. Use the component when the markup lives in your template, or `WrDrawerManager` to open any component as a drawer and read its close result.

Source: https://ngwr.dev/reference/components/drawer  
Kind: Component, Service, Directives, CDK Overlay

## Installation

```angular-ts
import {
  WrDrawer,
  WrDrawerTitle,
  WrDrawerContent,
  WrDrawerFooter,
  WrDrawerClose
} from 'ngwr/drawer';
```

## Basic usage

```angular-html
<wr-btn (click)="open.set(true)">Open</wr-btn>

<wr-drawer [(open)]="open" position="right" width="22rem">
  <h2 wrDrawerTitle>Settings</h2>
  <div wrDrawerContent>…</div>
  <div wrDrawerFooter>
    <wr-btn wrDrawerClose>Close</wr-btn>
    <wr-btn color="primary" wrDrawerClose>Save</wr-btn>
  </div>
</wr-drawer>
```

```html
dialog.open(...)
```

## Opening a drawer from code

`WrDrawerManager.open()` renders any component as a drawer — no `<wr-drawer>` in the template. Reach for it when the caller doesn't own the markup (a toolbar action, an effect, a route guard) or when you need the close result. The content uses the same layout directives and injects `WR_DRAWER_DATA` / `WrDrawerRef`, exactly like a dialog.

```angular-ts
import { WrDrawerManager } from 'ngwr/drawer';

@Component({...})
export class ToolbarComponent {
  private readonly drawers = inject(WrDrawerManager);

  async openChat(threadId: string): Promise<void> {
    const ref = this.drawers.open<ChatComponent, string, ChatData>(ChatComponent, {
      data: { thread: threadId },
      position: 'right',
      width: '24rem',
    });

    const sent = await ref.awaitClose(); // string | undefined
  }
}
```

```angular-ts
// The opened component — same layout directives, no <wr-drawer> wrapper.
import { WR_DRAWER_DATA, WrDrawerRef } from 'ngwr/drawer';

@Component({...})
export class ChatComponent {
  protected readonly data = inject<ChatData>(WR_DRAWER_DATA);
  private readonly ref = inject(WrDrawerRef);

  send(): void {
    this.store.send(this.draft());
    this.ref.close('sent');        // the caller's awaitClose() resolves
  }
}

// The class token already types the generics — no cast, no WR_DRAWER_REF needed:
private readonly ref = inject<WrDrawerRef<ChatComponent, string>>(WR_DRAWER_REF);
```

```html
drawers.open(ChatComponent, { data, position: 'right', width: '24rem' })
```

## Drawer API

| Name | Description | Type | Default |
| --- | --- | --- | --- |
| `open` | Open state. Two-way bindable. | `boolean` | `false` |
| `position` | Side the drawer slides in from. | `'left' \| 'right' \| 'top' \| 'bottom'` | `'right'` |
| `width` | Width when position is left/right. | `string` | `'20rem'` |
| `height` | Height when position is top/bottom. | `string` | `'16rem'` |
| `showHandle` | Render a grab handle on the leading edge and enable swipe-to-dismiss — drag it toward that edge and release past ~30% of the panel to close. | `boolean` | `false` |
| `hasBackdrop` | Show the dimming backdrop. | `boolean` | `true` |
| `closeOnBackdropClick` | Close when backdrop is clicked. | `boolean` | `true` |
| `closeOnEscape` | Close on Escape. | `boolean` | `true` |
| `closable` | Show the built-in dismiss (×) in the top-right corner. | `boolean` | `true` |
| `closeLabel` | Accessible name for the dismiss button. Falls back to the drawer.close catalog key. | `string \| null` | `null` |

## Focus and lifetime — the two forms differ

Both trap focus while open and both return it, on close, to whatever was focused when the drawer opened. Where the caret LANDS on open is not the same, and it follows from DOM order rather than from a rule: the focus trap takes `cdkFocusInitial` if the content marks one, and otherwise the first tabbable element in the panel.

```angular-html
<!-- Say where focus starts and the two forms stop differing. The attribute
     needs no import — the focus trap looks it up by name. -->
<wr-drawer [(open)]="open" position="right">
  <h2 wrDrawerTitle>Filters</h2>
  <div wrDrawerContent>
    <wr-form-field label="Search">
      <input wrInput cdkFocusInitial [(value)]="query" />
    </wr-form-field>
  </div>
</wr-drawer>

<!-- Unmarked:
       <wr-drawer>          -> the ✕, which its template renders first
       WrDrawerManager.open -> the first control, since its ✕ is appended last
     Either way, closing returns focus to whatever was focused when it opened. -->
```

In `<wr-drawer>` the ✕ is part of the component's own template and comes before your projected content, so it is the first tabbable element and takes the focus. `WrDrawerManager` appends its ✕ after the content instead, so focus lands on the first control in the drawer. Mark the element you want with `cdkFocusInitial` and the two behave identically.

Lifetime differs the same way, and here the component is the safer form. `<wr-drawer>` belongs to the template that declares it: destroy that view and the overlay goes with it, leaving nothing behind. A service-opened drawer outlives its caller exactly as a dialog does — `closeOnNavigation` (on by default) covers a route change, and everything else is yours: close the ref from `DestroyRef.onDestroy` when the opener can disappear on its own.

## Service API

| Name | Description | Type | Default |
| --- | --- | --- | --- |
| `open(component, options?)` | Opens a component as a drawer. Returns a WrDrawerRef. | `(component, WrDrawerOptions) => WrDrawerRef` | `—` |

## WrDrawerOptions

Mirrors the component inputs, minus `showHandle` — swipe-to-dismiss needs the component's own wrapper markup.

| Name | Description | Type | Default |
| --- | --- | --- | --- |
| `data` | Payload exposed to the content via WR_DRAWER_DATA. | `D` | `—` |
| `position` | Side the drawer slides in from. | `'left' \| 'right' \| 'top' \| 'bottom'` | `'right'` |
| `width` | Width when position is left/right. | `string` | `'20rem'` |
| `height` | Height when position is top/bottom. | `string` | `'16rem'` |
| `maxHeight` | Height cap for top/bottom positions. | `string \| null` | `null` |
| `rounded` | Round the leading corners. | `boolean` | `false` |
| `safeArea` | Pad the trailing edge with the safe-area inset. | `boolean` | `false` |
| `hasBackdrop` | Show the dimming backdrop. | `boolean` | `true` |
| `closeOnBackdropClick` | Close when backdrop is clicked. | `boolean` | `true` |
| `closeOnEscape` | Close on Escape. | `boolean` | `true` |
| `closable` | Show the built-in dismiss (×) in the top-right corner. | `boolean` | `true` |
| `closeLabel` | Accessible name for the dismiss button. Falls back to the drawer.close catalog key. | `string` | `—` |
| `panelClass` | Extra class(es) on the panel. | `string \| readonly string[]` | `—` |

## Layout directives

| Name | Description | Type | Default |
| --- | --- | --- | --- |
| `[wrDrawerTitle]` | Styles the title row. | `directive` | `—` |
| `[wrDrawerContent]` | Styles the scrollable body. | `directive` | `—` |
| `[wrDrawerFooter]` | Styles the footer row. | `directive` | `—` |
| `align` | Horizontal alignment of the footer content. | `'start' \| 'center' \| 'end'` | `'end'` |
| `[wrDrawerClose]="value?"` | Closes the parent drawer on click — the component or the service-opened one. For service drawers the optional value becomes the close result. | `directive` | `—` |

## Available inside a service-opened drawer

| Name | Description | Type | Default |
| --- | --- | --- | --- |
| `WR_DRAWER_DATA` | The data payload passed to open(). undefined when you didn't pass any. | `InjectionToken<D>` | `—` |
| `WrDrawerRef` | The open drawer’s own ref — call close(result) to dismiss it from the content. | `WrDrawerRef<unknown, unknown>` | `—` |
| `WR_DRAWER_REF` | The same ref under a second key, used by `[wrDrawerClose]`. Prefer `inject(WrDrawerRef)` — it already supports `{ optional: true }` and typed generics. | `InjectionToken<WrDrawerRef<C, R>>` | `—` |

## CSS variables

Custom properties `ngwr/drawer` publishes. Each default below is declared on the component's own selector, so a `:root` override is shadowed by it — set them on that selector, on a wrapper you scope yourself, or inline on the element. Unlike the BEM class names, these are the supported way to restyle the component.

| Variable | Default | Declared on |
| --- | --- | --- |
| `--wr-drawer-bg` | `var(--wr-color-surface)` | `.wr-drawer__panel` |
| `--wr-drawer-shadow` | `var(--wr-shadow-modal)` | `.wr-drawer__panel` |
