@sarunyu/system-one 4.9.28 → 4.9.31

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 CHANGED
@@ -24,6 +24,8 @@ in this package.** This file is the short version: the rules you must follow.
24
24
  - Custom toast / snackbar notifications → use `<Toast>` / `<ToastStack>` in a `fixed` portal.
25
25
  - Filter buttons with count badges → use `<Badge>` (`variant="button"`).
26
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.
27
+ - User profile photos, initials circles, or silhouette placeholders → use `<Avatar>`. For grouped users, use `<AvatarStack>`. Never hand-roll `<img>` or `<div>` avatar shapes.
28
+ - Custom breadcrumb / navigation trails → use `<Breadcrumb>`.
27
29
 
28
30
  2. **Use token-backed Tailwind classes for color.** Never emit hard-coded colors:
29
31
  - Hex (`#3b82f6`), arbitrary (`bg-[#...]`), and palette utilities
@@ -154,7 +156,7 @@ Build it with tokens. Never introduce new colors.
154
156
 
155
157
  If the custom thing is conceptually a button/input/tag/chip/tab/card/table,
156
158
  **stop and use the library's component instead.** Only build custom when the
157
- concept isn't covered (hero section, chart, breadcrumb, etc.).
159
+ concept isn't covered (hero section, chart, etc.).
158
160
 
159
161
  ## Read this before generating code
160
162
 
package/DESIGN.md CHANGED
@@ -248,9 +248,9 @@ Use sparingly. Corporate UIs prefer border separation over heavy elevation.
248
248
 
249
249
  ## Component library
250
250
 
251
- This design system ships 24 pre-built components. **Always use them — never recreate with raw HTML.**
251
+ This design system ships 27 pre-built components. **Always use them — never recreate with raw HTML.**
252
252
 
253
- Key components: `Button`, `Input`, `TextArea`, `SearchInput`, `Dropdown`, `DropdownMultiple`, `Checkbox`, `Radio`, `Toggle`, `DateInput`, `TimeInput`, `Tag`, `StatusTag`, `Chip`, `TabGroup`, `Card`, `Table` + `TableRow` + `TableHeaderCell` + `TableCell`, `Modal`, `BottomSheet`, `Alert`, `Toast`, `ToastStack`, `Notification`, `Badge`.
253
+ Key components: `Button`, `Input`, `TextArea`, `SearchInput`, `Dropdown`, `DropdownMultiple`, `Checkbox`, `Radio`, `Toggle`, `DateInput`, `TimeInput`, `Avatar`, `AvatarStack`, `Breadcrumb`, `Tag`, `StatusTag`, `Chip`, `TabGroup`, `Card`, `Table` + `TableRow` + `TableHeaderCell` + `TableCell`, `Modal`, `BottomSheet`, `Alert`, `Toast`, `ToastStack`, `Notification`, `Badge`.
254
254
 
255
255
  For props, variants, and usage rules → read `AGENTS.md` and `llms.txt` in this package.
256
256
 
package/dist/index.cjs CHANGED
@@ -1584,21 +1584,37 @@ function Header({
1584
1584
  rightIcon,
1585
1585
  onActionClick
1586
1586
  }) {
1587
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex items-center gap-3", rightSide === "action" ? "pr-2" : void 0), children: [
1588
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
1589
- /* @__PURE__ */ jsxRuntime.jsx(Leading, { headerType, imageSrc, leftIcon }),
1590
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "truncate text-base leading-6 font-bold text-foreground", children: title })
1591
- ] }),
1592
- /* @__PURE__ */ jsxRuntime.jsx(
1593
- Trailing,
1594
- {
1595
- rightSide,
1596
- actionLabel,
1597
- rightIcon,
1598
- onActionClick
1599
- }
1600
- )
1601
- ] });
1587
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1588
+ "div",
1589
+ {
1590
+ className: cn(
1591
+ "flex items-center gap-3",
1592
+ rightSide === "action" ? "pr-2" : void 0
1593
+ ),
1594
+ children: [
1595
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
1596
+ /* @__PURE__ */ jsxRuntime.jsx(
1597
+ Leading,
1598
+ {
1599
+ headerType,
1600
+ imageSrc,
1601
+ leftIcon
1602
+ }
1603
+ ),
1604
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "truncate text-base leading-6 font-bold text-foreground", children: title })
1605
+ ] }),
1606
+ /* @__PURE__ */ jsxRuntime.jsx(
1607
+ Trailing,
1608
+ {
1609
+ rightSide,
1610
+ actionLabel,
1611
+ rightIcon,
1612
+ onActionClick
1613
+ }
1614
+ )
1615
+ ]
1616
+ }
1617
+ );
1602
1618
  }
