@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.
@@ -1,5 +1,7 @@
1
1
  "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- var propTypes = { exports: {} };
4
- exports.__module = propTypes;
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":";;;;;;"}
@@ -1,5 +1,8 @@
1
- var propTypes = { exports: {} };
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
- propTypes as __module
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":";;;;"}
@@ -1,7 +1,5 @@
1
1
  "use strict";
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;
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":";;;;"}
@@ -1,8 +1,5 @@
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);
1
+ var propTypes = { exports: {} };
5
2
  export {
6
- isEqual as default
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: "xs", align: "center", wrap: "nowrap", children: [
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: 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
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,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,+BAyFf"}
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: "xs", align: "center", wrap: "nowrap", children: [
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: 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
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/index4.cjs");
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/index4.js";
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/index3.cjs");
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/index3.js";
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";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fairmint/ui",
3
- "version": "0.4.15",
3
+ "version": "0.4.16",
4
4
  "type": "module",
5
5
  "description": "Fairmint design system and UI component library",
6
6
  "license": "SEE LICENSE IN LICENSE",