Action Panel

The <smart-action-panel> component renders a standalone panel with a heading, description and one or more actions (buttons or links). It is typically used to surface a small "card" of contextual settings (e.g. Manage subscription, Update your email, Renew automatically). It follows the Base + Standard + Wrapper pattern with an InjectionToken-based extension mechanism. The abstract ActionPanelBaseComponent defines the shared API — options (IActionPanelOptions), cssClass (alias class), and the actionClick output. ActionPanelStandardComponent is a barebones placeholder using native <section>, <h3>, <p>, <a>, and <button> elements. ActionPanelComponent is the public wrapper that renders ActionPanelStandardComponent by default and accepts a custom replacement via ACTION_PANEL_STANDARD_COMPONENT_TOKEN.


Usage

<smart-action-panel [options]="options" (actionClick)="onActionClick($event)" />

Components

ActionPanelComponent (<smart-action-panel>)

Main wrapper. Delegates to ActionPanelStandardComponent by default. When ACTION_PANEL_STANDARD_COMPONENT_TOKEN is provided, renders the injected component via NgComponentOutlet. Re-emits actionClick.

ActionPanelStandardComponent (<smart-action-panel-standard>)

Barebones placeholder using native HTML. Renders an outer wrapper with cssClass, a <section class="action-panel"> containing an optional <h3>{{ options.title }}</h3>, an optional description (string options.description rendered as <p class="description"> or options.descriptionTpl rendered via NgTemplateOutlet), an optional <div class="content"> slot from options.contentTpl, and an optional <div class="actions"> row of <button class="action variant-{variant}"> (or <a> when action.href is provided) per options.actions entry that emits { actionId } via actionClick.

ActionPanelBaseComponent (abstract)

Abstract base directive. Exposes:

  • options: InputSignal<IActionPanelOptions | undefined>
  • cssClass: InputSignal<string> (alias class)
  • actionClick: OutputEmitterRef<IActionPanelActionClick>

IActionPanelActionClick = { actionId: string }.

API

Inputs

InputTypeDefaultDescription
optionsInputSignal<IActionPanelOptions | undefined>-Panel configuration
classInputSignal<string>''External CSS classes (alias for cssClass)

Outputs

OutputTypeDescription
actionClickOutputEmitterRef<IActionPanelActionClick>Emitted when a button-type action is clicked

Extending the base class

import {
  ChangeDetectionStrategy,
  Component,
  input,
  ViewEncapsulation,
} from '@angular/core';

import {
  ActionPanelBaseComponent,
  ActionPanelComponent,
  ACTION_PANEL_STANDARD_COMPONENT_TOKEN,
  IActionPanelOptions,
} from '@smartsoft001/angular';

@Component({
  selector: 'docs-custom-action-panel',
  template: `
    <section class="docs-action-panel" [class]="cssClass()">
      @if (options()?.title) {
        <h3 class="docs-action-panel__title">{{ options()!.title }}</h3>
      }
      @if (options()?.description) {
        <p class="docs-action-panel__description">
          {{ options()!.description }}
        </p>
      }
      @for (action of options()?.actions ?? []; track action.id) {
        <button
          type="button"
          class="docs-action-panel__action"
          (click)="actionClick.emit({ actionId: action.id })"
        >
          {{ action.label }}
        </button>
      }
    </section>
  `,
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CustomActionPanelComponent extends ActionPanelBaseComponent {
  // NgComponentOutlet passes 'cssClass' by canonical name, not the 'class' alias.
  override cssClass = input<string>('');
}

@Component({
  selector: 'docs-action-panel-custom-example',
  imports: [ActionPanelComponent],
  providers: [
    {
      provide: ACTION_PANEL_STANDARD_COMPONENT_TOKEN,
      useValue: CustomActionPanelComponent,
    },
  ],
  template: `<smart-action-panel [options]="options" />`,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ActionPanelCustomExampleComponent {
  options: IActionPanelOptions = {
    layout: 'simple',
    title: 'Transfer ownership',
    description: 'Move this project to another workspace member.',
    actions: [
      { id: 'transfer', label: 'Transfer', variant: 'primary' },
      { id: 'cancel', label: 'Cancel' },
    ],
  };
}

Preset

ActionPanelPresetComponent (smart-action-panel-preset, action-panel/preset/) extends ActionPanelStandardComponent and restyles the panel as a bordered card (rounded-xl border bg-white p-4 shadow-2xs sm:p-6 + dark twin). Title uses text-base font-semibold text-gray-900, description text-sm text-gray-500; descriptionTpl overrides the description text. Class recipes live in preset-classes.util.ts (getActionPanel*Classes, not barrel-exported).

All eight SmartActionPanelLayout values are realized through a @switch (default simple):

  • simple / with-input / payment-method — title, description, content slot, then actions stacked below.
  • with-link — actions rendered as text-blue-600 hover:underline links.
  • right-button — flex row, content left, actions right, vertically centered.
  • top-right-button — actions in the title row, right-aligned.
  • with-toggle — content slot placed beside the text (flex row), actions below.
  • well — content and actions wrapped in an inset bg-gray-50 dark:bg-gray-900/50 rounded-lg p-4 panel.

Actions map by variant: primary → solid bg-blue-600 text-white, others → outline border-gray-200 bg-white text-gray-800 + dark. href actions render as <a>, the rest as <button> calling the inherited onActionClick(id) (emits actionClick with the action id). DOM hooks: data-role of panel/title/description/content/actions/well, plus per-action data-role="action" with data-action-id; the panel also carries data-layout.

Unlike the standard component, the preset drops the class input alias (override cssClass = input<string>('')) so the wrapper's canonical componentInputs binding reaches it via NgComponentOutlet.

Register through the token to restyle every <smart-action-panel>:

Gaps: with-toggle / with-input / payment-method only position the content slot — the interactive control (toggle switch, input, card list) is supplied by the caller via contentTpl.

Source

The component lives in packages/shared/angular/src/lib/components/action-panel and is documented for Claude Code by the angular-components-action-panel skill.