Notifications

A notification feed with dismissible items, unread badges, and clear-all action.

Preview

Notifications5
Welcome!Your account has been created successfully.2 min ago

New commentAlice replied to your post.15 min ago

New followerBob started following you.1 hour ago

ReminderYour subscription renews tomorrow.3 hours ago

Update availableng-oat v2.1 is now available.1 day ago

html
<div class="max-w-lg">
  <div class="card">
    <header class="hstack justify-between">
      <div class="hstack gap-2">
        <strong>Notifications</strong>
        <ng-oat-badge variant="secondary">{{ notifications().length }}</ng-oat-badge>
      </div>
      <button class="btn ghost small" (click)="clearAll()">Clear all</button>
    </header>
    <div class="vstack gap-1">
      @for (item of notifications(); track item.id) {
        <div class="hstack gap-3 items-start p-2">
          <ng-oat-icon [icon]="item.icon" [size]="18" />
          <div class="vstack gap-1 flex-1">
            <strong class="text-small">{{ item.title }}</strong>
            <span class="text-small text-light">{{ item.description }}</span>
            <small class="text-light">{{ item.time }}</small>
          </div>
          <button class="btn ghost small" (click)="dismiss(item.id)"><ng-oat-icon [icon]="iconX" [size]="14" /></button>
        </div>
        <hr />
      } @empty {
        <p class="text-center text-light py-4">No notifications</p>
      }
    </div>
  </div>
</div>

SFC Source

typescript
import { Component, signal } from '@angular/core';
import { NgOatBadge } from '@letsprogram/ng-oat/badge';
import { NgOatIcon, type NgOatIconData, iconConfetti, iconMessage, iconStar, iconX } from '@letsprogram/ng-oat/icons';

@Component({
  selector: 'app-notifications',
  imports: [NgOatBadge, NgOatIcon],
  template: `
    <div class="max-w-lg">
      <div class="card">
        <header class="hstack justify-between">
          <div class="hstack gap-2">
            <strong>Notifications</strong>
            <ng-oat-badge variant="secondary">{{ notifications().length }}</ng-oat-badge>
          </div>
          <button class="btn ghost small" (click)="clearAll()">Clear all</button>
        </header>
        <div class="vstack gap-1">
          @for (item of notifications(); track item.id) {
            <div class="hstack gap-3 items-start p-2">
              <ng-oat-icon [icon]="item.icon" [size]="18" />
              <div class="vstack gap-1 flex-1">
                <strong class="text-small">{{ item.title }}</strong>
                <span class="text-small text-light">{{ item.description }}</span>
                <small class="text-light">{{ item.time }}</small>
              </div>
              <button class="btn ghost small" (click)="dismiss(item.id)"><ng-oat-icon [icon]="iconX" [size]="14" /></button>
            </div>
            <hr />
          } @empty {
            <p class="text-center text-light py-4">No notifications</p>
          }
        </div>
      </div>
    </div>
  `,
})
export class NotificationsComponent {
  iconX = iconX;

  notifications = signal<{ id: number; icon: NgOatIconData; title: string; description: string; time: string }[]>([
    { id: 1, icon: iconConfetti, title: 'Welcome!', description: 'Account created.', time: '2 min ago' },
    { id: 2, icon: iconMessage, title: 'New comment', description: 'Alice replied.', time: '15 min ago' },
    { id: 3, icon: iconStar, title: 'New follower', description: 'Bob followed you.', time: '1 hour ago' },
  ]);

  dismiss(id: number) {
    this.notifications.update(list => list.filter(n => n.id !== id));
  }

  clearAll() {
    this.notifications.set([]);
  }
}