@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.
@@ -2635,6 +2635,67 @@ var init_Radio = __esm({
2635
2635
  Radio.displayName = "Radio";
2636
2636
  }
2637
2637
  });
2638
+ var COLOR_VAR, Sparkline;
2639
+ var init_Sparkline = __esm({
2640
+ "components/atoms/Sparkline.tsx"() {
2641
+ init_cn();
2642
+ COLOR_VAR = {
2643
+ primary: "var(--color-primary)",
2644
+ success: "var(--color-success)",
2645
+ warning: "var(--color-warning)",
2646
+ error: "var(--color-error)",
2647
+ info: "var(--color-info)",
2648
+ muted: "var(--color-muted-foreground)"
2649
+ };
2650
+ Sparkline = ({
2651
+ data,
2652
+ color = "auto",
2653
+ width = 80,
2654
+ height = 32,
2655
+ strokeWidth = 2,
2656
+ fill = false,
2657
+ className
2658
+ }) => {
2659
+ if (data.length < 2) return null;
2660
+ const pad = 2;
2661
+ const min = Math.min(...data);
2662
+ const max = Math.max(...data);
2663
+ const range = max - min || 1;
2664
+ const points = data.map((v, i) => {
2665
+ const x = pad + i / (data.length - 1) * (width - pad * 2);
2666
+ const y = pad + (1 - (v - min) / range) * (height - pad * 2);
2667
+ return `${x},${y}`;
2668
+ }).join(" ");
2669
+ const resolvedColor = color === "auto" ? data[data.length - 1] >= data[0] ? COLOR_VAR.success : COLOR_VAR.error : COLOR_VAR[color];
2670
+ const areaPath = fill ? `M ${pad},${height - pad} L ${points.split(" ").join(" L ")} L ${width - pad},${height - pad} Z` : null;
2671
+ return /* @__PURE__ */ jsxs(
2672
+ "svg",
2673
+ {
2674
+ width,
2675
+ height,
2676
+ viewBox: `0 0 ${width} ${height}`,
2677
+ className: cn("flex-shrink-0", className),
2678
+ "aria-hidden": "true",
2679
+ children: [
2680
+ areaPath && /* @__PURE__ */ jsx("path", { d: areaPath, fill: resolvedColor, opacity: 0.15 }),
2681
+ /* @__PURE__ */ jsx(
2682
+ "polyline",
2683
+ {
2684
+ fill: "none",
2685
+ stroke: resolvedColor,
2686
+ strokeWidth,
2687
+ strokeLinecap: "round",
2688
+ strokeLinejoin: "round",
2689
+ points
2690
+ }
2691
+ )
2692
+ ]
2693
+ }
2694
+ );
2695
+ };
2696
+ Sparkline.displayName = "Sparkline";
2697
+ }
2698
+ });
2638
2699
  var Switch;
