@almadar/ui 4.51.14 → 4.51.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.
@@ -2681,6 +2681,67 @@ var init_Radio = __esm({
2681
2681
  exports.Radio.displayName = "Radio";
2682
2682
  }
2683
2683
  });
2684
+ var COLOR_VAR; exports.Sparkline = void 0;
2685
+ var init_Sparkline = __esm({
2686
+ "components/atoms/Sparkline.tsx"() {
2687
+ init_cn();
2688
+ COLOR_VAR = {
2689
+ primary: "var(--color-primary)",
2690
+ success: "var(--color-success)",
2691
+ warning: "var(--color-warning)",
2692
+ error: "var(--color-error)",
2693
+ info: "var(--color-info)",
2694
+ muted: "var(--color-muted-foreground)"
2695
+ };
2696
+ exports.Sparkline = ({
2697
+ data,
2698
+ color = "auto",
2699
+ width = 80,
2700
+ height = 32,
2701
+ strokeWidth = 2,
2702
+ fill = false,
2703
+ className
2704
+ }) => {
2705
+ if (data.length < 2) return null;
2706
+ const pad = 2;
2707
+ const min = Math.min(...data);
2708
+ const max = Math.max(...data);
2709
+ const range = max - min || 1;
2710
+ const points = data.map((v, i) => {
2711
+ const x = pad + i / (data.length - 1) * (width - pad * 2);
2712
+ const y = pad + (1 - (v - min) / range) * (height - pad * 2);
2713
+ return `${x},${y}`;
2714
+ }).join(" ");
2715
+ const resolvedColor = color === "auto" ? data[data.length - 1] >= data[0] ? COLOR_VAR.success : COLOR_VAR.error : COLOR_VAR[color];
2716
+ const areaPath = fill ? `M ${pad},${height - pad} L ${points.split(" ").join(" L ")} L ${width - pad},${height - pad} Z` : null;
2717
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2718
+ "svg",
2719
+ {
2720
+ width,
2721
+ height,
2722
+ viewBox: `0 0 ${width} ${height}`,
2723
+ className: cn("flex-shrink-0", className),
2724
+ "aria-hidden": "true",
2725
+ children: [
2726
+ areaPath && /* @__PURE__ */ jsxRuntime.jsx("path", { d: areaPath, fill: resolvedColor, opacity: 0.15 }),
2727
+ /* @__PURE__ */ jsxRuntime.jsx(
2728
+ "polyline",
2729
+ {
2730
+ fill: "none",
2731
+ stroke: resolvedColor,
2732
+ strokeWidth,
2733
+ strokeLinecap: "round",
2734
+ strokeLinejoin: "round",
2735
+ points
2736
+ }
2737
+ )
2738
+ ]
2739
+ }
2740
+ );
2741
+ };
2742
+ exports.Sparkline.displayName = "Sparkline";
2743
+ }
2744
+ });
2684
2745
  exports.Switch = void 0;
