Action Sheet

Offer a short list of actions in a group that slides up from the bottom of the screen, with a separate Cancel button below it.

Installation

npx @blotch/dolan add action-sheet
import {
  UiActionSheet,
  UiActionSheetTitle,
  UiActionSheetItem,
  UiActionSheetCancel,
} from '../components/ui/action-sheet';

Usage

import { Component, signal } from '@angular/core';
import {
  UiActionSheet,
  UiActionSheetTitle,
  UiActionSheetItem,
  UiActionSheetCancel,
} from '../components/ui/action-sheet';
import { UiButton } from '../components/ui/button';

@Component({
  selector: 'app-share-menu',
  imports: [
    UiActionSheet,
    UiActionSheetTitle,
    UiActionSheetItem,
    UiActionSheetCancel,
    UiButton,
  ],
  template: `
    <ui-button (pressed)="open.set(true)">Share</ui-button>

    <ui-action-sheet [(open)]="open">
      <ui-action-sheet-title>Share this document</ui-action-sheet-title>
      <ui-action-sheet-item (pressed)="copyLink()">
        Copy link
      </ui-action-sheet-item>
      <ui-action-sheet-item (pressed)="sendEmail()">
        Send by email
      </ui-action-sheet-item>
      <ui-action-sheet-item variant="destructive" (pressed)="remove()">
        Delete
      </ui-action-sheet-item>
      <ui-action-sheet-cancel />
    </ui-action-sheet>
  `,
})
export class ShareMenu {
  protected readonly open = signal(false);

  protected copyLink(): void {}
  protected sendEmail(): void {}
  protected remove(): void {}
}
  • [(open)] two-way binds a signal. Set it to true to slide the sheet up.
  • Tapping an item emits its pressed, then closes the sheet. You don't need to set open yourself.
  • Tapping Cancel or the backdrop closes the sheet without emitting anything.
  • The title and items stack in one rounded group. <ui-action-sheet-cancel> always renders as its own tile below the group, wherever you put it.
  • Items and the title render their content in a <text>, so put plain text in them.
  • The sheet draws in an <overlay>, so it covers the whole screen wherever it sits in your template.

Destructive items

variant="destructive" colors an item's text red, for actions such as delete.

<ui-action-sheet-item variant="destructive" (pressed)="remove()">
  Delete
</ui-action-sheet-item>

pressed fires inside the tap. Changing signals there is safe, but router navigation isn't: wrap it in setTimeout. See the setTimeout rule for why.

readonly #router = inject(Router);

protected openSettings(): void {
  setTimeout(() => this.#router.navigateByUrl('/settings'));
}

You can also wait for closed, which emits after the slide-down animation, from a timer.

Cancel label

<ui-action-sheet-cancel label="Not now" />

API

UiActionSheet

InputTypeDefaultDescription
openbooleanfalseWhether the sheet is shown. Supports [(open)].
classstring''Extra Tailwind classes for the column that holds the group and Cancel.
OutputTypeDescription
openChangebooleanEmits false when an item, Cancel or the backdrop closes the sheet. Part of [(open)].
closedvoidEmits after the close animation finishes.
MethodDescription
close()Closes the sheet.

UiActionSheetItem

InputTypeDefaultDescription
variant'default' | 'destructive''default'Text color.
OutputTypeDescription
pressedvoidEmits when the item is tapped, before the sheet closes.

UiActionSheetCancel

InputTypeDefaultDescription
labelstring'Cancel'The button's text.

No outputs. Tapping it closes the sheet.

All sub-components (UiActionSheetTitle, UiActionSheetItem, UiActionSheetCancel) accept class. Items and Cancel only work inside a <ui-action-sheet>, because they close it.