@pos-360/horizon 0.5.5 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,10 +1,18 @@
1
1
  import { cn, Text, Badge } from './chunk-E3UN74IA.mjs';
2
2
  import { motion, AnimatePresence } from 'framer-motion';
3
- import { jsxs, jsx } from 'react/jsx-runtime';
3
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
4
4
  import * as React from 'react';
5
- import { forwardRef, useState, useEffect, createContext, useRef, useCallback, useContext } from 'react';
6
- import { Info, ChevronDown, ChevronUp, ChevronRight, LayoutGrid, List, Check, ExternalLink, X, Moon, Rocket, Star, Globe, Orbit, Sparkles, AlertTriangle } from 'lucide-react';
5
+ import { createContext, forwardRef, useId, useMemo, useState, useEffect, useRef, useCallback, useContext } from 'react';
6
+ import { ChevronLeft, ChevronRight, Info, ChevronDown, ChevronUp, TrendingUp, TrendingDown, Minus, LayoutGrid, List, Check, ExternalLink, X, Moon, Rocket, Star, Globe, Orbit, Sparkles, AlertTriangle } from 'lucide-react';
7
7
  export { Globe, Moon, Orbit, Rocket, Sparkles, Star } from 'lucide-react';
8
+ import { Bar, AreaClosed, LinePath, Pie } from '@visx/shape';
9
+ import { Group } from '@visx/group';
10
+ import { scaleBand, scaleLinear, scaleOrdinal } from '@visx/scale';
11
+ import { AxisBottom, AxisLeft } from '@visx/axis';
12
+ import { GridRows } from '@visx/grid';
13
+ import { LinearGradient } from '@visx/gradient';
14
+ import { ParentSize } from '@visx/responsive';
15
+ import { curveMonotoneX } from '@visx/curve';
8
16
  import * as Tooltip from '@radix-ui/react-tooltip';
9
17
 
