126 lines
4.8 KiB
TypeScript
126 lines
4.8 KiB
TypeScript
import { For, Show, type JSX } from "solid-js";
|
||
import { Bell, Settings } from "../../../lib/icons";
|
||
import { notificationItems, unreadNotificationCount } from "../data/shell.data";
|
||
import styles from "./NotificationsMenu.module.scss";
|
||
|
||
type NotificationsMenuProps = {
|
||
id: string;
|
||
menuRef?: (element: HTMLDivElement) => void;
|
||
onSelect: () => void;
|
||
variant?: "popover" | "workspace";
|
||
};
|
||
|
||
export const NotificationsMenu = (props: NotificationsMenuProps): JSX.Element => {
|
||
const unreadItems = notificationItems.filter((item) => item.unread);
|
||
const earlierItems = notificationItems.filter((item) => !item.unread);
|
||
const hasNotifications = notificationItems.length > 0;
|
||
const isCaughtUp = unreadItems.length === 0 && hasNotifications;
|
||
const variant = props.variant ?? "popover";
|
||
|
||
return (
|
||
<div
|
||
id={props.id}
|
||
classList={{
|
||
[styles.menu]: true,
|
||
[styles.menuWorkspace]: variant === "workspace",
|
||
}}
|
||
role="menu"
|
||
aria-label="Notifications"
|
||
ref={props.menuRef}
|
||
data-ui="notifications-menu"
|
||
data-variant={variant}
|
||
>
|
||
<div class={styles.header} data-slot="notifications-header">
|
||
<div class={styles.headerCopy} data-slot="notifications-header-copy">
|
||
<strong class={styles.title}>Notifications</strong>
|
||
<span class={styles.subtitle}>
|
||
{unreadNotificationCount > 0
|
||
? `You have ${unreadNotificationCount} unread`
|
||
: "You’re all caught up"}
|
||
</span>
|
||
</div>
|
||
|
||
<Show when={unreadNotificationCount > 0}>
|
||
<button type="button" role="menuitem" class={styles.headerAction} onClick={props.onSelect}>
|
||
Mark all read
|
||
</button>
|
||
</Show>
|
||
</div>
|
||
|
||
<div class={styles.listWrap} data-slot="notifications-body">
|
||
<Show when={!hasNotifications}>
|
||
<div class={styles.stateCard}>
|
||
<span class={styles.stateIcon} aria-hidden="true">
|
||
<Bell size={18} strokeWidth={2} />
|
||
</span>
|
||
<strong class={styles.stateTitle}>No notifications yet</strong>
|
||
<span class={styles.stateCopy}>When activity starts across your workspace, it’ll show up here.</span>
|
||
</div>
|
||
</Show>
|
||
|
||
<Show when={isCaughtUp}>
|
||
<div class={styles.stateCard}>
|
||
<span class={styles.stateIcon} aria-hidden="true">
|
||
<Bell size={18} strokeWidth={2} />
|
||
</span>
|
||
<strong class={styles.stateTitle}>You’re all caught up</strong>
|
||
<span class={styles.stateCopy}>No unread notifications right now. Earlier activity is still available below.</span>
|
||
</div>
|
||
</Show>
|
||
|
||
<Show when={unreadItems.length > 0}>
|
||
<section class={styles.section} aria-label="Unread notifications" data-slot="notifications-section" data-section-id="unread">
|
||
<span class={styles.sectionLabel}>Unread</span>
|
||
<div class={styles.list} data-slot="notifications-list" data-section-id="unread">
|
||
<For each={unreadItems}>
|
||
{(item): JSX.Element => (
|
||
<button type="button" role="menuitem" classList={{ [styles.item]: true, [styles.itemUnread]: true }} data-slot="notification-item" data-state="unread" onClick={props.onSelect}>
|
||
<span class={styles.itemMarker} aria-hidden="true" />
|
||
<div class={styles.itemBody}>
|
||
<span class={styles.itemTitle}>{item.title}</span>
|
||
<span class={styles.itemMeta}>{item.contextLabel}</span>
|
||
</div>
|
||
<span class={styles.itemTime}>{item.timeLabel}</span>
|
||
</button>
|
||
)}
|
||
</For>
|
||
</div>
|
||
</section>
|
||
</Show>
|
||
|
||
<Show when={earlierItems.length > 0}>
|
||
<section class={styles.section} aria-label="Earlier notifications" data-slot="notifications-section" data-section-id="earlier">
|
||
<span class={styles.sectionLabel}>Earlier</span>
|
||
<div class={styles.list} data-slot="notifications-list" data-section-id="earlier">
|
||
<For each={earlierItems}>
|
||
{(item): JSX.Element => (
|
||
<button type="button" role="menuitem" class={styles.item} data-slot="notification-item" data-state="read" onClick={props.onSelect}>
|
||
<span class={styles.itemMarkerMuted} aria-hidden="true" />
|
||
<div class={styles.itemBody}>
|
||
<span class={styles.itemTitle}>{item.title}</span>
|
||
<span class={styles.itemMeta}>{item.contextLabel}</span>
|
||
</div>
|
||
<span class={styles.itemTime}>{item.timeLabel}</span>
|
||
</button>
|
||
)}
|
||
</For>
|
||
</div>
|
||
</section>
|
||
</Show>
|
||
</div>
|
||
|
||
<div class={styles.footer} data-slot="notifications-footer">
|
||
<button type="button" role="menuitem" class={styles.footerAction} onClick={props.onSelect}>
|
||
<Settings size={16} strokeWidth={2} />
|
||
<span>Notification settings</span>
|
||
</button>
|
||
|
||
<button type="button" role="menuitem" class={styles.footerAction} onClick={props.onSelect}>
|
||
<Bell size={16} strokeWidth={2} />
|
||
<span>View all notifications</span>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|