@lablup/ui-common 0.1.0-alpha.22 → 0.1.0-alpha.23
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/assets/components/DigitPopIn/DigitPopIn.css +1 -0
- package/dist/assets/components/StatCard/StatCard.css +1 -1
- package/dist/components/DigitPopIn/DigitPopIn.d.ts +8 -0
- package/dist/components/DigitPopIn/DigitPopIn2.js +30 -0
- package/dist/components/DigitPopIn/DigitPopIn2.js.map +1 -0
- package/dist/components/DigitPopIn/index.d.ts +2 -0
- package/dist/components/DigitPopIn/index.js +2 -0
- package/dist/components/StatCard/StatCard.d.ts +12 -3
- package/dist/components/StatCard/StatCard2.js +60 -59
- package/dist/components/StatCard/StatCard2.js.map +1 -1
- package/dist/components/StatCard/index.d.ts +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +21 -20
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.digit-pop-in{--digit-pop-in-duration:.5s;--digit-pop-in-distance:8px;--digit-pop-in-stagger:70ms;--digit-pop-in-blur:2px;--digit-pop-in-ease:cubic-bezier(.34, 1.45, .64, 1);display:inline-block;position:relative}.digit-pop-in__digits{align-items:baseline;display:inline-flex}.digit-pop-in__digit{animation:digit-pop-in var(--digit-pop-in-duration) var(--digit-pop-in-ease) both;animation-delay:calc(var(--digit-pop-in-index,0) * var(--digit-pop-in-stagger));display:inline-block}.digit-pop-in__text{clip-path:inset(50%);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}@keyframes digit-pop-in{0%{transform:translateY(var(--digit-pop-in-distance));opacity:0;filter:blur(var(--digit-pop-in-blur))}to{opacity:1;filter:blur();transform:translateY(0)}}@media (prefers-reduced-motion:reduce){.digit-pop-in__digit{animation:none}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
.stat-card{gap:var(--token-paddingSM,.5rem);min-width:0;overflow:hidden}.stat-card__header{justify-content:space-between;align-items:center;gap:var(--token-paddingXS,.25rem);display:flex}.stat-card__label{font-size:var(--token-fontSizeSM,.75rem);color:var(--token-colorTextSecondary,#ffffff73);text-transform:uppercase;letter-spacing:.04em;white-space:nowrap;text-overflow:ellipsis;font-weight:600;overflow:hidden}.stat-card__icon{border-radius:var(--token-borderRadiusSM,.25rem);background:var(--token-colorFillSecondary,#ffffff0a);width:28px;height:28px;color:var(--token-colorTextSecondary,#fff9);flex-shrink:0;justify-content:center;align-items:center;display:inline-flex}.stat-card__body{flex-direction:column;gap:4px;display:flex}.stat-card__value-row{flex-wrap:wrap;align-items:baseline;gap:.3em;display:inline-flex}.stat-card__value-line{justify-content:space-between;align-items:center;gap:var(--token-paddingSM,.5rem);min-width:0;display:flex}.stat-card__sparkline{flex-shrink:0;align-items:center;display:inline-flex}.stat-card__value{font-size:var(--token-fontSizeXXL,1.5rem);color:var(--token-colorText,#141414);font-variant-numeric:tabular-nums;text-overflow:ellipsis;white-space:nowrap;min-width:0;font-weight:600;line-height:1.1;overflow:hidden}.stat-card__value-suffix{font-size:var(--token-fontSize,.875rem);color:var(--token-colorTextSecondary,#ffffff73);font-weight:400}.stat-card__trend{font-size:var(--token-fontSizeSM,.75rem);align-items:center;gap:4px;font-weight:500;display:inline-flex}.stat-card__trend--up{color:var(--token-colorSuccess,#52c41a)}.stat-card__trend--down{color:var(--token-colorError,#f5222d)}.stat-card__trend--flat{color:var(--token-colorTextSecondary,#ffffff73)}.stat-card__hint{font-size:var(--token-fontSizeSM,.75rem);color:var(--token-colorTextSecondary,#ffffff73);line-height:1.4}.stat-card.stat-card--prominent{padding:var(--token-paddingLG,1.5rem)}.stat-card--prominent{background:linear-gradient(135deg, var(--token-colorBgContainer,#fff) 0%, color-mix(in srgb, var(--token-colorPrimary,#8b5cf6) 4%, transparent) 100%)}.stat-card--prominent:hover{border-color:color-mix(in srgb, var(--token-colorPrimary,#8b5cf6) 30%, transparent)}.stat-card--prominent .stat-card__value{font-size:var(--token-fontSizeHeading1,2.25rem)}.stat-card--prominent .stat-card__icon{width:34px;height:34px}.stat-card.stat-card--compact{padding:var(--token-paddingSM,.5rem)}.stat-card--compact .stat-card__value{font-size:var(--token-fontSizeHeading3,1.5rem)}.stat-card--tone-success .stat-card__value{color:var(--token-colorSuccess,#52c41a)}.stat-card--tone-success .stat-card__icon{color:var(--token-colorSuccess,#52c41a);background:#52c41a1f}.stat-card--tone-warning .stat-card__value{color:var(--token-colorWarning,#faad14)}.stat-card--tone-warning .stat-card__icon{color:var(--token-colorWarning,#faad14);background:#faad1424}.stat-card--tone-danger .stat-card__value{color:var(--token-colorError,#f5222d)}.stat-card--tone-danger .stat-card__icon{color:var(--token-colorError,#f5222d);background:#f5222d1f}.stat-card--tone-info .stat-card__value{color:var(--token-colorPrimary,#8b5cf6)}.stat-card--tone-info .stat-card__icon{color:var(--token-colorPrimary,#8b5cf6);background:#6a35fc1f}.stat-card--tone-success{--corner-accent-color:var(--token-colorSuccess,#52c41a)}.stat-card--tone-warning{--corner-accent-color:var(--token-colorWarning,#faad14)}.stat-card--tone-danger{--corner-accent-color:var(--token-colorError,#f5222d)}.stat-card--tone-info{--corner-accent-color:var(--token-colorPrimary,#8b5cf6)}@media (prefers-reduced-motion:reduce){.stat-card__trend{transition:none}}
|
|
1
|
+
.stat-card{gap:var(--token-paddingSM,.5rem);min-width:0;overflow:hidden}.stat-card__header{justify-content:space-between;align-items:center;gap:var(--token-paddingXS,.25rem);display:flex}.stat-card__label{font-size:var(--token-fontSizeSM,.75rem);color:var(--token-colorTextSecondary,#ffffff73);text-transform:uppercase;letter-spacing:.04em;white-space:nowrap;text-overflow:ellipsis;font-weight:600;overflow:hidden}.stat-card__icon{border-radius:var(--token-borderRadiusSM,.25rem);background:var(--token-colorFillSecondary,#ffffff0a);width:28px;height:28px;color:var(--token-colorTextSecondary,#fff9);flex-shrink:0;justify-content:center;align-items:center;display:inline-flex}.stat-card__body{flex-direction:column;gap:4px;display:flex}.stat-card__value-row{flex-wrap:wrap;align-items:baseline;gap:.3em;display:inline-flex}.stat-card__value-line{justify-content:space-between;align-items:center;gap:var(--token-paddingSM,.5rem);min-width:0;display:flex}.stat-card__sparkline{flex-shrink:0;align-items:center;display:inline-flex}.stat-card__value{font-size:var(--token-fontSizeXXL,1.5rem);color:var(--token-colorText,#141414);font-variant-numeric:tabular-nums;text-overflow:ellipsis;white-space:nowrap;min-width:0;font-weight:600;line-height:1.1;overflow:hidden}.stat-card__value--digits{overflow:clip visible}.stat-card__value-suffix{font-size:var(--token-fontSize,.875rem);color:var(--token-colorTextSecondary,#ffffff73);font-weight:400}.stat-card__trend{font-size:var(--token-fontSizeSM,.75rem);align-items:center;gap:4px;font-weight:500;display:inline-flex}.stat-card__trend--up{color:var(--token-colorSuccess,#52c41a)}.stat-card__trend--down{color:var(--token-colorError,#f5222d)}.stat-card__trend--flat{color:var(--token-colorTextSecondary,#ffffff73)}.stat-card__hint{font-size:var(--token-fontSizeSM,.75rem);color:var(--token-colorTextSecondary,#ffffff73);line-height:1.4}.stat-card.stat-card--prominent{padding:var(--token-paddingLG,1.5rem)}.stat-card--prominent{background:linear-gradient(135deg, var(--token-colorBgContainer,#fff) 0%, color-mix(in srgb, var(--token-colorPrimary,#8b5cf6) 4%, transparent) 100%)}.stat-card--prominent:hover{border-color:color-mix(in srgb, var(--token-colorPrimary,#8b5cf6) 30%, transparent)}.stat-card--prominent .stat-card__value{font-size:var(--token-fontSizeHeading1,2.25rem)}.stat-card--prominent .stat-card__icon{width:34px;height:34px}.stat-card.stat-card--compact{padding:var(--token-paddingSM,.5rem)}.stat-card--compact .stat-card__value{font-size:var(--token-fontSizeHeading3,1.5rem)}.stat-card--tone-success .stat-card__value{color:var(--token-colorSuccess,#52c41a)}.stat-card--tone-success .stat-card__icon{color:var(--token-colorSuccess,#52c41a);background:#52c41a1f}.stat-card--tone-warning .stat-card__value{color:var(--token-colorWarning,#faad14)}.stat-card--tone-warning .stat-card__icon{color:var(--token-colorWarning,#faad14);background:#faad1424}.stat-card--tone-danger .stat-card__value{color:var(--token-colorError,#f5222d)}.stat-card--tone-danger .stat-card__icon{color:var(--token-colorError,#f5222d);background:#f5222d1f}.stat-card--tone-info .stat-card__value{color:var(--token-colorPrimary,#8b5cf6)}.stat-card--tone-info .stat-card__icon{color:var(--token-colorPrimary,#8b5cf6);background:#6a35fc1f}.stat-card--tone-success{--corner-accent-color:var(--token-colorSuccess,#52c41a)}.stat-card--tone-warning{--corner-accent-color:var(--token-colorWarning,#faad14)}.stat-card--tone-danger{--corner-accent-color:var(--token-colorError,#f5222d)}.stat-card--tone-info{--corner-accent-color:var(--token-colorPrimary,#8b5cf6)}@media (prefers-reduced-motion:reduce){.stat-card__trend{transition:none}}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { JSX } from 'react';
|
|
2
|
+
export interface DigitPopInProps {
|
|
3
|
+
/** The text to animate, already formatted ("1,234", "98.5%"). */
|
|
4
|
+
text: string;
|
|
5
|
+
/** Extra class name applied to the root. */
|
|
6
|
+
className?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function DigitPopIn({ text, className }: DigitPopInProps): JSX.Element;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { usePrefersReducedMotion as e } from "../../hooks/usePrefersReducedMotion.js";
|
|
2
|
+
/* empty css */
|
|
3
|
+
import { jsx as t, jsxs as n } from "react/jsx-runtime";
|
|
4
|
+
//#region src/components/DigitPopIn/DigitPopIn.tsx
|
|
5
|
+
function r({ text: r, className: i = "" }) {
|
|
6
|
+
let a = e(), o = ["digit-pop-in", i].filter(Boolean).join(" ");
|
|
7
|
+
return a ? /* @__PURE__ */ t("span", {
|
|
8
|
+
className: o,
|
|
9
|
+
children: r
|
|
10
|
+
}) : /* @__PURE__ */ n("span", {
|
|
11
|
+
className: o,
|
|
12
|
+
children: [/* @__PURE__ */ t("span", {
|
|
13
|
+
className: "digit-pop-in__text",
|
|
14
|
+
children: r
|
|
15
|
+
}), /* @__PURE__ */ t("span", {
|
|
16
|
+
className: "digit-pop-in__digits",
|
|
17
|
+
"aria-hidden": "true",
|
|
18
|
+
children: Array.from(r).map((e, n) => /* @__PURE__ */ t("span", {
|
|
19
|
+
className: "digit-pop-in__digit",
|
|
20
|
+
style: { "--digit-pop-in-index": n },
|
|
21
|
+
children: e === " " ? "\xA0" : e
|
|
22
|
+
}, n))
|
|
23
|
+
}, r)]
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
//#endregion
|
|
27
|
+
export { r as DigitPopIn };
|
|
28
|
+
|
|
29
|
+
//# sourceMappingURL=DigitPopIn2.js.map
|
|
30
|
+
import "../../assets/components/DigitPopIn/DigitPopIn.css";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DigitPopIn2.js","names":[],"sources":["../../../src/components/DigitPopIn/DigitPopIn.tsx"],"sourcesContent":["/**\n * DigitPopIn Component\n *\n * Renders text, usually a formatted number, one element per character, each\n * rising into place from a light blur after the one before it. A new `text`\n * replays the animation. Under `prefers-reduced-motion` it renders the plain\n * text and never animates.\n *\n * The characters are hidden from assistive tech and a plain copy of the text\n * is read instead, so a screen reader hears \"1,234\" rather than five glyphs.\n *\n * `StatCard` uses it for `animate=\"digits\"`; use it directly for a number\n * that is not in a card.\n */\n\nimport type { CSSProperties, JSX } from \"react\";\nimport { usePrefersReducedMotion } from \"../../hooks/usePrefersReducedMotion\";\nimport \"./DigitPopIn.css\";\n\nexport interface DigitPopInProps {\n /** The text to animate, already formatted (\"1,234\", \"98.5%\"). */\n text: string;\n /** Extra class name applied to the root. */\n className?: string;\n}\n\nexport function DigitPopIn({ text, className = \"\" }: DigitPopInProps): JSX.Element {\n const prefersReducedMotion = usePrefersReducedMotion();\n const rootClass = [\"digit-pop-in\", className].filter(Boolean).join(\" \");\n\n if (prefersReducedMotion) {\n return <span className={rootClass}>{text}</span>;\n }\n\n return (\n <span className={rootClass}>\n <span className=\"digit-pop-in__text\">{text}</span>\n {/* Keyed on the text, so a new value remounts the characters and the\n animation plays again. */}\n <span key={text} className=\"digit-pop-in__digits\" aria-hidden=\"true\">\n {Array.from(text).map((char, index) => (\n <span\n // Position is the identity: the list is rebuilt whole whenever\n // the text changes.\n key={index}\n className=\"digit-pop-in__digit\"\n style={{ \"--digit-pop-in-index\": index } as CSSProperties}\n >\n {char === \" \" ? \" \" : char}\n </span>\n ))}\n </span>\n </span>\n );\n}\n"],"mappings":";;;;AA0BA,SAAgB,EAAW,EAAE,SAAM,eAAY,MAAoC;CACjF,IAAM,IAAuB,EAAwB,GAC/C,IAAY,CAAC,gBAAgB,CAAS,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;CAMtE,OAJI,IACK,kBAAC,QAAD;EAAM,WAAW;EAAY,UAAA;CAAW,CAAA,IAI/C,kBAAC,QAAD;EAAM,WAAW;EAAjB,UAAA,CACE,kBAAC,QAAD;GAAM,WAAU;GAAsB,UAAA;EAAW,CAAA,GAGjD,kBAAC,QAAD;GAAiB,WAAU;GAAuB,eAAY;GAC3D,UAAA,MAAM,KAAK,CAAI,CAAC,CAAC,KAAK,GAAM,MAC3B,kBAAC,QAAD;IAIE,WAAU;IACV,OAAO,EAAE,wBAAwB,EAAM;IAEtC,UAAA,MAAS,MAAM,SAAM;GAClB,GALC,CAKD,CACP;EACG,GAZK,CAYL,CACF;;AAEV"}
|
|
@@ -2,6 +2,12 @@ import { ReactNode, JSX } from 'react';
|
|
|
2
2
|
export type StatCardEmphasis = "compact" | "default" | "prominent";
|
|
3
3
|
export type StatCardTone = "default" | "success" | "warning" | "danger" | "info";
|
|
4
4
|
export type StatCardTrendDirection = "up" | "down" | "flat";
|
|
5
|
+
/**
|
|
6
|
+
* How a numeric value arrives. `count` counts up from the previous value;
|
|
7
|
+
* `digits` pops each character of the formatted value in, staggered, and
|
|
8
|
+
* replays when the value changes.
|
|
9
|
+
*/
|
|
10
|
+
export type StatCardAnimation = "count" | "digits";
|
|
5
11
|
export interface StatCardTrend {
|
|
6
12
|
/** Direction indicator (up / down / flat) */
|
|
7
13
|
direction: StatCardTrendDirection;
|
|
@@ -50,11 +56,14 @@ export interface StatCardProps {
|
|
|
50
56
|
*/
|
|
51
57
|
format?: (value: number) => string;
|
|
52
58
|
/**
|
|
53
|
-
*
|
|
59
|
+
* Animate a numeric value on mount and on change. `true` or `"count"`
|
|
60
|
+
* counts up to it; `"digits"` pops each character of the formatted value
|
|
61
|
+
* in, staggered, rising into place from a light blur. Off by default so
|
|
54
62
|
* existing consumers render the final value immediately; opt in for
|
|
55
|
-
* dashboard
|
|
63
|
+
* dashboard metrics. Suppressed under `prefers-reduced-motion`, and ignored
|
|
64
|
+
* for string values and while loading.
|
|
56
65
|
*/
|
|
57
|
-
animate?: boolean;
|
|
66
|
+
animate?: boolean | StatCardAnimation;
|
|
58
67
|
/**
|
|
59
68
|
* Optional trailing visual on the value line (e.g. a sparkline). Kept as a
|
|
60
69
|
* slot so the card does not depend on any particular chart component.
|
|
@@ -1,112 +1,113 @@
|
|
|
1
1
|
import { BaseCard as e } from "../BaseCard/BaseCard2.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { usePrefersReducedMotion as t } from "../../hooks/usePrefersReducedMotion.js";
|
|
3
|
+
import { DigitPopIn as n } from "../DigitPopIn/DigitPopIn2.js";
|
|
4
|
+
import { Skeleton as r } from "../Skeleton/Skeleton2.js";
|
|
4
5
|
/* empty css */
|
|
5
|
-
import { memo as
|
|
6
|
-
import { jsx as
|
|
6
|
+
import { memo as i, useEffect as a, useRef as o, useState as s } from "react";
|
|
7
|
+
import { jsx as c, jsxs as l } from "react/jsx-runtime";
|
|
7
8
|
//#region src/components/StatCard/StatCard.tsx
|
|
8
|
-
var
|
|
9
|
-
function
|
|
10
|
-
let [n, r] =
|
|
11
|
-
return
|
|
9
|
+
var u = 800;
|
|
10
|
+
function d(e, t) {
|
|
11
|
+
let [n, r] = s(t ? 0 : e), i = o(t ? 0 : e), c = o(null);
|
|
12
|
+
return a(() => {
|
|
12
13
|
if (!t) {
|
|
13
|
-
|
|
14
|
+
i.current = e, r(e);
|
|
14
15
|
return;
|
|
15
16
|
}
|
|
16
|
-
let n =
|
|
17
|
-
let
|
|
18
|
-
r(Math.round(n + (e - n) *
|
|
17
|
+
let n = i.current, a = performance.now(), o = (t) => {
|
|
18
|
+
let s = Math.min((t - a) / u, 1), l = 1 - (1 - s) ** 3;
|
|
19
|
+
r(Math.round(n + (e - n) * l)), s < 1 ? c.current = requestAnimationFrame(o) : i.current = e;
|
|
19
20
|
};
|
|
20
|
-
return c.current = requestAnimationFrame(
|
|
21
|
+
return c.current = requestAnimationFrame(o), () => {
|
|
21
22
|
c.current !== null && cancelAnimationFrame(c.current);
|
|
22
23
|
};
|
|
23
24
|
}, [e, t]), t ? n : e;
|
|
24
25
|
}
|
|
25
|
-
var
|
|
26
|
+
var f = {
|
|
26
27
|
up: "▲",
|
|
27
28
|
down: "▼",
|
|
28
29
|
flat: "•"
|
|
29
30
|
};
|
|
30
|
-
function
|
|
31
|
+
function p(e, t) {
|
|
31
32
|
return typeof e == "number" ? t ? t(e) : e.toLocaleString() : e;
|
|
32
33
|
}
|
|
33
|
-
function
|
|
34
|
-
let
|
|
34
|
+
function m({ label: i, labelNode: a, value: o, valueSuffix: s, hint: u, icon: m, tone: h = "default", trend: g, onClick: _, loading: v = !1, ariaLabel: y, className: b = "", testId: x, format: S, animate: C = !1, sparkline: w, emphasis: T = "default" }) {
|
|
35
|
+
let E = t(), D = typeof o == "number", O = C === !0 ? "count" : C === !1 ? null : C, k = D && !v && !E, A = O === "count" && k, j = O === "digits" && k, M = d(D ? o : 0, A), N = p(D && A ? M : o, S), P = j ? /* @__PURE__ */ c(n, { text: N }) : N, F = j ? "stat-card__value stat-card__value--digits" : "stat-card__value", I = y ?? (v ? i : `${i}: ${p(o, S)}${s ?? ""}`), L = [
|
|
35
36
|
"stat-card",
|
|
36
37
|
"corner-accent",
|
|
37
|
-
`stat-card--tone-${
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
`stat-card--tone-${h}`,
|
|
39
|
+
T === "default" ? "" : `stat-card--${T}`,
|
|
40
|
+
b
|
|
40
41
|
].filter(Boolean).join(" ");
|
|
41
|
-
return /* @__PURE__ */
|
|
42
|
-
className:
|
|
43
|
-
onClick:
|
|
44
|
-
role:
|
|
45
|
-
ariaLabel:
|
|
46
|
-
testId:
|
|
42
|
+
return /* @__PURE__ */ l(e, {
|
|
43
|
+
className: L,
|
|
44
|
+
onClick: _,
|
|
45
|
+
role: _ ? void 0 : "group",
|
|
46
|
+
ariaLabel: I,
|
|
47
|
+
testId: x,
|
|
47
48
|
children: [
|
|
48
|
-
/* @__PURE__ */
|
|
49
|
+
/* @__PURE__ */ l("div", {
|
|
49
50
|
className: "stat-card__header",
|
|
50
|
-
children: [/* @__PURE__ */
|
|
51
|
+
children: [/* @__PURE__ */ c("span", {
|
|
51
52
|
className: "stat-card__label",
|
|
52
|
-
children:
|
|
53
|
-
}),
|
|
53
|
+
children: a ?? i
|
|
54
|
+
}), m && /* @__PURE__ */ c("span", {
|
|
54
55
|
className: "stat-card__icon",
|
|
55
56
|
"aria-hidden": "true",
|
|
56
|
-
children:
|
|
57
|
+
children: m
|
|
57
58
|
})]
|
|
58
59
|
}),
|
|
59
|
-
/* @__PURE__ */
|
|
60
|
+
/* @__PURE__ */ l("div", {
|
|
60
61
|
className: "stat-card__body",
|
|
61
|
-
children: [
|
|
62
|
+
children: [v ? /* @__PURE__ */ c(r, {
|
|
62
63
|
width: "60%",
|
|
63
64
|
height: "2rem"
|
|
64
|
-
}) :
|
|
65
|
+
}) : w ? /* @__PURE__ */ l("span", {
|
|
65
66
|
className: "stat-card__value-line",
|
|
66
|
-
children: [/* @__PURE__ */
|
|
67
|
+
children: [/* @__PURE__ */ l("span", {
|
|
67
68
|
className: "stat-card__value-row",
|
|
68
|
-
children: [/* @__PURE__ */
|
|
69
|
-
className:
|
|
70
|
-
children:
|
|
71
|
-
}),
|
|
69
|
+
children: [/* @__PURE__ */ c("span", {
|
|
70
|
+
className: F,
|
|
71
|
+
children: P
|
|
72
|
+
}), s && /* @__PURE__ */ c("span", {
|
|
72
73
|
className: "stat-card__value-suffix",
|
|
73
|
-
children:
|
|
74
|
+
children: s
|
|
74
75
|
})]
|
|
75
|
-
}), /* @__PURE__ */
|
|
76
|
+
}), /* @__PURE__ */ c("span", {
|
|
76
77
|
className: "stat-card__sparkline",
|
|
77
|
-
children:
|
|
78
|
+
children: w
|
|
78
79
|
})]
|
|
79
|
-
}) : /* @__PURE__ */
|
|
80
|
+
}) : /* @__PURE__ */ l("span", {
|
|
80
81
|
className: "stat-card__value-row",
|
|
81
|
-
children: [/* @__PURE__ */
|
|
82
|
-
className:
|
|
83
|
-
children:
|
|
84
|
-
}),
|
|
82
|
+
children: [/* @__PURE__ */ c("span", {
|
|
83
|
+
className: F,
|
|
84
|
+
children: P
|
|
85
|
+
}), s && /* @__PURE__ */ c("span", {
|
|
85
86
|
className: "stat-card__value-suffix",
|
|
86
|
-
children:
|
|
87
|
+
children: s
|
|
87
88
|
})]
|
|
88
|
-
}),
|
|
89
|
-
className: `stat-card__trend stat-card__trend--${
|
|
90
|
-
"aria-label":
|
|
91
|
-
children: [/* @__PURE__ */
|
|
89
|
+
}), g && !v && /* @__PURE__ */ l("span", {
|
|
90
|
+
className: `stat-card__trend stat-card__trend--${g.direction}`,
|
|
91
|
+
"aria-label": g.ariaLabel ?? g.label,
|
|
92
|
+
children: [/* @__PURE__ */ c("span", {
|
|
92
93
|
"aria-hidden": "true",
|
|
93
|
-
children:
|
|
94
|
-
}), /* @__PURE__ */
|
|
94
|
+
children: f[g.direction]
|
|
95
|
+
}), /* @__PURE__ */ c("span", { children: g.label })]
|
|
95
96
|
})]
|
|
96
97
|
}),
|
|
97
|
-
|
|
98
|
+
u != null && /* @__PURE__ */ c("div", {
|
|
98
99
|
className: "stat-card__hint",
|
|
99
|
-
children:
|
|
100
|
+
children: v ? /* @__PURE__ */ c(r, {
|
|
100
101
|
width: "80%",
|
|
101
102
|
height: "0.85rem"
|
|
102
|
-
}) :
|
|
103
|
+
}) : u
|
|
103
104
|
})
|
|
104
105
|
]
|
|
105
106
|
});
|
|
106
107
|
}
|
|
107
|
-
var
|
|
108
|
+
var h = i(m);
|
|
108
109
|
//#endregion
|
|
109
|
-
export {
|
|
110
|
+
export { h as StatCard };
|
|
110
111
|
|
|
111
112
|
//# sourceMappingURL=StatCard2.js.map
|
|
112
113
|
import "../../assets/components/StatCard/StatCard.css";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StatCard2.js","names":[],"sources":["../../../src/components/StatCard/StatCard.tsx"],"sourcesContent":["/**\n * StatCard Component\n *\n * Compact metric card for dashboards. Renders a label, a large numeric\n * value (with optional unit/suffix), and optional icon, hint, trend, or\n * accent tone. The label can be a node (`labelNode`) while the accessible name\n * stays the plain `label` string. Wraps `BaseCard` so it inherits hover/focus/click affordances\n * and design tokens consistently with the rest of the common library.\n *\n * Designed for cross-page reuse: Squad dashboard, Statistics page, Cowork\n * dashboard, etc.\n */\n\nimport { memo, useEffect, useRef, useState, type ReactNode, type JSX } from \"react\";\nimport { usePrefersReducedMotion } from \"../../hooks/usePrefersReducedMotion\";\nimport { BaseCard } from \"../BaseCard\";\nimport { Skeleton } from \"../Skeleton\";\nimport \"./StatCard.css\";\n\nexport type StatCardEmphasis = \"compact\" | \"default\" | \"prominent\";\n\nexport type StatCardTone = \"default\" | \"success\" | \"warning\" | \"danger\" | \"info\";\n\nexport type StatCardTrendDirection = \"up\" | \"down\" | \"flat\";\n\nexport interface StatCardTrend {\n /** Direction indicator (up / down / flat) */\n direction: StatCardTrendDirection;\n /** Human-readable delta label (e.g. \"+12%\", \"-3\", \"0\") */\n label: string;\n /** Optional ARIA description for screen readers */\n ariaLabel?: string;\n}\n\nexport interface StatCardProps {\n /** Short label rendered above the value (UPPERCASE styled) */\n label: string;\n /**\n * Rendered in place of `label` when the label is not plain text: a glossary\n * term carrying its definition, a unit badge, an info affordance.\n *\n * `label` stays required and stays the accessible name, so the name is a\n * string the consumer wrote rather than whatever text happens to fall out of\n * a node. Pass the same words in both.\n */\n labelNode?: ReactNode;\n /** Primary metric value (number is coerced to localised string) */\n value: string | number;\n /** Optional suffix appended to the value (e.g. \"/ 4\", \"GB\") */\n valueSuffix?: string;\n /** Optional helper line under the value (e.g. context, secondary metric) */\n hint?: ReactNode;\n /** Optional leading icon node, rendered inside an accent badge */\n icon?: ReactNode;\n /** Visual tone, mirrors Badge variants. Default: \"default\" */\n tone?: StatCardTone;\n /** Optional trend indicator under the value */\n trend?: StatCardTrend;\n /** Click handler. Renders the card as keyboard-accessible button. */\n onClick?: () => void;\n /** Render skeletons instead of value/label content while loading */\n loading?: boolean;\n /** Optional aria label override (defaults to label + value) */\n ariaLabel?: string;\n /** Extra class name applied to the root */\n className?: string;\n /** Test id for unit tests */\n testId?: string;\n /**\n * Formatter for numeric values (e.g. abbreviate to \"1.2K\", append \"%\").\n * Defaults to `toLocaleString`. Ignored when `value` is a string.\n */\n format?: (value: number) => string;\n /**\n * Count up to a numeric value on mount and on change. Off by default so\n * existing consumers render the final value immediately; opt in for\n * dashboard hero metrics. Suppressed under `prefers-reduced-motion`.\n */\n animate?: boolean;\n /**\n * Optional trailing visual on the value line (e.g. a sparkline). Kept as a\n * slot so the card does not depend on any particular chart component.\n */\n sparkline?: ReactNode;\n /** Visual weight. Default: \"default\" */\n emphasis?: StatCardEmphasis;\n}\n\nconst ANIMATION_DURATION_MS = 800;\n\n/**\n * Counts from the previous value to `target` with an ease-out-cubic curve.\n * When `enabled` is false the target is returned directly and no animation\n * frame is ever scheduled.\n */\nfunction useAnimatedValue(target: number, enabled: boolean): number {\n const [displayValue, setDisplayValue] = useState(enabled ? 0 : target);\n const previousValue = useRef(enabled ? 0 : target);\n const frameRef = useRef<number | null>(null);\n\n useEffect(() => {\n if (!enabled) {\n previousValue.current = target;\n setDisplayValue(target);\n return;\n }\n\n const startValue = previousValue.current;\n const startTime = performance.now();\n\n const step = (now: number): void => {\n const progress = Math.min((now - startTime) / ANIMATION_DURATION_MS, 1);\n const eased = 1 - Math.pow(1 - progress, 3);\n setDisplayValue(Math.round(startValue + (target - startValue) * eased));\n\n if (progress < 1) {\n frameRef.current = requestAnimationFrame(step);\n } else {\n previousValue.current = target;\n }\n };\n\n frameRef.current = requestAnimationFrame(step);\n\n return () => {\n if (frameRef.current !== null) {\n cancelAnimationFrame(frameRef.current);\n }\n };\n }, [target, enabled]);\n\n return enabled ? displayValue : target;\n}\n\nconst TREND_GLYPH: Record<StatCardTrendDirection, string> = {\n up: \"▲\",\n down: \"▼\",\n flat: \"•\",\n};\n\nfunction formatValue(\n value: string | number,\n format?: (value: number) => string,\n): string {\n if (typeof value === \"number\") {\n return format ? format(value) : value.toLocaleString();\n }\n return value;\n}\n\nfunction StatCardComponent({\n label,\n labelNode,\n value,\n valueSuffix,\n hint,\n icon,\n tone = \"default\",\n trend,\n onClick,\n loading = false,\n ariaLabel,\n className = \"\",\n testId,\n format,\n animate = false,\n sparkline,\n emphasis = \"default\",\n}: StatCardProps): JSX.Element {\n const prefersReducedMotion = usePrefersReducedMotion();\n const isNumeric = typeof value === \"number\";\n const shouldAnimate = animate && isNumeric && !loading && !prefersReducedMotion;\n const animatedValue = useAnimatedValue(isNumeric ? value : 0, shouldAnimate);\n\n const displayValue = formatValue(\n isNumeric && shouldAnimate ? animatedValue : value,\n format,\n );\n\n // The aria-label always describes the settled value, never an in-flight\n // animation frame, so assistive tech is not read a counting-up number.\n const composedAriaLabel =\n ariaLabel ??\n (loading ? label : `${label}: ${formatValue(value, format)}${valueSuffix ?? \"\"}`);\n\n const rootClass = [\n \"stat-card\",\n \"corner-accent\",\n `stat-card--tone-${tone}`,\n emphasis !== \"default\" ? `stat-card--${emphasis}` : \"\",\n className,\n ]\n .filter(Boolean)\n .join(\" \");\n\n return (\n <BaseCard\n className={rootClass}\n onClick={onClick}\n role={onClick ? undefined : \"group\"}\n ariaLabel={composedAriaLabel}\n testId={testId}\n >\n <div className=\"stat-card__header\">\n <span className=\"stat-card__label\">{labelNode ?? label}</span>\n {icon && (\n <span className=\"stat-card__icon\" aria-hidden=\"true\">\n {icon}\n </span>\n )}\n </div>\n\n <div className=\"stat-card__body\">\n {loading ? (\n <Skeleton width=\"60%\" height=\"2rem\" />\n ) : sparkline ? (\n // Only wrap when a sparkline is present, so cards without one keep\n // the exact DOM they had before the slot existed.\n <span className=\"stat-card__value-line\">\n <span className=\"stat-card__value-row\">\n <span className=\"stat-card__value\">{displayValue}</span>\n {valueSuffix && (\n <span className=\"stat-card__value-suffix\">{valueSuffix}</span>\n )}\n </span>\n <span className=\"stat-card__sparkline\">{sparkline}</span>\n </span>\n ) : (\n <span className=\"stat-card__value-row\">\n <span className=\"stat-card__value\">{displayValue}</span>\n {valueSuffix && (\n <span className=\"stat-card__value-suffix\">{valueSuffix}</span>\n )}\n </span>\n )}\n\n {trend && !loading && (\n <span\n className={`stat-card__trend stat-card__trend--${trend.direction}`}\n aria-label={trend.ariaLabel ?? trend.label}\n >\n <span aria-hidden=\"true\">{TREND_GLYPH[trend.direction]}</span>\n <span>{trend.label}</span>\n </span>\n )}\n </div>\n\n {hint !== undefined && hint !== null && (\n <div className=\"stat-card__hint\">\n {loading ? <Skeleton width=\"80%\" height=\"0.85rem\" /> : hint}\n </div>\n )}\n </BaseCard>\n );\n}\n\nexport const StatCard = memo(StatCardComponent);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"StatCard2.js","names":[],"sources":["../../../src/components/StatCard/StatCard.tsx"],"sourcesContent":["/**\n * StatCard Component\n *\n * Compact metric card for dashboards. Renders a label, a large numeric\n * value (with optional unit/suffix), and optional icon, hint, trend, or\n * accent tone. The label can be a node (`labelNode`) while the accessible name\n * stays the plain `label` string. Wraps `BaseCard` so it inherits hover/focus/click affordances\n * and design tokens consistently with the rest of the common library.\n *\n * Designed for cross-page reuse: Squad dashboard, Statistics page, Cowork\n * dashboard, etc.\n */\n\nimport { memo, useEffect, useRef, useState, type ReactNode, type JSX } from \"react\";\nimport { usePrefersReducedMotion } from \"../../hooks/usePrefersReducedMotion\";\nimport { BaseCard } from \"../BaseCard\";\nimport { DigitPopIn } from \"../DigitPopIn\";\nimport { Skeleton } from \"../Skeleton\";\nimport \"./StatCard.css\";\n\nexport type StatCardEmphasis = \"compact\" | \"default\" | \"prominent\";\n\nexport type StatCardTone = \"default\" | \"success\" | \"warning\" | \"danger\" | \"info\";\n\nexport type StatCardTrendDirection = \"up\" | \"down\" | \"flat\";\n\n/**\n * How a numeric value arrives. `count` counts up from the previous value;\n * `digits` pops each character of the formatted value in, staggered, and\n * replays when the value changes.\n */\nexport type StatCardAnimation = \"count\" | \"digits\";\n\nexport interface StatCardTrend {\n /** Direction indicator (up / down / flat) */\n direction: StatCardTrendDirection;\n /** Human-readable delta label (e.g. \"+12%\", \"-3\", \"0\") */\n label: string;\n /** Optional ARIA description for screen readers */\n ariaLabel?: string;\n}\n\nexport interface StatCardProps {\n /** Short label rendered above the value (UPPERCASE styled) */\n label: string;\n /**\n * Rendered in place of `label` when the label is not plain text: a glossary\n * term carrying its definition, a unit badge, an info affordance.\n *\n * `label` stays required and stays the accessible name, so the name is a\n * string the consumer wrote rather than whatever text happens to fall out of\n * a node. Pass the same words in both.\n */\n labelNode?: ReactNode;\n /** Primary metric value (number is coerced to localised string) */\n value: string | number;\n /** Optional suffix appended to the value (e.g. \"/ 4\", \"GB\") */\n valueSuffix?: string;\n /** Optional helper line under the value (e.g. context, secondary metric) */\n hint?: ReactNode;\n /** Optional leading icon node, rendered inside an accent badge */\n icon?: ReactNode;\n /** Visual tone, mirrors Badge variants. Default: \"default\" */\n tone?: StatCardTone;\n /** Optional trend indicator under the value */\n trend?: StatCardTrend;\n /** Click handler. Renders the card as keyboard-accessible button. */\n onClick?: () => void;\n /** Render skeletons instead of value/label content while loading */\n loading?: boolean;\n /** Optional aria label override (defaults to label + value) */\n ariaLabel?: string;\n /** Extra class name applied to the root */\n className?: string;\n /** Test id for unit tests */\n testId?: string;\n /**\n * Formatter for numeric values (e.g. abbreviate to \"1.2K\", append \"%\").\n * Defaults to `toLocaleString`. Ignored when `value` is a string.\n */\n format?: (value: number) => string;\n /**\n * Animate a numeric value on mount and on change. `true` or `\"count\"`\n * counts up to it; `\"digits\"` pops each character of the formatted value\n * in, staggered, rising into place from a light blur. Off by default so\n * existing consumers render the final value immediately; opt in for\n * dashboard metrics. Suppressed under `prefers-reduced-motion`, and ignored\n * for string values and while loading.\n */\n animate?: boolean | StatCardAnimation;\n /**\n * Optional trailing visual on the value line (e.g. a sparkline). Kept as a\n * slot so the card does not depend on any particular chart component.\n */\n sparkline?: ReactNode;\n /** Visual weight. Default: \"default\" */\n emphasis?: StatCardEmphasis;\n}\n\nconst ANIMATION_DURATION_MS = 800;\n\n/**\n * Counts from the previous value to `target` with an ease-out-cubic curve.\n * When `enabled` is false the target is returned directly and no animation\n * frame is ever scheduled.\n */\nfunction useAnimatedValue(target: number, enabled: boolean): number {\n const [displayValue, setDisplayValue] = useState(enabled ? 0 : target);\n const previousValue = useRef(enabled ? 0 : target);\n const frameRef = useRef<number | null>(null);\n\n useEffect(() => {\n if (!enabled) {\n previousValue.current = target;\n setDisplayValue(target);\n return;\n }\n\n const startValue = previousValue.current;\n const startTime = performance.now();\n\n const step = (now: number): void => {\n const progress = Math.min((now - startTime) / ANIMATION_DURATION_MS, 1);\n const eased = 1 - Math.pow(1 - progress, 3);\n setDisplayValue(Math.round(startValue + (target - startValue) * eased));\n\n if (progress < 1) {\n frameRef.current = requestAnimationFrame(step);\n } else {\n previousValue.current = target;\n }\n };\n\n frameRef.current = requestAnimationFrame(step);\n\n return () => {\n if (frameRef.current !== null) {\n cancelAnimationFrame(frameRef.current);\n }\n };\n }, [target, enabled]);\n\n return enabled ? displayValue : target;\n}\n\nconst TREND_GLYPH: Record<StatCardTrendDirection, string> = {\n up: \"▲\",\n down: \"▼\",\n flat: \"•\",\n};\n\nfunction formatValue(\n value: string | number,\n format?: (value: number) => string,\n): string {\n if (typeof value === \"number\") {\n return format ? format(value) : value.toLocaleString();\n }\n return value;\n}\n\nfunction StatCardComponent({\n label,\n labelNode,\n value,\n valueSuffix,\n hint,\n icon,\n tone = \"default\",\n trend,\n onClick,\n loading = false,\n ariaLabel,\n className = \"\",\n testId,\n format,\n animate = false,\n sparkline,\n emphasis = \"default\",\n}: StatCardProps): JSX.Element {\n const prefersReducedMotion = usePrefersReducedMotion();\n const isNumeric = typeof value === \"number\";\n const animation: StatCardAnimation | null =\n animate === true ? \"count\" : animate === false ? null : animate;\n const canAnimate = isNumeric && !loading && !prefersReducedMotion;\n const shouldCount = animation === \"count\" && canAnimate;\n const shouldPopDigits = animation === \"digits\" && canAnimate;\n const animatedValue = useAnimatedValue(isNumeric ? value : 0, shouldCount);\n\n const displayValue = formatValue(\n isNumeric && shouldCount ? animatedValue : value,\n format,\n );\n const valueContent = shouldPopDigits ? (\n <DigitPopIn text={displayValue} />\n ) : (\n displayValue\n );\n const valueClass = shouldPopDigits\n ? \"stat-card__value stat-card__value--digits\"\n : \"stat-card__value\";\n\n // The aria-label always describes the settled value, never an in-flight\n // animation frame, so assistive tech is not read a counting-up number.\n const composedAriaLabel =\n ariaLabel ??\n (loading ? label : `${label}: ${formatValue(value, format)}${valueSuffix ?? \"\"}`);\n\n const rootClass = [\n \"stat-card\",\n \"corner-accent\",\n `stat-card--tone-${tone}`,\n emphasis !== \"default\" ? `stat-card--${emphasis}` : \"\",\n className,\n ]\n .filter(Boolean)\n .join(\" \");\n\n return (\n <BaseCard\n className={rootClass}\n onClick={onClick}\n role={onClick ? undefined : \"group\"}\n ariaLabel={composedAriaLabel}\n testId={testId}\n >\n <div className=\"stat-card__header\">\n <span className=\"stat-card__label\">{labelNode ?? label}</span>\n {icon && (\n <span className=\"stat-card__icon\" aria-hidden=\"true\">\n {icon}\n </span>\n )}\n </div>\n\n <div className=\"stat-card__body\">\n {loading ? (\n <Skeleton width=\"60%\" height=\"2rem\" />\n ) : sparkline ? (\n // Only wrap when a sparkline is present, so cards without one keep\n // the exact DOM they had before the slot existed.\n <span className=\"stat-card__value-line\">\n <span className=\"stat-card__value-row\">\n <span className={valueClass}>{valueContent}</span>\n {valueSuffix && (\n <span className=\"stat-card__value-suffix\">{valueSuffix}</span>\n )}\n </span>\n <span className=\"stat-card__sparkline\">{sparkline}</span>\n </span>\n ) : (\n <span className=\"stat-card__value-row\">\n <span className={valueClass}>{valueContent}</span>\n {valueSuffix && (\n <span className=\"stat-card__value-suffix\">{valueSuffix}</span>\n )}\n </span>\n )}\n\n {trend && !loading && (\n <span\n className={`stat-card__trend stat-card__trend--${trend.direction}`}\n aria-label={trend.ariaLabel ?? trend.label}\n >\n <span aria-hidden=\"true\">{TREND_GLYPH[trend.direction]}</span>\n <span>{trend.label}</span>\n </span>\n )}\n </div>\n\n {hint !== undefined && hint !== null && (\n <div className=\"stat-card__hint\">\n {loading ? <Skeleton width=\"80%\" height=\"0.85rem\" /> : hint}\n </div>\n )}\n </BaseCard>\n );\n}\n\nexport const StatCard = memo(StatCardComponent);\n"],"mappings":"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"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { StatCard } from './StatCard';
|
|
2
2
|
export { formatCompactNumber } from './formatters';
|
|
3
|
-
export type { StatCardProps, StatCardEmphasis, StatCardTone, StatCardTrend, StatCardTrendDirection, } from './StatCard';
|
|
3
|
+
export type { StatCardProps, StatCardAnimation, StatCardEmphasis, StatCardTone, StatCardTrend, StatCardTrendDirection, } from './StatCard';
|
package/dist/index.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export * from './components/BaseCard';
|
|
|
16
16
|
export * from './components/Badge';
|
|
17
17
|
export * from './components/Button';
|
|
18
18
|
export * from './components/DataTable';
|
|
19
|
+
export * from './components/DigitPopIn';
|
|
19
20
|
export * from './components/Drawer';
|
|
20
21
|
export * from './components/EmptyState';
|
|
21
22
|
export * from './components/ErrorState';
|
package/dist/index.js
CHANGED
|
@@ -2,23 +2,24 @@ import { BaseCard as e } from "./components/BaseCard/BaseCard2.js";
|
|
|
2
2
|
import { Badge as t } from "./components/Badge/Badge2.js";
|
|
3
3
|
import { Button as n } from "./components/Button/Button2.js";
|
|
4
4
|
import { DataTable as r } from "./components/DataTable/DataTable2.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
|
|
5
|
+
import { usePrefersReducedMotion as i } from "./hooks/usePrefersReducedMotion.js";
|
|
6
|
+
import { DigitPopIn as a } from "./components/DigitPopIn/DigitPopIn2.js";
|
|
7
|
+
import { Drawer as o } from "./components/Drawer/Drawer2.js";
|
|
8
|
+
import { EmptyState as s } from "./components/EmptyState/EmptyState2.js";
|
|
9
|
+
import { ErrorState as c } from "./components/ErrorState/ErrorState2.js";
|
|
10
|
+
import { PageHeader as l } from "./components/PageHeader/PageHeader2.js";
|
|
11
|
+
import { PageLayout as u } from "./components/PageLayout/PageLayout2.js";
|
|
12
|
+
import { ProgressBar as d } from "./components/ProgressBar/ProgressBar2.js";
|
|
13
|
+
import { Skeleton as f } from "./components/Skeleton/Skeleton2.js";
|
|
14
|
+
import { SkeletonCard as p } from "./components/Skeleton/SkeletonCard2.js";
|
|
15
|
+
import { SkeletonText as m } from "./components/Skeleton/SkeletonText2.js";
|
|
16
|
+
import { SkeletonChart as h } from "./components/Skeleton/SkeletonChart2.js";
|
|
17
|
+
import { SkeletonRow as g } from "./components/Skeleton/SkeletonRow2.js";
|
|
18
|
+
import { SmoothHeight as _ } from "./components/SmoothHeight/SmoothHeight2.js";
|
|
19
|
+
import { Select as v } from "./components/Select/Select2.js";
|
|
20
|
+
import { StatCard as y } from "./components/StatCard/StatCard2.js";
|
|
21
|
+
import { formatCompactNumber as b } from "./components/StatCard/formatters.js";
|
|
22
|
+
import { StatusTag as x } from "./components/StatusTag/StatusTag2.js";
|
|
23
|
+
import { Tabs as S } from "./components/Tabs/Tabs2.js";
|
|
24
|
+
import { Tooltip as C } from "./components/Tooltip/Tooltip2.js";
|
|
25
|
+
export { t as Badge, e as BaseCard, n as Button, r as DataTable, a as DigitPopIn, o as Drawer, s as EmptyState, c as ErrorState, l as PageHeader, u as PageLayout, d as ProgressBar, v as Select, f as Skeleton, p as SkeletonCard, h as SkeletonChart, g as SkeletonRow, m as SkeletonText, _ as SmoothHeight, y as StatCard, x as StatusTag, S as Tabs, C as Tooltip, b as formatCompactNumber, i as usePrefersReducedMotion };
|
package/package.json
CHANGED