@sarunyu/system-one 4.5.1 → 4.6.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/AGENTS.md +25 -4
- package/DESIGN.md +263 -0
- package/dist/index.cjs +166 -114
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +167 -115
- package/dist/index.js.map +1 -1
- package/dist/src/components/badge.d.ts.map +1 -1
- package/dist/src/components/card.d.ts +1 -1
- package/dist/src/components/card.d.ts.map +1 -1
- package/dist/src/components/checkbox.d.ts +4 -0
- package/dist/src/components/checkbox.d.ts.map +1 -1
- package/dist/src/components/notification.d.ts +6 -1
- package/dist/src/components/notification.d.ts.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/style.css +1 -1
- package/llms.txt +21 -5
- package/package.json +5 -1
package/dist/index.cjs
CHANGED
|
@@ -263,11 +263,10 @@ function Badge({
|
|
|
263
263
|
...props
|
|
264
264
|
}) {
|
|
265
265
|
const hasCount = count > 0;
|
|
266
|
-
const isActive = hasCount;
|
|
267
266
|
const resolvedNotificationState = notificationState ?? (hasCount ? "noti" : "default");
|
|
268
267
|
const notificationIsFilled = resolvedNotificationState === "active" || resolvedNotificationState === "noti";
|
|
269
268
|
const showNotificationDot = resolvedNotificationState === "noti" && hasCount;
|
|
270
|
-
const visualIcon = variant === "notification" ?
|
|
269
|
+
const visualIcon = variant === "notification" ? /* @__PURE__ */ jsxRuntime.jsx(react.BellSimple, { size: 18, weight: notificationIsFilled ? "fill" : "regular" }) : icon ?? /* @__PURE__ */ jsxRuntime.jsx(react.FunnelSimple, { size: 18, weight: "regular" });
|
|
271
270
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("relative inline-flex", className), children: [
|
|
272
271
|
variant === "notification" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
273
272
|
Button,
|
|
@@ -275,10 +274,7 @@ function Badge({
|
|
|
275
274
|
"aria-label": "Notification",
|
|
276
275
|
size: "icon-xs",
|
|
277
276
|
variant: "plain-black",
|
|
278
|
-
className:
|
|
279
|
-
"text-subtle-text",
|
|
280
|
-
notificationIsFilled && "text-primary-action"
|
|
281
|
-
),
|
|
277
|
+
className: "text-icon-brand [&>span]:!h-[18px] [&>span]:!w-[18px]",
|
|
282
278
|
...props,
|
|
283
279
|
children: visualIcon
|
|
284
280
|
}
|
|
@@ -287,8 +283,8 @@ function Badge({
|
|
|
287
283
|
{
|
|
288
284
|
"aria-label": label,
|
|
289
285
|
size: "icon-md",
|
|
290
|
-
variant:
|
|
291
|
-
className: cn(
|
|
286
|
+
variant: hasCount ? "outline" : "outline-black",
|
|
287
|
+
className: cn(hasCount && "bg-primary-action-light border-primary-action-light"),
|
|
292
288
|
...props,
|
|
293
289
|
children: visualIcon
|
|
294
290
|
}
|
|
@@ -297,8 +293,8 @@ function Badge({
|
|
|
297
293
|
{
|
|
298
294
|
size: "md",
|
|
299
295
|
leftIcon: visualIcon,
|
|
300
|
-
variant:
|
|
301
|
-
className: cn(
|
|
296
|
+
variant: hasCount ? "outline" : "outline-black",
|
|
297
|
+
className: cn(hasCount && "bg-primary-action-light border-primary-action-light"),
|
|
302
298
|
...props,
|
|
303
299
|
children: label
|
|
304
300
|
}
|
|
@@ -307,10 +303,10 @@ function Badge({
|
|
|
307
303
|
"div",
|
|
308
304
|
{
|
|
309
305
|
className: cn(
|
|
310
|
-
"absolute flex items-center justify-center rounded-[60px]
|
|
311
|
-
variant === "notification" ? "-right-0.5 -top-0.5
|
|
306
|
+
"absolute flex h-[14px] min-h-[14px] min-w-[14px] items-center justify-center rounded-[60px]",
|
|
307
|
+
variant === "notification" ? "-right-0.5 -top-0.5 bg-destructive px-[2.5px]" : "-right-1 -top-[7px] h-4 min-w-4 bg-primary-action"
|
|
312
308
|
),
|
|
313
|
-
children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-center text-xs leading-4 text-
|
|
309
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-center text-xs leading-4 font-normal text-text-default-white", children: formatCount(count, maxCount) })
|
|
314
310
|
}
|
|
315
311
|
)
|
|
316
312
|
] });
|
|
@@ -382,6 +378,22 @@ const Alert = React.forwardRef(function Alert2({ status = "normal", message, mul
|
|
|
382
378
|
);
|
|
383
379
|
});
|
|
384
380
|
Alert.displayName = "Alert";
|
|
381
|
+
const MOBILE_BREAKPOINT = 768;
|
|
382
|
+
function useIsMobile() {
|
|
383
|
+
const [isMobile, setIsMobile] = React__namespace.useState(
|
|
384
|
+
void 0
|
|
385
|
+
);
|
|
386
|
+
React__namespace.useEffect(() => {
|
|
387
|
+
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
|
|
388
|
+
const onChange = () => {
|
|
389
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
390
|
+
};
|
|
391
|
+
mql.addEventListener("change", onChange);
|
|
392
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
393
|
+
return () => mql.removeEventListener("change", onChange);
|
|
394
|
+
}, []);
|
|
395
|
+
return !!isMobile;
|
|
396
|
+
}
|
|
385
397
|
function BannerMedia({ src, alt }) {
|
|
386
398
|
if (!src) {
|
|
387
399
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-hidden": "true", className: "absolute inset-0 bg-muted" });
|
|
@@ -489,7 +501,7 @@ const tagConfig = {
|
|
|
489
501
|
};
|
|
490
502
|
const Card = React.forwardRef(function Card2({
|
|
491
503
|
variant = "default",
|
|
492
|
-
size
|
|
504
|
+
size: sizeProp,
|
|
493
505
|
children,
|
|
494
506
|
title,
|
|
495
507
|
locked = true,
|
|
@@ -513,9 +525,11 @@ const Card = React.forwardRef(function Card2({
|
|
|
513
525
|
// live
|
|
514
526
|
duration
|
|
515
527
|
}, ref) {
|
|
528
|
+
const isMobile = useIsMobile();
|
|
529
|
+
const size = sizeProp ?? (isMobile ? "mobile" : "desktop");
|
|
516
530
|
const bannerSrc = image ?? "";
|
|
517
531
|
if (variant === "default") {
|
|
518
|
-
const shellPadding = size === "desktop" ? "p-4" :
|
|
532
|
+
const shellPadding = size === "desktop" ? "p-4" : "p-3";
|
|
519
533
|
const shellRadius = size === "mobile" ? "rounded-[6px]" : "rounded-[8px]";
|
|
520
534
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
521
535
|
"div",
|
|
@@ -973,7 +987,8 @@ function NewsContent({
|
|
|
973
987
|
}
|
|
974
988
|
function CheckboxVisual({
|
|
975
989
|
state,
|
|
976
|
-
disabled
|
|
990
|
+
disabled,
|
|
991
|
+
error
|
|
977
992
|
}) {
|
|
978
993
|
if (state === "default") {
|
|
979
994
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -982,12 +997,12 @@ function CheckboxVisual({
|
|
|
982
997
|
"aria-hidden": "true",
|
|
983
998
|
className: cn(
|
|
984
999
|
"block w-4 h-4 rounded-[2px] border-[1.5px]",
|
|
985
|
-
disabled ? "bg-disabled-bg border-[var(--fill-black-100)]" : "bg-background border-[var(--fill-black-200)]"
|
|
1000
|
+
disabled ? "bg-disabled-bg border-[var(--fill-black-100)]" : error ? "bg-background border-destructive" : "bg-background border-[var(--fill-black-200)]"
|
|
986
1001
|
)
|
|
987
1002
|
}
|
|
988
1003
|
);
|
|
989
1004
|
}
|
|
990
|
-
const containerFill = disabled ? "var(--
|
|
1005
|
+
const containerFill = disabled ? "var(--disabled-bg)" : "var(--fill-p1-600)";
|
|
991
1006
|
const iconFill = disabled ? "var(--fill-gray-400)" : "var(--fill-white-1000)";
|
|
992
1007
|
if (state === "checked") {
|
|
993
1008
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -1049,6 +1064,8 @@ function CheckboxVisual({
|
|
|
1049
1064
|
const Checkbox = React.forwardRef(function Checkbox2({
|
|
1050
1065
|
checked = false,
|
|
1051
1066
|
disabled = false,
|
|
1067
|
+
error = false,
|
|
1068
|
+
errorMessage = "Error message",
|
|
1052
1069
|
label,
|
|
1053
1070
|
description,
|
|
1054
1071
|
variant = "text",
|
|
@@ -1080,59 +1097,72 @@ const Checkbox = React.forwardRef(function Checkbox2({
|
|
|
1080
1097
|
const hasText = label !== void 0 || description !== void 0;
|
|
1081
1098
|
const hasActiveBorder = state === "checked" || state === "indeterminate";
|
|
1082
1099
|
const isButton = variant === "button";
|
|
1083
|
-
const buttonBorder = disabled ? "border-[var(--fill-black-100)]" : hasActiveBorder ? "border-primary-action" : "border-[var(--fill-black-200)]";
|
|
1100
|
+
const buttonBorder = disabled ? "border-[var(--fill-black-100)]" : error ? "border-destructive" : hasActiveBorder ? "border-primary-action" : "border-[var(--fill-black-200)]";
|
|
1101
|
+
const showError = error && !disabled;
|
|
1084
1102
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1085
1103
|
"label",
|
|
1086
1104
|
{
|
|
1087
1105
|
className: cn(
|
|
1088
|
-
"
|
|
1089
|
-
description ? "items-start" : "items-center",
|
|
1106
|
+
"flex flex-col select-none",
|
|
1090
1107
|
disabled ? "cursor-not-allowed" : "cursor-pointer",
|
|
1091
|
-
isButton && cn("bg-background rounded-lg border py-2.5 pl-3 pr-4", buttonBorder),
|
|
1092
1108
|
className
|
|
1093
1109
|
),
|
|
1094
1110
|
children: [
|
|
1095
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1111
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1112
|
+
"span",
|
|
1113
|
+
{
|
|
1114
|
+
className: cn(
|
|
1115
|
+
"inline-flex gap-1",
|
|
1116
|
+
description ? "items-start" : "items-center",
|
|
1117
|
+
isButton && cn("bg-background rounded-lg border py-2.5 pl-3 pr-4", buttonBorder)
|
|
1118
|
+
),
|
|
1119
|
+
children: [
|
|
1120
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "relative inline-flex items-center justify-center w-6 h-6 shrink-0", children: [
|
|
1121
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1122
|
+
"input",
|
|
1123
|
+
{
|
|
1124
|
+
ref: setRefs,
|
|
1125
|
+
id,
|
|
1126
|
+
name,
|
|
1127
|
+
value,
|
|
1128
|
+
type: "checkbox",
|
|
1129
|
+
checked: checked === true,
|
|
1130
|
+
disabled,
|
|
1131
|
+
onChange: handleChange,
|
|
1132
|
+
"aria-label": ariaLabel,
|
|
1133
|
+
"aria-checked": checked === "indeterminate" ? "mixed" : checked,
|
|
1134
|
+
"aria-invalid": showError || void 0,
|
|
1135
|
+
className: "absolute inset-0 w-full h-full opacity-0 m-0 cursor-[inherit] disabled:cursor-[inherit]"
|
|
1136
|
+
}
|
|
1137
|
+
),
|
|
1138
|
+
/* @__PURE__ */ jsxRuntime.jsx(CheckboxVisual, { state, disabled, error: showError })
|
|
1139
|
+
] }),
|
|
1140
|
+
hasText && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex flex-col", children: [
|
|
1141
|
+
label !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1142
|
+
"span",
|
|
1143
|
+
{
|
|
1144
|
+
className: cn(
|
|
1145
|
+
"text-base leading-6",
|
|
1146
|
+
disabled ? "text-disabled" : "text-foreground"
|
|
1147
|
+
),
|
|
1148
|
+
children: label
|
|
1149
|
+
}
|
|
1150
|
+
),
|
|
1151
|
+
description !== void 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1152
|
+
"span",
|
|
1153
|
+
{
|
|
1154
|
+
className: cn(
|
|
1155
|
+
"text-xs leading-4",
|
|
1156
|
+
disabled ? "text-disabled" : "text-subtle-text"
|
|
1157
|
+
),
|
|
1158
|
+
children: description
|
|
1159
|
+
}
|
|
1160
|
+
)
|
|
1161
|
+
] })
|
|
1162
|
+
]
|
|
1163
|
+
}
|
|
1164
|
+
),
|
|
1165
|
+
showError && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mt-1 ml-7 text-xs text-destructive", children: errorMessage })
|
|
1136
1166
|
]
|
|
1137
1167
|
}
|
|
1138
1168
|
);
|
|
@@ -1400,22 +1430,6 @@ const Chip = React.forwardRef(function Chip2({
|
|
|
1400
1430
|
);
|
|
1401
1431
|
});
|
|
1402
1432
|
Chip.displayName = "Chip";
|
|
1403
|
-
const MOBILE_BREAKPOINT = 768;
|
|
1404
|
-
function useIsMobile() {
|
|
1405
|
-
const [isMobile, setIsMobile] = React__namespace.useState(
|
|
1406
|
-
void 0
|
|
1407
|
-
);
|
|
1408
|
-
React__namespace.useEffect(() => {
|
|
1409
|
-
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
|
|
1410
|
-
const onChange = () => {
|
|
1411
|
-
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
1412
|
-
};
|
|
1413
|
-
mql.addEventListener("change", onChange);
|
|
1414
|
-
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
1415
|
-
return () => mql.removeEventListener("change", onChange);
|
|
1416
|
-
}, []);
|
|
1417
|
-
return !!isMobile;
|
|
1418
|
-
}
|
|
1419
1433
|
function Drawer({
|
|
1420
1434
|
...props
|
|
1421
1435
|
}) {
|
|
@@ -3666,7 +3680,7 @@ const Input = React.forwardRef(function Input2({
|
|
|
3666
3680
|
Input.displayName = "Input";
|
|
3667
3681
|
const ALERT_CONFIG = {
|
|
3668
3682
|
warning: {
|
|
3669
|
-
titleColor: "var(--
|
|
3683
|
+
titleColor: "var(--text-warning-primary)",
|
|
3670
3684
|
background: "https://www.figma.com/api/mcp/asset/f4ca68ad-5732-4124-9ff4-cfb69330cc02",
|
|
3671
3685
|
layers: [
|
|
3672
3686
|
{
|
|
@@ -3684,7 +3698,7 @@ const ALERT_CONFIG = {
|
|
|
3684
3698
|
]
|
|
3685
3699
|
},
|
|
3686
3700
|
success: {
|
|
3687
|
-
titleColor: "var(--success)",
|
|
3701
|
+
titleColor: "var(--text-success-primary)",
|
|
3688
3702
|
background: "https://www.figma.com/api/mcp/asset/2a865e6f-8a92-4496-88b5-71ac99e2c385",
|
|
3689
3703
|
layers: [
|
|
3690
3704
|
{
|
|
@@ -3698,7 +3712,7 @@ const ALERT_CONFIG = {
|
|
|
3698
3712
|
]
|
|
3699
3713
|
},
|
|
3700
3714
|
danger: {
|
|
3701
|
-
titleColor: "var(--
|
|
3715
|
+
titleColor: "var(--text-danger-primary)",
|
|
3702
3716
|
background: "https://www.figma.com/api/mcp/asset/c7a65595-684e-4a04-b7fd-d443951f680a",
|
|
3703
3717
|
layers: [
|
|
3704
3718
|
{
|
|
@@ -3883,18 +3897,19 @@ function NotificationDivider({ label }) {
|
|
|
3883
3897
|
}
|
|
3884
3898
|
function NotificationRow({
|
|
3885
3899
|
item,
|
|
3886
|
-
onItemClick
|
|
3900
|
+
onItemClick,
|
|
3901
|
+
hideIndicator = false,
|
|
3902
|
+
demoteNewBackground = false
|
|
3887
3903
|
}) {
|
|
3888
3904
|
const rowType = item.type ?? "icon";
|
|
3889
3905
|
const showImage = rowType === "image";
|
|
3890
|
-
const
|
|
3906
|
+
const status = item.status ?? (item.unread ? "unread" : "read");
|
|
3907
|
+
const showIndicator = (status === "new" || status === "unread") && !hideIndicator;
|
|
3908
|
+
const rowBackground = status === "new" && !demoteNewBackground ? "bg-muted" : "bg-background";
|
|
3891
3909
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3892
3910
|
"div",
|
|
3893
3911
|
{
|
|
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
|
-
),
|
|
3912
|
+
className: cn("flex w-full items-start gap-3 px-4 py-3", rowBackground),
|
|
3898
3913
|
role: "button",
|
|
3899
3914
|
tabIndex: 0,
|
|
3900
3915
|
onClick: () => onItemClick == null ? void 0 : onItemClick(item),
|
|
@@ -3912,22 +3927,11 @@ function NotificationRow({
|
|
|
3912
3927
|
className: "h-10 w-10 rounded object-cover",
|
|
3913
3928
|
src: item.imageSrc
|
|
3914
3929
|
}
|
|
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.
|
|
3916
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
3917
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3918
|
-
|
|
3919
|
-
|
|
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
|
-
] }),
|
|
3930
|
+
) : /* @__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.Gift, { size: 20, weight: "regular" }) }) }),
|
|
3931
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1 space-y-1", children: [
|
|
3932
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "line-clamp-2 text-base leading-6 font-semibold text-foreground", children: item.title }),
|
|
3933
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "line-clamp-3 text-sm leading-5 text-muted-foreground", children: item.description }),
|
|
3934
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs leading-4 text-muted-foreground", children: item.time }),
|
|
3931
3935
|
item.actionLabel && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3932
3936
|
Button,
|
|
3933
3937
|
{
|
|
@@ -3942,7 +3946,14 @@ function NotificationRow({
|
|
|
3942
3946
|
children: item.actionLabel
|
|
3943
3947
|
}
|
|
3944
3948
|
)
|
|
3945
|
-
] })
|
|
3949
|
+
] }),
|
|
3950
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex w-2 shrink-0 items-start justify-center pt-2", children: showIndicator ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
3951
|
+
"span",
|
|
3952
|
+
{
|
|
3953
|
+
"aria-hidden": "true",
|
|
3954
|
+
className: "h-2 w-2 rounded-full bg-destructive"
|
|
3955
|
+
}
|
|
3956
|
+
) : null })
|
|
3946
3957
|
]
|
|
3947
3958
|
}
|
|
3948
3959
|
);
|
|
@@ -3953,6 +3964,7 @@ const Notification = React.forwardRef(
|
|
|
3953
3964
|
badgeCount,
|
|
3954
3965
|
panelWidth = 375,
|
|
3955
3966
|
emptyText = "No notifications",
|
|
3967
|
+
showGroupLabels = true,
|
|
3956
3968
|
clearBadgeOnOpen = true,
|
|
3957
3969
|
open,
|
|
3958
3970
|
defaultOpen,
|
|
@@ -3964,16 +3976,23 @@ const Notification = React.forwardRef(
|
|
|
3964
3976
|
}, ref) {
|
|
3965
3977
|
const [internalOpen, setInternalOpen] = React.useState(defaultOpen ?? false);
|
|
3966
3978
|
const [isBadgeCleared, setIsBadgeCleared] = React.useState(false);
|
|
3979
|
+
const [clickedItemIds, setClickedItemIds] = React.useState(/* @__PURE__ */ new Set());
|
|
3980
|
+
const [wasDismissed, setWasDismissed] = React.useState(false);
|
|
3981
|
+
const [mobileAlign, setMobileAlign] = React.useState(null);
|
|
3982
|
+
const triggerRef = React.useRef(null);
|
|
3967
3983
|
const controlled = open !== void 0;
|
|
3968
3984
|
const resolvedOpen = controlled ? open : internalOpen;
|
|
3969
|
-
const
|
|
3985
|
+
const newCount = React.useMemo(
|
|
3970
3986
|
() => groups.reduce(
|
|
3971
|
-
(acc, group) => acc + group.items.filter((item) =>
|
|
3987
|
+
(acc, group) => acc + group.items.filter((item) => {
|
|
3988
|
+
const status = item.status ?? (item.unread ? "unread" : "read");
|
|
3989
|
+
return status === "new";
|
|
3990
|
+
}).length,
|
|
3972
3991
|
0
|
|
3973
3992
|
),
|
|
3974
3993
|
[groups]
|
|
3975
3994
|
);
|
|
3976
|
-
const nextCount = badgeCount ??
|
|
3995
|
+
const nextCount = badgeCount ?? newCount;
|
|
3977
3996
|
const prevCountRef = React.useRef(nextCount);
|
|
3978
3997
|
React.useEffect(() => {
|
|
3979
3998
|
const prevCount = prevCountRef.current;
|
|
@@ -3982,9 +4001,29 @@ const Notification = React.forwardRef(
|
|
|
3982
4001
|
}
|
|
3983
4002
|
prevCountRef.current = nextCount;
|
|
3984
4003
|
}, [nextCount]);
|
|
4004
|
+
React.useEffect(() => {
|
|
4005
|
+
const update = () => {
|
|
4006
|
+
if (window.innerWidth > 640 || !triggerRef.current) {
|
|
4007
|
+
setMobileAlign(null);
|
|
4008
|
+
return;
|
|
4009
|
+
}
|
|
4010
|
+
const contentWidth = Math.min(panelWidth, window.innerWidth - 32);
|
|
4011
|
+
const triggerLeft = triggerRef.current.getBoundingClientRect().left;
|
|
4012
|
+
setMobileAlign({
|
|
4013
|
+
alignOffset: (window.innerWidth - contentWidth) / 2 - triggerLeft,
|
|
4014
|
+
width: contentWidth
|
|
4015
|
+
});
|
|
4016
|
+
};
|
|
4017
|
+
update();
|
|
4018
|
+
window.addEventListener("resize", update);
|
|
4019
|
+
return () => window.removeEventListener("resize", update);
|
|
4020
|
+
}, [panelWidth]);
|
|
3985
4021
|
const displayCount = clearBadgeOnOpen && isBadgeCleared ? 0 : nextCount;
|
|
3986
4022
|
const hasItems = groups.some((group) => group.items.length > 0);
|
|
3987
4023
|
const handleOpenChange = (next) => {
|
|
4024
|
+
if (resolvedOpen && !next) {
|
|
4025
|
+
setWasDismissed(true);
|
|
4026
|
+
}
|
|
3988
4027
|
if (next && clearBadgeOnOpen && nextCount > 0) {
|
|
3989
4028
|
setIsBadgeCleared(true);
|
|
3990
4029
|
onBadgeCleared == null ? void 0 : onBadgeCleared();
|
|
@@ -3992,8 +4031,17 @@ const Notification = React.forwardRef(
|
|
|
3992
4031
|
if (!controlled) setInternalOpen(next);
|
|
3993
4032
|
onOpenChange == null ? void 0 : onOpenChange(next);
|
|
3994
4033
|
};
|
|
4034
|
+
const handleItemClick = (item) => {
|
|
4035
|
+
setClickedItemIds((prev) => {
|
|
4036
|
+
if (prev.has(item.id)) return prev;
|
|
4037
|
+
const next = new Set(prev);
|
|
4038
|
+
next.add(item.id);
|
|
4039
|
+
return next;
|
|
4040
|
+
});
|
|
4041
|
+
onItemClick == null ? void 0 : onItemClick(item);
|
|
4042
|
+
};
|
|
3995
4043
|
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(
|
|
4044
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: cn("inline-flex", className), children: /* @__PURE__ */ jsxRuntime.jsx(Popover__namespace.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx("div", { ref: triggerRef, className: "relative", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3997
4045
|
Badge,
|
|
3998
4046
|
{
|
|
3999
4047
|
variant: "notification",
|
|
@@ -4006,22 +4054,26 @@ const Notification = React.forwardRef(
|
|
|
4006
4054
|
/* @__PURE__ */ jsxRuntime.jsx(Popover__namespace.Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
4007
4055
|
Popover__namespace.Content,
|
|
4008
4056
|
{
|
|
4009
|
-
align: "end",
|
|
4057
|
+
align: mobileAlign ? "start" : "end",
|
|
4058
|
+
alignOffset: (mobileAlign == null ? void 0 : mobileAlign.alignOffset) ?? 0,
|
|
4059
|
+
avoidCollisions: !mobileAlign,
|
|
4010
4060
|
sideOffset: 10,
|
|
4011
4061
|
className: cn(
|
|
4012
4062
|
"z-50 overflow-hidden rounded-lg border border-border bg-background shadow-lg",
|
|
4013
4063
|
panelClassName
|
|
4014
4064
|
),
|
|
4015
|
-
style: { width: panelWidth },
|
|
4016
|
-
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-h-[480px] overflow-y-auto
|
|
4065
|
+
style: { width: (mobileAlign == null ? void 0 : mobileAlign.width) ?? panelWidth },
|
|
4066
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-h-[480px] overflow-y-auto", children: [
|
|
4017
4067
|
!hasItems && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-8 text-center text-sm text-muted-foreground", children: emptyText }),
|
|
4018
4068
|
groups.map((group) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full", children: [
|
|
4019
|
-
/* @__PURE__ */ jsxRuntime.jsx(NotificationDivider, { label: group.label }),
|
|
4069
|
+
showGroupLabels && group.label ? /* @__PURE__ */ jsxRuntime.jsx(NotificationDivider, { label: group.label }) : null,
|
|
4020
4070
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "divide-y divide-divider", children: group.items.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
4021
4071
|
NotificationRow,
|
|
4022
4072
|
{
|
|
4023
4073
|
item,
|
|
4024
|
-
onItemClick
|
|
4074
|
+
onItemClick: handleItemClick,
|
|
4075
|
+
hideIndicator: clickedItemIds.has(item.id),
|
|
4076
|
+
demoteNewBackground: wasDismissed
|
|
4025
4077
|
},
|
|
4026
4078
|
item.id
|
|
4027
4079
|
)) })
|