Grid List

The <smart-grid-list> component renders a grid of card-like records with optional title, description, per-item icon/image, link, badge, action template, and bottom footer slot. It follows the Base + Standard + Wrapper pattern with an InjectionToken-based extension mechanism. The abstract GridListBaseComponent defines the shared API — optional IGridListOptions and cssClass (alias class). GridListStandardComponent is a barebones placeholder concrete implementation. GridListComponent is the public wrapper that renders GridListStandardComponent by default and accepts a custom replacement via GRID_LIST_STANDARD_COMPONENT_TOKEN.


Usage

<smart-grid-list [options]="options" />

Components

GridListComponent (<smart-grid-list>)

Main wrapper component. Renders GridListStandardComponent by default. When GRID_LIST_STANDARD_COMPONENT_TOKEN is provided, renders the injected component via NgComponentOutlet.

GridListStandardComponent (<smart-grid-list-standard>)

Barebones placeholder concrete implementation. Renders a wrapper <div> containing an optional <h3 class="title">, optional <p class="description">, and a <ul role="list"> with one <li class="item"> per item. Each item renders the icon/image (icon template wins over image URL), a body group with the title (rendered as <a class="title"> if href is provided, otherwise <span class="title">) and optional description, plus optional badge/action template slots. When the item list is empty, the optional emptyTpl is rendered inside <div class="empty">. A bottom footerTpl renders inside <div class="footer">. The external cssClass is applied to the root wrapper. It does not include any visual styling — it exists solely as the default structural placeholder until a custom implementation is registered through the token.

GridListBaseComponent (abstract)

Abstract base directive for extending custom grid-list implementations. Exposes options as an InputSignal<IGridListOptions | undefined> and cssClass as an InputSignal<string> (with alias class).

API

Inputs

InputTypeDefaultDescription
optionsInputSignal<IGridListOptions | undefined>-Optional configuration (title, description, items, columns, gap, layout, slots)
classInputSignal<string>''External CSS classes (alias for cssClass)

IGridListOptions

All properties are optional except IGridListItem.title. The default GridListStandardComponent consumes every property; a section is rendered only when its template/string is provided. Within an item, iconTpl takes precedence over imageUrl when both are set. columns, gap and layout are hints for custom implementations registered via the token.

GRID_LIST_STANDARD_COMPONENT_TOKEN

InjectionToken that allows replacing the default GridListStandardComponent with a custom implementation. Provide a Type<GridListBaseComponent> to override.

Extending the base class

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

import {
  GRID_LIST_STANDARD_COMPONENT_TOKEN,
  GridListBaseComponent,
  GridListComponent,
  IGridListOptions,
} from '@smartsoft001/angular';

@Component({
  selector: 'docs-custom-grid-list',
  template: `
    <div [class]="containerClasses()">
      @if (options()?.title) {
        <h3 class="docs-grid-list__title">{{ options()?.title }}</h3>
      }

      <ul
        class="docs-grid-list__items"
        [attr.data-columns]="options()?.columns ?? null"
      >
        @for (item of options()?.items ?? []; track item.id ?? $index) {
          <li class="docs-grid-list__item">
            @if (item.imageUrl) {
              <img
                class="docs-grid-list__image"
                [src]="item.imageUrl"
                [attr.alt]="item.imageAlt ?? ''"
              />
            }
            @if (item.href) {
              <a class="docs-grid-list__link" [attr.href]="item.href">
                {{ item.title }}
              </a>
            } @else {
              <span class="docs-grid-list__label">{{ item.title }}</span>
            }
            @if (item.description) {
              <p class="docs-grid-list__description">{{ item.description }}</p>
            }
          </li>
        }
      </ul>
    </div>
  `,
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CustomGridListComponent extends GridListBaseComponent {
  // NgComponentOutlet passes 'cssClass' by canonical name, not the 'class'
  // alias, so a grid list registered through the token declares it explicitly.
  override cssClass = input<string>('');

  containerClasses = computed(() => {
    const classes = ['docs-grid-list'];
    const extra = this.cssClass();
    if (extra) classes.push(extra);
    return classes.join(' ');
  });
}

@Component({
  selector: 'docs-grid-list-custom-example',
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [GridListComponent],
  // The token swaps the standard grid list for the custom one everywhere
  // below this component, so consumers keep writing `<smart-grid-list>`.
  providers: [
    {
      provide: GRID_LIST_STANDARD_COMPONENT_TOKEN,
      useValue: CustomGridListComponent,
    },
  ],
  template: `<smart-grid-list [options]="options" />`,
})
export class GridListCustomExampleComponent {
  options: IGridListOptions = {
    title: 'Team',
    columns: 3,
    layout: 'cards',
    items: [
      {
        id: 'lindsay',
        title: 'Lindsay Walton',
        description: 'Front-end Developer',
        href: '/team/lindsay-walton',
      },
      { id: 'courtney', title: 'Courtney Henry', description: 'Designer' },
      { id: 'tom', title: 'Tom Cook', description: 'Director of Product' },
    ],
  };
}

Preset

GridListPresetComponent (<smart-grid-list-preset>) is a styled drop-in replacement for the barebones standard component, built entirely with vanilla Tailwind utilities (every class carries the smart: prefix) and explicit smart:dark:* twins in the same template. It extends GridListStandardComponent and reads the same IGridListOptions — no new options fields are introduced.

Layout:

  • Optional title / description render as a header block above the grid.
  • The grid is smart:grid smart:grid-cols-1, widened responsively from columns (smart:sm:grid-cols-2 from sm, then smart:lg:grid-cols-<n> from lg; 1/unset stays single-column) and spaced from gap (sm→smart:gap-3, md/default→smart:gap-4, lg→smart:gap-6).
  • Each item is a bordered rounded card. layout drives the interior arrangement: cards (default) stacks media on top of the body, horizontal places media inline to the left of the body, logos centers a contained logo above a centered caption.
  • Per item: iconTpl (wins over imageUrl) or imageUrl render as the media slot; the title renders as a link with a smart:hover:text-blue-600 accent when href is set, otherwise plain text; badgeTpl sits beside the title; description renders under it; actionTpl renders in a bordered tile footer.
  • Empty items render emptyTpl or a centered default message; footerTpl renders below the grid.

Every zone is addressable via data-role hooks: header, grid, item, media, title, description, badge, action, empty, footer.

Register it through the token to restyle every <smart-grid-list>:

Because GridListComponent forwards inputs canonically through NgComponentOutlet, the preset overrides cssClass to drop the inherited class alias (override cssClass = input<string>('')); the value is merged onto the root wrapper.

Class recipes live in preset/preset-classes.util.ts (getGridListColumnsClasses, getGridListGapClasses, getGridListGridClasses, getGridListTileClasses, getGridListMediaClasses) and are intentionally not re-exported from the barrel to avoid export * collisions.

Documented gaps: layout only affects the tile interior arrangement (not per-item overrides); there is no built-in pagination or selection state.

  • Preset: packages/shared/angular/src/lib/components/grid-list/preset/preset.component.ts

Source

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