2685
2746
  var init_Switch = __esm({
2686
2747
  "components/atoms/Switch.tsx"() {
@@ -7754,7 +7815,7 @@ var init_MapView = __esm({
7754
7815
  shadowSize: [41, 41]
7755
7816
  });
7756
7817
  L.Marker.prototype.options.icon = defaultIcon;
7757
- const { useEffect: useEffect70, useRef: useRef66, useCallback: useCallback124, useState: useState106 } = React75__namespace.default;
7818
+ const { useEffect: useEffect70, useRef: useRef66, useCallback: useCallback126, useState: useState107 } = React75__namespace.default;
7758
7819
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
7759
7820
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
7760
7821
  function MapUpdater({ centerLat, centerLng, zoom }) {
@@ -7799,8 +7860,8 @@ var init_MapView = __esm({
7799
7860
  showAttribution = true
7800
7861
  }) {
7801
7862
  const eventBus = useEventBus2();
7802
- const [clickedPosition, setClickedPosition] = useState106(null);
7803
- const handleMapClick = useCallback124((lat, lng) => {
7863
+ const [clickedPosition, setClickedPosition] = useState107(null);
7864
+ const handleMapClick = useCallback126((lat, lng) => {
7804
7865
  if (showClickedPin) {
7805
7866
  setClickedPosition({ lat, lng });
7806
7867
  }
@@ -7809,7 +7870,7 @@ var init_MapView = __esm({
7809
7870
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
7810
7871
  }
7811
7872
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
7812
- const handleMarkerClick = useCallback124((marker) => {
7873
+ const handleMarkerClick = useCallback126((marker) => {
7813
7874
  onMarkerClick?.(marker);
7814
7875
  if (markerClickEvent) {
7815
7876
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -17801,7 +17862,7 @@ var init_CastleTemplate = __esm({
17801
17862
  CastleTemplate.displayName = "CastleTemplate";
17802
17863
  }
17803
17864
  });
17804
- var CHART_COLORS, BarChart, PieChart, LineChart; exports.Chart = void 0;
17865
+ var CHART_COLORS, seriesColor, monthFormatter, formatTimeLabel, BarChart, PieChart, LineChart, ScatterChart; exports.Chart = void 0;
17805
17866
  var init_Chart = __esm({
17806
17867
  "components/organisms/Chart.tsx"() {
17807
17868
  "use client";
@@ -17821,38 +17882,159 @@ var init_Chart = __esm({
17821
17882
  "var(--color-info)",
17822
17883
  "var(--color-accent)"
17823
17884
  ];
17824
- BarChart = ({ data, height, showValues }) => {
17825
- const maxValue = Math.max(...data.map((d) => d.value), 1);
17826
- return /* @__PURE__ */ jsxRuntime.jsx(exports.HStack, { gap: "xs", align: "end", className: "w-full", style: { height }, children: data.map((point, idx) => {
17827
- const barHeight = point.value / maxValue * 100;
17828
- const color = point.color || CHART_COLORS[idx % CHART_COLORS.length];
17829
- return /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "xs", align: "center", flex: true, className: "min-w-0", children: [
17830
- showValues && /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", color: "secondary", className: "tabular-nums", children: point.value }),
17831
- /* @__PURE__ */ jsxRuntime.jsx(
17832
- exports.Box,
17833
- {
17834
- className: cn(
17835
- "w-full rounded-t-sm transition-all duration-500 ease-out min-h-[4px]"
17836
- ),
17837
- style: {
17838
- height: `${barHeight}%`,
17839
- backgroundColor: color
17840
- }
17885
+ seriesColor = (series, idx) => series.color ?? CHART_COLORS[idx % CHART_COLORS.length];
17886
+ monthFormatter = new Intl.DateTimeFormat(void 0, {
17887
+ month: "short",
17888
+ year: "2-digit"
17889
+ });
17890
+ formatTimeLabel = (raw) => {
17891
+ const parsed = new Date(raw);
17892
+ if (Number.isNaN(parsed.getTime())) return raw;
17893
+ return monthFormatter.format(parsed);
17894
+ };
17895
+ BarChart = ({ series, height, showValues, stack, timeAxis, histogram = false, onPointClick }) => {
17896
+ const categories = React75.useMemo(() => {
17897
+ const set = [];
17898
+ const seen = /* @__PURE__ */ new Set();
17899
+ for (const s of series) {
17900
+ for (const p2 of s.data) {
17901
+ if (!seen.has(p2.label)) {
17902
+ seen.add(p2.label);
17903
+ set.push(p2.label);
17841
17904
  }
17842
- ),
17843
- /* @__PURE__ */ jsxRuntime.jsx(
17844
- exports.Typography,
17845
- {
17846
- variant: "caption",
17847
- color: "secondary",
17848
- className: "truncate w-full text-center",
17849
- children: point.label
17905
+ }
17906
+ }
17907
+ return set;
17908
+ }, [series]);
17909
+ const valueAt = React75.useCallback(
17910
+ (s, label) => {
17911
+ const p2 = s.data.find((d) => d.label === label);
17912
+ return p2 ? p2.value : 0;
17913
+ },
17914
+ []
17915
+ );
17916
+ const columnTotals = React75.useMemo(() => {
17917
+ if (stack === "none") return null;
17918
+ return categories.map(
17919
+ (label) => series.reduce((sum, s) => sum + valueAt(s, label), 0)
17920
+ );
17921
+ }, [categories, series, stack, valueAt]);
17922
+ const maxValue = React75.useMemo(() => {
17923
+ if (stack === "normalize") return 100;
17924
+ if (stack === "stack" && columnTotals) {
17925
+ return Math.max(...columnTotals, 1);
17926
+ }
17927
+ let m = 1;
17928
+ for (const s of series) {
17929
+ for (const p2 of s.data) if (p2.value > m) m = p2.value;
17930
+ }
17931
+ return m;
17932
+ }, [series, stack, columnTotals]);
17933
+ return /* @__PURE__ */ jsxRuntime.jsx(
17934
+ exports.HStack,
17935
+ {
17936
+ gap: histogram ? "none" : "xs",
17937
+ align: "end",
17938
+ className: "w-full",
17939
+ style: { height },
17940
+ children: categories.map((label, catIdx) => {
17941
+ const displayLabel = timeAxis ? formatTimeLabel(label) : label;
17942
+ if (stack === "none") {
17943
+ return /* @__PURE__ */ jsxRuntime.jsxs(
17944
+ exports.VStack,
17945
+ {
17946
+ gap: "xs",
17947
+ align: "center",
17948
+ flex: true,
17949
+ className: "min-w-0",
17950
+ children: [
17951
+ /* @__PURE__ */ jsxRuntime.jsx(exports.HStack, { gap: histogram ? "none" : "xs", align: "end", className: "w-full", style: { height: "100%" }, children: series.map((s, sIdx) => {
17952
+ const value = valueAt(s, label);
17953
+ const barHeight = value / maxValue * 100;
17954
+ const color = seriesColor(s, sIdx);
17955
+ return /* @__PURE__ */ jsxRuntime.jsx(
17956
+ exports.Box,
17957
+ {
17958
+ className: cn(
17959
+ "rounded-t-sm transition-all duration-500 ease-out min-h-[4px] cursor-pointer hover:opacity-80",
17960
+ histogram ? "flex-1 mx-0" : "flex-1"
17961
+ ),
17962
+ style: {
17963
+ height: `${barHeight}%`,
17964
+ backgroundColor: color
17965
+ },
17966
+ onClick: () => onPointClick?.(
17967
+ { label, value, color },
17968
+ s.name
17969
+ ),
17970
+ title: `${s.name}: ${value}`
17971
+ },
17972
+ s.name
17973
+ );
17974
+ }) }),
17975
+ showValues && series.length === 1 && /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", color: "secondary", className: "tabular-nums", children: valueAt(series[0], label) }),
17976
+ /* @__PURE__ */ jsxRuntime.jsx(
17977
+ exports.Typography,
17978
+ {
17979
+ variant: "caption",
17980
+ color: "secondary",
17981
+ className: "truncate w-full text-center",
17982
+ children: displayLabel
17983
+ }
17984
+ )
17985
+ ]
17986
+ },
17987
+ label
17988
+ );
17850
17989
  }
17851
- )
17852
- ] }, point.label);
17853
- }) });
17990
+ const total = columnTotals?.[catIdx] ?? 1;
17991
+ return /* @__PURE__ */ jsxRuntime.jsxs(
17992
+ exports.VStack,
17993
+ {
17994
+ gap: "xs",
17995
+ align: "center",
17996
+ flex: true,
17997
+ className: "min-w-0",
17998
+ children: [
17999
+ /* @__PURE__ */ jsxRuntime.jsx(exports.VStack, { gap: "none", className: "w-full", style: { height: "100%" }, justify: "end", children: series.map((s, sIdx) => {
18000
+ const value = valueAt(s, label);
18001
+ const ratio = stack === "normalize" ? total === 0 ? 0 : value / total * 100 : value / maxValue * 100;
18002
+ const color = seriesColor(s, sIdx);
18003
+ return /* @__PURE__ */ jsxRuntime.jsx(
18004
+ exports.Box,
18005
+ {
18006
+ className: "w-full transition-all duration-500 ease-out cursor-pointer hover:opacity-80",
18007
+ style: {
18008
+ height: `${ratio}%`,
18009
+ backgroundColor: color
18010
+ },
18011
+ onClick: () => onPointClick?.(
18012
+ { label, value, color },
18013
+ s.name
18014
+ ),
18015
+ title: `${s.name}: ${value}`
18016
+ },
18017
+ s.name
18018
+ );
18019
+ }) }),
18020
+ /* @__PURE__ */ jsxRuntime.jsx(
18021
+ exports.Typography,
18022
+ {
18023
+ variant: "caption",
18024
+ color: "secondary",
18025
+ className: "truncate w-full text-center",
18026
+ children: displayLabel
18027
+ }
18028
+ )
18029
+ ]
18030
+ },
18031
+ label
18032
+ );
18033
+ })
18034
+ }
18035
+ );
17854
18036
  };
17855
- PieChart = ({ data, height, showValues, donut = false }) => {
18037
+ PieChart = ({ data, height, showValues, donut = false, onPointClick }) => {
17856
18038
  const total = data.reduce((sum, d) => sum + d.value, 0);
17857
18039
  const size = Math.min(height, 200);
17858
18040
  const radius = size / 2 - 8;
@@ -17898,7 +18080,11 @@ var init_Chart = __esm({
17898
18080
  fill: seg.color,
17899
18081
  stroke: "var(--color-card)",
17900
18082
  strokeWidth: "2",
17901
- className: "transition-opacity duration-200 hover:opacity-80"
18083
+ className: "transition-opacity duration-200 hover:opacity-80 cursor-pointer",
18084
+ onClick: () => onPointClick?.(
18085
+ { label: seg.label, value: seg.value, color: seg.color },
18086
+ "default"
18087
+ )
17902
18088
  },
17903
18089
  idx
17904
18090
  )),
@@ -17933,56 +18119,243 @@ var init_Chart = __esm({
17933
18119
  ] }, idx)) })
17934
18120
  ] });
17935
18121
  };
17936
- LineChart = ({ data, height, showValues, fill = false }) => {
17937
- const maxValue = Math.max(...data.map((d) => d.value), 1);
18122
+ LineChart = ({ series, height, showValues, fill = false, timeAxis, onPointClick }) => {
17938
18123
  const width = 400;
17939
18124
  const padding = { top: 20, right: 20, bottom: 30, left: 40 };
17940
18125
  const chartWidth = width - padding.left - padding.right;
17941
18126
  const chartHeight = height - padding.top - padding.bottom;
17942
- const points = React75.useMemo(() => {
17943
- return data.map((point, idx) => ({
17944
- x: padding.left + idx / Math.max(data.length - 1, 1) * chartWidth,
17945
- y: padding.top + chartHeight - point.value / maxValue * chartHeight,
17946
- ...point
17947
- }));
17948
- }, [data, maxValue, chartWidth, chartHeight, padding]);
17949
- const linePath = points.map((p2, i) => `${i === 0 ? "M" : "L"} ${p2.x} ${p2.y}`).join(" ");
17950
- const areaPath = `${linePath} L ${points[points.length - 1]?.x ?? 0} ${padding.top + chartHeight} L ${padding.left} ${padding.top + chartHeight} Z`;
17951
- return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "100%", height, viewBox: `0 0 ${width} ${height}`, preserveAspectRatio: "xMidYMid meet", children: [
17952
- [0, 0.25, 0.5, 0.75, 1].map((frac) => {
17953
- const y = padding.top + chartHeight * (1 - frac);
17954
- return /* @__PURE__ */ jsxRuntime.jsx(
17955
- "line",
17956
- {
17957
- x1: padding.left,
17958
- y1: y,
17959
- x2: width - padding.right,
17960
- y2: y,
17961
- stroke: "var(--color-border)",
17962
- strokeDasharray: "4 4",
17963
- opacity: 0.5
17964
- },
17965
- frac
17966
- );
17967
- }),
17968
- fill && /* @__PURE__ */ jsxRuntime.jsx("path", { d: areaPath, fill: "var(--color-primary)", opacity: 0.1 }),
17969
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: linePath, fill: "none", stroke: "var(--color-primary)", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }),
17970
- points.map((p2, idx) => /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
17971
- /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: p2.x, cy: p2.y, r: "4", fill: "var(--color-card)", stroke: "var(--color-primary)", strokeWidth: "2" }),
17972
- showValues && /* @__PURE__ */ jsxRuntime.jsx("text", { x: p2.x, y: p2.y - 10, textAnchor: "middle", fill: "var(--color-foreground)", fontSize: "10", fontWeight: "500", children: p2.value }),
17973
- /* @__PURE__ */ jsxRuntime.jsx(
17974
- "text",
17975
- {
17976
- x: p2.x,
17977
- y: height - 8,
17978
- textAnchor: "middle",
17979
- fill: "var(--color-muted-foreground)",
17980
- fontSize: "9",
17981
- children: p2.label
18127
+ const labels = React75.useMemo(() => {
18128
+ const seen = /* @__PURE__ */ new Set();
18129
+ const out = [];
18130
+ for (const s of series) {
18131
+ for (const p2 of s.data) {
18132
+ if (!seen.has(p2.label)) {
18133
+ seen.add(p2.label);
18134
+ out.push(p2.label);
17982
18135
  }
17983
- )
17984
- ] }, idx))
17985
- ] });
18136
+ }
18137
+ }
18138
+ return out;
18139
+ }, [series]);
18140
+ const maxValue = React75.useMemo(() => {
18141
+ let m = 1;
18142
+ for (const s of series) {
18143
+ for (const p2 of s.data) if (p2.value > m) m = p2.value;
18144
+ }
18145
+ return m;
18146
+ }, [series]);
18147
+ const xFor = React75.useCallback(
18148
+ (idx) => padding.left + idx / Math.max(labels.length - 1, 1) * chartWidth,
18149
+ [labels.length, chartWidth, padding.left]
18150
+ );
18151
+ const yFor = React75.useCallback(
18152
+ (value) => padding.top + chartHeight - value / maxValue * chartHeight,
18153
+ [maxValue, chartHeight, padding.top]
18154
+ );
18155
+ return /* @__PURE__ */ jsxRuntime.jsxs(
18156
+ "svg",
18157
+ {
18158
+ width: "100%",
18159
+ height,
18160
+ viewBox: `0 0 ${width} ${height}`,
18161
+ preserveAspectRatio: "xMidYMid meet",
18162
+ children: [
18163
+ [0, 0.25, 0.5, 0.75, 1].map((frac) => {
18164
+ const y = padding.top + chartHeight * (1 - frac);
18165
+ return /* @__PURE__ */ jsxRuntime.jsx(
18166
+ "line",
18167
+ {
18168
+ x1: padding.left,
18169
+ y1: y,
18170
+ x2: width - padding.right,
18171
+ y2: y,
18172
+ stroke: "var(--color-border)",
18173
+ strokeDasharray: "4 4",
18174
+ opacity: 0.5
18175
+ },
18176
+ frac
18177
+ );
18178
+ }),
18179
+ series.map((s, sIdx) => {
18180
+ const color = seriesColor(s, sIdx);
18181
+ const points = labels.map((label, idx) => {
18182
+ const point = s.data.find((d) => d.label === label);
18183
+ return {
18184
+ x: xFor(idx),
18185
+ y: yFor(point ? point.value : 0),
18186
+ value: point ? point.value : 0,
18187
+ label
18188
+ };
18189
+ });
18190
+ const linePath = points.map((p2, i) => `${i === 0 ? "M" : "L"} ${p2.x} ${p2.y}`).join(" ");
18191
+ const areaPath = `${linePath} L ${points[points.length - 1]?.x ?? 0} ${padding.top + chartHeight} L ${padding.left} ${padding.top + chartHeight} Z`;
18192
+ return /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
18193
+ fill && /* @__PURE__ */ jsxRuntime.jsx(
18194
+ "path",
18195
+ {
18196
+ d: areaPath,
18197
+ fill: color,
18198
+ opacity: series.length > 1 ? 0.08 : 0.1
18199
+ }
18200
+ ),
18201
+ /* @__PURE__ */ jsxRuntime.jsx(
18202
+ "path",
18203
+ {
18204
+ d: linePath,
18205
+ fill: "none",
18206
+ stroke: color,
18207
+ strokeWidth: "2",
18208
+ strokeLinecap: "round",
18209
+ strokeLinejoin: "round",
18210
+ strokeDasharray: s.dashed ? "6 4" : void 0
18211
+ }
18212
+ ),
18213
+ points.map((p2, idx) => /* @__PURE__ */ jsxRuntime.jsxs("g", { children: [
18214
+ /* @__PURE__ */ jsxRuntime.jsx(
18215
+ "circle",
18216
+ {
18217
+ cx: p2.x,
18218
+ cy: p2.y,
18219
+ r: "4",
18220
+ fill: "var(--color-card)",
18221
+ stroke: color,
18222
+ strokeWidth: "2",
18223
+ className: "cursor-pointer",
18224
+ onClick: () => onPointClick?.(
18225
+ { label: p2.label, value: p2.value, color },
18226
+ s.name
18227
+ )
18228
+ }
18229
+ ),
18230
+ showValues && series.length === 1 && /* @__PURE__ */ jsxRuntime.jsx(
18231
+ "text",
18232
+ {
18233
+ x: p2.x,
18234
+ y: p2.y - 10,
18235
+ textAnchor: "middle",
18236
+ fill: "var(--color-foreground)",
18237
+ fontSize: "10",
18238
+ fontWeight: "500",
18239
+ children: p2.value
18240
+ }
18241
+ )
18242
+ ] }, idx))
18243
+ ] }, s.name);
18244
+ }),
18245
+ labels.map((label, idx) => /* @__PURE__ */ jsxRuntime.jsx(
18246
+ "text",
18247
+ {
18248
+ x: xFor(idx),
18249
+ y: height - 8,
18250
+ textAnchor: "middle",
18251
+ fill: "var(--color-muted-foreground)",
18252
+ fontSize: "9",
18253
+ children: timeAxis ? formatTimeLabel(label) : label
18254
+ },
18255
+ label
18256
+ ))
18257
+ ]
18258
+ }
18259
+ );
18260
+ };
18261
+ ScatterChart = ({ data, height, onPointClick }) => {
18262
+ const width = 400;
18263
+ const padding = { top: 20, right: 20, bottom: 30, left: 40 };
18264
+ const chartWidth = width - padding.left - padding.right;
18265
+ const chartHeight = height - padding.top - padding.bottom;
18266
+ const { minX, maxX, minY, maxY } = React75.useMemo(() => {
18267
+ if (data.length === 0) {
18268
+ return { minX: 0, maxX: 1, minY: 0, maxY: 1 };
18269
+ }
18270
+ let mnX = data[0].x;
18271
+ let mxX = data[0].x;
18272
+ let mnY = data[0].y;
18273
+ let mxY = data[0].y;
18274
+ for (const p2 of data) {
18275
+ if (p2.x < mnX) mnX = p2.x;
18276
+ if (p2.x > mxX) mxX = p2.x;
18277
+ if (p2.y < mnY) mnY = p2.y;
18278
+ if (p2.y > mxY) mxY = p2.y;
18279
+ }
18280
+ return { minX: mnX, maxX: mxX, minY: mnY, maxY: mxY };
18281
+ }, [data]);
18282
+ const rangeX = maxX - minX || 1;
18283
+ const rangeY = maxY - minY || 1;
18284
+ return /* @__PURE__ */ jsxRuntime.jsxs(
18285
+ "svg",
18286
+ {
18287
+ width: "100%",
18288
+ height,
18289
+ viewBox: `0 0 ${width} ${height}`,
18290
+ preserveAspectRatio: "xMidYMid meet",
18291
+ children: [
18292
+ [0, 0.25, 0.5, 0.75, 1].map((frac) => {
18293
+ const y = padding.top + chartHeight * (1 - frac);
18294
+ return /* @__PURE__ */ jsxRuntime.jsx(
18295
+ "line",
18296
+ {
18297
+ x1: padding.left,
18298
+ y1: y,
18299
+ x2: width - padding.right,
18300
+ y2: y,
18301
+ stroke: "var(--color-border)",
18302
+ strokeDasharray: "4 4",
18303
+ opacity: 0.5
18304
+ },
18305
+ frac
18306
+ );
18307
+ }),
18308
+ data.map((p2, idx) => {
18309
+ const cx = padding.left + (p2.x - minX) / rangeX * chartWidth;
18310
+ const cy = padding.top + chartHeight - (p2.y - minY) / rangeY * chartHeight;
18311
+ const r = p2.size ?? 5;
18312
+ const color = p2.color ?? CHART_COLORS[idx % CHART_COLORS.length];
18313
+ return /* @__PURE__ */ jsxRuntime.jsx(
18314
+ "circle",
18315
+ {
18316
+ cx,
18317
+ cy,
18318
+ r,
18319
+ fill: color,
18320
+ opacity: 0.7,
18321
+ className: "cursor-pointer hover:opacity-100",
18322
+ onClick: () => onPointClick?.(
18323
+ {
18324
+ label: p2.label ?? `(${p2.x}, ${p2.y})`,
18325
+ value: p2.y,
18326
+ color
18327
+ },
18328
+ "default"
18329
+ ),
18330
+ children: /* @__PURE__ */ jsxRuntime.jsx("title", { children: p2.label ?? `(${p2.x}, ${p2.y})` })
18331
+ },
18332
+ idx
18333
+ );
18334
+ }),
18335
+ /* @__PURE__ */ jsxRuntime.jsx(
18336
+ "text",
18337
+ {
18338
+ x: padding.left,
18339
+ y: height - 8,
18340
+ fill: "var(--color-muted-foreground)",
18341
+ fontSize: "9",
18342
+ children: minX.toFixed(1)
18343
+ }
18344
+ ),
18345
+ /* @__PURE__ */ jsxRuntime.jsx(
18346
+ "text",
18347
+ {
18348
+ x: width - padding.right,
18349
+ y: height - 8,
18350
+ textAnchor: "end",
18351
+ fill: "var(--color-muted-foreground)",
18352
+ fontSize: "9",
18353
+ children: maxX.toFixed(1)
18354
+ }
18355
+ )
18356
+ ]
18357
+ }
18358
+ );
17986
18359
  };
17987
18360
  exports.Chart = ({
17988
18361
  title,
@@ -17990,9 +18363,13 @@ var init_Chart = __esm({
17990
18363
  chartType = "bar",
17991
18364
  series,
17992
18365
  data: simpleData,
18366
+ scatterData,
17993
18367
  height = 200,
17994
18368
  showLegend = true,
17995
18369
  showValues = false,
18370
+ stack = "none",
18371
+ timeAxis = false,
18372
+ drillEvent,
17996
18373
  actions,
17997
18374
  entity,
17998
18375
  isLoading = false,
@@ -18009,11 +18386,25 @@ var init_Chart = __esm({
18009
18386
  },
18010
18387
  [eventBus]
18011
18388
  );
18012
- const normalizedData = React75.useMemo(() => {
18013
- if (simpleData) return simpleData;
18014
- if (series && series.length > 0) return series[0].data;
18389
+ const handlePointClick = React75.useCallback(
18390
+ (point, seriesName) => {
18391
+ if (drillEvent) {
18392
+ eventBus.emit(`UI:${drillEvent}`, {
18393
+ label: point.label,
18394
+ value: point.value,
18395
+ seriesLabel: seriesName === "default" ? void 0 : seriesName
18396
+ });
18397
+ }
18398
+ },
18399
+ [drillEvent, eventBus]
18400
+ );
18401
+ const normalizedSeries = React75.useMemo(() => {
18402
+ if (series && series.length > 0) return series;
18403
+ if (simpleData) return [{ name: "default", data: simpleData }];
18015
18404
  return [];
18016
18405
  }, [simpleData, series]);
18406
+ const firstSeriesData = normalizedSeries[0]?.data ?? [];
18407
+ const hasContent = chartType === "scatter" ? (scatterData?.length ?? 0) > 0 : normalizedSeries.some((s) => s.data.length > 0);
18017
18408
  if (isLoading) {
18018
18409
  return /* @__PURE__ */ jsxRuntime.jsx(exports.LoadingState, { message: "Loading chart...", className });
18019
18410
  }
@@ -18027,7 +18418,7 @@ var init_Chart = __esm({
18027
18418
  }
18028
18419
  );
18029
18420
  }
18030
- if (normalizedData.length === 0) {
18421
+ if (!hasContent) {
18031
18422
  return /* @__PURE__ */ jsxRuntime.jsx(exports.EmptyState, { title: t("empty.noData"), description: t("empty.noData"), className });
18032
18423
  }
18033
18424
  return /* @__PURE__ */ jsxRuntime.jsx(exports.Card, { className: cn("p-6", className), children: /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "md", children: [
@@ -18048,18 +18439,84 @@ var init_Chart = __esm({
18048
18439
  )) })
18049
18440
  ] }),
18050
18441
  /* @__PURE__ */ jsxRuntime.jsxs(exports.Box, { className: "w-full", children: [
18051
- chartType === "bar" && /* @__PURE__ */ jsxRuntime.jsx(BarChart, { data: normalizedData, height, showValues }),
18052
- chartType === "line" && /* @__PURE__ */ jsxRuntime.jsx(LineChart, { data: normalizedData, height, showValues }),
18053
- chartType === "area" && /* @__PURE__ */ jsxRuntime.jsx(LineChart, { data: normalizedData, height, showValues, fill: true }),
18054
- chartType === "pie" && /* @__PURE__ */ jsxRuntime.jsx(PieChart, { data: normalizedData, height, showValues: showLegend }),
18055
- chartType === "donut" && /* @__PURE__ */ jsxRuntime.jsx(PieChart, { data: normalizedData, height, showValues: showLegend, donut: true })
18442
+ chartType === "bar" && /* @__PURE__ */ jsxRuntime.jsx(
18443
+ BarChart,
18444
+ {
18445
+ series: normalizedSeries,
18446
+ height,
18447
+ showValues,
18448
+ stack,
18449
+ timeAxis,
18450
+ onPointClick: handlePointClick
18451
+ }
18452
+ ),
18453
+ chartType === "histogram" && /* @__PURE__ */ jsxRuntime.jsx(
18454
+ BarChart,
18455
+ {
18456
+ series: normalizedSeries,
18457
+ height,
18458
+ showValues,
18459
+ stack: "none",
18460
+ timeAxis: false,
18461
+ histogram: true,
18462
+ onPointClick: handlePointClick
18463
+ }
18464
+ ),
18465
+ chartType === "line" && /* @__PURE__ */ jsxRuntime.jsx(
18466
+ LineChart,
18467
+ {
18468
+ series: normalizedSeries,
18469
+ height,
18470
+ showValues,
18471
+ timeAxis,
18472
+ onPointClick: handlePointClick
18473
+ }
18474
+ ),
18475
+ chartType === "area" && /* @__PURE__ */ jsxRuntime.jsx(
18476
+ LineChart,
18477
+ {
18478
+ series: normalizedSeries,
18479
+ height,
18480
+ showValues,
18481
+ timeAxis,
18482
+ fill: true,
18483
+ onPointClick: handlePointClick
18484
+ }
18485
+ ),
18486
+ chartType === "pie" && /* @__PURE__ */ jsxRuntime.jsx(
18487
+ PieChart,
18488
+ {
18489
+ data: firstSeriesData,
18490
+ height,
18491
+ showValues: showLegend,
18492
+ onPointClick: handlePointClick
18493
+ }
18494
+ ),
18495
+ chartType === "donut" && /* @__PURE__ */ jsxRuntime.jsx(
18496
+ PieChart,
18497
+ {
18498
+ data: firstSeriesData,
18499
+ height,
18500
+ showValues: showLegend,
18501
+ donut: true,
18502
+ onPointClick: handlePointClick
18503
+ }
18504
+ ),
18505
+ chartType === "scatter" && /* @__PURE__ */ jsxRuntime.jsx(
18506
+ ScatterChart,
18507
+ {
18508
+ data: scatterData ?? [],
18509
+ height,
18510
+ onPointClick: handlePointClick
18511
+ }
18512
+ )
18056
18513
  ] }),
18057
- showLegend && series && series.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(exports.HStack, { gap: "md", justify: "center", wrap: true, children: series.map((s, idx) => /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "xs", align: "center", children: [
18514
+ showLegend && normalizedSeries.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(exports.HStack, { gap: "md", justify: "center", wrap: true, children: normalizedSeries.map((s, idx) => /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "xs", align: "center", children: [
18058
18515
  /* @__PURE__ */ jsxRuntime.jsx(
18059
18516
  exports.Box,
18060
18517
  {
18061
18518
  className: "w-3 h-3 rounded-full flex-shrink-0",
18062
- style: { backgroundColor: s.color || CHART_COLORS[idx % CHART_COLORS.length] }
18519
+ style: { backgroundColor: seriesColor(s, idx) }
18063
18520
  }
18064
18521
  ),
18065
18522
  /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", color: "secondary", children: s.name })
@@ -23214,6 +23671,151 @@ var init_FlipCard = __esm({
23214
23671
  exports.FlipCard.displayName = "FlipCard";
23215
23672
  }
23216
23673
  });
23674
+ function toISODate(d) {
23675
+ return d.toISOString().slice(0, 10);
23676
+ }
23677
+ function startOfMonth(d) {
23678
+ return new Date(d.getFullYear(), d.getMonth(), 1);
23679
+ }
23680
+ function startOfQuarter(d) {
23681
+ return new Date(d.getFullYear(), Math.floor(d.getMonth() / 3) * 3, 1);
23682
+ }
23683
+ function startOfYear(d) {
23684
+ return new Date(d.getFullYear(), 0, 1);
23685
+ }
23686
+ function daysAgo(n) {
23687
+ const d = /* @__PURE__ */ new Date();
23688
+ d.setDate(d.getDate() - n);
23689
+ return d;
23690
+ }
23691
+ var DEFAULT_PRESETS; exports.DateRangePicker = void 0;
23692
+ var init_DateRangePicker = __esm({
23693
+ "components/molecules/DateRangePicker.tsx"() {
23694
+ "use client";
23695
+ init_cn();
23696
+ init_Button();
23697
+ init_Input();
23698
+ init_Stack();
23699
+ init_Typography();
23700
+ init_useEventBus();
23701
+ DEFAULT_PRESETS = [
23702
+ {
23703
+ label: "Last 7 days",
23704
+ value: "7d",
23705
+ range: () => ({ from: toISODate(daysAgo(7)), to: toISODate(/* @__PURE__ */ new Date()) })
23706
+ },
23707
+ {
23708
+ label: "Last 30 days",
23709
+ value: "30d",
23710
+ range: () => ({ from: toISODate(daysAgo(30)), to: toISODate(/* @__PURE__ */ new Date()) })
23711
+ },
23712
+ {
23713
+ label: "This Month",
23714
+ value: "month",
23715
+ range: () => ({ from: toISODate(startOfMonth(/* @__PURE__ */ new Date())), to: toISODate(/* @__PURE__ */ new Date()) })
23716
+ },
23717
+ {
23718
+ label: "This Quarter",
23719
+ value: "quarter",
23720
+ range: () => ({ from: toISODate(startOfQuarter(/* @__PURE__ */ new Date())), to: toISODate(/* @__PURE__ */ new Date()) })
23721
+ },
23722
+ {
23723
+ label: "YTD",
23724
+ value: "ytd",
23725
+ range: () => ({ from: toISODate(startOfYear(/* @__PURE__ */ new Date())), to: toISODate(/* @__PURE__ */ new Date()) })
23726
+ }
23727
+ ];
23728
+ exports.DateRangePicker = ({
23729
+ from: fromProp,
23730
+ to: toProp,
23731
+ event,
23732
+ onChange,
23733
+ presets = DEFAULT_PRESETS,
23734
+ fromLabel = "From",
23735
+ toLabel = "To",
23736
+ className
23737
+ }) => {
23738
+ const eventBus = useEventBus();
23739
+ const [from, setFrom] = React75.useState(fromProp ?? "");
23740
+ const [to, setTo] = React75.useState(toProp ?? "");
23741
+ const [activePreset, setActivePreset] = React75.useState(null);
23742
+ const emit = React75.useCallback(
23743
+ (range) => {
23744
+ onChange?.(range);
23745
+ if (event) eventBus.emit(`UI:${event}`, range);
23746
+ },
23747
+ [onChange, event, eventBus]
23748
+ );
23749
+ const handleFromChange = React75.useCallback(
23750
+ (next) => {
23751
+ setFrom(next);
23752
+ setActivePreset(null);
23753
+ emit({ from: next, to });
23754
+ },
23755
+ [to, emit]
23756
+ );
23757
+ const handleToChange = React75.useCallback(
23758
+ (next) => {
23759
+ setTo(next);
23760
+ setActivePreset(null);
23761
+ emit({ from, to: next });
23762
+ },
23763
+ [from, emit]
23764
+ );
23765
+ const handlePreset = React75.useCallback(
23766
+ (preset) => {
23767
+ const range = preset.range();
23768
+ setFrom(range.from);
23769
+ setTo(range.to);
23770
+ setActivePreset(preset.value);
23771
+ emit(range);
23772
+ },
23773
+ [emit]
23774
+ );
23775
+ const presetButtons = React75.useMemo(
23776
+ () => presets.map((preset) => /* @__PURE__ */ jsxRuntime.jsx(
23777
+ exports.Button,
23778
+ {
23779
+ variant: activePreset === preset.value ? "primary" : "ghost",
23780
+ size: "sm",
23781
+ onClick: () => handlePreset(preset),
23782
+ children: preset.label
23783
+ },
23784
+ preset.value
23785
+ )),
23786
+ [presets, activePreset, handlePreset]
23787
+ );
23788
+ return /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "sm", className: cn(className), children: [
23789
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "md", align: "end", children: [
23790
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "xs", children: [
23791
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", color: "secondary", children: fromLabel }),
23792
+ /* @__PURE__ */ jsxRuntime.jsx(
23793
+ exports.Input,
23794
+ {
23795
+ type: "date",
23796
+ value: from,
23797
+ onChange: (e) => handleFromChange(e.target.value)
23798
+ }
23799
+ )
23800
+ ] }),
23801
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "xs", children: [
23802
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", color: "secondary", children: toLabel }),
23803
+ /* @__PURE__ */ jsxRuntime.jsx(
23804
+ exports.Input,
23805
+ {
23806
+ type: "date",
23807
+ value: to,
23808
+ onChange: (e) => handleToChange(e.target.value)
23809
+ }
23810
+ )
23811
+ ] })
23812
+ ] }),
23813
+ presets.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(exports.HStack, { gap: "xs", wrap: true, children: presetButtons })
23814
+ ] });
23815
+ };
23816
+ exports.DateRangePicker.displayName = "DateRangePicker";
23817
+ }
23818
+ });
23217
23819
  var DEFAULT_OPTIONS; exports.DateRangeSelector = void 0;
23218
23820
  var init_DateRangeSelector = __esm({
23219
23821
  "components/molecules/DateRangeSelector.tsx"() {
@@ -26469,7 +27071,9 @@ var init_StatDisplay = __esm({
26469
27071
  init_Typography();
26470
27072
  init_Box();
26471
27073
  init_Stack();
27074
+ init_Sparkline();
26472
27075
  init_Icon();
27076
+ init_useEventBus();
26473
27077
  variantColor = {
26474
27078
  default: "text-foreground",
26475
27079
  primary: "text-primary",
@@ -26484,6 +27088,10 @@ var init_StatDisplay = __esm({
26484
27088
  max,
26485
27089
  target,
26486
27090
  trend,
27091
+ trendPolarity = "higher-is-better",
27092
+ trendFormat = "absolute",
27093
+ sparklineData,
27094
+ clickEvent,
26487
27095
  prefix,
26488
27096
  suffix,
26489
27097
  icon: iconProp,
@@ -26497,6 +27105,10 @@ var init_StatDisplay = __esm({
26497
27105
  isLoading = false,
26498
27106
  error = null
26499
27107
  }) => {
27108
+ const eventBus = useEventBus();
27109
+ const handleClick = React75.useCallback(() => {
27110
+ if (clickEvent) eventBus.emit(`UI:${clickEvent}`, { metricLabel: label });
27111
+ }, [clickEvent, eventBus, label]);
26500
27112
  const ResolvedIcon = typeof iconProp === "string" ? resolveIcon(iconProp) : null;
26501
27113
  const iconSizes3 = { sm: "h-4 w-4", md: "h-5 w-5", lg: "h-6 w-6" };
26502
27114
  const valueSizes = { sm: "text-lg", md: "text-2xl", lg: "text-3xl" };
@@ -26507,7 +27119,10 @@ var init_StatDisplay = __esm({
26507
27119
  const targetPct = showTarget ? Math.max(0, Math.min(100, numericValue / target * 100)) : 0;
26508
27120
  const showTrend = typeof trend === "number" && trend !== 0 && Number.isFinite(trend);
26509
27121
  const trendUp = showTrend && trend > 0;
26510
- const trendLabel = showTrend ? `${trendUp ? "\u2191" : "\u2193"} ${Math.abs(trend)}` : "";
27122
+ const trendIsGood = trendPolarity === "lower-is-better" ? !trendUp : trendUp;
27123
+ const trendMagnitude = Math.abs(trend);
27124
+ const trendSuffix = trendFormat === "percent" ? "%" : "";
27125
+ const trendLabel = showTrend ? `${trendUp ? "\u2191" : "\u2193"} ${trendMagnitude}${trendSuffix}` : "";
26511
27126
  if (error) {
26512
27127
  return /* @__PURE__ */ jsxRuntime.jsx(exports.Card, { className: cn(padSizes[size], className), children: /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "small", color: "error", children: error.message }) });
26513
27128
  }
@@ -26518,38 +27133,57 @@ var init_StatDisplay = __esm({
26518
27133
  ] }) });
26519
27134
  }
26520
27135
  if (compact) {
26521
- return /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "sm", className: cn("items-center", className), children: [
26522
- ResolvedIcon && /* @__PURE__ */ jsxRuntime.jsx(ResolvedIcon, { className: cn(iconSizes3[size], iconColor) }),
26523
- typeof iconProp !== "string" && iconProp,
26524
- /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", color: "secondary", children: label }),
26525
- /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h4", className: cn("font-bold", valueSizes[size], variantColor[variant]), children: displayValue }),
26526
- showTrend && /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", className: cn("font-semibold", trendUp ? "text-success" : "text-error"), children: trendLabel })
26527
- ] });
27136
+ return /* @__PURE__ */ jsxRuntime.jsxs(
27137
+ exports.HStack,
27138
+ {
27139
+ gap: "sm",
27140
+ className: cn("items-center", clickEvent && "cursor-pointer hover:opacity-80", className),
27141
+ onClick: clickEvent ? handleClick : void 0,
27142
+ children: [
27143
+ ResolvedIcon && /* @__PURE__ */ jsxRuntime.jsx(ResolvedIcon, { className: cn(iconSizes3[size], iconColor) }),
27144
+ typeof iconProp !== "string" && iconProp,
27145
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", color: "secondary", children: label }),
27146
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h4", className: cn("font-bold", valueSizes[size], variantColor[variant]), children: displayValue }),
27147
+ showTrend && /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "caption", className: cn("font-semibold", trendIsGood ? "text-success" : "text-error"), children: trendLabel }),
27148
+ sparklineData && sparklineData.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(exports.Sparkline, { data: sparklineData, color: "auto", width: 60, height: 20 })
27149
+ ]
27150
+ }
27151
+ );
26528
27152
  }
26529
- return /* @__PURE__ */ jsxRuntime.jsx(exports.Card, { className: cn(padSizes[size], className), children: /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { align: "start", justify: "between", children: [
26530
- /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "none", className: "space-y-1 flex-1", children: [
26531
- /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "overline", color: "secondary", children: label }),
26532
- /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "sm", align: "end", children: [
26533
- /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h4", className: cn("font-bold", valueSizes[size], variantColor[variant]), children: displayValue }),
26534
- showTrend && /* @__PURE__ */ jsxRuntime.jsx(
26535
- exports.Typography,
26536
- {
26537
- variant: "caption",
26538
- className: cn("font-semibold pb-1", trendUp ? "text-success" : "text-error"),
26539
- children: trendLabel
26540
- }
26541
- )
26542
- ] }),
26543
- showTarget && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "mt-2 h-1.5 w-full bg-muted rounded-full overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
26544
- exports.Box,
26545
- {
26546
- className: cn("h-full rounded-full transition-all", `bg-${variant === "default" ? "primary" : variant}`),
26547
- style: { width: `${targetPct}%` }
26548
- }
26549
- ) })
26550
- ] }),
26551
- (ResolvedIcon || typeof iconProp !== "string" && iconProp) && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: cn("p-3 rounded-md", iconBg), children: ResolvedIcon ? /* @__PURE__ */ jsxRuntime.jsx(ResolvedIcon, { className: cn(iconSizes3[size], iconColor) }) : iconProp })
26552
- ] }) });
27153
+ return /* @__PURE__ */ jsxRuntime.jsx(
27154
+ exports.Card,
27155
+ {
27156
+ className: cn(padSizes[size], clickEvent && "cursor-pointer hover:shadow-md transition-shadow", className),
27157
+ onClick: clickEvent ? handleClick : void 0,
27158
+ children: /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { align: "start", justify: "between", children: [
27159
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "none", className: "space-y-1 flex-1", children: [
27160
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "overline", color: "secondary", children: label }),
27161
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.HStack, { gap: "sm", align: "end", children: [
27162
+ /* @__PURE__ */ jsxRuntime.jsx(exports.Typography, { variant: "h4", className: cn("font-bold", valueSizes[size], variantColor[variant]), children: displayValue }),
27163
+ showTrend && /* @__PURE__ */ jsxRuntime.jsx(
27164
+ exports.Typography,
27165
+ {
27166
+ variant: "caption",
27167
+ className: cn("font-semibold pb-1", trendIsGood ? "text-success" : "text-error"),
27168
+ children: trendLabel
27169
+ }
27170
+ )
27171
+ ] }),
27172
+ showTarget && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: "mt-2 h-1.5 w-full bg-muted rounded-full overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx(
27173
+ exports.Box,
27174
+ {
27175
+ className: cn("h-full rounded-full transition-all", `bg-${variant === "default" ? "primary" : variant}`),
27176
+ style: { width: `${targetPct}%` }
27177
+ }
27178
+ ) })
27179
+ ] }),
27180
+ /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "xs", align: "end", children: [
27181
+ (ResolvedIcon || typeof iconProp !== "string" && iconProp) && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: cn("p-3 rounded-md", iconBg), children: ResolvedIcon ? /* @__PURE__ */ jsxRuntime.jsx(ResolvedIcon, { className: cn(iconSizes3[size], iconColor) }) : iconProp }),
27182
+ sparklineData && sparklineData.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(exports.Sparkline, { data: sparklineData, color: "auto" })
27183
+ ] })
27184
+ ] })
27185
+ }
27186
+ );
26553
27187
  };
