Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

NotificationMenu

A bell-anchored dropdown listing notifications — each row pairs a StatusPip severity with inline acknowledge / archive / delete actions that never close the menu.

Import

NotificationMenu is a registry composite — you own the source after installing:

npx ploxum add notification-menu
import { NotificationMenu, type NotificationItem } from "@/components/notification-menu";

Depends on @roger-emerson/ploxum-icons (Archive, Check, Trash2), @roger-emerson/ploxum-primitives (DropdownMenu, EmptyState, StatusPip) and @roger-emerson/ploxum-utils (cn).

Usage

const notifications: NotificationItem[] = [
  {
    id: "n1",
    severity: "critical",
    title: "Ticketing API p99 breached 500ms",
    source: "Ingress",
    timestamp: "12:04",
    read: false,
  },
  {
    id: "n2",
    severity: "warning",
    title: "Camera mesh down to 47/48",
    source: "Cameras",
    timestamp: "11:58",
    read: true,
  },
];
 
<NotificationMenu
  notifications={notifications}
  onAcknowledge={(id) => ack(id)}
  onArchive={(id) => archive(id)}
  onDelete={(id) => remove(id)}
  onMarkAllRead={() => markAllRead()}
  trigger={
    <button aria-label="Notifications" type="button">
      <BellIcon />
    </button>
  }
/>;

The unread count is derived from notifications.filter((n) => !n.read) and shown as an amber pill in the header. Unread rows carry an amber left border + tint wash. With an empty list the menu renders an EmptyState. A row action is omitted entirely when its handler prop is absent.

Props

NotificationMenu

PropTypeDefaultDescription
notificationsNotificationItem[]Rows, in display order.
triggerReactNodeCaller-owned bell + badge, passed to DropdownMenu.Trigger asChild. Required.
onAcknowledge(id: string) => voidRenders the acknowledge (check) action when present.
onArchive(id: string) => voidRenders the archive action when present.
onDelete(id: string) => voidRenders the delete (danger) action when present.
onMarkAllRead() => voidRenders the header "Mark all read" button when present and there are unread rows.
widthnumber360Content width in px.

NotificationItem

FieldTypeDescription
idstringStable key; passed to every row action handler.
severityStatusPipStatus"live" | "ok" | "warning" | "critical" | "offline" | "unknown". Renders a leading StatusPip with a mapped label.
titlestringHeadline, truncates on overflow.
sourcestringOrigin subsystem, shown in the meta line.
timestampstringPre-formatted time, rendered tabular-nums in the meta line.
readboolean?Unread (false/omitted) rows get the amber border + tint and bold title; counted toward the unread badge.

Design rules

  • Rows are bespoke <div>s, not DropdownMenu.Item. This keeps the inline acknowledge / archive / delete buttons from selecting the row or closing the menu — each action stopPropagations and calls its handler with the row id.
  • Critical and other high-severity rows render motionless. Only the dropdown Content open/close is animated (the sanctioned palette-style flourish); rows never animate on state change (P1 rule).
  • Delete is the danger action — Trash2 icon, danger-tinted hover. It sits last in the action cluster and is icon-only, distinct from the neutral acknowledge/archive actions.
  • Status is StatusPip shape + color, never hue alone; the pip carries the mapped severity label (Live / Operational / Warning / Critical / Offline / Unknown).

Storybook

Composites/NotificationMenu ↗