@lessly/ui 0.14.1 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +53 -4
- package/dist/index.js +231 -7
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1352,6 +1352,15 @@ interface NotificationProduct {
|
|
|
1352
1352
|
}
|
|
1353
1353
|
declare const severityLabels: Record<NotificationSeverity, string>;
|
|
1354
1354
|
declare function severityBadgeVariant(severity: NotificationSeverity): 'destructive' | 'warning' | 'secondary';
|
|
1355
|
+
/** Per-severity dwell in seconds. `Infinity` means the toast stays until dismissed. */
|
|
1356
|
+
type NotificationToastDwell = Record<NotificationSeverity, number>;
|
|
1357
|
+
/**
|
|
1358
|
+
* Toasts peek, then tuck away — the record behind the bell is the durable copy, so
|
|
1359
|
+
* nothing is lost when a card leaves. Critical is the exception: it stays in the
|
|
1360
|
+
* corner until it is handled, because for an incident the persistent toast IS the
|
|
1361
|
+
* escalation. Timing lives in the toaster; this is only the default table.
|
|
1362
|
+
*/
|
|
1363
|
+
declare const TOAST_DWELL: NotificationToastDwell;
|
|
1355
1364
|
/**
|
|
1356
1365
|
* Reads a stamp as a relative age. `now` is injectable so the components stay
|
|
1357
1366
|
* testable and consumers can substitute an absolute or localized formatter; the
|
|
@@ -1372,9 +1381,8 @@ interface NotificationBellProps {
|
|
|
1372
1381
|
/** Rows shown in the popover. Default 5. */
|
|
1373
1382
|
maxItems?: number;
|
|
1374
1383
|
/**
|
|
1375
|
-
*
|
|
1376
|
-
*
|
|
1377
|
-
* host that renders its own control has the callback the model expects.
|
|
1384
|
+
* Marks a single item read. Supplying it puts a quiet check control on every
|
|
1385
|
+
* unread row — "Read all" and clicking through are no longer the only ways.
|
|
1378
1386
|
*/
|
|
1379
1387
|
onMarkRead?(id: string): void;
|
|
1380
1388
|
onMarkAllRead?(): void;
|
|
@@ -1406,6 +1414,11 @@ interface NotificationCenterProps {
|
|
|
1406
1414
|
onDeepLinkClick?(item: NotificationItem): void;
|
|
1407
1415
|
/** "Read all". Rendered only when supplied. */
|
|
1408
1416
|
onMarkAllRead?(): void;
|
|
1417
|
+
/**
|
|
1418
|
+
* Marks a single item read. Supplying it puts a quiet check control on every
|
|
1419
|
+
* unread row — "Read all" and clicking through are no longer the only ways.
|
|
1420
|
+
*/
|
|
1421
|
+
onMarkRead?(id: string): void;
|
|
1409
1422
|
/** Override the default relative-age label. */
|
|
1410
1423
|
formatAge?(createdAt: Date): string;
|
|
1411
1424
|
className?: string;
|
|
@@ -1428,4 +1441,40 @@ interface NotificationSettingsProps {
|
|
|
1428
1441
|
}
|
|
1429
1442
|
declare const NotificationSettings: React$1.ForwardRefExoticComponent<NotificationSettingsProps & React$1.RefAttributes<HTMLDivElement>>;
|
|
1430
1443
|
|
|
1431
|
-
|
|
1444
|
+
interface NotificationToastProps {
|
|
1445
|
+
item: NotificationItem;
|
|
1446
|
+
/** Provenance shown once per run of same-source cards. Default `true`. */
|
|
1447
|
+
showSource?: boolean;
|
|
1448
|
+
/** A short state line under the body, e.g. "Awaiting your decision — expires in 20s." */
|
|
1449
|
+
note?: React$1.ReactNode;
|
|
1450
|
+
onItemClick?(item: NotificationItem): void;
|
|
1451
|
+
onAction?(action: NotificationAction, item: NotificationItem): void;
|
|
1452
|
+
onDismiss?(item: NotificationItem): void;
|
|
1453
|
+
/** Accessible label for the close button. Default `"Dismiss"`. */
|
|
1454
|
+
dismissLabel?: string;
|
|
1455
|
+
className?: string;
|
|
1456
|
+
}
|
|
1457
|
+
declare const NotificationToast: React$1.ForwardRefExoticComponent<NotificationToastProps & React$1.RefAttributes<HTMLDivElement>>;
|
|
1458
|
+
|
|
1459
|
+
type NotificationToasterPosition = 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
|
|
1460
|
+
interface NotificationToasterProps {
|
|
1461
|
+
/** The live queue. The consumer decides what becomes a toast at all. */
|
|
1462
|
+
items: NotificationItem[];
|
|
1463
|
+
/** Per-severity dwell in seconds, merged over `TOAST_DWELL`. `Infinity` never hides. */
|
|
1464
|
+
dwell?: Partial<NotificationToastDwell>;
|
|
1465
|
+
/** Most cards on screen at once. Default `4`. */
|
|
1466
|
+
max?: number;
|
|
1467
|
+
position?: NotificationToasterPosition;
|
|
1468
|
+
/** Freeze every countdown while the pointer is over the stack. Default `true`. */
|
|
1469
|
+
pauseOnHover?: boolean;
|
|
1470
|
+
/** Fires on auto-hide and on the close button alike — the item leaves the same way. */
|
|
1471
|
+
onDismiss?(id: string): void;
|
|
1472
|
+
onItemClick?(item: NotificationItem): void;
|
|
1473
|
+
onAction?(action: NotificationAction, item: NotificationItem): void;
|
|
1474
|
+
/** Per-item state line, e.g. a TTL countdown the consumer already tracks. */
|
|
1475
|
+
renderNote?(item: NotificationItem): React$1.ReactNode;
|
|
1476
|
+
className?: string;
|
|
1477
|
+
}
|
|
1478
|
+
declare const NotificationToaster: React$1.ForwardRefExoticComponent<NotificationToasterProps & React$1.RefAttributes<HTMLDivElement>>;
|
|
1479
|
+
|
|
1480
|
+
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AppShell, type AppShellProps, AppSidebar, type AppSidebarProps, AspectRatio, AuthenticatedLayout, Avatar, AvatarFallback, AvatarImage, Badge, type BadgeProps, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, type ButtonProps, Calendar, type CalendarProps, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, type CarouselApi, CarouselContent, type CarouselContextProps, CarouselItem, CarouselNext, type CarouselOptions, type CarouselPlugin, CarouselPrevious, type CarouselProps, Checkbox, Code, CodeBlock, type CodeBlockProps, type CodeLang, type CodeProps, Col, type ColProps, Collapsible, CollapsibleContent, CollapsibleTrigger, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, ConfirmDialog, type ConfirmDialogProps, type ConnectionAuth, ConnectionCard, type ConnectionCardProps, type ConnectionScope, type ConnectionStatus, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, CopyButton, type CopyButtonProps, type CreateProductSubmit, CreateProductWindow, type CreateProductWindowOrganization, type CreateProductWindowProduct, type CreateProductWindowProps, DatePicker, type DatePickerProps, type DeltaDirection, type DeltaTone, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DocsLink, type DocsLinkProps, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, type EmptyStateProps, ExtensionIcon, type ExtensionIconProps, ExtensionLink, type ExtensionLinkProps, type ExtensionLinkUiMode, FeedbackButton, type FeedbackButtonLabels, type FeedbackButtonProps, Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, Grid, GridOverlay, type GridOverlayProps, type GridProps, HoverCard, HoverCardContent, HoverCardTrigger, InlineError, type InlineErrorProps, Input, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, type InputProps, Label, MenuDivider, MenuPopup, type MenuPopupProps, Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarPortal, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, type NavItem, type NavLinkComponent, NavRow, type NavRowProps, type NavRowVariant, NavSectionLabel, type NavSectionLabelProps, NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, type NotifClass, type NotificationAction, NotificationBell, type NotificationBellProps, NotificationCenter, type NotificationCenterProps, type NotificationClass, type NotificationDeepLink, type NotificationItem, type NotificationMute, type NotificationPreferences, type NotificationProduct, NotificationSettings, type NotificationSettingsProps, type NotificationSeverity, type NotificationSubscription, type NotificationThreshold, NotificationToast, type NotificationToastDwell, type NotificationToastProps, NotificationToaster, type NotificationToasterPosition, type NotificationToasterProps, OrgProductSwitcher, type OrgProductSwitcherAction, type OrgProductSwitcherItem, type OrgProductSwitcherProps, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, type PaginationLinkProps, PaginationNext, PaginationPrevious, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, Progress, RadioGroup, RadioGroupItem, RequestRow, type RequestRowProps, type RequestState, type RequestSurface, type ResolvedTheme, type Responsive, ScrollArea, ScrollBar, SectionIntro, type SectionIntroProps, SegmentChip, type SegmentChipProps, Select, type SelectOption, type SelectProps, Separator, type Severity, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, SidebarBackHeader, type SidebarBackHeaderProps, SidebarNav, SidebarNavLink, type SidebarNavLinkProps, type SidebarNavProps, SidebarProductHeader, type SidebarProductHeaderProps, SignInScreen, type SignInScreenProps, type SignInScreenState, Skeleton, Slider, StatCard, type StatCardProps, StatusDot, type StatusDotProps, Switch, type SwitchProps, TOAST_DWELL, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, type TextareaProps, type Theme, ThemeToggle, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, type UseSidebarOptions, type UseSidebarResult, UserMenu, type UserMenuItem, type UserMenuProps, type UserMenuUser, alertVariants, badgeVariants, buttonVariants, cn, formatRelativeAge, isKnownExtensionIcon, isNotificationSettled, navigationMenuTriggerStyle, segmentChipVariants, severityBadgeVariant, severityLabels, statusDotVariants, toggleVariants, useCarousel, useFormField, useIsMobile, useSidebar, useTheme };
|
package/dist/index.js
CHANGED
|
@@ -4285,6 +4285,11 @@ var severityBadgeVariants = {
|
|
|
4285
4285
|
function severityBadgeVariant(severity) {
|
|
4286
4286
|
return severityBadgeVariants[severity];
|
|
4287
4287
|
}
|
|
4288
|
+
var TOAST_DWELL = {
|
|
4289
|
+
fyi: 6,
|
|
4290
|
+
important: 8,
|
|
4291
|
+
critical: Infinity
|
|
4292
|
+
};
|
|
4288
4293
|
function formatRelativeAge(createdAt, now = /* @__PURE__ */ new Date()) {
|
|
4289
4294
|
const then = createdAt instanceof Date ? createdAt : new Date(createdAt);
|
|
4290
4295
|
const seconds = Math.max(0, Math.floor((now.getTime() - then.getTime()) / 1e3));
|
|
@@ -4301,19 +4306,33 @@ function isNotificationSettled(item) {
|
|
|
4301
4306
|
|
|
4302
4307
|
// src/components/notification-bell.tsx
|
|
4303
4308
|
import * as React61 from "react";
|
|
4304
|
-
import { BellRing, Settings } from "lucide-react";
|
|
4309
|
+
import { BellRing, Check as Check7, Settings } from "lucide-react";
|
|
4305
4310
|
import { jsx as jsx74, jsxs as jsxs41 } from "react/jsx-runtime";
|
|
4311
|
+
function MarkReadButton({ title, onClick }) {
|
|
4312
|
+
return /* @__PURE__ */ jsx74(
|
|
4313
|
+
"button",
|
|
4314
|
+
{
|
|
4315
|
+
type: "button",
|
|
4316
|
+
"aria-label": `Mark "${title}" as read`,
|
|
4317
|
+
onClick,
|
|
4318
|
+
className: "flex size-5 shrink-0 items-center justify-center rounded text-text-tertiary opacity-0 transition-opacity hover:text-text-primary focus-visible:opacity-100 group-hover:opacity-100",
|
|
4319
|
+
children: /* @__PURE__ */ jsx74(Check7, { className: "size-3.5", "aria-hidden": "true" })
|
|
4320
|
+
}
|
|
4321
|
+
);
|
|
4322
|
+
}
|
|
4306
4323
|
function BellRow({
|
|
4307
4324
|
item,
|
|
4308
4325
|
onItemClick,
|
|
4309
4326
|
onActionClick,
|
|
4327
|
+
onMarkRead,
|
|
4310
4328
|
formatAge
|
|
4311
4329
|
}) {
|
|
4312
4330
|
const settled = isNotificationSettled(item);
|
|
4313
4331
|
const unread = !settled && !item.read;
|
|
4332
|
+
const markable = Boolean(onMarkRead) && unread && item.cls !== "ephemeral";
|
|
4314
4333
|
const actions = settled ? [] : item.actions ?? [];
|
|
4315
4334
|
const age = item.createdAt ? formatAge(item.createdAt instanceof Date ? item.createdAt : new Date(item.createdAt)) : null;
|
|
4316
|
-
return /* @__PURE__ */ jsxs41("div", { className: cn("rounded-lg bg-bg-surface px-3 py-2.5", settled && "opacity-45"), children: [
|
|
4335
|
+
return /* @__PURE__ */ jsxs41("div", { className: cn("group rounded-lg bg-bg-surface px-3 py-2.5", settled && "opacity-45"), children: [
|
|
4317
4336
|
/* @__PURE__ */ jsxs41("div", { className: "flex items-start gap-2.5", children: [
|
|
4318
4337
|
/* @__PURE__ */ jsxs41("div", { className: "min-w-0 flex-1", children: [
|
|
4319
4338
|
/* @__PURE__ */ jsx74(
|
|
@@ -4333,7 +4352,10 @@ function BellRow({
|
|
|
4333
4352
|
item.receipt ? `, ${item.receipt}` : ""
|
|
4334
4353
|
] })
|
|
4335
4354
|
] }),
|
|
4336
|
-
age && /* @__PURE__ */
|
|
4355
|
+
(age || markable) && /* @__PURE__ */ jsxs41("div", { className: "mt-px flex shrink-0 items-center gap-1", children: [
|
|
4356
|
+
age && /* @__PURE__ */ jsx74("span", { className: "text-xs text-text-tertiary", children: age }),
|
|
4357
|
+
markable && /* @__PURE__ */ jsx74(MarkReadButton, { title: item.title, onClick: () => onMarkRead(item.id) })
|
|
4358
|
+
] })
|
|
4337
4359
|
] }),
|
|
4338
4360
|
(actions.length > 0 || item.deepLink) && /* @__PURE__ */ jsxs41("div", { className: "mt-2 flex flex-wrap items-center gap-x-4 gap-y-2", children: [
|
|
4339
4361
|
actions.map((action) => {
|
|
@@ -4370,6 +4392,7 @@ var NotificationBell = React61.forwardRef(
|
|
|
4370
4392
|
items,
|
|
4371
4393
|
unreadCount,
|
|
4372
4394
|
maxItems = 5,
|
|
4395
|
+
onMarkRead,
|
|
4373
4396
|
onMarkAllRead,
|
|
4374
4397
|
onItemClick,
|
|
4375
4398
|
onActionClick,
|
|
@@ -4438,6 +4461,7 @@ var NotificationBell = React61.forwardRef(
|
|
|
4438
4461
|
item,
|
|
4439
4462
|
onItemClick,
|
|
4440
4463
|
onActionClick,
|
|
4464
|
+
onMarkRead,
|
|
4441
4465
|
formatAge: age
|
|
4442
4466
|
},
|
|
4443
4467
|
item.id
|
|
@@ -4459,24 +4483,41 @@ NotificationBell.displayName = "NotificationBell";
|
|
|
4459
4483
|
|
|
4460
4484
|
// src/components/notification-center.tsx
|
|
4461
4485
|
import * as React62 from "react";
|
|
4462
|
-
import { ArrowUpRight, Check as
|
|
4486
|
+
import { ArrowUpRight, Check as Check8, Search as Search2 } from "lucide-react";
|
|
4463
4487
|
import { jsx as jsx75, jsxs as jsxs42 } from "react/jsx-runtime";
|
|
4464
4488
|
var chipBase = "rounded-lg px-3 py-1.5 text-sm font-medium transition-colors";
|
|
4465
4489
|
var chipOn = "bg-bg-brand-subtle text-text-brand";
|
|
4466
4490
|
var chipOff = "bg-bg-elevated text-text-secondary hover:text-text-primary";
|
|
4491
|
+
function MarkReadButton2({ title, onClick }) {
|
|
4492
|
+
return /* @__PURE__ */ jsx75(
|
|
4493
|
+
"button",
|
|
4494
|
+
{
|
|
4495
|
+
type: "button",
|
|
4496
|
+
"aria-label": `Mark "${title}" as read`,
|
|
4497
|
+
onClick,
|
|
4498
|
+
className: "flex size-5 shrink-0 items-center justify-center rounded text-text-tertiary opacity-0 transition-opacity hover:text-text-primary focus-visible:opacity-100 group-hover:opacity-100",
|
|
4499
|
+
children: /* @__PURE__ */ jsx75(Check8, { className: "size-3.5", "aria-hidden": "true" })
|
|
4500
|
+
}
|
|
4501
|
+
);
|
|
4502
|
+
}
|
|
4467
4503
|
function CenterRow({
|
|
4468
4504
|
item,
|
|
4469
4505
|
onItemClick,
|
|
4470
4506
|
onDeepLinkClick,
|
|
4507
|
+
onMarkRead,
|
|
4471
4508
|
formatAge
|
|
4472
4509
|
}) {
|
|
4473
4510
|
const unread = !item.read;
|
|
4511
|
+
const markable = Boolean(onMarkRead) && unread && item.cls !== "ephemeral";
|
|
4474
4512
|
const age = item.createdAt ? formatAge(item.createdAt instanceof Date ? item.createdAt : new Date(item.createdAt)) : null;
|
|
4475
|
-
return /* @__PURE__ */ jsxs42("article", { className: "rounded-lg bg-bg-elevated px-4 py-3", children: [
|
|
4513
|
+
return /* @__PURE__ */ jsxs42("article", { className: "group rounded-lg bg-bg-elevated px-4 py-3", children: [
|
|
4476
4514
|
/* @__PURE__ */ jsxs42("div", { className: "flex items-center gap-2", children: [
|
|
4477
4515
|
/* @__PURE__ */ jsx75("span", { className: "text-xs font-semibold uppercase tracking-[0.09em] text-text-secondary", children: item.source }),
|
|
4478
4516
|
/* @__PURE__ */ jsx75(Badge, { variant: severityBadgeVariant(item.severity), children: severityLabels[item.severity] }),
|
|
4479
|
-
age && /* @__PURE__ */
|
|
4517
|
+
(age || markable) && /* @__PURE__ */ jsxs42("div", { className: "ml-auto flex shrink-0 items-center gap-1.5", children: [
|
|
4518
|
+
age && /* @__PURE__ */ jsx75("span", { className: "text-xs tabular-nums text-text-tertiary", children: age }),
|
|
4519
|
+
markable && /* @__PURE__ */ jsx75(MarkReadButton2, { title: item.title, onClick: () => onMarkRead(item.id) })
|
|
4520
|
+
] })
|
|
4480
4521
|
] }),
|
|
4481
4522
|
/* @__PURE__ */ jsx75(
|
|
4482
4523
|
"button",
|
|
@@ -4493,7 +4534,7 @@ function CenterRow({
|
|
|
4493
4534
|
item.body && /* @__PURE__ */ jsx75("p", { className: "mt-0.5 text-sm leading-snug text-text-secondary", children: item.body }),
|
|
4494
4535
|
(item.receipt || item.deepLink) && /* @__PURE__ */ jsxs42("div", { className: "mt-2 flex flex-wrap items-center gap-x-4 gap-y-1.5", children: [
|
|
4495
4536
|
item.receipt && /* @__PURE__ */ jsxs42("span", { className: "inline-flex items-center gap-1.5 text-sm text-text-tertiary", children: [
|
|
4496
|
-
/* @__PURE__ */ jsx75(
|
|
4537
|
+
/* @__PURE__ */ jsx75(Check8, { className: "size-3 shrink-0 text-text-success", "aria-hidden": "true" }),
|
|
4497
4538
|
item.receipt
|
|
4498
4539
|
] }),
|
|
4499
4540
|
item.deepLink && /* @__PURE__ */ jsxs42(
|
|
@@ -4523,6 +4564,7 @@ var NotificationCenter = React62.forwardRef(
|
|
|
4523
4564
|
onItemClick,
|
|
4524
4565
|
onDeepLinkClick,
|
|
4525
4566
|
onMarkAllRead,
|
|
4567
|
+
onMarkRead,
|
|
4526
4568
|
formatAge,
|
|
4527
4569
|
className
|
|
4528
4570
|
}, ref) => {
|
|
@@ -4644,6 +4686,7 @@ var NotificationCenter = React62.forwardRef(
|
|
|
4644
4686
|
item,
|
|
4645
4687
|
onItemClick,
|
|
4646
4688
|
onDeepLinkClick,
|
|
4689
|
+
onMarkRead,
|
|
4647
4690
|
formatAge: age
|
|
4648
4691
|
},
|
|
4649
4692
|
item.id
|
|
@@ -4871,6 +4914,184 @@ var NotificationSettings = React63.forwardRef(
|
|
|
4871
4914
|
}
|
|
4872
4915
|
);
|
|
4873
4916
|
NotificationSettings.displayName = "NotificationSettings";
|
|
4917
|
+
|
|
4918
|
+
// src/components/notification-toast.tsx
|
|
4919
|
+
import * as React64 from "react";
|
|
4920
|
+
import { X as X3 } from "lucide-react";
|
|
4921
|
+
import { jsx as jsx77, jsxs as jsxs44 } from "react/jsx-runtime";
|
|
4922
|
+
var chipBase2 = "rounded-lg px-2.5 py-1 text-sm font-medium transition-colors";
|
|
4923
|
+
var chipPrimary = "bg-bg-brand text-text-on-brand hover:bg-bg-brand-hover";
|
|
4924
|
+
var chipDestructive = "bg-bg-elevated text-text-danger hover:bg-bg-surface";
|
|
4925
|
+
var linkAction = "text-sm font-semibold text-text-brand transition-colors hover:text-text-primary";
|
|
4926
|
+
var NotificationToast = React64.forwardRef(
|
|
4927
|
+
({
|
|
4928
|
+
item,
|
|
4929
|
+
showSource = true,
|
|
4930
|
+
note,
|
|
4931
|
+
onItemClick,
|
|
4932
|
+
onAction,
|
|
4933
|
+
onDismiss,
|
|
4934
|
+
dismissLabel = "Dismiss",
|
|
4935
|
+
className
|
|
4936
|
+
}, ref) => {
|
|
4937
|
+
const actions = item.actions ?? [];
|
|
4938
|
+
return /* @__PURE__ */ jsxs44(
|
|
4939
|
+
"div",
|
|
4940
|
+
{
|
|
4941
|
+
ref,
|
|
4942
|
+
role: "status",
|
|
4943
|
+
"aria-live": item.severity === "critical" ? "assertive" : "polite",
|
|
4944
|
+
className: cn(
|
|
4945
|
+
"rounded-2xl border border-border-subtle bg-bg-elevated p-4 shadow-(--menu-shadow)",
|
|
4946
|
+
className
|
|
4947
|
+
),
|
|
4948
|
+
children: [
|
|
4949
|
+
/* @__PURE__ */ jsxs44("div", { className: "flex items-center gap-2", children: [
|
|
4950
|
+
showSource && /* @__PURE__ */ jsx77("span", { className: "text-xs font-semibold uppercase tracking-[0.09em] text-text-secondary", children: item.source }),
|
|
4951
|
+
/* @__PURE__ */ jsx77(Badge, { variant: severityBadgeVariant(item.severity), children: severityLabels[item.severity] }),
|
|
4952
|
+
onDismiss && /* @__PURE__ */ jsx77(
|
|
4953
|
+
"button",
|
|
4954
|
+
{
|
|
4955
|
+
type: "button",
|
|
4956
|
+
"aria-label": dismissLabel,
|
|
4957
|
+
onClick: () => onDismiss(item),
|
|
4958
|
+
className: "-mr-1 ml-auto flex size-5 shrink-0 items-center justify-center rounded text-text-tertiary transition-colors hover:bg-bg-surface hover:text-text-primary",
|
|
4959
|
+
children: /* @__PURE__ */ jsx77(X3, { className: "size-3.5", "aria-hidden": "true" })
|
|
4960
|
+
}
|
|
4961
|
+
)
|
|
4962
|
+
] }),
|
|
4963
|
+
onItemClick ? /* @__PURE__ */ jsx77(
|
|
4964
|
+
"button",
|
|
4965
|
+
{
|
|
4966
|
+
type: "button",
|
|
4967
|
+
onClick: () => onItemClick(item),
|
|
4968
|
+
className: "mt-1 block text-left text-sm font-semibold leading-snug text-text-primary underline-offset-2 hover:underline",
|
|
4969
|
+
children: item.title
|
|
4970
|
+
}
|
|
4971
|
+
) : /* @__PURE__ */ jsx77("p", { className: "mt-1 text-sm font-semibold leading-snug text-text-primary", children: item.title }),
|
|
4972
|
+
item.body && /* @__PURE__ */ jsx77("p", { className: "mt-0.5 text-sm leading-snug text-text-secondary", children: item.body }),
|
|
4973
|
+
note && /* @__PURE__ */ jsx77("p", { className: "mt-1.5 text-xs font-medium text-text-warning", children: note }),
|
|
4974
|
+
actions.length > 0 && /* @__PURE__ */ jsx77("div", { className: "mt-2.5 flex flex-wrap items-center gap-2", children: actions.map((action) => {
|
|
4975
|
+
const variant = action.variant ?? "link";
|
|
4976
|
+
return /* @__PURE__ */ jsx77(
|
|
4977
|
+
"button",
|
|
4978
|
+
{
|
|
4979
|
+
type: "button",
|
|
4980
|
+
onClick: () => onAction?.(action, item),
|
|
4981
|
+
className: variant === "primary" ? cn(chipBase2, chipPrimary) : variant === "destructive" ? cn(chipBase2, chipDestructive) : linkAction,
|
|
4982
|
+
children: action.label
|
|
4983
|
+
},
|
|
4984
|
+
action.id
|
|
4985
|
+
);
|
|
4986
|
+
}) })
|
|
4987
|
+
]
|
|
4988
|
+
}
|
|
4989
|
+
);
|
|
4990
|
+
}
|
|
4991
|
+
);
|
|
4992
|
+
NotificationToast.displayName = "NotificationToast";
|
|
4993
|
+
|
|
4994
|
+
// src/components/notification-toaster.tsx
|
|
4995
|
+
import * as React65 from "react";
|
|
4996
|
+
import { jsx as jsx78 } from "react/jsx-runtime";
|
|
4997
|
+
var positions = {
|
|
4998
|
+
"bottom-right": "bottom-6 right-6 flex-col",
|
|
4999
|
+
"bottom-left": "bottom-6 left-6 flex-col",
|
|
5000
|
+
"top-right": "top-6 right-6 flex-col-reverse",
|
|
5001
|
+
"top-left": "top-6 left-6 flex-col-reverse"
|
|
5002
|
+
};
|
|
5003
|
+
var NotificationToaster = React65.forwardRef(
|
|
5004
|
+
({
|
|
5005
|
+
items,
|
|
5006
|
+
dwell,
|
|
5007
|
+
max = 4,
|
|
5008
|
+
position = "bottom-right",
|
|
5009
|
+
pauseOnHover = true,
|
|
5010
|
+
onDismiss,
|
|
5011
|
+
onItemClick,
|
|
5012
|
+
onAction,
|
|
5013
|
+
renderNote,
|
|
5014
|
+
className
|
|
5015
|
+
}, ref) => {
|
|
5016
|
+
const shown = items.slice(0, max);
|
|
5017
|
+
const [paused, setPaused] = React65.useState(false);
|
|
5018
|
+
const onDismissRef = React65.useRef(onDismiss);
|
|
5019
|
+
onDismissRef.current = onDismiss;
|
|
5020
|
+
const fyiDwell = dwell?.fyi;
|
|
5021
|
+
const importantDwell = dwell?.important;
|
|
5022
|
+
const criticalDwell = dwell?.critical;
|
|
5023
|
+
const remaining = React65.useRef(/* @__PURE__ */ new Map());
|
|
5024
|
+
const liveIds = shown.map((item) => `${item.id}:${item.severity}`).join(" ");
|
|
5025
|
+
React65.useEffect(() => {
|
|
5026
|
+
const owedById = remaining.current;
|
|
5027
|
+
const dwellSeconds = {
|
|
5028
|
+
fyi: fyiDwell ?? TOAST_DWELL.fyi,
|
|
5029
|
+
important: importantDwell ?? TOAST_DWELL.important,
|
|
5030
|
+
critical: criticalDwell ?? TOAST_DWELL.critical
|
|
5031
|
+
};
|
|
5032
|
+
const live = liveIds === "" ? [] : liveIds.split(" ");
|
|
5033
|
+
const ids = new Set(live.map((entry) => entry.slice(0, entry.lastIndexOf(":"))));
|
|
5034
|
+
for (const id of owedById.keys()) {
|
|
5035
|
+
if (!ids.has(id)) owedById.delete(id);
|
|
5036
|
+
}
|
|
5037
|
+
for (const entry of live) {
|
|
5038
|
+
const at = entry.lastIndexOf(":");
|
|
5039
|
+
const id = entry.slice(0, at);
|
|
5040
|
+
const severity = entry.slice(at + 1);
|
|
5041
|
+
if (!owedById.has(id)) {
|
|
5042
|
+
owedById.set(id, dwellSeconds[severity] * 1e3);
|
|
5043
|
+
}
|
|
5044
|
+
}
|
|
5045
|
+
if (paused) return;
|
|
5046
|
+
const startedAt = Date.now();
|
|
5047
|
+
const timers = [];
|
|
5048
|
+
for (const [id, owed] of owedById) {
|
|
5049
|
+
if (!Number.isFinite(owed)) continue;
|
|
5050
|
+
timers.push(
|
|
5051
|
+
setTimeout(() => {
|
|
5052
|
+
owedById.delete(id);
|
|
5053
|
+
onDismissRef.current?.(id);
|
|
5054
|
+
}, owed)
|
|
5055
|
+
);
|
|
5056
|
+
}
|
|
5057
|
+
return () => {
|
|
5058
|
+
timers.forEach(clearTimeout);
|
|
5059
|
+
const elapsed = Date.now() - startedAt;
|
|
5060
|
+
for (const [id, owed] of owedById) {
|
|
5061
|
+
if (Number.isFinite(owed)) owedById.set(id, Math.max(0, owed - elapsed));
|
|
5062
|
+
}
|
|
5063
|
+
};
|
|
5064
|
+
}, [liveIds, paused, fyiDwell, importantDwell, criticalDwell]);
|
|
5065
|
+
if (shown.length === 0) return null;
|
|
5066
|
+
return /* @__PURE__ */ jsx78(
|
|
5067
|
+
"div",
|
|
5068
|
+
{
|
|
5069
|
+
ref,
|
|
5070
|
+
role: "region",
|
|
5071
|
+
"aria-label": "Notifications",
|
|
5072
|
+
onMouseEnter: pauseOnHover ? () => setPaused(true) : void 0,
|
|
5073
|
+
onMouseLeave: pauseOnHover ? () => setPaused(false) : void 0,
|
|
5074
|
+
className: cn(
|
|
5075
|
+
"pointer-events-none fixed z-50 flex w-[350px] gap-3",
|
|
5076
|
+
positions[position],
|
|
5077
|
+
className
|
|
5078
|
+
),
|
|
5079
|
+
children: shown.map((item, i) => /* @__PURE__ */ jsx78("div", { className: "pointer-events-auto", children: /* @__PURE__ */ jsx78(
|
|
5080
|
+
NotificationToast,
|
|
5081
|
+
{
|
|
5082
|
+
item,
|
|
5083
|
+
showSource: i === 0 || shown[i - 1].source !== item.source,
|
|
5084
|
+
note: renderNote?.(item),
|
|
5085
|
+
onItemClick,
|
|
5086
|
+
onAction,
|
|
5087
|
+
onDismiss: onDismiss ? () => onDismiss(item.id) : void 0
|
|
5088
|
+
}
|
|
5089
|
+
) }, item.id))
|
|
5090
|
+
}
|
|
5091
|
+
);
|
|
5092
|
+
}
|
|
5093
|
+
);
|
|
5094
|
+
NotificationToaster.displayName = "NotificationToaster";
|
|
4874
5095
|
export {
|
|
4875
5096
|
Accordion,
|
|
4876
5097
|
AccordionContent,
|
|
@@ -5044,6 +5265,8 @@ export {
|
|
|
5044
5265
|
NotificationBell,
|
|
5045
5266
|
NotificationCenter,
|
|
5046
5267
|
NotificationSettings,
|
|
5268
|
+
NotificationToast,
|
|
5269
|
+
NotificationToaster,
|
|
5047
5270
|
OrgProductSwitcher,
|
|
5048
5271
|
Pagination,
|
|
5049
5272
|
PaginationContent,
|
|
@@ -5086,6 +5309,7 @@ export {
|
|
|
5086
5309
|
StatCard,
|
|
5087
5310
|
StatusDot,
|
|
5088
5311
|
Switch,
|
|
5312
|
+
TOAST_DWELL,
|
|
5089
5313
|
Table,
|
|
5090
5314
|
TableBody,
|
|
5091
5315
|
TableCaption,
|