# Drawer

Bottom-first modal drawer built on the local Sheet overlay primitive. It mirrors the shadcn Drawer composition while using Angular signals and explicit standalone imports.

## Import

```ts
import { ButtonComponent } from '@edsis/component/button';
import {
  DrawerCloseDirective,
  DrawerComponent,
  DrawerContentComponent,
  DrawerDescriptionComponent,
  DrawerFooterComponent,
  DrawerHeaderComponent,
  DrawerTitleComponent,
} from '@edsis/component/drawer';
```

## Usage

```ts
import { Component, signal } from '@angular/core';
import { ButtonComponent } from '@edsis/component/button';
import {
  DrawerCloseDirective,
  DrawerComponent,
  DrawerDescriptionComponent,
  DrawerFooterComponent,
  DrawerHeaderComponent,
  DrawerTitleComponent,
} from '@edsis/component/drawer';

@Component({
  selector: 'app-drawer-example',
  imports: [
    ButtonComponent,
    DrawerCloseDirective,
    DrawerComponent,
    DrawerDescriptionComponent,
    DrawerFooterComponent,
    DrawerHeaderComponent,
    DrawerTitleComponent,
  ],
  template: `
    <button type="button" Button variant="outline" (click)="open.set(true)">Open Drawer</button>

    <Drawer [(open)]="open" aria-labelledby="drawer-title" aria-describedby="drawer-description">
      <div class="mx-auto w-full max-w-sm">
        <DrawerHeader>
          <DrawerTitle id="drawer-title">Move Goal</DrawerTitle>
          <DrawerDescription id="drawer-description"
            >Set your daily activity goal.</DrawerDescription
          >
        </DrawerHeader>
        <DrawerFooter>
          <button type="button" Button>Submit</button>
          <button type="button" Button variant="outline" DrawerClose>Cancel</button>
        </DrawerFooter>
      </div>
    </Drawer>
  `,
})
export class DrawerExampleComponent {
  protected readonly open = signal(false);
}
```

## Common Patterns

### Sides

Drawer defaults to `bottom`, matching the common shadcn and Vaul pattern. Set `side` when the drawer should enter from a different edge.

```html
<Drawer [(open)]="open" side="right" aria-labelledby="drawer-side-title">
  <DrawerHeader>
    <DrawerTitle id="drawer-side-title">Scrollable Content</DrawerTitle>
  </DrawerHeader>
  <DrawerContent class="max-h-[50vh] overflow-y-auto px-1">
    <!-- long content -->
  </DrawerContent>
</Drawer>
```

### Responsive Dialog

Use a desktop breakpoint in the owning component and render Dialog for larger viewports, Drawer for smaller viewports. Keep both surfaces bound to the same `open` signal so trigger and submit actions stay consistent.

```html
@if (isDesktop()) {
<dialog [(open)]="open">...</dialog>
} @else {
<Drawer [(open)]="open">...</Drawer>
}
```

## API Reference

### DrawerComponent

| Input                  | Type                                     | Default                 |
| ---------------------- | ---------------------------------------- | ----------------------- |
| `open`                 | `boolean` model                          | `false`                 |
| `side`                 | `'top' \| 'right' \| 'bottom' \| 'left'` | `'bottom'` for `Drawer` |
| `closeOnEscape`        | `boolean`                                | `true`                  |
| `closeOnBackdropClick` | `boolean`                                | `true`                  |
| `aria-labelledby`      | `string \| null`                         | `null`                  |
| `aria-describedby`     | `string \| null`                         | `null`                  |
| `class`                | `string`                                 | empty string            |

### Parts

| Part                                    | Purpose                                                                              |
| --------------------------------------- | ------------------------------------------------------------------------------------ |
| `DrawerHeader`                          | Title and description wrapper.                                                       |
| `DrawerTitle`                           | Accessible title. Provide an `id` and connect it with `aria-labelledby`.             |
| `DrawerDescription`                     | Supporting text. Provide an `id` and connect it with `aria-describedby` when useful. |
| `DrawerContent`                         | Optional scroll/body region inside the surface.                                      |
| `DrawerFooter`                          | Action row.                                                                          |
| `button[DrawerClose]`, `a[DrawerClose]` | Projected close action equivalent to shadcn `DrawerClose`.                           |

## Styling And Theming

The drawer uses the same theme tokens as Sheet: `bg-background`, `border-border`, `text-foreground`, and `text-muted-foreground`. Pass `class` to tune dimensions such as `max-h-[85vh]`, `sm:max-w-md`, or side-specific scroll behavior.

## Accessibility

- The surface renders with `role="dialog"` and `aria-modal="true"`.
- Focus is trapped inside the CDK overlay while open and restored to the previously focused element on close.
- Provide `aria-labelledby` and `aria-describedby` when the drawer contains a title and description.
- Use native buttons for open, submit, and close actions.

## Keyboard Interactions

- `Tab` and `Shift+Tab` move within the focus-trapped drawer content.
- `Escape` closes the drawer when `closeOnEscape` is `true`.
- Enter and Space activate native buttons, including `button[DrawerClose]`.

## Angular Notes

The Angular Drawer deliberately uses an external trigger button and `[(open)]` signal binding instead of a `DrawerTrigger` component. This keeps ownership of state explicit and avoids hidden context wiring across overlay portals. `DrawerComponent` is exported as an alias over the same implementation as `SheetComponent`, so fixes to focus management, backdrop dismissal, and edge positioning are shared.

## Source Parity

shadcn Drawer is built on Vaul and includes drag gestures and scale-background options. This Angular version maps the core modal drawer behavior to CDK Overlay and FocusTrap: bottom-first direction, side selection, focus trapping, Escape/backdrop dismissal, close actions, scrollable content, responsive dialog composition, and RTL-friendly content. It does not implement Vaul drag gestures.
