@sarunyu/system-one 4.4.1 → 4.5.1
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/AGENTS.md +9 -0
- package/README.md +7 -0
- package/dist/index.cjs +427 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +429 -2
- package/dist/index.js.map +1 -1
- package/dist/src/components/alert.d.ts +9 -0
- package/dist/src/components/alert.d.ts.map +1 -0
- package/dist/src/components/badge.d.ts +23 -0
- package/dist/src/components/badge.d.ts.map +1 -0
- package/dist/src/components/notification.d.ts +47 -0
- package/dist/src/components/notification.d.ts.map +1 -0
- package/dist/src/components/toast.d.ts +25 -0
- package/dist/src/components/toast.d.ts.map +1 -0
- package/dist/src/index.d.ts +8 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/style.css +1 -1
- package/llms.txt +127 -2
- package/package.json +1 -1
package/AGENTS.md
CHANGED
|
@@ -20,6 +20,10 @@ in this package.** This file is the short version: the rules you must follow.
|
|
|
20
20
|
- Custom tables → use `<Table>` + `<TableRow>` + `<TableHeaderCell>` + `<TableCell>`.
|
|
21
21
|
- Custom modals/dialogs/alerts → use `<Modal>` (wrap it in your own `fixed inset-0` backdrop).
|
|
22
22
|
- Custom bottom sheets / drawers from the bottom → use `<BottomSheet>` (it ships its own backdrop via Vaul).
|
|
23
|
+
- Custom inline status banners → use `<Alert>` (never hand-roll tinted divs with icons).
|
|
24
|
+
- Custom toast / snackbar notifications → use `<Toast>` / `<ToastStack>` in a `fixed` portal.
|
|
25
|
+
- Filter buttons with count badges → use `<Badge>` (`variant="button"`).
|
|
26
|
+
- Notification bell + list panel → use `<Notification>`. Never use `<Badge variant="notification">` standalone or wire it to a custom `onClick` (toast, popover, etc.) — `<Notification>` handles both the bell and the panel.
|
|
23
27
|
|
|
24
28
|
2. **Use token-backed Tailwind classes for color.** Never emit hard-coded colors:
|
|
25
29
|
- Hex (`#3b82f6`), arbitrary (`bg-[#...]`), and palette utilities
|
|
@@ -43,6 +47,11 @@ in this package.** This file is the short version: the rules you must follow.
|
|
|
43
47
|
- One `<Button variant="primary">` per context.
|
|
44
48
|
- `Modal` renders the panel only — provide your own `fixed inset-0` backdrop + open/close state. One primary action per modal.
|
|
45
49
|
- `BottomSheet` is mobile-only. On desktop, use `Modal` instead.
|
|
50
|
+
- `Alert` is always-visible (no open state). Pass `status` + `message`; add `onClose` only for dismissible alerts.
|
|
51
|
+
- `Toast` / `ToastStack` are floating — render them in a `fixed` portal (`fixed bottom-4 right-4 z-50`). Never inline them in page flow.
|
|
52
|
+
- `Badge variant="button"` (default) — filter button with optional count. Pass `count`, `label`, `iconOnly`, and `onClick`.
|
|
53
|
+
- `Badge variant="notification"` — internal to `<Notification>`; never use standalone or attach your own `onClick` to it.
|
|
54
|
+
- `Notification` manages its own popover; pass `groups` (array of `{ label, items }`). It renders both the bell trigger and the panel. This is the only correct way to show a notification list.
|
|
46
55
|
|
|
47
56
|
6. **Mobile forms and action-heavy modals MUST use `<BottomSheet>`, not `<Modal>`.**
|
|
48
57
|
Login, signup, settings panels, profile editors, any multi-field form,
|
package/README.md
CHANGED
|
@@ -68,6 +68,10 @@ ships no `Page`/`Section`/`Stack` primitives — use whatever layout looks right
|
|
|
68
68
|
| `Table` | Data tables (`TableRow` / `TableHeaderCell` / `TableCell`) |
|
|
69
69
|
| `Modal` | Centered overlay — dialog / content / alert variants |
|
|
70
70
|
| `BottomSheet` | Mobile-first bottom sheet (Vaul-based, with backdrop) |
|
|
71
|
+
| `Alert` | Inline persistent status banner — information / success / warning / critical |
|
|
72
|
+
| `Toast` / `ToastStack` | Floating transient notification — default and broadcast variants |
|
|
73
|
+
| `Badge` | Count badge overlay on a trigger (notification bell or filter button) |
|
|
74
|
+
| `Notification` | Bell trigger + popover panel with grouped notification items |
|
|
71
75
|
|
|
72
76
|
Full prop reference: [`llms.txt`](./llms.txt).
|
|
73
77
|
|
|
@@ -124,6 +128,9 @@ import type {
|
|
|
124
128
|
InputProps, DropdownOption,
|
|
125
129
|
ToggleSize, ToggleProps,
|
|
126
130
|
ModalVariant, ModalActionLayout,
|
|
131
|
+
AlertStatus,
|
|
132
|
+
ToastVariant, ToastStatus,
|
|
133
|
+
NotificationItem, NotificationGroup,
|
|
127
134
|
} from "@sarunyu/system-one";
|
|
128
135
|
```
|
|
129
136
|
|
package/dist/index.cjs
CHANGED
|
@@ -247,6 +247,141 @@ const Button = React.forwardRef(function Button2({
|
|
|
247
247
|
);
|
|
248
248
|
});
|
|
249
249
|
Button.displayName = "Button";
|
|
250
|
+
function formatCount(count, maxCount) {
|
|
251
|
+
if (count > maxCount) return `${maxCount}+`;
|
|
252
|
+
return String(count);
|
|
253
|
+
}
|
|
254
|
+
function Badge({
|
|
255
|
+
variant = "button",
|
|
256
|
+
count = 0,
|
|
257
|
+
maxCount = 99,
|
|
258
|
+
label = "Filter",
|
|
259
|
+
iconOnly = false,
|
|
260
|
+
icon,
|
|
261
|
+
notificationState,
|
|
262
|
+
className,
|
|
263
|
+
...props
|
|
264
|
+
}) {
|
|
265
|
+
const hasCount = count > 0;
|
|
266
|
+
const isActive = hasCount;
|
|
267
|
+
const resolvedNotificationState = notificationState ?? (hasCount ? "noti" : "default");
|
|
268
|
+
const notificationIsFilled = resolvedNotificationState === "active" || resolvedNotificationState === "noti";
|
|
269
|
+
const showNotificationDot = resolvedNotificationState === "noti" && hasCount;
|
|
270
|
+
const visualIcon = variant === "notification" ? notificationIsFilled ? /* @__PURE__ */ jsxRuntime.jsx(react.BellSimple, { size: 19, weight: "fill" }) : /* @__PURE__ */ jsxRuntime.jsx(react.BellSimple, { size: 19, weight: "regular" }) : icon ?? /* @__PURE__ */ jsxRuntime.jsx(react.FunnelSimple, { size: 18, weight: "regular" });
|
|
271
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("relative inline-flex", className), children: [
|
|
272
|
+
variant === "notification" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
273
|
+
Button,
|
|
274
|
+
{
|
|
275
|
+
"aria-label": "Notification",
|
|
276
|
+
size: "icon-xs",
|
|
277
|
+
variant: "plain-black",
|
|
278
|
+
className: cn(
|
|
279
|
+
"text-subtle-text",
|
|
280
|
+
notificationIsFilled && "text-primary-action"
|
|
281
|
+
),
|
|
282
|
+
...props,
|
|
283
|
+
children: visualIcon
|
|
284
|
+
}
|
|
285
|
+
) : iconOnly ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
286
|
+
Button,
|
|
287
|
+
{
|
|
288
|
+
"aria-label": label,
|
|
289
|
+
size: "icon-md",
|
|
290
|
+
variant: isActive ? "outline" : "outline-black",
|
|
291
|
+
className: cn(isActive && "bg-primary-action-light border-primary-action-light"),
|
|
292
|
+
...props,
|
|
293
|
+
children: visualIcon
|
|
294
|
+
}
|
|
295
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
296
|
+
Button,
|
|
297
|
+
{
|
|
298
|
+
size: "md",
|
|
299
|
+
leftIcon: visualIcon,
|
|
300
|
+
variant: isActive ? "outline" : "outline-black",
|
|
301
|
+
className: cn(isActive && "bg-primary-action-light border-primary-action-light"),
|
|
302
|
+
...props,
|
|
303
|
+
children: label
|
|
304
|
+
}
|
|
305
|
+
),
|
|
306
|
+
(variant === "notification" ? showNotificationDot : hasCount) && /* @__PURE__ */ jsxRuntime.jsx(
|
|
307
|
+
"div",
|
|
308
|
+
{
|
|
309
|
+
className: cn(
|
|
310
|
+
"absolute flex items-center justify-center rounded-[60px] px-1",
|
|
311
|
+
variant === "notification" ? "-right-0.5 -top-0.5 h-[14px] min-w-[14px] bg-destructive" : "-right-1 -top-[7px] h-4 min-w-4 bg-primary-action"
|
|
312
|
+
),
|
|
313
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-center text-xs leading-4 text-on-primary-action", children: formatCount(count, maxCount) })
|
|
314
|
+
}
|
|
315
|
+
)
|
|
316
|
+
] });
|
|
317
|
+
}
|
|
318
|
+
const alertStyles = {
|
|
319
|
+
normal: {
|
|
320
|
+
container: "bg-default-secondary",
|
|
321
|
+
text: "text-default-secondary",
|
|
322
|
+
icon: "text-default-secondary"
|
|
323
|
+
},
|
|
324
|
+
information: {
|
|
325
|
+
container: "bg-[var(--bg-info-light)]",
|
|
326
|
+
text: "text-[var(--text-info-primary)]",
|
|
327
|
+
icon: "text-[var(--icon-brand-primary)]"
|
|
328
|
+
},
|
|
329
|
+
success: {
|
|
330
|
+
container: "bg-[var(--bg-success-light)]",
|
|
331
|
+
text: "text-[var(--text-success-primary)]",
|
|
332
|
+
icon: "text-[var(--icon-success)]"
|
|
333
|
+
},
|
|
334
|
+
warning: {
|
|
335
|
+
container: "bg-[var(--bg-warning-soft)]",
|
|
336
|
+
text: "text-[var(--text-warning-primary)]",
|
|
337
|
+
icon: "text-[var(--icon-warning)]"
|
|
338
|
+
},
|
|
339
|
+
critical: {
|
|
340
|
+
container: "bg-[var(--bg-danger-light)]",
|
|
341
|
+
text: "text-[var(--text-danger-primary)]",
|
|
342
|
+
icon: "text-[var(--icon-danger)]"
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
function AlertStatusIcon({
|
|
346
|
+
status,
|
|
347
|
+
className
|
|
348
|
+
}) {
|
|
349
|
+
if (status === "success") return /* @__PURE__ */ jsxRuntime.jsx(react.CheckCircle, { size: 16, weight: "fill", className });
|
|
350
|
+
if (status === "warning") return /* @__PURE__ */ jsxRuntime.jsx(react.Warning, { size: 16, weight: "fill", className });
|
|
351
|
+
if (status === "critical") return /* @__PURE__ */ jsxRuntime.jsx(react.XCircle, { size: 16, weight: "fill", className });
|
|
352
|
+
return /* @__PURE__ */ jsxRuntime.jsx(react.Info, { size: 16, weight: "fill", className });
|
|
353
|
+
}
|
|
354
|
+
const Alert = React.forwardRef(function Alert2({ status = "normal", message, multiline = false, className }, ref) {
|
|
355
|
+
const style = alertStyles[status];
|
|
356
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
357
|
+
"div",
|
|
358
|
+
{
|
|
359
|
+
ref,
|
|
360
|
+
role: "status",
|
|
361
|
+
className: cn(
|
|
362
|
+
"flex w-full items-center gap-1.5 rounded px-2 py-1",
|
|
363
|
+
multiline && "items-start",
|
|
364
|
+
style.container,
|
|
365
|
+
className
|
|
366
|
+
),
|
|
367
|
+
children: [
|
|
368
|
+
/* @__PURE__ */ jsxRuntime.jsx(AlertStatusIcon, { status, className: cn("shrink-0", multiline && "mt-0.5", style.icon) }),
|
|
369
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
370
|
+
"p",
|
|
371
|
+
{
|
|
372
|
+
className: cn(
|
|
373
|
+
"min-w-0 flex-1 text-sm leading-5 font-normal",
|
|
374
|
+
multiline ? "line-clamp-2" : "truncate",
|
|
375
|
+
style.text
|
|
376
|
+
),
|
|
377
|
+
children: message
|
|
378
|
+
}
|
|
379
|
+
)
|
|
380
|
+
]
|
|
381
|
+
}
|
|
382
|
+
);
|
|
383
|
+
});
|
|
384
|
+
Alert.displayName = "Alert";
|
|
250
385
|
function BannerMedia({ src, alt }) {
|
|
251
386
|
if (!src) {
|
|
252
387
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-hidden": "true", className: "absolute inset-0 bg-muted" });
|
|
@@ -3740,6 +3875,293 @@ function ModalActions({
|
|
|
3740
3875
|
)
|
|
3741
3876
|
] });
|
|
3742
3877
|
}
|
|
3878
|
+
function NotificationDivider({ label }) {
|
|
3879
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 px-4 py-2", children: [
|
|
3880
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "shrink-0 text-sm leading-5 text-subtle-text", children: label }),
|
|
3881
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { "aria-hidden": "true", className: "h-px min-w-0 flex-1 bg-divider" })
|
|
3882
|
+
] });
|
|
3883
|
+
}
|
|
3884
|
+
function NotificationRow({
|
|
3885
|
+
item,
|
|
3886
|
+
onItemClick
|
|
3887
|
+
}) {
|
|
3888
|
+
const rowType = item.type ?? "icon";
|
|
3889
|
+
const showImage = rowType === "image";
|
|
3890
|
+
const showUnread = Boolean(item.unread);
|
|
3891
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3892
|
+
"div",
|
|
3893
|
+
{
|
|
3894
|
+
className: cn(
|
|
3895
|
+
"flex w-full items-start gap-3 px-4 py-3",
|
|
3896
|
+
showUnread ? "bg-primary-action-light/40" : "bg-background"
|
|
3897
|
+
),
|
|
3898
|
+
role: "button",
|
|
3899
|
+
tabIndex: 0,
|
|
3900
|
+
onClick: () => onItemClick == null ? void 0 : onItemClick(item),
|
|
3901
|
+
onKeyDown: (e) => {
|
|
3902
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
3903
|
+
e.preventDefault();
|
|
3904
|
+
onItemClick == null ? void 0 : onItemClick(item);
|
|
3905
|
+
}
|
|
3906
|
+
},
|
|
3907
|
+
children: [
|
|
3908
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex w-10 shrink-0 items-start justify-center py-0.5", children: showImage ? item.imageSrc ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
3909
|
+
"img",
|
|
3910
|
+
{
|
|
3911
|
+
alt: "",
|
|
3912
|
+
className: "h-10 w-10 rounded object-cover",
|
|
3913
|
+
src: item.imageSrc
|
|
3914
|
+
}
|
|
3915
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-10 w-10 items-center justify-center rounded bg-disabled-bg text-disabled", children: /* @__PURE__ */ jsxRuntime.jsx(react.ImageSquare, { size: 20, weight: "regular" }) }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-6 w-6 items-center justify-center text-subtle-text", children: item.icon ?? /* @__PURE__ */ jsxRuntime.jsx(react.Circle, { size: 20, weight: "regular" }) }) }),
|
|
3916
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
3917
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-[minmax(0,1fr)_auto] items-start gap-x-2", children: [
|
|
3918
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "min-w-0 flex-1 truncate text-base leading-6 font-bold text-foreground", children: item.title }),
|
|
3919
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [
|
|
3920
|
+
showUnread && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3921
|
+
"span",
|
|
3922
|
+
{
|
|
3923
|
+
"aria-hidden": "true",
|
|
3924
|
+
className: "h-2 w-2 rounded-full bg-primary-action"
|
|
3925
|
+
}
|
|
3926
|
+
),
|
|
3927
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs leading-4 text-muted-foreground", children: item.time })
|
|
3928
|
+
] }),
|
|
3929
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "col-start-1 mt-1 line-clamp-2 text-sm leading-5 text-muted-foreground", children: item.description })
|
|
3930
|
+
] }),
|
|
3931
|
+
item.actionLabel && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3932
|
+
Button,
|
|
3933
|
+
{
|
|
3934
|
+
className: "mt-2",
|
|
3935
|
+
size: "md",
|
|
3936
|
+
variant: "primary",
|
|
3937
|
+
onClick: (e) => {
|
|
3938
|
+
var _a;
|
|
3939
|
+
e.stopPropagation();
|
|
3940
|
+
(_a = item.onActionClick) == null ? void 0 : _a.call(item);
|
|
3941
|
+
},
|
|
3942
|
+
children: item.actionLabel
|
|
3943
|
+
}
|
|
3944
|
+
)
|
|
3945
|
+
] })
|
|
3946
|
+
]
|
|
3947
|
+
}
|
|
3948
|
+
);
|
|
3949
|
+
}
|
|
3950
|
+
const Notification = React.forwardRef(
|
|
3951
|
+
function Notification2({
|
|
3952
|
+
groups,
|
|
3953
|
+
badgeCount,
|
|
3954
|
+
panelWidth = 375,
|
|
3955
|
+
emptyText = "No notifications",
|
|
3956
|
+
clearBadgeOnOpen = true,
|
|
3957
|
+
open,
|
|
3958
|
+
defaultOpen,
|
|
3959
|
+
onOpenChange,
|
|
3960
|
+
onBadgeCleared,
|
|
3961
|
+
onItemClick,
|
|
3962
|
+
className,
|
|
3963
|
+
panelClassName
|
|
3964
|
+
}, ref) {
|
|
3965
|
+
const [internalOpen, setInternalOpen] = React.useState(defaultOpen ?? false);
|
|
3966
|
+
const [isBadgeCleared, setIsBadgeCleared] = React.useState(false);
|
|
3967
|
+
const controlled = open !== void 0;
|
|
3968
|
+
const resolvedOpen = controlled ? open : internalOpen;
|
|
3969
|
+
const unreadCount = React.useMemo(
|
|
3970
|
+
() => groups.reduce(
|
|
3971
|
+
(acc, group) => acc + group.items.filter((item) => Boolean(item.unread)).length,
|
|
3972
|
+
0
|
|
3973
|
+
),
|
|
3974
|
+
[groups]
|
|
3975
|
+
);
|
|
3976
|
+
const nextCount = badgeCount ?? unreadCount;
|
|
3977
|
+
const prevCountRef = React.useRef(nextCount);
|
|
3978
|
+
React.useEffect(() => {
|
|
3979
|
+
const prevCount = prevCountRef.current;
|
|
3980
|
+
if (nextCount <= 0 || nextCount > prevCount) {
|
|
3981
|
+
setIsBadgeCleared(false);
|
|
3982
|
+
}
|
|
3983
|
+
prevCountRef.current = nextCount;
|
|
3984
|
+
}, [nextCount]);
|
|
3985
|
+
const displayCount = clearBadgeOnOpen && isBadgeCleared ? 0 : nextCount;
|
|
3986
|
+
const hasItems = groups.some((group) => group.items.length > 0);
|
|
3987
|
+
const handleOpenChange = (next) => {
|
|
3988
|
+
if (next && clearBadgeOnOpen && nextCount > 0) {
|
|
3989
|
+
setIsBadgeCleared(true);
|
|
3990
|
+
onBadgeCleared == null ? void 0 : onBadgeCleared();
|
|
3991
|
+
}
|
|
3992
|
+
if (!controlled) setInternalOpen(next);
|
|
3993
|
+
onOpenChange == null ? void 0 : onOpenChange(next);
|
|
3994
|
+
};
|
|
3995
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(Popover__namespace.Root, { open: resolvedOpen, onOpenChange: handleOpenChange, children: [
|
|
3996
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: cn("inline-flex", className), children: /* @__PURE__ */ jsxRuntime.jsx(Popover__namespace.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3997
|
+
Badge,
|
|
3998
|
+
{
|
|
3999
|
+
variant: "notification",
|
|
4000
|
+
count: displayCount,
|
|
4001
|
+
maxCount: 99,
|
|
4002
|
+
notificationState: displayCount > 0 ? "noti" : resolvedOpen ? "active" : "default",
|
|
4003
|
+
"aria-label": "Open notifications"
|
|
4004
|
+
}
|
|
4005
|
+
) }) }) }),
|
|
4006
|
+
/* @__PURE__ */ jsxRuntime.jsx(Popover__namespace.Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
4007
|
+
Popover__namespace.Content,
|
|
4008
|
+
{
|
|
4009
|
+
align: "end",
|
|
4010
|
+
sideOffset: 10,
|
|
4011
|
+
className: cn(
|
|
4012
|
+
"z-50 overflow-hidden rounded-lg border border-border bg-background shadow-lg",
|
|
4013
|
+
panelClassName
|
|
4014
|
+
),
|
|
4015
|
+
style: { width: panelWidth },
|
|
4016
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-h-[480px] overflow-y-auto py-2", children: [
|
|
4017
|
+
!hasItems && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-8 text-center text-sm text-muted-foreground", children: emptyText }),
|
|
4018
|
+
groups.map((group) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full", children: [
|
|
4019
|
+
/* @__PURE__ */ jsxRuntime.jsx(NotificationDivider, { label: group.label }),
|
|
4020
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "divide-y divide-divider", children: group.items.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
4021
|
+
NotificationRow,
|
|
4022
|
+
{
|
|
4023
|
+
item,
|
|
4024
|
+
onItemClick
|
|
4025
|
+
},
|
|
4026
|
+
item.id
|
|
4027
|
+
)) })
|
|
4028
|
+
] }, group.label))
|
|
4029
|
+
] })
|
|
4030
|
+
}
|
|
4031
|
+
) })
|
|
4032
|
+
] });
|
|
4033
|
+
}
|
|
4034
|
+
);
|
|
4035
|
+
Notification.displayName = "Notification";
|
|
4036
|
+
const statusStyles = {
|
|
4037
|
+
information: {
|
|
4038
|
+
bg: "bg-[var(--bg-info-light)]",
|
|
4039
|
+
text: "text-[var(--text-info-primary)]",
|
|
4040
|
+
icon: "text-[var(--icon-brand-primary)]",
|
|
4041
|
+
link: "text-[var(--text-brand-link-primary)]"
|
|
4042
|
+
},
|
|
4043
|
+
success: {
|
|
4044
|
+
bg: "bg-[var(--bg-success-light)]",
|
|
4045
|
+
text: "text-[var(--text-success-primary)]",
|
|
4046
|
+
icon: "text-[var(--icon-success)]",
|
|
4047
|
+
link: "text-[var(--text-success-link)]"
|
|
4048
|
+
},
|
|
4049
|
+
warning: {
|
|
4050
|
+
bg: "bg-[var(--bg-warning-soft)]",
|
|
4051
|
+
text: "text-[var(--text-warning-primary)]",
|
|
4052
|
+
icon: "text-[var(--icon-warning)]",
|
|
4053
|
+
link: "text-[var(--text-warning-link)]"
|
|
4054
|
+
},
|
|
4055
|
+
critical: {
|
|
4056
|
+
bg: "bg-[var(--bg-danger-light)]",
|
|
4057
|
+
text: "text-[var(--text-danger-primary)]",
|
|
4058
|
+
icon: "text-[var(--icon-danger)]",
|
|
4059
|
+
link: "text-[var(--text-danger-link)]"
|
|
4060
|
+
}
|
|
4061
|
+
};
|
|
4062
|
+
function DefaultIcon({
|
|
4063
|
+
status,
|
|
4064
|
+
className
|
|
4065
|
+
}) {
|
|
4066
|
+
if (status === "success") return /* @__PURE__ */ jsxRuntime.jsx(react.CheckCircle, { className, size: 24, weight: "fill" });
|
|
4067
|
+
if (status === "warning") return /* @__PURE__ */ jsxRuntime.jsx(react.Warning, { className, size: 24, weight: "fill" });
|
|
4068
|
+
if (status === "critical") return /* @__PURE__ */ jsxRuntime.jsx(react.XCircle, { className, size: 24, weight: "fill" });
|
|
4069
|
+
return /* @__PURE__ */ jsxRuntime.jsx(react.Info, { className, size: 24, weight: "fill" });
|
|
4070
|
+
}
|
|
4071
|
+
function BroadcastIcon({
|
|
4072
|
+
status,
|
|
4073
|
+
className
|
|
4074
|
+
}) {
|
|
4075
|
+
if (status === "information") {
|
|
4076
|
+
return /* @__PURE__ */ jsxRuntime.jsx(react.MegaphoneSimple, { className, size: 20, weight: "fill" });
|
|
4077
|
+
}
|
|
4078
|
+
return /* @__PURE__ */ jsxRuntime.jsx(react.GearSix, { className, size: 20, weight: "fill" });
|
|
4079
|
+
}
|
|
4080
|
+
function ToastCloseButton({
|
|
4081
|
+
colorClass,
|
|
4082
|
+
onClose
|
|
4083
|
+
}) {
|
|
4084
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4085
|
+
"button",
|
|
4086
|
+
{
|
|
4087
|
+
type: "button",
|
|
4088
|
+
"aria-label": "Close toast",
|
|
4089
|
+
className: cn(
|
|
4090
|
+
"inline-flex h-[18px] w-[18px] shrink-0 cursor-pointer items-center justify-center",
|
|
4091
|
+
colorClass
|
|
4092
|
+
),
|
|
4093
|
+
onClick: onClose,
|
|
4094
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(react.X, { size: 12, weight: "bold" })
|
|
4095
|
+
}
|
|
4096
|
+
);
|
|
4097
|
+
}
|
|
4098
|
+
const Toast = React.forwardRef(function Toast2({
|
|
4099
|
+
variant = "default",
|
|
4100
|
+
status = "information",
|
|
4101
|
+
message,
|
|
4102
|
+
actionLabel,
|
|
4103
|
+
multiline = false,
|
|
4104
|
+
onActionClick,
|
|
4105
|
+
onClose,
|
|
4106
|
+
className
|
|
4107
|
+
}, ref) {
|
|
4108
|
+
const hasAction = Boolean(actionLabel);
|
|
4109
|
+
const effectiveStatus = variant === "broadcast" && status === "success" ? "information" : status;
|
|
4110
|
+
const effectiveStyle = statusStyles[effectiveStatus];
|
|
4111
|
+
const Icon = variant === "broadcast" ? BroadcastIcon : DefaultIcon;
|
|
4112
|
+
const showActions = variant === "default";
|
|
4113
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4114
|
+
"div",
|
|
4115
|
+
{
|
|
4116
|
+
ref,
|
|
4117
|
+
role: "status",
|
|
4118
|
+
className: cn(
|
|
4119
|
+
"flex w-full p-3",
|
|
4120
|
+
variant === "default" ? "items-center gap-2 rounded-lg shadow-[0px_1px_2px_0px_rgba(0,0,0,0.10),0px_1px_3px_1px_rgba(0,0,0,0.05)]" : "items-center gap-2",
|
|
4121
|
+
multiline && "items-start",
|
|
4122
|
+
effectiveStyle.bg,
|
|
4123
|
+
className
|
|
4124
|
+
),
|
|
4125
|
+
children: [
|
|
4126
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
4127
|
+
"div",
|
|
4128
|
+
{
|
|
4129
|
+
className: cn(
|
|
4130
|
+
"flex min-w-0 flex-1 gap-2",
|
|
4131
|
+
multiline ? "items-start" : "items-center",
|
|
4132
|
+
variant === "default" && !hasAction && "opacity-80"
|
|
4133
|
+
),
|
|
4134
|
+
children: [
|
|
4135
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("shrink-0", multiline && "pt-0.5"), children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { status: effectiveStatus, className: effectiveStyle.icon }) }),
|
|
4136
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: cn("min-w-0 flex-1 text-sm leading-5 font-normal", effectiveStyle.text), children: message })
|
|
4137
|
+
]
|
|
4138
|
+
}
|
|
4139
|
+
),
|
|
4140
|
+
showActions && hasAction ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex shrink-0 items-center gap-3", children: [
|
|
4141
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4142
|
+
"button",
|
|
4143
|
+
{
|
|
4144
|
+
type: "button",
|
|
4145
|
+
className: cn(
|
|
4146
|
+
"cursor-pointer text-sm leading-5 underline underline-offset-2",
|
|
4147
|
+
effectiveStyle.link
|
|
4148
|
+
),
|
|
4149
|
+
onClick: onActionClick,
|
|
4150
|
+
children: actionLabel
|
|
4151
|
+
}
|
|
4152
|
+
),
|
|
4153
|
+
/* @__PURE__ */ jsxRuntime.jsx(ToastCloseButton, { colorClass: effectiveStyle.icon, onClose })
|
|
4154
|
+
] }) : showActions ? /* @__PURE__ */ jsxRuntime.jsx(ToastCloseButton, { colorClass: effectiveStyle.icon, onClose }) : null
|
|
4155
|
+
]
|
|
4156
|
+
}
|
|
4157
|
+
);
|
|
4158
|
+
});
|
|
4159
|
+
Toast.displayName = "Toast";
|
|
4160
|
+
function ToastStack({ items, className, renderItem }) {
|
|
4161
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("flex flex-col gap-2", className), children: items.map(
|
|
4162
|
+
(item) => renderItem ? /* @__PURE__ */ jsxRuntime.jsx("div", { children: renderItem(item) }, item.id) : /* @__PURE__ */ jsxRuntime.jsx(Toast, { ...item }, item.id)
|
|
4163
|
+
) });
|
|
4164
|
+
}
|
|
3743
4165
|
const OptionList = React.forwardRef(
|
|
3744
4166
|
function OptionList2({
|
|
3745
4167
|
options,
|
|
@@ -5372,6 +5794,8 @@ const TimeInput = React.forwardRef(
|
|
|
5372
5794
|
}
|
|
5373
5795
|
);
|
|
5374
5796
|
TimeInput.displayName = "TimeInput";
|
|
5797
|
+
exports.Alert = Alert;
|
|
5798
|
+
exports.Badge = Badge;
|
|
5375
5799
|
exports.BottomSheet = BottomSheet;
|
|
5376
5800
|
exports.Button = Button;
|
|
5377
5801
|
exports.Card = Card;
|
|
@@ -5382,6 +5806,7 @@ exports.Dropdown = Dropdown;
|
|
|
5382
5806
|
exports.DropdownMultiple = DropdownMultiple;
|
|
5383
5807
|
exports.Input = Input;
|
|
5384
5808
|
exports.Modal = Modal;
|
|
5809
|
+
exports.Notification = Notification;
|
|
5385
5810
|
exports.OptionList = OptionList;
|
|
5386
5811
|
exports.Radio = Radio;
|
|
5387
5812
|
exports.SearchInput = SearchInput;
|
|
@@ -5395,6 +5820,8 @@ exports.TableRow = TableRow;
|
|
|
5395
5820
|
exports.Tag = Tag;
|
|
5396
5821
|
exports.TextArea = TextArea;
|
|
5397
5822
|
exports.TimeInput = TimeInput;
|
|
5823
|
+
exports.Toast = Toast;
|
|
5824
|
+
exports.ToastStack = ToastStack;
|
|
5398
5825
|
exports.Toggle = Toggle;
|
|
5399
5826
|
exports.cn = cn;
|
|
5400
5827
|
exports.useIsMobile = useIsMobile;
|