Skip to main content

Documentation

NotificationBulkActions

Toolbar for selecting and performing bulk actions on notifications

Tags: component notification bulk actions toolbar

View Demo

Toolbar for selecting multiple notifications and performing bulk actions like mark as read, mark as unread, and delete.

Import​

Notification components are available via a separate entry point to make @classic-homes/notifications an optional dependency.

<script>
  import { NotificationBulkActions } from '@classic-homes/theme-svelte/notifications';
</script>

Note: Using notification components requires @classic-homes/notifications to be installed.

Basic Usage​

<script>
  let selectedIds = $state(new Set<string>());
  const notifications = [...];
</script>

<NotificationBulkActions
  selectedCount={selectedIds.size}
  totalCount={notifications.length}
  allSelected={selectedIds.size === notifications.length}
  onSelectAll={(selected) => {
    /* ... */
  }}
  onMarkRead={() => {
    /* ... */
  }}
  onMarkUnread={() => {
    /* ... */
  }}
  onDelete={() => {
    /* ... */
  }}
/>

Props​

Prop Type Default Description
selectedCount number required Number of currently selected notifications
totalCount number required Total number of notifications available
allSelected boolean false Whether all notifications are selected
onSelectAll (selected: boolean) => void - Callback to select/deselect all
onMarkRead () => void - Callback to mark selected as read
onMarkUnread () => void - Callback to mark selected as unread
onDelete () => void - Callback to delete selected
onClearSelection () => void - Callback to clear selection
class string - Additional CSS classes

Visual States​

No Selection​

When no items are selected, only the "Select all" checkbox is shown:

<NotificationBulkActions selectedCount={0} totalCount={10} allSelected={false} />

Partial Selection​

When some items are selected, action buttons appear:

<NotificationBulkActions selectedCount={3} totalCount={10} allSelected={false} />
<!-- Shows: "3 of 10 selected" with Clear button and action buttons -->

All Selected​

When all items are selected:

<NotificationBulkActions selectedCount={10} totalCount={10} allSelected={true} />

Actions​

Select All​

Toggle selection of all visible notifications:

<script>
  function handleSelectAll(selected: boolean) {
    if (selected) {
      selectedIds = new Set(filteredNotifications().map(n => n.id));
    } else {
      selectedIds = new Set();
    }
  }
</script>

<NotificationBulkActions
  onSelectAll={handleSelectAll}
  {...}
/>

Mark as Read​

Mark all selected notifications as read:

<script>
  function handleBulkMarkRead() {
    const count = selectedIds.size;
    notifications = notifications.map(n =>
      selectedIds.has(n.id)
        ? { ...n, read: true, readAt: new Date().toISOString() }
        : n
    );
    selectedIds = new Set();
    toastStore.success(`Marked ${count} notifications as read`);
  }
</script>

<NotificationBulkActions
  onMarkRead={handleBulkMarkRead}
  {...}
/>

Mark as Unread​

Mark all selected notifications as unread:

<script>
  function handleBulkMarkUnread() {
    const count = selectedIds.size;
    notifications = notifications.map(n =>
      selectedIds.has(n.id)
        ? { ...n, read: false, readAt: undefined }
        : n
    );
    selectedIds = new Set();
    toastStore.info(`Marked ${count} notifications as unread`);
  }
</script>

<NotificationBulkActions
  onMarkUnread={handleBulkMarkUnread}
  {...}
/>

Delete​

Delete all selected notifications:

<script>
  function handleBulkDelete() {
    const count = selectedIds.size;
    notifications = notifications.filter(n => !selectedIds.has(n.id));
    selectedIds = new Set();
    toastStore.info(`Deleted ${count} notifications`);
  }
</script>

<NotificationBulkActions
  onDelete={handleBulkDelete}
  {...}
/>

Examples​

Complete Implementation​

<script>
  import { toastStore } from '@classic-homes/theme-svelte';
  import {
    NotificationList,
    NotificationFilters,
    NotificationBulkActions,
  } from '@classic-homes/theme-svelte/notifications';
  import type { Notification, NotificationFilter, NotificationCounts } from '@classic-homes/notifications/core';

  let notifications = $state<Notification[]>([...]);
  let filter = $state<NotificationFilter>('all');
  let selectedIds = $state(new Set<string>());

  const filteredNotifications = $derived(() => {
    switch (filter) {
      case 'unread': return notifications.filter(n => !n.read);
      case 'read': return notifications.filter(n => n.read);
      default: return notifications;
    }
  });

  const counts = $derived<NotificationCounts>({
    all: notifications.length,
    unread: notifications.filter(n => !n.read).length,
    read: notifications.filter(n => n.read).length,
  });

  const allSelected = $derived(
    filteredNotifications().length > 0 &&
    filteredNotifications().every(n => selectedIds.has(n.id))
  );

  function handleToggleSelect(id: string, selected: boolean) {
    const newSet = new Set(selectedIds);
    if (selected) newSet.add(id);
    else newSet.delete(id);
    selectedIds = newSet;
  }

  function handleSelectAll(selected: boolean) {
    if (selected) {
      selectedIds = new Set(filteredNotifications().map(n => n.id));
    } else {
      selectedIds = new Set();
    }
  }

  function handleClearSelection() {
    selectedIds = new Set();
  }

  function handleBulkMarkRead() {
    notifications = notifications.map(n =>
      selectedIds.has(n.id)
        ? { ...n, read: true, readAt: new Date().toISOString() }
        : n
    );
    selectedIds = new Set();
    toastStore.success('Marked as read');
  }

  function handleBulkMarkUnread() {
    notifications = notifications.map(n =>
      selectedIds.has(n.id)
        ? { ...n, read: false, readAt: undefined }
        : n
    );
    selectedIds = new Set();
    toastStore.info('Marked as unread');
  }

  function handleBulkDelete() {
    notifications = notifications.filter(n => !selectedIds.has(n.id));
    selectedIds = new Set();
    toastStore.info('Deleted');
  }
</script>

<NotificationFilters bind:filter {counts} onFilterChange={() => (selectedIds = new Set())} />

{#if filteredNotifications().length > 0}
  <NotificationBulkActions
    selectedCount={selectedIds.size}
    totalCount={filteredNotifications().length}
    {allSelected}
    onSelectAll={handleSelectAll}
    onMarkRead={handleBulkMarkRead}
    onMarkUnread={handleBulkMarkUnread}
    onDelete={handleBulkDelete}
    onClearSelection={handleClearSelection}
  />
{/if}

<NotificationList
  notifications={filteredNotifications()}
  {selectedIds}
  selectable={true}
  onToggleSelect={handleToggleSelect}
/>
On this page