@fairmint/ui 0.3.14 → 0.3.16
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/components/generic/StatCard/StatCard.cjs +98 -16
- package/dist/components/generic/StatCard/StatCard.cjs.map +1 -1
- package/dist/components/generic/StatCard/StatCard.d.ts +27 -4
- package/dist/components/generic/StatCard/StatCard.d.ts.map +1 -1
- package/dist/components/generic/StatCard/StatCard.js +98 -16
- package/dist/components/generic/StatCard/StatCard.js.map +1 -1
- package/dist/components/layout/SideNav/SideNav.cjs +55 -7
- package/dist/components/layout/SideNav/SideNav.cjs.map +1 -1
- package/dist/components/layout/SideNav/SideNav.d.ts +27 -5
- package/dist/components/layout/SideNav/SideNav.d.ts.map +1 -1
- package/dist/components/layout/SideNav/SideNav.js +56 -8
- package/dist/components/layout/SideNav/SideNav.js.map +1 -1
- package/dist/components/primitives/Card/Card.cjs +3 -1
- package/dist/components/primitives/Card/Card.cjs.map +1 -1
- package/dist/components/primitives/Card/Card.d.ts.map +1 -1
- package/dist/components/primitives/Card/Card.js +3 -1
- package/dist/components/primitives/Card/Card.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/theme/index.cjs +24 -1
- package/dist/theme/index.cjs.map +1 -1
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/theme/index.js +24 -1
- package/dist/theme/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -3,42 +3,124 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
3
3
|
const jsxRuntime = require("react/jsx-runtime");
|
|
4
4
|
const index = require("../../../theme/index.cjs");
|
|
5
5
|
const colors = require("../../../theme/colors.cjs");
|
|
6
|
+
const ActionIcon = require("../../primitives/ActionIcon/ActionIcon.cjs");
|
|
7
|
+
const Avatar = require("../../primitives/Avatar/Avatar.cjs");
|
|
6
8
|
const Card = require("../../primitives/Card/Card.cjs");
|
|
7
9
|
const layout = require("../../primitives/layout.cjs");
|
|
10
|
+
const Tooltip = require("../../primitives/Tooltip/Tooltip.cjs");
|
|
8
11
|
const Group = require("../../../node_modules/@mantine/core/esm/components/Group/Group.cjs");
|
|
9
12
|
const Text = require("../../../node_modules/@mantine/core/esm/components/Text/Text.cjs");
|
|
13
|
+
function DefaultInfoIcon() {
|
|
14
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
15
|
+
"svg",
|
|
16
|
+
{
|
|
17
|
+
width: "14",
|
|
18
|
+
height: "14",
|
|
19
|
+
viewBox: "0 0 24 24",
|
|
20
|
+
fill: "none",
|
|
21
|
+
stroke: "currentColor",
|
|
22
|
+
strokeWidth: "2",
|
|
23
|
+
strokeLinecap: "round",
|
|
24
|
+
strokeLinejoin: "round",
|
|
25
|
+
"aria-hidden": "true",
|
|
26
|
+
children: [
|
|
27
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10" }),
|
|
28
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 16v-4" }),
|
|
29
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 8h.01" })
|
|
30
|
+
]
|
|
31
|
+
}
|
|
32
|
+
);
|
|
33
|
+
}
|
|
10
34
|
function StatCard({
|
|
11
35
|
label,
|
|
12
36
|
value,
|
|
13
37
|
icon,
|
|
14
|
-
color =
|
|
38
|
+
color = "neutral",
|
|
15
39
|
trend,
|
|
40
|
+
change,
|
|
41
|
+
infoTooltip,
|
|
42
|
+
infoIcon,
|
|
43
|
+
onValueClick,
|
|
44
|
+
iconVariant = "plain",
|
|
16
45
|
...rest
|
|
17
46
|
}) {
|
|
18
47
|
const isNeutral = color === "neutral";
|
|
48
|
+
const accent = isNeutral ? colors.colors.primary : color;
|
|
49
|
+
const valueInteractive = Boolean(onValueClick);
|
|
50
|
+
const handleValueKeyDown = (event) => {
|
|
51
|
+
if (!onValueClick) return;
|
|
52
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
53
|
+
event.preventDefault();
|
|
54
|
+
onValueClick();
|
|
55
|
+
}
|
|
56
|
+
};
|
|
19
57
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
20
58
|
Card.Card,
|
|
21
59
|
{
|
|
22
60
|
bg: isNeutral ? index.fairmintVars.colorBgSurface : `${color}.0`,
|
|
23
61
|
bd: isNeutral ? `1px solid ${index.fairmintVars.colorBorderDefault}` : `1px solid ${color}.2`,
|
|
62
|
+
shadow: "none",
|
|
24
63
|
...rest,
|
|
25
|
-
children: /* @__PURE__ */ jsxRuntime.jsxs(Group.Group, { justify: "space-between", align: "
|
|
26
|
-
/* @__PURE__ */ jsxRuntime.jsxs(layout.Stack, { gap: 4, children: [
|
|
27
|
-
/* @__PURE__ */ jsxRuntime.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
64
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(Group.Group, { justify: "space-between", align: "center", wrap: "nowrap", gap: "md", children: [
|
|
65
|
+
/* @__PURE__ */ jsxRuntime.jsxs(layout.Stack, { gap: 4, style: { minWidth: 0, flex: 1 }, children: [
|
|
66
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Group.Group, { gap: 4, align: "center", wrap: "nowrap", children: [
|
|
67
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
68
|
+
Text.Text,
|
|
69
|
+
{
|
|
70
|
+
size: "xs",
|
|
71
|
+
fw: 600,
|
|
72
|
+
tt: "uppercase",
|
|
73
|
+
c: isNeutral ? index.fairmintVars.colorTextTertiary : `${color}.6`,
|
|
74
|
+
style: { letterSpacing: "0.05em" },
|
|
75
|
+
truncate: true,
|
|
76
|
+
children: label
|
|
77
|
+
}
|
|
78
|
+
),
|
|
79
|
+
infoTooltip != null && infoTooltip !== "" && /* @__PURE__ */ jsxRuntime.jsx(Tooltip.Tooltip, { label: infoTooltip, withArrow: true, position: "top", multiline: true, maw: 280, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
80
|
+
ActionIcon.ActionIcon,
|
|
81
|
+
{
|
|
82
|
+
size: "sm",
|
|
83
|
+
variant: "transparent",
|
|
84
|
+
color: colors.colors.neutral,
|
|
85
|
+
"aria-label": `${label} information`,
|
|
86
|
+
children: infoIcon ?? /* @__PURE__ */ jsxRuntime.jsx(DefaultInfoIcon, {})
|
|
87
|
+
}
|
|
88
|
+
) })
|
|
89
|
+
] }),
|
|
90
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Group.Group, { gap: "xs", align: "center", wrap: "nowrap", children: [
|
|
91
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
92
|
+
Text.Text,
|
|
93
|
+
{
|
|
94
|
+
component: "span",
|
|
95
|
+
fw: 700,
|
|
96
|
+
size: "xl",
|
|
97
|
+
lh: 1,
|
|
98
|
+
truncate: true,
|
|
99
|
+
role: valueInteractive ? "button" : void 0,
|
|
100
|
+
tabIndex: valueInteractive ? 0 : void 0,
|
|
101
|
+
onClick: onValueClick,
|
|
102
|
+
onKeyDown: handleValueKeyDown,
|
|
103
|
+
style: {
|
|
104
|
+
cursor: valueInteractive ? "pointer" : void 0,
|
|
105
|
+
outlineOffset: 2
|
|
106
|
+
},
|
|
107
|
+
children: value
|
|
108
|
+
}
|
|
109
|
+
),
|
|
110
|
+
change
|
|
111
|
+
] }),
|
|
39
112
|
trend && /* @__PURE__ */ jsxRuntime.jsx(Text.Text, { size: "xs", c: index.fairmintVars.colorTextSecondary, children: trend })
|
|
40
113
|
] }),
|
|
41
|
-
icon && /* @__PURE__ */ jsxRuntime.jsx(
|
|
114
|
+
icon && iconVariant === "filled" && /* @__PURE__ */ jsxRuntime.jsx(Avatar.Avatar, { color: accent, radius: "md", size: 48, style: { flexShrink: 0 }, children: icon }),
|
|
115
|
+
icon && iconVariant === "plain" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
116
|
+
layout.Box,
|
|
117
|
+
{
|
|
118
|
+
c: isNeutral ? index.fairmintVars.colorTextSecondary : `${color}.5`,
|
|
119
|
+
fz: "1.5rem",
|
|
120
|
+
style: { flexShrink: 0 },
|
|
121
|
+
children: icon
|
|
122
|
+
}
|
|
123
|
+
)
|
|
42
124
|
] })
|
|
43
125
|
}
|
|
44
126
|
);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StatCard.cjs","sources":["../../../../src/components/generic/StatCard/StatCard.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { fairmintVars } from \"../../../theme\";\nimport { colors } from \"../../../theme/colors\";\nimport { Card, type CardProps } from \"../../primitives/Card/Card\";\nimport { Box, Group, Stack, Text } from \"../../primitives/layout\";\n\nexport interface StatCardProps extends Omit<CardProps, \"children\"> {\n /** Card label (e.g., \"
|
|
1
|
+
{"version":3,"file":"StatCard.cjs","sources":["../../../../src/components/generic/StatCard/StatCard.tsx"],"sourcesContent":["import type { KeyboardEvent, ReactNode } from \"react\";\nimport { fairmintVars } from \"../../../theme\";\nimport { colors } from \"../../../theme/colors\";\nimport { ActionIcon } from \"../../primitives/ActionIcon/ActionIcon\";\nimport { Avatar } from \"../../primitives/Avatar/Avatar\";\nimport { Card, type CardProps } from \"../../primitives/Card/Card\";\nimport { Box, Group, Stack, Text } from \"../../primitives/layout\";\nimport { Tooltip } from \"../../primitives/Tooltip/Tooltip\";\n\n/** Default info glyph — inline SVG so consumers need not import an icon library. */\nfunction DefaultInfoIcon() {\n return (\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M12 16v-4\" />\n <path d=\"M12 8h.01\" />\n </svg>\n );\n}\n\nexport interface StatCardProps extends Omit<CardProps, \"children\"> {\n /** Card label (e.g., \"Amount Raised\"). */\n label: string;\n /** Formatted value string (e.g., \"$125,000\"). */\n value: string;\n /** Optional icon element. */\n icon?: ReactNode;\n /**\n * Surface accent. Use `\"neutral\"` for the canonical bordered KPI tile\n * (Studio / Investor metric rows). Pass a Mantine color name for a tinted tile.\n * @default \"neutral\"\n */\n color?: string;\n /** Optional trend or supporting text below the value. */\n trend?: string;\n /** Optional change indicator rendered beside the value (e.g. a Badge). */\n change?: ReactNode;\n /** Tooltip content for the info control next to the label. */\n infoTooltip?: ReactNode;\n /** Override the info control icon. Defaults to a built-in info glyph. */\n infoIcon?: ReactNode;\n /** Called when the value is activated. */\n onValueClick?: () => void;\n /**\n * How the optional icon is rendered.\n * - `\"plain\"` — icon tint only\n * - `\"filled\"` — primary Avatar chip (Studio metric tiles)\n * @default \"plain\"\n */\n iconVariant?: \"plain\" | \"filled\";\n}\n\n/**\n * Shared KPI / metric tile for Studio and Investor dashboards.\n *\n * Default surface is a white card with a light border (no elevation). Apps own\n * value formatting, icon choice, and change-badge content; StatCard owns the\n * layout and visual contract.\n */\nexport function StatCard({\n label,\n value,\n icon,\n color = \"neutral\",\n trend,\n change,\n infoTooltip,\n infoIcon,\n onValueClick,\n iconVariant = \"plain\",\n ...rest\n}: StatCardProps) {\n const isNeutral = color === \"neutral\";\n const accent = isNeutral ? colors.primary : color;\n const valueInteractive = Boolean(onValueClick);\n\n const handleValueKeyDown = (event: KeyboardEvent<HTMLSpanElement>) => {\n if (!onValueClick) return;\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n onValueClick();\n }\n };\n\n return (\n <Card\n bg={isNeutral ? fairmintVars.colorBgSurface : `${color}.0`}\n bd={isNeutral ? `1px solid ${fairmintVars.colorBorderDefault}` : `1px solid ${color}.2`}\n shadow=\"none\"\n {...rest}\n >\n <Group justify=\"space-between\" align=\"center\" wrap=\"nowrap\" gap=\"md\">\n <Stack gap={4} style={{ minWidth: 0, flex: 1 }}>\n <Group gap={4} align=\"center\" wrap=\"nowrap\">\n <Text\n size=\"xs\"\n fw={600}\n tt=\"uppercase\"\n c={isNeutral ? fairmintVars.colorTextTertiary : `${color}.6`}\n style={{ letterSpacing: \"0.05em\" }}\n truncate\n >\n {label}\n </Text>\n {infoTooltip != null && infoTooltip !== \"\" && (\n <Tooltip label={infoTooltip} withArrow position=\"top\" multiline maw={280}>\n <ActionIcon\n size=\"sm\"\n variant=\"transparent\"\n color={colors.neutral}\n aria-label={`${label} information`}\n >\n {infoIcon ?? <DefaultInfoIcon />}\n </ActionIcon>\n </Tooltip>\n )}\n </Group>\n <Group gap=\"xs\" align=\"center\" wrap=\"nowrap\">\n <Text\n component=\"span\"\n fw={700}\n size=\"xl\"\n lh={1}\n truncate\n role={valueInteractive ? \"button\" : undefined}\n tabIndex={valueInteractive ? 0 : undefined}\n onClick={onValueClick}\n onKeyDown={handleValueKeyDown}\n style={{\n cursor: valueInteractive ? \"pointer\" : undefined,\n outlineOffset: 2,\n }}\n >\n {value}\n </Text>\n {change}\n </Group>\n {trend && (\n <Text size=\"xs\" c={fairmintVars.colorTextSecondary}>\n {trend}\n </Text>\n )}\n </Stack>\n {icon && iconVariant === \"filled\" && (\n <Avatar color={accent} radius=\"md\" size={48} style={{ flexShrink: 0 }}>\n {icon}\n </Avatar>\n )}\n {icon && iconVariant === \"plain\" && (\n <Box\n c={isNeutral ? fairmintVars.colorTextSecondary : `${color}.5`}\n fz=\"1.5rem\"\n style={{ flexShrink: 0 }}\n >\n {icon}\n </Box>\n )}\n </Group>\n </Card>\n );\n}\n"],"names":["jsxs","jsx","colors","Card","fairmintVars","Group","Stack","Text","Tooltip","ActionIcon","Avatar","Box"],"mappings":"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"}
|
|
@@ -1,19 +1,42 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
2
|
import { CardProps } from '../../primitives/Card/Card';
|
|
3
3
|
export interface StatCardProps extends Omit<CardProps, "children"> {
|
|
4
|
-
/** Card label (e.g., "
|
|
4
|
+
/** Card label (e.g., "Amount Raised"). */
|
|
5
5
|
label: string;
|
|
6
6
|
/** Formatted value string (e.g., "$125,000"). */
|
|
7
7
|
value: string;
|
|
8
8
|
/** Optional icon element. */
|
|
9
9
|
icon?: ReactNode;
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
11
|
+
* Surface accent. Use `"neutral"` for the canonical bordered KPI tile
|
|
12
|
+
* (Studio / Investor metric rows). Pass a Mantine color name for a tinted tile.
|
|
13
|
+
* @default "neutral"
|
|
13
14
|
*/
|
|
14
15
|
color?: string;
|
|
15
16
|
/** Optional trend or supporting text below the value. */
|
|
16
17
|
trend?: string;
|
|
18
|
+
/** Optional change indicator rendered beside the value (e.g. a Badge). */
|
|
19
|
+
change?: ReactNode;
|
|
20
|
+
/** Tooltip content for the info control next to the label. */
|
|
21
|
+
infoTooltip?: ReactNode;
|
|
22
|
+
/** Override the info control icon. Defaults to a built-in info glyph. */
|
|
23
|
+
infoIcon?: ReactNode;
|
|
24
|
+
/** Called when the value is activated. */
|
|
25
|
+
onValueClick?: () => void;
|
|
26
|
+
/**
|
|
27
|
+
* How the optional icon is rendered.
|
|
28
|
+
* - `"plain"` — icon tint only
|
|
29
|
+
* - `"filled"` — primary Avatar chip (Studio metric tiles)
|
|
30
|
+
* @default "plain"
|
|
31
|
+
*/
|
|
32
|
+
iconVariant?: "plain" | "filled";
|
|
17
33
|
}
|
|
18
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Shared KPI / metric tile for Studio and Investor dashboards.
|
|
36
|
+
*
|
|
37
|
+
* Default surface is a white card with a light border (no elevation). Apps own
|
|
38
|
+
* value formatting, icon choice, and change-badge content; StatCard owns the
|
|
39
|
+
* layout and visual contract.
|
|
40
|
+
*/
|
|
41
|
+
export declare function StatCard({ label, value, icon, color, trend, change, infoTooltip, infoIcon, onValueClick, iconVariant, ...rest }: StatCardProps): import("react/jsx-runtime").JSX.Element;
|
|
19
42
|
//# sourceMappingURL=StatCard.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StatCard.d.ts","sourceRoot":"","sources":["../../../../src/components/generic/StatCard/StatCard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"StatCard.d.ts","sourceRoot":"","sources":["../../../../src/components/generic/StatCard/StatCard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,SAAS,EAAE,MAAM,OAAO,CAAC;AAKtD,OAAO,EAAQ,KAAK,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAyBlE,MAAM,WAAW,aAAc,SAAQ,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC;IAChE,0CAA0C;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,iDAAiD;IACjD,KAAK,EAAE,MAAM,CAAC;IACd,6BAA6B;IAC7B,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0EAA0E;IAC1E,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,8DAA8D;IAC9D,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,0CAA0C;IAC1C,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B;;;;;OAKG;IACH,WAAW,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;CAClC;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,EACvB,KAAK,EACL,KAAK,EACL,IAAI,EACJ,KAAiB,EACjB,KAAK,EACL,MAAM,EACN,WAAW,EACX,QAAQ,EACR,YAAY,EACZ,WAAqB,EACrB,GAAG,IAAI,EACR,EAAE,aAAa,2CAyFf"}
|
|
@@ -1,42 +1,124 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { fairmintVars } from "../../../theme/index.js";
|
|
3
3
|
import { colors } from "../../../theme/colors.js";
|
|
4
|
+
import { ActionIcon } from "../../primitives/ActionIcon/ActionIcon.js";
|
|
5
|
+
import { Avatar } from "../../primitives/Avatar/Avatar.js";
|
|
4
6
|
import { Card } from "../../primitives/Card/Card.js";
|
|
5
7
|
import { Stack, Box } from "../../primitives/layout.js";
|
|
8
|
+
import { Tooltip } from "../../primitives/Tooltip/Tooltip.js";
|
|
6
9
|
import { Group } from "../../../node_modules/@mantine/core/esm/components/Group/Group.js";
|
|
7
10
|
import { Text } from "../../../node_modules/@mantine/core/esm/components/Text/Text.js";
|
|
11
|
+
function DefaultInfoIcon() {
|
|
12
|
+
return /* @__PURE__ */ jsxs(
|
|
13
|
+
"svg",
|
|
14
|
+
{
|
|
15
|
+
width: "14",
|
|
16
|
+
height: "14",
|
|
17
|
+
viewBox: "0 0 24 24",
|
|
18
|
+
fill: "none",
|
|
19
|
+
stroke: "currentColor",
|
|
20
|
+
strokeWidth: "2",
|
|
21
|
+
strokeLinecap: "round",
|
|
22
|
+
strokeLinejoin: "round",
|
|
23
|
+
"aria-hidden": "true",
|
|
24
|
+
children: [
|
|
25
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
|
|
26
|
+
/* @__PURE__ */ jsx("path", { d: "M12 16v-4" }),
|
|
27
|
+
/* @__PURE__ */ jsx("path", { d: "M12 8h.01" })
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
);
|
|
31
|
+
}
|
|
8
32
|
function StatCard({
|
|
9
33
|
label,
|
|
10
34
|
value,
|
|
11
35
|
icon,
|
|
12
|
-
color =
|
|
36
|
+
color = "neutral",
|
|
13
37
|
trend,
|
|
38
|
+
change,
|
|
39
|
+
infoTooltip,
|
|
40
|
+
infoIcon,
|
|
41
|
+
onValueClick,
|
|
42
|
+
iconVariant = "plain",
|
|
14
43
|
...rest
|
|
15
44
|
}) {
|
|
16
45
|
const isNeutral = color === "neutral";
|
|
46
|
+
const accent = isNeutral ? colors.primary : color;
|
|
47
|
+
const valueInteractive = Boolean(onValueClick);
|
|
48
|
+
const handleValueKeyDown = (event) => {
|
|
49
|
+
if (!onValueClick) return;
|
|
50
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
51
|
+
event.preventDefault();
|
|
52
|
+
onValueClick();
|
|
53
|
+
}
|
|
54
|
+
};
|
|
17
55
|
return /* @__PURE__ */ jsx(
|
|
18
56
|
Card,
|
|
19
57
|
{
|
|
20
58
|
bg: isNeutral ? fairmintVars.colorBgSurface : `${color}.0`,
|
|
21
59
|
bd: isNeutral ? `1px solid ${fairmintVars.colorBorderDefault}` : `1px solid ${color}.2`,
|
|
60
|
+
shadow: "none",
|
|
22
61
|
...rest,
|
|
23
|
-
children: /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "
|
|
24
|
-
/* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
|
|
25
|
-
/* @__PURE__ */
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
62
|
+
children: /* @__PURE__ */ jsxs(Group, { justify: "space-between", align: "center", wrap: "nowrap", gap: "md", children: [
|
|
63
|
+
/* @__PURE__ */ jsxs(Stack, { gap: 4, style: { minWidth: 0, flex: 1 }, children: [
|
|
64
|
+
/* @__PURE__ */ jsxs(Group, { gap: 4, align: "center", wrap: "nowrap", children: [
|
|
65
|
+
/* @__PURE__ */ jsx(
|
|
66
|
+
Text,
|
|
67
|
+
{
|
|
68
|
+
size: "xs",
|
|
69
|
+
fw: 600,
|
|
70
|
+
tt: "uppercase",
|
|
71
|
+
c: isNeutral ? fairmintVars.colorTextTertiary : `${color}.6`,
|
|
72
|
+
style: { letterSpacing: "0.05em" },
|
|
73
|
+
truncate: true,
|
|
74
|
+
children: label
|
|
75
|
+
}
|
|
76
|
+
),
|
|
77
|
+
infoTooltip != null && infoTooltip !== "" && /* @__PURE__ */ jsx(Tooltip, { label: infoTooltip, withArrow: true, position: "top", multiline: true, maw: 280, children: /* @__PURE__ */ jsx(
|
|
78
|
+
ActionIcon,
|
|
79
|
+
{
|
|
80
|
+
size: "sm",
|
|
81
|
+
variant: "transparent",
|
|
82
|
+
color: colors.neutral,
|
|
83
|
+
"aria-label": `${label} information`,
|
|
84
|
+
children: infoIcon ?? /* @__PURE__ */ jsx(DefaultInfoIcon, {})
|
|
85
|
+
}
|
|
86
|
+
) })
|
|
87
|
+
] }),
|
|
88
|
+
/* @__PURE__ */ jsxs(Group, { gap: "xs", align: "center", wrap: "nowrap", children: [
|
|
89
|
+
/* @__PURE__ */ jsx(
|
|
90
|
+
Text,
|
|
91
|
+
{
|
|
92
|
+
component: "span",
|
|
93
|
+
fw: 700,
|
|
94
|
+
size: "xl",
|
|
95
|
+
lh: 1,
|
|
96
|
+
truncate: true,
|
|
97
|
+
role: valueInteractive ? "button" : void 0,
|
|
98
|
+
tabIndex: valueInteractive ? 0 : void 0,
|
|
99
|
+
onClick: onValueClick,
|
|
100
|
+
onKeyDown: handleValueKeyDown,
|
|
101
|
+
style: {
|
|
102
|
+
cursor: valueInteractive ? "pointer" : void 0,
|
|
103
|
+
outlineOffset: 2
|
|
104
|
+
},
|
|
105
|
+
children: value
|
|
106
|
+
}
|
|
107
|
+
),
|
|
108
|
+
change
|
|
109
|
+
] }),
|
|
37
110
|
trend && /* @__PURE__ */ jsx(Text, { size: "xs", c: fairmintVars.colorTextSecondary, children: trend })
|
|
38
111
|
] }),
|
|
39
|
-
icon && /* @__PURE__ */ jsx(
|
|
112
|
+
icon && iconVariant === "filled" && /* @__PURE__ */ jsx(Avatar, { color: accent, radius: "md", size: 48, style: { flexShrink: 0 }, children: icon }),
|
|
113
|
+
icon && iconVariant === "plain" && /* @__PURE__ */ jsx(
|
|
114
|
+
Box,
|
|
115
|
+
{
|
|
116
|
+
c: isNeutral ? fairmintVars.colorTextSecondary : `${color}.5`,
|
|
117
|
+
fz: "1.5rem",
|
|
118
|
+
style: { flexShrink: 0 },
|
|
119
|
+
children: icon
|
|
120
|
+
}
|
|
121
|
+
)
|
|
40
122
|
] })
|
|
41
123
|
}
|
|
42
124
|
);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StatCard.js","sources":["../../../../src/components/generic/StatCard/StatCard.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { fairmintVars } from \"../../../theme\";\nimport { colors } from \"../../../theme/colors\";\nimport { Card, type CardProps } from \"../../primitives/Card/Card\";\nimport { Box, Group, Stack, Text } from \"../../primitives/layout\";\n\nexport interface StatCardProps extends Omit<CardProps, \"children\"> {\n /** Card label (e.g., \"
|
|
1
|
+
{"version":3,"file":"StatCard.js","sources":["../../../../src/components/generic/StatCard/StatCard.tsx"],"sourcesContent":["import type { KeyboardEvent, ReactNode } from \"react\";\nimport { fairmintVars } from \"../../../theme\";\nimport { colors } from \"../../../theme/colors\";\nimport { ActionIcon } from \"../../primitives/ActionIcon/ActionIcon\";\nimport { Avatar } from \"../../primitives/Avatar/Avatar\";\nimport { Card, type CardProps } from \"../../primitives/Card/Card\";\nimport { Box, Group, Stack, Text } from \"../../primitives/layout\";\nimport { Tooltip } from \"../../primitives/Tooltip/Tooltip\";\n\n/** Default info glyph — inline SVG so consumers need not import an icon library. */\nfunction DefaultInfoIcon() {\n return (\n <svg\n width=\"14\"\n height=\"14\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n >\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"M12 16v-4\" />\n <path d=\"M12 8h.01\" />\n </svg>\n );\n}\n\nexport interface StatCardProps extends Omit<CardProps, \"children\"> {\n /** Card label (e.g., \"Amount Raised\"). */\n label: string;\n /** Formatted value string (e.g., \"$125,000\"). */\n value: string;\n /** Optional icon element. */\n icon?: ReactNode;\n /**\n * Surface accent. Use `\"neutral\"` for the canonical bordered KPI tile\n * (Studio / Investor metric rows). Pass a Mantine color name for a tinted tile.\n * @default \"neutral\"\n */\n color?: string;\n /** Optional trend or supporting text below the value. */\n trend?: string;\n /** Optional change indicator rendered beside the value (e.g. a Badge). */\n change?: ReactNode;\n /** Tooltip content for the info control next to the label. */\n infoTooltip?: ReactNode;\n /** Override the info control icon. Defaults to a built-in info glyph. */\n infoIcon?: ReactNode;\n /** Called when the value is activated. */\n onValueClick?: () => void;\n /**\n * How the optional icon is rendered.\n * - `\"plain\"` — icon tint only\n * - `\"filled\"` — primary Avatar chip (Studio metric tiles)\n * @default \"plain\"\n */\n iconVariant?: \"plain\" | \"filled\";\n}\n\n/**\n * Shared KPI / metric tile for Studio and Investor dashboards.\n *\n * Default surface is a white card with a light border (no elevation). Apps own\n * value formatting, icon choice, and change-badge content; StatCard owns the\n * layout and visual contract.\n */\nexport function StatCard({\n label,\n value,\n icon,\n color = \"neutral\",\n trend,\n change,\n infoTooltip,\n infoIcon,\n onValueClick,\n iconVariant = \"plain\",\n ...rest\n}: StatCardProps) {\n const isNeutral = color === \"neutral\";\n const accent = isNeutral ? colors.primary : color;\n const valueInteractive = Boolean(onValueClick);\n\n const handleValueKeyDown = (event: KeyboardEvent<HTMLSpanElement>) => {\n if (!onValueClick) return;\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n onValueClick();\n }\n };\n\n return (\n <Card\n bg={isNeutral ? fairmintVars.colorBgSurface : `${color}.0`}\n bd={isNeutral ? `1px solid ${fairmintVars.colorBorderDefault}` : `1px solid ${color}.2`}\n shadow=\"none\"\n {...rest}\n >\n <Group justify=\"space-between\" align=\"center\" wrap=\"nowrap\" gap=\"md\">\n <Stack gap={4} style={{ minWidth: 0, flex: 1 }}>\n <Group gap={4} align=\"center\" wrap=\"nowrap\">\n <Text\n size=\"xs\"\n fw={600}\n tt=\"uppercase\"\n c={isNeutral ? fairmintVars.colorTextTertiary : `${color}.6`}\n style={{ letterSpacing: \"0.05em\" }}\n truncate\n >\n {label}\n </Text>\n {infoTooltip != null && infoTooltip !== \"\" && (\n <Tooltip label={infoTooltip} withArrow position=\"top\" multiline maw={280}>\n <ActionIcon\n size=\"sm\"\n variant=\"transparent\"\n color={colors.neutral}\n aria-label={`${label} information`}\n >\n {infoIcon ?? <DefaultInfoIcon />}\n </ActionIcon>\n </Tooltip>\n )}\n </Group>\n <Group gap=\"xs\" align=\"center\" wrap=\"nowrap\">\n <Text\n component=\"span\"\n fw={700}\n size=\"xl\"\n lh={1}\n truncate\n role={valueInteractive ? \"button\" : undefined}\n tabIndex={valueInteractive ? 0 : undefined}\n onClick={onValueClick}\n onKeyDown={handleValueKeyDown}\n style={{\n cursor: valueInteractive ? \"pointer\" : undefined,\n outlineOffset: 2,\n }}\n >\n {value}\n </Text>\n {change}\n </Group>\n {trend && (\n <Text size=\"xs\" c={fairmintVars.colorTextSecondary}>\n {trend}\n </Text>\n )}\n </Stack>\n {icon && iconVariant === \"filled\" && (\n <Avatar color={accent} radius=\"md\" size={48} style={{ flexShrink: 0 }}>\n {icon}\n </Avatar>\n )}\n {icon && iconVariant === \"plain\" && (\n <Box\n c={isNeutral ? fairmintVars.colorTextSecondary : `${color}.5`}\n fz=\"1.5rem\"\n style={{ flexShrink: 0 }}\n >\n {icon}\n </Box>\n )}\n </Group>\n </Card>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,19 +1,38 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const jsxRuntime = require("react/jsx-runtime");
|
|
4
|
+
const React = require("react");
|
|
4
5
|
const index = require("../../../theme/index.cjs");
|
|
5
6
|
const Stack = require("../../../node_modules/@mantine/core/esm/components/Stack/Stack.cjs");
|
|
7
|
+
const Text = require("../../../node_modules/@mantine/core/esm/components/Text/Text.cjs");
|
|
6
8
|
const NavLink = require("../../../node_modules/@mantine/core/esm/components/NavLink/NavLink.cjs");
|
|
7
|
-
function
|
|
8
|
-
return
|
|
9
|
+
function hasActiveDescendant(item) {
|
|
10
|
+
return item.children?.some((child) => child.active || hasActiveDescendant(child)) ?? false;
|
|
11
|
+
}
|
|
12
|
+
function SideNavNode({ color, item, onItemClick }) {
|
|
13
|
+
const containsActiveItem = hasActiveDescendant(item);
|
|
14
|
+
const [opened, setOpened] = React.useState(item.open ?? containsActiveItem);
|
|
15
|
+
React.useEffect(() => {
|
|
16
|
+
if (item.open !== void 0) {
|
|
17
|
+
setOpened(item.open);
|
|
18
|
+
} else if (containsActiveItem) {
|
|
19
|
+
setOpened(true);
|
|
20
|
+
}
|
|
21
|
+
}, [containsActiveItem, item.open]);
|
|
22
|
+
const hasChildren = Boolean(item.children?.length);
|
|
23
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
9
24
|
NavLink.NavLink,
|
|
10
25
|
{
|
|
11
26
|
label: item.label,
|
|
12
27
|
leftSection: item.icon,
|
|
28
|
+
rightSection: item.rightSection,
|
|
13
29
|
active: item.active,
|
|
14
|
-
|
|
30
|
+
disabled: item.disabled,
|
|
15
31
|
color,
|
|
16
32
|
variant: "light",
|
|
33
|
+
childrenOffset: 16,
|
|
34
|
+
opened: hasChildren ? opened : void 0,
|
|
35
|
+
onClick: hasChildren ? () => setOpened((currentOpened) => !currentOpened) : () => onItemClick(item.id),
|
|
17
36
|
style: { borderRadius: index.radii.md },
|
|
18
37
|
styles: {
|
|
19
38
|
root: {
|
|
@@ -23,10 +42,39 @@ function SideNav({ items, onItemClick, color = "indigo" }) {
|
|
|
23
42
|
section: {
|
|
24
43
|
color: item.active ? index.fairmintVars.colorBrandPrimary : index.fairmintVars.colorIconTertiary
|
|
25
44
|
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
)
|
|
45
|
+
},
|
|
46
|
+
children: item.children?.map((child) => /* @__PURE__ */ jsxRuntime.jsx(SideNavNode, { item: child, color, onItemClick }, child.id))
|
|
47
|
+
}
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
function SideNav({
|
|
51
|
+
items = [],
|
|
52
|
+
sections,
|
|
53
|
+
onItemClick,
|
|
54
|
+
color = "indigo",
|
|
55
|
+
ariaLabel = "Main navigation",
|
|
56
|
+
withBorder = false
|
|
57
|
+
}) {
|
|
58
|
+
const resolvedSections = sections ?? [{ id: "main", items }];
|
|
59
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
60
|
+
Stack.Stack,
|
|
61
|
+
{
|
|
62
|
+
component: "nav",
|
|
63
|
+
gap: "md",
|
|
64
|
+
h: "100%",
|
|
65
|
+
p: "md",
|
|
66
|
+
"aria-label": ariaLabel,
|
|
67
|
+
style: {
|
|
68
|
+
borderRight: withBorder ? "1px solid" : void 0,
|
|
69
|
+
borderRightColor: withBorder ? index.fairmintVars.colorBorderDefault : void 0,
|
|
70
|
+
boxShadow: "none"
|
|
71
|
+
},
|
|
72
|
+
children: resolvedSections.map((section) => /* @__PURE__ */ jsxRuntime.jsxs(Stack.Stack, { gap: 4, children: [
|
|
73
|
+
section.label && /* @__PURE__ */ jsxRuntime.jsx(Text.Text, { size: "xs", fw: 700, tt: "uppercase", c: index.fairmintVars.colorTextTertiary, px: "sm", children: section.label }),
|
|
74
|
+
section.items.map((item) => /* @__PURE__ */ jsxRuntime.jsx(SideNavNode, { item, color, onItemClick }, item.id))
|
|
75
|
+
] }, section.id))
|
|
76
|
+
}
|
|
77
|
+
);
|
|
30
78
|
}
|
|
31
79
|
exports.SideNav = SideNav;
|
|
32
80
|
//# sourceMappingURL=SideNav.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SideNav.cjs","sources":["../../../../src/components/layout/SideNav/SideNav.tsx"],"sourcesContent":["import { NavLink, Stack } from \"@mantine/core\";\nimport type
|
|
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;;"}
|
|
@@ -3,19 +3,41 @@ export interface SideNavItem {
|
|
|
3
3
|
/** Unique identifier for this nav item (e.g., route path or slug). */
|
|
4
4
|
id: string;
|
|
5
5
|
/** Display label. */
|
|
6
|
-
label:
|
|
6
|
+
label: ReactNode;
|
|
7
7
|
/** Icon element. */
|
|
8
8
|
icon?: ReactNode;
|
|
9
|
+
/** Optional content rendered at the end of the item, such as a badge. */
|
|
10
|
+
rightSection?: ReactNode;
|
|
9
11
|
/** Whether this item is currently active. */
|
|
10
12
|
active?: boolean;
|
|
13
|
+
/** Whether the item is unavailable. */
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
/** Nested navigation items. */
|
|
16
|
+
children?: SideNavItem[];
|
|
17
|
+
/** Controlled expanded state for items with children. */
|
|
18
|
+
open?: boolean;
|
|
11
19
|
}
|
|
12
|
-
export interface
|
|
13
|
-
/**
|
|
20
|
+
export interface SideNavSection {
|
|
21
|
+
/** Unique identifier for the section. */
|
|
22
|
+
id: string;
|
|
23
|
+
/** Optional visible section heading. */
|
|
24
|
+
label?: ReactNode;
|
|
25
|
+
/** Navigation items in this section. */
|
|
14
26
|
items: SideNavItem[];
|
|
27
|
+
}
|
|
28
|
+
export interface SideNavProps {
|
|
29
|
+
/** Navigation items to render when section headings are not needed. */
|
|
30
|
+
items?: SideNavItem[];
|
|
31
|
+
/** Grouped navigation items with optional section headings. */
|
|
32
|
+
sections?: SideNavSection[];
|
|
15
33
|
/** Callback when a nav item is clicked. Receives the item's `id`. */
|
|
16
34
|
onItemClick: (id: string) => void;
|
|
17
|
-
/** Active color. @default
|
|
35
|
+
/** Active color. @default colors.primary */
|
|
18
36
|
color?: string;
|
|
37
|
+
/** Accessible label for the navigation landmark. @default "Main navigation" */
|
|
38
|
+
ariaLabel?: string;
|
|
39
|
+
/** Render a light right border when the parent shell does not already own the divider. @default false */
|
|
40
|
+
withBorder?: boolean;
|
|
19
41
|
}
|
|
20
|
-
export declare function SideNav({ items, onItemClick, color }: SideNavProps): import("react/jsx-runtime").JSX.Element;
|
|
42
|
+
export declare function SideNav({ items, sections, onItemClick, color, ariaLabel, withBorder, }: SideNavProps): import("react/jsx-runtime").JSX.Element;
|
|
21
43
|
//# sourceMappingURL=SideNav.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SideNav.d.ts","sourceRoot":"","sources":["../../../../src/components/layout/SideNav/SideNav.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,
|
|
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,MAAM,WAAW,WAAW;IAC1B,sEAAsE;IACtE,EAAE,EAAE,MAAM,CAAC;IACX,qBAAqB;IACrB,KAAK,EAAE,SAAS,CAAC;IACjB,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,CAAC,EAAE,SAAS,CAAC;IAClB,wCAAwC;IACxC,KAAK,EAAE,WAAW,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,uEAAuE;IACvE,KAAK,CAAC,EAAE,WAAW,EAAE,CAAC;IACtB,+DAA+D;IAC/D,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;CACtB;AA4DD,wBAAgB,OAAO,CAAC,EACtB,KAAU,EACV,QAAQ,EACR,WAAW,EACX,KAAgB,EAChB,SAA6B,EAC7B,UAAkB,GACnB,EAAE,YAAY,2CA8Bd"}
|