26554
27188
  exports.StatDisplay.displayName = "StatDisplay";
26555
27189
  }
@@ -31898,6 +32532,7 @@ var init_molecules = __esm({
31898
32532
  init_ViolationAlert();
31899
32533
  init_FormSectionHeader();
31900
32534
  init_FlipCard();
32535
+ init_DateRangePicker();
31901
32536
  init_DateRangeSelector();
31902
32537
  init_ChartLegend();
31903
32538
  init_LineChart();
@@ -41454,6 +42089,7 @@ var init_StatCard = __esm({
41454
42089
  init_Box();
41455
42090
  init_Stack();
41456
42091
  init_Button();
42092
+ init_Sparkline();
41457
42093
  init_useEventBus();
41458
42094
  init_useTranslate();
41459
42095
  init_Icon();
@@ -41623,32 +42259,7 @@ var init_StatCard = __esm({
41623
42259
  ] }),
41624
42260
  /* @__PURE__ */ jsxRuntime.jsxs(exports.VStack, { gap: "xs", align: "end", children: [
41625
42261
  Icon3 && /* @__PURE__ */ jsxRuntime.jsx(exports.Box, { className: cn("p-3", iconBg), children: /* @__PURE__ */ jsxRuntime.jsx(Icon3, { className: cn("h-6 w-6", iconColor) }) }),
41626
- sparklineData && sparklineData.length > 1 && (() => {
41627
- const w = 80;
41628
- const h = 32;
41629
- const pad = 2;
41630
- const min = Math.min(...sparklineData);
41631
- const max = Math.max(...sparklineData);
41632
- const range = max - min || 1;
41633
- const points = sparklineData.map((v, i) => {
41634
- const x = pad + i / (sparklineData.length - 1) * (w - pad * 2);
41635
- const y = pad + (1 - (v - min) / range) * (h - pad * 2);
41636
- return `${x},${y}`;
41637
- }).join(" ");
41638
- const trending = sparklineData[sparklineData.length - 1] >= sparklineData[0];
41639
- const strokeColor = trending ? "var(--color-success)" : "var(--color-error)";
41640
- return /* @__PURE__ */ jsxRuntime.jsx("svg", { width: w, height: h, viewBox: `0 0 ${w} ${h}`, className: "flex-shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(
41641
- "polyline",
41642
- {
41643
- fill: "none",
41644
- stroke: strokeColor,
41645
- strokeWidth: "2",
41646
- strokeLinecap: "round",
41647
- strokeLinejoin: "round",
41648
- points
41649
- }
41650
- ) });
41651
- })()
42262
+ sparklineData && sparklineData.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(exports.Sparkline, { data: sparklineData, color: "auto" })
41652
42263
  ] })
41653
42264
  ] }),
