@designa8/vibedesign-stripe-deepseek-85ea 0.2.1 → 0.2.3
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/README.md +1 -1
- package/dist/index.cjs +159 -33
- package/dist/index.js +159 -33
- package/package.json +1 -1
- package/src/atoms/Icon/icons.data.ts +1 -0
- package/src/atoms/Sidebar/preview.tsx +74 -116
- package/src/atoms/SidebarDesktop/SidebarDesktop.tsx +135 -15
- package/src/atoms/SidebarMobile/SidebarMobile.tsx +115 -9
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @designa8/vibedesign-stripe-deepseek-85ea
|
|
2
2
|
|
|
3
|
-
Complete design system generated by Atoms Design, version **0.2.
|
|
3
|
+
Complete design system generated by Atoms Design, version **0.2.3**.
|
|
4
4
|
Tokens, atoms, molecules, organisms, and templates — import from the package root;
|
|
5
5
|
never hardcode visual values in consumer code.
|
|
6
6
|
|
package/dist/index.cjs
CHANGED
|
@@ -239,7 +239,8 @@ var ICON_REGISTRY = {
|
|
|
239
239
|
"server": { viewBox: "0 0 24 24", nodes: [{ tag: "rect", width: "20", height: "8", x: "2", y: "2", rx: "2", ry: "2" }, { tag: "rect", width: "20", height: "8", x: "2", y: "14", rx: "2", ry: "2" }, { tag: "line", x1: "6", x2: "6.01", y1: "6", y2: "6" }, { tag: "line", x1: "6", x2: "6.01", y1: "18", y2: "18" }] },
|
|
240
240
|
"grip-vertical": { viewBox: "0 0 24 24", nodes: [{ tag: "circle", cx: "9", cy: "12", r: "1" }, { tag: "circle", cx: "9", cy: "5", r: "1" }, { tag: "circle", cx: "9", cy: "19", r: "1" }, { tag: "circle", cx: "15", cy: "12", r: "1" }, { tag: "circle", cx: "15", cy: "5", r: "1" }, { tag: "circle", cx: "15", cy: "19", r: "1" }] },
|
|
241
241
|
"euro": { viewBox: "0 0 24 24", nodes: [{ tag: "path", d: "M4 10h12" }, { tag: "path", d: "M4 14h9" }, { tag: "path", d: "M19 6a7.7 7.7 0 0 0-5.2-2A7.9 7.9 0 0 0 6 12c0 4.4 3.5 8 7.8 8 2 0 3.8-.8 5.2-2" }] },
|
|
242
|
-
"square": { viewBox: "0 0 24 24", nodes: [{ tag: "rect", width: "18", height: "18", x: "3", y: "3", rx: "2" }] }
|
|
242
|
+
"square": { viewBox: "0 0 24 24", nodes: [{ tag: "rect", width: "18", height: "18", x: "3", y: "3", rx: "2" }] },
|
|
243
|
+
"eye-off": { viewBox: "0 0 24 24", nodes: [{ tag: "path", d: "M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49" }, { tag: "path", d: "M14.084 14.158a3 3 0 0 1-4.242-4.242" }, { tag: "path", d: "M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143" }, { tag: "path", d: "m2 2 20 20" }] }
|
|
243
244
|
};
|
|
244
245
|
|
|
245
246
|
// virtual-pkg:src/atoms/Icon/Icon.tsx
|
|
@@ -14669,12 +14670,59 @@ var glyph3 = (name, size = 16, strokeWidth = 1.75) => /* @__PURE__ */ (0, import
|
|
|
14669
14670
|
style: { color: "inherit" }
|
|
14670
14671
|
}
|
|
14671
14672
|
);
|
|
14673
|
+
var hasBadge = (badge) => {
|
|
14674
|
+
if (badge === void 0 || badge === null) {
|
|
14675
|
+
return false;
|
|
14676
|
+
}
|
|
14677
|
+
if (typeof badge === "number") {
|
|
14678
|
+
return Number.isFinite(badge) && badge !== 0;
|
|
14679
|
+
}
|
|
14680
|
+
const trimmed = badge.trim();
|
|
14681
|
+
return trimmed.length > 0 && trimmed !== "0";
|
|
14682
|
+
};
|
|
14683
|
+
var badgeText = (badge) => typeof badge === "number" ? badge > 99 ? "99+" : String(badge) : badge.trim();
|
|
14684
|
+
var rowAccessibleName = (label, badge) => {
|
|
14685
|
+
if (!hasBadge(badge)) {
|
|
14686
|
+
return label;
|
|
14687
|
+
}
|
|
14688
|
+
return typeof badge === "number" ? `${label}, ${badge} pending` : `${label}, ${badgeText(badge)}`;
|
|
14689
|
+
};
|
|
14690
|
+
var RailBadge = ({
|
|
14691
|
+
badge,
|
|
14692
|
+
active
|
|
14693
|
+
}) => /* @__PURE__ */ (0, import_jsx_runtime74.jsx)(
|
|
14694
|
+
"span",
|
|
14695
|
+
{
|
|
14696
|
+
"aria-hidden": "true",
|
|
14697
|
+
"data-active": active || void 0,
|
|
14698
|
+
className: cn65(
|
|
14699
|
+
"ml-auto flex h-4 min-w-4 shrink-0 items-center justify-center rounded-full",
|
|
14700
|
+
"bg-primary px-xxs text-caption text-on-primary",
|
|
14701
|
+
"data-[active=true]:bg-on-primary data-[active=true]:text-primary"
|
|
14702
|
+
),
|
|
14703
|
+
children: badgeText(badge)
|
|
14704
|
+
}
|
|
14705
|
+
);
|
|
14706
|
+
var railGlyph = (item, folded) => /* @__PURE__ */ (0, import_jsx_runtime74.jsxs)("span", { className: "relative inline-flex shrink-0 items-center", children: [
|
|
14707
|
+
glyph3(item.icon, 16, 1.75),
|
|
14708
|
+
folded && hasBadge(item.badge) ? /* @__PURE__ */ (0, import_jsx_runtime74.jsx)(
|
|
14709
|
+
"span",
|
|
14710
|
+
{
|
|
14711
|
+
"aria-hidden": "true",
|
|
14712
|
+
className: cn65(
|
|
14713
|
+
"pointer-events-none absolute -right-xxs -top-xxs size-2 rounded-full",
|
|
14714
|
+
"bg-primary",
|
|
14715
|
+
"group-data-[active=true]:bg-on-primary"
|
|
14716
|
+
)
|
|
14717
|
+
}
|
|
14718
|
+
) : null
|
|
14719
|
+
] });
|
|
14672
14720
|
var RowLabel = ({
|
|
14673
14721
|
folded,
|
|
14674
14722
|
children
|
|
14675
14723
|
}) => folded ? /* @__PURE__ */ (0, import_jsx_runtime74.jsx)("span", { className: "sr-only", children }) : /* @__PURE__ */ (0, import_jsx_runtime74.jsx)("span", { className: "truncate", children });
|
|
14676
14724
|
var railRowClass = (active, folded) => cn65(
|
|
14677
|
-
"flex w-full cursor-pointer items-center rounded-lg border-0 bg-transparent",
|
|
14725
|
+
"group flex w-full cursor-pointer items-center rounded-lg border-0 bg-transparent",
|
|
14678
14726
|
"text-body-md transition duration-150 ease-out",
|
|
14679
14727
|
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-focus-blue",
|
|
14680
14728
|
"focus-visible:ring-inset",
|
|
@@ -15010,6 +15058,7 @@ var SidebarDesktop = React74.forwardRef(
|
|
|
15010
15058
|
className: "flex flex-col gap-xxs",
|
|
15011
15059
|
children: navItems.map((item) => {
|
|
15012
15060
|
const active = item.id === activeId;
|
|
15061
|
+
const badged = hasBadge(item.badge);
|
|
15013
15062
|
if (!item.children?.length) {
|
|
15014
15063
|
return /* @__PURE__ */ (0, import_jsx_runtime74.jsxs)(
|
|
15015
15064
|
"a",
|
|
@@ -15017,11 +15066,13 @@ var SidebarDesktop = React74.forwardRef(
|
|
|
15017
15066
|
href: item.href ?? `#${item.id}`,
|
|
15018
15067
|
onClick: navigate({ id: item.id, href: item.href }),
|
|
15019
15068
|
"aria-current": active ? "page" : void 0,
|
|
15020
|
-
"
|
|
15069
|
+
"data-active": active,
|
|
15070
|
+
"aria-label": isFolded || badged ? rowAccessibleName(item.label, item.badge) : void 0,
|
|
15021
15071
|
className: railRowClass(active, isFolded),
|
|
15022
15072
|
children: [
|
|
15023
|
-
|
|
15024
|
-
/* @__PURE__ */ (0, import_jsx_runtime74.jsx)(RowLabel, { folded: isFolded, children: item.label })
|
|
15073
|
+
railGlyph(item, isFolded),
|
|
15074
|
+
/* @__PURE__ */ (0, import_jsx_runtime74.jsx)(RowLabel, { folded: isFolded, children: item.label }),
|
|
15075
|
+
!isFolded && badged ? /* @__PURE__ */ (0, import_jsx_runtime74.jsx)(RailBadge, { badge: item.badge, active }) : null
|
|
15025
15076
|
]
|
|
15026
15077
|
},
|
|
15027
15078
|
item.id
|
|
@@ -15037,22 +15088,32 @@ var SidebarDesktop = React74.forwardRef(
|
|
|
15037
15088
|
size: "rail",
|
|
15038
15089
|
"aria-expanded": expanded,
|
|
15039
15090
|
"aria-controls": subId,
|
|
15040
|
-
"aria-label": isFolded ? item.label : void 0,
|
|
15091
|
+
"aria-label": isFolded || badged ? rowAccessibleName(item.label, item.badge) : void 0,
|
|
15041
15092
|
"data-active": active,
|
|
15042
15093
|
"data-folded": isFolded,
|
|
15043
15094
|
onClick: () => toggleExpanded(item.id),
|
|
15044
|
-
|
|
15045
|
-
|
|
15046
|
-
|
|
15047
|
-
|
|
15048
|
-
|
|
15049
|
-
|
|
15050
|
-
|
|
15051
|
-
|
|
15052
|
-
|
|
15053
|
-
|
|
15054
|
-
|
|
15055
|
-
|
|
15095
|
+
className: "group",
|
|
15096
|
+
leadingIcon: railGlyph(item, isFolded),
|
|
15097
|
+
trailingIcon: isFolded ? void 0 : /* @__PURE__ */ (0, import_jsx_runtime74.jsxs)("span", { className: "flex items-center gap-xs", children: [
|
|
15098
|
+
badged ? /* @__PURE__ */ (0, import_jsx_runtime74.jsx)(
|
|
15099
|
+
RailBadge,
|
|
15100
|
+
{
|
|
15101
|
+
badge: item.badge,
|
|
15102
|
+
active
|
|
15103
|
+
}
|
|
15104
|
+
) : null,
|
|
15105
|
+
/* @__PURE__ */ (0, import_jsx_runtime74.jsx)(
|
|
15106
|
+
"span",
|
|
15107
|
+
{
|
|
15108
|
+
"aria-hidden": "true",
|
|
15109
|
+
className: cn65(
|
|
15110
|
+
"flex shrink-0 items-center transition-transform duration-200 ease-out",
|
|
15111
|
+
expanded ? "rotate-180" : "rotate-0"
|
|
15112
|
+
),
|
|
15113
|
+
children: glyph3("chevron-down", 16, 2)
|
|
15114
|
+
}
|
|
15115
|
+
)
|
|
15116
|
+
] }),
|
|
15056
15117
|
children: /* @__PURE__ */ (0, import_jsx_runtime74.jsx)(RowLabel, { folded: isFolded, children: item.label })
|
|
15057
15118
|
}
|
|
15058
15119
|
),
|
|
@@ -15105,17 +15166,20 @@ var SidebarDesktop = React74.forwardRef(
|
|
|
15105
15166
|
),
|
|
15106
15167
|
/* @__PURE__ */ (0, import_jsx_runtime74.jsx)("nav", { "aria-label": "Secondary", className: "mt-lg flex flex-col gap-xxs", children: secondaryNav.map((item) => {
|
|
15107
15168
|
const active = item.id === secondaryActiveId;
|
|
15169
|
+
const badged = hasBadge(item.badge);
|
|
15108
15170
|
return /* @__PURE__ */ (0, import_jsx_runtime74.jsxs)(
|
|
15109
15171
|
"a",
|
|
15110
15172
|
{
|
|
15111
15173
|
href: item.href ?? `#${item.id}`,
|
|
15112
15174
|
onClick: navigate({ id: item.id, href: item.href }),
|
|
15113
15175
|
"aria-current": active ? "page" : void 0,
|
|
15114
|
-
"
|
|
15176
|
+
"data-active": active,
|
|
15177
|
+
"aria-label": isFolded || badged ? rowAccessibleName(item.label, item.badge) : void 0,
|
|
15115
15178
|
className: railRowClass(active, isFolded),
|
|
15116
15179
|
children: [
|
|
15117
|
-
|
|
15118
|
-
/* @__PURE__ */ (0, import_jsx_runtime74.jsx)(RowLabel, { folded: isFolded, children: item.label })
|
|
15180
|
+
railGlyph(item, isFolded),
|
|
15181
|
+
/* @__PURE__ */ (0, import_jsx_runtime74.jsx)(RowLabel, { folded: isFolded, children: item.label }),
|
|
15182
|
+
!isFolded && badged ? /* @__PURE__ */ (0, import_jsx_runtime74.jsx)(RailBadge, { badge: item.badge, active }) : null
|
|
15119
15183
|
]
|
|
15120
15184
|
},
|
|
15121
15185
|
item.id
|
|
@@ -15242,6 +15306,39 @@ var SAMPLE_SECONDARY2 = [
|
|
|
15242
15306
|
];
|
|
15243
15307
|
var SAMPLE_PLAN2 = { name: "Pro", price: "$2,400.00 / month", href: "#plan" };
|
|
15244
15308
|
var glyph4 = (name, size = 18, strokeWidth = 1.75) => /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(Icon, { name, size, strokeWidth, style: { color: "inherit" } });
|
|
15309
|
+
var hasBadge2 = (badge) => {
|
|
15310
|
+
if (badge === void 0 || badge === null) {
|
|
15311
|
+
return false;
|
|
15312
|
+
}
|
|
15313
|
+
if (typeof badge === "number") {
|
|
15314
|
+
return Number.isFinite(badge) && badge !== 0;
|
|
15315
|
+
}
|
|
15316
|
+
const trimmed = badge.trim();
|
|
15317
|
+
return trimmed.length > 0 && trimmed !== "0";
|
|
15318
|
+
};
|
|
15319
|
+
var badgeText2 = (badge) => typeof badge === "number" ? badge > 99 ? "99+" : String(badge) : badge.trim();
|
|
15320
|
+
var rowAccessibleName2 = (label, badge) => {
|
|
15321
|
+
if (!hasBadge2(badge)) {
|
|
15322
|
+
return label;
|
|
15323
|
+
}
|
|
15324
|
+
return typeof badge === "number" ? `${label}, ${badge} pending` : `${label}, ${badgeText2(badge)}`;
|
|
15325
|
+
};
|
|
15326
|
+
var NavBadge = ({
|
|
15327
|
+
badge,
|
|
15328
|
+
active
|
|
15329
|
+
}) => /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(
|
|
15330
|
+
"span",
|
|
15331
|
+
{
|
|
15332
|
+
"aria-hidden": "true",
|
|
15333
|
+
"data-active": active || void 0,
|
|
15334
|
+
className: cn66(
|
|
15335
|
+
"ml-auto flex h-4 min-w-4 shrink-0 items-center justify-center rounded-full",
|
|
15336
|
+
"bg-primary px-xxs text-caption text-on-primary",
|
|
15337
|
+
"data-[active=true]:bg-on-primary data-[active=true]:text-primary"
|
|
15338
|
+
),
|
|
15339
|
+
children: badgeText2(badge)
|
|
15340
|
+
}
|
|
15341
|
+
);
|
|
15245
15342
|
var rowClass = (active) => cn66(
|
|
15246
15343
|
"flex min-h-10 w-full cursor-pointer items-center gap-md rounded-lg px-md py-sm",
|
|
15247
15344
|
"text-body-md transition duration-150 ease-out",
|
|
@@ -15554,6 +15651,7 @@ var SidebarMobile = React75.forwardRef(
|
|
|
15554
15651
|
] }),
|
|
15555
15652
|
/* @__PURE__ */ (0, import_jsx_runtime75.jsx)("nav", { "aria-label": "Primary", className: "mt-md flex flex-col gap-xxs", children: navItems.map((item) => {
|
|
15556
15653
|
const active = item.id === activeId;
|
|
15654
|
+
const badged = hasBadge2(item.badge);
|
|
15557
15655
|
if (!item.children?.length) {
|
|
15558
15656
|
return /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)(
|
|
15559
15657
|
"a",
|
|
@@ -15561,10 +15659,18 @@ var SidebarMobile = React75.forwardRef(
|
|
|
15561
15659
|
href: item.href ?? `#${item.id}`,
|
|
15562
15660
|
onClick: navigate({ id: item.id, href: item.href }),
|
|
15563
15661
|
"aria-current": active ? "page" : void 0,
|
|
15662
|
+
"aria-label": badged ? rowAccessibleName2(item.label, item.badge) : void 0,
|
|
15564
15663
|
className: rowClass(active),
|
|
15565
15664
|
children: [
|
|
15566
15665
|
glyph4(item.icon, 18, 1.75),
|
|
15567
|
-
/* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { className: "truncate", children: item.label })
|
|
15666
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { className: "truncate", children: item.label }),
|
|
15667
|
+
badged ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(
|
|
15668
|
+
NavBadge,
|
|
15669
|
+
{
|
|
15670
|
+
badge: item.badge,
|
|
15671
|
+
active
|
|
15672
|
+
}
|
|
15673
|
+
) : null
|
|
15568
15674
|
]
|
|
15569
15675
|
},
|
|
15570
15676
|
item.id
|
|
@@ -15580,20 +15686,31 @@ var SidebarMobile = React75.forwardRef(
|
|
|
15580
15686
|
size: "rail",
|
|
15581
15687
|
"aria-expanded": expanded,
|
|
15582
15688
|
"aria-controls": subId,
|
|
15689
|
+
"aria-label": badged ? rowAccessibleName2(item.label, item.badge) : void 0,
|
|
15583
15690
|
"data-active": active,
|
|
15584
15691
|
onClick: () => toggleExpanded(item.id),
|
|
15585
15692
|
className: "px-md",
|
|
15586
15693
|
leadingIcon: glyph4(item.icon, 18, 1.75),
|
|
15587
|
-
trailingIcon: /* @__PURE__ */ (0, import_jsx_runtime75.
|
|
15588
|
-
|
|
15589
|
-
|
|
15590
|
-
|
|
15591
|
-
|
|
15592
|
-
|
|
15593
|
-
|
|
15594
|
-
|
|
15595
|
-
|
|
15596
|
-
|
|
15694
|
+
trailingIcon: /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)("span", { className: "flex items-center gap-xs", children: [
|
|
15695
|
+
badged ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(
|
|
15696
|
+
NavBadge,
|
|
15697
|
+
{
|
|
15698
|
+
badge: item.badge,
|
|
15699
|
+
active
|
|
15700
|
+
}
|
|
15701
|
+
) : null,
|
|
15702
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsx)(
|
|
15703
|
+
"span",
|
|
15704
|
+
{
|
|
15705
|
+
"aria-hidden": "true",
|
|
15706
|
+
className: cn66(
|
|
15707
|
+
"flex shrink-0 items-center transition-transform duration-200 ease-out",
|
|
15708
|
+
expanded ? "rotate-180" : "rotate-0"
|
|
15709
|
+
),
|
|
15710
|
+
children: glyph4("chevron-down", 18, 2)
|
|
15711
|
+
}
|
|
15712
|
+
)
|
|
15713
|
+
] }),
|
|
15597
15714
|
children: /* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { className: "truncate text-left", children: item.label })
|
|
15598
15715
|
}
|
|
15599
15716
|
),
|
|
@@ -15649,16 +15766,25 @@ var SidebarMobile = React75.forwardRef(
|
|
|
15649
15766
|
/* @__PURE__ */ (0, import_jsx_runtime75.jsx)(Divider, { className: "my-md" }),
|
|
15650
15767
|
/* @__PURE__ */ (0, import_jsx_runtime75.jsx)("nav", { "aria-label": "Secondary", className: "flex flex-col gap-xxs", children: secondaryNav.map((item) => {
|
|
15651
15768
|
const active = item.id === secondaryActiveId;
|
|
15769
|
+
const badged = hasBadge2(item.badge);
|
|
15652
15770
|
return /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)(
|
|
15653
15771
|
"a",
|
|
15654
15772
|
{
|
|
15655
15773
|
href: item.href ?? `#${item.id}`,
|
|
15656
15774
|
onClick: navigate({ id: item.id, href: item.href }),
|
|
15657
15775
|
"aria-current": active ? "page" : void 0,
|
|
15776
|
+
"aria-label": badged ? rowAccessibleName2(item.label, item.badge) : void 0,
|
|
15658
15777
|
className: rowClass(active),
|
|
15659
15778
|
children: [
|
|
15660
15779
|
glyph4(item.icon, 18, 1.75),
|
|
15661
|
-
/* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { className: "truncate", children: item.label })
|
|
15780
|
+
/* @__PURE__ */ (0, import_jsx_runtime75.jsx)("span", { className: "truncate", children: item.label }),
|
|
15781
|
+
badged ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(
|
|
15782
|
+
NavBadge,
|
|
15783
|
+
{
|
|
15784
|
+
badge: item.badge,
|
|
15785
|
+
active
|
|
15786
|
+
}
|
|
15787
|
+
) : null
|
|
15662
15788
|
]
|
|
15663
15789
|
},
|
|
15664
15790
|
item.id
|
package/dist/index.js
CHANGED
|
@@ -102,7 +102,8 @@ var ICON_REGISTRY = {
|
|
|
102
102
|
"server": { viewBox: "0 0 24 24", nodes: [{ tag: "rect", width: "20", height: "8", x: "2", y: "2", rx: "2", ry: "2" }, { tag: "rect", width: "20", height: "8", x: "2", y: "14", rx: "2", ry: "2" }, { tag: "line", x1: "6", x2: "6.01", y1: "6", y2: "6" }, { tag: "line", x1: "6", x2: "6.01", y1: "18", y2: "18" }] },
|
|
103
103
|
"grip-vertical": { viewBox: "0 0 24 24", nodes: [{ tag: "circle", cx: "9", cy: "12", r: "1" }, { tag: "circle", cx: "9", cy: "5", r: "1" }, { tag: "circle", cx: "9", cy: "19", r: "1" }, { tag: "circle", cx: "15", cy: "12", r: "1" }, { tag: "circle", cx: "15", cy: "5", r: "1" }, { tag: "circle", cx: "15", cy: "19", r: "1" }] },
|
|
104
104
|
"euro": { viewBox: "0 0 24 24", nodes: [{ tag: "path", d: "M4 10h12" }, { tag: "path", d: "M4 14h9" }, { tag: "path", d: "M19 6a7.7 7.7 0 0 0-5.2-2A7.9 7.9 0 0 0 6 12c0 4.4 3.5 8 7.8 8 2 0 3.8-.8 5.2-2" }] },
|
|
105
|
-
"square": { viewBox: "0 0 24 24", nodes: [{ tag: "rect", width: "18", height: "18", x: "3", y: "3", rx: "2" }] }
|
|
105
|
+
"square": { viewBox: "0 0 24 24", nodes: [{ tag: "rect", width: "18", height: "18", x: "3", y: "3", rx: "2" }] },
|
|
106
|
+
"eye-off": { viewBox: "0 0 24 24", nodes: [{ tag: "path", d: "M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49" }, { tag: "path", d: "M14.084 14.158a3 3 0 0 1-4.242-4.242" }, { tag: "path", d: "M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143" }, { tag: "path", d: "m2 2 20 20" }] }
|
|
106
107
|
};
|
|
107
108
|
|
|
108
109
|
// virtual-pkg:src/atoms/Icon/Icon.tsx
|
|
@@ -14532,12 +14533,59 @@ var glyph3 = (name, size = 16, strokeWidth = 1.75) => /* @__PURE__ */ jsx74(
|
|
|
14532
14533
|
style: { color: "inherit" }
|
|
14533
14534
|
}
|
|
14534
14535
|
);
|
|
14536
|
+
var hasBadge = (badge) => {
|
|
14537
|
+
if (badge === void 0 || badge === null) {
|
|
14538
|
+
return false;
|
|
14539
|
+
}
|
|
14540
|
+
if (typeof badge === "number") {
|
|
14541
|
+
return Number.isFinite(badge) && badge !== 0;
|
|
14542
|
+
}
|
|
14543
|
+
const trimmed = badge.trim();
|
|
14544
|
+
return trimmed.length > 0 && trimmed !== "0";
|
|
14545
|
+
};
|
|
14546
|
+
var badgeText = (badge) => typeof badge === "number" ? badge > 99 ? "99+" : String(badge) : badge.trim();
|
|
14547
|
+
var rowAccessibleName = (label, badge) => {
|
|
14548
|
+
if (!hasBadge(badge)) {
|
|
14549
|
+
return label;
|
|
14550
|
+
}
|
|
14551
|
+
return typeof badge === "number" ? `${label}, ${badge} pending` : `${label}, ${badgeText(badge)}`;
|
|
14552
|
+
};
|
|
14553
|
+
var RailBadge = ({
|
|
14554
|
+
badge,
|
|
14555
|
+
active
|
|
14556
|
+
}) => /* @__PURE__ */ jsx74(
|
|
14557
|
+
"span",
|
|
14558
|
+
{
|
|
14559
|
+
"aria-hidden": "true",
|
|
14560
|
+
"data-active": active || void 0,
|
|
14561
|
+
className: cn65(
|
|
14562
|
+
"ml-auto flex h-4 min-w-4 shrink-0 items-center justify-center rounded-full",
|
|
14563
|
+
"bg-primary px-xxs text-caption text-on-primary",
|
|
14564
|
+
"data-[active=true]:bg-on-primary data-[active=true]:text-primary"
|
|
14565
|
+
),
|
|
14566
|
+
children: badgeText(badge)
|
|
14567
|
+
}
|
|
14568
|
+
);
|
|
14569
|
+
var railGlyph = (item, folded) => /* @__PURE__ */ jsxs71("span", { className: "relative inline-flex shrink-0 items-center", children: [
|
|
14570
|
+
glyph3(item.icon, 16, 1.75),
|
|
14571
|
+
folded && hasBadge(item.badge) ? /* @__PURE__ */ jsx74(
|
|
14572
|
+
"span",
|
|
14573
|
+
{
|
|
14574
|
+
"aria-hidden": "true",
|
|
14575
|
+
className: cn65(
|
|
14576
|
+
"pointer-events-none absolute -right-xxs -top-xxs size-2 rounded-full",
|
|
14577
|
+
"bg-primary",
|
|
14578
|
+
"group-data-[active=true]:bg-on-primary"
|
|
14579
|
+
)
|
|
14580
|
+
}
|
|
14581
|
+
) : null
|
|
14582
|
+
] });
|
|
14535
14583
|
var RowLabel = ({
|
|
14536
14584
|
folded,
|
|
14537
14585
|
children
|
|
14538
14586
|
}) => folded ? /* @__PURE__ */ jsx74("span", { className: "sr-only", children }) : /* @__PURE__ */ jsx74("span", { className: "truncate", children });
|
|
14539
14587
|
var railRowClass = (active, folded) => cn65(
|
|
14540
|
-
"flex w-full cursor-pointer items-center rounded-lg border-0 bg-transparent",
|
|
14588
|
+
"group flex w-full cursor-pointer items-center rounded-lg border-0 bg-transparent",
|
|
14541
14589
|
"text-body-md transition duration-150 ease-out",
|
|
14542
14590
|
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-focus-blue",
|
|
14543
14591
|
"focus-visible:ring-inset",
|
|
@@ -14873,6 +14921,7 @@ var SidebarDesktop = React74.forwardRef(
|
|
|
14873
14921
|
className: "flex flex-col gap-xxs",
|
|
14874
14922
|
children: navItems.map((item) => {
|
|
14875
14923
|
const active = item.id === activeId;
|
|
14924
|
+
const badged = hasBadge(item.badge);
|
|
14876
14925
|
if (!item.children?.length) {
|
|
14877
14926
|
return /* @__PURE__ */ jsxs71(
|
|
14878
14927
|
"a",
|
|
@@ -14880,11 +14929,13 @@ var SidebarDesktop = React74.forwardRef(
|
|
|
14880
14929
|
href: item.href ?? `#${item.id}`,
|
|
14881
14930
|
onClick: navigate({ id: item.id, href: item.href }),
|
|
14882
14931
|
"aria-current": active ? "page" : void 0,
|
|
14883
|
-
"
|
|
14932
|
+
"data-active": active,
|
|
14933
|
+
"aria-label": isFolded || badged ? rowAccessibleName(item.label, item.badge) : void 0,
|
|
14884
14934
|
className: railRowClass(active, isFolded),
|
|
14885
14935
|
children: [
|
|
14886
|
-
|
|
14887
|
-
/* @__PURE__ */ jsx74(RowLabel, { folded: isFolded, children: item.label })
|
|
14936
|
+
railGlyph(item, isFolded),
|
|
14937
|
+
/* @__PURE__ */ jsx74(RowLabel, { folded: isFolded, children: item.label }),
|
|
14938
|
+
!isFolded && badged ? /* @__PURE__ */ jsx74(RailBadge, { badge: item.badge, active }) : null
|
|
14888
14939
|
]
|
|
14889
14940
|
},
|
|
14890
14941
|
item.id
|
|
@@ -14900,22 +14951,32 @@ var SidebarDesktop = React74.forwardRef(
|
|
|
14900
14951
|
size: "rail",
|
|
14901
14952
|
"aria-expanded": expanded,
|
|
14902
14953
|
"aria-controls": subId,
|
|
14903
|
-
"aria-label": isFolded ? item.label : void 0,
|
|
14954
|
+
"aria-label": isFolded || badged ? rowAccessibleName(item.label, item.badge) : void 0,
|
|
14904
14955
|
"data-active": active,
|
|
14905
14956
|
"data-folded": isFolded,
|
|
14906
14957
|
onClick: () => toggleExpanded(item.id),
|
|
14907
|
-
|
|
14908
|
-
|
|
14909
|
-
|
|
14910
|
-
|
|
14911
|
-
|
|
14912
|
-
|
|
14913
|
-
|
|
14914
|
-
|
|
14915
|
-
|
|
14916
|
-
|
|
14917
|
-
|
|
14918
|
-
|
|
14958
|
+
className: "group",
|
|
14959
|
+
leadingIcon: railGlyph(item, isFolded),
|
|
14960
|
+
trailingIcon: isFolded ? void 0 : /* @__PURE__ */ jsxs71("span", { className: "flex items-center gap-xs", children: [
|
|
14961
|
+
badged ? /* @__PURE__ */ jsx74(
|
|
14962
|
+
RailBadge,
|
|
14963
|
+
{
|
|
14964
|
+
badge: item.badge,
|
|
14965
|
+
active
|
|
14966
|
+
}
|
|
14967
|
+
) : null,
|
|
14968
|
+
/* @__PURE__ */ jsx74(
|
|
14969
|
+
"span",
|
|
14970
|
+
{
|
|
14971
|
+
"aria-hidden": "true",
|
|
14972
|
+
className: cn65(
|
|
14973
|
+
"flex shrink-0 items-center transition-transform duration-200 ease-out",
|
|
14974
|
+
expanded ? "rotate-180" : "rotate-0"
|
|
14975
|
+
),
|
|
14976
|
+
children: glyph3("chevron-down", 16, 2)
|
|
14977
|
+
}
|
|
14978
|
+
)
|
|
14979
|
+
] }),
|
|
14919
14980
|
children: /* @__PURE__ */ jsx74(RowLabel, { folded: isFolded, children: item.label })
|
|
14920
14981
|
}
|
|
14921
14982
|
),
|
|
@@ -14968,17 +15029,20 @@ var SidebarDesktop = React74.forwardRef(
|
|
|
14968
15029
|
),
|
|
14969
15030
|
/* @__PURE__ */ jsx74("nav", { "aria-label": "Secondary", className: "mt-lg flex flex-col gap-xxs", children: secondaryNav.map((item) => {
|
|
14970
15031
|
const active = item.id === secondaryActiveId;
|
|
15032
|
+
const badged = hasBadge(item.badge);
|
|
14971
15033
|
return /* @__PURE__ */ jsxs71(
|
|
14972
15034
|
"a",
|
|
14973
15035
|
{
|
|
14974
15036
|
href: item.href ?? `#${item.id}`,
|
|
14975
15037
|
onClick: navigate({ id: item.id, href: item.href }),
|
|
14976
15038
|
"aria-current": active ? "page" : void 0,
|
|
14977
|
-
"
|
|
15039
|
+
"data-active": active,
|
|
15040
|
+
"aria-label": isFolded || badged ? rowAccessibleName(item.label, item.badge) : void 0,
|
|
14978
15041
|
className: railRowClass(active, isFolded),
|
|
14979
15042
|
children: [
|
|
14980
|
-
|
|
14981
|
-
/* @__PURE__ */ jsx74(RowLabel, { folded: isFolded, children: item.label })
|
|
15043
|
+
railGlyph(item, isFolded),
|
|
15044
|
+
/* @__PURE__ */ jsx74(RowLabel, { folded: isFolded, children: item.label }),
|
|
15045
|
+
!isFolded && badged ? /* @__PURE__ */ jsx74(RailBadge, { badge: item.badge, active }) : null
|
|
14982
15046
|
]
|
|
14983
15047
|
},
|
|
14984
15048
|
item.id
|
|
@@ -15105,6 +15169,39 @@ var SAMPLE_SECONDARY2 = [
|
|
|
15105
15169
|
];
|
|
15106
15170
|
var SAMPLE_PLAN2 = { name: "Pro", price: "$2,400.00 / month", href: "#plan" };
|
|
15107
15171
|
var glyph4 = (name, size = 18, strokeWidth = 1.75) => /* @__PURE__ */ jsx75(Icon, { name, size, strokeWidth, style: { color: "inherit" } });
|
|
15172
|
+
var hasBadge2 = (badge) => {
|
|
15173
|
+
if (badge === void 0 || badge === null) {
|
|
15174
|
+
return false;
|
|
15175
|
+
}
|
|
15176
|
+
if (typeof badge === "number") {
|
|
15177
|
+
return Number.isFinite(badge) && badge !== 0;
|
|
15178
|
+
}
|
|
15179
|
+
const trimmed = badge.trim();
|
|
15180
|
+
return trimmed.length > 0 && trimmed !== "0";
|
|
15181
|
+
};
|
|
15182
|
+
var badgeText2 = (badge) => typeof badge === "number" ? badge > 99 ? "99+" : String(badge) : badge.trim();
|
|
15183
|
+
var rowAccessibleName2 = (label, badge) => {
|
|
15184
|
+
if (!hasBadge2(badge)) {
|
|
15185
|
+
return label;
|
|
15186
|
+
}
|
|
15187
|
+
return typeof badge === "number" ? `${label}, ${badge} pending` : `${label}, ${badgeText2(badge)}`;
|
|
15188
|
+
};
|
|
15189
|
+
var NavBadge = ({
|
|
15190
|
+
badge,
|
|
15191
|
+
active
|
|
15192
|
+
}) => /* @__PURE__ */ jsx75(
|
|
15193
|
+
"span",
|
|
15194
|
+
{
|
|
15195
|
+
"aria-hidden": "true",
|
|
15196
|
+
"data-active": active || void 0,
|
|
15197
|
+
className: cn66(
|
|
15198
|
+
"ml-auto flex h-4 min-w-4 shrink-0 items-center justify-center rounded-full",
|
|
15199
|
+
"bg-primary px-xxs text-caption text-on-primary",
|
|
15200
|
+
"data-[active=true]:bg-on-primary data-[active=true]:text-primary"
|
|
15201
|
+
),
|
|
15202
|
+
children: badgeText2(badge)
|
|
15203
|
+
}
|
|
15204
|
+
);
|
|
15108
15205
|
var rowClass = (active) => cn66(
|
|
15109
15206
|
"flex min-h-10 w-full cursor-pointer items-center gap-md rounded-lg px-md py-sm",
|
|
15110
15207
|
"text-body-md transition duration-150 ease-out",
|
|
@@ -15417,6 +15514,7 @@ var SidebarMobile = React75.forwardRef(
|
|
|
15417
15514
|
] }),
|
|
15418
15515
|
/* @__PURE__ */ jsx75("nav", { "aria-label": "Primary", className: "mt-md flex flex-col gap-xxs", children: navItems.map((item) => {
|
|
15419
15516
|
const active = item.id === activeId;
|
|
15517
|
+
const badged = hasBadge2(item.badge);
|
|
15420
15518
|
if (!item.children?.length) {
|
|
15421
15519
|
return /* @__PURE__ */ jsxs72(
|
|
15422
15520
|
"a",
|
|
@@ -15424,10 +15522,18 @@ var SidebarMobile = React75.forwardRef(
|
|
|
15424
15522
|
href: item.href ?? `#${item.id}`,
|
|
15425
15523
|
onClick: navigate({ id: item.id, href: item.href }),
|
|
15426
15524
|
"aria-current": active ? "page" : void 0,
|
|
15525
|
+
"aria-label": badged ? rowAccessibleName2(item.label, item.badge) : void 0,
|
|
15427
15526
|
className: rowClass(active),
|
|
15428
15527
|
children: [
|
|
15429
15528
|
glyph4(item.icon, 18, 1.75),
|
|
15430
|
-
/* @__PURE__ */ jsx75("span", { className: "truncate", children: item.label })
|
|
15529
|
+
/* @__PURE__ */ jsx75("span", { className: "truncate", children: item.label }),
|
|
15530
|
+
badged ? /* @__PURE__ */ jsx75(
|
|
15531
|
+
NavBadge,
|
|
15532
|
+
{
|
|
15533
|
+
badge: item.badge,
|
|
15534
|
+
active
|
|
15535
|
+
}
|
|
15536
|
+
) : null
|
|
15431
15537
|
]
|
|
15432
15538
|
},
|
|
15433
15539
|
item.id
|
|
@@ -15443,20 +15549,31 @@ var SidebarMobile = React75.forwardRef(
|
|
|
15443
15549
|
size: "rail",
|
|
15444
15550
|
"aria-expanded": expanded,
|
|
15445
15551
|
"aria-controls": subId,
|
|
15552
|
+
"aria-label": badged ? rowAccessibleName2(item.label, item.badge) : void 0,
|
|
15446
15553
|
"data-active": active,
|
|
15447
15554
|
onClick: () => toggleExpanded(item.id),
|
|
15448
15555
|
className: "px-md",
|
|
15449
15556
|
leadingIcon: glyph4(item.icon, 18, 1.75),
|
|
15450
|
-
trailingIcon: /* @__PURE__ */
|
|
15451
|
-
|
|
15452
|
-
|
|
15453
|
-
|
|
15454
|
-
|
|
15455
|
-
|
|
15456
|
-
|
|
15457
|
-
|
|
15458
|
-
|
|
15459
|
-
|
|
15557
|
+
trailingIcon: /* @__PURE__ */ jsxs72("span", { className: "flex items-center gap-xs", children: [
|
|
15558
|
+
badged ? /* @__PURE__ */ jsx75(
|
|
15559
|
+
NavBadge,
|
|
15560
|
+
{
|
|
15561
|
+
badge: item.badge,
|
|
15562
|
+
active
|
|
15563
|
+
}
|
|
15564
|
+
) : null,
|
|
15565
|
+
/* @__PURE__ */ jsx75(
|
|
15566
|
+
"span",
|
|
15567
|
+
{
|
|
15568
|
+
"aria-hidden": "true",
|
|
15569
|
+
className: cn66(
|
|
15570
|
+
"flex shrink-0 items-center transition-transform duration-200 ease-out",
|
|
15571
|
+
expanded ? "rotate-180" : "rotate-0"
|
|
15572
|
+
),
|
|
15573
|
+
children: glyph4("chevron-down", 18, 2)
|
|
15574
|
+
}
|
|
15575
|
+
)
|
|
15576
|
+
] }),
|
|
15460
15577
|
children: /* @__PURE__ */ jsx75("span", { className: "truncate text-left", children: item.label })
|
|
15461
15578
|
}
|
|
15462
15579
|
),
|
|
@@ -15512,16 +15629,25 @@ var SidebarMobile = React75.forwardRef(
|
|
|
15512
15629
|
/* @__PURE__ */ jsx75(Divider, { className: "my-md" }),
|
|
15513
15630
|
/* @__PURE__ */ jsx75("nav", { "aria-label": "Secondary", className: "flex flex-col gap-xxs", children: secondaryNav.map((item) => {
|
|
15514
15631
|
const active = item.id === secondaryActiveId;
|
|
15632
|
+
const badged = hasBadge2(item.badge);
|
|
15515
15633
|
return /* @__PURE__ */ jsxs72(
|
|
15516
15634
|
"a",
|
|
15517
15635
|
{
|
|
15518
15636
|
href: item.href ?? `#${item.id}`,
|
|
15519
15637
|
onClick: navigate({ id: item.id, href: item.href }),
|
|
15520
15638
|
"aria-current": active ? "page" : void 0,
|
|
15639
|
+
"aria-label": badged ? rowAccessibleName2(item.label, item.badge) : void 0,
|
|
15521
15640
|
className: rowClass(active),
|
|
15522
15641
|
children: [
|
|
15523
15642
|
glyph4(item.icon, 18, 1.75),
|
|
15524
|
-
/* @__PURE__ */ jsx75("span", { className: "truncate", children: item.label })
|
|
15643
|
+
/* @__PURE__ */ jsx75("span", { className: "truncate", children: item.label }),
|
|
15644
|
+
badged ? /* @__PURE__ */ jsx75(
|
|
15645
|
+
NavBadge,
|
|
15646
|
+
{
|
|
15647
|
+
badge: item.badge,
|
|
15648
|
+
active
|
|
15649
|
+
}
|
|
15650
|
+
) : null
|
|
15525
15651
|
]
|
|
15526
15652
|
},
|
|
15527
15653
|
item.id
|
package/package.json
CHANGED
|
@@ -93,6 +93,7 @@ export const ICON_REGISTRY = {
|
|
|
93
93
|
"grip-vertical": { viewBox: "0 0 24 24", nodes: [{ tag: "circle", cx: "9", cy: "12", r: "1" }, { tag: "circle", cx: "9", cy: "5", r: "1" }, { tag: "circle", cx: "9", cy: "19", r: "1" }, { tag: "circle", cx: "15", cy: "12", r: "1" }, { tag: "circle", cx: "15", cy: "5", r: "1" }, { tag: "circle", cx: "15", cy: "19", r: "1" }] },
|
|
94
94
|
"euro": { viewBox: "0 0 24 24", nodes: [{ tag: "path", d: "M4 10h12" }, { tag: "path", d: "M4 14h9" }, { tag: "path", d: "M19 6a7.7 7.7 0 0 0-5.2-2A7.9 7.9 0 0 0 6 12c0 4.4 3.5 8 7.8 8 2 0 3.8-.8 5.2-2" }] },
|
|
95
95
|
"square": { viewBox: "0 0 24 24", nodes: [{ tag: "rect", width: "18", height: "18", x: "3", y: "3", rx: "2" }] },
|
|
96
|
+
"eye-off": { viewBox: "0 0 24 24", nodes: [{ tag: "path", d: "M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49" }, { tag: "path", d: "M14.084 14.158a3 3 0 0 1-4.242-4.242" }, { tag: "path", d: "M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143" }, { tag: "path", d: "m2 2 20 20" }] },
|
|
96
97
|
} as const;
|
|
97
98
|
|
|
98
99
|
export type IconName = keyof typeof ICON_REGISTRY;
|
|
@@ -2,30 +2,35 @@ import * as React from "react";
|
|
|
2
2
|
import { Avatar } from "../Avatar";
|
|
3
3
|
import { Button } from "../Button";
|
|
4
4
|
import { Icon } from "../Icon";
|
|
5
|
-
import {
|
|
6
|
-
import { SidebarMobile } from "../SidebarMobile";
|
|
5
|
+
import { type SidebarDesktopNavItem } from "../SidebarDesktop";
|
|
7
6
|
import { Sidebar, type SidebarNavTarget } from "./Sidebar";
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
/** Nav fixtures with the count wired to state, so the pill really hides at 0. */
|
|
9
|
+
const buildItems = (pending: number): SidebarDesktopNavItem[] => [
|
|
10
|
+
{
|
|
11
|
+
id: "payments",
|
|
12
|
+
label: "Tasks",
|
|
13
|
+
icon: "credit-card",
|
|
14
|
+
href: "/tasks",
|
|
15
|
+
badge: pending,
|
|
16
|
+
},
|
|
11
17
|
{ id: "balances", label: "Balances", icon: "layers", href: "/balances" },
|
|
12
18
|
{
|
|
13
19
|
id: "payouts",
|
|
14
|
-
label: "
|
|
20
|
+
label: "Approvals",
|
|
15
21
|
icon: "arrow-left-right",
|
|
22
|
+
badge: pending,
|
|
16
23
|
children: [
|
|
17
|
-
{ id: "scheduled", label: "Scheduled payouts", href: "/
|
|
18
|
-
{ id: "payout-accounts", label: "Payout accounts", href: "/
|
|
19
|
-
{ id: "payout-limits", label: "Payout limits", href: "/payouts/limits" },
|
|
24
|
+
{ id: "scheduled", label: "Scheduled payouts", href: "/approvals/scheduled" },
|
|
25
|
+
{ id: "payout-accounts", label: "Payout accounts", href: "/approvals/accounts" },
|
|
20
26
|
],
|
|
21
27
|
},
|
|
22
28
|
{ id: "developers", label: "Developers", icon: "code", href: "/developers" },
|
|
23
|
-
{ id: "api-logs", label: "API logs", icon: "file", href: "/api-logs" },
|
|
24
29
|
];
|
|
25
30
|
|
|
26
31
|
const SECONDARY_ITEMS: SidebarDesktopNavItem[] = [
|
|
27
32
|
{ id: "settings", label: "Settings", icon: "settings", href: "/settings" },
|
|
28
|
-
{ id: "team", label: "Team", icon: "group", href: "/team" },
|
|
33
|
+
{ id: "team", label: "Team", icon: "group", href: "/team", badge: "New" },
|
|
29
34
|
];
|
|
30
35
|
|
|
31
36
|
const ACCOUNTS = [
|
|
@@ -35,27 +40,11 @@ const ACCOUNTS = [
|
|
|
35
40
|
|
|
36
41
|
const PROFILE = { name: "Priya Shah", email: "priya@acme.com" };
|
|
37
42
|
|
|
38
|
-
/** A tiny content pane so the routed shell reads as a page, not a floating rail. */
|
|
39
|
-
const ContentPane = ({ route, unread }: { route: string; unread: number }) => (
|
|
40
|
-
<div className="flex min-w-0 flex-col gap-sm bg-canvas p-lg">
|
|
41
|
-
<p className="text-caption uppercase text-muted">Current route</p>
|
|
42
|
-
<p className="text-heading-md text-ink">{route}</p>
|
|
43
|
-
<p className="text-body-sm text-body">
|
|
44
|
-
Click any destination in the navigation to route there. The rail keeps its
|
|
45
|
-
header and footer pinned while the destination list scrolls on its own.
|
|
46
|
-
</p>
|
|
47
|
-
<p className="text-body-sm text-body">
|
|
48
|
-
{unread > 0
|
|
49
|
-
? `${unread} unread notifications waiting in the footer bell.`
|
|
50
|
-
: "Notifications cleared — the badge is gone."}
|
|
51
|
-
</p>
|
|
52
|
-
</div>
|
|
53
|
-
);
|
|
54
|
-
|
|
55
43
|
export default function Preview() {
|
|
56
44
|
const [activeId, setActiveId] = React.useState("payments");
|
|
57
|
-
const [route, setRoute] = React.useState("/
|
|
45
|
+
const [route, setRoute] = React.useState("/tasks");
|
|
58
46
|
const [unread, setUnread] = React.useState(4);
|
|
47
|
+
const [pending, setPending] = React.useState(2);
|
|
59
48
|
|
|
60
49
|
const handleNavigate = (item: SidebarNavTarget) => {
|
|
61
50
|
setRoute(item.href ?? `#${item.id}`);
|
|
@@ -63,99 +52,68 @@ export default function Preview() {
|
|
|
63
52
|
};
|
|
64
53
|
|
|
65
54
|
return (
|
|
66
|
-
<div className="mx-auto w-full max-w-3xl space-y-
|
|
67
|
-
|
|
68
|
-
<section className="space-y-sm">
|
|
55
|
+
<div className="mx-auto w-full max-w-3xl space-y-sm p-lg">
|
|
56
|
+
<div className="flex flex-wrap items-center justify-between gap-sm">
|
|
69
57
|
<p className="text-caption uppercase text-muted">
|
|
70
|
-
|
|
58
|
+
Count pill unfolded, dot on the glyph once folded
|
|
71
59
|
</p>
|
|
72
|
-
<
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
secondaryActiveId="settings"
|
|
81
|
-
accounts={ACCOUNTS}
|
|
82
|
-
profile={PROFILE}
|
|
83
|
-
plan={{ name: "Pro", price: "$2,400.00 / month", href: "/billing" }}
|
|
84
|
-
notificationCount={unread}
|
|
85
|
-
onNotifications={() => setUnread(0)}
|
|
86
|
-
mobileActions={
|
|
87
|
-
<>
|
|
88
|
-
<Button variant="bare" size="icon-sm" aria-label="Search">
|
|
89
|
-
<Icon name="search" size={20} strokeWidth={1.75} />
|
|
90
|
-
</Button>
|
|
91
|
-
<Button variant="bare" size="icon-sm" aria-label="Notifications">
|
|
92
|
-
<Icon name="bell" size={20} strokeWidth={1.75} />
|
|
93
|
-
</Button>
|
|
94
|
-
<Avatar name={PROFILE.name} size="sm" />
|
|
95
|
-
</>
|
|
96
|
-
}
|
|
97
|
-
mobileDrawerFooter={
|
|
98
|
-
<Button variant="outline-soft" size="md" fullWidth>
|
|
99
|
-
Switch theme
|
|
100
|
-
</Button>
|
|
101
|
-
}
|
|
102
|
-
/>
|
|
103
|
-
<ContentPane route={route} unread={unread} />
|
|
104
|
-
</div>
|
|
105
|
-
</section>
|
|
60
|
+
<Button
|
|
61
|
+
variant="outline-soft"
|
|
62
|
+
size="sm"
|
|
63
|
+
onClick={() => setPending((current) => (current === 0 ? 2 : 0))}
|
|
64
|
+
>
|
|
65
|
+
{pending === 0 ? "Show pending counts" : "Clear pending counts"}
|
|
66
|
+
</Button>
|
|
67
|
+
</div>
|
|
106
68
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
<p className="text-caption uppercase text-muted">
|
|
130
|
-
Mobile header — tap the menu to open the drawer
|
|
131
|
-
</p>
|
|
132
|
-
<div className="w-full overflow-hidden rounded-xl border border-hairline bg-surface">
|
|
133
|
-
<SidebarMobile
|
|
134
|
-
items={ITEMS}
|
|
135
|
-
activeId={activeId}
|
|
136
|
-
onNavigate={handleNavigate}
|
|
137
|
-
secondaryItems={SECONDARY_ITEMS}
|
|
138
|
-
accounts={ACCOUNTS}
|
|
139
|
-
profile={PROFILE}
|
|
140
|
-
plan={null}
|
|
141
|
-
notificationCount={unread}
|
|
142
|
-
mobileActions={
|
|
143
|
-
<>
|
|
144
|
-
<Button variant="bare" size="icon-sm" aria-label="Search">
|
|
145
|
-
<Icon name="search" size={20} strokeWidth={1.75} />
|
|
146
|
-
</Button>
|
|
147
|
-
<Avatar name={PROFILE.name} size="sm" />
|
|
148
|
-
</>
|
|
149
|
-
}
|
|
150
|
-
mobileDrawerFooter={
|
|
151
|
-
<Button variant="outline-soft" size="md" fullWidth>
|
|
152
|
-
Switch theme
|
|
69
|
+
<div className="grid w-full grid-cols-1 overflow-hidden rounded-xl border border-hairline bg-surface md:grid-cols-[auto_minmax(0,1fr)]">
|
|
70
|
+
<Sidebar
|
|
71
|
+
wordmark="Nimbus"
|
|
72
|
+
homeHref="/"
|
|
73
|
+
items={buildItems(pending)}
|
|
74
|
+
activeId={activeId}
|
|
75
|
+
onNavigate={handleNavigate}
|
|
76
|
+
secondaryItems={SECONDARY_ITEMS}
|
|
77
|
+
secondaryActiveId="settings"
|
|
78
|
+
accounts={ACCOUNTS}
|
|
79
|
+
profile={PROFILE}
|
|
80
|
+
plan={{ name: "Pro", price: "$2,400.00 / month", href: "/billing" }}
|
|
81
|
+
notificationCount={unread}
|
|
82
|
+
onNotifications={() => setUnread(0)}
|
|
83
|
+
defaultExpandedId={null}
|
|
84
|
+
mobileActions={
|
|
85
|
+
<>
|
|
86
|
+
<Button variant="bare" size="icon-sm" aria-label="Search">
|
|
87
|
+
<Icon name="search" size={20} strokeWidth={1.75} />
|
|
88
|
+
</Button>
|
|
89
|
+
<Button variant="bare" size="icon-sm" aria-label="Notifications">
|
|
90
|
+
<Icon name="bell" size={20} strokeWidth={1.75} />
|
|
153
91
|
</Button>
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
92
|
+
<Avatar name={PROFILE.name} size="sm" />
|
|
93
|
+
</>
|
|
94
|
+
}
|
|
95
|
+
mobileDrawerFooter={
|
|
96
|
+
<Button variant="outline-soft" size="md" fullWidth>
|
|
97
|
+
Switch theme
|
|
98
|
+
</Button>
|
|
99
|
+
}
|
|
100
|
+
/>
|
|
101
|
+
|
|
102
|
+
<div className="flex min-w-0 flex-col gap-sm bg-canvas p-lg">
|
|
103
|
+
<p className="text-caption uppercase text-muted">Current route</p>
|
|
104
|
+
<p className="text-heading-md text-ink">{route}</p>
|
|
105
|
+
<p className="text-body-sm text-body">
|
|
106
|
+
Tasks carries a count pill after its label. Collapse the rail and the
|
|
107
|
+
same count becomes a dot on the glyph, while the row still announces
|
|
108
|
+
the number to a screen reader.
|
|
109
|
+
</p>
|
|
110
|
+
<p className="text-body-sm text-body">
|
|
111
|
+
{unread > 0
|
|
112
|
+
? `${unread} unread notifications sit in the footer bell.`
|
|
113
|
+
: "Notifications cleared — the bell badge is gone."}
|
|
114
|
+
</p>
|
|
157
115
|
</div>
|
|
158
|
-
</
|
|
116
|
+
</div>
|
|
159
117
|
</div>
|
|
160
118
|
);
|
|
161
119
|
}
|
|
@@ -36,6 +36,12 @@ export interface SidebarDesktopNavItem {
|
|
|
36
36
|
icon: IconName;
|
|
37
37
|
/** Destination for a plain row. */
|
|
38
38
|
href?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Pending-work count painted as a pill after the label, and as a dot on the
|
|
41
|
+
* glyph while the rail is folded. A number (99+ collapses), or a short string
|
|
42
|
+
* such as "New". Hidden when undefined, empty, or zero.
|
|
43
|
+
*/
|
|
44
|
+
badge?: number | string;
|
|
39
45
|
/** Nested rows: turns the parent into a disclosure. */
|
|
40
46
|
children?: Array<{ id: string; label: string; href?: string }>;
|
|
41
47
|
}
|
|
@@ -149,6 +155,85 @@ const glyph = (name: IconName, size = 16, strokeWidth = 1.75) => (
|
|
|
149
155
|
/>
|
|
150
156
|
);
|
|
151
157
|
|
|
158
|
+
/** True when an item carries a count worth painting. Zero and empties hide. */
|
|
159
|
+
const hasBadge = (badge?: number | string) => {
|
|
160
|
+
if (badge === undefined || badge === null) {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
if (typeof badge === "number") {
|
|
164
|
+
return Number.isFinite(badge) && badge !== 0;
|
|
165
|
+
}
|
|
166
|
+
const trimmed = badge.trim();
|
|
167
|
+
return trimmed.length > 0 && trimmed !== "0";
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
/** The pill copy: numbers above 99 collapse so a row never reflows. */
|
|
171
|
+
const badgeText = (badge: number | string) =>
|
|
172
|
+
typeof badge === "number"
|
|
173
|
+
? badge > 99
|
|
174
|
+
? "99+"
|
|
175
|
+
: String(badge)
|
|
176
|
+
: badge.trim();
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* The row's accessible name. The count rides along as pending work, so a folded
|
|
180
|
+
* icon rail still announces "API logs, 12 pending" instead of a bare glyph.
|
|
181
|
+
*/
|
|
182
|
+
const rowAccessibleName = (label: string, badge?: number | string) => {
|
|
183
|
+
if (!hasBadge(badge)) {
|
|
184
|
+
return label;
|
|
185
|
+
}
|
|
186
|
+
return typeof badge === "number"
|
|
187
|
+
? `${label}, ${badge} pending`
|
|
188
|
+
: `${label}, ${badgeText(badge as string)}`;
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* The count pill that follows a row's label. It wears the same recipe as the
|
|
193
|
+
* footer bell: the brand fill with inverse type on the navy rail, flipped to the
|
|
194
|
+
* inverse fill while the row itself is the selected brand block.
|
|
195
|
+
*/
|
|
196
|
+
const RailBadge = ({
|
|
197
|
+
badge,
|
|
198
|
+
active,
|
|
199
|
+
}: {
|
|
200
|
+
badge: number | string;
|
|
201
|
+
active?: boolean;
|
|
202
|
+
}) => (
|
|
203
|
+
<span
|
|
204
|
+
aria-hidden="true"
|
|
205
|
+
data-active={active || undefined}
|
|
206
|
+
className={cn(
|
|
207
|
+
"ml-auto flex h-4 min-w-4 shrink-0 items-center justify-center rounded-full",
|
|
208
|
+
"bg-primary px-xxs text-caption text-on-primary",
|
|
209
|
+
"data-[active=true]:bg-on-primary data-[active=true]:text-primary",
|
|
210
|
+
)}
|
|
211
|
+
>
|
|
212
|
+
{badgeText(badge)}
|
|
213
|
+
</span>
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* The leading glyph plus, while the rail is folded, a dot on its top corner.
|
|
218
|
+
* The folded rail has no room for a pill, so the dot carries the signal and the
|
|
219
|
+
* count lives in the row's accessible name.
|
|
220
|
+
*/
|
|
221
|
+
const railGlyph = (item: SidebarDesktopNavItem, folded: boolean) => (
|
|
222
|
+
<span className="relative inline-flex shrink-0 items-center">
|
|
223
|
+
{glyph(item.icon, 16, 1.75)}
|
|
224
|
+
{folded && hasBadge(item.badge) ? (
|
|
225
|
+
<span
|
|
226
|
+
aria-hidden="true"
|
|
227
|
+
className={cn(
|
|
228
|
+
"pointer-events-none absolute -right-xxs -top-xxs size-2 rounded-full",
|
|
229
|
+
"bg-primary",
|
|
230
|
+
"group-data-[active=true]:bg-on-primary",
|
|
231
|
+
)}
|
|
232
|
+
/>
|
|
233
|
+
) : null}
|
|
234
|
+
</span>
|
|
235
|
+
);
|
|
236
|
+
|
|
152
237
|
/** The visible label, kept in the tree as an accessible name while folded. */
|
|
153
238
|
const RowLabel = ({
|
|
154
239
|
folded,
|
|
@@ -170,7 +255,7 @@ const RowLabel = ({
|
|
|
170
255
|
*/
|
|
171
256
|
const railRowClass = (active: boolean, folded: boolean) =>
|
|
172
257
|
cn(
|
|
173
|
-
"flex w-full cursor-pointer items-center rounded-lg border-0 bg-transparent",
|
|
258
|
+
"group flex w-full cursor-pointer items-center rounded-lg border-0 bg-transparent",
|
|
174
259
|
"text-body-md transition duration-150 ease-out",
|
|
175
260
|
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-focus-blue",
|
|
176
261
|
"focus-visible:ring-inset",
|
|
@@ -201,6 +286,10 @@ interface PanelBox {
|
|
|
201
286
|
* width and the labels change, so the item order and the active row survive the
|
|
202
287
|
* fold.
|
|
203
288
|
*
|
|
289
|
+
* A destination may carry a count: unfolded it reads as a pill after the label,
|
|
290
|
+
* folded it becomes a dot on the glyph, and either way the number is part of the
|
|
291
|
+
* row's accessible name. Nothing is painted when the count is absent or zero.
|
|
292
|
+
*
|
|
204
293
|
* The rail fills the height of its parent (`h-full`), so a one-viewport shell
|
|
205
294
|
* (`md:sticky md:top-0 md:h-dvh`) is all the sizing it needs. The header, the
|
|
206
295
|
* account switcher and the footer stay pinned; only the destination list
|
|
@@ -606,6 +695,7 @@ export const SidebarDesktop = React.forwardRef<HTMLElement, SidebarDesktopProps>
|
|
|
606
695
|
>
|
|
607
696
|
{navItems.map((item) => {
|
|
608
697
|
const active = item.id === activeId;
|
|
698
|
+
const badged = hasBadge(item.badge);
|
|
609
699
|
|
|
610
700
|
if (!item.children?.length) {
|
|
611
701
|
return (
|
|
@@ -614,11 +704,19 @@ export const SidebarDesktop = React.forwardRef<HTMLElement, SidebarDesktopProps>
|
|
|
614
704
|
href={item.href ?? `#${item.id}`}
|
|
615
705
|
onClick={navigate({ id: item.id, href: item.href })}
|
|
616
706
|
aria-current={active ? "page" : undefined}
|
|
617
|
-
|
|
707
|
+
data-active={active}
|
|
708
|
+
aria-label={
|
|
709
|
+
isFolded || badged
|
|
710
|
+
? rowAccessibleName(item.label, item.badge)
|
|
711
|
+
: undefined
|
|
712
|
+
}
|
|
618
713
|
className={railRowClass(active, isFolded)}
|
|
619
714
|
>
|
|
620
|
-
{
|
|
715
|
+
{railGlyph(item, isFolded)}
|
|
621
716
|
<RowLabel folded={isFolded}>{item.label}</RowLabel>
|
|
717
|
+
{!isFolded && badged ? (
|
|
718
|
+
<RailBadge badge={item.badge as number | string} active={active} />
|
|
719
|
+
) : null}
|
|
622
720
|
</a>
|
|
623
721
|
);
|
|
624
722
|
}
|
|
@@ -633,21 +731,34 @@ export const SidebarDesktop = React.forwardRef<HTMLElement, SidebarDesktopProps>
|
|
|
633
731
|
size="rail"
|
|
634
732
|
aria-expanded={expanded}
|
|
635
733
|
aria-controls={subId}
|
|
636
|
-
aria-label={
|
|
734
|
+
aria-label={
|
|
735
|
+
isFolded || badged
|
|
736
|
+
? rowAccessibleName(item.label, item.badge)
|
|
737
|
+
: undefined
|
|
738
|
+
}
|
|
637
739
|
data-active={active}
|
|
638
740
|
data-folded={isFolded}
|
|
639
741
|
onClick={() => toggleExpanded(item.id)}
|
|
640
|
-
|
|
742
|
+
className="group"
|
|
743
|
+
leadingIcon={railGlyph(item, isFolded)}
|
|
641
744
|
trailingIcon={
|
|
642
745
|
isFolded ? undefined : (
|
|
643
|
-
<span
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
746
|
+
<span className="flex items-center gap-xs">
|
|
747
|
+
{badged ? (
|
|
748
|
+
<RailBadge
|
|
749
|
+
badge={item.badge as number | string}
|
|
750
|
+
active={active}
|
|
751
|
+
/>
|
|
752
|
+
) : null}
|
|
753
|
+
<span
|
|
754
|
+
aria-hidden="true"
|
|
755
|
+
className={cn(
|
|
756
|
+
"flex shrink-0 items-center transition-transform duration-200 ease-out",
|
|
757
|
+
expanded ? "rotate-180" : "rotate-0",
|
|
758
|
+
)}
|
|
759
|
+
>
|
|
760
|
+
{glyph("chevron-down", 16, 2)}
|
|
761
|
+
</span>
|
|
651
762
|
</span>
|
|
652
763
|
)
|
|
653
764
|
}
|
|
@@ -713,6 +824,7 @@ export const SidebarDesktop = React.forwardRef<HTMLElement, SidebarDesktopProps>
|
|
|
713
824
|
<nav aria-label="Secondary" className="mt-lg flex flex-col gap-xxs">
|
|
714
825
|
{secondaryNav.map((item) => {
|
|
715
826
|
const active = item.id === secondaryActiveId;
|
|
827
|
+
const badged = hasBadge(item.badge);
|
|
716
828
|
|
|
717
829
|
return (
|
|
718
830
|
<a
|
|
@@ -720,11 +832,19 @@ export const SidebarDesktop = React.forwardRef<HTMLElement, SidebarDesktopProps>
|
|
|
720
832
|
href={item.href ?? `#${item.id}`}
|
|
721
833
|
onClick={navigate({ id: item.id, href: item.href })}
|
|
722
834
|
aria-current={active ? "page" : undefined}
|
|
723
|
-
|
|
835
|
+
data-active={active}
|
|
836
|
+
aria-label={
|
|
837
|
+
isFolded || badged
|
|
838
|
+
? rowAccessibleName(item.label, item.badge)
|
|
839
|
+
: undefined
|
|
840
|
+
}
|
|
724
841
|
className={railRowClass(active, isFolded)}
|
|
725
842
|
>
|
|
726
|
-
{
|
|
843
|
+
{railGlyph(item, isFolded)}
|
|
727
844
|
<RowLabel folded={isFolded}>{item.label}</RowLabel>
|
|
845
|
+
{!isFolded && badged ? (
|
|
846
|
+
<RailBadge badge={item.badge as number | string} active={active} />
|
|
847
|
+
) : null}
|
|
728
848
|
</a>
|
|
729
849
|
);
|
|
730
850
|
})}
|
|
@@ -33,6 +33,12 @@ export interface SidebarMobileNavItem {
|
|
|
33
33
|
icon: IconName;
|
|
34
34
|
/** Destination for a plain row. */
|
|
35
35
|
href?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Pending-work count painted as a pill after the label. A number (99+
|
|
38
|
+
* collapses), or a short string such as "New". Hidden when undefined, empty,
|
|
39
|
+
* or zero, and always part of the row's accessible name.
|
|
40
|
+
*/
|
|
41
|
+
badge?: number | string;
|
|
36
42
|
/** Nested rows: turns the parent into a disclosure. */
|
|
37
43
|
children?: Array<{ id: string; label: string; href?: string }>;
|
|
38
44
|
}
|
|
@@ -156,6 +162,64 @@ const glyph = (name: IconName, size = 18, strokeWidth = 1.75) => (
|
|
|
156
162
|
<Icon name={name} size={size} strokeWidth={strokeWidth} style={{ color: "inherit" }} />
|
|
157
163
|
);
|
|
158
164
|
|
|
165
|
+
/** True when an item carries a count worth painting. Zero and empties hide. */
|
|
166
|
+
const hasBadge = (badge?: number | string) => {
|
|
167
|
+
if (badge === undefined || badge === null) {
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
if (typeof badge === "number") {
|
|
171
|
+
return Number.isFinite(badge) && badge !== 0;
|
|
172
|
+
}
|
|
173
|
+
const trimmed = badge.trim();
|
|
174
|
+
return trimmed.length > 0 && trimmed !== "0";
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
/** The pill copy: numbers above 99 collapse so a row never reflows. */
|
|
178
|
+
const badgeText = (badge: number | string) =>
|
|
179
|
+
typeof badge === "number"
|
|
180
|
+
? badge > 99
|
|
181
|
+
? "99+"
|
|
182
|
+
: String(badge)
|
|
183
|
+
: badge.trim();
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* The row's accessible name. The count rides along as pending work, so the row
|
|
187
|
+
* announces "Tasks, 2 pending" instead of only its label.
|
|
188
|
+
*/
|
|
189
|
+
const rowAccessibleName = (label: string, badge?: number | string) => {
|
|
190
|
+
if (!hasBadge(badge)) {
|
|
191
|
+
return label;
|
|
192
|
+
}
|
|
193
|
+
return typeof badge === "number"
|
|
194
|
+
? `${label}, ${badge} pending`
|
|
195
|
+
: `${label}, ${badgeText(badge as string)}`;
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* The count pill that follows a row's label. It wears the brand fill with
|
|
200
|
+
* inverse type on the light drawer, flipped to the inverse fill while the row
|
|
201
|
+
* itself is the selected brand block.
|
|
202
|
+
*/
|
|
203
|
+
const NavBadge = ({
|
|
204
|
+
badge,
|
|
205
|
+
active,
|
|
206
|
+
}: {
|
|
207
|
+
badge: number | string;
|
|
208
|
+
active?: boolean;
|
|
209
|
+
}) => (
|
|
210
|
+
<span
|
|
211
|
+
aria-hidden="true"
|
|
212
|
+
data-active={active || undefined}
|
|
213
|
+
className={cn(
|
|
214
|
+
"ml-auto flex h-4 min-w-4 shrink-0 items-center justify-center rounded-full",
|
|
215
|
+
"bg-primary px-xxs text-caption text-on-primary",
|
|
216
|
+
"data-[active=true]:bg-on-primary data-[active=true]:text-primary",
|
|
217
|
+
)}
|
|
218
|
+
>
|
|
219
|
+
{badgeText(badge)}
|
|
220
|
+
</span>
|
|
221
|
+
);
|
|
222
|
+
|
|
159
223
|
/**
|
|
160
224
|
* A destination row in the drawer. A nav row spans the panel's gutter, so its
|
|
161
225
|
* press is a tint rather than a scale — scaling would pull the fill away from
|
|
@@ -174,8 +238,8 @@ const rowClass = (active: boolean) =>
|
|
|
174
238
|
);
|
|
175
239
|
|
|
176
240
|
/** A nested destination: one step quieter and tighter than its parent, indented
|
|
177
|
-
*
|
|
178
|
-
*
|
|
241
|
+
* onto the parent's label by an invisible glyph-sized spacer rather than a
|
|
242
|
+
* second padding utility. */
|
|
179
243
|
const subRowClass = cn(
|
|
180
244
|
"flex min-h-7 w-full cursor-pointer items-center gap-md rounded-md px-md py-xxs",
|
|
181
245
|
"text-body-sm text-body transition duration-150 ease-out",
|
|
@@ -203,6 +267,10 @@ const isModified = (event: React.MouseEvent) =>
|
|
|
203
267
|
* profile row pinned to the foot of the panel. A row click closes the drawer, so
|
|
204
268
|
* a routed navigation never leaves the panel hanging open behind the new page.
|
|
205
269
|
*
|
|
270
|
+
* A destination may carry a count: it reads as a pill after the label, and the
|
|
271
|
+
* number is part of the row's accessible name. Nothing is painted when the
|
|
272
|
+
* count is absent or zero.
|
|
273
|
+
*
|
|
206
274
|
* The panel stays mounted and travels with `translate-x`, so the close
|
|
207
275
|
* animation is never skipped; it only drops out of the paint once it has
|
|
208
276
|
* finished travelling, and nothing inside can be tabbed while it is off-canvas.
|
|
@@ -557,6 +625,7 @@ export const SidebarMobile = React.forwardRef<HTMLDivElement, SidebarMobileProps
|
|
|
557
625
|
<nav aria-label="Primary" className="mt-md flex flex-col gap-xxs">
|
|
558
626
|
{navItems.map((item) => {
|
|
559
627
|
const active = item.id === activeId;
|
|
628
|
+
const badged = hasBadge(item.badge);
|
|
560
629
|
|
|
561
630
|
if (!item.children?.length) {
|
|
562
631
|
return (
|
|
@@ -565,10 +634,21 @@ export const SidebarMobile = React.forwardRef<HTMLDivElement, SidebarMobileProps
|
|
|
565
634
|
href={item.href ?? `#${item.id}`}
|
|
566
635
|
onClick={navigate({ id: item.id, href: item.href })}
|
|
567
636
|
aria-current={active ? "page" : undefined}
|
|
637
|
+
aria-label={
|
|
638
|
+
badged
|
|
639
|
+
? rowAccessibleName(item.label, item.badge)
|
|
640
|
+
: undefined
|
|
641
|
+
}
|
|
568
642
|
className={rowClass(active)}
|
|
569
643
|
>
|
|
570
644
|
{glyph(item.icon, 18, 1.75)}
|
|
571
645
|
<span className="truncate">{item.label}</span>
|
|
646
|
+
{badged ? (
|
|
647
|
+
<NavBadge
|
|
648
|
+
badge={item.badge as number | string}
|
|
649
|
+
active={active}
|
|
650
|
+
/>
|
|
651
|
+
) : null}
|
|
572
652
|
</a>
|
|
573
653
|
);
|
|
574
654
|
}
|
|
@@ -583,18 +663,32 @@ export const SidebarMobile = React.forwardRef<HTMLDivElement, SidebarMobileProps
|
|
|
583
663
|
size="rail"
|
|
584
664
|
aria-expanded={expanded}
|
|
585
665
|
aria-controls={subId}
|
|
666
|
+
aria-label={
|
|
667
|
+
badged
|
|
668
|
+
? rowAccessibleName(item.label, item.badge)
|
|
669
|
+
: undefined
|
|
670
|
+
}
|
|
586
671
|
data-active={active}
|
|
587
672
|
onClick={() => toggleExpanded(item.id)}
|
|
588
673
|
className="px-md"
|
|
589
674
|
leadingIcon={glyph(item.icon, 18, 1.75)}
|
|
590
675
|
trailingIcon={
|
|
591
|
-
<span
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
676
|
+
<span className="flex items-center gap-xs">
|
|
677
|
+
{badged ? (
|
|
678
|
+
<NavBadge
|
|
679
|
+
badge={item.badge as number | string}
|
|
680
|
+
active={active}
|
|
681
|
+
/>
|
|
682
|
+
) : null}
|
|
683
|
+
<span
|
|
684
|
+
aria-hidden="true"
|
|
685
|
+
className={cn(
|
|
686
|
+
"flex shrink-0 items-center transition-transform duration-200 ease-out",
|
|
687
|
+
expanded ? "rotate-180" : "rotate-0",
|
|
688
|
+
)}
|
|
689
|
+
>
|
|
690
|
+
{glyph("chevron-down", 18, 2)}
|
|
691
|
+
</span>
|
|
598
692
|
</span>
|
|
599
693
|
}
|
|
600
694
|
>
|
|
@@ -659,6 +753,7 @@ export const SidebarMobile = React.forwardRef<HTMLDivElement, SidebarMobileProps
|
|
|
659
753
|
<nav aria-label="Secondary" className="flex flex-col gap-xxs">
|
|
660
754
|
{secondaryNav.map((item) => {
|
|
661
755
|
const active = item.id === secondaryActiveId;
|
|
756
|
+
const badged = hasBadge(item.badge);
|
|
662
757
|
|
|
663
758
|
return (
|
|
664
759
|
<a
|
|
@@ -666,10 +761,21 @@ export const SidebarMobile = React.forwardRef<HTMLDivElement, SidebarMobileProps
|
|
|
666
761
|
href={item.href ?? `#${item.id}`}
|
|
667
762
|
onClick={navigate({ id: item.id, href: item.href })}
|
|
668
763
|
aria-current={active ? "page" : undefined}
|
|
764
|
+
aria-label={
|
|
765
|
+
badged
|
|
766
|
+
? rowAccessibleName(item.label, item.badge)
|
|
767
|
+
: undefined
|
|
768
|
+
}
|
|
669
769
|
className={rowClass(active)}
|
|
670
770
|
>
|
|
671
771
|
{glyph(item.icon, 18, 1.75)}
|
|
672
772
|
<span className="truncate">{item.label}</span>
|
|
773
|
+
{badged ? (
|
|
774
|
+
<NavBadge
|
|
775
|
+
badge={item.badge as number | string}
|
|
776
|
+
active={active}
|
|
777
|
+
/>
|
|
778
|
+
) : null}
|
|
673
779
|
</a>
|
|
674
780
|
);
|
|
675
781
|
})}
|