1603
1619
  function Leading({
1604
1620
  headerType,
@@ -1606,7 +1622,14 @@ function Leading({
1606
1622
  leftIcon
1607
1623
  }) {
1608
1624
  if (headerType === "image") {
1609
- return imageSrc ? /* @__PURE__ */ jsxRuntime.jsx("img", { alt: "", className: "size-8 shrink-0 rounded-md object-cover", src: imageSrc }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "size-8 shrink-0 rounded-md bg-muted" });
1625
+ return imageSrc ? /* @__PURE__ */ jsxRuntime.jsx(
1626
+ "img",
1627
+ {
1628
+ alt: "",
1629
+ className: "size-8 shrink-0 rounded-md object-cover",
1630
+ src: imageSrc
1631
+ }
1632
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "size-8 shrink-0 rounded-md bg-muted" });
1610
1633
  }
1611
1634
  if (headerType === "icon") {
1612
1635
  return /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", className: "text-foreground", children: leftIcon ?? /* @__PURE__ */ jsxRuntime.jsx(react.Circle, { size: 22 }) });
@@ -1623,7 +1646,7 @@ function Trailing({
1623
1646
  return /* @__PURE__ */ jsxRuntime.jsx(
1624
1647
  Button,
1625
1648
  {
1626
- className: "px-0 py-0 text-base leading-6 font-bold",
1649
+ className: "text-base leading-6 font-bold",
1627
1650
  onClick: onActionClick,
1628
1651
  size: "md",
1629
1652
  variant: "plain",
@@ -5966,6 +5989,157 @@ const TimeInput = React.forwardRef(
5966
5989
  }
5967
5990
  );
5968
5991
  TimeInput.displayName = "TimeInput";
5992
+ const avatarPlaceholder = "data:image/png;base64,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";
5993
+ const TEXT_GRADIENT = "linear-gradient(to top right, #B5D3F8, #E6F6FB)";
5994
+ const TEXT_COLOR = "#00A2D9";
5995
+ const SIZE_CONFIG = {
5996
+ xxs: { container: "size-4", fontSize: "text-[11px]", dotSize: "size-[5px]", dotRing: "ring-[1.5px]" },
5997
+ xs: { container: "size-5", fontSize: "text-sm", dotSize: "size-[5px]", dotRing: "ring-[1.5px]" },
5998
+ s: { container: "size-6", fontSize: "text-base", dotSize: "size-[6px]", dotRing: "ring-[1.5px]" },
5999
+ m: { container: "size-8", fontSize: "text-xl", dotSize: "size-[7px]", dotRing: "ring-2" },
6000
+ l: { container: "size-10", fontSize: "text-[26px]", dotSize: "size-[9px]", dotRing: "ring-2" },
6001
+ xl: { container: "size-12", fontSize: "text-[32px]", dotSize: "size-[11px]", dotRing: "ring-2" },
6002
+ xxl: { container: "size-13", fontSize: "text-[37px]", dotSize: "size-[13px]", dotRing: "ring-2" }
6003
+ };
6004
+ function Avatar({
6005
+ type = "photo",
6006
+ size = "m",
6007
+ status = false,
6008
+ src,
6009
+ alt = "",
6010
+ initials,
6011
+ className
6012
+ }) {
6013
+ const { container, fontSize, dotSize, dotRing } = SIZE_CONFIG[size];
6014
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("relative inline-flex shrink-0", container, className), children: [
6015
+ type === "photo" && /* @__PURE__ */ jsxRuntime.jsx(
6016
+ "img",
6017
+ {
6018
+ src,
6019
+ alt,
6020
+ className: "size-full rounded-full object-cover bg-bg-default-tertiary"
6021
+ }
6022
+ ),
6023
+ type === "placeholder" && /* @__PURE__ */ jsxRuntime.jsx(
6024
+ "img",
6025
+ {
6026
+ src: avatarPlaceholder,
6027
+ alt: "",
6028
+ className: "size-full rounded-full object-cover",
6029
+ "aria-hidden": "true"
6030
+ }
6031
+ ),
6032
+ type === "text" && /* @__PURE__ */ jsxRuntime.jsx(
6033
+ "div",
6034
+ {
6035
+ className: "relative size-full rounded-full overflow-hidden",
6036
+ style: { background: TEXT_GRADIENT },
6037
+ children: /* @__PURE__ */ jsxRuntime.jsx(
6038
+ "span",
6039
+ {
6040
+ className: cn(
6041
+ "absolute left-1/2 top-1/2 font-bold leading-none select-none whitespace-nowrap",
6042
+ fontSize
6043
+ ),
6044
+ style: {
6045
+ color: TEXT_COLOR,
6046
+ transform: "translate(-50%, calc(-50% + 0.07em))"
6047
+ },
6048
+ children: (initials ?? "").slice(0, 2).toUpperCase()
6049
+ }
6050
+ )
6051
+ }
6052
+ ),
6053
+ status && type === "photo" && /* @__PURE__ */ jsxRuntime.jsx(
6054
+ "span",
6055
+ {
6056
+ "aria-label": "Online",
6057
+ className: cn(
6058
+ "absolute bottom-0 right-0 block rounded-full bg-bg-success",
6059
+ dotSize,
6060
+ dotRing,
6061
+ "ring-white"
6062
+ )
6063
+ }
6064
+ )
6065
+ ] });
6066
+ }
6067
+ const STACK_CONFIG = {
6068
+ small: { avatarSize: "xxs", avatarPx: 16, overlapPx: 4 },
6069
+ medium: { avatarSize: "xs", avatarPx: 20, overlapPx: 5 },
6070
+ large: { avatarSize: "s", avatarPx: 24, overlapPx: 5 }
6071
+ };
6072
+ function subtractMask(avatarPx, overlapPx) {
6073
+ const r = avatarPx / 2;
6074
+ const spacingPx = avatarPx - overlapPx;
6075
+ const biteX = spacingPx + r;
6076
+ const biteY = r;
6077
+ const biteR = r + 1;
6078
+ const self = `radial-gradient(circle at ${r}px ${r}px, white ${r}px, transparent ${r}px)`;
6079
+ const bite = `radial-gradient(circle at ${biteX}px ${biteY}px, white ${biteR}px, transparent ${biteR}px)`;
6080
+ return {
6081
+ maskImage: `${self}, ${bite}`,
6082
+ maskComposite: "subtract",
6083
+ WebkitMaskImage: `${bite}, ${self}`,
6084
+ WebkitMaskComposite: "destination-out"
6085
+ };
6086
+ }
6087
+ function AvatarStack({
6088
+ items,
6089
+ size = "small",
6090
+ max = 5,
6091
+ className
6092
+ }) {
6093
+ const { avatarSize, avatarPx, overlapPx } = STACK_CONFIG[size];
6094
+ const visible = items.slice(0, max);
6095
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("flex shrink-0", className), children: visible.map((item, i) => {
6096
+ const isLast = i === visible.length - 1;
6097
+ return /* @__PURE__ */ jsxRuntime.jsx(
6098
+ "div",
6099
+ {
6100
+ className: "relative flex shrink-0",
6101
+ style: {
6102
+ width: avatarPx,
6103
+ height: avatarPx,
6104
+ marginLeft: i > 0 ? -overlapPx : 0,
6105
+ // Leftmost = highest z-index: its transparent bite reveals the avatar below.
6106
+ zIndex: visible.length - i,
6107
+ ...isLast ? {} : subtractMask(avatarPx, overlapPx)
6108
+ },
6109
+ children: /* @__PURE__ */ jsxRuntime.jsx(
6110
+ Avatar,
6111
+ {
6112
+ type: item.type ?? "photo",
6113
+ size: avatarSize,
6114
+ src: item.src,
6115
+ alt: item.alt,
6116
+ initials: item.initials
6117
+ }
6118
+ )
6119
+ },
6120
+ i
6121
+ );
6122
+ }) });
6123
+ }
6124
+ function BreadcrumbLabel({ item, isLast }) {
6125
+ const base = "shrink-0 text-sm leading-5";
6126
+ if (isLast) {
6127
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-current": "page", className: `${base} text-text-brand-secondary`, children: item.label });
6128
+ }
6129
+ if (item.href) {
6130
+ return /* @__PURE__ */ jsxRuntime.jsx("a", { href: item.href, className: `${base} text-text-default-secondary hover:text-foreground transition-colors`, children: item.label });
6131
+ }
6132
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: `${base} text-text-default-secondary`, children: item.label });
6133
+ }
6134
+ function Breadcrumb({ items, className }) {
6135
+ return /* @__PURE__ */ jsxRuntime.jsx("nav", { "aria-label": "Breadcrumb", className: cn("flex items-center gap-1", className), children: items.map((item, index2) => {
6136
+ const isLast = index2 === items.length - 1;
6137
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-1", children: [
6138
+ /* @__PURE__ */ jsxRuntime.jsx(BreadcrumbLabel, { item, isLast }),
6139
+ !isLast && /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", className: "shrink-0 text-sm leading-5 text-text-default-placeholder select-none", children: "/" })
6140
+ ] }, index2);
6141
+ }) });
6142
+ }
5969
6143
  const index = {
5970
6144
  Button,
5971
6145
  Badge,
@@ -5999,12 +6173,18 @@ const index = {
5999
6173
  TableCell,
6000
6174
  TextArea,
6001
6175
  TimeInput,
6176
+ Avatar,
6177
+ AvatarStack,
6178
+ Breadcrumb,
6002
6179
  cn,
6003
6180
  useIsMobile
6004
6181
  };
6005
6182
  exports.Alert = Alert;
6183
+ exports.Avatar = Avatar;
6184
+ exports.AvatarStack = AvatarStack;
6006
6185
  exports.Badge = Badge;
6007
6186
  exports.BottomSheet = BottomSheet;
6187
+ exports.Breadcrumb = Breadcrumb;
6008
6188
  exports.Button = Button;
6009
6189
  exports.Card = Card;
6010
6190
  exports.Checkbox = Checkbox;