@fairmint/ui 0.4.15 → 0.4.17
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 +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/styles.css +9 -0
- package/package.json +1 -1
|
@@ -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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"}
|
package/dist/styles.css
CHANGED
|
@@ -9425,6 +9425,15 @@ fieldset:disabled .m_38aeed47:where(:not([data-inverted])),
|
|
|
9425
9425
|
.fairmint-multiselect-pills-list > * {
|
|
9426
9426
|
flex-shrink: 0;
|
|
9427
9427
|
}
|
|
9428
|
+
|
|
9429
|
+
/* Badges set overflow: hidden, so table layout can shrink them and clip the
|
|
9430
|
+
label ("Earning fees" becomes "EARNING…"). Keep a badge inside a cell at
|
|
9431
|
+
least as wide as its label; the table scrolls instead of clipping it.
|
|
9432
|
+
Same rule as the ta-service workaround. */
|
|
9433
|
+
|
|
9434
|
+
.mantine-Table-td .mantine-Badge-root {
|
|
9435
|
+
min-width: max-content;
|
|
9436
|
+
}
|
|
9428
9437
|
.fairmint-equity-ribbon {
|
|
9429
9438
|
position: relative;
|
|
9430
9439
|
width: 100%;
|