Sheet

A dialog that slides in from an edge of the screen.

import { … } from '@inlaylab/ivorie/sheet'
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { IvButton } from '@inlaylab/ivorie/button';
import { IV_INPUT_IMPORTS } from '@inlaylab/ivorie/input';
import { IV_SHEET_IMPORTS } from '@inlaylab/ivorie/sheet';

@Component({
  selector: 'demo-sheet',
  imports: [IV_SHEET_IMPORTS, IV_INPUT_IMPORTS, IvButton],
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
    <button ivButton variant="outline" [ivSheetTrigger]="sheet">Open</button>
    <ng-template #sheet>
      <iv-sheet-header>
        <iv-sheet-title>Edit profile</iv-sheet-title>
        <iv-sheet-description>Make changes to your profile here. Click save when you're done.</iv-sheet-description>
      </iv-sheet-header>
      <div class="grid gap-4 px-6">
        <div class="grid gap-2">
          <label ivLabel for="sheet-name">Name</label>
          <input ivInput id="sheet-name" value="Ada Lovelace" />
        </div>
        <div class="grid gap-2">
          <label ivLabel for="sheet-user">Username</label>
          <input ivInput id="sheet-user" value="@ada" />
        </div>
      </div>
      <iv-sheet-footer>
        <button ivButton ivSheetClose="saved">Save changes</button>
        <button ivButton variant="outline" ivSheetClose>Cancel</button>
      </iv-sheet-footer>
    </ng-template>
  `,
})
export default class SheetDemo {}

Installation

CLInpm
npx ivorie add sheet

A sheet is a Dialog docked to an edge. It uses the same service, close reasons and focus rules.

Usage

import { IV_SHEET_IMPORTS } from '@inlaylab/ivorie/sheet';
<button ivButton [ivSheetTrigger]="sheet" [ivSheetConfig]="{ side: 'left' }">Open</button>
<ng-template #sheet>
  <iv-sheet-header>
    <iv-sheet-title>Title</iv-sheet-title>
  </iv-sheet-header>
</ng-template>

Or open it from code: inject(IvSheet).open(MyComponent, { side: 'bottom' }). It returns an IvDialogRef.

Examples

Side

import { ChangeDetectionStrategy, Component } from '@angular/core';
import { IvButton } from '@inlaylab/ivorie/button';
import { IV_SHEET_IMPORTS } from '@inlaylab/ivorie/sheet';

@Component({
  selector: 'demo-sheet-side',
  imports: [IV_SHEET_IMPORTS, IvButton],
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
    <div class="grid grid-cols-2 gap-2">
      @for (side of sides; track side) {
        <button ivButton variant="outline" class="capitalize" [ivSheetTrigger]="sheet" [ivSheetConfig]="{ side }">{{ side }}</button>
      }
    </div>
    <ng-template #sheet>
      <iv-sheet-header>
        <iv-sheet-title>Notifications</iv-sheet-title>
        <iv-sheet-description>You have no new notifications.</iv-sheet-description>
      </iv-sheet-header>
    </ng-template>
  `,
})
export default class SheetSideDemo {
  protected readonly sides = ['top', 'right', 'bottom', 'left'] as const;
}

API

Every part also accepts class.

SheetConfig

All Dialog config options except size and height, plus:

Name Type Default Description
side 'top' | 'right' | 'bottom' | 'left' 'right' Edge the sheet docks to.

[ivSheetTrigger]

Name Type Default Description
ivSheetTrigger TemplateRef | Type required Content to show.
ivSheetConfig SheetConfig {} Options.

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

Other parts

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

[ivSheetClose]

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

Accessibility

Same as Dialog: role="dialog" with aria-modal, labelled by the title, focus trapped inside, Escape closes, focus returns to the opener.