41654
42265
  action && /* @__PURE__ */ jsxRuntime.jsxs(
@@ -43735,6 +44346,7 @@ var init_component_registry_generated = __esm({
43735
44346
  init_DataGrid();
43736
44347
  init_DataList();
43737
44348
  init_DataTable();
44349
+ init_DateRangePicker();
43738
44350
  init_DateRangeSelector();
43739
44351
  init_DayCell();
43740
44352
  init_DebuggerBoard();
@@ -43867,6 +44479,7 @@ var init_component_registry_generated = __esm({
43867
44479
  init_Skeleton();
43868
44480
  init_SocialProof();
43869
44481
  init_SortableList();
44482
+ init_Sparkline();
43870
44483
  init_Split();
43871
44484
  init_SplitPane();
43872
44485
  init_SplitSection();
@@ -44015,6 +44628,7 @@ var init_component_registry_generated = __esm({
44015
44628
  "DataGrid": DataGrid,
44016
44629
  "DataList": DataList,
44017
44630
  "DataTable": DataTable,
44631
+ "DateRangePicker": exports.DateRangePicker,
44018
44632
  "DateRangeSelector": exports.DateRangeSelector,
44019
44633
  "DayCell": DayCell,
44020
44634
  "DebuggerBoard": DebuggerBoard,
@@ -44176,6 +44790,7 @@ var init_component_registry_generated = __esm({
44176
44790
  "SortableList": exports.SortableList,
44177
44791
  "Spacer": SpacerPattern,
44178
44792
  "SpacerPattern": SpacerPattern,
44793
+ "Sparkline": exports.Sparkline,
44179
44794
  "Spinner": SpinnerPattern,
44180
44795
  "SpinnerPattern": SpinnerPattern,
44181
44796
  "Split": exports.Split,
@@ -45180,6 +45795,7 @@ var init_atoms = __esm({
45180
45795
  init_Icon();
45181
45796
  init_ProgressBar();
45182
45797
  init_Radio();
45798
+ init_Sparkline();
45183
45799
  init_Switch();
45184
45800
  init_Spacer();
45185
45801
  init_Stack();