テーマカラーの定義を更新しました

最新のCSSを確認

Notification

Notification は、お知らせ(通知)一覧を表示するコンポーネントです。
未読・既読を分けて表示でき、未読と既読の両方が存在する場合は区切りとして「新規」を表示します。
各通知のレイアウトは itemContent を渡して自由に構成できます。

notification/default is coming soon.

プロパティ

Notificationは、以下のプロパティをサポートしています。

名前 デフォルト値 説明
notifications NotificationItem[] 表示する通知一覧です。
itemContent Snippet<[NotificationItem]> 表示内容を描画する Snippet です(引数に notification を受け取ります)。

NotificationItemは、以下のプロパティをサポートしています。

名前 デフォルト値 説明
unread boolean 未読かどうかを表します。
message string 通知本文です。
createdAt string 作成日です。
icon any アイコンコンポーネントです。
iconImage string アイコン画像 URL です。
onClick () => void クリック時に呼ばれるコールバック関数です。

インストールの手順

以下のコンポーネントのコードを、使いたいプロジェクトにコピー&ペーストします。
パスは実際のプロジェクトの構成にあわせて更新します。

modules/Notification.svelte
        <!--
@component
## 概要
- お知らせ一覧を表示するコンポーネントです

## 機能
- お知らせ一覧を表示

## Props
- notification: お知らせデータ
- itemContent: 表示内容を描画する snippet

## Usage
```svelte
  <Notification {notifications} >
    {#snippet itemContent(notification)}
    {/snippet}
  </Notification>
```
-->

<script module lang="ts">
  import type { Snippet } from 'svelte';

  export interface NotificationProps {
    /** 通知一覧 */
    notifications: NotificationItem[];
    itemContent: Snippet<[NotificationItem]>;
  }

  export interface NotificationItem {
    /** 未読かどうか */
    unread: boolean;
    /** テキスト */
    message: string;
    /** 作成日 */
    createdAt: string;
    /** アイコン */
    icon?: any;
    /** アイコンイメージ */
    iconImage?: string;
    /** コールバック関数 */
    onClick?: () => void;
  }
</script>

<script lang="ts">
  let { notifications, itemContent }: NotificationProps = $props();

  // 未読→既読の順で並べ替え
  const sorted = $derived([...notifications].sort((a, b) => Number(b.unread) - Number(a.unread)));

  // 既読が始まる最初のindex(未読しかないなら -1)
  const firstReadIndex = $derived(sorted.findIndex((notification) => !notification.unread));

  // 未読の最後のindex(未読/既読が両方ある時だけ有効)
  const lastUnreadIndex = $derived(firstReadIndex > 0 ? firstReadIndex - 1 : -1);

  // 既読の最後のindex
  const lastIndex = $derived(sorted.length - 1);
</script>

<div class="w-80 max-h-72 bg-surface border border-border rounded-md overflow-hidden overflow-y-auto" data-rabee-ui="notification">
  {#if notifications.length}
    {#each sorted as notification, index}
      {#if index === firstReadIndex && firstReadIndex !== 0}
        <!-- 未読と既読の区切り(未読も既読もある時だけ表示になる) -->
        <div class="relative h-px -mt-px">
          <span class="absolute top-1/2 left-1/2 px-1.5 text-destructive text-xs pointer-events-none -translate-x-1/2 -translate-y-1/2">
            新規
          </span>
          <div class="absolute top-1/2 right-1/2 left-0 h-px bg-destructive mr-4 -translate-y-1/2"></div>
          <div class="absolute top-1/2 right-0 left-1/2 h-px bg-destructive ml-4 -translate-y-1/2"></div>
        </div>
      {/if}

      <!-- 1件分の通知行(borderの有無を index に応じて切り替え) -->
      <div class={[index === lastIndex || index === lastUnreadIndex ? 'border-b-0' : 'border-b border-border/50']}>
        <!-- 未読/既読でレイアウトを変更 -->
        <button class={['size-full outline-ring cursor-pointer focus-visible:relative focus-visible:z-10 focus-visible:rounded-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring', notification.unread ? 'bg-primary/5 hover:bg-primary/10' : 'hover:bg-accent/90']} type="button" onclick={() => notification.onClick?.()}>
          <!-- 通知1件分の表示内容(レイアウト含めて使用側の itemContent に委譲) -->
          {@render itemContent(notification)}
        </button>
      </div>
    {/each}
  {:else}
    <!-- empty -->
    <div class="p-2">
      <div class="flex items-center justify-center w-full py-16 bg-muted/10 rounded-lg">
        <p class="text-foreground/70 text-xs">まだ通知がありません</p>
      </div>
    </div>
  {/if}
</div>

      

使い方

notification/default is coming soon.


サンプル

Default

Notification の基本的な表示サンプルです。
各通知の表示内容は itemContent の Snippet で構成し、アイコン・アバター、本文、日時などを自由にレイアウトできます。

notification/default is coming soon.

Read-Unread

Notification の 未読/既読表示のサンプルです。
未読の通知は上側にまとめて表示し、既読の通知は下側に表示します。
未読と既読の両方が存在する場合は、間に「新規」の区切りを表示します。

notification/read-unread is coming soon.