10
18
  var AnimatedButton = ({
@@ -33,6 +41,1201 @@ var AnimatedButton = ({
33
41
  }
34
42
  );
35
43
  };
44
+ var DashboardContext = createContext({ columns: 12 });
45
+ var useDashboardContext = () => useContext(DashboardContext);
46
+ var breakpoints = {
47
+ sm: 640,
48
+ md: 768,
49
+ lg: 1024,
50
+ xl: 1280,
51
+ "2xl": 1536
52
+ };
53
+ function isResponsive(value) {
54
+ return typeof value === "object" && value !== null && !Array.isArray(value);
55
+ }
56
+ function generateResponsiveCSS(id, columns, rowHeight, gap) {
57
+ const cssRules = [];
58
+ const selector = `[data-dashboard-id="${id}"]`;
59
+ const getValue = (value, bp) => {
60
+ if (!isResponsive(value)) return bp === "default" ? value : void 0;
61
+ return value[bp];
62
+ };
63
+ const allBreakpoints = ["default", "sm", "md", "lg", "xl", "2xl"];
64
+ let lastColumns;
65
+ let lastRowHeight;
66
+ let lastGap;
67
+ for (const bp of allBreakpoints) {
68
+ const colVal = getValue(columns, bp);
69
+ const rowVal = rowHeight ? getValue(rowHeight, bp) : void 0;
70
+ const gapVal = getValue(gap, bp);
71
+ if (colVal !== void 0) lastColumns = colVal;
72
+ if (rowVal !== void 0) lastRowHeight = rowVal;
73
+ if (gapVal !== void 0) lastGap = gapVal;
74
+ if (colVal !== void 0 || rowVal !== void 0 || gapVal !== void 0) {
75
+ const props = [];
76
+ if (lastColumns !== void 0) {
77
+ props.push(`grid-template-columns: repeat(${lastColumns}, minmax(0, 1fr))`);
78
+ }
79
+ if (lastRowHeight !== void 0) {
80
+ props.push(`grid-auto-rows: ${lastRowHeight}px`);
81
+ }
82
+ if (lastGap !== void 0) {
83
+ props.push(`gap: ${lastGap * 0.25}rem`);
84
+ }
85
+ if (props.length > 0) {
86
+ const rule = `${selector} { ${props.join("; ")}; }`;
87
+ if (bp === "default") {
88
+ cssRules.push(rule);
89
+ } else {
90
+ cssRules.push(`@media (min-width: ${breakpoints[bp]}px) { ${rule} }`);
91
+ }
92
+ }
93
+ }
94
+ }
95
+ return cssRules.join("\n");
96
+ }
97
+ var Dashboard = forwardRef(
98
+ ({ columns = 12, rowHeight, gap = 4, className, children }, ref) => {
99
+ const reactId = useId();
100
+ const dashboardId = `dashboard-${reactId.replace(/:/g, "")}`;
101
+ const responsiveCSS = useMemo(
102
+ () => generateResponsiveCSS(dashboardId, columns, rowHeight, gap),
103
+ [dashboardId, columns, rowHeight, gap]
104
+ );
105
+ return /* @__PURE__ */ jsxs(DashboardContext.Provider, { value: { columns }, children: [
106
+ /* @__PURE__ */ jsx("style", { dangerouslySetInnerHTML: { __html: responsiveCSS } }),
107
+ /* @__PURE__ */ jsx(
108
+ "div",
109
+ {
110
+ ref,
111
+ "data-dashboard-id": dashboardId,
112
+ className: cn("hz-rounded grid w-full", className),
113
+ children
114
+ }
115
+ )
116
+ ] });
117
+ }
118
+ );
119
+ Dashboard.displayName = "Dashboard";
120
+ var breakpoints2 = {
121
+ sm: 640,
122
+ md: 768,
123
+ lg: 1024,
124
+ xl: 1280,
125
+ "2xl": 1536
126
+ };
127
+ function isResponsive2(value) {
128
+ return typeof value === "object" && value !== null && !Array.isArray(value);
129
+ }
130
+ function generatePanelCSS(id, span, rowSpan) {
131
+ const cssRules = [];
132
+ const selector = `[data-panel-id="${id}"]`;
133
+ const getValue = (value, bp) => {
134
+ if (!isResponsive2(value)) return bp === "default" ? value : void 0;
135
+ return value[bp];
136
+ };
137
+ const allBreakpoints = ["default", "sm", "md", "lg", "xl", "2xl"];
138
+ let lastSpan;
139
+ let lastRowSpan;
140
+ for (const bp of allBreakpoints) {
141
+ const spanVal = getValue(span, bp);
142
+ const rowSpanVal = getValue(rowSpan, bp);
143
+ if (spanVal !== void 0) lastSpan = spanVal;
144
+ if (rowSpanVal !== void 0) lastRowSpan = rowSpanVal;
145
+ if (spanVal !== void 0 || rowSpanVal !== void 0) {
146
+ const props = [];
147
+ if (lastSpan !== void 0) {
148
+ props.push(`grid-column: span ${lastSpan} / span ${lastSpan}`);
149
+ }
150
+ if (lastRowSpan !== void 0) {
151
+ props.push(`grid-row: span ${lastRowSpan} / span ${lastRowSpan}`);
152
+ }
153
+ if (props.length > 0) {
154
+ const rule = `${selector} { ${props.join("; ")}; }`;
155
+ if (bp === "default") {
156
+ cssRules.push(rule);
157
+ } else {
158
+ cssRules.push(`@media (min-width: ${breakpoints2[bp]}px) { ${rule} }`);
159
+ }
160
+ }
161
+ }
162
+ }
163
+ return cssRules.join("\n");
164
+ }
165
+ var DashboardPanel = forwardRef(
166
+ ({ span = 1, rowSpan = 1, className, children }, ref) => {
167
+ const reactId = useId();
168
+ const panelId = `panel-${reactId.replace(/:/g, "")}`;
169
+ const needsResponsiveCSS = isResponsive2(span) || isResponsive2(rowSpan);
170
+ const responsiveCSS = useMemo(() => {
171
+ if (!needsResponsiveCSS) return null;
172
+ return generatePanelCSS(panelId, span, rowSpan);
173
+ }, [needsResponsiveCSS, panelId, span, rowSpan]);
174
+ const inlineStyle = !needsResponsiveCSS ? {
175
+ gridColumn: `span ${span} / span ${span}`,
176
+ gridRow: `span ${rowSpan} / span ${rowSpan}`
177
+ } : void 0;
178
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
179
+ responsiveCSS && /* @__PURE__ */ jsx("style", { dangerouslySetInnerHTML: { __html: responsiveCSS } }),
180
+ /* @__PURE__ */ jsx(
181
+ "div",
182
+ {
183
+ ref,
184
+ "data-panel-id": needsResponsiveCSS ? panelId : void 0,
185
+ className: cn("min-w-0 h-full", className),
186
+ style: inlineStyle,
187
+ children
188
+ }
189
+ )
190
+ ] });
191
+ }
192
+ );
193
+ DashboardPanel.displayName = "DashboardPanel";
194
+ var TrendBadge = ({ trend, size }) => {
195
+ const Icon = trend.direction === "up" ? TrendingUp : trend.direction === "down" ? TrendingDown : Minus;
196
+ const iconSize = size === "sm" ? "w-3 h-3" : "w-3.5 h-3.5";
197
+ return /* @__PURE__ */ jsxs(
198
+ "div",
199
+ {
200
+ className: cn(
201
+ "inline-flex items-center gap-1 px-2 py-0.5 rounded-hz-full font-medium",
202
+ size === "sm" && "text-xs",
203
+ size === "md" && "text-xs",
204
+ size === "lg" && "text-sm",
205
+ trend.direction === "up" && "bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400",
206
+ trend.direction === "down" && "bg-rose-100 text-rose-700 dark:bg-rose-900/30 dark:text-rose-400",
207
+ trend.direction === "neutral" && "bg-gray-100 text-gray-600 dark:bg-neutral-800 dark:text-gray-400"
208
+ ),
209
+ children: [
210
+ /* @__PURE__ */ jsx(Icon, { className: iconSize }),
211
+ /* @__PURE__ */ jsxs("span", { children: [
212
+ Math.abs(trend.value).toFixed(1),
213
+ "%"
214
+ ] })
215
+ ]
216
+ }
217
+ );
218
+ };
219
+ var StatDisplay = forwardRef(
220
+ ({ data, size = "md", className }, ref) => {
221
+ const { value, label, trend, prefix = "", suffix = "" } = data;
222
+ const formattedValue = useMemo(() => {
223
+ if (typeof value === "number") {
224
+ return value.toLocaleString("en-US", {
225
+ minimumFractionDigits: 0,
226
+ maximumFractionDigits: 2
227
+ });
228
+ }
229
+ return value;
230
+ }, [value]);
231
+ return /* @__PURE__ */ jsxs("div", { ref, className: cn("flex flex-col", className), children: [
232
+ /* @__PURE__ */ jsx(
233
+ Text,
234
+ {
235
+ as: "span",
236
+ size: "xs",
237
+ color: "muted",
238
+ className: "uppercase tracking-wide",
239
+ children: label
240
+ }
241
+ ),
242
+ /* @__PURE__ */ jsxs(
243
+ "span",
244
+ {
245
+ className: cn(
246
+ "font-bold text-gray-900 dark:text-gray-100 mt-0.5",
247
+ size === "sm" && "text-lg",
248
+ size === "md" && "text-xl",
249
+ size === "lg" && "text-2xl"
250
+ ),
251
+ children: [
252
+ prefix,
253
+ formattedValue,
254
+ suffix
255
+ ]
256
+ }
257
+ ),
258
+ trend && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mt-1", children: [
259
+ /* @__PURE__ */ jsx(TrendBadge, { trend, size }),
260
+ trend.label && /* @__PURE__ */ jsx(Text, { as: "span", size: "xs", color: "muted", children: trend.label })
261
+ ] })
262
+ ] });
263
+ }
264
+ );
265
+ StatDisplay.displayName = "StatDisplay";
266
+ var defaultColors = [
267
+ "#3b82f6",
268
+ // blue-500
269
+ "#8b5cf6",
270
+ // violet-500
271
+ "#ec4899",
272
+ // pink-500
273
+ "#f59e0b",
274
+ // amber-500
275
+ "#10b981",
276
+ // emerald-500
277
+ "#6366f1"
278
+ // indigo-500
279
+ ];
280
+ var DonutChart = ({
281
+ data,
282
+ width,
283
+ height,
284
+ centerLabel,
285
+ centerValue,
286
+ variant = "half"
287
+ }) => {
288
+ const isHalf = variant === "half";
289
+ const radius = isHalf ? Math.min(width, height * 2) / 2 : Math.min(width, height) / 2;
290
+ const innerRadius = radius * 0.65;
291
+ const centerX = width / 2;
292
+ const centerY = isHalf ? height : height / 2;
293
+ const colorScale = useMemo(
294
+ () => scaleOrdinal({
295
+ domain: data.map((d) => d.label),
296
+ range: data.map((d, i) => d.color || defaultColors[i % defaultColors.length])
297
+ }),
298
+ [data]
299
+ );
300
+ const getValue = (d) => d.value;
301
+ return /* @__PURE__ */ jsx("svg", { width, height, children: /* @__PURE__ */ jsxs(Group, { top: centerY, left: centerX, children: [
302
+ /* @__PURE__ */ jsx(
303
+ Pie,
304
+ {
305
+ data,
306
+ pieValue: getValue,
307
+ outerRadius: radius,
308
+ innerRadius,
309
+ startAngle: isHalf ? -Math.PI / 2 : 0,
310
+ endAngle: isHalf ? Math.PI / 2 : Math.PI * 2,
311
+ padAngle: 0.02,
312
+ cornerRadius: 3,
313
+ children: (pie) => pie.arcs.map((arc, index) => /* @__PURE__ */ jsx(
314
+ "path",
315
+ {
316
+ d: pie.path(arc) || "",
317
+ fill: colorScale(arc.data.label),
318
+ className: "transition-opacity hover:opacity-80"
319
+ },
320
+ `arc-${index}`
321
+ ))
322
+ }
323
+ ),
324
+ (centerLabel || centerValue) && /* @__PURE__ */ jsxs("g", { children: [
325
+ centerValue && /* @__PURE__ */ jsx(
326
+ "text",
327
+ {
328
+ y: isHalf ? -20 : -8,
329
+ textAnchor: "middle",
330
+ className: "fill-gray-900 dark:fill-gray-100",
331
+ style: { fontSize: "1.25rem", fontWeight: 700 },
332
+ children: centerValue
333
+ }
334
+ ),
335
+ centerLabel && /* @__PURE__ */ jsx(
336
+ "text",
337
+ {
338
+ y: isHalf ? 0 : 12,
339
+ textAnchor: "middle",
340
+ className: "fill-gray-500 dark:fill-gray-400",
341
+ style: { fontSize: "0.75rem" },
342
+ children: centerLabel
343
+ }
344
+ )
345
+ ] })
346
+ ] }) });
347
+ };
348
+ var margin = { top: 20, right: 20, bottom: 30, left: 50 };
349
+ var LineChart = ({ data, width, height, color = "#3b82f6", showArea = true }) => {
350
+ const innerWidth = width - margin.left - margin.right;
351
+ const innerHeight = height - margin.top - margin.bottom;
352
+ const xScale = useMemo(
353
+ () => scaleBand({
354
+ domain: data.map((d) => d.label),
355
+ range: [0, innerWidth],
356
+ padding: 0.2
357
+ }),
358
+ [data, innerWidth]
359
+ );
360
+ const yScale = useMemo(() => {
361
+ const maxValue = Math.max(...data.map((d) => d.value));
362
+ return scaleLinear({
363
+ domain: [0, maxValue * 1.1],
364
+ range: [innerHeight, 0],
365
+ nice: true
366
+ });
367
+ }, [data, innerHeight]);
368
+ const getX = (d) => (xScale(d.label) || 0) + xScale.bandwidth() / 2;
369
+ const getY = (d) => yScale(d.value);
370
+ if (width < 100 || height < 80) return null;
371
+ return /* @__PURE__ */ jsxs("svg", { width, height, children: [
372
+ /* @__PURE__ */ jsx(LinearGradient, { id: "area-gradient", from: color, to: color, fromOpacity: 0.3, toOpacity: 0 }),
373
+ /* @__PURE__ */ jsxs(Group, { left: margin.left, top: margin.top, children: [
374
+ /* @__PURE__ */ jsx(
375
+ GridRows,
376
+ {
377
+ scale: yScale,
378
+ width: innerWidth,
379
+ strokeDasharray: "3,3",
380
+ stroke: "currentColor",
381
+ strokeOpacity: 0.1,
382
+ className: "text-gray-400 dark:text-neutral-600"
383
+ }
384
+ ),
385
+ showArea && /* @__PURE__ */ jsx(
386
+ AreaClosed,
387
+ {
388
+ data,
389
+ x: getX,
390
+ y: getY,
391
+ yScale,
392
+ curve: curveMonotoneX,
393
+ fill: "url(#area-gradient)"
394
+ }
395
+ ),
396
+ /* @__PURE__ */ jsx(LinePath, { data, x: getX, y: getY, stroke: color, strokeWidth: 2.5, curve: curveMonotoneX }),
397
+ data.map((d, i) => /* @__PURE__ */ jsx(
398
+ "circle",
399
+ {
400
+ cx: getX(d),
401
+ cy: getY(d),
402
+ r: 3,
403
+ fill: "white",
404
+ stroke: color,
405
+ strokeWidth: 2
406
+ },
407
+ `point-${i}`
408
+ )),
409
+ /* @__PURE__ */ jsx(
410
+ AxisBottom,
411
+ {
412
+ top: innerHeight,
413
+ scale: xScale,
414
+ tickLabelProps: () => ({
415
+ fill: "currentColor",
416
+ fontSize: 10,
417
+ textAnchor: "middle",
418
+ className: "text-gray-500 dark:text-gray-400"
419
+ }),
420
+ hideAxisLine: true,
421
+ tickStroke: "transparent"
422
+ }
423
+ ),
424
+ /* @__PURE__ */ jsx(
425
+ AxisLeft,
426
+ {
427
+ scale: yScale,
428
+ numTicks: 4,
429
+ tickLabelProps: () => ({
430
+ fill: "currentColor",
431
+ fontSize: 10,
432
+ textAnchor: "end",
433
+ dx: -4,
434
+ dy: 4,
435
+ className: "text-gray-500 dark:text-gray-400"
436
+ }),
437
+ hideAxisLine: true,
438
+ tickStroke: "transparent"
439
+ }
440
+ )
441
+ ] })
442
+ ] });
443
+ };
444
+ var MultiLineChart = ({ series, width, height, showArea = false }) => {
445
+ const innerWidth = width - margin.left - margin.right;
446
+ const innerHeight = height - margin.top - margin.bottom;
447
+ const allLabels = useMemo(() => {
448
+ const labels = /* @__PURE__ */ new Set();
449
+ series.forEach((s) => s.data.forEach((d) => labels.add(d.label)));
450
+ return Array.from(labels);
451
+ }, [series]);
452
+ const xScale = useMemo(
453
+ () => scaleBand({
454
+ domain: allLabels,
455
+ range: [0, innerWidth],
456
+ padding: 0.2
457
+ }),
458
+ [allLabels, innerWidth]
459
+ );
460
+ const yScale = useMemo(() => {
461
+ const maxValue = Math.max(...series.flatMap((s) => s.data.map((d) => d.value)));
462
+ return scaleLinear({
463
+ domain: [0, maxValue * 1.1],
464
+ range: [innerHeight, 0],
465
+ nice: true
466
+ });
467
+ }, [series, innerHeight]);
468
+ const getX = (d) => (xScale(d.label) || 0) + xScale.bandwidth() / 2;
469
+ const getY = (d) => yScale(d.value);
470
+ if (width < 100 || height < 80) return null;
471
+ return /* @__PURE__ */ jsxs("svg", { width, height, children: [
472
+ series.map((s, i) => /* @__PURE__ */ jsx(
473
+ LinearGradient,
474
+ {
475
+ id: `area-gradient-${i}`,
476
+ from: s.color || defaultColors[i % defaultColors.length],
477
+ to: s.color || defaultColors[i % defaultColors.length],
478
+ fromOpacity: 0.2,
479
+ toOpacity: 0
480
+ },
481
+ `gradient-${i}`
482
+ )),
483
+ /* @__PURE__ */ jsxs(Group, { left: margin.left, top: margin.top, children: [
484
+ /* @__PURE__ */ jsx(
485
+ GridRows,
486
+ {
487
+ scale: yScale,
488
+ width: innerWidth,
489
+ strokeDasharray: "3,3",
490
+ stroke: "currentColor",
491
+ strokeOpacity: 0.1,
492
+ className: "text-gray-400 dark:text-neutral-600"
493
+ }
494
+ ),
495
+ series.map((s, i) => {
496
+ const color = s.color || defaultColors[i % defaultColors.length];
497
+ return /* @__PURE__ */ jsxs("g", { children: [
498
+ showArea && /* @__PURE__ */ jsx(
499
+ AreaClosed,
500
+ {
501
+ data: s.data,
502
+ x: getX,
503
+ y: getY,
504
+ yScale,
505
+ curve: curveMonotoneX,
506
+ fill: `url(#area-gradient-${i})`
507
+ }
508
+ ),
509
+ /* @__PURE__ */ jsx(
510
+ LinePath,
511
+ {
512
+ data: s.data,
513
+ x: getX,
514
+ y: getY,
515
+ stroke: color,
516
+ strokeWidth: 2,
517
+ curve: curveMonotoneX
518
+ }
519
+ ),
520
+ s.data.map((d, j) => /* @__PURE__ */ jsx(
521
+ "circle",
522
+ {
523
+ cx: getX(d),
524
+ cy: getY(d),
525
+ r: 2.5,
526
+ fill: "white",
527
+ stroke: color,
528
+ strokeWidth: 1.5
529
+ },
530
+ `point-${i}-${j}`
531
+ ))
532
+ ] }, `series-${i}`);
533
+ }),
534
+ /* @__PURE__ */ jsx(
535
+ AxisBottom,
536
+ {
537
+ top: innerHeight,
538
+ scale: xScale,
539
+ tickLabelProps: () => ({
540
+ fill: "currentColor",
541
+ fontSize: 10,
542
+ textAnchor: "middle",
543
+ className: "text-gray-500 dark:text-gray-400"
544
+ }),
545
+ hideAxisLine: true,
546
+ tickStroke: "transparent"
547
+ }
548
+ ),
549
+ /* @__PURE__ */ jsx(
550
+ AxisLeft,
551
+ {
552
+ scale: yScale,
553
+ numTicks: 4,
554
+ tickLabelProps: () => ({
555
+ fill: "currentColor",
556
+ fontSize: 10,
557
+ textAnchor: "end",
558
+ dx: -4,
559
+ dy: 4,
560
+ className: "text-gray-500 dark:text-gray-400"
561
+ }),
562
+ hideAxisLine: true,
563
+ tickStroke: "transparent"
564
+ }
565
+ )
566
+ ] })
567
+ ] });
568
+ };
569
+ var BarChart = ({ data, width, height, color = "#3b82f6" }) => {
570
+ const innerWidth = width - margin.left - margin.right;
571
+ const innerHeight = height - margin.top - margin.bottom;
572
+ const xScale = useMemo(
573
+ () => scaleBand({
574
+ domain: data.map((d) => d.label),
575
+ range: [0, innerWidth],
576
+ padding: 0.3
577
+ }),
578
+ [data, innerWidth]
579
+ );
580
+ const yScale = useMemo(() => {
581
+ const maxValue = Math.max(...data.map((d) => d.value));
582
+ return scaleLinear({
583
+ domain: [0, maxValue * 1.1],
584
+ range: [innerHeight, 0],
585
+ nice: true
586
+ });
587
+ }, [data, innerHeight]);
588
+ if (width < 100 || height < 80) return null;
589
+ return /* @__PURE__ */ jsx("svg", { width, height, children: /* @__PURE__ */ jsxs(Group, { left: margin.left, top: margin.top, children: [
590
+ /* @__PURE__ */ jsx(
591
+ GridRows,
592
+ {
593
+ scale: yScale,
594
+ width: innerWidth,
595
+ strokeDasharray: "3,3",
596
+ stroke: "currentColor",
597
+ strokeOpacity: 0.1,
598
+ className: "text-gray-400 dark:text-neutral-600"
599
+ }
600
+ ),
601
+ data.map((d, i) => {
602
+ const barWidth = xScale.bandwidth();
603
+ const barHeight = innerHeight - yScale(d.value);
604
+ const barX = xScale(d.label) || 0;
605
+ const barY = yScale(d.value);
606
+ return /* @__PURE__ */ jsx(
607
+ Bar,
608
+ {
609
+ x: barX,
610
+ y: barY,
611
+ width: barWidth,
612
+ height: barHeight,
613
+ fill: d.color || color,
614
+ rx: 4,
615
+ className: "transition-opacity hover:opacity-80"
616
+ },
617
+ `bar-${i}`
618
+ );
619
+ }),
620
+ /* @__PURE__ */ jsx(
621
+ AxisBottom,
622
+ {
623
+ top: innerHeight,
624
+ scale: xScale,
625
+ tickLabelProps: () => ({
626
+ fill: "currentColor",
627
+ fontSize: 10,
628
+ textAnchor: "middle",
629
+ className: "text-gray-500 dark:text-gray-400"
630
+ }),
631
+ hideAxisLine: true,
632
+ tickStroke: "transparent"
633
+ }
634
+ ),
635
+ /* @__PURE__ */ jsx(
636
+ AxisLeft,
637
+ {
638
+ scale: yScale,
639
+ numTicks: 4,
640
+ tickLabelProps: () => ({
641
+ fill: "currentColor",
642
+ fontSize: 10,
643
+ textAnchor: "end",
644
+ dx: -4,
645
+ dy: 4,
646
+ className: "text-gray-500 dark:text-gray-400"
647
+ }),
648
+ hideAxisLine: true,
649
+ tickStroke: "transparent"
650
+ }
651
+ )
652
+ ] }) });
653
+ };
654
+ var horizontalMargin = { top: 10, right: 20, bottom: 10, left: 80 };
655
+ var HorizontalBarChart = ({ data, width, height, color = "#3b82f6" }) => {
656
+ const innerWidth = width - horizontalMargin.left - horizontalMargin.right;
657
+ const innerHeight = height - horizontalMargin.top - horizontalMargin.bottom;
658
+ const yScale = useMemo(
659
+ () => scaleBand({
660
+ domain: data.map((d) => d.label),
661
+ range: [0, innerHeight],
662
+ padding: 0.3
663
+ }),
664
+ [data, innerHeight]
665
+ );
666
+ const xScale = useMemo(() => {
667
+ const maxValue = Math.max(...data.map((d) => d.value));
668
+ return scaleLinear({
669
+ domain: [0, maxValue * 1.1],
670
+ range: [0, innerWidth],
671
+ nice: true
672
+ });
673
+ }, [data, innerWidth]);
674
+ if (width < 100 || height < 80) return null;
675
+ return /* @__PURE__ */ jsx("svg", { width, height, children: /* @__PURE__ */ jsxs(Group, { left: horizontalMargin.left, top: horizontalMargin.top, children: [
676
+ xScale.ticks(4).map((tick, i) => /* @__PURE__ */ jsx(
677
+ "line",
678
+ {
679
+ x1: xScale(tick),
680
+ x2: xScale(tick),
681
+ y1: 0,
682
+ y2: innerHeight,
683
+ stroke: "currentColor",
684
+ strokeOpacity: 0.1,
685
+ strokeDasharray: "3,3",
686
+ className: "text-gray-400 dark:text-neutral-600"
687
+ },
688
+ `grid-${i}`
689
+ )),
690
+ data.map((d, i) => {
691
+ const barHeight = yScale.bandwidth();
692
+ const barWidth = xScale(d.value);
693
+ const barY = yScale(d.label) || 0;
694
+ return /* @__PURE__ */ jsxs("g", { children: [
695
+ /* @__PURE__ */ jsx(
696
+ Bar,
697
+ {
698
+ x: 0,
699
+ y: barY,
700
+ width: barWidth,
701
+ height: barHeight,
702
+ fill: d.color || color,
703
+ rx: 4,
704
+ className: "transition-opacity hover:opacity-80"
705
+ }
706
+ ),
707
+ /* @__PURE__ */ jsx(
708
+ "text",
709
+ {
710
+ x: barWidth + 6,
711
+ y: barY + barHeight / 2,
712
+ dy: ".35em",
713
+ fill: "currentColor",
714
+ fontSize: 10,
715
+ className: "text-gray-600 dark:text-gray-400",
716
+ children: d.value.toLocaleString()
717
+ }
718
+ )
719
+ ] }, `bar-${i}`);
720
+ }),
721
+ data.map((d, i) => /* @__PURE__ */ jsx(
722
+ "text",
723
+ {
724
+ x: -8,
725
+ y: (yScale(d.label) || 0) + yScale.bandwidth() / 2,
726
+ dy: ".35em",
727
+ fill: "currentColor",
728
+ fontSize: 10,
729
+ textAnchor: "end",
730
+ className: "text-gray-500 dark:text-gray-400",
731
+ children: d.label
732
+ },
733
+ `label-${i}`
734
+ ))
735
+ ] }) });
736
+ };
737
+ var ChartRenderer = forwardRef(
738
+ ({ config, height, showLegend = true, className }, ref) => {
739
+ const total = useMemo(() => {
740
+ if (config.type === "donut") {
741
+ return config.data.reduce((sum, d) => sum + d.value, 0);
742
+ }
743
+ return 0;
744
+ }, [config]);
745
+ const fillMode = height === void 0;
746
+ return /* @__PURE__ */ jsxs("div", { ref, className: cn("flex flex-col", fillMode && "flex-1 min-h-0", className), children: [
747
+ /* @__PURE__ */ jsx(
748
+ "div",
749
+ {
750
+ className: cn("w-full", fillMode && "flex-1 min-h-0"),
751
+ style: height !== void 0 ? { height } : void 0,
752
+ children: /* @__PURE__ */ jsx(ParentSize, { children: ({ width, height: h }) => {
753
+ switch (config.type) {
754
+ case "donut":
755
+ return /* @__PURE__ */ jsx(
756
+ DonutChart,
757
+ {
758
+ data: config.data,
759
+ width,
760
+ height: h,
761
+ centerLabel: config.centerLabel,
762
+ centerValue: config.centerValue
763
+ }
764
+ );
765
+ case "line":
766
+ return /* @__PURE__ */ jsx(
767
+ LineChart,
768
+ {
769
+ data: config.data,
770
+ width,
771
+ height: h,
772
+ color: config.color,
773
+ showArea: config.showArea
774
+ }
775
+ );
776
+ case "line-multi":
777
+ return /* @__PURE__ */ jsx(
778
+ MultiLineChart,
779
+ {
780
+ series: config.series,
781
+ width,
782
+ height: h,
783
+ showArea: config.showArea
784
+ }
785
+ );
786
+ case "bar":
787
+ return /* @__PURE__ */ jsx(BarChart, { data: config.data, width, height: h, color: config.color });
788
+ case "bar-horizontal":
789
+ return /* @__PURE__ */ jsx(HorizontalBarChart, { data: config.data, width, height: h, color: config.color });
790
+ default:
791
+ return null;
792
+ }
793
+ } })
794
+ }
795
+ ),
796
+ showLegend && config.type === "donut" && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-x-4 gap-y-2 mt-3", children: config.data.map((item, index) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
797
+ /* @__PURE__ */ jsx(
798
+ "div",
799
+ {
800
+ className: "w-2.5 h-2.5 rounded-full",
801
+ style: { backgroundColor: item.color || defaultColors[index % defaultColors.length] }
802
+ }
803
+ ),
804
+ /* @__PURE__ */ jsx(Text, { as: "span", size: "xs", color: "muted", children: item.label }),
805
+ /* @__PURE__ */ jsxs(Text, { as: "span", size: "xs", weight: "medium", className: "text-gray-700 dark:text-gray-300", children: [
806
+ (item.value / total * 100).toFixed(0),
807
+ "%"
808
+ ] })
809
+ ] }, item.label)) }),
810
+ showLegend && config.type === "line-multi" && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-x-4 gap-y-2 mt-3", children: config.series.map((s, index) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
811
+ /* @__PURE__ */ jsx(
812
+ "div",
813
+ {
814
+ className: "w-2.5 h-2.5 rounded-full",
815
+ style: { backgroundColor: s.color || defaultColors[index % defaultColors.length] }
816
+ }
817
+ ),
818
+ /* @__PURE__ */ jsx(Text, { as: "span", size: "xs", color: "muted", children: s.name })
819
+ ] }, s.name)) })
820
+ ] });
821
+ }
822
+ );
823
+ ChartRenderer.displayName = "ChartRenderer";
824
+ var CompactPanel = forwardRef(
825
+ (props, ref) => {
826
+ const { className, title, fillHeight = true, headerControls, lastUpdated } = props;
827
+ const isChartMode = props.mode === "chart";
828
+ return /* @__PURE__ */ jsxs(
829
+ "div",
830
+ {
831
+ ref,
832
+ className: cn(
833
+ fillHeight && "h-full",
834
+ "rounded-hz-lg bg-white dark:bg-neutral-900",
835
+ "border border-gray-200 dark:border-neutral-800",
836
+ "p-4 shadow-sm flex flex-col",
837
+ className
838
+ ),
839
+ children: [
840
+ (title || headerControls || lastUpdated) && /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2 mb-3", children: [
841
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
842
+ title && /* @__PURE__ */ jsx(
843
+ Text,
844
+ {
845
+ as: "span",
846
+ size: "xs",
847
+ weight: "medium",
848
+ color: "muted",
849
+ children: title
850
+ }
851
+ ),
852
+ lastUpdated && /* @__PURE__ */ jsxs(Text, { as: "span", size: "xs", className: "text-gray-400 dark:text-gray-500", children: [
853
+ "\xB7 ",
854
+ lastUpdated
855
+ ] })
856
+ ] }),
857
+ headerControls && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1 -mt-1 -mr-1", children: headerControls })
858
+ ] }),
859
+ /* @__PURE__ */ jsx("div", { className: cn(
860
+ "flex-1 flex flex-col min-h-0",
861
+ isChartMode ? "justify-center" : "justify-end"
862
+ ), children: isChartMode ? /* @__PURE__ */ jsx(ChartRenderer, { config: props.chart, showLegend: false, className: "flex-1" }) : /* @__PURE__ */ jsx(
863
+ StatDisplay,
864
+ {
865
+ data: {
866
+ value: props.value,
867
+ label: props.label || "",
868
+ prefix: props.prefix,
869
+ suffix: props.suffix,
870
+ trend: props.trend
871
+ },
872
+ size: "sm"
873
+ }
874
+ ) })
875
+ ]
876
+ }
877
+ );
878
+ }
879
+ );
880
+ CompactPanel.displayName = "CompactPanel";
881
+ var TrendIndicator = ({ trend }) => {
882
+ const Icon = trend.direction === "up" ? TrendingUp : trend.direction === "down" ? TrendingDown : Minus;
883
+ return /* @__PURE__ */ jsxs(
884
+ "span",
885
+ {
886
+ className: cn(
887
+ "inline-flex items-center gap-1",
888
+ trend.direction === "up" && "text-emerald-600 dark:text-emerald-400",
889
+ trend.direction === "down" && "text-rose-600 dark:text-rose-400",
890
+ trend.direction === "neutral" && "text-gray-500 dark:text-gray-400"
891
+ ),
892
+ children: [
893
+ /* @__PURE__ */ jsx(Icon, { className: "w-4 h-4" }),
894
+ /* @__PURE__ */ jsxs("span", { className: "text-sm font-semibold", children: [
895
+ Math.abs(trend.value).toFixed(1),
896
+ "%"
897
+ ] })
898
+ ]
899
+ }
900
+ );
901
+ };
902
+ var isTableCellValue = (value) => {
903
+ return typeof value === "object" && value !== null && "value" in value;
904
+ };
905
+ var formatValue = (value) => {
906
+ if (typeof value === "number") {
907
+ return value.toLocaleString("en-US", {
908
+ minimumFractionDigits: 0,
909
+ maximumFractionDigits: 2
910
+ });
911
+ }
912
+ return value;
913
+ };
914
+ var renderCellContent = (cellValue, align) => {
915
+ if (cellValue === void 0 || cellValue === null) {
916
+ return /* @__PURE__ */ jsx("span", { className: "text-gray-400", children: "\u2014" });
917
+ }
918
+ if (isTableCellValue(cellValue)) {
919
+ const { value, trend, prefix = "", suffix = "", image } = cellValue;
920
+ return /* @__PURE__ */ jsxs(
921
+ "div",
922
+ {
923
+ className: cn(
924
+ "flex items-center gap-2",
925
+ align === "right" && "justify-end",
926
+ align === "center" && "justify-center"
927
+ ),
928
+ children: [
929
+ image && /* @__PURE__ */ jsx(
930
+ "img",
931
+ {
932
+ src: image,
933
+ alt: "",
934
+ className: "w-8 h-8 rounded-hz-md object-contain flex-shrink-0"
935
+ }
936
+ ),
937
+ /* @__PURE__ */ jsxs("span", { children: [
938
+ prefix,
939
+ formatValue(value),
940
+ suffix
941
+ ] }),
942
+ trend && /* @__PURE__ */ jsx(TrendIndicator, { trend })
943
+ ]
944
+ }
945
+ );
946
+ }
947
+ return formatValue(cellValue);
948
+ };
949
+ var TableRenderer = forwardRef(
950
+ ({ config, className }, ref) => {
951
+ const { columns, data, striped = false, compact = false, maxRows, pagination } = config;
952
+ const displayData = useMemo(() => {
953
+ if (maxRows && maxRows > 0) {
954
+ return data.slice(0, maxRows);
955
+ }
956
+ return data;
957
+ }, [data, maxRows]);
958
+ return /* @__PURE__ */ jsxs("div", { ref, className: cn("h-full flex flex-col", className), children: [
959
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-x-auto", children: [
960
+ /* @__PURE__ */ jsxs("table", { className: "w-full text-sm", children: [
961
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { className: "border-b border-gray-200 dark:border-neutral-700", children: columns.map((column) => /* @__PURE__ */ jsx(
962
+ "th",
963
+ {
964
+ className: cn(
965
+ "text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider",
966
+ "border-r border-gray-200 dark:border-neutral-700 last:border-r-0",
967
+ compact ? "py-2 px-3" : "py-3 px-4",
968
+ column.align === "center" && "text-center",
969
+ column.align === "right" && "text-right",
970
+ (!column.align || column.align === "left") && "text-left"
971
+ ),
972
+ style: column.width ? { width: column.width } : void 0,
973
+ children: column.label
974
+ },
975
+ column.key
976
+ )) }) }),
977
+ /* @__PURE__ */ jsx("tbody", { children: displayData.map((row, rowIndex) => /* @__PURE__ */ jsx(
978
+ "tr",
979
+ {
980
+ className: cn(
981
+ "border-b border-gray-100 dark:border-neutral-800 last:border-0",
982
+ striped && rowIndex % 2 === 1 && "bg-gray-50 dark:bg-neutral-800/50"
983
+ ),
984
+ children: columns.map((column) => /* @__PURE__ */ jsx(
985
+ "td",
986
+ {
987
+ className: cn(
988
+ "text-gray-900 dark:text-gray-100",
989
+ "border-r border-gray-200 dark:border-neutral-700 last:border-r-0",
990
+ compact ? "py-2 px-3" : "py-3 px-4",
991
+ column.align === "center" && "text-center",
992
+ column.align === "right" && "text-right",
993
+ (!column.align || column.align === "left") && "text-left"
994
+ ),
995
+ children: renderCellContent(row[column.key], column.align)
996
+ },
997
+ `${row.id}-${column.key}`
998
+ ))
999
+ },
1000
+ row.id
1001
+ )) })
1002
+ ] }),
1003
+ displayData.length === 0 && /* @__PURE__ */ jsx("div", { className: "py-8 text-center text-gray-500 dark:text-gray-400 text-sm", children: "No data available" })
1004
+ ] }),
1005
+ pagination && /* @__PURE__ */ jsxs("footer", { className: "flex items-center justify-between px-4 py-3 bg-gray-50 dark:bg-neutral-800/50 border-t border-gray-200 dark:border-neutral-700 rounded-b-hz-lg", children: [
1006
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 text-xs text-gray-500 dark:text-gray-400", children: [
1007
+ pagination.totalItems !== void 0 && pagination.pageSize !== void 0 ? /* @__PURE__ */ jsxs("span", { children: [
1008
+ "Showing ",
1009
+ Math.min((pagination.currentPage - 1) * pagination.pageSize + 1, pagination.totalItems),
1010
+ "\u2013",
1011
+ Math.min(pagination.currentPage * pagination.pageSize, pagination.totalItems),
1012
+ " of ",
1013
+ pagination.totalItems
1014
+ ] }) : /* @__PURE__ */ jsxs("span", { children: [
1015
+ "Page ",
1016
+ pagination.currentPage,
1017
+ " of ",
1018
+ pagination.totalPages
1019
+ ] }),
1020
+ pagination.lastUpdated && /* @__PURE__ */ jsxs(Fragment, { children: [
1021
+ /* @__PURE__ */ jsx("span", { className: "text-gray-300 dark:text-gray-600", children: "\xB7" }),
1022
+ /* @__PURE__ */ jsxs("span", { children: [
1023
+ "Updated ",
1024
+ pagination.lastUpdated
1025
+ ] })
1026
+ ] })
1027
+ ] }),
1028
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
1029
+ /* @__PURE__ */ jsx(
1030
+ "button",
1031
+ {
1032
+ type: "button",
1033
+ onClick: () => pagination.onPageChange(pagination.currentPage - 1),
1034
+ disabled: pagination.currentPage <= 1,
1035
+ className: cn(
1036
+ "p-1.5 rounded-hz-md transition-colors",
1037
+ pagination.currentPage <= 1 ? "text-gray-300 dark:text-gray-600 cursor-not-allowed" : "text-gray-500 hover:text-gray-700 hover:bg-gray-200 dark:text-gray-400 dark:hover:text-gray-200 dark:hover:bg-neutral-700"
1038
+ ),
1039
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "w-4 h-4" })
1040
+ }
1041
+ ),
1042
+ /* @__PURE__ */ jsx(
1043
+ "button",
1044
+ {
1045
+ type: "button",
1046
+ onClick: () => pagination.onPageChange(pagination.currentPage + 1),
1047
+ disabled: pagination.currentPage >= pagination.totalPages,
1048
+ className: cn(
1049
+ "p-1.5 rounded-hz-md transition-colors",
1050
+ pagination.currentPage >= pagination.totalPages ? "text-gray-300 dark:text-gray-600 cursor-not-allowed" : "text-gray-500 hover:text-gray-700 hover:bg-gray-200 dark:text-gray-400 dark:hover:text-gray-200 dark:hover:bg-neutral-700"
1051
+ ),
1052
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "w-4 h-4" })
1053
+ }
1054
+ )
1055
+ ] })
1056
+ ] })
1057
+ ] });
1058
+ }
1059
+ );
1060
+ TableRenderer.displayName = "TableRenderer";
1061
+ var MediumPanelContext = createContext({ inPanel: false });
1062
+ var MediumPanelStat = forwardRef(
1063
+ ({ data, className }, ref) => {
1064
+ return /* @__PURE__ */ jsx("div", { ref, className: cn("px-5 pt-4", className), children: /* @__PURE__ */ jsx(StatDisplay, { data, size: "md" }) });
1065
+ }
1066
+ );
1067
+ MediumPanelStat.displayName = "MediumPanel.Stat";
1068
+ var MediumPanelChart = forwardRef(
1069
+ ({ config, className }, ref) => {
1070
+ return /* @__PURE__ */ jsx(ChartRenderer, { ref, config, className: cn("flex-1 px-5 pt-4", className) });
1071
+ }
1072
+ );
1073
+ MediumPanelChart.displayName = "MediumPanel.Chart";
1074
+ var MediumPanelTable = forwardRef(
1075
+ ({ config, className }, ref) => {
1076
+ return /* @__PURE__ */ jsx(
1077
+ TableRenderer,
1078
+ {
1079
+ ref,
1080
+ config: { ...config, compact: true },
1081
+ className: cn("flex-1", className)
1082
+ }
1083
+ );
1084
+ }
1085
+ );
1086
+ MediumPanelTable.displayName = "MediumPanel.Table";
1087
+ var MediumPanelBase = forwardRef(
1088
+ ({ title, subtitle, stat, chart, children, className, fillHeight = true, headerControls, lastUpdated }, ref) => {
1089
+ const hasPropsContent = stat || chart;
1090
+ const hasChildren = !!children;
1091
+ return /* @__PURE__ */ jsx(MediumPanelContext.Provider, { value: { inPanel: true }, children: /* @__PURE__ */ jsxs(
1092
+ "div",
1093
+ {
1094
+ ref,
1095
+ className: cn(
1096
+ fillHeight && "h-full",
1097
+ "rounded-hz-lg bg-white dark:bg-neutral-900",
1098
+ "border border-gray-200 dark:border-neutral-800",
1099
+ "shadow-sm flex flex-col",
1100
+ className
1101
+ ),
1102
+ children: [
1103
+ (title || subtitle || headerControls || lastUpdated) && /* @__PURE__ */ jsxs(Fragment, { children: [
1104
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2 px-5 py-4", children: [
1105
+ /* @__PURE__ */ jsxs("div", { children: [
1106
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1107
+ title && /* @__PURE__ */ jsx(
1108
+ Text,
1109
+ {
1110
+ as: "span",
1111
+ weight: "semibold",
1112
+ size: "sm",
1113
+ className: "text-gray-900 dark:text-gray-100",
1114
+ children: title
1115
+ }
1116
+ ),
1117
+ lastUpdated && /* @__PURE__ */ jsxs(Text, { as: "span", size: "xs", className: "text-gray-400 dark:text-gray-500", children: [
1118
+ "\xB7 ",
1119
+ lastUpdated
1120
+ ] })
1121
+ ] }),
1122
+ subtitle && /* @__PURE__ */ jsx(Text, { as: "p", size: "xs", color: "muted", className: "mt-0.5", children: subtitle })
1123
+ ] }),
1124
+ headerControls && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1", children: headerControls })
1125
+ ] }),
1126
+ /* @__PURE__ */ jsx("div", { className: "border-b border-gray-200 dark:border-neutral-800" })
1127
+ ] }),
1128
+ hasPropsContent && !hasChildren && /* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col gap-4 min-h-0 px-5 py-4", children: [
1129
+ stat && /* @__PURE__ */ jsx(StatDisplay, { data: stat, size: "md" }),
1130
+ chart && /* @__PURE__ */ jsx(ChartRenderer, { config: chart })
1131
+ ] }),
1132
+ hasChildren && /* @__PURE__ */ jsx("div", { className: "flex-1 flex flex-col min-h-0", children })
1133
+ ]
1134
+ }
1135
+ ) });
1136
+ }
1137
+ );
1138
+ MediumPanelBase.displayName = "MediumPanel";
1139
+ var MediumPanel = MediumPanelBase;
1140
+ MediumPanel.Stat = MediumPanelStat;
1141
+ MediumPanel.Chart = MediumPanelChart;
1142
+ MediumPanel.Table = MediumPanelTable;
1143
+ var LargePanelContext = createContext({ inPanel: false });
1144
+ var LargePanelHeader = forwardRef(
1145
+ ({ children, className }, ref) => {
1146
+ return /* @__PURE__ */ jsx("div", { ref, className: cn("mb-4", className), children });
1147
+ }
1148
+ );
1149
+ LargePanelHeader.displayName = "LargePanel.Header";
1150
+ var LargePanelStats = forwardRef(
1151
+ ({ data, columns = 3, className }, ref) => {
1152
+ return /* @__PURE__ */ jsx(
1153
+ "div",
1154
+ {
1155
+ ref,
1156
+ className: cn(
1157
+ "grid gap-4 px-6 pt-4",
1158
+ columns === 2 && "grid-cols-2",
1159
+ columns === 3 && "grid-cols-3",
1160
+ columns === 4 && "grid-cols-4",
1161
+ className
1162
+ ),
1163
+ children: data.map((stat, index) => /* @__PURE__ */ jsx(StatDisplay, { data: stat, size: "sm" }, index))
1164
+ }
1165
+ );
1166
+ }
1167
+ );
1168
+ LargePanelStats.displayName = "LargePanel.Stats";
1169
+ var LargePanelChart = forwardRef(
1170
+ ({ config, height, className }, ref) => {
1171
+ return /* @__PURE__ */ jsx(ChartRenderer, { ref, config, height, className: cn("flex-1 min-h-0 px-6 pt-4", className) });
1172
+ }
1173
+ );
1174
+ LargePanelChart.displayName = "LargePanel.Chart";
1175
+ var LargePanelContent = forwardRef(
1176
+ ({ children, className }, ref) => {
1177
+ return /* @__PURE__ */ jsx("div", { ref, className: cn("px-6", className), children });
1178
+ }
1179
+ );
1180
+ LargePanelContent.displayName = "LargePanel.Content";
1181
+ var LargePanelTable = forwardRef(
1182
+ ({ config, className }, ref) => {
1183
+ return /* @__PURE__ */ jsx(TableRenderer, { ref, config, className: cn("flex-1 min-h-0", className) });
1184
+ }
1185
+ );
1186
+ LargePanelTable.displayName = "LargePanel.Table";
1187
+ var LargePanelBase = forwardRef(
1188
+ ({ title, subtitle, children, className, fillHeight = true, headerControls, lastUpdated }, ref) => {
1189
+ return /* @__PURE__ */ jsx(LargePanelContext.Provider, { value: { inPanel: true }, children: /* @__PURE__ */ jsxs(
1190
+ "div",
1191
+ {
1192
+ ref,
1193
+ className: cn(
1194
+ fillHeight && "h-full",
1195
+ "rounded-hz-lg bg-white dark:bg-neutral-900",
1196
+ "border border-gray-200 dark:border-neutral-800",
1197
+ "shadow-sm flex flex-col",
1198
+ className
1199
+ ),
1200
+ children: [
1201
+ (title || subtitle || headerControls || lastUpdated) && /* @__PURE__ */ jsxs(Fragment, { children: [
1202
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2 px-6 py-4", children: [
1203
+ /* @__PURE__ */ jsxs("div", { children: [
1204
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1205
+ title && /* @__PURE__ */ jsx(
1206
+ Text,
1207
+ {
1208
+ as: "span",
1209
+ weight: "semibold",
1210
+ size: "sm",
1211
+ className: "text-gray-900 dark:text-gray-100",
1212
+ children: title
1213
+ }
1214
+ ),
1215
+ lastUpdated && /* @__PURE__ */ jsxs(Text, { as: "span", size: "xs", className: "text-gray-400 dark:text-gray-500", children: [
1216
+ "\xB7 ",
1217
+ lastUpdated
1218
+ ] })
1219
+ ] }),
1220
+ subtitle && /* @__PURE__ */ jsx(Text, { as: "p", size: "xs", color: "muted", className: "mt-0.5", children: subtitle })
1221
+ ] }),
1222
+ headerControls && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1", children: headerControls })
1223
+ ] }),
1224
+ /* @__PURE__ */ jsx("div", { className: "border-b border-gray-200 dark:border-neutral-800" })
1225
+ ] }),
1226
+ /* @__PURE__ */ jsx("div", { className: "flex-1 flex flex-col min-h-0", children })
1227
+ ]
1228
+ }
1229
+ ) });
1230
+ }
1231
+ );
1232
+ LargePanelBase.displayName = "LargePanel";
1233
+ var LargePanel = LargePanelBase;
1234
+ LargePanel.Header = LargePanelHeader;
1235
+ LargePanel.Stats = LargePanelStats;
1236
+ LargePanel.Chart = LargePanelChart;
1237
+ LargePanel.Content = LargePanelContent;
1238
+ LargePanel.Table = LargePanelTable;
36
1239
  var Input = forwardRef(
37
1240
  ({
38
1241
  label,
@@ -1329,6 +2532,6 @@ var SideNavFooter = forwardRef(
1329
2532
  );
1330
2533
  SideNavFooter.displayName = "SideNavFooter";
1331
2534
 
1332
- export { AnimatedButton, Input, SideNav, SideNavFooter, SideNavHeader, SideNavItem, SideNavSection, TemplateSelector, TextButton, Toast, useSideNavContext };
1333
- //# sourceMappingURL=chunk-MEJ3HYYK.mjs.map
1334
- //# sourceMappingURL=chunk-MEJ3HYYK.mjs.map
2535
+ export { AnimatedButton, ChartRenderer, CompactPanel, Dashboard, DashboardPanel, Input, LargePanel, MediumPanel, SideNav, SideNavFooter, SideNavHeader, SideNavItem, SideNavSection, StatDisplay, TableRenderer, TemplateSelector, TextButton, Toast, useDashboardContext, useSideNavContext };
2536
+ //# sourceMappingURL=chunk-EBJJNTPC.mjs.map
2537
+ //# sourceMappingURL=chunk-EBJJNTPC.mjs.map