2639
2700
  var init_Switch = __esm({
2640
2701
  "components/atoms/Switch.tsx"() {
@@ -7708,7 +7769,7 @@ var init_MapView = __esm({
7708
7769
  shadowSize: [41, 41]
7709
7770
  });
7710
7771
  L.Marker.prototype.options.icon = defaultIcon;
7711
- const { useEffect: useEffect70, useRef: useRef66, useCallback: useCallback124, useState: useState106 } = React75__default;
7772
+ const { useEffect: useEffect70, useRef: useRef66, useCallback: useCallback126, useState: useState107 } = React75__default;
7712
7773
  const { Typography: Typography2 } = await Promise.resolve().then(() => (init_Typography(), Typography_exports));
7713
7774
  const { useEventBus: useEventBus2 } = await Promise.resolve().then(() => (init_useEventBus(), useEventBus_exports));
7714
7775
  function MapUpdater({ centerLat, centerLng, zoom }) {
@@ -7753,8 +7814,8 @@ var init_MapView = __esm({
7753
7814
  showAttribution = true
7754
7815
  }) {
7755
7816
  const eventBus = useEventBus2();
7756
- const [clickedPosition, setClickedPosition] = useState106(null);
7757
- const handleMapClick = useCallback124((lat, lng) => {
7817
+ const [clickedPosition, setClickedPosition] = useState107(null);
7818
+ const handleMapClick = useCallback126((lat, lng) => {
7758
7819
  if (showClickedPin) {
7759
7820
  setClickedPosition({ lat, lng });
7760
7821
  }
@@ -7763,7 +7824,7 @@ var init_MapView = __esm({
7763
7824
  eventBus.emit(`UI:${mapClickEvent}`, { latitude: lat, longitude: lng });
7764
7825
  }
7765
7826
  }, [onMapClick, mapClickEvent, eventBus, showClickedPin]);
7766
- const handleMarkerClick = useCallback124((marker) => {
7827
+ const handleMarkerClick = useCallback126((marker) => {
7767
7828
  onMarkerClick?.(marker);
7768
7829
  if (markerClickEvent) {
7769
7830
  eventBus.emit(`UI:${markerClickEvent}`, { ...marker });
@@ -17755,7 +17816,7 @@ var init_CastleTemplate = __esm({
17755
17816
  CastleTemplate.displayName = "CastleTemplate";
17756
17817
  }
17757
17818
  });
17758
- var CHART_COLORS, BarChart, PieChart, LineChart, Chart;
17819
+ var CHART_COLORS, seriesColor, monthFormatter, formatTimeLabel, BarChart, PieChart, LineChart, ScatterChart, Chart;
17759
17820
  var init_Chart = __esm({
17760
17821
  "components/organisms/Chart.tsx"() {
17761
17822
  "use client";
@@ -17775,38 +17836,159 @@ var init_Chart = __esm({
17775
17836
  "var(--color-info)",
17776
17837
  "var(--color-accent)"
17777
17838
  ];
17778
- BarChart = ({ data, height, showValues }) => {
17779
- const maxValue = Math.max(...data.map((d) => d.value), 1);
17780
- return /* @__PURE__ */ jsx(HStack, { gap: "xs", align: "end", className: "w-full", style: { height }, children: data.map((point, idx) => {
17781
- const barHeight = point.value / maxValue * 100;
17782
- const color = point.color || CHART_COLORS[idx % CHART_COLORS.length];
17783
- return /* @__PURE__ */ jsxs(VStack, { gap: "xs", align: "center", flex: true, className: "min-w-0", children: [
17784
- showValues && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", className: "tabular-nums", children: point.value }),
17785
- /* @__PURE__ */ jsx(
17786
- Box,
17787
- {
17788
- className: cn(
17789
- "w-full rounded-t-sm transition-all duration-500 ease-out min-h-[4px]"
17790
- ),
17791
- style: {
17792
- height: `${barHeight}%`,
17793
- backgroundColor: color
17794
- }
17839
+ seriesColor = (series, idx) => series.color ?? CHART_COLORS[idx % CHART_COLORS.length];
17840
+ monthFormatter = new Intl.DateTimeFormat(void 0, {
17841
+ month: "short",
17842
+ year: "2-digit"
17843
+ });
17844
+ formatTimeLabel = (raw) => {
17845
+ const parsed = new Date(raw);
17846
+ if (Number.isNaN(parsed.getTime())) return raw;
17847
+ return monthFormatter.format(parsed);
17848
+ };
17849
+ BarChart = ({ series, height, showValues, stack, timeAxis, histogram = false, onPointClick }) => {
17850
+ const categories = useMemo(() => {
17851
+ const set = [];
17852
+ const seen = /* @__PURE__ */ new Set();
17853
+ for (const s of series) {
17854
+ for (const p2 of s.data) {
17855
+ if (!seen.has(p2.label)) {
17856
+ seen.add(p2.label);
17857
+ set.push(p2.label);
17795
17858
  }
17796
- ),
17797
- /* @__PURE__ */ jsx(
17798
- Typography,
17799
- {
17800
- variant: "caption",
17801
- color: "secondary",
17802
- className: "truncate w-full text-center",
17803
- children: point.label
17859
+ }
17860
+ }
17861
+ return set;
17862
+ }, [series]);
17863
+ const valueAt = useCallback(
17864
+ (s, label) => {
17865
+ const p2 = s.data.find((d) => d.label === label);
17866
+ return p2 ? p2.value : 0;
17867
+ },
17868
+ []
17869
+ );
17870
+ const columnTotals = useMemo(() => {
17871
+ if (stack === "none") return null;
17872
+ return categories.map(
17873
+ (label) => series.reduce((sum, s) => sum + valueAt(s, label), 0)
17874
+ );
17875
+ }, [categories, series, stack, valueAt]);
17876
+ const maxValue = useMemo(() => {
17877
+ if (stack === "normalize") return 100;
17878
+ if (stack === "stack" && columnTotals) {
17879
+ return Math.max(...columnTotals, 1);
17880
+ }
17881
+ let m = 1;
17882
+ for (const s of series) {
17883
+ for (const p2 of s.data) if (p2.value > m) m = p2.value;
17884
+ }
17885
+ return m;
17886
+ }, [series, stack, columnTotals]);
17887
+ return /* @__PURE__ */ jsx(
17888
+ HStack,
17889
+ {
17890
+ gap: histogram ? "none" : "xs",
17891
+ align: "end",
17892
+ className: "w-full",
17893
+ style: { height },
17894
+ children: categories.map((label, catIdx) => {
17895
+ const displayLabel = timeAxis ? formatTimeLabel(label) : label;
17896
+ if (stack === "none") {
17897
+ return /* @__PURE__ */ jsxs(
17898
+ VStack,
17899
+ {
17900
+ gap: "xs",
17901
+ align: "center",
17902
+ flex: true,
17903
+ className: "min-w-0",
17904
+ children: [
17905
+ /* @__PURE__ */ jsx(HStack, { gap: histogram ? "none" : "xs", align: "end", className: "w-full", style: { height: "100%" }, children: series.map((s, sIdx) => {
17906
+ const value = valueAt(s, label);
17907
+ const barHeight = value / maxValue * 100;
17908
+ const color = seriesColor(s, sIdx);
17909
+ return /* @__PURE__ */ jsx(
17910
+ Box,
17911
+ {
17912
+ className: cn(
17913
+ "rounded-t-sm transition-all duration-500 ease-out min-h-[4px] cursor-pointer hover:opacity-80",
17914
+ histogram ? "flex-1 mx-0" : "flex-1"
17915
+ ),
17916
+ style: {
17917
+ height: `${barHeight}%`,
17918
+ backgroundColor: color
17919
+ },
17920
+ onClick: () => onPointClick?.(
17921
+ { label, value, color },
17922
+ s.name
17923
+ ),
17924
+ title: `${s.name}: ${value}`
17925
+ },
17926
+ s.name
17927
+ );
17928
+ }) }),
17929
+ showValues && series.length === 1 && /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", className: "tabular-nums", children: valueAt(series[0], label) }),
17930
+ /* @__PURE__ */ jsx(
17931
+ Typography,
17932
+ {
17933
+ variant: "caption",
17934
+ color: "secondary",
17935
+ className: "truncate w-full text-center",
17936
+ children: displayLabel
17937
+ }
17938
+ )
17939
+ ]
17940
+ },
17941
+ label
17942
+ );
17804
17943
  }
17805
- )
17806
- ] }, point.label);
17807
- }) });
17944
+ const total = columnTotals?.[catIdx] ?? 1;
17945
+ return /* @__PURE__ */ jsxs(
17946
+ VStack,
17947
+ {
17948
+ gap: "xs",
17949
+ align: "center",
17950
+ flex: true,
17951
+ className: "min-w-0",
17952
+ children: [
17953
+ /* @__PURE__ */ jsx(VStack, { gap: "none", className: "w-full", style: { height: "100%" }, justify: "end", children: series.map((s, sIdx) => {
17954
+ const value = valueAt(s, label);
17955
+ const ratio = stack === "normalize" ? total === 0 ? 0 : value / total * 100 : value / maxValue * 100;
17956
+ const color = seriesColor(s, sIdx);
17957
+ return /* @__PURE__ */ jsx(
17958
+ Box,
17959
+ {
17960
+ className: "w-full transition-all duration-500 ease-out cursor-pointer hover:opacity-80",
17961
+ style: {
17962
+ height: `${ratio}%`,
17963
+ backgroundColor: color
17964
+ },
17965
+ onClick: () => onPointClick?.(
17966
+ { label, value, color },
17967
+ s.name
17968
+ ),
17969
+ title: `${s.name}: ${value}`
17970
+ },
17971
+ s.name
17972
+ );
17973
+ }) }),
17974
+ /* @__PURE__ */ jsx(
17975
+ Typography,
17976
+ {
17977
+ variant: "caption",
17978
+ color: "secondary",
17979
+ className: "truncate w-full text-center",
17980
+ children: displayLabel
17981
+ }
17982
+ )
17983
+ ]
17984
+ },
17985
+ label
17986
+ );
17987
+ })
17988
+ }
17989
+ );
17808
17990
  };
17809
- PieChart = ({ data, height, showValues, donut = false }) => {
17991
+ PieChart = ({ data, height, showValues, donut = false, onPointClick }) => {
17810
17992
  const total = data.reduce((sum, d) => sum + d.value, 0);
17811
17993
  const size = Math.min(height, 200);
17812
17994
  const radius = size / 2 - 8;
@@ -17852,7 +18034,11 @@ var init_Chart = __esm({
17852
18034
  fill: seg.color,
17853
18035
  stroke: "var(--color-card)",
17854
18036
  strokeWidth: "2",
17855
- className: "transition-opacity duration-200 hover:opacity-80"
18037
+ className: "transition-opacity duration-200 hover:opacity-80 cursor-pointer",
18038
+ onClick: () => onPointClick?.(
18039
+ { label: seg.label, value: seg.value, color: seg.color },
18040
+ "default"
18041
+ )
17856
18042
  },
17857
18043
  idx
17858
18044
  )),
@@ -17887,56 +18073,243 @@ var init_Chart = __esm({
17887
18073
  ] }, idx)) })
17888
18074
  ] });
17889
18075
  };
17890
- LineChart = ({ data, height, showValues, fill = false }) => {
17891
- const maxValue = Math.max(...data.map((d) => d.value), 1);
18076
+ LineChart = ({ series, height, showValues, fill = false, timeAxis, onPointClick }) => {
17892
18077
  const width = 400;
17893
18078
  const padding = { top: 20, right: 20, bottom: 30, left: 40 };
17894
18079
  const chartWidth = width - padding.left - padding.right;
17895
18080
  const chartHeight = height - padding.top - padding.bottom;
17896
- const points = useMemo(() => {
17897
- return data.map((point, idx) => ({
17898
- x: padding.left + idx / Math.max(data.length - 1, 1) * chartWidth,
17899
- y: padding.top + chartHeight - point.value / maxValue * chartHeight,
17900
- ...point
17901
- }));
17902
- }, [data, maxValue, chartWidth, chartHeight, padding]);
17903
- const linePath = points.map((p2, i) => `${i === 0 ? "M" : "L"} ${p2.x} ${p2.y}`).join(" ");
17904
- const areaPath = `${linePath} L ${points[points.length - 1]?.x ?? 0} ${padding.top + chartHeight} L ${padding.left} ${padding.top + chartHeight} Z`;
17905
- return /* @__PURE__ */ jsxs("svg", { width: "100%", height, viewBox: `0 0 ${width} ${height}`, preserveAspectRatio: "xMidYMid meet", children: [
17906
- [0, 0.25, 0.5, 0.75, 1].map((frac) => {
17907
- const y = padding.top + chartHeight * (1 - frac);
17908
- return /* @__PURE__ */ jsx(
17909
- "line",
17910
- {
17911
- x1: padding.left,
17912
- y1: y,
17913
- x2: width - padding.right,
17914
- y2: y,
17915
- stroke: "var(--color-border)",
17916
- strokeDasharray: "4 4",
17917
- opacity: 0.5
17918
- },
17919
- frac
17920
- );
17921
- }),
17922
- fill && /* @__PURE__ */ jsx("path", { d: areaPath, fill: "var(--color-primary)", opacity: 0.1 }),
17923
- /* @__PURE__ */ jsx("path", { d: linePath, fill: "none", stroke: "var(--color-primary)", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }),
17924
- points.map((p2, idx) => /* @__PURE__ */ jsxs("g", { children: [
17925
- /* @__PURE__ */ jsx("circle", { cx: p2.x, cy: p2.y, r: "4", fill: "var(--color-card)", stroke: "var(--color-primary)", strokeWidth: "2" }),
17926
- showValues && /* @__PURE__ */ jsx("text", { x: p2.x, y: p2.y - 10, textAnchor: "middle", fill: "var(--color-foreground)", fontSize: "10", fontWeight: "500", children: p2.value }),
17927
- /* @__PURE__ */ jsx(
17928
- "text",
17929
- {
17930
- x: p2.x,
17931
- y: height - 8,
17932
- textAnchor: "middle",
17933
- fill: "var(--color-muted-foreground)",
17934
- fontSize: "9",
17935
- children: p2.label
18081
+ const labels = useMemo(() => {
18082
+ const seen = /* @__PURE__ */ new Set();
18083
+ const out = [];
18084
+ for (const s of series) {
18085
+ for (const p2 of s.data) {
18086
+ if (!seen.has(p2.label)) {
18087
+ seen.add(p2.label);
18088
+ out.push(p2.label);
17936
18089
  }
17937
- )
17938
- ] }, idx))
17939
- ] });
18090
+ }
18091
+ }
18092
+ return out;
18093
+ }, [series]);
18094
+ const maxValue = useMemo(() => {
18095
+ let m = 1;
18096
+ for (const s of series) {
18097
+ for (const p2 of s.data) if (p2.value > m) m = p2.value;
18098
+ }
18099
+ return m;
18100
+ }, [series]);
18101
+ const xFor = useCallback(
18102
+ (idx) => padding.left + idx / Math.max(labels.length - 1, 1) * chartWidth,
18103
+ [labels.length, chartWidth, padding.left]
18104
+ );
18105
+ const yFor = useCallback(
18106
+ (value) => padding.top + chartHeight - value / maxValue * chartHeight,
18107
+ [maxValue, chartHeight, padding.top]
18108
+ );
18109
+ return /* @__PURE__ */ jsxs(
18110
+ "svg",
18111
+ {
18112
+ width: "100%",
18113
+ height,
18114
+ viewBox: `0 0 ${width} ${height}`,
18115
+ preserveAspectRatio: "xMidYMid meet",
18116
+ children: [
18117
+ [0, 0.25, 0.5, 0.75, 1].map((frac) => {
18118
+ const y = padding.top + chartHeight * (1 - frac);
18119
+ return /* @__PURE__ */ jsx(
18120
+ "line",
18121
+ {
18122
+ x1: padding.left,
18123
+ y1: y,
18124
+ x2: width - padding.right,
18125
+ y2: y,
18126
+ stroke: "var(--color-border)",
18127
+ strokeDasharray: "4 4",
18128
+ opacity: 0.5
18129
+ },
18130
+ frac
18131
+ );
18132
+ }),
18133
+ series.map((s, sIdx) => {
18134
+ const color = seriesColor(s, sIdx);
18135
+ const points = labels.map((label, idx) => {
18136
+ const point = s.data.find((d) => d.label === label);
18137
+ return {
18138
+ x: xFor(idx),
18139
+ y: yFor(point ? point.value : 0),
18140
+ value: point ? point.value : 0,
18141
+ label
18142
+ };
18143
+ });
18144
+ const linePath = points.map((p2, i) => `${i === 0 ? "M" : "L"} ${p2.x} ${p2.y}`).join(" ");
18145
+ const areaPath = `${linePath} L ${points[points.length - 1]?.x ?? 0} ${padding.top + chartHeight} L ${padding.left} ${padding.top + chartHeight} Z`;
18146
+ return /* @__PURE__ */ jsxs("g", { children: [
18147
+ fill && /* @__PURE__ */ jsx(
18148
+ "path",
18149
+ {
18150
+ d: areaPath,
18151
+ fill: color,
18152
+ opacity: series.length > 1 ? 0.08 : 0.1
18153
+ }
18154
+ ),
18155
+ /* @__PURE__ */ jsx(
18156
+ "path",
18157
+ {
18158
+ d: linePath,
18159
+ fill: "none",
18160
+ stroke: color,
18161
+ strokeWidth: "2",
18162
+ strokeLinecap: "round",
18163
+ strokeLinejoin: "round",
18164
+ strokeDasharray: s.dashed ? "6 4" : void 0
18165
+ }
18166
+ ),
18167
+ points.map((p2, idx) => /* @__PURE__ */ jsxs("g", { children: [
18168
+ /* @__PURE__ */ jsx(
18169
+ "circle",
18170
+ {
18171
+ cx: p2.x,
18172
+ cy: p2.y,
18173
+ r: "4",
18174
+ fill: "var(--color-card)",
18175
+ stroke: color,
18176
+ strokeWidth: "2",
18177
+ className: "cursor-pointer",
18178
+ onClick: () => onPointClick?.(
18179
+ { label: p2.label, value: p2.value, color },
18180
+ s.name
18181
+ )
18182
+ }
18183
+ ),
18184
+ showValues && series.length === 1 && /* @__PURE__ */ jsx(
18185
+ "text",
18186
+ {
18187
+ x: p2.x,
18188
+ y: p2.y - 10,
18189
+ textAnchor: "middle",
18190
+ fill: "var(--color-foreground)",
18191
+ fontSize: "10",
18192
+ fontWeight: "500",
18193
+ children: p2.value
18194
+ }
18195
+ )
18196
+ ] }, idx))
18197
+ ] }, s.name);
18198
+ }),
18199
+ labels.map((label, idx) => /* @__PURE__ */ jsx(
18200
+ "text",
18201
+ {
18202
+ x: xFor(idx),
18203
+ y: height - 8,
18204
+ textAnchor: "middle",
18205
+ fill: "var(--color-muted-foreground)",
18206
+ fontSize: "9",
18207
+ children: timeAxis ? formatTimeLabel(label) : label
18208
+ },
18209
+ label
18210
+ ))
18211
+ ]
18212
+ }
18213
+ );
18214
+ };
18215
+ ScatterChart = ({ data, height, onPointClick }) => {
18216
+ const width = 400;
18217
+ const padding = { top: 20, right: 20, bottom: 30, left: 40 };
18218
+ const chartWidth = width - padding.left - padding.right;
18219
+ const chartHeight = height - padding.top - padding.bottom;
18220
+ const { minX, maxX, minY, maxY } = useMemo(() => {
18221
+ if (data.length === 0) {
18222
+ return { minX: 0, maxX: 1, minY: 0, maxY: 1 };
18223
+ }
18224
+ let mnX = data[0].x;
18225
+ let mxX = data[0].x;
18226
+ let mnY = data[0].y;
18227
+ let mxY = data[0].y;
18228
+ for (const p2 of data) {
18229
+ if (p2.x < mnX) mnX = p2.x;
18230
+ if (p2.x > mxX) mxX = p2.x;
18231
+ if (p2.y < mnY) mnY = p2.y;
18232
+ if (p2.y > mxY) mxY = p2.y;
18233
+ }
18234
+ return { minX: mnX, maxX: mxX, minY: mnY, maxY: mxY };
18235
+ }, [data]);
18236
+ const rangeX = maxX - minX || 1;
18237
+ const rangeY = maxY - minY || 1;
18238
+ return /* @__PURE__ */ jsxs(
18239
+ "svg",
18240
+ {
18241
+ width: "100%",
18242
+ height,
18243
+ viewBox: `0 0 ${width} ${height}`,
18244
+ preserveAspectRatio: "xMidYMid meet",
18245
+ children: [
18246
+ [0, 0.25, 0.5, 0.75, 1].map((frac) => {
18247
+ const y = padding.top + chartHeight * (1 - frac);
18248
+ return /* @__PURE__ */ jsx(
18249
+ "line",
18250
+ {
18251
+ x1: padding.left,
18252
+ y1: y,
18253
+ x2: width - padding.right,
18254
+ y2: y,
18255
+ stroke: "var(--color-border)",
18256
+ strokeDasharray: "4 4",
18257
+ opacity: 0.5
18258
+ },
18259
+ frac
18260
+ );
18261
+ }),
18262
+ data.map((p2, idx) => {
18263
+ const cx = padding.left + (p2.x - minX) / rangeX * chartWidth;
18264
+ const cy = padding.top + chartHeight - (p2.y - minY) / rangeY * chartHeight;
18265
+ const r = p2.size ?? 5;
18266
+ const color = p2.color ?? CHART_COLORS[idx % CHART_COLORS.length];
18267
+ return /* @__PURE__ */ jsx(
18268
+ "circle",
18269
+ {
18270
+ cx,
18271
+ cy,
18272
+ r,
18273
+ fill: color,
18274
+ opacity: 0.7,
18275
+ className: "cursor-pointer hover:opacity-100",
18276
+ onClick: () => onPointClick?.(
18277
+ {
18278
+ label: p2.label ?? `(${p2.x}, ${p2.y})`,
18279
+ value: p2.y,
18280
+ color
18281
+ },
18282
+ "default"
18283
+ ),
18284
+ children: /* @__PURE__ */ jsx("title", { children: p2.label ?? `(${p2.x}, ${p2.y})` })
18285
+ },
18286
+ idx
18287
+ );
18288
+ }),
18289
+ /* @__PURE__ */ jsx(
18290
+ "text",
18291
+ {
18292
+ x: padding.left,
18293
+ y: height - 8,
18294
+ fill: "var(--color-muted-foreground)",
18295
+ fontSize: "9",
18296
+ children: minX.toFixed(1)
18297
+ }
18298
+ ),
18299
+ /* @__PURE__ */ jsx(
18300
+ "text",
18301
+ {
18302
+ x: width - padding.right,
18303
+ y: height - 8,
18304
+ textAnchor: "end",
18305
+ fill: "var(--color-muted-foreground)",
18306
+ fontSize: "9",
18307
+ children: maxX.toFixed(1)
18308
+ }
18309
+ )
18310
+ ]
18311
+ }
18312
+ );
17940
18313
  };
17941
18314
  Chart = ({
17942
18315
  title,
@@ -17944,9 +18317,13 @@ var init_Chart = __esm({
17944
18317
  chartType = "bar",
17945
18318
  series,
17946
18319
  data: simpleData,
18320
+ scatterData,
17947
18321
  height = 200,
17948
18322
  showLegend = true,
17949
18323
  showValues = false,
18324
+ stack = "none",
18325
+ timeAxis = false,
18326
+ drillEvent,
17950
18327
  actions,
17951
18328
  entity,
17952
18329
  isLoading = false,
@@ -17963,11 +18340,25 @@ var init_Chart = __esm({
17963
18340
  },
17964
18341
  [eventBus]
17965
18342
  );
17966
- const normalizedData = useMemo(() => {
17967
- if (simpleData) return simpleData;
17968
- if (series && series.length > 0) return series[0].data;
18343
+ const handlePointClick = useCallback(
18344
+ (point, seriesName) => {
18345
+ if (drillEvent) {
18346
+ eventBus.emit(`UI:${drillEvent}`, {
18347
+ label: point.label,
18348
+ value: point.value,
18349
+ seriesLabel: seriesName === "default" ? void 0 : seriesName
18350
+ });
18351
+ }
18352
+ },
18353
+ [drillEvent, eventBus]
18354
+ );
18355
+ const normalizedSeries = useMemo(() => {
18356
+ if (series && series.length > 0) return series;
18357
+ if (simpleData) return [{ name: "default", data: simpleData }];
17969
18358
  return [];
17970
18359
  }, [simpleData, series]);
18360
+ const firstSeriesData = normalizedSeries[0]?.data ?? [];
18361
+ const hasContent = chartType === "scatter" ? (scatterData?.length ?? 0) > 0 : normalizedSeries.some((s) => s.data.length > 0);
17971
18362
  if (isLoading) {
17972
18363
  return /* @__PURE__ */ jsx(LoadingState, { message: "Loading chart...", className });
17973
18364
  }
@@ -17981,7 +18372,7 @@ var init_Chart = __esm({
17981
18372
  }
17982
18373
  );
17983
18374
  }
17984
- if (normalizedData.length === 0) {
18375
+ if (!hasContent) {
17985
18376
  return /* @__PURE__ */ jsx(EmptyState, { title: t("empty.noData"), description: t("empty.noData"), className });
17986
18377
  }
17987
18378
  return /* @__PURE__ */ jsx(Card, { className: cn("p-6", className), children: /* @__PURE__ */ jsxs(VStack, { gap: "md", children: [
@@ -18002,18 +18393,84 @@ var init_Chart = __esm({
18002
18393
  )) })
18003
18394
  ] }),
18004
18395
  /* @__PURE__ */ jsxs(Box, { className: "w-full", children: [
18005
- chartType === "bar" && /* @__PURE__ */ jsx(BarChart, { data: normalizedData, height, showValues }),
18006
- chartType === "line" && /* @__PURE__ */ jsx(LineChart, { data: normalizedData, height, showValues }),
18007
- chartType === "area" && /* @__PURE__ */ jsx(LineChart, { data: normalizedData, height, showValues, fill: true }),
18008
- chartType === "pie" && /* @__PURE__ */ jsx(PieChart, { data: normalizedData, height, showValues: showLegend }),
18009
- chartType === "donut" && /* @__PURE__ */ jsx(PieChart, { data: normalizedData, height, showValues: showLegend, donut: true })
18396
+ chartType === "bar" && /* @__PURE__ */ jsx(
18397
+ BarChart,
18398
+ {
18399
+ series: normalizedSeries,
18400
+ height,
18401
+ showValues,
18402
+ stack,
18403
+ timeAxis,
18404
+ onPointClick: handlePointClick
18405
+ }
18406
+ ),
18407
+ chartType === "histogram" && /* @__PURE__ */ jsx(
18408
+ BarChart,
18409
+ {
18410
+ series: normalizedSeries,
18411
+ height,
18412
+ showValues,
18413
+ stack: "none",
18414
+ timeAxis: false,
18415
+ histogram: true,
18416
+ onPointClick: handlePointClick
18417
+ }
18418
+ ),
18419
+ chartType === "line" && /* @__PURE__ */ jsx(
18420
+ LineChart,
18421
+ {
18422
+ series: normalizedSeries,
18423
+ height,
18424
+ showValues,
18425
+ timeAxis,
18426
+ onPointClick: handlePointClick
18427
+ }
18428
+ ),
18429
+ chartType === "area" && /* @__PURE__ */ jsx(
18430
+ LineChart,
18431
+ {
18432
+ series: normalizedSeries,
18433
+ height,
18434
+ showValues,
18435
+ timeAxis,
18436
+ fill: true,
18437
+ onPointClick: handlePointClick
18438
+ }
18439
+ ),
18440
+ chartType === "pie" && /* @__PURE__ */ jsx(
18441
+ PieChart,
18442
+ {
18443
+ data: firstSeriesData,
18444
+ height,
18445
+ showValues: showLegend,
18446
+ onPointClick: handlePointClick
18447
+ }
18448
+ ),
18449
+ chartType === "donut" && /* @__PURE__ */ jsx(
18450
+ PieChart,
18451
+ {
18452
+ data: firstSeriesData,
18453
+ height,
18454
+ showValues: showLegend,
18455
+ donut: true,
18456
+ onPointClick: handlePointClick
18457
+ }
18458
+ ),
18459
+ chartType === "scatter" && /* @__PURE__ */ jsx(
18460
+ ScatterChart,
18461
+ {
18462
+ data: scatterData ?? [],
18463
+ height,
18464
+ onPointClick: handlePointClick
18465
+ }
18466
+ )
18010
18467
  ] }),
18011
- showLegend && series && series.length > 1 && /* @__PURE__ */ jsx(HStack, { gap: "md", justify: "center", wrap: true, children: series.map((s, idx) => /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
18468
+ showLegend && normalizedSeries.length > 1 && /* @__PURE__ */ jsx(HStack, { gap: "md", justify: "center", wrap: true, children: normalizedSeries.map((s, idx) => /* @__PURE__ */ jsxs(HStack, { gap: "xs", align: "center", children: [
18012
18469
  /* @__PURE__ */ jsx(
18013
18470
  Box,
18014
18471
  {
18015
18472
  className: "w-3 h-3 rounded-full flex-shrink-0",
18016
- style: { backgroundColor: s.color || CHART_COLORS[idx % CHART_COLORS.length] }
18473
+ style: { backgroundColor: seriesColor(s, idx) }
18017
18474
  }
18018
18475
  ),
18019
18476
  /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: s.name })
@@ -23168,6 +23625,151 @@ var init_FlipCard = __esm({
23168
23625
  FlipCard.displayName = "FlipCard";
23169
23626
  }
23170
23627
  });
23628
+ function toISODate(d) {
23629
+ return d.toISOString().slice(0, 10);
23630
+ }
23631
+ function startOfMonth(d) {
23632
+ return new Date(d.getFullYear(), d.getMonth(), 1);
23633
+ }
23634
+ function startOfQuarter(d) {
23635
+ return new Date(d.getFullYear(), Math.floor(d.getMonth() / 3) * 3, 1);
23636
+ }
23637
+ function startOfYear(d) {
23638
+ return new Date(d.getFullYear(), 0, 1);
23639
+ }
23640
+ function daysAgo(n) {
23641
+ const d = /* @__PURE__ */ new Date();
23642
+ d.setDate(d.getDate() - n);
23643
+ return d;
23644
+ }
23645
+ var DEFAULT_PRESETS, DateRangePicker;
23646
+ var init_DateRangePicker = __esm({
23647
+ "components/molecules/DateRangePicker.tsx"() {
23648
+ "use client";
23649
+ init_cn();
23650
+ init_Button();
23651
+ init_Input();
23652
+ init_Stack();
23653
+ init_Typography();
23654
+ init_useEventBus();
23655
+ DEFAULT_PRESETS = [
23656
+ {
23657
+ label: "Last 7 days",
23658
+ value: "7d",
23659
+ range: () => ({ from: toISODate(daysAgo(7)), to: toISODate(/* @__PURE__ */ new Date()) })
23660
+ },
23661
+ {
23662
+ label: "Last 30 days",
23663
+ value: "30d",
23664
+ range: () => ({ from: toISODate(daysAgo(30)), to: toISODate(/* @__PURE__ */ new Date()) })
23665
+ },
23666
+ {
23667
+ label: "This Month",
23668
+ value: "month",
23669
+ range: () => ({ from: toISODate(startOfMonth(/* @__PURE__ */ new Date())), to: toISODate(/* @__PURE__ */ new Date()) })
23670
+ },
23671
+ {
23672
+ label: "This Quarter",
23673
+ value: "quarter",
23674
+ range: () => ({ from: toISODate(startOfQuarter(/* @__PURE__ */ new Date())), to: toISODate(/* @__PURE__ */ new Date()) })
23675
+ },
23676
+ {
23677
+ label: "YTD",
23678
+ value: "ytd",
23679
+ range: () => ({ from: toISODate(startOfYear(/* @__PURE__ */ new Date())), to: toISODate(/* @__PURE__ */ new Date()) })
23680
+ }
23681
+ ];
23682
+ DateRangePicker = ({
23683
+ from: fromProp,
23684
+ to: toProp,
23685
+ event,
23686
+ onChange,
23687
+ presets = DEFAULT_PRESETS,
23688
+ fromLabel = "From",
23689
+ toLabel = "To",
23690
+ className
23691
+ }) => {
23692
+ const eventBus = useEventBus();
23693
+ const [from, setFrom] = useState(fromProp ?? "");
23694
+ const [to, setTo] = useState(toProp ?? "");
23695
+ const [activePreset, setActivePreset] = useState(null);
23696
+ const emit = useCallback(
23697
+ (range) => {
23698
+ onChange?.(range);
23699
+ if (event) eventBus.emit(`UI:${event}`, range);
23700
+ },
23701
+ [onChange, event, eventBus]
23702
+ );
23703
+ const handleFromChange = useCallback(
23704
+ (next) => {
23705
+ setFrom(next);
23706
+ setActivePreset(null);
23707
+ emit({ from: next, to });
23708
+ },
23709
+ [to, emit]
23710
+ );
23711
+ const handleToChange = useCallback(
23712
+ (next) => {
23713
+ setTo(next);
23714
+ setActivePreset(null);
23715
+ emit({ from, to: next });
23716
+ },
23717
+ [from, emit]
23718
+ );
23719
+ const handlePreset = useCallback(
23720
+ (preset) => {
23721
+ const range = preset.range();
23722
+ setFrom(range.from);
23723
+ setTo(range.to);
23724
+ setActivePreset(preset.value);
23725
+ emit(range);
23726
+ },
23727
+ [emit]
23728
+ );
23729
+ const presetButtons = useMemo(
23730
+ () => presets.map((preset) => /* @__PURE__ */ jsx(
23731
+ Button,
23732
+ {
23733
+ variant: activePreset === preset.value ? "primary" : "ghost",
23734
+ size: "sm",
23735
+ onClick: () => handlePreset(preset),
23736
+ children: preset.label
23737
+ },
23738
+ preset.value
23739
+ )),
23740
+ [presets, activePreset, handlePreset]
23741
+ );
23742
+ return /* @__PURE__ */ jsxs(VStack, { gap: "sm", className: cn(className), children: [
23743
+ /* @__PURE__ */ jsxs(HStack, { gap: "md", align: "end", children: [
23744
+ /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
23745
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: fromLabel }),
23746
+ /* @__PURE__ */ jsx(
23747
+ Input,
23748
+ {
23749
+ type: "date",
23750
+ value: from,
23751
+ onChange: (e) => handleFromChange(e.target.value)
23752
+ }
23753
+ )
23754
+ ] }),
23755
+ /* @__PURE__ */ jsxs(VStack, { gap: "xs", children: [
23756
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: toLabel }),
23757
+ /* @__PURE__ */ jsx(
23758
+ Input,
23759
+ {
23760
+ type: "date",
23761
+ value: to,
23762
+ onChange: (e) => handleToChange(e.target.value)
23763
+ }
23764
+ )
23765
+ ] })
23766
+ ] }),
23767
+ presets.length > 0 && /* @__PURE__ */ jsx(HStack, { gap: "xs", wrap: true, children: presetButtons })
23768
+ ] });
23769
+ };
23770
+ DateRangePicker.displayName = "DateRangePicker";
23771
+ }
23772
+ });
23171
23773
  var DEFAULT_OPTIONS, DateRangeSelector;
23172
23774
  var init_DateRangeSelector = __esm({
23173
23775
  "components/molecules/DateRangeSelector.tsx"() {
@@ -26423,7 +27025,9 @@ var init_StatDisplay = __esm({
26423
27025
  init_Typography();
26424
27026
  init_Box();
26425
27027
  init_Stack();
27028
+ init_Sparkline();
26426
27029
  init_Icon();
27030
+ init_useEventBus();
26427
27031
  variantColor = {
26428
27032
  default: "text-foreground",
26429
27033
  primary: "text-primary",
@@ -26438,6 +27042,10 @@ var init_StatDisplay = __esm({
26438
27042
  max,
26439
27043
  target,
26440
27044
  trend,
27045
+ trendPolarity = "higher-is-better",
27046
+ trendFormat = "absolute",
27047
+ sparklineData,
27048
+ clickEvent,
26441
27049
  prefix,
26442
27050
  suffix,
26443
27051
  icon: iconProp,
@@ -26451,6 +27059,10 @@ var init_StatDisplay = __esm({
26451
27059
  isLoading = false,
26452
27060
  error = null
26453
27061
  }) => {
27062
+ const eventBus = useEventBus();
27063
+ const handleClick = useCallback(() => {
27064
+ if (clickEvent) eventBus.emit(`UI:${clickEvent}`, { metricLabel: label });
27065
+ }, [clickEvent, eventBus, label]);
26454
27066
  const ResolvedIcon = typeof iconProp === "string" ? resolveIcon(iconProp) : null;
26455
27067
  const iconSizes3 = { sm: "h-4 w-4", md: "h-5 w-5", lg: "h-6 w-6" };
26456
27068
  const valueSizes = { sm: "text-lg", md: "text-2xl", lg: "text-3xl" };
@@ -26461,7 +27073,10 @@ var init_StatDisplay = __esm({
26461
27073
  const targetPct = showTarget ? Math.max(0, Math.min(100, numericValue / target * 100)) : 0;
26462
27074
  const showTrend = typeof trend === "number" && trend !== 0 && Number.isFinite(trend);
26463
27075
  const trendUp = showTrend && trend > 0;
26464
- const trendLabel = showTrend ? `${trendUp ? "\u2191" : "\u2193"} ${Math.abs(trend)}` : "";
27076
+ const trendIsGood = trendPolarity === "lower-is-better" ? !trendUp : trendUp;
27077
+ const trendMagnitude = Math.abs(trend);
27078
+ const trendSuffix = trendFormat === "percent" ? "%" : "";
27079
+ const trendLabel = showTrend ? `${trendUp ? "\u2191" : "\u2193"} ${trendMagnitude}${trendSuffix}` : "";
26465
27080
  if (error) {
26466
27081
  return /* @__PURE__ */ jsx(Card, { className: cn(padSizes[size], className), children: /* @__PURE__ */ jsx(Typography, { variant: "small", color: "error", children: error.message }) });
26467
27082
  }
@@ -26472,38 +27087,57 @@ var init_StatDisplay = __esm({
26472
27087
  ] }) });
26473
27088
  }
26474
27089
  if (compact) {
26475
- return /* @__PURE__ */ jsxs(HStack, { gap: "sm", className: cn("items-center", className), children: [
26476
- ResolvedIcon && /* @__PURE__ */ jsx(ResolvedIcon, { className: cn(iconSizes3[size], iconColor) }),
26477
- typeof iconProp !== "string" && iconProp,
26478
- /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: label }),
26479
- /* @__PURE__ */ jsx(Typography, { variant: "h4", className: cn("font-bold", valueSizes[size], variantColor[variant]), children: displayValue }),
26480
- showTrend && /* @__PURE__ */ jsx(Typography, { variant: "caption", className: cn("font-semibold", trendUp ? "text-success" : "text-error"), children: trendLabel })
26481
- ] });
27090
+ return /* @__PURE__ */ jsxs(
27091
+ HStack,
27092
+ {
27093
+ gap: "sm",
27094
+ className: cn("items-center", clickEvent && "cursor-pointer hover:opacity-80", className),
27095
+ onClick: clickEvent ? handleClick : void 0,
27096
+ children: [
27097
+ ResolvedIcon && /* @__PURE__ */ jsx(ResolvedIcon, { className: cn(iconSizes3[size], iconColor) }),
27098
+ typeof iconProp !== "string" && iconProp,
27099
+ /* @__PURE__ */ jsx(Typography, { variant: "caption", color: "secondary", children: label }),
27100
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", className: cn("font-bold", valueSizes[size], variantColor[variant]), children: displayValue }),
27101
+ showTrend && /* @__PURE__ */ jsx(Typography, { variant: "caption", className: cn("font-semibold", trendIsGood ? "text-success" : "text-error"), children: trendLabel }),
27102
+ sparklineData && sparklineData.length > 1 && /* @__PURE__ */ jsx(Sparkline, { data: sparklineData, color: "auto", width: 60, height: 20 })
27103
+ ]
27104
+ }
27105
+ );
26482
27106
  }
26483
- return /* @__PURE__ */ jsx(Card, { className: cn(padSizes[size], className), children: /* @__PURE__ */ jsxs(HStack, { align: "start", justify: "between", children: [
26484
- /* @__PURE__ */ jsxs(VStack, { gap: "none", className: "space-y-1 flex-1", children: [
26485
- /* @__PURE__ */ jsx(Typography, { variant: "overline", color: "secondary", children: label }),
26486
- /* @__PURE__ */ jsxs(HStack, { gap: "sm", align: "end", children: [
26487
- /* @__PURE__ */ jsx(Typography, { variant: "h4", className: cn("font-bold", valueSizes[size], variantColor[variant]), children: displayValue }),
26488
- showTrend && /* @__PURE__ */ jsx(
26489
- Typography,
26490
- {
26491
- variant: "caption",
26492
- className: cn("font-semibold pb-1", trendUp ? "text-success" : "text-error"),
26493
- children: trendLabel
26494
- }
26495
- )
26496
- ] }),
26497
- showTarget && /* @__PURE__ */ jsx(Box, { className: "mt-2 h-1.5 w-full bg-muted rounded-full overflow-hidden", children: /* @__PURE__ */ jsx(
26498
- Box,
26499
- {
26500
- className: cn("h-full rounded-full transition-all", `bg-${variant === "default" ? "primary" : variant}`),
26501
- style: { width: `${targetPct}%` }
26502
- }
26503
- ) })
26504
- ] }),
26505
- (ResolvedIcon || typeof iconProp !== "string" && iconProp) && /* @__PURE__ */ jsx(Box, { className: cn("p-3 rounded-md", iconBg), children: ResolvedIcon ? /* @__PURE__ */ jsx(ResolvedIcon, { className: cn(iconSizes3[size], iconColor) }) : iconProp })
26506
- ] }) });
27107
+ return /* @__PURE__ */ jsx(
27108
+ Card,
27109
+ {
27110
+ className: cn(padSizes[size], clickEvent && "cursor-pointer hover:shadow-md transition-shadow", className),
27111
+ onClick: clickEvent ? handleClick : void 0,
27112
+ children: /* @__PURE__ */ jsxs(HStack, { align: "start", justify: "between", children: [
27113
+ /* @__PURE__ */ jsxs(VStack, { gap: "none", className: "space-y-1 flex-1", children: [
27114
+ /* @__PURE__ */ jsx(Typography, { variant: "overline", color: "secondary", children: label }),
27115
+ /* @__PURE__ */ jsxs(HStack, { gap: "sm", align: "end", children: [
27116
+ /* @__PURE__ */ jsx(Typography, { variant: "h4", className: cn("font-bold", valueSizes[size], variantColor[variant]), children: displayValue }),
27117
+ showTrend && /* @__PURE__ */ jsx(
27118
+ Typography,
27119
+ {
27120
+ variant: "caption",
27121
+ className: cn("font-semibold pb-1", trendIsGood ? "text-success" : "text-error"),
27122
+ children: trendLabel
27123
+ }
27124
+ )
27125
+ ] }),
27126
+ showTarget && /* @__PURE__ */ jsx(Box, { className: "mt-2 h-1.5 w-full bg-muted rounded-full overflow-hidden", children: /* @__PURE__ */ jsx(
27127
+ Box,
27128
+ {
27129
+ className: cn("h-full rounded-full transition-all", `bg-${variant === "default" ? "primary" : variant}`),
27130
+ style: { width: `${targetPct}%` }
27131
+ }
27132
+ ) })
27133
+ ] }),
27134
+ /* @__PURE__ */ jsxs(VStack, { gap: "xs", align: "end", children: [
27135
+ (ResolvedIcon || typeof iconProp !== "string" && iconProp) && /* @__PURE__ */ jsx(Box, { className: cn("p-3 rounded-md", iconBg), children: ResolvedIcon ? /* @__PURE__ */ jsx(ResolvedIcon, { className: cn(iconSizes3[size], iconColor) }) : iconProp }),
27136
+ sparklineData && sparklineData.length > 1 && /* @__PURE__ */ jsx(Sparkline, { data: sparklineData, color: "auto" })
27137
+ ] })
27138
+ ] })
27139
+ }
27140
+ );
26507
27141
  };
26508
27142
  StatDisplay.displayName = "StatDisplay";
26509
27143
  }
@@ -31852,6 +32486,7 @@ var init_molecules = __esm({
31852
32486
  init_ViolationAlert();
31853
32487
  init_FormSectionHeader();
31854
32488
  init_FlipCard();
32489
+ init_DateRangePicker();
31855
32490
  init_DateRangeSelector();
31856
32491
  init_ChartLegend();
31857
32492
  init_LineChart();
@@ -41408,6 +42043,7 @@ var init_StatCard = __esm({
41408
42043
  init_Box();
41409
42044
  init_Stack();
41410
42045
  init_Button();
42046
+ init_Sparkline();
41411
42047
  init_useEventBus();
41412
42048
  init_useTranslate();
41413
42049
  init_Icon();
@@ -41577,32 +42213,7 @@ var init_StatCard = __esm({
41577
42213
  ] }),
41578
42214
  /* @__PURE__ */ jsxs(VStack, { gap: "xs", align: "end", children: [
41579
42215
  Icon3 && /* @__PURE__ */ jsx(Box, { className: cn("p-3", iconBg), children: /* @__PURE__ */ jsx(Icon3, { className: cn("h-6 w-6", iconColor) }) }),
41580
- sparklineData && sparklineData.length > 1 && (() => {
41581
- const w = 80;
41582
- const h = 32;
41583
- const pad = 2;
41584
- const min = Math.min(...sparklineData);
41585
- const max = Math.max(...sparklineData);
41586
- const range = max - min || 1;
41587
- const points = sparklineData.map((v, i) => {
41588
- const x = pad + i / (sparklineData.length - 1) * (w - pad * 2);
41589
- const y = pad + (1 - (v - min) / range) * (h - pad * 2);
41590
- return `${x},${y}`;
41591
- }).join(" ");
41592
- const trending = sparklineData[sparklineData.length - 1] >= sparklineData[0];
41593
- const strokeColor = trending ? "var(--color-success)" : "var(--color-error)";
41594
- return /* @__PURE__ */ jsx("svg", { width: w, height: h, viewBox: `0 0 ${w} ${h}`, className: "flex-shrink-0", children: /* @__PURE__ */ jsx(
41595
- "polyline",
41596
- {
41597
- fill: "none",
41598
- stroke: strokeColor,
41599
- strokeWidth: "2",
41600
- strokeLinecap: "round",
41601
- strokeLinejoin: "round",
41602
- points
41603
- }
41604
- ) });
41605
- })()
42216
+ sparklineData && sparklineData.length > 1 && /* @__PURE__ */ jsx(Sparkline, { data: sparklineData, color: "auto" })
41606
42217
  ] })
41607
42218
  ] }),
41608
42219
  action && /* @__PURE__ */ jsxs(
@@ -43689,6 +44300,7 @@ var init_component_registry_generated = __esm({
43689
44300
  init_DataGrid();
43690
44301
  init_DataList();
43691
44302
  init_DataTable();
44303
+ init_DateRangePicker();
43692
44304
  init_DateRangeSelector();
43693
44305
  init_DayCell();
43694
44306
  init_DebuggerBoard();
@@ -43821,6 +44433,7 @@ var init_component_registry_generated = __esm({
43821
44433
  init_Skeleton();
43822
44434
  init_SocialProof();
43823
44435
  init_SortableList();
44436
+ init_Sparkline();
43824
44437
  init_Split();
43825
44438
  init_SplitPane();
43826
44439
  init_SplitSection();
@@ -43969,6 +44582,7 @@ var init_component_registry_generated = __esm({
43969
44582
  "DataGrid": DataGrid,
43970
44583
  "DataList": DataList,
43971
44584
  "DataTable": DataTable,
44585
+ "DateRangePicker": DateRangePicker,
43972
44586
  "DateRangeSelector": DateRangeSelector,
43973
44587
  "DayCell": DayCell,
43974
44588
  "DebuggerBoard": DebuggerBoard,
@@ -44130,6 +44744,7 @@ var init_component_registry_generated = __esm({
44130
44744
  "SortableList": SortableList,
44131
44745
  "Spacer": SpacerPattern,
44132
44746
  "SpacerPattern": SpacerPattern,
44747
+ "Sparkline": Sparkline,
44133
44748
  "Spinner": SpinnerPattern,
44134
44749
  "SpinnerPattern": SpinnerPattern,
44135
44750
  "Split": Split,
@@ -45134,6 +45749,7 @@ var init_atoms = __esm({
45134
45749
  init_Icon();
45135
45750
  init_ProgressBar();
45136
45751
  init_Radio();
45752
+ init_Sparkline();
45137
45753
  init_Switch();
45138
45754
  init_Spacer();
45139
45755
  init_Stack();
@@ -48072,4 +48688,4 @@ function useGitHubBranches(owner, repo, enabled = true) {
48072
48688
  });
48073
48689
  }
48074
48690
 
48075
- export { ALL_PRESETS, ALMADAR_DND_MIME, AR_BOOK_FIELDS, AboutPageTemplate, Accordion, ActionButton, ActionButtons, Card2 as ActionCard, ActionPalette, ActionTile, Alert, AnimatedCounter, AnimatedGraphic, AnimatedReveal, ArticleSection, AuthLayout, Avatar, Badge, BattleBoard, BattleTemplate, BehaviorView, BookChapterView, BookCoverPage, BookNavBar, BookTableOfContents, BookViewer, Box, BranchingLogicBuilder, Breadcrumb, BuilderBoard, Button, ButtonGroup, CTABanner, CalendarGrid, CanvasEffect, Card, CardBody, CardContent, CardFooter, CardGrid, CardHeader, CardTitle, Carousel, CaseStudyCard, CaseStudyOrganism, CastleBoard, CastleTemplate, Center, Chart, ChartLegend, Checkbox, ChoiceButton, ClassifierBoard, CodeBlock, CodeExample, CodeView, CodeViewer, CollapsibleSection, CombatLog, ComboCounter, CommunityLinks, ConditionalWrapper, ConfettiEffect, ConfirmDialog, Container, ContentRenderer, ContentSection, ControlButton, CounterTemplate, CraftingRecipe, DEFAULT_LIKERT_OPTIONS, DEFAULT_MATRIX_COLUMNS, DEFAULT_SLOTS, DIAMOND_TOP_Y, DPad, DamageNumber, DashboardGrid, DashboardLayout, DataGrid, DataList, DataTable, DateRangeSelector, DayCell, DebuggerBoard, DetailPanel, DialogueBox, DialogueBubble, Divider, DocBreadcrumb, DocCodeBlock, DocPagination, DocSearch, DocSidebar, DocTOC, DocumentViewer, StateMachineView as DomStateMachineVisualizer, Drawer, DrawerSlot, EdgeDecoration, EditorCheckbox, EditorSelect, EditorSlider, EditorTextInput, EditorToolbar, EmptyState, EnemyPlate, EntityDisplayEvents, ErrorBoundary, ErrorState, EventHandlerBoard, EventLog, FEATURE_COLORS, FEATURE_TYPES, FLOOR_HEIGHT, FeatureCard, FeatureDetailPageTemplate, FeatureGrid, FeatureGridOrganism, FeatureRenderer2 as FeatureRenderer, FileTree, FilterGroup, FilterPill, Flex, FlipCard, FlipContainer, FloatingActionButton, Form, FormActions, FormField, FormLayout, FormSection, FormSectionHeader, GameAudioContext, GameAudioProvider, GameAudioToggle, GameCanvas2D, GameHud, GameMenu, GameOverScreen, GameShell, GameTemplate, GenericAppTemplate, GeometricPattern, GradientDivider, GraphCanvas, GraphView, Grid, HStack, Header, Heading, HealthBar, HealthPanel, HeroOrganism, HeroSection, I18nProvider, IDENTITY_BOOK_FIELDS, Icon, InfiniteScrollSentinel, Input, InputGroup, InstallBox, InventoryGrid, InventoryPanel, IsometricCanvas, ItemSlot, JazariStateMachine, Label, LandingPageTemplate, LawReferenceTooltip, Lightbox, LikertScale, LineChart2 as LineChart, List3 as List, LoadingState, MapView, MarkdownContent, MarketingStatCard, MasterDetail, MasterDetailLayout, MatrixQuestion, MediaGallery, Menu, Meter, MiniMap, Modal, ModalSlot, ModuleCard, Navigation, NegotiatorBoard, NotifyListener, NumberStepper, ObjectRulePanel, OptionConstraintGroup, StateMachineView as OrbitalStateMachineView, OrbitalVisualization, Overlay, PageHeader, Pagination, PatternTile, PhysicsManager, PlatformerCanvas, Popover, PositionedCanvas, PowerupSlots, PricingCard, PricingGrid, PricingOrganism, PricingPageTemplate, ProgressBar, ProgressDots, PullQuote, PullToRefresh, QrScanner, QuestTracker, QuizBlock, Radio, RangeSlider, RelationSelect, RepeatableFormSection, ReplyTree, ResourceBar, ResourceCounter, RichBlockEditor, RuleEditor, RuntimeDebugger, SHEET_COLUMNS, SPRITE_SHEET_LAYOUT, ScaledDiagram, ScoreBoard, ScoreDisplay, SearchInput, Section, SectionHeader, Select, SequenceBar, SequencerBoard, ServiceCatalog, ShowcaseCard, ShowcaseOrganism, SidePanel, Sidebar, SignaturePad, SimpleGrid, SimulationCanvas, SimulationControls, SimulationGraph, SimulatorBoard, Skeleton, SlotContentRenderer, SocialProof, SortableList, Spacer, Spinner, Split, SplitPane, SplitSection, Sprite, Stack, StarRating, StatBadge, StatCard, StatDisplay, StateArchitectBoard, StateIndicator, StateMachineView, StateNode2 as StateNode, StatsGrid, StatsOrganism, StatusBar, StatusDot, StatusEffect, StepFlow, StepFlowOrganism, SvgBranch, SvgConnection, SvgFlow, SvgGrid, SvgLobe, SvgMesh, SvgMorph, SvgNode, SvgPulse, SvgRing, SvgShield, SvgStack, SwipeableRow, Switch, TERRAIN_COLORS, TILE_HEIGHT, TILE_WIDTH, TabbedContainer, Table, Tabs, TagCloud, TeamCard, TeamOrganism, TerrainPalette, Text, TextHighlight, Textarea, ThemeSelector, ThemeToggle, TimeSlotCell, Timeline, TimerDisplay, Toast, ToastSlot, Tooltip, TraitFrame, TraitSlot, TraitStateViewer, TransitionArrow, TrendIndicator, TurnIndicator, TurnPanel, TypewriterText, Typography, UISlotComponent, UISlotRenderer, UncontrolledBattleBoard, UnitCommandBar, UploadDropZone, VStack, VariablePanel, VersionDiff, ViolationAlert, VoteStack, WaypointMarker, WizardContainer, WizardNavigation, WizardProgress, WorldMapBoard, WorldMapTemplate, XPBar, applyTemporaryEffect, calculateAttackTargets, calculateDamage, calculateValidMoves, clearEntities, cn, combatAnimations, combatClasses, combatEffects, createInitialGameState, createTranslate, createUnitAnimationState, drawSprite, generateCombatMessage, getAllEntities, getByType, getCurrentFrame, getEntity, getSingleton, getTileDimensions, inferDirection, isoToScreen, mapBookData, parseQueryBinding, pendulum, projectileMotion, removeEntity, resolveFieldMap, resolveFrame, resolveSheetDirection, screenToIso, spawnEntity, springOscillator, tickAnimationState, transitionAnimation, updateEntity, updateSingleton, useAgentChat, useAuthContext, useBattleState, useCamera, useCompile, useConnectGitHub, useDeepAgentGeneration, useDisconnectGitHub, useDragReorder, useDraggable, useDropZone, useEmitEvent, useEntities, useEntitiesByType, useEntity as useEntityById, useEventBus, useEventListener, useExtensions, useFileEditor, useFileSystem, useGameAudio, useGameAudioContext, useGitHubBranches, useGitHubRepo, useGitHubRepos, useGitHubStatus, useImageCache, useInfiniteScroll, useInput, useLongPress, useOrbitalHistory, usePhysics, usePhysics2D, usePinchZoom, usePlayer, usePreview, usePullToRefresh, useQuerySingleton, useSingletonEntity, useSpriteAnimations, useSwipeGesture, useTraitListens, useTranslate, useUIEvents, useUISlotManager, useValidation };
48691
+ export { ALL_PRESETS, ALMADAR_DND_MIME, AR_BOOK_FIELDS, AboutPageTemplate, Accordion, ActionButton, ActionButtons, Card2 as ActionCard, ActionPalette, ActionTile, Alert, AnimatedCounter, AnimatedGraphic, AnimatedReveal, ArticleSection, AuthLayout, Avatar, Badge, BattleBoard, BattleTemplate, BehaviorView, BookChapterView, BookCoverPage, BookNavBar, BookTableOfContents, BookViewer, Box, BranchingLogicBuilder, Breadcrumb, BuilderBoard, Button, ButtonGroup, CTABanner, CalendarGrid, CanvasEffect, Card, CardBody, CardContent, CardFooter, CardGrid, CardHeader, CardTitle, Carousel, CaseStudyCard, CaseStudyOrganism, CastleBoard, CastleTemplate, Center, Chart, ChartLegend, Checkbox, ChoiceButton, ClassifierBoard, CodeBlock, CodeExample, CodeView, CodeViewer, CollapsibleSection, CombatLog, ComboCounter, CommunityLinks, ConditionalWrapper, ConfettiEffect, ConfirmDialog, Container, ContentRenderer, ContentSection, ControlButton, CounterTemplate, CraftingRecipe, DEFAULT_LIKERT_OPTIONS, DEFAULT_MATRIX_COLUMNS, DEFAULT_SLOTS, DIAMOND_TOP_Y, DPad, DamageNumber, DashboardGrid, DashboardLayout, DataGrid, DataList, DataTable, DateRangePicker, DateRangeSelector, DayCell, DebuggerBoard, DetailPanel, DialogueBox, DialogueBubble, Divider, DocBreadcrumb, DocCodeBlock, DocPagination, DocSearch, DocSidebar, DocTOC, DocumentViewer, StateMachineView as DomStateMachineVisualizer, Drawer, DrawerSlot, EdgeDecoration, EditorCheckbox, EditorSelect, EditorSlider, EditorTextInput, EditorToolbar, EmptyState, EnemyPlate, EntityDisplayEvents, ErrorBoundary, ErrorState, EventHandlerBoard, EventLog, FEATURE_COLORS, FEATURE_TYPES, FLOOR_HEIGHT, FeatureCard, FeatureDetailPageTemplate, FeatureGrid, FeatureGridOrganism, FeatureRenderer2 as FeatureRenderer, FileTree, FilterGroup, FilterPill, Flex, FlipCard, FlipContainer, FloatingActionButton, Form, FormActions, FormField, FormLayout, FormSection, FormSectionHeader, GameAudioContext, GameAudioProvider, GameAudioToggle, GameCanvas2D, GameHud, GameMenu, GameOverScreen, GameShell, GameTemplate, GenericAppTemplate, GeometricPattern, GradientDivider, GraphCanvas, GraphView, Grid, HStack, Header, Heading, HealthBar, HealthPanel, HeroOrganism, HeroSection, I18nProvider, IDENTITY_BOOK_FIELDS, Icon, InfiniteScrollSentinel, Input, InputGroup, InstallBox, InventoryGrid, InventoryPanel, IsometricCanvas, ItemSlot, JazariStateMachine, Label, LandingPageTemplate, LawReferenceTooltip, Lightbox, LikertScale, LineChart2 as LineChart, List3 as List, LoadingState, MapView, MarkdownContent, MarketingStatCard, MasterDetail, MasterDetailLayout, MatrixQuestion, MediaGallery, Menu, Meter, MiniMap, Modal, ModalSlot, ModuleCard, Navigation, NegotiatorBoard, NotifyListener, NumberStepper, ObjectRulePanel, OptionConstraintGroup, StateMachineView as OrbitalStateMachineView, OrbitalVisualization, Overlay, PageHeader, Pagination, PatternTile, PhysicsManager, PlatformerCanvas, Popover, PositionedCanvas, PowerupSlots, PricingCard, PricingGrid, PricingOrganism, PricingPageTemplate, ProgressBar, ProgressDots, PullQuote, PullToRefresh, QrScanner, QuestTracker, QuizBlock, Radio, RangeSlider, RelationSelect, RepeatableFormSection, ReplyTree, ResourceBar, ResourceCounter, RichBlockEditor, RuleEditor, RuntimeDebugger, SHEET_COLUMNS, SPRITE_SHEET_LAYOUT, ScaledDiagram, ScoreBoard, ScoreDisplay, SearchInput, Section, SectionHeader, Select, SequenceBar, SequencerBoard, ServiceCatalog, ShowcaseCard, ShowcaseOrganism, SidePanel, Sidebar, SignaturePad, SimpleGrid, SimulationCanvas, SimulationControls, SimulationGraph, SimulatorBoard, Skeleton, SlotContentRenderer, SocialProof, SortableList, Spacer, Sparkline, Spinner, Split, SplitPane, SplitSection, Sprite, Stack, StarRating, StatBadge, StatCard, StatDisplay, StateArchitectBoard, StateIndicator, StateMachineView, StateNode2 as StateNode, StatsGrid, StatsOrganism, StatusBar, StatusDot, StatusEffect, StepFlow, StepFlowOrganism, SvgBranch, SvgConnection, SvgFlow, SvgGrid, SvgLobe, SvgMesh, SvgMorph, SvgNode, SvgPulse, SvgRing, SvgShield, SvgStack, SwipeableRow, Switch, TERRAIN_COLORS, TILE_HEIGHT, TILE_WIDTH, TabbedContainer, Table, Tabs, TagCloud, TeamCard, TeamOrganism, TerrainPalette, Text, TextHighlight, Textarea, ThemeSelector, ThemeToggle, TimeSlotCell, Timeline, TimerDisplay, Toast, ToastSlot, Tooltip, TraitFrame, TraitSlot, TraitStateViewer, TransitionArrow, TrendIndicator, TurnIndicator, TurnPanel, TypewriterText, Typography, UISlotComponent, UISlotRenderer, UncontrolledBattleBoard, UnitCommandBar, UploadDropZone, VStack, VariablePanel, VersionDiff, ViolationAlert, VoteStack, WaypointMarker, WizardContainer, WizardNavigation, WizardProgress, WorldMapBoard, WorldMapTemplate, XPBar, applyTemporaryEffect, calculateAttackTargets, calculateDamage, calculateValidMoves, clearEntities, cn, combatAnimations, combatClasses, combatEffects, createInitialGameState, createTranslate, createUnitAnimationState, drawSprite, generateCombatMessage, getAllEntities, getByType, getCurrentFrame, getEntity, getSingleton, getTileDimensions, inferDirection, isoToScreen, mapBookData, parseQueryBinding, pendulum, projectileMotion, removeEntity, resolveFieldMap, resolveFrame, resolveSheetDirection, screenToIso, spawnEntity, springOscillator, tickAnimationState, transitionAnimation, updateEntity, updateSingleton, useAgentChat, useAuthContext, useBattleState, useCamera, useCompile, useConnectGitHub, useDeepAgentGeneration, useDisconnectGitHub, useDragReorder, useDraggable, useDropZone, useEmitEvent, useEntities, useEntitiesByType, useEntity as useEntityById, useEventBus, useEventListener, useExtensions, useFileEditor, useFileSystem, useGameAudio, useGameAudioContext, useGitHubBranches, useGitHubRepo, useGitHubRepos, useGitHubStatus, useImageCache, useInfiniteScroll, useInput, useLongPress, useOrbitalHistory, usePhysics, usePhysics2D, usePinchZoom, usePlayer, usePreview, usePullToRefresh, useQuerySingleton, useSingletonEntity, useSpriteAnimations, useSwipeGesture, useTraitListens, useTranslate, useUIEvents, useUISlotManager, useValidation };