@machinemetrics/mm-react-components 0.2.3-13 → 0.2.3-15
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/README.md +31 -1
- package/agent-docs/agent-documentation-reference.md +114 -0
- package/agent-docs/ai-agent-guide.md +5 -1
- package/agent-docs/ai-agent-init-guide.md +5 -1
- package/dist/README.md +31 -1
- package/dist/components/ui/hero-metric-card/HeroMetricCard.d.ts +4 -0
- package/dist/components/ui/hero-metric-card/HeroMetricCard.d.ts.map +1 -0
- package/dist/components/ui/hero-metric-card/HeroMetricCardItem.d.ts +4 -0
- package/dist/components/ui/hero-metric-card/HeroMetricCardItem.d.ts.map +1 -0
- package/dist/components/ui/hero-metric-card/constants.d.ts +13 -0
- package/dist/components/ui/hero-metric-card/constants.d.ts.map +1 -0
- package/dist/components/ui/hero-metric-card/grid.d.ts +4 -0
- package/dist/components/ui/hero-metric-card/grid.d.ts.map +1 -0
- package/dist/components/ui/hero-metric-card/hooks.d.ts +6 -0
- package/dist/components/ui/hero-metric-card/hooks.d.ts.map +1 -0
- package/dist/components/ui/hero-metric-card/index.d.ts +5 -0
- package/dist/components/ui/hero-metric-card/index.d.ts.map +1 -0
- package/dist/components/ui/hero-metric-card/parsing.d.ts +11 -0
- package/dist/components/ui/hero-metric-card/parsing.d.ts.map +1 -0
- package/dist/components/ui/hero-metric-card/refs.d.ts +3 -0
- package/dist/components/ui/hero-metric-card/refs.d.ts.map +1 -0
- package/dist/components/ui/hero-metric-card/trend.d.ts +4 -0
- package/dist/components/ui/hero-metric-card/trend.d.ts.map +1 -0
- package/dist/components/ui/hero-metric-card/types.d.ts +60 -0
- package/dist/components/ui/hero-metric-card/types.d.ts.map +1 -0
- package/dist/components/ui/hero-metric-card/utils.d.ts +10 -0
- package/dist/components/ui/hero-metric-card/utils.d.ts.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/lib/mm-react-components.css +1 -1
- package/dist/mm-react-components.es.js +461 -2
- package/dist/mm-react-components.es.js.map +1 -1
- package/dist/mm-react-components.umd.js +10 -10
- package/dist/mm-react-components.umd.js.map +1 -1
- package/dist/preview/HeroMetricCardPreview.d.ts +2 -0
- package/dist/preview/HeroMetricCardPreview.d.ts.map +1 -0
- package/dist/scripts/chakra-to-shadcn-migrator/chakra-to-shadcn.config.json +130 -65
- package/dist/themes/carbide.css +23 -3
- package/package.json +1 -1
|
@@ -17592,7 +17592,7 @@ function LabeledSlider({
|
|
|
17592
17592
|
},
|
|
17593
17593
|
[onValueChange]
|
|
17594
17594
|
);
|
|
17595
|
-
const
|
|
17595
|
+
const renderValue2 = React.useMemo(() => {
|
|
17596
17596
|
if (formatValue) return formatValue(displayValue);
|
|
17597
17597
|
return displayValue.length === 1 ? displayValue[0] : `${displayValue[0]} - ${displayValue[1]}`;
|
|
17598
17598
|
}, [displayValue, formatValue]);
|
|
@@ -17636,7 +17636,7 @@ function LabeledSlider({
|
|
|
17636
17636
|
{
|
|
17637
17637
|
"data-slot": "slider-value",
|
|
17638
17638
|
className: "text-sm text-muted-foreground tabular-nums whitespace-nowrap",
|
|
17639
|
-
children:
|
|
17639
|
+
children: renderValue2
|
|
17640
17640
|
}
|
|
17641
17641
|
)
|
|
17642
17642
|
]
|
|
@@ -43619,6 +43619,463 @@ function BreadcrumbEllipsis({
|
|
|
43619
43619
|
}
|
|
43620
43620
|
);
|
|
43621
43621
|
}
|
|
43622
|
+
const HERO_METRIC_CONSTANTS = {
|
|
43623
|
+
MIN_ITEM_WIDTH: 160,
|
|
43624
|
+
MIN_ITEMS: 1,
|
|
43625
|
+
MAX_ITEMS: 6,
|
|
43626
|
+
WRAPPED_GAP_DEFAULT: "gap-4",
|
|
43627
|
+
UNWRAPPED_GAP: "gap-0",
|
|
43628
|
+
PADDING_INTERACTIVE: "px-3",
|
|
43629
|
+
PADDING_STATIC: "px-6",
|
|
43630
|
+
CONTAINER_PADDING_Y: "py-4",
|
|
43631
|
+
CONTAINER_PADDING_X: "px-0"
|
|
43632
|
+
};
|
|
43633
|
+
const GRID_COLS_MAP = {
|
|
43634
|
+
1: "grid-cols-1",
|
|
43635
|
+
2: "grid-cols-2",
|
|
43636
|
+
3: "grid-cols-3",
|
|
43637
|
+
4: "grid-cols-4",
|
|
43638
|
+
5: "grid-cols-5",
|
|
43639
|
+
6: "grid-cols-6"
|
|
43640
|
+
};
|
|
43641
|
+
function parseNumeric(input) {
|
|
43642
|
+
if (input === null || input === void 0) return void 0;
|
|
43643
|
+
if (typeof input !== "string" && typeof input !== "number") return void 0;
|
|
43644
|
+
const n = parseFloat(String(input).replace(/[^0-9.-]/g, ""));
|
|
43645
|
+
return Number.isFinite(n) ? n : void 0;
|
|
43646
|
+
}
|
|
43647
|
+
function parsePercent(value) {
|
|
43648
|
+
return parseNumeric(value);
|
|
43649
|
+
}
|
|
43650
|
+
function parseCount(value) {
|
|
43651
|
+
return parseNumeric(value);
|
|
43652
|
+
}
|
|
43653
|
+
function parseMinutes(value, unit) {
|
|
43654
|
+
if (value === null || value === void 0) return void 0;
|
|
43655
|
+
if (typeof value === "number") {
|
|
43656
|
+
if (!Number.isFinite(value)) return void 0;
|
|
43657
|
+
if (unit && /h/i.test(unit)) return value * 60;
|
|
43658
|
+
return value;
|
|
43659
|
+
}
|
|
43660
|
+
if (typeof value !== "string") return void 0;
|
|
43661
|
+
const s2 = value;
|
|
43662
|
+
const hm = /(\d+)\s*h(?:\s*(\d+)\s*m)?/i.exec(s2);
|
|
43663
|
+
if (hm) {
|
|
43664
|
+
const h = parseInt(hm[1] ?? "0", 10);
|
|
43665
|
+
const m = parseInt(hm[2] ?? "0", 10);
|
|
43666
|
+
return h * 60 + m;
|
|
43667
|
+
}
|
|
43668
|
+
const mOnly = /(\d+)\s*m/i.exec(s2);
|
|
43669
|
+
if (mOnly) return parseInt(mOnly[1] ?? "0", 10);
|
|
43670
|
+
const n = parseFloat(s2);
|
|
43671
|
+
if (Number.isFinite(n)) {
|
|
43672
|
+
if (/h/i.test(s2) || unit && /h/i.test(unit)) return n * 60;
|
|
43673
|
+
return n;
|
|
43674
|
+
}
|
|
43675
|
+
return void 0;
|
|
43676
|
+
}
|
|
43677
|
+
function formatDuration(mins) {
|
|
43678
|
+
const h = Math.floor(mins / 60);
|
|
43679
|
+
const m = Math.round(mins % 60);
|
|
43680
|
+
if (h > 0) return `${h}h ${m}m`;
|
|
43681
|
+
const s2 = Math.round((mins - m) * 60);
|
|
43682
|
+
return `${m}m ${s2}s`;
|
|
43683
|
+
}
|
|
43684
|
+
function getNumericForKind(value, unit, kind) {
|
|
43685
|
+
if (kind === "percent") return parsePercent(value);
|
|
43686
|
+
if (kind === "duration-minutes") return parseMinutes(value, unit);
|
|
43687
|
+
return parseCount(value);
|
|
43688
|
+
}
|
|
43689
|
+
function getColorIntent(direction, higherIsBetter) {
|
|
43690
|
+
if (direction === "neutral") return "neutral";
|
|
43691
|
+
if (direction === "up") return higherIsBetter ? "success" : "destructive";
|
|
43692
|
+
return higherIsBetter ? "destructive" : "success";
|
|
43693
|
+
}
|
|
43694
|
+
function getTrendText(kind, absDiff, relativePercent) {
|
|
43695
|
+
if (kind === "percent")
|
|
43696
|
+
return `${Math.abs(absDiff).toFixed(1)}% (${relativePercent}pp)`;
|
|
43697
|
+
if (kind === "duration-minutes")
|
|
43698
|
+
return `${formatDuration(Math.abs(absDiff))} (${relativePercent}%)`;
|
|
43699
|
+
return `${Math.abs(absDiff).toFixed(1)} (${relativePercent}%)`;
|
|
43700
|
+
}
|
|
43701
|
+
function computeTrend(value, unit, trendComparisonValue, higherIsBetter = true, numericValue) {
|
|
43702
|
+
if (!trendComparisonValue || typeof trendComparisonValue.value !== "number")
|
|
43703
|
+
return void 0;
|
|
43704
|
+
const currentNumeric = typeof numericValue === "number" ? numericValue : getNumericForKind(value, unit, trendComparisonValue.kind);
|
|
43705
|
+
if (currentNumeric === void 0) return void 0;
|
|
43706
|
+
const absoluteDifference = currentNumeric - trendComparisonValue.value;
|
|
43707
|
+
let direction;
|
|
43708
|
+
if (absoluteDifference > 0) direction = "up";
|
|
43709
|
+
else if (absoluteDifference < 0) direction = "down";
|
|
43710
|
+
else direction = "neutral";
|
|
43711
|
+
const colorIntent = getColorIntent(direction, higherIsBetter);
|
|
43712
|
+
const relativePercent = Math.abs(
|
|
43713
|
+
absoluteDifference / Math.max(1e-6, trendComparisonValue.value) * 100
|
|
43714
|
+
).toFixed(1);
|
|
43715
|
+
const text2 = getTrendText(
|
|
43716
|
+
trendComparisonValue.kind,
|
|
43717
|
+
Math.abs(absoluteDifference),
|
|
43718
|
+
relativePercent
|
|
43719
|
+
);
|
|
43720
|
+
return { direction, text: text2, colorIntent };
|
|
43721
|
+
}
|
|
43722
|
+
function getGridColsClass(n) {
|
|
43723
|
+
if (!n) return void 0;
|
|
43724
|
+
return GRID_COLS_MAP[n];
|
|
43725
|
+
}
|
|
43726
|
+
function resolveGridClasses(columns) {
|
|
43727
|
+
if (!columns) {
|
|
43728
|
+
return "[grid-template-columns:repeat(auto-fit,minmax(10rem,1fr))]";
|
|
43729
|
+
}
|
|
43730
|
+
if (typeof columns === "number") {
|
|
43731
|
+
return getGridColsClass(columns) ?? "grid-cols-1";
|
|
43732
|
+
}
|
|
43733
|
+
const base = getGridColsClass(columns.base) ?? "grid-cols-1";
|
|
43734
|
+
const md = columns.md ? `md:${getGridColsClass(columns.md)}` : "";
|
|
43735
|
+
const lg = columns.lg ? `lg:${getGridColsClass(columns.lg)}` : "";
|
|
43736
|
+
return cn$1(base, md, lg);
|
|
43737
|
+
}
|
|
43738
|
+
function mergeRefs(...refs) {
|
|
43739
|
+
return (node) => {
|
|
43740
|
+
refs.forEach((ref) => {
|
|
43741
|
+
if (!ref) return;
|
|
43742
|
+
if (typeof ref === "function") {
|
|
43743
|
+
ref(node);
|
|
43744
|
+
} else {
|
|
43745
|
+
ref.current = node;
|
|
43746
|
+
}
|
|
43747
|
+
});
|
|
43748
|
+
};
|
|
43749
|
+
}
|
|
43750
|
+
function formatNumber(value) {
|
|
43751
|
+
if (typeof value === "number") return value.toLocaleString();
|
|
43752
|
+
return value;
|
|
43753
|
+
}
|
|
43754
|
+
const renderLabel = (label, tooltip) => {
|
|
43755
|
+
const labelElement = /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
43756
|
+
"span",
|
|
43757
|
+
{
|
|
43758
|
+
className: cn$1(
|
|
43759
|
+
"mmc-hero-metric-card__label",
|
|
43760
|
+
"text-xs font-normal text-foreground",
|
|
43761
|
+
tooltip && "mmc-underline-dashed hover:text-foreground"
|
|
43762
|
+
),
|
|
43763
|
+
"data-slot": "metric-label",
|
|
43764
|
+
children: label
|
|
43765
|
+
}
|
|
43766
|
+
);
|
|
43767
|
+
if (!tooltip) return labelElement;
|
|
43768
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip$1, { children: [
|
|
43769
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "inline-block cursor-help", children: labelElement }) }),
|
|
43770
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: tooltip })
|
|
43771
|
+
] });
|
|
43772
|
+
};
|
|
43773
|
+
const renderValue = (value, unit, icon, loading) => {
|
|
43774
|
+
const formatDisplayValue = () => {
|
|
43775
|
+
if (value === null || value === void 0) return "—";
|
|
43776
|
+
if (typeof value === "number") return formatNumber(value);
|
|
43777
|
+
return value;
|
|
43778
|
+
};
|
|
43779
|
+
const content = loading ? /* @__PURE__ */ jsxRuntimeExports.jsx(Skeleton, { className: "h-9 w-28" }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "inline-flex items-baseline gap-0", children: [
|
|
43780
|
+
icon && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "mmc-hero-metric-card__icon", "data-slot": "metric-icon", children: icon }),
|
|
43781
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-foreground", children: formatDisplayValue() }),
|
|
43782
|
+
unit && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
43783
|
+
"span",
|
|
43784
|
+
{
|
|
43785
|
+
className: "mmc-hero-metric-card__unit text-foreground text-xl font-medium",
|
|
43786
|
+
"data-slot": "metric-unit",
|
|
43787
|
+
children: unit
|
|
43788
|
+
}
|
|
43789
|
+
)
|
|
43790
|
+
] });
|
|
43791
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
43792
|
+
"div",
|
|
43793
|
+
{
|
|
43794
|
+
className: "mmc-hero-metric-card__value text-xl font-medium text-foreground",
|
|
43795
|
+
"data-slot": "metric-value",
|
|
43796
|
+
children: content
|
|
43797
|
+
}
|
|
43798
|
+
);
|
|
43799
|
+
};
|
|
43800
|
+
const renderCaption = (caption, loading) => {
|
|
43801
|
+
if (!caption) return null;
|
|
43802
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
43803
|
+
"div",
|
|
43804
|
+
{
|
|
43805
|
+
className: "mmc-hero-metric-card__caption text-xs font-normal text-foreground",
|
|
43806
|
+
"data-slot": "metric-caption",
|
|
43807
|
+
children: loading ? /* @__PURE__ */ jsxRuntimeExports.jsx(Skeleton, { className: "h-4 w-32" }) : caption
|
|
43808
|
+
}
|
|
43809
|
+
);
|
|
43810
|
+
};
|
|
43811
|
+
const getTrendContent = (computedTrend, loading) => {
|
|
43812
|
+
if (!computedTrend) return null;
|
|
43813
|
+
if (loading) return /* @__PURE__ */ jsxRuntimeExports.jsx(Skeleton, { className: "h-3.5 w-16" });
|
|
43814
|
+
if (computedTrend.colorIntent === "neutral") {
|
|
43815
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-muted-foreground", children: "—" });
|
|
43816
|
+
}
|
|
43817
|
+
const trendColorClass = computedTrend.colorIntent === "success" ? "text-[var(--success-foreground,theme(colors.green.600))]" : "text-[var(--destructive-foreground,theme(colors.red.600))]";
|
|
43818
|
+
const ArrowIcon = computedTrend.direction === "up" ? ArrowUp : ArrowDown;
|
|
43819
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
43820
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(ArrowIcon, { className: cn$1("h-3.5 w-3.5", trendColorClass) }),
|
|
43821
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: trendColorClass, children: String(computedTrend.text).replace(/^[-+]/, "") })
|
|
43822
|
+
] });
|
|
43823
|
+
};
|
|
43824
|
+
const getInnerContainerClasses = (interactive, variant, dataState) => {
|
|
43825
|
+
return cn$1(
|
|
43826
|
+
interactive && cn$1(
|
|
43827
|
+
"px-3 py-2",
|
|
43828
|
+
"transition-colors",
|
|
43829
|
+
"rounded-lg",
|
|
43830
|
+
dataState !== "active" && "hover:bg-accent"
|
|
43831
|
+
),
|
|
43832
|
+
variant === "filter" && dataState === "active" && "bg-[var(--primary-subtle)] ring-2 ring-primary",
|
|
43833
|
+
variant === "filter" && dataState === "unfocused" && "opacity-60"
|
|
43834
|
+
);
|
|
43835
|
+
};
|
|
43836
|
+
const HeroMetricCardItem = React.forwardRef(
|
|
43837
|
+
({
|
|
43838
|
+
label,
|
|
43839
|
+
value,
|
|
43840
|
+
unit,
|
|
43841
|
+
caption,
|
|
43842
|
+
tooltip,
|
|
43843
|
+
icon,
|
|
43844
|
+
loading,
|
|
43845
|
+
className,
|
|
43846
|
+
interactive = false,
|
|
43847
|
+
pressed = false,
|
|
43848
|
+
onActivate,
|
|
43849
|
+
onClick,
|
|
43850
|
+
trendComparisonValue,
|
|
43851
|
+
higherIsBetter = true,
|
|
43852
|
+
"data-variant": variant,
|
|
43853
|
+
"data-state": dataState,
|
|
43854
|
+
...rest
|
|
43855
|
+
}, ref) => {
|
|
43856
|
+
const isInteractiveVariant = variant === "button" || variant === "filter";
|
|
43857
|
+
const outerContainer = cn$1(
|
|
43858
|
+
"mmc-hero-metric-card__item",
|
|
43859
|
+
isInteractiveVariant ? HERO_METRIC_CONSTANTS.PADDING_INTERACTIVE : HERO_METRIC_CONSTANTS.PADDING_STATIC,
|
|
43860
|
+
"items-start text-left",
|
|
43861
|
+
className
|
|
43862
|
+
);
|
|
43863
|
+
const innerContainer = getInnerContainerClasses(
|
|
43864
|
+
interactive,
|
|
43865
|
+
variant,
|
|
43866
|
+
dataState
|
|
43867
|
+
);
|
|
43868
|
+
const computedTrend = computeTrend(
|
|
43869
|
+
value,
|
|
43870
|
+
unit,
|
|
43871
|
+
trendComparisonValue,
|
|
43872
|
+
higherIsBetter,
|
|
43873
|
+
rest.numericValue
|
|
43874
|
+
);
|
|
43875
|
+
const trendContent = getTrendContent(computedTrend, loading);
|
|
43876
|
+
const trend = trendContent ? /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
43877
|
+
"div",
|
|
43878
|
+
{
|
|
43879
|
+
className: "mmc-hero-metric-card__trend text-xs font-normal inline-flex items-center gap-0.5",
|
|
43880
|
+
"data-slot": "metric-trend",
|
|
43881
|
+
children: trendContent
|
|
43882
|
+
}
|
|
43883
|
+
) : null;
|
|
43884
|
+
const body = /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1.5", children: [
|
|
43885
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "pb-0.5", children: renderLabel(label, tooltip) }),
|
|
43886
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
43887
|
+
renderValue(value, unit, icon, loading),
|
|
43888
|
+
trend,
|
|
43889
|
+
renderCaption(caption, loading)
|
|
43890
|
+
] })
|
|
43891
|
+
] });
|
|
43892
|
+
if (interactive) {
|
|
43893
|
+
const handleClick = variant === "button" && onClick ? onClick : onActivate;
|
|
43894
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
43895
|
+
"button",
|
|
43896
|
+
{
|
|
43897
|
+
ref,
|
|
43898
|
+
type: "button",
|
|
43899
|
+
className: cn$1(outerContainer, "focus-visible:outline-none"),
|
|
43900
|
+
onClick: handleClick,
|
|
43901
|
+
"aria-pressed": variant === "filter" ? pressed : void 0,
|
|
43902
|
+
"data-slot": "metric-item",
|
|
43903
|
+
"data-variant": variant,
|
|
43904
|
+
"data-state": dataState,
|
|
43905
|
+
...rest,
|
|
43906
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: innerContainer, children: body })
|
|
43907
|
+
}
|
|
43908
|
+
);
|
|
43909
|
+
}
|
|
43910
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
43911
|
+
"div",
|
|
43912
|
+
{
|
|
43913
|
+
ref,
|
|
43914
|
+
className: outerContainer,
|
|
43915
|
+
"data-slot": "metric-item",
|
|
43916
|
+
...rest,
|
|
43917
|
+
children: body
|
|
43918
|
+
}
|
|
43919
|
+
);
|
|
43920
|
+
}
|
|
43921
|
+
);
|
|
43922
|
+
HeroMetricCardItem.displayName = "HeroMetricCardItem";
|
|
43923
|
+
function useWrappedDetection(itemCount, containerRef, children) {
|
|
43924
|
+
const [hasWrapped, setHasWrapped] = React.useState(false);
|
|
43925
|
+
React.useEffect(() => {
|
|
43926
|
+
if (!containerRef.current) return;
|
|
43927
|
+
const observer = new ResizeObserver(() => {
|
|
43928
|
+
if (!containerRef.current) return;
|
|
43929
|
+
const count2 = itemCount || React.Children.count(children);
|
|
43930
|
+
if (count2 === 0) return;
|
|
43931
|
+
const containerWidth = containerRef.current.offsetWidth;
|
|
43932
|
+
const totalMinWidth = count2 * HERO_METRIC_CONSTANTS.MIN_ITEM_WIDTH;
|
|
43933
|
+
setHasWrapped(containerWidth < totalMinWidth);
|
|
43934
|
+
});
|
|
43935
|
+
observer.observe(containerRef.current);
|
|
43936
|
+
return () => observer.disconnect();
|
|
43937
|
+
}, [itemCount, containerRef, children]);
|
|
43938
|
+
return hasWrapped;
|
|
43939
|
+
}
|
|
43940
|
+
function getItemProps(_idx, itemId, _itemCount, mode, activeId, handleItemActivate) {
|
|
43941
|
+
const isFilter = mode === "filter";
|
|
43942
|
+
const isButton = mode === "button";
|
|
43943
|
+
const isActive = isFilter && activeId === itemId;
|
|
43944
|
+
let dataState;
|
|
43945
|
+
if (isFilter) {
|
|
43946
|
+
if (isActive) {
|
|
43947
|
+
dataState = "active";
|
|
43948
|
+
} else if (activeId) {
|
|
43949
|
+
dataState = "unfocused";
|
|
43950
|
+
} else {
|
|
43951
|
+
dataState = "default";
|
|
43952
|
+
}
|
|
43953
|
+
}
|
|
43954
|
+
return {
|
|
43955
|
+
"data-variant": mode,
|
|
43956
|
+
"data-state": dataState,
|
|
43957
|
+
onActivate: isFilter ? () => handleItemActivate(itemId) : void 0,
|
|
43958
|
+
interactive: isFilter || isButton,
|
|
43959
|
+
pressed: isFilter ? isActive : false
|
|
43960
|
+
};
|
|
43961
|
+
}
|
|
43962
|
+
const HeroMetricCard = React.forwardRef(
|
|
43963
|
+
({
|
|
43964
|
+
children,
|
|
43965
|
+
items,
|
|
43966
|
+
mode = "static",
|
|
43967
|
+
activeId: controlledActiveId,
|
|
43968
|
+
defaultActiveId = null,
|
|
43969
|
+
onActiveChange,
|
|
43970
|
+
columns,
|
|
43971
|
+
showTrends: _showTrends = true,
|
|
43972
|
+
loading = false,
|
|
43973
|
+
className,
|
|
43974
|
+
"data-testid": dataTestId = "hero-metric-card",
|
|
43975
|
+
...rest
|
|
43976
|
+
}, ref) => {
|
|
43977
|
+
const isFilter = mode === "filter";
|
|
43978
|
+
const [uncontrolledActive, setUncontrolledActive] = React.useState(defaultActiveId ?? null);
|
|
43979
|
+
const activeId = controlledActiveId ?? uncontrolledActive;
|
|
43980
|
+
const containerRef = React.useRef(null);
|
|
43981
|
+
const itemCount = items?.length ?? React.Children.count(children);
|
|
43982
|
+
const hasWrapped = useWrappedDetection(itemCount, containerRef, children);
|
|
43983
|
+
const handleItemActivate = (id) => {
|
|
43984
|
+
if (isFilter) {
|
|
43985
|
+
const next = activeId === id ? null : id;
|
|
43986
|
+
if (controlledActiveId === void 0) setUncontrolledActive(next);
|
|
43987
|
+
onActiveChange?.(next);
|
|
43988
|
+
}
|
|
43989
|
+
};
|
|
43990
|
+
const renderFromItems = (src) => {
|
|
43991
|
+
const normalized = src.slice(0, HERO_METRIC_CONSTANTS.MAX_ITEMS);
|
|
43992
|
+
if (normalized.length < HERO_METRIC_CONSTANTS.MIN_ITEMS) {
|
|
43993
|
+
return null;
|
|
43994
|
+
}
|
|
43995
|
+
return normalized.map((it, idx) => {
|
|
43996
|
+
const itemId = it.id ?? String(idx);
|
|
43997
|
+
const itemProps = getItemProps(
|
|
43998
|
+
idx,
|
|
43999
|
+
itemId,
|
|
44000
|
+
normalized.length,
|
|
44001
|
+
mode,
|
|
44002
|
+
activeId,
|
|
44003
|
+
handleItemActivate
|
|
44004
|
+
);
|
|
44005
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
44006
|
+
HeroMetricCardItem,
|
|
44007
|
+
{
|
|
44008
|
+
...it,
|
|
44009
|
+
trendComparisonValue: _showTrends ? it.trendComparisonValue : void 0,
|
|
44010
|
+
loading,
|
|
44011
|
+
...itemProps
|
|
44012
|
+
},
|
|
44013
|
+
itemId
|
|
44014
|
+
);
|
|
44015
|
+
});
|
|
44016
|
+
};
|
|
44017
|
+
const renderFromChildren = (nodes) => {
|
|
44018
|
+
const arr = React.Children.toArray(nodes).slice(
|
|
44019
|
+
0,
|
|
44020
|
+
HERO_METRIC_CONSTANTS.MAX_ITEMS
|
|
44021
|
+
);
|
|
44022
|
+
if (arr.length < HERO_METRIC_CONSTANTS.MIN_ITEMS) {
|
|
44023
|
+
return null;
|
|
44024
|
+
}
|
|
44025
|
+
return arr.map((child, idx) => {
|
|
44026
|
+
if (React.isValidElement(child) && child.type === HeroMetricCardItem) {
|
|
44027
|
+
const childProps = child.props;
|
|
44028
|
+
const itemId = childProps.id ?? String(idx);
|
|
44029
|
+
const itemProps = getItemProps(
|
|
44030
|
+
idx,
|
|
44031
|
+
itemId,
|
|
44032
|
+
arr.length,
|
|
44033
|
+
mode,
|
|
44034
|
+
activeId,
|
|
44035
|
+
handleItemActivate
|
|
44036
|
+
);
|
|
44037
|
+
const nextProps = {
|
|
44038
|
+
loading,
|
|
44039
|
+
...itemProps,
|
|
44040
|
+
trendComparisonValue: _showTrends ? childProps.trendComparisonValue : void 0
|
|
44041
|
+
};
|
|
44042
|
+
return React.cloneElement(child, nextProps);
|
|
44043
|
+
}
|
|
44044
|
+
return child;
|
|
44045
|
+
});
|
|
44046
|
+
};
|
|
44047
|
+
const content = items ? renderFromItems(items) : renderFromChildren(children);
|
|
44048
|
+
const mergedRef = mergeRefs(containerRef, ref);
|
|
44049
|
+
const gapClass = hasWrapped ? HERO_METRIC_CONSTANTS.WRAPPED_GAP_DEFAULT : HERO_METRIC_CONSTANTS.UNWRAPPED_GAP;
|
|
44050
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
44051
|
+
"div",
|
|
44052
|
+
{
|
|
44053
|
+
ref: mergedRef,
|
|
44054
|
+
className: cn$1(
|
|
44055
|
+
"mmc-hero-metric-card",
|
|
44056
|
+
// Card container carries border/rounded, not items
|
|
44057
|
+
"rounded-lg border border-[var(--border-input)] bg-card",
|
|
44058
|
+
// Grid layout inside card
|
|
44059
|
+
"grid",
|
|
44060
|
+
resolveGridClasses(columns),
|
|
44061
|
+
// Use gaps when wrapped, no gaps when single-row with borders
|
|
44062
|
+
gapClass,
|
|
44063
|
+
// Static: 16px top/bottom, 0px left/right
|
|
44064
|
+
HERO_METRIC_CONSTANTS.CONTAINER_PADDING_Y,
|
|
44065
|
+
HERO_METRIC_CONSTANTS.CONTAINER_PADDING_X,
|
|
44066
|
+
className
|
|
44067
|
+
),
|
|
44068
|
+
"data-slot": "hero-metric-card",
|
|
44069
|
+
"data-mode": mode,
|
|
44070
|
+
"data-wrapped": hasWrapped ? "true" : "false",
|
|
44071
|
+
"data-testid": dataTestId,
|
|
44072
|
+
...rest,
|
|
44073
|
+
children: content
|
|
44074
|
+
}
|
|
44075
|
+
);
|
|
44076
|
+
}
|
|
44077
|
+
);
|
|
44078
|
+
HeroMetricCard.displayName = "HeroMetricCard";
|
|
43622
44079
|
export {
|
|
43623
44080
|
Accordion,
|
|
43624
44081
|
AccordionContent,
|
|
@@ -43717,6 +44174,8 @@ export {
|
|
|
43717
44174
|
FormItem,
|
|
43718
44175
|
FormLabel,
|
|
43719
44176
|
FormMessage,
|
|
44177
|
+
HeroMetricCard,
|
|
44178
|
+
HeroMetricCardItem,
|
|
43720
44179
|
Input,
|
|
43721
44180
|
Label$2 as Label,
|
|
43722
44181
|
LabeledSlider,
|