@fairmint/ui 0.4.15 → 0.4.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/_virtual/index3.cjs +5 -3
- package/dist/_virtual/index3.cjs.map +1 -1
- package/dist/_virtual/index3.js +5 -2
- package/dist/_virtual/index3.js.map +1 -1
- package/dist/_virtual/index4.cjs +3 -5
- package/dist/_virtual/index4.cjs.map +1 -1
- package/dist/_virtual/index4.js +2 -5
- package/dist/_virtual/index4.js.map +1 -1
- package/dist/components/generic/StatCard/StatCard.cjs +80 -15
- package/dist/components/generic/StatCard/StatCard.cjs.map +1 -1
- package/dist/components/generic/StatCard/StatCard.d.ts +11 -1
- package/dist/components/generic/StatCard/StatCard.d.ts.map +1 -1
- package/dist/components/generic/StatCard/StatCard.js +80 -15
- package/dist/components/generic/StatCard/StatCard.js.map +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/prop-types/index.cjs +1 -1
- package/dist/node_modules/prop-types/index.js +1 -1
- package/package.json +1 -1
package/dist/_virtual/index3.cjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
const _commonjsHelpers = require("./_commonjsHelpers.cjs");
|
|
3
|
+
const index = require("../node_modules/fast-deep-equal/index.cjs");
|
|
4
|
+
var fastDeepEqualExports = index.__require();
|
|
5
|
+
const isEqual = /* @__PURE__ */ _commonjsHelpers.getDefaultExportFromCjs(fastDeepEqualExports);
|
|
6
|
+
module.exports = isEqual;
|
|
5
7
|
//# sourceMappingURL=index3.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index3.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index3.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;"}
|
package/dist/_virtual/index3.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
import { getDefaultExportFromCjs } from "./_commonjsHelpers.js";
|
|
2
|
+
import { __require as requireFastDeepEqual } from "../node_modules/fast-deep-equal/index.js";
|
|
3
|
+
var fastDeepEqualExports = requireFastDeepEqual();
|
|
4
|
+
const isEqual = /* @__PURE__ */ getDefaultExportFromCjs(fastDeepEqualExports);
|
|
2
5
|
export {
|
|
3
|
-
|
|
6
|
+
isEqual as default
|
|
4
7
|
};
|
|
5
8
|
//# sourceMappingURL=index3.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index3.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index3.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
|
package/dist/_virtual/index4.cjs
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const isEqual = /* @__PURE__ */ _commonjsHelpers.getDefaultExportFromCjs(fastDeepEqualExports);
|
|
6
|
-
module.exports = isEqual;
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
var propTypes = { exports: {} };
|
|
4
|
+
exports.__module = propTypes;
|
|
7
5
|
//# sourceMappingURL=index4.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index4.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index4.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
|
package/dist/_virtual/index4.js
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
import { __require as requireFastDeepEqual } from "../node_modules/fast-deep-equal/index.js";
|
|
3
|
-
var fastDeepEqualExports = requireFastDeepEqual();
|
|
4
|
-
const isEqual = /* @__PURE__ */ getDefaultExportFromCjs(fastDeepEqualExports);
|
|
1
|
+
var propTypes = { exports: {} };
|
|
5
2
|
export {
|
|
6
|
-
|
|
3
|
+
propTypes as __module
|
|
7
4
|
};
|
|
8
5
|
//# sourceMappingURL=index4.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index4.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index4.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
|
|
@@ -1,6 +1,7 @@
|
|
|
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 colors = require("../../../theme/colors.cjs");
|
|
6
7
|
const ActionIcon = require("../../primitives/ActionIcon/ActionIcon.cjs");
|
|
@@ -31,9 +32,56 @@ function DefaultInfoIcon() {
|
|
|
31
32
|
}
|
|
32
33
|
);
|
|
33
34
|
}
|
|
35
|
+
function isOverflowing(element) {
|
|
36
|
+
return element.scrollWidth > element.clientWidth;
|
|
37
|
+
}
|
|
38
|
+
function StatCardValue({
|
|
39
|
+
value,
|
|
40
|
+
interactive,
|
|
41
|
+
onActivate,
|
|
42
|
+
onKeyDown
|
|
43
|
+
}) {
|
|
44
|
+
const ref = React.useRef(null);
|
|
45
|
+
const [truncated, setTruncated] = React.useState(false);
|
|
46
|
+
React.useLayoutEffect(() => {
|
|
47
|
+
const element = ref.current;
|
|
48
|
+
if (!element || element.textContent !== value) return;
|
|
49
|
+
const measure = () => {
|
|
50
|
+
const next = isOverflowing(element);
|
|
51
|
+
setTruncated((current) => current === next ? current : next);
|
|
52
|
+
};
|
|
53
|
+
measure();
|
|
54
|
+
const observer = new ResizeObserver(measure);
|
|
55
|
+
observer.observe(element);
|
|
56
|
+
return () => observer.disconnect();
|
|
57
|
+
}, [value]);
|
|
58
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Tooltip.Tooltip, { label: value, disabled: !truncated, withArrow: true, position: "top", multiline: true, maw: 280, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
59
|
+
Text.Text,
|
|
60
|
+
{
|
|
61
|
+
ref,
|
|
62
|
+
component: "span",
|
|
63
|
+
fw: 700,
|
|
64
|
+
size: "xl",
|
|
65
|
+
lh: 1,
|
|
66
|
+
truncate: true,
|
|
67
|
+
role: interactive ? "button" : void 0,
|
|
68
|
+
tabIndex: interactive ? 0 : void 0,
|
|
69
|
+
onClick: onActivate,
|
|
70
|
+
onKeyDown,
|
|
71
|
+
style: {
|
|
72
|
+
cursor: interactive ? "pointer" : void 0,
|
|
73
|
+
outlineOffset: 2,
|
|
74
|
+
minWidth: 0,
|
|
75
|
+
maxWidth: "100%"
|
|
76
|
+
},
|
|
77
|
+
children: value
|
|
78
|
+
}
|
|
79
|
+
) });
|
|
80
|
+
}
|
|
34
81
|
function StatCard({
|
|
35
82
|
label,
|
|
36
83
|
value,
|
|
84
|
+
unit,
|
|
37
85
|
icon,
|
|
38
86
|
color = "neutral",
|
|
39
87
|
trend,
|
|
@@ -47,6 +95,7 @@ function StatCard({
|
|
|
47
95
|
const isNeutral = color === "neutral";
|
|
48
96
|
const accent = isNeutral ? colors.colors.primary : color;
|
|
49
97
|
const valueInteractive = Boolean(onValueClick);
|
|
98
|
+
const unitText = unit?.trim() ? unit.trim() : void 0;
|
|
50
99
|
const handleValueKeyDown = (event) => {
|
|
51
100
|
if (!onValueClick) return;
|
|
52
101
|
if (event.key === "Enter" || event.key === " ") {
|
|
@@ -54,6 +103,15 @@ function StatCard({
|
|
|
54
103
|
onValueClick();
|
|
55
104
|
}
|
|
56
105
|
};
|
|
106
|
+
const valueNode = /* @__PURE__ */ jsxRuntime.jsx(
|
|
107
|
+
StatCardValue,
|
|
108
|
+
{
|
|
109
|
+
value,
|
|
110
|
+
interactive: valueInteractive,
|
|
111
|
+
onActivate: onValueClick,
|
|
112
|
+
onKeyDown: handleValueKeyDown
|
|
113
|
+
}
|
|
114
|
+
);
|
|
57
115
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
58
116
|
Card.Card,
|
|
59
117
|
{
|
|
@@ -87,26 +145,33 @@ function StatCard({
|
|
|
87
145
|
}
|
|
88
146
|
) })
|
|
89
147
|
] }),
|
|
90
|
-
/* @__PURE__ */ jsxRuntime.jsxs(Group.Group, { gap:
|
|
148
|
+
unitText ? /* @__PURE__ */ jsxRuntime.jsxs(Group.Group, { gap: 6, align: "baseline", wrap: "wrap", style: { minWidth: 0 }, children: [
|
|
149
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
150
|
+
Group.Group,
|
|
151
|
+
{
|
|
152
|
+
gap: "xs",
|
|
153
|
+
align: "center",
|
|
154
|
+
wrap: "nowrap",
|
|
155
|
+
style: { flex: "0 1 auto", maxWidth: "100%", minWidth: 0 },
|
|
156
|
+
children: [
|
|
157
|
+
valueNode,
|
|
158
|
+
change ? /* @__PURE__ */ jsxRuntime.jsx(layout.Box, { style: { flexShrink: 0 }, children: change }) : null
|
|
159
|
+
]
|
|
160
|
+
}
|
|
161
|
+
),
|
|
91
162
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
92
163
|
Text.Text,
|
|
93
164
|
{
|
|
94
165
|
component: "span",
|
|
95
|
-
fw:
|
|
96
|
-
size: "
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
|
166
|
+
fw: 600,
|
|
167
|
+
size: "md",
|
|
168
|
+
c: index.fairmintVars.colorTextSecondary,
|
|
169
|
+
style: { whiteSpace: "nowrap", flex: "0 0 auto" },
|
|
170
|
+
children: unitText
|
|
108
171
|
}
|
|
109
|
-
)
|
|
172
|
+
)
|
|
173
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs(Group.Group, { gap: "xs", align: "center", wrap: "nowrap", children: [
|
|
174
|
+
valueNode,
|
|
110
175
|
change
|
|
111
176
|
] }),
|
|
112
177
|
trend && /* @__PURE__ */ jsxRuntime.jsx(Text.Text, { size: "xs", c: index.fairmintVars.colorTextSecondary, children: trend })
|
|
@@ -1 +1 @@
|
|
|
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
|
+
{"version":3,"file":"StatCard.cjs","sources":["../../../../src/components/generic/StatCard/StatCard.tsx"],"sourcesContent":["import { type KeyboardEvent, type ReactNode, useLayoutEffect, useRef, useState } 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\nfunction isOverflowing(element: HTMLElement): boolean {\n return element.scrollWidth > element.clientWidth;\n}\n\n/**\n * The figure. It stays on one line and ellipsizes when the tile is narrower than the text.\n * A cut-off figure keeps its full text in a tooltip — the clipped end is often the unit.\n */\nfunction StatCardValue({\n value,\n interactive,\n onActivate,\n onKeyDown,\n}: {\n value: string;\n interactive: boolean;\n onActivate?: () => void;\n onKeyDown: (event: KeyboardEvent<HTMLSpanElement>) => void;\n}) {\n const ref = useRef<HTMLParagraphElement>(null);\n const [truncated, setTruncated] = useState(false);\n\n useLayoutEffect(() => {\n const element = ref.current;\n // The DOM text updates before this effect. Re-measure when `value` changes,\n // because a new string can clip without the box itself resizing.\n if (!element || element.textContent !== value) return;\n\n const measure = () => {\n const next = isOverflowing(element);\n setTruncated((current) => (current === next ? current : next));\n };\n\n measure();\n const observer = new ResizeObserver(measure);\n observer.observe(element);\n return () => observer.disconnect();\n }, [value]);\n\n return (\n <Tooltip label={value} disabled={!truncated} withArrow position=\"top\" multiline maw={280}>\n <Text\n ref={ref}\n component=\"span\"\n fw={700}\n size=\"xl\"\n lh={1}\n truncate\n role={interactive ? \"button\" : undefined}\n tabIndex={interactive ? 0 : undefined}\n onClick={onActivate}\n onKeyDown={onKeyDown}\n style={{\n cursor: interactive ? \"pointer\" : undefined,\n outlineOffset: 2,\n minWidth: 0,\n maxWidth: \"100%\",\n }}\n >\n {value}\n </Text>\n </Tooltip>\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 /**\n * Suffix that belongs to the figure, such as an instrument (`USDCx`).\n * Rendered smaller, in the secondary color, and kept whole. When the tile is\n * too narrow it moves to the next line instead of being clipped off `value`.\n */\n unit?: 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 *\n * `value` stays on one line. Pass `unit` for a suffix that must remain readable\n * when the tile is narrow. A value that is still cut off exposes its full text\n * in a tooltip.\n */\nexport function StatCard({\n label,\n value,\n unit,\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 const unitText = unit?.trim() ? unit.trim() : undefined;\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 const valueNode = (\n <StatCardValue\n value={value}\n interactive={valueInteractive}\n onActivate={onValueClick}\n onKeyDown={handleValueKeyDown}\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 {unitText ? (\n <Group gap={6} align=\"baseline\" wrap=\"wrap\" style={{ minWidth: 0 }}>\n <Group\n gap=\"xs\"\n align=\"center\"\n wrap=\"nowrap\"\n style={{ flex: \"0 1 auto\", maxWidth: \"100%\", minWidth: 0 }}\n >\n {valueNode}\n {change ? <Box style={{ flexShrink: 0 }}>{change}</Box> : null}\n </Group>\n <Text\n component=\"span\"\n fw={600}\n size=\"md\"\n c={fairmintVars.colorTextSecondary}\n style={{ whiteSpace: \"nowrap\", flex: \"0 0 auto\" }}\n >\n {unitText}\n </Text>\n </Group>\n ) : (\n <Group gap=\"xs\" align=\"center\" wrap=\"nowrap\">\n {valueNode}\n {change}\n </Group>\n )}\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","useRef","useState","useLayoutEffect","Tooltip","Text","colors","Card","fairmintVars","Group","Stack","ActionIcon","Box","Avatar"],"mappings":"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"}
|
|
@@ -5,6 +5,12 @@ export interface StatCardProps extends Omit<CardProps, "children"> {
|
|
|
5
5
|
label: string;
|
|
6
6
|
/** Formatted value string (e.g., "$125,000"). */
|
|
7
7
|
value: string;
|
|
8
|
+
/**
|
|
9
|
+
* Suffix that belongs to the figure, such as an instrument (`USDCx`).
|
|
10
|
+
* Rendered smaller, in the secondary color, and kept whole. When the tile is
|
|
11
|
+
* too narrow it moves to the next line instead of being clipped off `value`.
|
|
12
|
+
*/
|
|
13
|
+
unit?: string;
|
|
8
14
|
/** Optional icon element. */
|
|
9
15
|
icon?: ReactNode;
|
|
10
16
|
/**
|
|
@@ -37,6 +43,10 @@ export interface StatCardProps extends Omit<CardProps, "children"> {
|
|
|
37
43
|
* Default surface is a white card with a light border (no elevation). Apps own
|
|
38
44
|
* value formatting, icon choice, and change-badge content; StatCard owns the
|
|
39
45
|
* layout and visual contract.
|
|
46
|
+
*
|
|
47
|
+
* `value` stays on one line. Pass `unit` for a suffix that must remain readable
|
|
48
|
+
* when the tile is narrow. A value that is still cut off exposes its full text
|
|
49
|
+
* in a tooltip.
|
|
40
50
|
*/
|
|
41
|
-
export declare function StatCard({ label, value, icon, color, trend, change, infoTooltip, infoIcon, onValueClick, iconVariant, ...rest }: StatCardProps): import("react").JSX.Element;
|
|
51
|
+
export declare function StatCard({ label, value, unit, icon, color, trend, change, infoTooltip, infoIcon, onValueClick, iconVariant, ...rest }: StatCardProps): import("react").JSX.Element;
|
|
42
52
|
//# 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,EAAsB,KAAK,SAAS,EAAqC,MAAM,OAAO,CAAC;AAK9F,OAAO,EAAQ,KAAK,SAAS,EAAE,MAAM,4BAA4B,CAAC;AA0FlE,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;;;;OAIG;IACH,IAAI,CAAC,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;;;;;;;;;;GAUG;AACH,wBAAgB,QAAQ,CAAC,EACvB,KAAK,EACL,KAAK,EACL,IAAI,EACJ,IAAI,EACJ,KAAiB,EACjB,KAAK,EACL,MAAM,EACN,WAAW,EACX,QAAQ,EACR,YAAY,EACZ,WAAqB,EACrB,GAAG,IAAI,EACR,EAAE,aAAa,+BA0Gf"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useRef, useState, useLayoutEffect } from "react";
|
|
2
3
|
import { fairmintVars } from "../../../theme/index.js";
|
|
3
4
|
import { colors } from "../../../theme/colors.js";
|
|
4
5
|
import { ActionIcon } from "../../primitives/ActionIcon/ActionIcon.js";
|
|
@@ -29,9 +30,56 @@ function DefaultInfoIcon() {
|
|
|
29
30
|
}
|
|
30
31
|
);
|
|
31
32
|
}
|
|
33
|
+
function isOverflowing(element) {
|
|
34
|
+
return element.scrollWidth > element.clientWidth;
|
|
35
|
+
}
|
|
36
|
+
function StatCardValue({
|
|
37
|
+
value,
|
|
38
|
+
interactive,
|
|
39
|
+
onActivate,
|
|
40
|
+
onKeyDown
|
|
41
|
+
}) {
|
|
42
|
+
const ref = useRef(null);
|
|
43
|
+
const [truncated, setTruncated] = useState(false);
|
|
44
|
+
useLayoutEffect(() => {
|
|
45
|
+
const element = ref.current;
|
|
46
|
+
if (!element || element.textContent !== value) return;
|
|
47
|
+
const measure = () => {
|
|
48
|
+
const next = isOverflowing(element);
|
|
49
|
+
setTruncated((current) => current === next ? current : next);
|
|
50
|
+
};
|
|
51
|
+
measure();
|
|
52
|
+
const observer = new ResizeObserver(measure);
|
|
53
|
+
observer.observe(element);
|
|
54
|
+
return () => observer.disconnect();
|
|
55
|
+
}, [value]);
|
|
56
|
+
return /* @__PURE__ */ jsx(Tooltip, { label: value, disabled: !truncated, withArrow: true, position: "top", multiline: true, maw: 280, children: /* @__PURE__ */ jsx(
|
|
57
|
+
Text,
|
|
58
|
+
{
|
|
59
|
+
ref,
|
|
60
|
+
component: "span",
|
|
61
|
+
fw: 700,
|
|
62
|
+
size: "xl",
|
|
63
|
+
lh: 1,
|
|
64
|
+
truncate: true,
|
|
65
|
+
role: interactive ? "button" : void 0,
|
|
66
|
+
tabIndex: interactive ? 0 : void 0,
|
|
67
|
+
onClick: onActivate,
|
|
68
|
+
onKeyDown,
|
|
69
|
+
style: {
|
|
70
|
+
cursor: interactive ? "pointer" : void 0,
|
|
71
|
+
outlineOffset: 2,
|
|
72
|
+
minWidth: 0,
|
|
73
|
+
maxWidth: "100%"
|
|
74
|
+
},
|
|
75
|
+
children: value
|
|
76
|
+
}
|
|
77
|
+
) });
|
|
78
|
+
}
|
|
32
79
|
function StatCard({
|
|
33
80
|
label,
|
|
34
81
|
value,
|
|
82
|
+
unit,
|
|
35
83
|
icon,
|
|
36
84
|
color = "neutral",
|
|
37
85
|
trend,
|
|
@@ -45,6 +93,7 @@ function StatCard({
|
|
|
45
93
|
const isNeutral = color === "neutral";
|
|
46
94
|
const accent = isNeutral ? colors.primary : color;
|
|
47
95
|
const valueInteractive = Boolean(onValueClick);
|
|
96
|
+
const unitText = unit?.trim() ? unit.trim() : void 0;
|
|
48
97
|
const handleValueKeyDown = (event) => {
|
|
49
98
|
if (!onValueClick) return;
|
|
50
99
|
if (event.key === "Enter" || event.key === " ") {
|
|
@@ -52,6 +101,15 @@ function StatCard({
|
|
|
52
101
|
onValueClick();
|
|
53
102
|
}
|
|
54
103
|
};
|
|
104
|
+
const valueNode = /* @__PURE__ */ jsx(
|
|
105
|
+
StatCardValue,
|
|
106
|
+
{
|
|
107
|
+
value,
|
|
108
|
+
interactive: valueInteractive,
|
|
109
|
+
onActivate: onValueClick,
|
|
110
|
+
onKeyDown: handleValueKeyDown
|
|
111
|
+
}
|
|
112
|
+
);
|
|
55
113
|
return /* @__PURE__ */ jsx(
|
|
56
114
|
Card,
|
|
57
115
|
{
|
|
@@ -85,26 +143,33 @@ function StatCard({
|
|
|
85
143
|
}
|
|
86
144
|
) })
|
|
87
145
|
] }),
|
|
88
|
-
/* @__PURE__ */ jsxs(Group, { gap:
|
|
146
|
+
unitText ? /* @__PURE__ */ jsxs(Group, { gap: 6, align: "baseline", wrap: "wrap", style: { minWidth: 0 }, children: [
|
|
147
|
+
/* @__PURE__ */ jsxs(
|
|
148
|
+
Group,
|
|
149
|
+
{
|
|
150
|
+
gap: "xs",
|
|
151
|
+
align: "center",
|
|
152
|
+
wrap: "nowrap",
|
|
153
|
+
style: { flex: "0 1 auto", maxWidth: "100%", minWidth: 0 },
|
|
154
|
+
children: [
|
|
155
|
+
valueNode,
|
|
156
|
+
change ? /* @__PURE__ */ jsx(Box, { style: { flexShrink: 0 }, children: change }) : null
|
|
157
|
+
]
|
|
158
|
+
}
|
|
159
|
+
),
|
|
89
160
|
/* @__PURE__ */ jsx(
|
|
90
161
|
Text,
|
|
91
162
|
{
|
|
92
163
|
component: "span",
|
|
93
|
-
fw:
|
|
94
|
-
size: "
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
|
164
|
+
fw: 600,
|
|
165
|
+
size: "md",
|
|
166
|
+
c: fairmintVars.colorTextSecondary,
|
|
167
|
+
style: { whiteSpace: "nowrap", flex: "0 0 auto" },
|
|
168
|
+
children: unitText
|
|
106
169
|
}
|
|
107
|
-
)
|
|
170
|
+
)
|
|
171
|
+
] }) : /* @__PURE__ */ jsxs(Group, { gap: "xs", align: "center", wrap: "nowrap", children: [
|
|
172
|
+
valueNode,
|
|
108
173
|
change
|
|
109
174
|
] }),
|
|
110
175
|
trend && /* @__PURE__ */ jsx(Text, { size: "xs", c: fairmintVars.colorTextSecondary, children: trend })
|
|
@@ -1 +1 @@
|
|
|
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
|
+
{"version":3,"file":"StatCard.js","sources":["../../../../src/components/generic/StatCard/StatCard.tsx"],"sourcesContent":["import { type KeyboardEvent, type ReactNode, useLayoutEffect, useRef, useState } 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\nfunction isOverflowing(element: HTMLElement): boolean {\n return element.scrollWidth > element.clientWidth;\n}\n\n/**\n * The figure. It stays on one line and ellipsizes when the tile is narrower than the text.\n * A cut-off figure keeps its full text in a tooltip — the clipped end is often the unit.\n */\nfunction StatCardValue({\n value,\n interactive,\n onActivate,\n onKeyDown,\n}: {\n value: string;\n interactive: boolean;\n onActivate?: () => void;\n onKeyDown: (event: KeyboardEvent<HTMLSpanElement>) => void;\n}) {\n const ref = useRef<HTMLParagraphElement>(null);\n const [truncated, setTruncated] = useState(false);\n\n useLayoutEffect(() => {\n const element = ref.current;\n // The DOM text updates before this effect. Re-measure when `value` changes,\n // because a new string can clip without the box itself resizing.\n if (!element || element.textContent !== value) return;\n\n const measure = () => {\n const next = isOverflowing(element);\n setTruncated((current) => (current === next ? current : next));\n };\n\n measure();\n const observer = new ResizeObserver(measure);\n observer.observe(element);\n return () => observer.disconnect();\n }, [value]);\n\n return (\n <Tooltip label={value} disabled={!truncated} withArrow position=\"top\" multiline maw={280}>\n <Text\n ref={ref}\n component=\"span\"\n fw={700}\n size=\"xl\"\n lh={1}\n truncate\n role={interactive ? \"button\" : undefined}\n tabIndex={interactive ? 0 : undefined}\n onClick={onActivate}\n onKeyDown={onKeyDown}\n style={{\n cursor: interactive ? \"pointer\" : undefined,\n outlineOffset: 2,\n minWidth: 0,\n maxWidth: \"100%\",\n }}\n >\n {value}\n </Text>\n </Tooltip>\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 /**\n * Suffix that belongs to the figure, such as an instrument (`USDCx`).\n * Rendered smaller, in the secondary color, and kept whole. When the tile is\n * too narrow it moves to the next line instead of being clipped off `value`.\n */\n unit?: 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 *\n * `value` stays on one line. Pass `unit` for a suffix that must remain readable\n * when the tile is narrow. A value that is still cut off exposes its full text\n * in a tooltip.\n */\nexport function StatCard({\n label,\n value,\n unit,\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 const unitText = unit?.trim() ? unit.trim() : undefined;\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 const valueNode = (\n <StatCardValue\n value={value}\n interactive={valueInteractive}\n onActivate={onValueClick}\n onKeyDown={handleValueKeyDown}\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 {unitText ? (\n <Group gap={6} align=\"baseline\" wrap=\"wrap\" style={{ minWidth: 0 }}>\n <Group\n gap=\"xs\"\n align=\"center\"\n wrap=\"nowrap\"\n style={{ flex: \"0 1 auto\", maxWidth: \"100%\", minWidth: 0 }}\n >\n {valueNode}\n {change ? <Box style={{ flexShrink: 0 }}>{change}</Box> : null}\n </Group>\n <Text\n component=\"span\"\n fw={600}\n size=\"md\"\n c={fairmintVars.colorTextSecondary}\n style={{ whiteSpace: \"nowrap\", flex: \"0 0 auto\" }}\n >\n {unitText}\n </Text>\n </Group>\n ) : (\n <Group gap=\"xs\" align=\"center\" wrap=\"nowrap\">\n {valueNode}\n {change}\n </Group>\n )}\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,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const React = require("react");
|
|
4
|
-
const index = require("../../../../../../_virtual/
|
|
4
|
+
const index = require("../../../../../../_virtual/index3.cjs");
|
|
5
5
|
const getStatus = require("../../get-status/get-status.cjs");
|
|
6
6
|
const clearListState = require("../../lists/clear-list-state.cjs");
|
|
7
7
|
const getPath = require("../../paths/get-path.cjs");
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useState, useRef, useCallback } from "react";
|
|
2
|
-
import isEqual from "../../../../../../_virtual/
|
|
2
|
+
import isEqual from "../../../../../../_virtual/index3.js";
|
|
3
3
|
import { getStatus } from "../../get-status/get-status.js";
|
|
4
4
|
import { clearListState } from "../../lists/clear-list-state.js";
|
|
5
5
|
import { getPath } from "../../paths/get-path.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
const index$1 = require("../../_virtual/
|
|
3
|
+
const index$1 = require("../../_virtual/index4.cjs");
|
|
4
4
|
const index = require("./node_modules/react-is/index.cjs");
|
|
5
5
|
const factoryWithTypeCheckers = require("./factoryWithTypeCheckers.cjs");
|
|
6
6
|
const factoryWithThrowingShims = require("./factoryWithThrowingShims.cjs");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { __module as propTypes } from "../../_virtual/
|
|
1
|
+
import { __module as propTypes } from "../../_virtual/index4.js";
|
|
2
2
|
import { __require as requireReactIs } from "./node_modules/react-is/index.js";
|
|
3
3
|
import { __require as requireFactoryWithTypeCheckers } from "./factoryWithTypeCheckers.js";
|
|
4
4
|
import { __require as requireFactoryWithThrowingShims } from "./factoryWithThrowingShims.js";
|