style: improve the ui of the toast for notifications

This commit is contained in:
Nathnael
2026-07-06 10:38:27 +00:00
parent abe26c2c60
commit ca3a6072d8
7 changed files with 244 additions and 38 deletions

View File

@@ -0,0 +1,141 @@
import { ActionIcon, Box, Group, Stack, Text, ThemeIcon } from "@mantine/core";
import { Bell, ChevronRight, X } from "lucide-react";
import { timeAgo } from "./timeAgo";
import type { NotificationItemData, NotificationVisual } from "./types";
export interface NotificationToastProps {
item: NotificationItemData;
/** Resolved by the hosting app's registry from `item.type`/`item.data`. */
visual?: NotificationVisual;
/** react-hot-toast enter/exit flag; drives the slide/fade animation. */
visible?: boolean;
/** Whole-card click (usually: mark read + navigate). Shows a chevron affordance. */
onClick?: () => void;
onDismiss?: () => void;
width?: number;
}
/**
* Rich, ERP-grade notification toast: a colored-accent card with an icon, a
* bold title, a two-line body, a relative timestamp, and a dismiss button.
* Presentational — the host resolves the visual and click action and fires it
* via `toast.custom`.
*/
export function NotificationToast({
item,
visual,
visible = true,
onClick,
onDismiss,
width = 380,
}: NotificationToastProps) {
const color = visual?.color ?? "blue";
const icon = visual?.icon ?? <Bell size={18} strokeWidth={2} />;
const clickable = Boolean(onClick);
return (
<Box
style={{
position: "relative",
width,
maxWidth: "calc(100vw - 32px)",
display: "flex",
overflow: "hidden",
borderRadius: "var(--mantine-radius-md)",
background: "var(--mantine-color-body)",
border: "1px solid var(--mantine-color-default-border)",
boxShadow: "var(--mantine-shadow-lg)",
pointerEvents: "auto",
transform: visible ? "translateX(0)" : "translateX(16px)",
opacity: visible ? 1 : 0,
transition: "transform 180ms ease, opacity 180ms ease",
}}
>
{/* colored accent rail */}
<Box
aria-hidden
style={{
width: 4,
flexShrink: 0,
background: `var(--mantine-color-${color}-6)`,
}}
/>
<Box
role={clickable ? "button" : undefined}
tabIndex={clickable ? 0 : undefined}
onClick={onClick}
onKeyDown={(e) => {
if (clickable && (e.key === "Enter" || e.key === " ")) {
e.preventDefault();
onClick?.();
}
}}
p="sm"
style={{ flex: 1, minWidth: 0, cursor: clickable ? "pointer" : "default" }}
>
<Group align="flex-start" wrap="nowrap" gap="sm">
<ThemeIcon
variant="light"
color={color}
radius="xl"
size={40}
style={{ flexShrink: 0 }}
>
{icon}
</ThemeIcon>
<Stack gap={2} style={{ flex: 1, minWidth: 0 }}>
<Text
fw={600}
size="sm"
lineClamp={1}
pr={onDismiss ? 20 : 0}
>
{item.title}
</Text>
<Text c="dimmed" size="xs" lineClamp={2} style={{ lineHeight: 1.4 }}>
{item.body}
</Text>
<Group justify="space-between" wrap="nowrap" gap={6} mt={4}>
<Text c="dimmed" size="xs" style={{ whiteSpace: "nowrap" }}>
{timeAgo(item.createdAt)}
</Text>
{clickable && (
<Group gap={2} wrap="nowrap">
<Text size="xs" fw={600} c={`${color}.6`}>
View details
</Text>
<ChevronRight
size={13}
color={`var(--mantine-color-${color}-6)`}
/>
</Group>
)}
</Group>
</Stack>
</Group>
</Box>
{onDismiss && (
<ActionIcon
variant="subtle"
color="gray"
size="sm"
radius="xl"
aria-label="Dismiss"
onClick={(e) => {
e.stopPropagation();
onDismiss();
}}
style={{ position: "absolute", top: 6, right: 6 }}
>
<X size={14} />
</ActionIcon>
)}
</Box>
);
}
export default NotificationToast;

View File

@@ -7,6 +7,9 @@ export type { NotificationItemProps } from "./NotificationItem";
export { NotificationDrawer } from "./NotificationDrawer";
export type { NotificationDrawerProps } from "./NotificationDrawer";
export { NotificationToast } from "./NotificationToast";
export type { NotificationToastProps } from "./NotificationToast";
export type {
NotificationItemData,
NotificationVisual,

View File

@@ -32,11 +32,13 @@ export {
NotificationBell,
NotificationItem,
NotificationDrawer,
NotificationToast,
} from "./components/NotificationBell";
export type {
NotificationBellProps,
NotificationItemProps,
NotificationDrawerProps,
NotificationToastProps,
NotificationItemData,
NotificationVisual,
ResolveNotificationVisual,