Drawer

A panel from an edge of the screen that you can drag to close.

import { … } from '@inlaylab/ivorie/drawer'
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { IvButton } from '@inlaylab/ivorie/button';
import { IV_DRAWER_IMPORTS } from '@inlaylab/ivorie/drawer';

@Component({
  selector: 'demo-drawer',
  imports: [IV_DRAWER_IMPORTS, IvButton],
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
    <button ivButton variant="outline" [ivDrawerTrigger]="drawer">Open drawer</button>
    <ng-template #drawer>
      <div class="mx-auto w-full max-w-sm">
        <iv-drawer-header>
          <iv-drawer-title>Move goal</iv-drawer-title>
          <iv-drawer-description>Set your daily activity goal.</iv-drawer-description>
        </iv-drawer-header>
        <div class="flex items-center justify-center gap-4 p-4">
          <button ivButton variant="outline" size="icon" aria-label="Decrease" [disabled]="goal() <= 200" (click)="goal.set(goal() - 10)">−</button>
          <div class="text-center">
            <div class="text-6xl font-bold tracking-tighter tabular-nums">{{ goal() }}</div>
            <div class="text-muted-foreground text-xs uppercase">Calories/day</div>
          </div>
          <button ivButton variant="outline" size="icon" aria-label="Increase" [disabled]="goal() >= 400" (click)="goal.set(goal() + 10)">+</button>
        </div>
        <iv-drawer-footer>
          <button ivButton ivDrawerClose="saved">Submit</button>
          <button ivButton variant="outline" ivDrawerClose>Cancel</button>
        </iv-drawer-footer>
      </div>
    </ng-template>
  `,
})
export default class DrawerDemo {
  readonly goal = signal(350);
}

Installation

CLInpm
npx ivorie add drawer

A drawer is a Sheet with a drag gesture. Drag it more than a quarter of its size toward its edge and it closes with reason 'drag'. A shorter drag springs back. It uses the Dialog service, close reasons and focus rules.

Usage

import { IV_DRAWER_IMPORTS } from '@inlaylab/ivorie/drawer';
<button ivButton [ivDrawerTrigger]="drawer">Open</button>
<ng-template #drawer>
  <iv-drawer-header>
    <iv-drawer-title>Title</iv-drawer-title>
  </iv-drawer-header>
</ng-template>

Or open it from code: inject(IvDrawer).open(MyComponent, { direction: 'right' }). It returns an IvDialogRef.

Examples

Direction

import { ChangeDetectionStrategy, Component } from '@angular/core';
import { IvButton } from '@inlaylab/ivorie/button';
import { DrawerDirection, IV_DRAWER_IMPORTS } from '@inlaylab/ivorie/drawer';

@Component({
  selector: 'demo-drawer-direction',
  imports: [IV_DRAWER_IMPORTS, IvButton],
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
    <div class="flex flex-wrap gap-2">
      @for (direction of directions; track direction) {
        <button ivButton variant="outline" class="capitalize" [ivDrawerTrigger]="drawer" [ivDrawerConfig]="{ direction }">{{ direction }}</button>
      }
    </div>
    <ng-template #drawer>
      <iv-drawer-header>
        <iv-drawer-title>Drawer</iv-drawer-title>
        <iv-drawer-description>Drag it toward its edge to close.</iv-drawer-description>
      </iv-drawer-header>
      <iv-drawer-footer>
        <button ivButton variant="outline" ivDrawerClose>Close</button>
      </iv-drawer-footer>
    </ng-template>
  `,
})
export default class DrawerDirectionDemo {
  readonly directions: DrawerDirection[] = ['top', 'right', 'bottom', 'left'];
}

API

Every part also accepts class.

DrawerConfig

All Dialog config options except size, height and showCloseButton, plus:

Name Type Default Description
direction 'top' | 'right' | 'bottom' | 'left' 'bottom' Edge the drawer comes from.

[ivDrawerTrigger]

Name Type Default Description
ivDrawerTrigger TemplateRef | Type required Content to show.
ivDrawerConfig DrawerConfig {} Options.

Outputs: opened (IvDialogRef), closed (DialogCloseEvent).

[ivDrawerClose]

Name Type Default Description
ivDrawerClose unknown undefined Result passed to afterClosed.
reason DialogCloseReason 'close-button' Close reason.

Other parts

  • iv-drawer-header, iv-drawer-title, iv-drawer-description, iv-drawer-footer.

Accessibility

Same as Dialog: role="dialog" with aria-modal, labelled by the title, focus trapped inside, Escape closes, focus returns to the opener. Dragging is optional: a drawer can always close with Escape or a button.