@quantumwake/terminal-ux-dashboard-components 0.1.13 → 0.1.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +286 -160
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +25 -9
- package/dist/index.d.ts +25 -9
- package/dist/index.js +287 -162
- package/dist/index.js.map +1 -1
- package/package.json +6 -2
package/dist/index.cjs
CHANGED
|
@@ -9,15 +9,18 @@ var scatterplot = require('@nivo/scatterplot');
|
|
|
9
9
|
var heatmap = require('@nivo/heatmap');
|
|
10
10
|
var PivotTableUI = require('react-pivottable/PivotTableUI');
|
|
11
11
|
require('react-pivottable/pivottable.css');
|
|
12
|
+
var terminalUxComponents = require('@quantumwake/terminal-ux-components');
|
|
12
13
|
var lucideReact = require('lucide-react');
|
|
13
14
|
var CodeMirror = require('@uiw/react-codemirror');
|
|
14
15
|
var langSql = require('@codemirror/lang-sql');
|
|
15
16
|
var autocomplete = require('@codemirror/autocomplete');
|
|
17
|
+
var GridLayout = require('react-grid-layout');
|
|
16
18
|
|
|
17
19
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
18
20
|
|
|
19
21
|
var PivotTableUI__default = /*#__PURE__*/_interopDefault(PivotTableUI);
|
|
20
22
|
var CodeMirror__default = /*#__PURE__*/_interopDefault(CodeMirror);
|
|
23
|
+
var GridLayout__default = /*#__PURE__*/_interopDefault(GridLayout);
|
|
21
24
|
|
|
22
25
|
// src/context/DashboardContext.tsx
|
|
23
26
|
var DashboardContext = react.createContext(null);
|
|
@@ -46,7 +49,9 @@ function useCapabilities() {
|
|
|
46
49
|
canAddPanel: !!c.addPanel,
|
|
47
50
|
canEditPanels: !!c.removePanel,
|
|
48
51
|
// Studio can recompute + persist a panel's precomputed result.
|
|
49
|
-
canRefresh: !!c.persistPanelData
|
|
52
|
+
canRefresh: !!c.persistPanelData,
|
|
53
|
+
// Studio can drag/resize panels and persist the grid layout.
|
|
54
|
+
canEditLayout: !!c.persistLayout
|
|
50
55
|
};
|
|
51
56
|
}
|
|
52
57
|
|
|
@@ -231,8 +236,15 @@ var DEFAULT_CHART_STYLE = {
|
|
|
231
236
|
legendBold: false,
|
|
232
237
|
legendHighlight: "",
|
|
233
238
|
// Tick overflow handling.
|
|
234
|
-
xTickRotation:
|
|
239
|
+
xTickRotation: 0,
|
|
240
|
+
yTickRotation: 0,
|
|
235
241
|
tickTruncate: 0,
|
|
242
|
+
tickWrap: true,
|
|
243
|
+
tickWrapWidth: 14,
|
|
244
|
+
// Table cells.
|
|
245
|
+
cellWrap: true,
|
|
246
|
+
cellPrecision: 2,
|
|
247
|
+
cellTruncate: 0,
|
|
236
248
|
// Series legend placement (charts that have one: pie, grouped bar).
|
|
237
249
|
legendAnchor: "right",
|
|
238
250
|
// Panel title (rendered by DashboardRenderer's panel header).
|
|
@@ -307,7 +319,7 @@ var axisLegend = (style, axis, columnName, opts2 = {}) => {
|
|
|
307
319
|
out.legendPosition = isX ? s.xLegendPosition : s.yLegendPosition;
|
|
308
320
|
out.legendOffset = isX ? s.xLegendOffset : s.yLegendOffset;
|
|
309
321
|
}
|
|
310
|
-
|
|
322
|
+
out.tickRotation = isX ? s.xTickRotation : s.yTickRotation;
|
|
311
323
|
if (opts2.numeric) out.format = (v) => Number(v).toLocaleString();
|
|
312
324
|
else if (s.tickTruncate > 0) out.format = (v) => truncate(v, s.tickTruncate);
|
|
313
325
|
return out;
|
|
@@ -357,6 +369,78 @@ function MetricView({ records, config, value: presetValue }) {
|
|
|
357
369
|
function InsightView({ config }) {
|
|
358
370
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full p-4 overflow-auto", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-midnight-text-body leading-relaxed whitespace-pre-wrap", children: config.text }) });
|
|
359
371
|
}
|
|
372
|
+
function formatVal(value, s, numeric) {
|
|
373
|
+
let str = numeric ? Number(value).toLocaleString() : String(value);
|
|
374
|
+
if (s.tickTruncate > 0 && str.length > s.tickTruncate) str = `${str.slice(0, s.tickTruncate)}\u2026`;
|
|
375
|
+
return str;
|
|
376
|
+
}
|
|
377
|
+
function wrapText(str, width) {
|
|
378
|
+
if (width <= 0 || str.length <= width) return [str];
|
|
379
|
+
const words = str.split(/\s+/);
|
|
380
|
+
const lines = [];
|
|
381
|
+
let cur = "";
|
|
382
|
+
const push = (t) => {
|
|
383
|
+
if (t) lines.push(t);
|
|
384
|
+
};
|
|
385
|
+
for (const w of words) {
|
|
386
|
+
if (w.length > width) {
|
|
387
|
+
push(cur);
|
|
388
|
+
cur = "";
|
|
389
|
+
for (let i = 0; i < w.length; i += width) lines.push(w.slice(i, i + width));
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
if (!cur) cur = w;
|
|
393
|
+
else if (cur.length + 1 + w.length <= width) cur += ` ${w}`;
|
|
394
|
+
else {
|
|
395
|
+
push(cur);
|
|
396
|
+
cur = w;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
push(cur);
|
|
400
|
+
if (lines.length > 4) {
|
|
401
|
+
const kept = lines.slice(0, 4);
|
|
402
|
+
kept[3] = `${kept[3].slice(0, Math.max(0, width - 1))}\u2026`;
|
|
403
|
+
return kept;
|
|
404
|
+
}
|
|
405
|
+
return lines;
|
|
406
|
+
}
|
|
407
|
+
function makeAxis(style, axis, columnName, opts2 = {}) {
|
|
408
|
+
const s = withStyleDefaults(style);
|
|
409
|
+
const isX = axis === "x";
|
|
410
|
+
const numeric = !!opts2.numeric;
|
|
411
|
+
const show = isX ? s.showXLegend : s.showYLegend;
|
|
412
|
+
const label = (isX ? s.xAxisLabel : s.yAxisLabel) || columnName;
|
|
413
|
+
const rotate = isX ? s.xTickRotation : s.yTickRotation;
|
|
414
|
+
const out = { tickSize: 5, tickPadding: 5 };
|
|
415
|
+
if (show && label) {
|
|
416
|
+
out.legend = label;
|
|
417
|
+
out.legendPosition = isX ? s.xLegendPosition : s.yLegendPosition;
|
|
418
|
+
out.legendOffset = isX ? s.xLegendOffset : s.yLegendOffset;
|
|
419
|
+
}
|
|
420
|
+
if (s.tickWrap) {
|
|
421
|
+
out.renderTick = (tick) => {
|
|
422
|
+
const lines = wrapText(formatVal(tick.value, s, numeric), s.tickWrapWidth);
|
|
423
|
+
const firstDy = isX ? "0" : `${-((lines.length - 1) * 0.55)}em`;
|
|
424
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("g", { transform: `translate(${tick.x},${tick.y})`, children: [
|
|
425
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x2: isX ? 0 : -5, y2: isX ? 5 : 0, style: { stroke: s.textColor, strokeWidth: 1, opacity: 0.3 } }),
|
|
426
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
427
|
+
"text",
|
|
428
|
+
{
|
|
429
|
+
transform: `translate(${tick.textX},${tick.textY}) rotate(${rotate})`,
|
|
430
|
+
textAnchor: tick.textAnchor,
|
|
431
|
+
dominantBaseline: tick.textBaseline,
|
|
432
|
+
style: { fill: s.textColor, fontSize: s.fontSize },
|
|
433
|
+
children: lines.map((ln, i) => /* @__PURE__ */ jsxRuntime.jsx("tspan", { x: 0, dy: i === 0 ? firstDy : "1.1em", children: ln }, i))
|
|
434
|
+
}
|
|
435
|
+
)
|
|
436
|
+
] });
|
|
437
|
+
};
|
|
438
|
+
} else {
|
|
439
|
+
out.tickRotation = rotate;
|
|
440
|
+
out.format = (v) => formatVal(v, s, numeric);
|
|
441
|
+
}
|
|
442
|
+
return out;
|
|
443
|
+
}
|
|
360
444
|
function BarView({ records, groupColumn, valueColumn, aggFn = "count", data: presetData, style }) {
|
|
361
445
|
const computed = react.useMemo(() => {
|
|
362
446
|
if (presetData || !records) return [];
|
|
@@ -364,7 +448,7 @@ function BarView({ records, groupColumn, valueColumn, aggFn = "count", data: pre
|
|
|
364
448
|
return Object.entries(groups).map(([key, recs]) => ({ group: key, value: aggregate(recs, valueColumn, aggFn) })).sort((a, b) => b.value - a.value).slice(0, 50);
|
|
365
449
|
}, [records, groupColumn, valueColumn, aggFn, presetData]);
|
|
366
450
|
const data = presetData || computed;
|
|
367
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full min-h-[
|
|
451
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full w-full min-h-[160px]", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
368
452
|
bar.ResponsiveBar,
|
|
369
453
|
{
|
|
370
454
|
data,
|
|
@@ -374,8 +458,8 @@ function BarView({ records, groupColumn, valueColumn, aggFn = "count", data: pre
|
|
|
374
458
|
padding: 0.3,
|
|
375
459
|
colors: ["rgba(74, 222, 128, 0.8)"],
|
|
376
460
|
borderColor: { from: "color", modifiers: [["darker", 1.6]] },
|
|
377
|
-
axisBottom:
|
|
378
|
-
axisLeft:
|
|
461
|
+
axisBottom: makeAxis(style, "x", groupColumn),
|
|
462
|
+
axisLeft: makeAxis(style, "y", valueColumn, { numeric: true }),
|
|
379
463
|
labelSkipWidth: 12,
|
|
380
464
|
labelSkipHeight: 12,
|
|
381
465
|
labelTextColor: { from: "color", modifiers: [["darker", 3]] },
|
|
@@ -396,7 +480,7 @@ function PieView({ records, groupColumn, data: presetData, style }) {
|
|
|
396
480
|
}, [records, groupColumn, presetData]);
|
|
397
481
|
const data = presetData || computed;
|
|
398
482
|
const legend = legendConfig(style);
|
|
399
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full min-h-[
|
|
483
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full w-full min-h-[160px]", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
400
484
|
pie.ResponsivePie,
|
|
401
485
|
{
|
|
402
486
|
data,
|
|
@@ -432,7 +516,7 @@ function LineView({ records, xColumn, yColumn, data: presetData, style }) {
|
|
|
432
516
|
}];
|
|
433
517
|
}, [records, xColumn, yColumn, presetData]);
|
|
434
518
|
const data = presetData || computed;
|
|
435
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full min-h-[
|
|
519
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full w-full min-h-[160px]", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
436
520
|
line.ResponsiveLine,
|
|
437
521
|
{
|
|
438
522
|
data,
|
|
@@ -448,8 +532,8 @@ function LineView({ records, xColumn, yColumn, data: presetData, style }) {
|
|
|
448
532
|
pointBorderWidth: 2,
|
|
449
533
|
pointBorderColor: { from: "serieColor" },
|
|
450
534
|
enableGridX: false,
|
|
451
|
-
axisBottom:
|
|
452
|
-
axisLeft:
|
|
535
|
+
axisBottom: makeAxis(style, "x", xColumn),
|
|
536
|
+
axisLeft: makeAxis(style, "y", yColumn, { numeric: true }),
|
|
453
537
|
useMesh: true,
|
|
454
538
|
theme: buildNivoTheme(style)
|
|
455
539
|
}
|
|
@@ -462,7 +546,7 @@ function ScatterView({ records, xColumn, yColumn, data: presetData, style }) {
|
|
|
462
546
|
return [{ id: `${xColumn} vs ${yColumn}`, data: points }];
|
|
463
547
|
}, [records, xColumn, yColumn, presetData]);
|
|
464
548
|
const data = presetData || computed;
|
|
465
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full min-h-[
|
|
549
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full w-full min-h-[160px]", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
466
550
|
scatterplot.ResponsiveScatterPlot,
|
|
467
551
|
{
|
|
468
552
|
data,
|
|
@@ -471,8 +555,8 @@ function ScatterView({ records, xColumn, yColumn, data: presetData, style }) {
|
|
|
471
555
|
yScale: { type: "linear", min: "auto", max: "auto" },
|
|
472
556
|
colors: ["rgba(167, 139, 250, 0.7)"],
|
|
473
557
|
nodeSize: 6,
|
|
474
|
-
axisBottom:
|
|
475
|
-
axisLeft:
|
|
558
|
+
axisBottom: makeAxis(style, "x", xColumn, { numeric: true }),
|
|
559
|
+
axisLeft: makeAxis(style, "y", yColumn, { numeric: true }),
|
|
476
560
|
useMesh: true,
|
|
477
561
|
theme: buildNivoTheme(style)
|
|
478
562
|
}
|
|
@@ -566,7 +650,7 @@ function HeatmapView({
|
|
|
566
650
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-8 text-center text-midnight-text-muted", children: "Not enough distinct values for a heatmap" });
|
|
567
651
|
}
|
|
568
652
|
const margin = showTotals ? { top: 60, right: 70, bottom: 60, left: 100 } : { top: 60, right: 20, bottom: 20, left: 100 };
|
|
569
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full min-h-[
|
|
653
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full w-full min-h-[160px]", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
570
654
|
heatmap.ResponsiveHeatMap,
|
|
571
655
|
{
|
|
572
656
|
data,
|
|
@@ -616,120 +700,80 @@ function PivotView({ records }) {
|
|
|
616
700
|
)
|
|
617
701
|
] });
|
|
618
702
|
}
|
|
703
|
+
function Section({ title, children }) {
|
|
704
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t border-midnight-border font-mono", style: { paddingTop: 12, paddingBottom: 12 }, children: [
|
|
705
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "uppercase tracking-wider text-midnight-text-subdued", style: { fontSize: 10, marginBottom: 8 }, children: title }),
|
|
706
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column", rowGap: 6 }, children })
|
|
707
|
+
] });
|
|
708
|
+
}
|
|
619
709
|
function Row({ label, children }) {
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
/* @__PURE__ */ jsxRuntime.jsx("
|
|
623
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center", children })
|
|
710
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "grid", gridTemplateColumns: "84px minmax(0, 1fr)", alignItems: "center", columnGap: 8, height: 32 }, children: [
|
|
711
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-mono text-midnight-text-muted truncate", title: label, children: label }),
|
|
712
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", alignItems: "center", justifyContent: "flex-end", width: "100%", minWidth: 0 }, children })
|
|
624
713
|
] });
|
|
625
714
|
}
|
|
626
715
|
function ColorControl({ value, onChange }) {
|
|
627
|
-
const { theme } = useDashboard();
|
|
628
716
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
629
717
|
"input",
|
|
630
718
|
{
|
|
631
719
|
type: "color",
|
|
632
720
|
value: /^#/.test(value) ? value : "#94a3b8",
|
|
633
721
|
onChange: (e) => onChange(e.target.value),
|
|
634
|
-
className:
|
|
635
|
-
}
|
|
636
|
-
);
|
|
637
|
-
}
|
|
638
|
-
function NumberControl({ value, onChange, min, max }) {
|
|
639
|
-
const { theme } = useDashboard();
|
|
640
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
641
|
-
"input",
|
|
642
|
-
{
|
|
643
|
-
type: "number",
|
|
644
|
-
value,
|
|
645
|
-
min,
|
|
646
|
-
max,
|
|
647
|
-
onChange: (e) => onChange(Number(e.target.value)),
|
|
648
|
-
className: `w-20 px-2 py-1 text-xs ${theme.font} bg-midnight-surface border ${theme.border} text-midnight-text-body outline-none focus:border-midnight-accent`
|
|
649
|
-
}
|
|
650
|
-
);
|
|
651
|
-
}
|
|
652
|
-
function TextControl({ value, onChange, placeholder }) {
|
|
653
|
-
const { theme } = useDashboard();
|
|
654
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
655
|
-
"input",
|
|
656
|
-
{
|
|
657
|
-
type: "text",
|
|
658
|
-
value,
|
|
659
|
-
placeholder,
|
|
660
|
-
onChange: (e) => onChange(e.target.value),
|
|
661
|
-
className: `w-28 px-2 py-1 text-xs ${theme.font} bg-midnight-surface border ${theme.border} text-midnight-text-body outline-none focus:border-midnight-accent`
|
|
662
|
-
}
|
|
663
|
-
);
|
|
664
|
-
}
|
|
665
|
-
function Toggle({ value, onChange }) {
|
|
666
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
667
|
-
"input",
|
|
668
|
-
{
|
|
669
|
-
type: "checkbox",
|
|
670
|
-
checked: value,
|
|
671
|
-
onChange: (e) => onChange(e.target.checked),
|
|
672
|
-
className: "w-4 h-4 accent-midnight-accent cursor-pointer"
|
|
722
|
+
className: "w-9 h-6 bg-transparent border border-midnight-border cursor-pointer p-0"
|
|
673
723
|
}
|
|
674
724
|
);
|
|
675
725
|
}
|
|
676
726
|
function OptionalColor({ value, onChange, fallback = "#a78bfa" }) {
|
|
677
727
|
const on = !!value;
|
|
678
728
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
679
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
680
|
-
|
|
729
|
+
on && /* @__PURE__ */ jsxRuntime.jsx(ColorControl, { value, onChange }),
|
|
730
|
+
/* @__PURE__ */ jsxRuntime.jsx(terminalUxComponents.TerminalToggle, { size: "small", checked: on, onChange: (v) => onChange(v ? fallback : "") })
|
|
681
731
|
] });
|
|
682
732
|
}
|
|
683
|
-
function Choice({ value, options, onChange }) {
|
|
684
|
-
const { theme } = useDashboard();
|
|
685
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-28", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
686
|
-
"select",
|
|
687
|
-
{
|
|
688
|
-
value,
|
|
689
|
-
onChange: (e) => onChange(e.target.value),
|
|
690
|
-
className: `w-full px-2 py-1 text-xs ${theme.font} bg-midnight-surface border ${theme.border} text-midnight-text-body outline-none focus:border-midnight-accent`,
|
|
691
|
-
children: options.map((o) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: o, children: o }, o))
|
|
692
|
-
}
|
|
693
|
-
) });
|
|
694
|
-
}
|
|
695
|
-
function Group({ children, last }) {
|
|
696
|
-
const { theme } = useDashboard();
|
|
697
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `space-y-2 pb-3 ${last ? "" : `mb-3 border-b ${theme.border}`}`, children });
|
|
698
|
-
}
|
|
699
733
|
function ChartStyleControls({ style, onChange }) {
|
|
700
734
|
const s = withStyleDefaults(style);
|
|
701
735
|
const set = (key, value) => onChange({ ...s, [key]: value });
|
|
702
736
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
703
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
737
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Section, { title: "Canvas", children: [
|
|
704
738
|
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Background", children: /* @__PURE__ */ jsxRuntime.jsx(ColorControl, { value: s.background, onChange: (v) => set("background", v) }) }),
|
|
705
|
-
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Text
|
|
706
|
-
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Grid
|
|
707
|
-
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Font size", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
739
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Text", children: /* @__PURE__ */ jsxRuntime.jsx(ColorControl, { value: s.textColor, onChange: (v) => set("textColor", v) }) }),
|
|
740
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Grid", children: /* @__PURE__ */ jsxRuntime.jsx(ColorControl, { value: s.gridColor, onChange: (v) => set("gridColor", v) }) }),
|
|
741
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Font size", children: /* @__PURE__ */ jsxRuntime.jsx(terminalUxComponents.TerminalSlider, { value: s.fontSize, min: 6, max: 24, unit: "px", onChange: (v) => set("fontSize", v) }) })
|
|
708
742
|
] }),
|
|
709
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
710
|
-
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "X
|
|
711
|
-
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Show X
|
|
712
|
-
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Y
|
|
713
|
-
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Show Y
|
|
714
|
-
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "
|
|
715
|
-
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "
|
|
716
|
-
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "
|
|
717
|
-
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "X title pos", children: /* @__PURE__ */ jsxRuntime.jsx(Choice, { value: s.xLegendPosition, options: ["start", "middle", "end"], onChange: (v) => set("xLegendPosition", v) }) }),
|
|
718
|
-
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "X title offset", children: /* @__PURE__ */ jsxRuntime.jsx(NumberControl, { value: s.xLegendOffset, onChange: (v) => set("xLegendOffset", v) }) }),
|
|
719
|
-
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Y title pos", children: /* @__PURE__ */ jsxRuntime.jsx(Choice, { value: s.yLegendPosition, options: ["start", "middle", "end"], onChange: (v) => set("yLegendPosition", v) }) }),
|
|
720
|
-
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Y title offset", children: /* @__PURE__ */ jsxRuntime.jsx(NumberControl, { value: s.yLegendOffset, onChange: (v) => set("yLegendOffset", v) }) })
|
|
743
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Section, { title: "Axis titles", children: [
|
|
744
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "X title", children: /* @__PURE__ */ jsxRuntime.jsx(terminalUxComponents.TerminalInput, { size: "small", value: s.xAxisLabel, placeholder: "(column)", onChange: (e) => set("xAxisLabel", e.target.value) }) }),
|
|
745
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Show X", children: /* @__PURE__ */ jsxRuntime.jsx(terminalUxComponents.TerminalToggle, { size: "small", checked: s.showXLegend, onChange: (v) => set("showXLegend", v) }) }),
|
|
746
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Y title", children: /* @__PURE__ */ jsxRuntime.jsx(terminalUxComponents.TerminalInput, { size: "small", value: s.yAxisLabel, placeholder: "(column)", onChange: (e) => set("yAxisLabel", e.target.value) }) }),
|
|
747
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Show Y", children: /* @__PURE__ */ jsxRuntime.jsx(terminalUxComponents.TerminalToggle, { size: "small", checked: s.showYLegend, onChange: (v) => set("showYLegend", v) }) }),
|
|
748
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Color", children: /* @__PURE__ */ jsxRuntime.jsx(ColorControl, { value: s.legendColor, onChange: (v) => set("legendColor", v) }) }),
|
|
749
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Bold", children: /* @__PURE__ */ jsxRuntime.jsx(terminalUxComponents.TerminalToggle, { size: "small", checked: s.legendBold, onChange: (v) => set("legendBold", v) }) }),
|
|
750
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Highlight", children: /* @__PURE__ */ jsxRuntime.jsx(OptionalColor, { value: s.legendHighlight, onChange: (v) => set("legendHighlight", v) }) })
|
|
721
751
|
] }),
|
|
722
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
723
|
-
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "X
|
|
724
|
-
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "
|
|
752
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Section, { title: "Axis placement", children: [
|
|
753
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "X pos", children: /* @__PURE__ */ jsxRuntime.jsx(terminalUxComponents.TerminalSelect, { size: "small", value: s.xLegendPosition, options: ["start", "middle", "end"], onChange: (v) => set("xLegendPosition", v) }) }),
|
|
754
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "X offset", children: /* @__PURE__ */ jsxRuntime.jsx(terminalUxComponents.TerminalSlider, { value: s.xLegendOffset, min: -80, max: 80, onChange: (v) => set("xLegendOffset", v) }) }),
|
|
755
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Y pos", children: /* @__PURE__ */ jsxRuntime.jsx(terminalUxComponents.TerminalSelect, { size: "small", value: s.yLegendPosition, options: ["start", "middle", "end"], onChange: (v) => set("yLegendPosition", v) }) }),
|
|
756
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Y offset", children: /* @__PURE__ */ jsxRuntime.jsx(terminalUxComponents.TerminalSlider, { value: s.yLegendOffset, min: -80, max: 80, onChange: (v) => set("yLegendOffset", v) }) })
|
|
725
757
|
] }),
|
|
726
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
727
|
-
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "
|
|
728
|
-
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "
|
|
729
|
-
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "
|
|
730
|
-
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "
|
|
731
|
-
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "
|
|
732
|
-
] })
|
|
758
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Section, { title: "Ticks", children: [
|
|
759
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Wrap", children: /* @__PURE__ */ jsxRuntime.jsx(terminalUxComponents.TerminalToggle, { size: "small", checked: s.tickWrap, onChange: (v) => set("tickWrap", v) }) }),
|
|
760
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Wrap width", children: /* @__PURE__ */ jsxRuntime.jsx(terminalUxComponents.TerminalSlider, { value: s.tickWrapWidth, min: 4, max: 40, onChange: (v) => set("tickWrapWidth", v) }) }),
|
|
761
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "X angle", children: /* @__PURE__ */ jsxRuntime.jsx(terminalUxComponents.TerminalSlider, { value: s.xTickRotation, min: -90, max: 90, unit: "\xB0", onChange: (v) => set("xTickRotation", v) }) }),
|
|
762
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Y angle", children: /* @__PURE__ */ jsxRuntime.jsx(terminalUxComponents.TerminalSlider, { value: s.yTickRotation, min: -90, max: 90, unit: "\xB0", onChange: (v) => set("yTickRotation", v) }) }),
|
|
763
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Truncate", children: /* @__PURE__ */ jsxRuntime.jsx(terminalUxComponents.TerminalSlider, { value: s.tickTruncate, min: 0, max: 40, onChange: (v) => set("tickTruncate", v) }) })
|
|
764
|
+
] }),
|
|
765
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Section, { title: "Table cells", children: [
|
|
766
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Wrap", children: /* @__PURE__ */ jsxRuntime.jsx(terminalUxComponents.TerminalToggle, { size: "small", checked: s.cellWrap, onChange: (v) => set("cellWrap", v) }) }),
|
|
767
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Decimals", children: /* @__PURE__ */ jsxRuntime.jsx(terminalUxComponents.TerminalSlider, { value: s.cellPrecision, min: 0, max: 8, onChange: (v) => set("cellPrecision", v) }) }),
|
|
768
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Truncate", children: /* @__PURE__ */ jsxRuntime.jsx(terminalUxComponents.TerminalSlider, { value: s.cellTruncate, min: 0, max: 80, onChange: (v) => set("cellTruncate", v) }) })
|
|
769
|
+
] }),
|
|
770
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Section, { title: "Panel title", children: [
|
|
771
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Align", children: /* @__PURE__ */ jsxRuntime.jsx(terminalUxComponents.TerminalSelect, { size: "small", value: s.titleAlign, options: ["left", "center", "right"], onChange: (v) => set("titleAlign", v) }) }),
|
|
772
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Bold", children: /* @__PURE__ */ jsxRuntime.jsx(terminalUxComponents.TerminalToggle, { size: "small", checked: s.titleBold, onChange: (v) => set("titleBold", v) }) }),
|
|
773
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Color", children: /* @__PURE__ */ jsxRuntime.jsx(OptionalColor, { value: s.titleColor, onChange: (v) => set("titleColor", v), fallback: "#e2e8f0" }) }),
|
|
774
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Background", children: /* @__PURE__ */ jsxRuntime.jsx(OptionalColor, { value: s.titleBackground, onChange: (v) => set("titleBackground", v), fallback: "#1e293b" }) })
|
|
775
|
+
] }),
|
|
776
|
+
/* @__PURE__ */ jsxRuntime.jsx(Section, { title: "Series legend", children: /* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Position", children: /* @__PURE__ */ jsxRuntime.jsx(terminalUxComponents.TerminalSelect, { size: "small", value: s.legendAnchor, options: ["right", "top-left", "top-right", "bottom-left", "bottom-right", "none"], onChange: (v) => set("legendAnchor", v) }) }) })
|
|
733
777
|
] });
|
|
734
778
|
}
|
|
735
779
|
var AGGREGATES = [
|
|
@@ -1337,7 +1381,7 @@ var GroupedBarChart = ({ records, xFields = [], yFields = [], groupField, data:
|
|
|
1337
1381
|
const keys = presetKeys || yFields.map((yf) => `${yf.agg}(${yf.name})`);
|
|
1338
1382
|
const colors = ["rgba(74,222,128,0.8)", "rgba(96,165,250,0.8)", "rgba(251,146,60,0.8)", "rgba(167,139,250,0.8)", "rgba(248,113,113,0.8)"];
|
|
1339
1383
|
const legend = legendConfig(style);
|
|
1340
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full min-h-[
|
|
1384
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-full w-full min-h-[160px]", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1341
1385
|
bar.ResponsiveBar,
|
|
1342
1386
|
{
|
|
1343
1387
|
data,
|
|
@@ -1347,8 +1391,8 @@ var GroupedBarChart = ({ records, xFields = [], yFields = [], groupField, data:
|
|
|
1347
1391
|
margin: { top: 20, right: 120, bottom: 60, left: 60 },
|
|
1348
1392
|
padding: 0.3,
|
|
1349
1393
|
colors: colors.slice(0, keys.length),
|
|
1350
|
-
axisBottom:
|
|
1351
|
-
axisLeft:
|
|
1394
|
+
axisBottom: makeAxis(style, "x", xFields[0]?.name || ""),
|
|
1395
|
+
axisLeft: makeAxis(style, "y", "", { numeric: true }),
|
|
1352
1396
|
labelSkipWidth: 12,
|
|
1353
1397
|
labelSkipHeight: 12,
|
|
1354
1398
|
labelTextColor: { from: "color", modifiers: [["darker", 3]] },
|
|
@@ -1454,6 +1498,7 @@ function ChartBuilder({ records, columns, stateId, onSave }) {
|
|
|
1454
1498
|
const [style, setStyle] = react.useState(DEFAULT_CHART_STYLE);
|
|
1455
1499
|
const [showStyle, setShowStyle] = react.useState(false);
|
|
1456
1500
|
const [showFields, setShowFields] = react.useState(false);
|
|
1501
|
+
const [fillContainer, setFillContainer] = react.useState(false);
|
|
1457
1502
|
const [sqlRows, setSqlRows] = react.useState(null);
|
|
1458
1503
|
const [sqlLoading, setSqlLoading] = react.useState(false);
|
|
1459
1504
|
const [sqlError, setSqlError] = react.useState(null);
|
|
@@ -1568,12 +1613,13 @@ function ChartBuilder({ records, columns, stateId, onSave }) {
|
|
|
1568
1613
|
}
|
|
1569
1614
|
}
|
|
1570
1615
|
config.style = style;
|
|
1616
|
+
config.fill = fillContainer;
|
|
1571
1617
|
onSave({
|
|
1572
1618
|
title: title || `${chartType} chart`,
|
|
1573
1619
|
type: chartType === "grouped-bar" ? "bar" : chartType,
|
|
1574
1620
|
config,
|
|
1575
|
-
width: 6,
|
|
1576
|
-
height: 2
|
|
1621
|
+
width: fillContainer ? 12 : 6,
|
|
1622
|
+
height: fillContainer ? 8 : 2
|
|
1577
1623
|
});
|
|
1578
1624
|
};
|
|
1579
1625
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-full", children: [
|
|
@@ -1729,6 +1775,10 @@ function ChartBuilder({ records, columns, stateId, onSave }) {
|
|
|
1729
1775
|
className: "w-full mb-2 px-2 py-1 text-xs font-mono bg-midnight-surface border border-midnight-border text-midnight-text-body outline-none focus:border-midnight-accent transition-colors"
|
|
1730
1776
|
}
|
|
1731
1777
|
),
|
|
1778
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-2", children: [
|
|
1779
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-mono text-midnight-text-muted", title: "Span the full dashboard width and height", children: "Maximize size" }),
|
|
1780
|
+
/* @__PURE__ */ jsxRuntime.jsx(terminalUxComponents.TerminalToggle, { size: "small", checked: fillContainer, onChange: setFillContainer })
|
|
1781
|
+
] }),
|
|
1732
1782
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1733
1783
|
"button",
|
|
1734
1784
|
{
|
|
@@ -1825,14 +1875,26 @@ function panelSql(type, config) {
|
|
|
1825
1875
|
if (SQL_CHART_TYPES.has(type)) return buildChartSQL(panelToChartConfig(type, config)) || "";
|
|
1826
1876
|
return "";
|
|
1827
1877
|
}
|
|
1828
|
-
function
|
|
1878
|
+
function formatCell(value, s) {
|
|
1879
|
+
if (value == null) return "";
|
|
1880
|
+
if (typeof value === "object") return JSON.stringify(value);
|
|
1881
|
+
let str;
|
|
1882
|
+
const n = typeof value === "number" ? value : Number(value);
|
|
1883
|
+
if (value !== "" && typeof value !== "boolean" && Number.isFinite(n)) {
|
|
1884
|
+
str = Number.isInteger(n) ? n.toLocaleString() : n.toLocaleString(void 0, { minimumFractionDigits: s.cellPrecision, maximumFractionDigits: s.cellPrecision });
|
|
1885
|
+
} else {
|
|
1886
|
+
str = String(value);
|
|
1887
|
+
}
|
|
1888
|
+
if (s.cellTruncate > 0 && str.length > s.cellTruncate) str = `${str.slice(0, s.cellTruncate)}\u2026`;
|
|
1889
|
+
return str;
|
|
1890
|
+
}
|
|
1891
|
+
function DataTable({ rows, columns, style }) {
|
|
1892
|
+
const s = withStyleDefaults(style);
|
|
1829
1893
|
const colNames = columns?.length ? columns : rows[0] ? Object.keys(rows[0]) : [];
|
|
1894
|
+
const cellCls = s.cellWrap ? "px-2 py-1 text-midnight-text-body align-top whitespace-normal break-words max-w-[280px]" : "px-2 py-1 text-midnight-text-body truncate max-w-[200px]";
|
|
1830
1895
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-auto h-full text-xs", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "w-full", children: [
|
|
1831
1896
|
/* @__PURE__ */ jsxRuntime.jsx("thead", { className: "sticky top-0 bg-midnight-elevated", children: /* @__PURE__ */ jsxRuntime.jsx("tr", { children: colNames.map((n) => /* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-2 py-1 text-left text-midnight-text-muted font-mono border-b border-midnight-border", children: n }, n)) }) }),
|
|
1832
|
-
/* @__PURE__ */ jsxRuntime.jsx("tbody", { children: rows.map((r, i) => /* @__PURE__ */ jsxRuntime.jsx("tr", { className: "border-b border-dashed border-midnight-border hover:bg-midnight-raised", children: colNames.map((n) => {
|
|
1833
|
-
const cellValue = r[n];
|
|
1834
|
-
return /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-2 py-1 text-midnight-text-body truncate max-w-[200px]", children: cellValue == null ? "" : typeof cellValue === "object" ? JSON.stringify(cellValue) : String(cellValue) }, n);
|
|
1835
|
-
}) }, i)) })
|
|
1897
|
+
/* @__PURE__ */ jsxRuntime.jsx("tbody", { children: rows.map((r, i) => /* @__PURE__ */ jsxRuntime.jsx("tr", { className: "border-b border-dashed border-midnight-border hover:bg-midnight-raised", children: colNames.map((n) => /* @__PURE__ */ jsxRuntime.jsx("td", { className: cellCls, children: formatCell(r[n], s) }, n)) }, i)) })
|
|
1836
1898
|
] }) });
|
|
1837
1899
|
}
|
|
1838
1900
|
function SqlPanel({ panel }) {
|
|
@@ -1927,7 +1989,7 @@ function SqlPanel({ panel }) {
|
|
|
1927
1989
|
const v = first ? Object.values(first)[0] : 0;
|
|
1928
1990
|
chart = /* @__PURE__ */ jsxRuntime.jsx(MetricView, { value: Number(v) || 0, config: { column: config.column || "", agg: config.agg, label: config.label } });
|
|
1929
1991
|
} else if (chartType === "table") {
|
|
1930
|
-
chart = /* @__PURE__ */ jsxRuntime.jsx(DataTable, { rows, columns: config.columns });
|
|
1992
|
+
chart = /* @__PURE__ */ jsxRuntime.jsx(DataTable, { rows, columns: config.columns, style: config.style });
|
|
1931
1993
|
} else {
|
|
1932
1994
|
const shaped = shapeChartData(chartType, rows, { yFields: config.yFields || [] });
|
|
1933
1995
|
switch (chartType) {
|
|
@@ -1991,7 +2053,7 @@ function PanelContent({ panel, records, columns }) {
|
|
|
1991
2053
|
case "insight":
|
|
1992
2054
|
return /* @__PURE__ */ jsxRuntime.jsx(InsightView, { config: { text: config.text } });
|
|
1993
2055
|
case "table":
|
|
1994
|
-
return /* @__PURE__ */ jsxRuntime.jsx(DataTable, { rows: (records || []).slice(0, 50), columns: config.columns || columns?.map((c) => c.name) });
|
|
2056
|
+
return /* @__PURE__ */ jsxRuntime.jsx(DataTable, { rows: (records || []).slice(0, 50), columns: config.columns || columns?.map((c) => c.name), style: config.style });
|
|
1995
2057
|
default:
|
|
1996
2058
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4 text-xs text-midnight-text-muted", children: [
|
|
1997
2059
|
"Unknown panel type: ",
|
|
@@ -1999,57 +2061,121 @@ function PanelContent({ panel, records, columns }) {
|
|
|
1999
2061
|
] });
|
|
2000
2062
|
}
|
|
2001
2063
|
}
|
|
2002
|
-
var
|
|
2064
|
+
var GRID_COLS = 12;
|
|
2065
|
+
var ROW_HEIGHT = 80;
|
|
2066
|
+
var GRID_MARGIN = 10;
|
|
2067
|
+
var DRAG_HANDLE = "panel-drag-handle";
|
|
2068
|
+
var RGL_CSS = `
|
|
2069
|
+
.react-grid-layout { position: relative; transition: height 200ms ease; }
|
|
2070
|
+
.react-grid-item { transition: all 200ms ease; transition-property: left, top, width, height; box-sizing: border-box; }
|
|
2071
|
+
.react-grid-item.cssTransforms { transition-property: transform, width, height; }
|
|
2072
|
+
.react-grid-item.resizing { transition: none; z-index: 3; will-change: width, height; }
|
|
2073
|
+
.react-grid-item.react-draggable-dragging { transition: none; z-index: 3; will-change: transform; }
|
|
2074
|
+
.react-grid-item.react-grid-placeholder { background: rgba(99,102,241,0.18); border: 1px dashed #6366f1; border-radius: 2px; transition-duration: 100ms; z-index: 2; user-select: none; }
|
|
2075
|
+
.react-grid-item > .react-resizable-handle { position: absolute; width: 18px; height: 18px; bottom: 0; right: 0; cursor: se-resize; }
|
|
2076
|
+
.react-grid-item > .react-resizable-handle::after { content: ''; position: absolute; right: 4px; bottom: 4px; width: 6px; height: 6px; border-right: 2px solid rgba(148,163,184,0.7); border-bottom: 2px solid rgba(148,163,184,0.7); }
|
|
2077
|
+
.${DRAG_HANDLE} { cursor: grab; }
|
|
2078
|
+
.react-grid-item.react-draggable-dragging .${DRAG_HANDLE} { cursor: grabbing; }
|
|
2079
|
+
`;
|
|
2080
|
+
var rglCssInjected = false;
|
|
2081
|
+
function useInjectRglCss() {
|
|
2082
|
+
react.useEffect(() => {
|
|
2083
|
+
if (rglCssInjected || typeof document === "undefined") return;
|
|
2084
|
+
const el = document.createElement("style");
|
|
2085
|
+
el.setAttribute("data-rgl", "dashboard-renderer");
|
|
2086
|
+
el.textContent = RGL_CSS;
|
|
2087
|
+
document.head.appendChild(el);
|
|
2088
|
+
rglCssInjected = true;
|
|
2089
|
+
}, []);
|
|
2090
|
+
}
|
|
2091
|
+
var GridLayoutWithWidth = GridLayout.WidthProvider(GridLayout__default.default);
|
|
2092
|
+
function buildLayout(panels) {
|
|
2093
|
+
let cx = 0, cy = 0, rowH = 0;
|
|
2094
|
+
return panels.map((p) => {
|
|
2095
|
+
const fill = !!p.config?.fill;
|
|
2096
|
+
const w = fill ? GRID_COLS : Math.min(Math.max(p.width || 6, 1), GRID_COLS);
|
|
2097
|
+
const h = fill ? Math.max(p.height || 8, 6) : Math.max(p.height || 4, 1);
|
|
2098
|
+
if (typeof p.x === "number" && typeof p.y === "number") {
|
|
2099
|
+
return { i: p.id, x: fill ? 0 : p.x, y: p.y, w, h, minW: 2, minH: 2 };
|
|
2100
|
+
}
|
|
2101
|
+
if (cx + w > GRID_COLS) {
|
|
2102
|
+
cx = 0;
|
|
2103
|
+
cy += rowH;
|
|
2104
|
+
rowH = 0;
|
|
2105
|
+
}
|
|
2106
|
+
const item = { i: p.id, x: cx, y: cy, w, h, minW: 2, minH: 2 };
|
|
2107
|
+
cx += w;
|
|
2108
|
+
rowH = Math.max(rowH, h);
|
|
2109
|
+
return item;
|
|
2110
|
+
});
|
|
2111
|
+
}
|
|
2003
2112
|
function DashboardRenderer({ dashboard, records, columns }) {
|
|
2004
|
-
const { theme, removePanel } = useDashboard();
|
|
2005
|
-
const { canEditPanels } = useCapabilities();
|
|
2113
|
+
const { theme, removePanel, persistLayout } = useDashboard();
|
|
2114
|
+
const { canEditPanels, canEditLayout } = useCapabilities();
|
|
2115
|
+
useInjectRglCss();
|
|
2116
|
+
const panels = dashboard?.panels ?? [];
|
|
2117
|
+
const layout = react.useMemo(() => buildLayout(panels), [panels]);
|
|
2118
|
+
const onLayoutChange = react.useCallback((next) => {
|
|
2119
|
+
if (!persistLayout) return;
|
|
2120
|
+
persistLayout(next.map((l) => ({ id: l.i, x: l.x, y: l.y, w: l.w, h: l.h })));
|
|
2121
|
+
}, [persistLayout]);
|
|
2006
2122
|
if (!dashboard) return null;
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2010
|
-
|
|
2011
|
-
] }),
|
|
2012
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-12 gap-3", children: dashboard.panels?.map((panel) => {
|
|
2013
|
-
const colSpan = Math.min(Math.max(panel.width || 6, 1), 12);
|
|
2014
|
-
const rowSpan = Math.min(Math.max(panel.height || 2, 1), 4);
|
|
2015
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2016
|
-
"div",
|
|
2123
|
+
const panelFrame = (panel, draggable) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `border ${theme.border} bg-midnight-surface flex flex-col overflow-hidden h-full`, children: [
|
|
2124
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center justify-between px-3 py-1.5 border-b ${theme.border} bg-midnight-elevated ${draggable ? DRAG_HANDLE : ""}`, children: [
|
|
2125
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2126
|
+
"span",
|
|
2017
2127
|
{
|
|
2018
|
-
className:
|
|
2128
|
+
className: "flex-1 text-xs font-mono text-midnight-text-body truncate px-1",
|
|
2019
2129
|
style: {
|
|
2020
|
-
|
|
2021
|
-
|
|
2130
|
+
textAlign: panel.config?.style?.titleAlign || "left",
|
|
2131
|
+
fontWeight: panel.config?.style?.titleBold ? 700 : void 0,
|
|
2132
|
+
background: panel.config?.style?.titleBackground || void 0,
|
|
2133
|
+
color: panel.config?.style?.titleColor || void 0
|
|
2022
2134
|
},
|
|
2023
|
-
children:
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2135
|
+
children: panel.title || panel.type
|
|
2136
|
+
}
|
|
2137
|
+
),
|
|
2138
|
+
canEditPanels && removePanel && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2139
|
+
"button",
|
|
2140
|
+
{
|
|
2141
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
2142
|
+
onClick: () => removePanel(panel.id),
|
|
2143
|
+
className: "p-0.5 hover:bg-midnight-raised text-midnight-text-muted hover:text-midnight-text-body transition-colors",
|
|
2144
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-3 h-3" })
|
|
2145
|
+
}
|
|
2146
|
+
)
|
|
2147
|
+
] }),
|
|
2148
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-0", children: /* @__PURE__ */ jsxRuntime.jsx(react.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx(ViewLoading, {}), children: /* @__PURE__ */ jsxRuntime.jsx(PanelContent, { panel, records, columns }) }) })
|
|
2149
|
+
] }, panel.id);
|
|
2150
|
+
const insightsBanner = dashboard.insights ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `border ${theme.border} bg-midnight-elevated px-4 py-3 flex items-start gap-3`, children: [
|
|
2151
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Sparkles, { className: "w-5 h-5 text-midnight-accent shrink-0 mt-0.5" }),
|
|
2152
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: `text-sm ${theme.text} leading-relaxed`, children: dashboard.insights })
|
|
2153
|
+
] }) : null;
|
|
2154
|
+
if (panels.length === 1) {
|
|
2155
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4 p-2 h-full min-h-[60vh]", children: [
|
|
2156
|
+
insightsBanner,
|
|
2157
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-h-0", children: panelFrame(panels[0], false) })
|
|
2158
|
+
] });
|
|
2159
|
+
}
|
|
2160
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4 p-2", children: [
|
|
2161
|
+
insightsBanner,
|
|
2162
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2163
|
+
GridLayoutWithWidth,
|
|
2164
|
+
{
|
|
2165
|
+
className: "layout",
|
|
2166
|
+
layout,
|
|
2167
|
+
cols: GRID_COLS,
|
|
2168
|
+
rowHeight: ROW_HEIGHT,
|
|
2169
|
+
margin: [GRID_MARGIN, GRID_MARGIN],
|
|
2170
|
+
isDraggable: canEditLayout,
|
|
2171
|
+
isResizable: canEditLayout,
|
|
2172
|
+
draggableHandle: `.${DRAG_HANDLE}`,
|
|
2173
|
+
onDragStop: onLayoutChange,
|
|
2174
|
+
onResizeStop: onLayoutChange,
|
|
2175
|
+
compactType: canEditLayout ? "vertical" : null,
|
|
2176
|
+
children: panels.map((panel) => panelFrame(panel, canEditLayout))
|
|
2177
|
+
}
|
|
2178
|
+
)
|
|
2053
2179
|
] });
|
|
2054
2180
|
}
|
|
2055
2181
|
function ProfileSummary({ profile, theme }) {
|