@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.js
CHANGED
|
@@ -4,7 +4,7 @@ import * as React from "react";
|
|
|
4
4
|
import React__default, { forwardRef, useState, useRef, useEffect, useCallback, useId, useMemo, useLayoutEffect, useContext, createContext } from "react";
|
|
5
5
|
import { clsx } from "clsx";
|
|
6
6
|
import { twMerge } from "tailwind-merge";
|
|
7
|
-
import { BellSimple, FunnelSimple, CheckCircle, Warning, XCircle, Info, BookmarkSimpleIcon, BroadcastIcon as BroadcastIcon$1, CalendarBlank, MapPin, Users, Lock, Check, Plus, Circle, Minus, CaretLeft, CaretRight, CaretDoubleLeft, CaretDoubleRight, CaretUp, CaretDown, X, ImageSquare, MegaphoneSimple, GearSix, MagnifyingGlass, ArrowUp, ArrowDown, ArrowsDownUp, Clock } from "@phosphor-icons/react";
|
|
7
|
+
import { BellSimple, FunnelSimple, CheckCircle, Warning, XCircle, Info, BookmarkSimpleIcon, BroadcastIcon as BroadcastIcon$1, CalendarBlank, MapPin, Users, Lock, Check, Plus, Circle, Minus, CaretLeft, CaretRight, CaretDoubleLeft, CaretDoubleRight, CaretUp, CaretDown, X, ImageSquare, Gift, MegaphoneSimple, GearSix, MagnifyingGlass, ArrowUp, ArrowDown, ArrowsDownUp, Clock } from "@phosphor-icons/react";
|
|
8
8
|
import { DayPicker, useNavigation } from "react-day-picker";
|
|
9
9
|
import * as Popover from "@radix-ui/react-popover";
|
|
10
10
|
import { Drawer as Drawer$1 } from "vaul";
|
|
@@ -244,11 +244,10 @@ function Badge({
|
|
|
244
244
|
...props
|
|
245
245
|
}) {
|
|
246
246
|
const hasCount = count > 0;
|
|
247
|
-
const isActive = hasCount;
|
|
248
247
|
const resolvedNotificationState = notificationState ?? (hasCount ? "noti" : "default");
|
|
249
248
|
const notificationIsFilled = resolvedNotificationState === "active" || resolvedNotificationState === "noti";
|
|
250
249
|
const showNotificationDot = resolvedNotificationState === "noti" && hasCount;
|
|
251
|
-
const visualIcon = variant === "notification" ?
|
|
250
|
+
const visualIcon = variant === "notification" ? /* @__PURE__ */ jsx(BellSimple, { size: 18, weight: notificationIsFilled ? "fill" : "regular" }) : icon ?? /* @__PURE__ */ jsx(FunnelSimple, { size: 18, weight: "regular" });
|
|
252
251
|
return /* @__PURE__ */ jsxs("div", { className: cn("relative inline-flex", className), children: [
|
|
253
252
|
variant === "notification" ? /* @__PURE__ */ jsx(
|
|
254
253
|
Button,
|
|
@@ -256,10 +255,7 @@ function Badge({
|
|
|
256
255
|
"aria-label": "Notification",
|
|
257
256
|
size: "icon-xs",
|
|
258
257
|
variant: "plain-black",
|
|
259
|
-
className:
|
|
260
|
-
"text-subtle-text",
|
|
261
|
-
notificationIsFilled && "text-primary-action"
|
|
262
|
-
),
|
|
258
|
+
className: "text-icon-brand [&>span]:!h-[18px] [&>span]:!w-[18px]",
|
|
263
259
|
...props,
|
|
264
260
|
children: visualIcon
|
|
265
261
|
}
|
|
@@ -268,8 +264,8 @@ function Badge({
|
|
|
268
264
|
{
|
|
269
265
|
"aria-label": label,
|
|
270
266
|
size: "icon-md",
|
|
271
|
-
variant:
|
|
272
|
-
className: cn(
|
|
267
|
+
variant: hasCount ? "outline" : "outline-black",
|
|
268
|
+
className: cn(hasCount && "bg-primary-action-light border-primary-action-light"),
|
|
273
269
|
...props,
|
|
274
270
|
children: visualIcon
|
|
275
271
|
}
|
|
@@ -278,8 +274,8 @@ function Badge({
|
|
|
278
274
|
{
|
|
279
275
|
size: "md",
|
|
280
276
|
leftIcon: visualIcon,
|
|
281
|
-
variant:
|
|
282
|
-
className: cn(
|
|
277
|
+
variant: hasCount ? "outline" : "outline-black",
|
|
278
|
+
className: cn(hasCount && "bg-primary-action-light border-primary-action-light"),
|
|
283
279
|
...props,
|
|
284
280
|
children: label
|
|
285
281
|
}
|
|
@@ -288,10 +284,10 @@ function Badge({
|
|
|
288
284
|
"div",
|
|
289
285
|
{
|
|
290
286
|
className: cn(
|
|
291
|
-
"absolute flex items-center justify-center rounded-[60px]
|
|
292
|
-
variant === "notification" ? "-right-0.5 -top-0.5
|
|
287
|
+
"absolute flex h-[14px] min-h-[14px] min-w-[14px] items-center justify-center rounded-[60px]",
|
|
288
|
+
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"
|
|
293
289
|
),
|
|
294
|
-
children: /* @__PURE__ */ jsx("p", { className: "text-center text-xs leading-4 text-
|
|
290
|
+
children: /* @__PURE__ */ jsx("p", { className: "text-center text-xs leading-4 font-normal text-text-default-white", children: formatCount(count, maxCount) })
|
|
295
291
|
}
|
|
296
292
|
)
|
|
297
293
|
] });
|
|
@@ -363,6 +359,22 @@ const Alert = forwardRef(function Alert2({ status = "normal", message, multiline
|
|
|
363
359
|
);
|
|
364
360
|
});
|
|
365
361
|
Alert.displayName = "Alert";
|
|
362
|
+
const MOBILE_BREAKPOINT = 768;
|
|
363
|
+
function useIsMobile() {
|
|
364
|
+
const [isMobile, setIsMobile] = React.useState(
|
|
365
|
+
void 0
|
|
366
|
+
);
|
|
367
|
+
React.useEffect(() => {
|
|
368
|
+
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
|
|
369
|
+
const onChange = () => {
|
|
370
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
371
|
+
};
|
|
372
|
+
mql.addEventListener("change", onChange);
|
|
373
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
374
|
+
return () => mql.removeEventListener("change", onChange);
|
|
375
|
+
}, []);
|
|
376
|
+
return !!isMobile;
|
|
377
|
+
}
|
|
366
378
|
function BannerMedia({ src, alt }) {
|
|
367
379
|
if (!src) {
|
|
368
380
|
return /* @__PURE__ */ jsx("div", { "aria-hidden": "true", className: "absolute inset-0 bg-muted" });
|
|
@@ -470,7 +482,7 @@ const tagConfig = {
|
|
|
470
482
|
};
|
|
471
483
|
const Card = forwardRef(function Card2({
|
|
472
484
|
variant = "default",
|
|
473
|
-
size
|
|
485
|
+
size: sizeProp,
|
|
474
486
|
children,
|
|
475
487
|
title,
|
|
476
488
|
locked = true,
|
|
@@ -494,9 +506,11 @@ const Card = forwardRef(function Card2({
|
|
|
494
506
|
// live
|
|
495
507
|
duration
|
|
496
508
|
}, ref) {
|
|
509
|
+
const isMobile = useIsMobile();
|
|
510
|
+
const size = sizeProp ?? (isMobile ? "mobile" : "desktop");
|
|
497
511
|
const bannerSrc = image ?? "";
|
|
498
512
|
if (variant === "default") {
|
|
499
|
-
const shellPadding = size === "desktop" ? "p-4" :
|
|
513
|
+
const shellPadding = size === "desktop" ? "p-4" : "p-3";
|
|
500
514
|
const shellRadius = size === "mobile" ? "rounded-[6px]" : "rounded-[8px]";
|
|
501
515
|
return /* @__PURE__ */ jsx(
|
|
502
516
|
"div",
|
|
@@ -954,7 +968,8 @@ function NewsContent({
|
|
|
954
968
|
}
|
|
955
969
|
function CheckboxVisual({
|
|
956
970
|
state,
|
|
957
|
-
disabled
|
|
971
|
+
disabled,
|
|
972
|
+
error
|
|
958
973
|
}) {
|
|
959
974
|
if (state === "default") {
|
|
960
975
|
return /* @__PURE__ */ jsx(
|
|
@@ -963,12 +978,12 @@ function CheckboxVisual({
|
|
|
963
978
|
"aria-hidden": "true",
|
|
964
979
|
className: cn(
|
|
965
980
|
"block w-4 h-4 rounded-[2px] border-[1.5px]",
|
|
966
|
-
disabled ? "bg-disabled-bg border-[var(--fill-black-100)]" : "bg-background border-[var(--fill-black-200)]"
|
|
981
|
+
disabled ? "bg-disabled-bg border-[var(--fill-black-100)]" : error ? "bg-background border-destructive" : "bg-background border-[var(--fill-black-200)]"
|
|
967
982
|
)
|
|
968
983
|
}
|
|
969
984
|
);
|
|
970
985
|
}
|
|
971
|
-
const containerFill = disabled ? "var(--
|
|
986
|
+
const containerFill = disabled ? "var(--disabled-bg)" : "var(--fill-p1-600)";
|
|
972
987
|
const iconFill = disabled ? "var(--fill-gray-400)" : "var(--fill-white-1000)";
|
|
973
988
|
if (state === "checked") {
|
|
974
989
|
return /* @__PURE__ */ jsxs(
|
|
@@ -1030,6 +1045,8 @@ function CheckboxVisual({
|
|
|
1030
1045
|
const Checkbox = forwardRef(function Checkbox2({
|
|
1031
1046
|
checked = false,
|
|
1032
1047
|
disabled = false,
|
|
1048
|
+
error = false,
|
|
1049
|
+
errorMessage = "Error message",
|
|
1033
1050
|
label,
|
|
1034
1051
|
description,
|
|
1035
1052
|
variant = "text",
|
|
@@ -1061,59 +1078,72 @@ const Checkbox = forwardRef(function Checkbox2({
|
|
|
1061
1078
|
const hasText = label !== void 0 || description !== void 0;
|
|
1062
1079
|
const hasActiveBorder = state === "checked" || state === "indeterminate";
|
|
1063
1080
|
const isButton = variant === "button";
|
|
1064
|
-
const buttonBorder = disabled ? "border-[var(--fill-black-100)]" : hasActiveBorder ? "border-primary-action" : "border-[var(--fill-black-200)]";
|
|
1081
|
+
const buttonBorder = disabled ? "border-[var(--fill-black-100)]" : error ? "border-destructive" : hasActiveBorder ? "border-primary-action" : "border-[var(--fill-black-200)]";
|
|
1082
|
+
const showError = error && !disabled;
|
|
1065
1083
|
return /* @__PURE__ */ jsxs(
|
|
1066
1084
|
"label",
|
|
1067
1085
|
{
|
|
1068
1086
|
className: cn(
|
|
1069
|
-
"
|
|
1070
|
-
description ? "items-start" : "items-center",
|
|
1087
|
+
"flex flex-col select-none",
|
|
1071
1088
|
disabled ? "cursor-not-allowed" : "cursor-pointer",
|
|
1072
|
-
isButton && cn("bg-background rounded-lg border py-2.5 pl-3 pr-4", buttonBorder),
|
|
1073
1089
|
className
|
|
1074
1090
|
),
|
|
1075
1091
|
children: [
|
|
1076
|
-
/* @__PURE__ */ jsxs(
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1092
|
+
/* @__PURE__ */ jsxs(
|
|
1093
|
+
"span",
|
|
1094
|
+
{
|
|
1095
|
+
className: cn(
|
|
1096
|
+
"inline-flex gap-1",
|
|
1097
|
+
description ? "items-start" : "items-center",
|
|
1098
|
+
isButton && cn("bg-background rounded-lg border py-2.5 pl-3 pr-4", buttonBorder)
|
|
1099
|
+
),
|
|
1100
|
+
children: [
|
|
1101
|
+
/* @__PURE__ */ jsxs("span", { className: "relative inline-flex items-center justify-center w-6 h-6 shrink-0", children: [
|
|
1102
|
+
/* @__PURE__ */ jsx(
|
|
1103
|
+
"input",
|
|
1104
|
+
{
|
|
1105
|
+
ref: setRefs,
|
|
1106
|
+
id,
|
|
1107
|
+
name,
|
|
1108
|
+
value,
|
|
1109
|
+
type: "checkbox",
|
|
1110
|
+
checked: checked === true,
|
|
1111
|
+
disabled,
|
|
1112
|
+
onChange: handleChange,
|
|
1113
|
+
"aria-label": ariaLabel,
|
|
1114
|
+
"aria-checked": checked === "indeterminate" ? "mixed" : checked,
|
|
1115
|
+
"aria-invalid": showError || void 0,
|
|
1116
|
+
className: "absolute inset-0 w-full h-full opacity-0 m-0 cursor-[inherit] disabled:cursor-[inherit]"
|
|
1117
|
+
}
|
|
1118
|
+
),
|
|
1119
|
+
/* @__PURE__ */ jsx(CheckboxVisual, { state, disabled, error: showError })
|
|
1120
|
+
] }),
|
|
1121
|
+
hasText && /* @__PURE__ */ jsxs("span", { className: "flex flex-col", children: [
|
|
1122
|
+
label !== void 0 && /* @__PURE__ */ jsx(
|
|
1123
|
+
"span",
|
|
1124
|
+
{
|
|
1125
|
+
className: cn(
|
|
1126
|
+
"text-base leading-6",
|
|
1127
|
+
disabled ? "text-disabled" : "text-foreground"
|
|
1128
|
+
),
|
|
1129
|
+
children: label
|
|
1130
|
+
}
|
|
1131
|
+
),
|
|
1132
|
+
description !== void 0 && /* @__PURE__ */ jsx(
|
|
1133
|
+
"span",
|
|
1134
|
+
{
|
|
1135
|
+
className: cn(
|
|
1136
|
+
"text-xs leading-4",
|
|
1137
|
+
disabled ? "text-disabled" : "text-subtle-text"
|
|
1138
|
+
),
|
|
1139
|
+
children: description
|
|
1140
|
+
}
|
|
1141
|
+
)
|
|
1142
|
+
] })
|
|
1143
|
+
]
|
|
1144
|
+
}
|
|
1145
|
+
),
|
|
1146
|
+
showError && /* @__PURE__ */ jsx("span", { className: "mt-1 ml-7 text-xs text-destructive", children: errorMessage })
|
|
1117
1147
|
]
|
|
1118
1148
|
}
|
|
1119
1149
|
);
|
|
@@ -1381,22 +1411,6 @@ const Chip = forwardRef(function Chip2({
|
|
|
1381
1411
|
);
|
|
1382
1412
|
});
|
|
1383
1413
|
Chip.displayName = "Chip";
|
|
1384
|
-
const MOBILE_BREAKPOINT = 768;
|
|
1385
|
-
function useIsMobile() {
|
|
1386
|
-
const [isMobile, setIsMobile] = React.useState(
|
|
1387
|
-
void 0
|
|
1388
|
-
);
|
|
1389
|
-
React.useEffect(() => {
|
|
1390
|
-
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
|
|
1391
|
-
const onChange = () => {
|
|
1392
|
-
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
1393
|
-
};
|
|
1394
|
-
mql.addEventListener("change", onChange);
|
|
1395
|
-
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
1396
|
-
return () => mql.removeEventListener("change", onChange);
|
|
1397
|
-
}, []);
|
|
1398
|
-
return !!isMobile;
|
|
1399
|
-
}
|
|
1400
1414
|
function Drawer({
|
|
1401
1415
|
...props
|
|
1402
1416
|
}) {
|
|
@@ -3647,7 +3661,7 @@ const Input = forwardRef(function Input2({
|
|
|
3647
3661
|
Input.displayName = "Input";
|
|
3648
3662
|
const ALERT_CONFIG = {
|
|
3649
3663
|
warning: {
|
|
3650
|
-
titleColor: "var(--
|
|
3664
|
+
titleColor: "var(--text-warning-primary)",
|
|
3651
3665
|
background: "https://www.figma.com/api/mcp/asset/f4ca68ad-5732-4124-9ff4-cfb69330cc02",
|
|
3652
3666
|
layers: [
|
|
3653
3667
|
{
|
|
@@ -3665,7 +3679,7 @@ const ALERT_CONFIG = {
|
|
|
3665
3679
|
]
|
|
3666
3680
|
},
|
|
3667
3681
|
success: {
|
|
3668
|
-
titleColor: "var(--success)",
|
|
3682
|
+
titleColor: "var(--text-success-primary)",
|
|
3669
3683
|
background: "https://www.figma.com/api/mcp/asset/2a865e6f-8a92-4496-88b5-71ac99e2c385",
|
|
3670
3684
|
layers: [
|
|
3671
3685
|
{
|
|
@@ -3679,7 +3693,7 @@ const ALERT_CONFIG = {
|
|
|
3679
3693
|
]
|
|
3680
3694
|
},
|
|
3681
3695
|
danger: {
|
|
3682
|
-
titleColor: "var(--
|
|
3696
|
+
titleColor: "var(--text-danger-primary)",
|
|
3683
3697
|
background: "https://www.figma.com/api/mcp/asset/c7a65595-684e-4a04-b7fd-d443951f680a",
|
|
3684
3698
|
layers: [
|
|
3685
3699
|
{
|
|
@@ -3864,18 +3878,19 @@ function NotificationDivider({ label }) {
|
|
|
3864
3878
|
}
|
|
3865
3879
|
function NotificationRow({
|
|
3866
3880
|
item,
|
|
3867
|
-
onItemClick
|
|
3881
|
+
onItemClick,
|
|
3882
|
+
hideIndicator = false,
|
|
3883
|
+
demoteNewBackground = false
|
|
3868
3884
|
}) {
|
|
3869
3885
|
const rowType = item.type ?? "icon";
|
|
3870
3886
|
const showImage = rowType === "image";
|
|
3871
|
-
const
|
|
3887
|
+
const status = item.status ?? (item.unread ? "unread" : "read");
|
|
3888
|
+
const showIndicator = (status === "new" || status === "unread") && !hideIndicator;
|
|
3889
|
+
const rowBackground = status === "new" && !demoteNewBackground ? "bg-muted" : "bg-background";
|
|
3872
3890
|
return /* @__PURE__ */ jsxs(
|
|
3873
3891
|
"div",
|
|
3874
3892
|
{
|
|
3875
|
-
className: cn(
|
|
3876
|
-
"flex w-full items-start gap-3 px-4 py-3",
|
|
3877
|
-
showUnread ? "bg-primary-action-light/40" : "bg-background"
|
|
3878
|
-
),
|
|
3893
|
+
className: cn("flex w-full items-start gap-3 px-4 py-3", rowBackground),
|
|
3879
3894
|
role: "button",
|
|
3880
3895
|
tabIndex: 0,
|
|
3881
3896
|
onClick: () => onItemClick == null ? void 0 : onItemClick(item),
|
|
@@ -3893,22 +3908,11 @@ function NotificationRow({
|
|
|
3893
3908
|
className: "h-10 w-10 rounded object-cover",
|
|
3894
3909
|
src: item.imageSrc
|
|
3895
3910
|
}
|
|
3896
|
-
) : /* @__PURE__ */ jsx("div", { className: "flex h-10 w-10 items-center justify-center rounded bg-disabled-bg text-disabled", children: /* @__PURE__ */ jsx(ImageSquare, { size: 20, weight: "regular" }) }) : /* @__PURE__ */ jsx("div", { className: "flex h-6 w-6 items-center justify-center text-subtle-text", children: item.icon ?? /* @__PURE__ */ jsx(
|
|
3897
|
-
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
3898
|
-
/* @__PURE__ */
|
|
3899
|
-
|
|
3900
|
-
|
|
3901
|
-
showUnread && /* @__PURE__ */ jsx(
|
|
3902
|
-
"span",
|
|
3903
|
-
{
|
|
3904
|
-
"aria-hidden": "true",
|
|
3905
|
-
className: "h-2 w-2 rounded-full bg-primary-action"
|
|
3906
|
-
}
|
|
3907
|
-
),
|
|
3908
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs leading-4 text-muted-foreground", children: item.time })
|
|
3909
|
-
] }),
|
|
3910
|
-
/* @__PURE__ */ jsx("p", { className: "col-start-1 mt-1 line-clamp-2 text-sm leading-5 text-muted-foreground", children: item.description })
|
|
3911
|
-
] }),
|
|
3911
|
+
) : /* @__PURE__ */ jsx("div", { className: "flex h-10 w-10 items-center justify-center rounded bg-disabled-bg text-disabled", children: /* @__PURE__ */ jsx(ImageSquare, { size: 20, weight: "regular" }) }) : /* @__PURE__ */ jsx("div", { className: "flex h-6 w-6 items-center justify-center text-subtle-text", children: item.icon ?? /* @__PURE__ */ jsx(Gift, { size: 20, weight: "regular" }) }) }),
|
|
3912
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 space-y-1", children: [
|
|
3913
|
+
/* @__PURE__ */ jsx("p", { className: "line-clamp-2 text-base leading-6 font-semibold text-foreground", children: item.title }),
|
|
3914
|
+
/* @__PURE__ */ jsx("p", { className: "line-clamp-3 text-sm leading-5 text-muted-foreground", children: item.description }),
|
|
3915
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs leading-4 text-muted-foreground", children: item.time }),
|
|
3912
3916
|
item.actionLabel && /* @__PURE__ */ jsx(
|
|
3913
3917
|
Button,
|
|
3914
3918
|
{
|
|
@@ -3923,7 +3927,14 @@ function NotificationRow({
|
|
|
3923
3927
|
children: item.actionLabel
|
|
3924
3928
|
}
|
|
3925
3929
|
)
|
|
3926
|
-
] })
|
|
3930
|
+
] }),
|
|
3931
|
+
/* @__PURE__ */ jsx("div", { className: "flex w-2 shrink-0 items-start justify-center pt-2", children: showIndicator ? /* @__PURE__ */ jsx(
|
|
3932
|
+
"span",
|
|
3933
|
+
{
|
|
3934
|
+
"aria-hidden": "true",
|
|
3935
|
+
className: "h-2 w-2 rounded-full bg-destructive"
|
|
3936
|
+
}
|
|
3937
|
+
) : null })
|
|
3927
3938
|
]
|
|
3928
3939
|
}
|
|
3929
3940
|
);
|
|
@@ -3934,6 +3945,7 @@ const Notification = forwardRef(
|
|
|
3934
3945
|
badgeCount,
|
|
3935
3946
|
panelWidth = 375,
|
|
3936
3947
|
emptyText = "No notifications",
|
|
3948
|
+
showGroupLabels = true,
|
|
3937
3949
|
clearBadgeOnOpen = true,
|
|
3938
3950
|
open,
|
|
3939
3951
|
defaultOpen,
|
|
@@ -3945,16 +3957,23 @@ const Notification = forwardRef(
|
|
|
3945
3957
|
}, ref) {
|
|
3946
3958
|
const [internalOpen, setInternalOpen] = useState(defaultOpen ?? false);
|
|
3947
3959
|
const [isBadgeCleared, setIsBadgeCleared] = useState(false);
|
|
3960
|
+
const [clickedItemIds, setClickedItemIds] = useState(/* @__PURE__ */ new Set());
|
|
3961
|
+
const [wasDismissed, setWasDismissed] = useState(false);
|
|
3962
|
+
const [mobileAlign, setMobileAlign] = useState(null);
|
|
3963
|
+
const triggerRef = useRef(null);
|
|
3948
3964
|
const controlled = open !== void 0;
|
|
3949
3965
|
const resolvedOpen = controlled ? open : internalOpen;
|
|
3950
|
-
const
|
|
3966
|
+
const newCount = useMemo(
|
|
3951
3967
|
() => groups.reduce(
|
|
3952
|
-
(acc, group) => acc + group.items.filter((item) =>
|
|
3968
|
+
(acc, group) => acc + group.items.filter((item) => {
|
|
3969
|
+
const status = item.status ?? (item.unread ? "unread" : "read");
|
|
3970
|
+
return status === "new";
|
|
3971
|
+
}).length,
|
|
3953
3972
|
0
|
|
3954
3973
|
),
|
|
3955
3974
|
[groups]
|
|
3956
3975
|
);
|
|
3957
|
-
const nextCount = badgeCount ??
|
|
3976
|
+
const nextCount = badgeCount ?? newCount;
|
|
3958
3977
|
const prevCountRef = useRef(nextCount);
|
|
3959
3978
|
useEffect(() => {
|
|
3960
3979
|
const prevCount = prevCountRef.current;
|
|
@@ -3963,9 +3982,29 @@ const Notification = forwardRef(
|
|
|
3963
3982
|
}
|
|
3964
3983
|
prevCountRef.current = nextCount;
|
|
3965
3984
|
}, [nextCount]);
|
|
3985
|
+
useEffect(() => {
|
|
3986
|
+
const update = () => {
|
|
3987
|
+
if (window.innerWidth > 640 || !triggerRef.current) {
|
|
3988
|
+
setMobileAlign(null);
|
|
3989
|
+
return;
|
|
3990
|
+
}
|
|
3991
|
+
const contentWidth = Math.min(panelWidth, window.innerWidth - 32);
|
|
3992
|
+
const triggerLeft = triggerRef.current.getBoundingClientRect().left;
|
|
3993
|
+
setMobileAlign({
|
|
3994
|
+
alignOffset: (window.innerWidth - contentWidth) / 2 - triggerLeft,
|
|
3995
|
+
width: contentWidth
|
|
3996
|
+
});
|
|
3997
|
+
};
|
|
3998
|
+
update();
|
|
3999
|
+
window.addEventListener("resize", update);
|
|
4000
|
+
return () => window.removeEventListener("resize", update);
|
|
4001
|
+
}, [panelWidth]);
|
|
3966
4002
|
const displayCount = clearBadgeOnOpen && isBadgeCleared ? 0 : nextCount;
|
|
3967
4003
|
const hasItems = groups.some((group) => group.items.length > 0);
|
|
3968
4004
|
const handleOpenChange = (next) => {
|
|
4005
|
+
if (resolvedOpen && !next) {
|
|
4006
|
+
setWasDismissed(true);
|
|
4007
|
+
}
|
|
3969
4008
|
if (next && clearBadgeOnOpen && nextCount > 0) {
|
|
3970
4009
|
setIsBadgeCleared(true);
|
|
3971
4010
|
onBadgeCleared == null ? void 0 : onBadgeCleared();
|
|
@@ -3973,8 +4012,17 @@ const Notification = forwardRef(
|
|
|
3973
4012
|
if (!controlled) setInternalOpen(next);
|
|
3974
4013
|
onOpenChange == null ? void 0 : onOpenChange(next);
|
|
3975
4014
|
};
|
|
4015
|
+
const handleItemClick = (item) => {
|
|
4016
|
+
setClickedItemIds((prev) => {
|
|
4017
|
+
if (prev.has(item.id)) return prev;
|
|
4018
|
+
const next = new Set(prev);
|
|
4019
|
+
next.add(item.id);
|
|
4020
|
+
return next;
|
|
4021
|
+
});
|
|
4022
|
+
onItemClick == null ? void 0 : onItemClick(item);
|
|
4023
|
+
};
|
|
3976
4024
|
return /* @__PURE__ */ jsxs(Popover.Root, { open: resolvedOpen, onOpenChange: handleOpenChange, children: [
|
|
3977
|
-
/* @__PURE__ */ jsx("div", { ref, className: cn("inline-flex", className), children: /* @__PURE__ */ jsx(Popover.Trigger, { asChild: true, children: /* @__PURE__ */ jsx("div", { className: "relative", children: /* @__PURE__ */ jsx(
|
|
4025
|
+
/* @__PURE__ */ jsx("div", { ref, className: cn("inline-flex", className), children: /* @__PURE__ */ jsx(Popover.Trigger, { asChild: true, children: /* @__PURE__ */ jsx("div", { ref: triggerRef, className: "relative", children: /* @__PURE__ */ jsx(
|
|
3978
4026
|
Badge,
|
|
3979
4027
|
{
|
|
3980
4028
|
variant: "notification",
|
|
@@ -3987,22 +4035,26 @@ const Notification = forwardRef(
|
|
|
3987
4035
|
/* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsx(
|
|
3988
4036
|
Popover.Content,
|
|
3989
4037
|
{
|
|
3990
|
-
align: "end",
|
|
4038
|
+
align: mobileAlign ? "start" : "end",
|
|
4039
|
+
alignOffset: (mobileAlign == null ? void 0 : mobileAlign.alignOffset) ?? 0,
|
|
4040
|
+
avoidCollisions: !mobileAlign,
|
|
3991
4041
|
sideOffset: 10,
|
|
3992
4042
|
className: cn(
|
|
3993
4043
|
"z-50 overflow-hidden rounded-lg border border-border bg-background shadow-lg",
|
|
3994
4044
|
panelClassName
|
|
3995
4045
|
),
|
|
3996
|
-
style: { width: panelWidth },
|
|
3997
|
-
children: /* @__PURE__ */ jsxs("div", { className: "max-h-[480px] overflow-y-auto
|
|
4046
|
+
style: { width: (mobileAlign == null ? void 0 : mobileAlign.width) ?? panelWidth },
|
|
4047
|
+
children: /* @__PURE__ */ jsxs("div", { className: "max-h-[480px] overflow-y-auto", children: [
|
|
3998
4048
|
!hasItems && /* @__PURE__ */ jsx("div", { className: "px-4 py-8 text-center text-sm text-muted-foreground", children: emptyText }),
|
|
3999
4049
|
groups.map((group) => /* @__PURE__ */ jsxs("div", { className: "w-full", children: [
|
|
4000
|
-
/* @__PURE__ */ jsx(NotificationDivider, { label: group.label }),
|
|
4050
|
+
showGroupLabels && group.label ? /* @__PURE__ */ jsx(NotificationDivider, { label: group.label }) : null,
|
|
4001
4051
|
/* @__PURE__ */ jsx("div", { className: "divide-y divide-divider", children: group.items.map((item) => /* @__PURE__ */ jsx(
|
|
4002
4052
|
NotificationRow,
|
|
4003
4053
|
{
|
|
4004
4054
|
item,
|
|
4005
|
-
onItemClick
|
|
4055
|
+
onItemClick: handleItemClick,
|
|
4056
|
+
hideIndicator: clickedItemIds.has(item.id),
|
|
4057
|
+
demoteNewBackground: wasDismissed
|
|
4006
4058
|
},
|
|
4007
4059
|
item.id
|
|
4008
4060
|
)) })
|