@fairmint/ui 0.3.26 → 0.3.27
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/dist/_virtual/index3.cjs +3 -5
- package/dist/_virtual/index3.cjs.map +1 -1
- package/dist/_virtual/index3.js +2 -5
- package/dist/_virtual/index3.js.map +1 -1
- package/dist/_virtual/index4.cjs +5 -3
- package/dist/_virtual/index4.cjs.map +1 -1
- package/dist/_virtual/index4.js +5 -2
- package/dist/_virtual/index4.js.map +1 -1
- package/dist/components/FairmintLogo/FairmintLogoMarkRounded.cjs +82 -0
- package/dist/components/FairmintLogo/FairmintLogoMarkRounded.cjs.map +1 -0
- package/dist/components/FairmintLogo/FairmintLogoMarkRounded.d.ts +41 -0
- package/dist/components/FairmintLogo/FairmintLogoMarkRounded.d.ts.map +1 -0
- package/dist/components/FairmintLogo/FairmintLogoMarkRounded.js +82 -0
- package/dist/components/FairmintLogo/FairmintLogoMarkRounded.js.map +1 -0
- package/dist/components/layout/SideNav/SideNav.cjs +362 -19
- package/dist/components/layout/SideNav/SideNav.cjs.map +1 -1
- package/dist/components/layout/SideNav/SideNav.d.ts +45 -4
- package/dist/components/layout/SideNav/SideNav.d.ts.map +1 -1
- package/dist/components/layout/SideNav/SideNav.js +364 -21
- package/dist/components/layout/SideNav/SideNav.js.map +1 -1
- package/dist/fairmint-ui.cjs +8 -0
- package/dist/fairmint-ui.cjs.map +1 -1
- package/dist/fairmint-ui.js +9 -1
- package/dist/fairmint-ui.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/node_modules/@mantine/core/esm/components/Slider/Slider/Slider.cjs +1 -1
- package/dist/node_modules/@mantine/core/esm/components/Slider/Slider/Slider.js +1 -1
- package/dist/node_modules/@mantine/form/esm/hooks/use-form-status/use-form-status.cjs +1 -1
- package/dist/node_modules/@mantine/form/esm/hooks/use-form-status/use-form-status.js +1 -1
- package/dist/node_modules/@tabler/icons-react/dist/esm/icons/IconLayoutSidebarLeftCollapse.cjs +8 -0
- package/dist/node_modules/@tabler/icons-react/dist/esm/icons/IconLayoutSidebarLeftCollapse.cjs.map +1 -0
- package/dist/node_modules/@tabler/icons-react/dist/esm/icons/IconLayoutSidebarLeftCollapse.js +8 -0
- package/dist/node_modules/@tabler/icons-react/dist/esm/icons/IconLayoutSidebarLeftCollapse.js.map +1 -0
- package/dist/node_modules/@tabler/icons-react/dist/esm/icons/IconLayoutSidebarLeftExpand.cjs +8 -0
- package/dist/node_modules/@tabler/icons-react/dist/esm/icons/IconLayoutSidebarLeftExpand.cjs.map +1 -0
- package/dist/node_modules/@tabler/icons-react/dist/esm/icons/IconLayoutSidebarLeftExpand.js +8 -0
- package/dist/node_modules/@tabler/icons-react/dist/esm/icons/IconLayoutSidebarLeftExpand.js.map +1 -0
- package/dist/node_modules/prop-types/index.cjs +1 -1
- package/dist/node_modules/prop-types/index.js +1 -1
- package/dist/theme/index.cjs +9 -4
- package/dist/theme/index.cjs.map +1 -1
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/theme/index.js +9 -4
- package/dist/theme/index.js.map +1 -1
- package/dist/theme/logo.cjs +2 -0
- package/dist/theme/logo.cjs.map +1 -1
- package/dist/theme/logo.d.ts +2 -0
- package/dist/theme/logo.d.ts.map +1 -1
- package/dist/theme/logo.js +2 -0
- package/dist/theme/logo.js.map +1 -1
- package/package.json +1 -1
|
@@ -3,15 +3,143 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
3
3
|
const jsxRuntime = require("react/jsx-runtime");
|
|
4
4
|
const React = require("react");
|
|
5
5
|
const index = require("../../../theme/index.cjs");
|
|
6
|
+
const useUncontrolled = require("../../../node_modules/@mantine/hooks/esm/use-uncontrolled/use-uncontrolled.cjs");
|
|
6
7
|
const Stack = require("../../../node_modules/@mantine/core/esm/components/Stack/Stack.cjs");
|
|
7
|
-
const
|
|
8
|
+
const Box = require("../../../node_modules/@mantine/core/esm/core/Box/Box.cjs");
|
|
9
|
+
const Group = require("../../../node_modules/@mantine/core/esm/components/Group/Group.cjs");
|
|
10
|
+
const Divider = require("../../../node_modules/@mantine/core/esm/components/Divider/Divider.cjs");
|
|
11
|
+
const UnstyledButton = require("../../../node_modules/@mantine/core/esm/components/UnstyledButton/UnstyledButton.cjs");
|
|
12
|
+
const IconLayoutSidebarLeftExpand = require("../../../node_modules/@tabler/icons-react/dist/esm/icons/IconLayoutSidebarLeftExpand.cjs");
|
|
13
|
+
const IconLayoutSidebarLeftCollapse = require("../../../node_modules/@tabler/icons-react/dist/esm/icons/IconLayoutSidebarLeftCollapse.cjs");
|
|
14
|
+
const Tooltip = require("../../../node_modules/@mantine/core/esm/components/Tooltip/Tooltip.cjs");
|
|
15
|
+
const ActionIcon = require("../../../node_modules/@mantine/core/esm/components/ActionIcon/ActionIcon.cjs");
|
|
16
|
+
const Menu = require("../../../node_modules/@mantine/core/esm/components/Menu/Menu.cjs");
|
|
8
17
|
const NavLink = require("../../../node_modules/@mantine/core/esm/components/NavLink/NavLink.cjs");
|
|
18
|
+
const SIDE_NAV_WIDTH = 260;
|
|
19
|
+
const SIDE_NAV_COLLAPSED_WIDTH = 80;
|
|
9
20
|
function hasActiveDescendant(item) {
|
|
10
21
|
return item.children?.some((child) => child.active || hasActiveDescendant(child)) ?? false;
|
|
11
22
|
}
|
|
12
|
-
function
|
|
23
|
+
function getSideNavItemLabelText(item) {
|
|
24
|
+
if (item.ariaLabel) {
|
|
25
|
+
return item.ariaLabel;
|
|
26
|
+
}
|
|
27
|
+
if (typeof item.label === "string") {
|
|
28
|
+
return item.label;
|
|
29
|
+
}
|
|
30
|
+
if (typeof item.label === "number") {
|
|
31
|
+
return String(item.label);
|
|
32
|
+
}
|
|
33
|
+
return "Item";
|
|
34
|
+
}
|
|
35
|
+
function getSideNavLetterFallback(labelText) {
|
|
36
|
+
const match = labelText.match(/[\p{L}\p{N}]/u);
|
|
37
|
+
return match ? match[0].toUpperCase() : "?";
|
|
38
|
+
}
|
|
39
|
+
function LetterFallback({ letter, active = false }) {
|
|
40
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
41
|
+
Box.Box,
|
|
42
|
+
{
|
|
43
|
+
component: "span",
|
|
44
|
+
"aria-hidden": true,
|
|
45
|
+
"data-active": active || void 0,
|
|
46
|
+
style: {
|
|
47
|
+
display: "inline-flex",
|
|
48
|
+
alignItems: "center",
|
|
49
|
+
justifyContent: "center",
|
|
50
|
+
width: 20,
|
|
51
|
+
height: 20,
|
|
52
|
+
flexShrink: 0,
|
|
53
|
+
fontSize: 12,
|
|
54
|
+
fontWeight: active ? 700 : 600,
|
|
55
|
+
lineHeight: 1,
|
|
56
|
+
// Selected chrome lives on the parent control — glyph matches icon sizing.
|
|
57
|
+
color: active ? index.fairmintVars.colorIconOnSurfaceSelected : "inherit"
|
|
58
|
+
},
|
|
59
|
+
children: letter
|
|
60
|
+
}
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
function CollapseToggle({
|
|
64
|
+
collapsed,
|
|
65
|
+
onToggle,
|
|
66
|
+
visible
|
|
67
|
+
}) {
|
|
68
|
+
const label = collapsed ? "Expand navigation" : "Collapse navigation";
|
|
69
|
+
const Icon = collapsed ? IconLayoutSidebarLeftExpand.default : IconLayoutSidebarLeftCollapse.default;
|
|
70
|
+
const button = /* @__PURE__ */ jsxRuntime.jsx(
|
|
71
|
+
UnstyledButton.UnstyledButton,
|
|
72
|
+
{
|
|
73
|
+
"data-sidenav-collapse": true,
|
|
74
|
+
"aria-label": label,
|
|
75
|
+
"aria-expanded": !collapsed,
|
|
76
|
+
onClick: onToggle,
|
|
77
|
+
style: {
|
|
78
|
+
display: "inline-flex",
|
|
79
|
+
alignItems: "center",
|
|
80
|
+
justifyContent: "center",
|
|
81
|
+
height: 32,
|
|
82
|
+
flexShrink: 0,
|
|
83
|
+
borderRadius: index.radii.md,
|
|
84
|
+
color: index.fairmintVars.colorIconTertiary,
|
|
85
|
+
opacity: visible ? 1 : 0,
|
|
86
|
+
pointerEvents: visible ? "auto" : "none",
|
|
87
|
+
transition: "opacity 120ms ease",
|
|
88
|
+
// Keep a layout slot when expanded so the wordmark does not shift on hover.
|
|
89
|
+
// Collapsed: always a fixed control below the logo.
|
|
90
|
+
width: 32,
|
|
91
|
+
overflow: "hidden",
|
|
92
|
+
padding: 0
|
|
93
|
+
},
|
|
94
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { size: 20, stroke: 1.5, "aria-hidden": true })
|
|
95
|
+
}
|
|
96
|
+
);
|
|
97
|
+
if (!collapsed) {
|
|
98
|
+
return button;
|
|
99
|
+
}
|
|
100
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
101
|
+
Tooltip.Tooltip,
|
|
102
|
+
{
|
|
103
|
+
label,
|
|
104
|
+
position: "right",
|
|
105
|
+
withArrow: true,
|
|
106
|
+
events: { hover: true, focus: true, touch: true },
|
|
107
|
+
disabled: !visible,
|
|
108
|
+
children: button
|
|
109
|
+
}
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
function resolveLeftSection(item, labelText, { requireIcon, active }) {
|
|
113
|
+
if (item.icon) {
|
|
114
|
+
return item.icon;
|
|
115
|
+
}
|
|
116
|
+
if (requireIcon) {
|
|
117
|
+
return /* @__PURE__ */ jsxRuntime.jsx(LetterFallback, { letter: getSideNavLetterFallback(labelText), active: Boolean(active) });
|
|
118
|
+
}
|
|
119
|
+
return void 0;
|
|
120
|
+
}
|
|
121
|
+
const COLLAPSED_ITEM_SIZE = 36;
|
|
122
|
+
const navLinkStyles = (active) => ({
|
|
123
|
+
root: {
|
|
124
|
+
color: active ? index.fairmintVars.colorTextPrimary : index.fairmintVars.colorTextSecondary,
|
|
125
|
+
fontWeight: active ? 500 : 400,
|
|
126
|
+
borderRadius: index.radii.md,
|
|
127
|
+
// Prefer Fairmint selected surface over Mantine defaults for active chrome.
|
|
128
|
+
...active ? {
|
|
129
|
+
"--nl-bg": index.fairmintVars.colorSurfaceSelected,
|
|
130
|
+
"--nl-hover": index.fairmintVars.colorSurfaceSelected,
|
|
131
|
+
"--nl-color": index.fairmintVars.colorIconOnSurfaceSelected,
|
|
132
|
+
backgroundColor: index.fairmintVars.colorSurfaceSelected
|
|
133
|
+
} : {}
|
|
134
|
+
},
|
|
135
|
+
section: {
|
|
136
|
+
color: active ? index.fairmintVars.colorIconOnSurfaceSelected : index.fairmintVars.colorIconTertiary
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
function ExpandedSideNavNode({ color, item, onItemClick }) {
|
|
13
140
|
const containsActiveItem = hasActiveDescendant(item);
|
|
14
141
|
const [opened, setOpened] = React.useState(item.open ?? containsActiveItem);
|
|
142
|
+
const labelText = getSideNavItemLabelText(item);
|
|
15
143
|
React.useEffect(() => {
|
|
16
144
|
if (item.open !== void 0) {
|
|
17
145
|
setOpened(item.open);
|
|
@@ -23,8 +151,13 @@ function SideNavNode({ color, item, onItemClick }) {
|
|
|
23
151
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
24
152
|
NavLink.NavLink,
|
|
25
153
|
{
|
|
154
|
+
component: "button",
|
|
155
|
+
type: "button",
|
|
26
156
|
label: item.label,
|
|
27
|
-
leftSection: item
|
|
157
|
+
leftSection: resolveLeftSection(item, labelText, {
|
|
158
|
+
requireIcon: false,
|
|
159
|
+
active: item.active
|
|
160
|
+
}),
|
|
28
161
|
rightSection: item.rightSection,
|
|
29
162
|
active: item.active,
|
|
30
163
|
disabled: item.disabled,
|
|
@@ -32,20 +165,125 @@ function SideNavNode({ color, item, onItemClick }) {
|
|
|
32
165
|
variant: "light",
|
|
33
166
|
childrenOffset: 16,
|
|
34
167
|
opened: hasChildren ? opened : void 0,
|
|
168
|
+
"aria-label": item.ariaLabel,
|
|
35
169
|
onClick: hasChildren ? () => setOpened((currentOpened) => !currentOpened) : () => onItemClick(item.id),
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
170
|
+
styles: navLinkStyles(item.active),
|
|
171
|
+
children: item.children?.map((child) => /* @__PURE__ */ jsxRuntime.jsx(ExpandedSideNavNode, { item: child, color, onItemClick }, child.id))
|
|
172
|
+
}
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
function collectCollapsedMenuLeaves(items) {
|
|
176
|
+
return items.flatMap(
|
|
177
|
+
(item) => item.children?.length ? collectCollapsedMenuLeaves(item.children) : [item]
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
function CollapsedNestedMenuItems({
|
|
181
|
+
items,
|
|
182
|
+
onItemClick,
|
|
183
|
+
color
|
|
184
|
+
}) {
|
|
185
|
+
return collectCollapsedMenuLeaves(items).map((child) => {
|
|
186
|
+
const childLabel = getSideNavItemLabelText(child);
|
|
187
|
+
const isActive = Boolean(child.active);
|
|
188
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
189
|
+
Menu.Menu.Item,
|
|
190
|
+
{
|
|
191
|
+
disabled: child.disabled,
|
|
192
|
+
leftSection: resolveLeftSection(child, childLabel, {
|
|
193
|
+
requireIcon: true,
|
|
194
|
+
active: isActive
|
|
195
|
+
}),
|
|
196
|
+
onClick: () => onItemClick(child.id),
|
|
197
|
+
"aria-current": isActive ? "page" : void 0,
|
|
198
|
+
"data-active": isActive || void 0,
|
|
199
|
+
color: isActive ? color : void 0,
|
|
200
|
+
style: isActive ? {
|
|
201
|
+
backgroundColor: index.fairmintVars.colorSurfaceSelected,
|
|
202
|
+
color: index.fairmintVars.colorIconOnSurfaceSelected,
|
|
203
|
+
fontWeight: 600
|
|
204
|
+
} : void 0,
|
|
205
|
+
children: child.label
|
|
206
|
+
},
|
|
207
|
+
child.id
|
|
208
|
+
);
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
function CollapsedSideNavNode({ color, item, onItemClick }) {
|
|
212
|
+
const labelText = getSideNavItemLabelText(item);
|
|
213
|
+
const hasChildren = Boolean(item.children?.length);
|
|
214
|
+
const isActive = Boolean(item.active || hasActiveDescendant(item));
|
|
215
|
+
const leftSection = resolveLeftSection(item, labelText, {
|
|
216
|
+
requireIcon: true,
|
|
217
|
+
active: isActive
|
|
218
|
+
});
|
|
219
|
+
const link = /* @__PURE__ */ jsxRuntime.jsx(
|
|
220
|
+
ActionIcon.ActionIcon,
|
|
221
|
+
{
|
|
222
|
+
type: "button",
|
|
223
|
+
variant: "subtle",
|
|
224
|
+
color,
|
|
225
|
+
size: COLLAPSED_ITEM_SIZE,
|
|
226
|
+
radius: "md",
|
|
227
|
+
disabled: item.disabled,
|
|
228
|
+
"aria-label": labelText,
|
|
229
|
+
"aria-current": isActive ? "page" : void 0,
|
|
230
|
+
"data-active": isActive || void 0,
|
|
231
|
+
onClick: hasChildren ? void 0 : () => onItemClick(item.id),
|
|
232
|
+
style: {
|
|
233
|
+
width: COLLAPSED_ITEM_SIZE,
|
|
234
|
+
height: COLLAPSED_ITEM_SIZE,
|
|
235
|
+
minWidth: COLLAPSED_ITEM_SIZE,
|
|
236
|
+
minHeight: COLLAPSED_ITEM_SIZE,
|
|
237
|
+
...isActive ? {
|
|
238
|
+
"--ai-bg": index.fairmintVars.colorSurfaceSelected,
|
|
239
|
+
"--ai-hover": index.fairmintVars.colorSurfaceSelected,
|
|
240
|
+
"--ai-color": index.fairmintVars.colorIconOnSurfaceSelected,
|
|
241
|
+
"--ai-hover-color": index.fairmintVars.colorIconOnSurfaceSelected,
|
|
242
|
+
backgroundColor: index.fairmintVars.colorSurfaceSelected,
|
|
243
|
+
color: index.fairmintVars.colorIconOnSurfaceSelected
|
|
244
|
+
} : {
|
|
245
|
+
color: index.fairmintVars.colorIconTertiary
|
|
44
246
|
}
|
|
45
247
|
},
|
|
46
|
-
children:
|
|
248
|
+
children: leftSection
|
|
249
|
+
}
|
|
250
|
+
);
|
|
251
|
+
const control = hasChildren ? /* @__PURE__ */ jsxRuntime.jsxs(Menu.Menu, { position: "right-start", offset: 8, withinPortal: true, children: [
|
|
252
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
253
|
+
Tooltip.Tooltip,
|
|
254
|
+
{
|
|
255
|
+
label: labelText,
|
|
256
|
+
position: "right",
|
|
257
|
+
withArrow: true,
|
|
258
|
+
events: { hover: true, focus: true, touch: true },
|
|
259
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(Menu.Menu.Target, { children: link })
|
|
260
|
+
}
|
|
261
|
+
),
|
|
262
|
+
/* @__PURE__ */ jsxRuntime.jsx(Menu.Menu.Dropdown, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
263
|
+
CollapsedNestedMenuItems,
|
|
264
|
+
{
|
|
265
|
+
items: item.children ?? [],
|
|
266
|
+
onItemClick,
|
|
267
|
+
color
|
|
268
|
+
}
|
|
269
|
+
) })
|
|
270
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
271
|
+
Tooltip.Tooltip,
|
|
272
|
+
{
|
|
273
|
+
label: labelText,
|
|
274
|
+
position: "right",
|
|
275
|
+
withArrow: true,
|
|
276
|
+
events: { hover: true, focus: true, touch: true },
|
|
277
|
+
children: link
|
|
47
278
|
}
|
|
48
279
|
);
|
|
280
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Box.Box, { style: { width: "100%", display: "flex", justifyContent: "center" }, children: control });
|
|
281
|
+
}
|
|
282
|
+
function SideNavNode(props) {
|
|
283
|
+
if (props.collapsed) {
|
|
284
|
+
return /* @__PURE__ */ jsxRuntime.jsx(CollapsedSideNavNode, { ...props });
|
|
285
|
+
}
|
|
286
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ExpandedSideNavNode, { ...props });
|
|
49
287
|
}
|
|
50
288
|
function SideNav({
|
|
51
289
|
items = [],
|
|
@@ -53,28 +291,133 @@ function SideNav({
|
|
|
53
291
|
onItemClick,
|
|
54
292
|
color = "indigo",
|
|
55
293
|
ariaLabel = "Main navigation",
|
|
56
|
-
withBorder = false
|
|
294
|
+
withBorder = false,
|
|
295
|
+
header,
|
|
296
|
+
cta,
|
|
297
|
+
profile,
|
|
298
|
+
footer,
|
|
299
|
+
collapsed,
|
|
300
|
+
defaultCollapsed = false,
|
|
301
|
+
onCollapsedChange,
|
|
302
|
+
collapsible = false
|
|
57
303
|
}) {
|
|
58
304
|
const resolvedSections = sections ?? [{ id: "main", items }];
|
|
59
|
-
|
|
305
|
+
const [collapsedState, setCollapsedState] = useUncontrolled.useUncontrolled({
|
|
306
|
+
value: collapsed,
|
|
307
|
+
defaultValue: defaultCollapsed,
|
|
308
|
+
finalValue: false,
|
|
309
|
+
onChange: onCollapsedChange
|
|
310
|
+
});
|
|
311
|
+
const hasBottomChrome = Boolean(footer || cta || profile);
|
|
312
|
+
const [headerHovered, setHeaderHovered] = React.useState(false);
|
|
313
|
+
const showExpandedCollapseControl = headerHovered;
|
|
314
|
+
const showHeaderChrome = Boolean(header) || collapsible;
|
|
315
|
+
const collapseToggle = collapsible ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
316
|
+
CollapseToggle,
|
|
317
|
+
{
|
|
318
|
+
collapsed: collapsedState,
|
|
319
|
+
visible: collapsedState ? true : showExpandedCollapseControl,
|
|
320
|
+
onToggle: () => setCollapsedState(!collapsedState)
|
|
321
|
+
}
|
|
322
|
+
) : null;
|
|
323
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
60
324
|
Stack.Stack,
|
|
61
325
|
{
|
|
62
326
|
component: "nav",
|
|
63
327
|
gap: "md",
|
|
64
328
|
h: "100%",
|
|
65
|
-
p: "md",
|
|
329
|
+
p: collapsedState ? "sm" : "md",
|
|
66
330
|
"aria-label": ariaLabel,
|
|
331
|
+
"data-collapsed": collapsedState || void 0,
|
|
67
332
|
style: {
|
|
68
333
|
borderRight: withBorder ? "1px solid" : void 0,
|
|
69
334
|
borderRightColor: withBorder ? index.fairmintVars.colorBorderDefault : void 0,
|
|
70
335
|
boxShadow: "none"
|
|
71
336
|
},
|
|
72
|
-
children:
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
337
|
+
children: [
|
|
338
|
+
showHeaderChrome ? collapsedState ? /* @__PURE__ */ jsxRuntime.jsxs(Stack.Stack, { gap: 6, align: "center", children: [
|
|
339
|
+
header ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
340
|
+
Box.Box,
|
|
341
|
+
{
|
|
342
|
+
style: {
|
|
343
|
+
display: "flex",
|
|
344
|
+
alignItems: "center",
|
|
345
|
+
justifyContent: "center",
|
|
346
|
+
width: "100%"
|
|
347
|
+
},
|
|
348
|
+
children: header
|
|
349
|
+
}
|
|
350
|
+
) : null,
|
|
351
|
+
collapseToggle
|
|
352
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs(
|
|
353
|
+
Group.Group,
|
|
354
|
+
{
|
|
355
|
+
justify: "space-between",
|
|
356
|
+
wrap: "nowrap",
|
|
357
|
+
gap: "xs",
|
|
358
|
+
align: "center",
|
|
359
|
+
style: { minHeight: 32 },
|
|
360
|
+
onMouseEnter: () => setHeaderHovered(true),
|
|
361
|
+
onMouseLeave: () => setHeaderHovered(false),
|
|
362
|
+
onFocusCapture: () => setHeaderHovered(true),
|
|
363
|
+
onBlurCapture: (event) => {
|
|
364
|
+
if (!event.currentTarget.contains(event.relatedTarget)) {
|
|
365
|
+
setHeaderHovered(false);
|
|
366
|
+
}
|
|
367
|
+
},
|
|
368
|
+
children: [
|
|
369
|
+
header ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
370
|
+
Box.Box,
|
|
371
|
+
{
|
|
372
|
+
style: {
|
|
373
|
+
flex: 1,
|
|
374
|
+
minWidth: 0,
|
|
375
|
+
overflow: "hidden",
|
|
376
|
+
display: "flex",
|
|
377
|
+
alignItems: "center",
|
|
378
|
+
justifyContent: "flex-start"
|
|
379
|
+
},
|
|
380
|
+
children: header
|
|
381
|
+
}
|
|
382
|
+
) : null,
|
|
383
|
+
collapseToggle
|
|
384
|
+
]
|
|
385
|
+
}
|
|
386
|
+
) : null,
|
|
387
|
+
/* @__PURE__ */ jsxRuntime.jsx(Stack.Stack, { gap: 4, style: { flex: 1, minHeight: 0, overflow: "auto" }, children: resolvedSections.flatMap(
|
|
388
|
+
(section) => section.items.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
389
|
+
SideNavNode,
|
|
390
|
+
{
|
|
391
|
+
item,
|
|
392
|
+
color,
|
|
393
|
+
onItemClick,
|
|
394
|
+
collapsed: collapsedState
|
|
395
|
+
},
|
|
396
|
+
`${section.id}:${item.id}`
|
|
397
|
+
))
|
|
398
|
+
) }),
|
|
399
|
+
hasBottomChrome ? /* @__PURE__ */ jsxRuntime.jsx(Stack.Stack, { gap: "sm", style: { flexShrink: 0 }, children: footer ?? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
400
|
+
cta ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
401
|
+
Box.Box,
|
|
402
|
+
{
|
|
403
|
+
style: {
|
|
404
|
+
width: "100%",
|
|
405
|
+
display: "flex",
|
|
406
|
+
justifyContent: collapsedState ? "center" : "stretch"
|
|
407
|
+
},
|
|
408
|
+
children: cta
|
|
409
|
+
}
|
|
410
|
+
) : null,
|
|
411
|
+
cta && profile ? /* @__PURE__ */ jsxRuntime.jsx(Divider.Divider, { color: index.fairmintVars.colorBorderDefault }) : null,
|
|
412
|
+
profile ? /* @__PURE__ */ jsxRuntime.jsx(Box.Box, { style: { width: "100%" }, children: profile }) : null
|
|
413
|
+
] }) }) : null
|
|
414
|
+
]
|
|
76
415
|
}
|
|
77
416
|
);
|
|
78
417
|
}
|
|
418
|
+
exports.SIDE_NAV_COLLAPSED_WIDTH = SIDE_NAV_COLLAPSED_WIDTH;
|
|
419
|
+
exports.SIDE_NAV_WIDTH = SIDE_NAV_WIDTH;
|
|
79
420
|
exports.SideNav = SideNav;
|
|
421
|
+
exports.getSideNavItemLabelText = getSideNavItemLabelText;
|
|
422
|
+
exports.getSideNavLetterFallback = getSideNavLetterFallback;
|
|
80
423
|
//# sourceMappingURL=SideNav.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SideNav.cjs","sources":["../../../../src/components/layout/SideNav/SideNav.tsx"],"sourcesContent":["import { NavLink, Stack, Text } from \"@mantine/core\";\nimport { type ReactNode, useEffect, useState } from \"react\";\nimport { fairmintVars, radii } from \"../../../theme\";\n\nexport interface SideNavItem {\n /** Unique identifier for this nav item (e.g., route path or slug). */\n id: string;\n /** Display label. */\n label: ReactNode;\n /** Icon element. */\n icon?: ReactNode;\n /** Optional content rendered at the end of the item, such as a badge. */\n rightSection?: ReactNode;\n /** Whether this item is currently active. */\n active?: boolean;\n /** Whether the item is unavailable. */\n disabled?: boolean;\n /** Nested navigation items. */\n children?: SideNavItem[];\n /** Controlled expanded state for items with children. */\n open?: boolean;\n}\n\nexport interface SideNavSection {\n /** Unique identifier for the section. */\n id: string;\n /** Optional visible section heading. */\n label?: ReactNode;\n /** Navigation items in this section. */\n items: SideNavItem[];\n}\n\nexport interface SideNavProps {\n /** Navigation items to render when section headings are not needed. */\n items?: SideNavItem[];\n /** Grouped navigation items with optional section headings. */\n sections?: SideNavSection[];\n /** Callback when a nav item is clicked. Receives the item's `id`. */\n onItemClick: (id: string) => void;\n /** Active color. @default colors.primary */\n color?: string;\n /** Accessible label for the navigation landmark. @default \"Main navigation\" */\n ariaLabel?: string;\n /** Render a light right border when the parent shell does not already own the divider. @default false */\n withBorder?: boolean;\n}\n\nfunction hasActiveDescendant(item: SideNavItem): boolean {\n return item.children?.some((child) => child.active || hasActiveDescendant(child)) ?? false;\n}\n\ninterface SideNavNodeProps {\n color: string;\n item: SideNavItem;\n onItemClick: (id: string) => void;\n}\n\nfunction SideNavNode({ color, item, onItemClick }: SideNavNodeProps) {\n const containsActiveItem = hasActiveDescendant(item);\n const [opened, setOpened] = useState(item.open ?? containsActiveItem);\n\n useEffect(() => {\n if (item.open !== undefined) {\n setOpened(item.open);\n } else if (containsActiveItem) {\n setOpened(true);\n }\n }, [containsActiveItem, item.open]);\n\n const hasChildren = Boolean(item.children?.length);\n\n return (\n <NavLink\n label={item.label}\n leftSection={item.icon}\n rightSection={item.rightSection}\n active={item.active}\n disabled={item.disabled}\n color={color}\n variant=\"light\"\n childrenOffset={16}\n opened={hasChildren ? opened : undefined}\n onClick={\n hasChildren\n ? () => setOpened((currentOpened) => !currentOpened)\n : () => onItemClick(item.id)\n }\n style={{ borderRadius: radii.md }}\n styles={{\n root: {\n color: item.active ? fairmintVars.colorTextPrimary : fairmintVars.colorTextSecondary,\n fontWeight: item.active ? 500 : 400,\n },\n section: {\n color: item.active ? fairmintVars.colorBrandPrimary : fairmintVars.colorIconTertiary,\n },\n }}\n >\n {item.children?.map((child) => (\n <SideNavNode key={child.id} item={child} color={color} onItemClick={onItemClick} />\n ))}\n </NavLink>\n );\n}\n\nexport function SideNav({\n items = [],\n sections,\n onItemClick,\n color = \"indigo\",\n ariaLabel = \"Main navigation\",\n withBorder = false,\n}: SideNavProps) {\n const resolvedSections = sections ?? [{ id: \"main\", items }];\n\n return (\n <Stack\n component=\"nav\"\n gap=\"md\"\n h=\"100%\"\n p=\"md\"\n aria-label={ariaLabel}\n style={{\n borderRight: withBorder ? \"1px solid\" : undefined,\n borderRightColor: withBorder ? fairmintVars.colorBorderDefault : undefined,\n boxShadow: \"none\",\n }}\n >\n {resolvedSections.map((section) => (\n <Stack key={section.id} gap={4}>\n {section.label && (\n <Text size=\"xs\" fw={700} tt=\"uppercase\" c={fairmintVars.colorTextTertiary} px=\"sm\">\n {section.label}\n </Text>\n )}\n {section.items.map((item) => (\n <SideNavNode key={item.id} item={item} color={color} onItemClick={onItemClick} />\n ))}\n </Stack>\n ))}\n </Stack>\n );\n}\n"],"names":["useState","useEffect","jsx","NavLink","radii","fairmintVars","Stack","jsxs","Text"],"mappings":";;;;;;;;AA+CA,SAAS,oBAAoB,MAA4B;AACvD,SAAO,KAAK,UAAU,KAAK,CAAC,UAAU,MAAM,UAAU,oBAAoB,KAAK,CAAC,KAAK;AACvF;AAQA,SAAS,YAAY,EAAE,OAAO,MAAM,eAAiC;AACnE,QAAM,qBAAqB,oBAAoB,IAAI;AACnD,QAAM,CAAC,QAAQ,SAAS,IAAIA,MAAAA,SAAS,KAAK,QAAQ,kBAAkB;AAEpEC,QAAAA,UAAU,MAAM;AACd,QAAI,KAAK,SAAS,QAAW;AAC3B,gBAAU,KAAK,IAAI;AAAA,IACrB,WAAW,oBAAoB;AAC7B,gBAAU,IAAI;AAAA,IAChB;AAAA,EACF,GAAG,CAAC,oBAAoB,KAAK,IAAI,CAAC;AAElC,QAAM,cAAc,QAAQ,KAAK,UAAU,MAAM;AAEjD,SACEC,2BAAAA;AAAAA,IAACC,QAAAA;AAAAA,IAAA;AAAA,MACC,OAAO,KAAK;AAAA,MACZ,aAAa,KAAK;AAAA,MAClB,cAAc,KAAK;AAAA,MACnB,QAAQ,KAAK;AAAA,MACb,UAAU,KAAK;AAAA,MACf;AAAA,MACA,SAAQ;AAAA,MACR,gBAAgB;AAAA,MAChB,QAAQ,cAAc,SAAS;AAAA,MAC/B,SACE,cACI,MAAM,UAAU,CAAC,kBAAkB,CAAC,aAAa,IACjD,MAAM,YAAY,KAAK,EAAE;AAAA,MAE/B,OAAO,EAAE,cAAcC,MAAAA,MAAM,GAAA;AAAA,MAC7B,QAAQ;AAAA,QACN,MAAM;AAAA,UACJ,OAAO,KAAK,SAASC,MAAAA,aAAa,mBAAmBA,MAAAA,aAAa;AAAA,UAClE,YAAY,KAAK,SAAS,MAAM;AAAA,QAAA;AAAA,QAElC,SAAS;AAAA,UACP,OAAO,KAAK,SAASA,MAAAA,aAAa,oBAAoBA,MAAAA,aAAa;AAAA,QAAA;AAAA,MACrE;AAAA,MAGD,UAAA,KAAK,UAAU,IAAI,CAAC,UACnBH,2BAAAA,IAAC,aAAA,EAA2B,MAAM,OAAO,OAAc,YAAA,GAArC,MAAM,EAAyD,CAClF;AAAA,IAAA;AAAA,EAAA;AAGP;AAEO,SAAS,QAAQ;AAAA,EACtB,QAAQ,CAAA;AAAA,EACR;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,aAAa;AACf,GAAiB;AACf,QAAM,mBAAmB,YAAY,CAAC,EAAE,IAAI,QAAQ,OAAO;AAE3D,SACEA,2BAAAA;AAAAA,IAACI,MAAAA;AAAAA,IAAA;AAAA,MACC,WAAU;AAAA,MACV,KAAI;AAAA,MACJ,GAAE;AAAA,MACF,GAAE;AAAA,MACF,cAAY;AAAA,MACZ,OAAO;AAAA,QACL,aAAa,aAAa,cAAc;AAAA,QACxC,kBAAkB,aAAaD,mBAAa,qBAAqB;AAAA,QACjE,WAAW;AAAA,MAAA;AAAA,MAGZ,2BAAiB,IAAI,CAAC,YACrBE,gCAACD,MAAAA,OAAA,EAAuB,KAAK,GAC1B,UAAA;AAAA,QAAA,QAAQ,SACPJ,2BAAAA,IAACM,KAAAA,MAAA,EAAK,MAAK,MAAK,IAAI,KAAK,IAAG,aAAY,GAAGH,MAAAA,aAAa,mBAAmB,IAAG,MAC3E,kBAAQ,OACX;AAAA,QAED,QAAQ,MAAM,IAAI,CAAC,SAClBH,2BAAAA,IAAC,aAAA,EAA0B,MAAY,OAAc,eAAnC,KAAK,EAAwD,CAChF;AAAA,MAAA,EAAA,GARS,QAAQ,EASpB,CACD;AAAA,IAAA;AAAA,EAAA;AAGP;;"}
|
|
1
|
+
{"version":3,"file":"SideNav.cjs","sources":["../../../../src/components/layout/SideNav/SideNav.tsx"],"sourcesContent":["import {\n ActionIcon,\n Box,\n Divider,\n Group,\n Menu,\n NavLink,\n type NavLinkProps,\n Stack,\n Tooltip,\n UnstyledButton,\n} from \"@mantine/core\";\nimport { useUncontrolled } from \"@mantine/hooks\";\nimport { IconLayoutSidebarLeftCollapse, IconLayoutSidebarLeftExpand } from \"@tabler/icons-react\";\nimport { type ReactNode, useEffect, useState } from \"react\";\nimport { fairmintVars, radii } from \"../../../theme\";\n\n/** Expanded product side-nav width (Brand System shell metric). */\nexport const SIDE_NAV_WIDTH = 260;\n\n/**\n * Collapsed icon-rail width.\n * Proposed shell metric for Dylan / Brand System review — not yet in Brand System v3.4.\n */\nexport const SIDE_NAV_COLLAPSED_WIDTH = 80;\n\nexport interface SideNavItem {\n /** Unique identifier for this nav item (e.g., route path or slug). */\n id: string;\n /** Display label. */\n label: ReactNode;\n /**\n * Accessible name when `label` is not a plain string.\n * Also used for collapsed tooltips and letter-fallback derivation.\n */\n ariaLabel?: string;\n /** Icon element. */\n icon?: ReactNode;\n /** Optional content rendered at the end of the item, such as a badge. */\n rightSection?: ReactNode;\n /** Whether this item is currently active. */\n active?: boolean;\n /** Whether the item is unavailable. */\n disabled?: boolean;\n /** Nested navigation items. */\n children?: SideNavItem[];\n /** Controlled expanded state for items with children. */\n open?: boolean;\n}\n\nexport interface SideNavSection {\n /** Unique identifier for the section. */\n id: string;\n /** Navigation items in this section. */\n items: SideNavItem[];\n}\n\nexport interface SideNavProps {\n /** Navigation items to render when section headings are not needed. */\n items?: SideNavItem[];\n /** Grouped navigation items. Sections are data-only — Fairmint product nav does not render section titles. */\n sections?: SideNavSection[];\n /** Callback when a nav item is clicked. Receives the item's `id`. */\n onItemClick: (id: string) => void;\n /** Active color. @default colors.primary */\n color?: string;\n /** Accessible label for the navigation landmark. @default \"Main navigation\" */\n ariaLabel?: string;\n /** Render a light right border when the parent shell does not already own the divider. @default false */\n withBorder?: boolean;\n /**\n * Logo or other chrome above the nav list.\n * Consumers own expanded wordmark vs collapsed mark switching.\n */\n header?: ReactNode;\n /**\n * Primary nav CTA pinned above the profile (Investor \"Add Investment\", Studio \"New\").\n * Prefer this over stuffing the CTA into `items`.\n */\n cta?: ReactNode;\n /**\n * Account / profile control pinned to the bottom of the rail.\n * Product shells keep profile here — not in the scrollable destination list or AppHeader.\n */\n profile?: ReactNode;\n /**\n * Custom bottom chrome. When set, replaces the standard `cta` + divider + `profile` composition.\n */\n footer?: ReactNode;\n /** Controlled collapsed (icon-rail) state. */\n collapsed?: boolean;\n /** Uncontrolled initial collapsed state. @default false */\n defaultCollapsed?: boolean;\n /** Called when the user toggles collapse. */\n onCollapsedChange?: (collapsed: boolean) => void;\n /** When true, render a collapse/expand control beside the logo (visible on header hover). @default false */\n collapsible?: boolean;\n}\n\nfunction hasActiveDescendant(item: SideNavItem): boolean {\n return item.children?.some((child) => child.active || hasActiveDescendant(child)) ?? false;\n}\n\n/** Resolve a plain-text name for tooltips, aria-label, and letter fallback. */\nexport function getSideNavItemLabelText(item: Pick<SideNavItem, \"label\" | \"ariaLabel\">): string {\n if (item.ariaLabel) {\n return item.ariaLabel;\n }\n if (typeof item.label === \"string\") {\n return item.label;\n }\n if (typeof item.label === \"number\") {\n return String(item.label);\n }\n return \"Item\";\n}\n\n/** First letter/number monogram for icon-less items in the collapsed rail. */\nexport function getSideNavLetterFallback(labelText: string): string {\n const match = labelText.match(/[\\p{L}\\p{N}]/u);\n return match ? match[0].toUpperCase() : \"?\";\n}\n\nfunction LetterFallback({ letter, active = false }: { letter: string; active?: boolean }) {\n return (\n <Box\n component=\"span\"\n aria-hidden\n data-active={active || undefined}\n style={{\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n width: 20,\n height: 20,\n flexShrink: 0,\n fontSize: 12,\n fontWeight: active ? 700 : 600,\n lineHeight: 1,\n // Selected chrome lives on the parent control — glyph matches icon sizing.\n color: active ? fairmintVars.colorIconOnSurfaceSelected : \"inherit\",\n }}\n >\n {letter}\n </Box>\n );\n}\n\nfunction CollapseToggle({\n collapsed,\n onToggle,\n visible,\n}: {\n collapsed: boolean;\n onToggle: () => void;\n /** When false, hide visually but keep focusable for keyboard users via opacity/focus styles. */\n visible: boolean;\n}) {\n const label = collapsed ? \"Expand navigation\" : \"Collapse navigation\";\n const Icon = collapsed ? IconLayoutSidebarLeftExpand : IconLayoutSidebarLeftCollapse;\n\n const button = (\n <UnstyledButton\n data-sidenav-collapse\n aria-label={label}\n aria-expanded={!collapsed}\n onClick={onToggle}\n style={{\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n height: 32,\n flexShrink: 0,\n borderRadius: radii.md,\n color: fairmintVars.colorIconTertiary,\n opacity: visible ? 1 : 0,\n pointerEvents: visible ? \"auto\" : \"none\",\n transition: \"opacity 120ms ease\",\n // Keep a layout slot when expanded so the wordmark does not shift on hover.\n // Collapsed: always a fixed control below the logo.\n width: 32,\n overflow: \"hidden\",\n padding: 0,\n }}\n >\n <Icon size={20} stroke={1.5} aria-hidden />\n </UnstyledButton>\n );\n\n if (!collapsed) {\n return button;\n }\n\n return (\n <Tooltip\n label={label}\n position=\"right\"\n withArrow\n events={{ hover: true, focus: true, touch: true }}\n disabled={!visible}\n >\n {button}\n </Tooltip>\n );\n}\n\nfunction resolveLeftSection(\n item: SideNavItem,\n labelText: string,\n { requireIcon, active }: { requireIcon: boolean; active?: boolean },\n): ReactNode {\n if (item.icon) {\n return item.icon;\n }\n if (requireIcon) {\n return <LetterFallback letter={getSideNavLetterFallback(labelText)} active={Boolean(active)} />;\n }\n return undefined;\n}\n\n/** Collapsed rail control size — matches square CTA chrome. */\nconst COLLAPSED_ITEM_SIZE = 36;\n\nconst navLinkStyles = (active: boolean | undefined): NavLinkProps[\"styles\"] => ({\n root: {\n color: active ? fairmintVars.colorTextPrimary : fairmintVars.colorTextSecondary,\n fontWeight: active ? 500 : 400,\n borderRadius: radii.md,\n // Prefer Fairmint selected surface over Mantine defaults for active chrome.\n ...(active\n ? {\n \"--nl-bg\": fairmintVars.colorSurfaceSelected,\n \"--nl-hover\": fairmintVars.colorSurfaceSelected,\n \"--nl-color\": fairmintVars.colorIconOnSurfaceSelected,\n backgroundColor: fairmintVars.colorSurfaceSelected,\n }\n : {}),\n },\n section: {\n color: active ? fairmintVars.colorIconOnSurfaceSelected : fairmintVars.colorIconTertiary,\n },\n});\n\ninterface SideNavNodeProps {\n color: string;\n item: SideNavItem;\n onItemClick: (id: string) => void;\n collapsed: boolean;\n}\n\nfunction ExpandedSideNavNode({ color, item, onItemClick }: Omit<SideNavNodeProps, \"collapsed\">) {\n const containsActiveItem = hasActiveDescendant(item);\n const [opened, setOpened] = useState(item.open ?? containsActiveItem);\n const labelText = getSideNavItemLabelText(item);\n\n useEffect(() => {\n if (item.open !== undefined) {\n setOpened(item.open);\n } else if (containsActiveItem) {\n setOpened(true);\n }\n }, [containsActiveItem, item.open]);\n\n const hasChildren = Boolean(item.children?.length);\n\n return (\n <NavLink\n component=\"button\"\n type=\"button\"\n label={item.label}\n leftSection={resolveLeftSection(item, labelText, {\n requireIcon: false,\n active: item.active,\n })}\n rightSection={item.rightSection}\n active={item.active}\n disabled={item.disabled}\n color={color}\n variant=\"light\"\n childrenOffset={16}\n opened={hasChildren ? opened : undefined}\n aria-label={item.ariaLabel}\n onClick={\n hasChildren\n ? () => setOpened((currentOpened) => !currentOpened)\n : () => onItemClick(item.id)\n }\n styles={navLinkStyles(item.active)}\n >\n {item.children?.map((child) => (\n <ExpandedSideNavNode key={child.id} item={child} color={color} onItemClick={onItemClick} />\n ))}\n </NavLink>\n );\n}\n\n/** Flatten nested groups to leaves for the collapsed flyout (one actionable list). */\nfunction collectCollapsedMenuLeaves(items: SideNavItem[]): SideNavItem[] {\n return items.flatMap((item) =>\n item.children?.length ? collectCollapsedMenuLeaves(item.children) : [item],\n );\n}\n\nfunction CollapsedNestedMenuItems({\n items,\n onItemClick,\n color,\n}: {\n items: SideNavItem[];\n onItemClick: (id: string) => void;\n color: string;\n}) {\n return collectCollapsedMenuLeaves(items).map((child) => {\n const childLabel = getSideNavItemLabelText(child);\n const isActive = Boolean(child.active);\n return (\n <Menu.Item\n key={child.id}\n disabled={child.disabled}\n leftSection={resolveLeftSection(child, childLabel, {\n requireIcon: true,\n active: isActive,\n })}\n onClick={() => onItemClick(child.id)}\n aria-current={isActive ? \"page\" : undefined}\n data-active={isActive || undefined}\n color={isActive ? color : undefined}\n style={\n isActive\n ? {\n backgroundColor: fairmintVars.colorSurfaceSelected,\n color: fairmintVars.colorIconOnSurfaceSelected,\n fontWeight: 600,\n }\n : undefined\n }\n >\n {child.label}\n </Menu.Item>\n );\n });\n}\n\nfunction CollapsedSideNavNode({ color, item, onItemClick }: Omit<SideNavNodeProps, \"collapsed\">) {\n const labelText = getSideNavItemLabelText(item);\n const hasChildren = Boolean(item.children?.length);\n const isActive = Boolean(item.active || hasActiveDescendant(item));\n const leftSection = resolveLeftSection(item, labelText, {\n requireIcon: true,\n active: isActive,\n });\n\n // ActionIcon (not NavLink): NavLink.css forces `width: 100%` and asymmetric\n // horizontal padding, which makes selected/hover chrome a wide pill.\n // Selected fill uses Fairmint surface/selected — Mantine `light` is too dim in dark mode.\n const link = (\n <ActionIcon\n type=\"button\"\n variant=\"subtle\"\n color={color}\n size={COLLAPSED_ITEM_SIZE}\n radius=\"md\"\n disabled={item.disabled}\n aria-label={labelText}\n aria-current={isActive ? \"page\" : undefined}\n data-active={isActive || undefined}\n onClick={hasChildren ? undefined : () => onItemClick(item.id)}\n style={{\n width: COLLAPSED_ITEM_SIZE,\n height: COLLAPSED_ITEM_SIZE,\n minWidth: COLLAPSED_ITEM_SIZE,\n minHeight: COLLAPSED_ITEM_SIZE,\n ...(isActive\n ? {\n \"--ai-bg\": fairmintVars.colorSurfaceSelected,\n \"--ai-hover\": fairmintVars.colorSurfaceSelected,\n \"--ai-color\": fairmintVars.colorIconOnSurfaceSelected,\n \"--ai-hover-color\": fairmintVars.colorIconOnSurfaceSelected,\n backgroundColor: fairmintVars.colorSurfaceSelected,\n color: fairmintVars.colorIconOnSurfaceSelected,\n }\n : {\n color: fairmintVars.colorIconTertiary,\n }),\n }}\n >\n {leftSection}\n </ActionIcon>\n );\n\n const control = hasChildren ? (\n <Menu position=\"right-start\" offset={8} withinPortal>\n <Tooltip\n label={labelText}\n position=\"right\"\n withArrow\n events={{ hover: true, focus: true, touch: true }}\n >\n <Menu.Target>{link}</Menu.Target>\n </Tooltip>\n <Menu.Dropdown>\n <CollapsedNestedMenuItems\n items={item.children ?? []}\n onItemClick={onItemClick}\n color={color}\n />\n </Menu.Dropdown>\n </Menu>\n ) : (\n <Tooltip\n label={labelText}\n position=\"right\"\n withArrow\n events={{ hover: true, focus: true, touch: true }}\n >\n {link}\n </Tooltip>\n );\n\n // Uniform Stack child so Menu vs Tooltip wrappers cannot skew item gaps.\n return <Box style={{ width: \"100%\", display: \"flex\", justifyContent: \"center\" }}>{control}</Box>;\n}\n\nfunction SideNavNode(props: SideNavNodeProps) {\n if (props.collapsed) {\n return <CollapsedSideNavNode {...props} />;\n }\n return <ExpandedSideNavNode {...props} />;\n}\n\nexport function SideNav({\n items = [],\n sections,\n onItemClick,\n color = \"indigo\",\n ariaLabel = \"Main navigation\",\n withBorder = false,\n header,\n cta,\n profile,\n footer,\n collapsed,\n defaultCollapsed = false,\n onCollapsedChange,\n collapsible = false,\n}: SideNavProps) {\n const resolvedSections = sections ?? [{ id: \"main\", items }];\n const [collapsedState, setCollapsedState] = useUncontrolled({\n value: collapsed,\n defaultValue: defaultCollapsed,\n finalValue: false,\n onChange: onCollapsedChange,\n });\n\n const hasBottomChrome = Boolean(footer || cta || profile);\n const [headerHovered, setHeaderHovered] = useState(false);\n const showExpandedCollapseControl = headerHovered;\n const showHeaderChrome = Boolean(header) || collapsible;\n\n const collapseToggle = collapsible ? (\n <CollapseToggle\n collapsed={collapsedState}\n visible={collapsedState ? true : showExpandedCollapseControl}\n onToggle={() => setCollapsedState(!collapsedState)}\n />\n ) : null;\n\n return (\n <Stack\n component=\"nav\"\n gap=\"md\"\n h=\"100%\"\n p={collapsedState ? \"sm\" : \"md\"}\n aria-label={ariaLabel}\n data-collapsed={collapsedState || undefined}\n style={{\n borderRight: withBorder ? \"1px solid\" : undefined,\n borderRightColor: withBorder ? fairmintVars.colorBorderDefault : undefined,\n boxShadow: \"none\",\n }}\n >\n {showHeaderChrome ? (\n collapsedState ? (\n <Stack gap={6} align=\"center\">\n {header ? (\n <Box\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n width: \"100%\",\n }}\n >\n {header}\n </Box>\n ) : null}\n {collapseToggle}\n </Stack>\n ) : (\n <Group\n justify=\"space-between\"\n wrap=\"nowrap\"\n gap=\"xs\"\n align=\"center\"\n style={{ minHeight: 32 }}\n onMouseEnter={() => setHeaderHovered(true)}\n onMouseLeave={() => setHeaderHovered(false)}\n onFocusCapture={() => setHeaderHovered(true)}\n onBlurCapture={(event) => {\n if (!event.currentTarget.contains(event.relatedTarget as Node | null)) {\n setHeaderHovered(false);\n }\n }}\n >\n {header ? (\n <Box\n style={{\n flex: 1,\n minWidth: 0,\n overflow: \"hidden\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"flex-start\",\n }}\n >\n {header}\n </Box>\n ) : null}\n {collapseToggle}\n </Group>\n )\n ) : null}\n\n <Stack gap={4} style={{ flex: 1, minHeight: 0, overflow: \"auto\" }}>\n {resolvedSections.flatMap((section) =>\n section.items.map((item) => (\n <SideNavNode\n key={`${section.id}:${item.id}`}\n item={item}\n color={color}\n onItemClick={onItemClick}\n collapsed={collapsedState}\n />\n )),\n )}\n </Stack>\n\n {hasBottomChrome ? (\n <Stack gap=\"sm\" style={{ flexShrink: 0 }}>\n {footer ?? (\n <>\n {cta ? (\n <Box\n style={{\n width: \"100%\",\n display: \"flex\",\n justifyContent: collapsedState ? \"center\" : \"stretch\",\n }}\n >\n {cta}\n </Box>\n ) : null}\n {cta && profile ? <Divider color={fairmintVars.colorBorderDefault} /> : null}\n {profile ? <Box style={{ width: \"100%\" }}>{profile}</Box> : null}\n </>\n )}\n </Stack>\n ) : null}\n </Stack>\n );\n}\n"],"names":["jsx","Box","fairmintVars","IconLayoutSidebarLeftExpand","IconLayoutSidebarLeftCollapse","UnstyledButton","radii","Tooltip","useState","useEffect","NavLink","Menu","ActionIcon","jsxs","useUncontrolled","Stack","Group","Fragment","Divider"],"mappings":"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"}
|
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
|
+
/** Expanded product side-nav width (Brand System shell metric). */
|
|
3
|
+
export declare const SIDE_NAV_WIDTH = 260;
|
|
4
|
+
/**
|
|
5
|
+
* Collapsed icon-rail width.
|
|
6
|
+
* Proposed shell metric for Dylan / Brand System review — not yet in Brand System v3.4.
|
|
7
|
+
*/
|
|
8
|
+
export declare const SIDE_NAV_COLLAPSED_WIDTH = 80;
|
|
2
9
|
export interface SideNavItem {
|
|
3
10
|
/** Unique identifier for this nav item (e.g., route path or slug). */
|
|
4
11
|
id: string;
|
|
5
12
|
/** Display label. */
|
|
6
13
|
label: ReactNode;
|
|
14
|
+
/**
|
|
15
|
+
* Accessible name when `label` is not a plain string.
|
|
16
|
+
* Also used for collapsed tooltips and letter-fallback derivation.
|
|
17
|
+
*/
|
|
18
|
+
ariaLabel?: string;
|
|
7
19
|
/** Icon element. */
|
|
8
20
|
icon?: ReactNode;
|
|
9
21
|
/** Optional content rendered at the end of the item, such as a badge. */
|
|
@@ -20,15 +32,13 @@ export interface SideNavItem {
|
|
|
20
32
|
export interface SideNavSection {
|
|
21
33
|
/** Unique identifier for the section. */
|
|
22
34
|
id: string;
|
|
23
|
-
/** Optional visible section heading. */
|
|
24
|
-
label?: ReactNode;
|
|
25
35
|
/** Navigation items in this section. */
|
|
26
36
|
items: SideNavItem[];
|
|
27
37
|
}
|
|
28
38
|
export interface SideNavProps {
|
|
29
39
|
/** Navigation items to render when section headings are not needed. */
|
|
30
40
|
items?: SideNavItem[];
|
|
31
|
-
/** Grouped navigation items
|
|
41
|
+
/** Grouped navigation items. Sections are data-only — Fairmint product nav does not render section titles. */
|
|
32
42
|
sections?: SideNavSection[];
|
|
33
43
|
/** Callback when a nav item is clicked. Receives the item's `id`. */
|
|
34
44
|
onItemClick: (id: string) => void;
|
|
@@ -38,6 +48,37 @@ export interface SideNavProps {
|
|
|
38
48
|
ariaLabel?: string;
|
|
39
49
|
/** Render a light right border when the parent shell does not already own the divider. @default false */
|
|
40
50
|
withBorder?: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Logo or other chrome above the nav list.
|
|
53
|
+
* Consumers own expanded wordmark vs collapsed mark switching.
|
|
54
|
+
*/
|
|
55
|
+
header?: ReactNode;
|
|
56
|
+
/**
|
|
57
|
+
* Primary nav CTA pinned above the profile (Investor "Add Investment", Studio "New").
|
|
58
|
+
* Prefer this over stuffing the CTA into `items`.
|
|
59
|
+
*/
|
|
60
|
+
cta?: ReactNode;
|
|
61
|
+
/**
|
|
62
|
+
* Account / profile control pinned to the bottom of the rail.
|
|
63
|
+
* Product shells keep profile here — not in the scrollable destination list or AppHeader.
|
|
64
|
+
*/
|
|
65
|
+
profile?: ReactNode;
|
|
66
|
+
/**
|
|
67
|
+
* Custom bottom chrome. When set, replaces the standard `cta` + divider + `profile` composition.
|
|
68
|
+
*/
|
|
69
|
+
footer?: ReactNode;
|
|
70
|
+
/** Controlled collapsed (icon-rail) state. */
|
|
71
|
+
collapsed?: boolean;
|
|
72
|
+
/** Uncontrolled initial collapsed state. @default false */
|
|
73
|
+
defaultCollapsed?: boolean;
|
|
74
|
+
/** Called when the user toggles collapse. */
|
|
75
|
+
onCollapsedChange?: (collapsed: boolean) => void;
|
|
76
|
+
/** When true, render a collapse/expand control beside the logo (visible on header hover). @default false */
|
|
77
|
+
collapsible?: boolean;
|
|
41
78
|
}
|
|
42
|
-
|
|
79
|
+
/** Resolve a plain-text name for tooltips, aria-label, and letter fallback. */
|
|
80
|
+
export declare function getSideNavItemLabelText(item: Pick<SideNavItem, "label" | "ariaLabel">): string;
|
|
81
|
+
/** First letter/number monogram for icon-less items in the collapsed rail. */
|
|
82
|
+
export declare function getSideNavLetterFallback(labelText: string): string;
|
|
83
|
+
export declare function SideNav({ items, sections, onItemClick, color, ariaLabel, withBorder, header, cta, profile, footer, collapsed, defaultCollapsed, onCollapsedChange, collapsible, }: SideNavProps): import("react/jsx-runtime").JSX.Element;
|
|
43
84
|
//# sourceMappingURL=SideNav.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SideNav.d.ts","sourceRoot":"","sources":["../../../../src/components/layout/SideNav/SideNav.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"SideNav.d.ts","sourceRoot":"","sources":["../../../../src/components/layout/SideNav/SideNav.tsx"],"names":[],"mappings":"AAcA,OAAO,EAAE,KAAK,SAAS,EAAuB,MAAM,OAAO,CAAC;AAG5D,mEAAmE;AACnE,eAAO,MAAM,cAAc,MAAM,CAAC;AAElC;;;GAGG;AACH,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAE3C,MAAM,WAAW,WAAW;IAC1B,sEAAsE;IACtE,EAAE,EAAE,MAAM,CAAC;IACX,qBAAqB;IACrB,KAAK,EAAE,SAAS,CAAC;IACjB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oBAAoB;IACpB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,yEAAyE;IACzE,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB,6CAA6C;IAC7C,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,+BAA+B;IAC/B,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAC;IACzB,yDAAyD;IACzD,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,yCAAyC;IACzC,EAAE,EAAE,MAAM,CAAC;IACX,wCAAwC;IACxC,KAAK,EAAE,WAAW,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,uEAAuE;IACvE,KAAK,CAAC,EAAE,WAAW,EAAE,CAAC;IACtB,8GAA8G;IAC9G,QAAQ,CAAC,EAAE,cAAc,EAAE,CAAC;IAC5B,qEAAqE;IACrE,WAAW,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,4CAA4C;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+EAA+E;IAC/E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yGAAyG;IACzG,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB;;;OAGG;IACH,GAAG,CAAC,EAAE,SAAS,CAAC;IAChB;;;OAGG;IACH,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB;;OAEG;IACH,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,8CAA8C;IAC9C,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,2DAA2D;IAC3D,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,6CAA6C;IAC7C,iBAAiB,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,CAAC;IACjD,4GAA4G;IAC5G,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAMD,+EAA+E;AAC/E,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,OAAO,GAAG,WAAW,CAAC,GAAG,MAAM,CAW9F;AAED,8EAA8E;AAC9E,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAGlE;AAqTD,wBAAgB,OAAO,CAAC,EACtB,KAAU,EACV,QAAQ,EACR,WAAW,EACX,KAAgB,EAChB,SAA6B,EAC7B,UAAkB,EAClB,MAAM,EACN,GAAG,EACH,OAAO,EACP,MAAM,EACN,SAAS,EACT,gBAAwB,EACxB,iBAAiB,EACjB,WAAmB,GACpB,EAAE,YAAY,2CA6Hd"}
|