@quantumwake/terminal-ux-dashboard-components 0.1.12 → 0.1.13
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 +167 -41
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -5
- package/dist/index.d.ts +17 -5
- package/dist/index.js +167 -41
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -142,8 +142,10 @@ var buildChartSQL = ({
|
|
|
142
142
|
}
|
|
143
143
|
case "scatter": {
|
|
144
144
|
if (!x || !y) return null;
|
|
145
|
-
const
|
|
146
|
-
|
|
145
|
+
const nx = `TRY_CAST(${qIdent(x)} AS DOUBLE)`, ny = `TRY_CAST(${qIdent(y)} AS DOUBLE)`;
|
|
146
|
+
const extra = `${nx} IS NOT NULL AND ${ny} IS NOT NULL`;
|
|
147
|
+
const w = where ? `${where} AND ${extra}` : ` WHERE ${extra}`;
|
|
148
|
+
return `SELECT ${nx} AS x, ${ny} AS y FROM ${TABLE}${w} LIMIT ${MAX_POINTS}`;
|
|
147
149
|
}
|
|
148
150
|
case "heatmap": {
|
|
149
151
|
if (!x || !y) return null;
|
|
@@ -175,7 +177,10 @@ var shapeChartData = (chartType, rows, { yFields = [] } = {}) => {
|
|
|
175
177
|
case "line":
|
|
176
178
|
return [{ id: "series", data: rows.map((r) => ({ x: String(r.x), y: Number(r.y) || 0 })) }];
|
|
177
179
|
case "scatter":
|
|
178
|
-
return [{
|
|
180
|
+
return [{
|
|
181
|
+
id: "points",
|
|
182
|
+
data: rows.map((r) => ({ x: Number(r.x), y: Number(r.y) })).filter((p) => Number.isFinite(p.x) && Number.isFinite(p.y))
|
|
183
|
+
}];
|
|
179
184
|
case "heatmap": {
|
|
180
185
|
const rowKeys = [];
|
|
181
186
|
const colKeys = [];
|
|
@@ -217,10 +222,24 @@ var DEFAULT_CHART_STYLE = {
|
|
|
217
222
|
xLegendOffset: 46,
|
|
218
223
|
yLegendPosition: "middle",
|
|
219
224
|
yLegendOffset: -50,
|
|
225
|
+
// Custom axis title text + visibility.
|
|
226
|
+
xAxisLabel: "",
|
|
227
|
+
yAxisLabel: "",
|
|
228
|
+
showXLegend: true,
|
|
229
|
+
showYLegend: true,
|
|
230
|
+
// Axis title emphasis.
|
|
231
|
+
legendBold: false,
|
|
232
|
+
legendHighlight: "",
|
|
233
|
+
// Tick overflow handling.
|
|
234
|
+
xTickRotation: -35,
|
|
235
|
+
tickTruncate: 0,
|
|
220
236
|
// Series legend placement (charts that have one: pie, grouped bar).
|
|
221
237
|
legendAnchor: "right",
|
|
222
|
-
// Panel title
|
|
223
|
-
titleAlign: "left"
|
|
238
|
+
// Panel title (rendered by DashboardRenderer's panel header).
|
|
239
|
+
titleAlign: "left",
|
|
240
|
+
titleBold: false,
|
|
241
|
+
titleBackground: "",
|
|
242
|
+
titleColor: ""
|
|
224
243
|
};
|
|
225
244
|
var LEGEND_ANCHORS = ["right", "top-left", "top-right", "bottom-left", "bottom-right", "none"];
|
|
226
245
|
var withStyleDefaults = (style) => ({
|
|
@@ -234,7 +253,17 @@ var buildNivoTheme = (style) => {
|
|
|
234
253
|
text: { fill: s.textColor, fontSize: s.fontSize },
|
|
235
254
|
axis: {
|
|
236
255
|
ticks: { text: { fill: s.textColor, fontSize: s.fontSize } },
|
|
237
|
-
legend: {
|
|
256
|
+
legend: {
|
|
257
|
+
text: {
|
|
258
|
+
fill: s.legendColor,
|
|
259
|
+
fontSize: s.fontSize + 2,
|
|
260
|
+
fontWeight: s.legendBold ? 700 : 400,
|
|
261
|
+
// A highlight is drawn as a text outline (halo) — the clean,
|
|
262
|
+
// SVG-native way to make an axis title stand out.
|
|
263
|
+
outlineWidth: s.legendHighlight ? 3 : 0,
|
|
264
|
+
outlineColor: s.legendHighlight || "transparent"
|
|
265
|
+
}
|
|
266
|
+
}
|
|
238
267
|
},
|
|
239
268
|
grid: { line: { stroke: s.gridColor } },
|
|
240
269
|
crosshair: { line: { stroke: s.textColor, strokeDasharray: "6 6" } },
|
|
@@ -263,14 +292,25 @@ var legendConfig = (style) => {
|
|
|
263
292
|
const ty = s.legendAnchor.includes("top") ? 10 : -10;
|
|
264
293
|
return { ...base, translateX: tx, translateY: ty };
|
|
265
294
|
};
|
|
266
|
-
var
|
|
295
|
+
var truncate = (v, n) => {
|
|
296
|
+
const str = String(v);
|
|
297
|
+
return n > 0 && str.length > n ? `${str.slice(0, n)}\u2026` : str;
|
|
298
|
+
};
|
|
299
|
+
var axisLegend = (style, axis, columnName, opts2 = {}) => {
|
|
267
300
|
const s = withStyleDefaults(style);
|
|
268
301
|
const isX = axis === "x";
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
302
|
+
const show = isX ? s.showXLegend : s.showYLegend;
|
|
303
|
+
const label = (isX ? s.xAxisLabel : s.yAxisLabel) || columnName;
|
|
304
|
+
const out = {};
|
|
305
|
+
if (show && label) {
|
|
306
|
+
out.legend = label;
|
|
307
|
+
out.legendPosition = isX ? s.xLegendPosition : s.yLegendPosition;
|
|
308
|
+
out.legendOffset = isX ? s.xLegendOffset : s.yLegendOffset;
|
|
309
|
+
}
|
|
310
|
+
if (isX) out.tickRotation = s.xTickRotation;
|
|
311
|
+
if (opts2.numeric) out.format = (v) => Number(v).toLocaleString();
|
|
312
|
+
else if (s.tickTruncate > 0) out.format = (v) => truncate(v, s.tickTruncate);
|
|
313
|
+
return out;
|
|
274
314
|
};
|
|
275
315
|
|
|
276
316
|
// src/dataShape.ts
|
|
@@ -334,8 +374,8 @@ function BarView({ records, groupColumn, valueColumn, aggFn = "count", data: pre
|
|
|
334
374
|
padding: 0.3,
|
|
335
375
|
colors: ["rgba(74, 222, 128, 0.8)"],
|
|
336
376
|
borderColor: { from: "color", modifiers: [["darker", 1.6]] },
|
|
337
|
-
axisBottom: { tickSize: 5, tickPadding: 5,
|
|
338
|
-
axisLeft: { tickSize: 5, tickPadding: 5,
|
|
377
|
+
axisBottom: { tickSize: 5, tickPadding: 5, ...axisLegend(style, "x", groupColumn) },
|
|
378
|
+
axisLeft: { tickSize: 5, tickPadding: 5, ...axisLegend(style, "y", valueColumn, { numeric: true }) },
|
|
339
379
|
labelSkipWidth: 12,
|
|
340
380
|
labelSkipHeight: 12,
|
|
341
381
|
labelTextColor: { from: "color", modifiers: [["darker", 3]] },
|
|
@@ -408,8 +448,8 @@ function LineView({ records, xColumn, yColumn, data: presetData, style }) {
|
|
|
408
448
|
pointBorderWidth: 2,
|
|
409
449
|
pointBorderColor: { from: "serieColor" },
|
|
410
450
|
enableGridX: false,
|
|
411
|
-
axisBottom: { tickSize: 5, tickPadding: 5,
|
|
412
|
-
axisLeft: { tickSize: 5, tickPadding: 5, ...axisLegend(style, "y", yColumn) },
|
|
451
|
+
axisBottom: { tickSize: 5, tickPadding: 5, ...axisLegend(style, "x", xColumn) },
|
|
452
|
+
axisLeft: { tickSize: 5, tickPadding: 5, ...axisLegend(style, "y", yColumn, { numeric: true }) },
|
|
413
453
|
useMesh: true,
|
|
414
454
|
theme: buildNivoTheme(style)
|
|
415
455
|
}
|
|
@@ -418,7 +458,7 @@ function LineView({ records, xColumn, yColumn, data: presetData, style }) {
|
|
|
418
458
|
function ScatterView({ records, xColumn, yColumn, data: presetData, style }) {
|
|
419
459
|
const computed = react.useMemo(() => {
|
|
420
460
|
if (presetData || !records) return [];
|
|
421
|
-
const points = records.
|
|
461
|
+
const points = records.map((r) => ({ x: Number(r[xColumn]), y: Number(r[yColumn]) })).filter((p) => Number.isFinite(p.x) && Number.isFinite(p.y)).slice(0, 1e3);
|
|
422
462
|
return [{ id: `${xColumn} vs ${yColumn}`, data: points }];
|
|
423
463
|
}, [records, xColumn, yColumn, presetData]);
|
|
424
464
|
const data = presetData || computed;
|
|
@@ -431,8 +471,8 @@ function ScatterView({ records, xColumn, yColumn, data: presetData, style }) {
|
|
|
431
471
|
yScale: { type: "linear", min: "auto", max: "auto" },
|
|
432
472
|
colors: ["rgba(167, 139, 250, 0.7)"],
|
|
433
473
|
nodeSize: 6,
|
|
434
|
-
axisBottom: { tickSize: 5, tickPadding: 5, ...axisLegend(style, "x", xColumn) },
|
|
435
|
-
axisLeft: { tickSize: 5, tickPadding: 5, ...axisLegend(style, "y", yColumn) },
|
|
474
|
+
axisBottom: { tickSize: 5, tickPadding: 5, ...axisLegend(style, "x", xColumn, { numeric: true }) },
|
|
475
|
+
axisLeft: { tickSize: 5, tickPadding: 5, ...axisLegend(style, "y", yColumn, { numeric: true }) },
|
|
436
476
|
useMesh: true,
|
|
437
477
|
theme: buildNivoTheme(style)
|
|
438
478
|
}
|
|
@@ -531,8 +571,8 @@ function HeatmapView({
|
|
|
531
571
|
{
|
|
532
572
|
data,
|
|
533
573
|
margin,
|
|
534
|
-
axisTop: { tickSize: 5, tickPadding: 5, tickRotation:
|
|
535
|
-
axisLeft: { tickSize: 5, tickPadding: 5, legend: rowColumn, legendPosition: s.yLegendPosition, legendOffset: -80 },
|
|
574
|
+
axisTop: { tickSize: 5, tickPadding: 5, tickRotation: s.xTickRotation, legend: s.showXLegend ? s.xAxisLabel || colColumn : "", legendPosition: s.xLegendPosition, legendOffset: -50 },
|
|
575
|
+
axisLeft: { tickSize: 5, tickPadding: 5, legend: s.showYLegend ? s.yAxisLabel || rowColumn : "", legendPosition: s.yLegendPosition, legendOffset: -80 },
|
|
536
576
|
axisRight: showTotals ? { tickSize: 5, tickPadding: 5, format: (id) => fmtNum(rowTotals[id]), legend: `${marginAgg} \u25B8`, legendOffset: 60 } : null,
|
|
537
577
|
axisBottom: showTotals ? { tickSize: 5, tickPadding: 5, tickRotation: -35, format: (id) => fmtNum(colTotals[id]) } : null,
|
|
538
578
|
colors: { type: "sequential", scheme: "blue_green", minValue: 0 },
|
|
@@ -609,6 +649,37 @@ function NumberControl({ value, onChange, min, max }) {
|
|
|
609
649
|
}
|
|
610
650
|
);
|
|
611
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"
|
|
673
|
+
}
|
|
674
|
+
);
|
|
675
|
+
}
|
|
676
|
+
function OptionalColor({ value, onChange, fallback = "#a78bfa" }) {
|
|
677
|
+
const on = !!value;
|
|
678
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
679
|
+
/* @__PURE__ */ jsxRuntime.jsx(Toggle, { value: on, onChange: (v) => onChange(v ? fallback : "") }),
|
|
680
|
+
on && /* @__PURE__ */ jsxRuntime.jsx(ColorControl, { value, onChange })
|
|
681
|
+
] });
|
|
682
|
+
}
|
|
612
683
|
function Choice({ value, options, onChange }) {
|
|
613
684
|
const { theme } = useDashboard();
|
|
614
685
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-28", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -632,19 +703,32 @@ function ChartStyleControls({ style, onChange }) {
|
|
|
632
703
|
/* @__PURE__ */ jsxRuntime.jsxs(Group, { children: [
|
|
633
704
|
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Background", children: /* @__PURE__ */ jsxRuntime.jsx(ColorControl, { value: s.background, onChange: (v) => set("background", v) }) }),
|
|
634
705
|
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Text color", children: /* @__PURE__ */ jsxRuntime.jsx(ColorControl, { value: s.textColor, onChange: (v) => set("textColor", v) }) }),
|
|
635
|
-
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Title color", children: /* @__PURE__ */ jsxRuntime.jsx(ColorControl, { value: s.legendColor, onChange: (v) => set("legendColor", v) }) }),
|
|
636
706
|
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Grid color", children: /* @__PURE__ */ jsxRuntime.jsx(ColorControl, { value: s.gridColor, onChange: (v) => set("gridColor", v) }) }),
|
|
637
707
|
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Font size", children: /* @__PURE__ */ jsxRuntime.jsx(NumberControl, { value: s.fontSize, min: 6, max: 24, onChange: (v) => set("fontSize", v) }) })
|
|
638
708
|
] }),
|
|
639
709
|
/* @__PURE__ */ jsxRuntime.jsxs(Group, { children: [
|
|
710
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "X axis title", children: /* @__PURE__ */ jsxRuntime.jsx(TextControl, { value: s.xAxisLabel, placeholder: "(column)", onChange: (v) => set("xAxisLabel", v) }) }),
|
|
711
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Show X title", children: /* @__PURE__ */ jsxRuntime.jsx(Toggle, { value: s.showXLegend, onChange: (v) => set("showXLegend", v) }) }),
|
|
712
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Y axis title", children: /* @__PURE__ */ jsxRuntime.jsx(TextControl, { value: s.yAxisLabel, placeholder: "(column)", onChange: (v) => set("yAxisLabel", v) }) }),
|
|
713
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Show Y title", children: /* @__PURE__ */ jsxRuntime.jsx(Toggle, { value: s.showYLegend, onChange: (v) => set("showYLegend", v) }) }),
|
|
714
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Title color", children: /* @__PURE__ */ jsxRuntime.jsx(ColorControl, { value: s.legendColor, onChange: (v) => set("legendColor", v) }) }),
|
|
715
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Title bold", children: /* @__PURE__ */ jsxRuntime.jsx(Toggle, { value: s.legendBold, onChange: (v) => set("legendBold", v) }) }),
|
|
716
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Title highlight", children: /* @__PURE__ */ jsxRuntime.jsx(OptionalColor, { value: s.legendHighlight, onChange: (v) => set("legendHighlight", v) }) }),
|
|
640
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) }) }),
|
|
641
718
|
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "X title offset", children: /* @__PURE__ */ jsxRuntime.jsx(NumberControl, { value: s.xLegendOffset, onChange: (v) => set("xLegendOffset", v) }) }),
|
|
642
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) }) }),
|
|
643
720
|
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Y title offset", children: /* @__PURE__ */ jsxRuntime.jsx(NumberControl, { value: s.yLegendOffset, onChange: (v) => set("yLegendOffset", v) }) })
|
|
644
721
|
] }),
|
|
722
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Group, { children: [
|
|
723
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "X tick angle", children: /* @__PURE__ */ jsxRuntime.jsx(NumberControl, { value: s.xTickRotation, min: -90, max: 90, onChange: (v) => set("xTickRotation", v) }) }),
|
|
724
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Truncate ticks", children: /* @__PURE__ */ jsxRuntime.jsx(NumberControl, { value: s.tickTruncate, min: 0, max: 40, onChange: (v) => set("tickTruncate", v) }) })
|
|
725
|
+
] }),
|
|
645
726
|
/* @__PURE__ */ jsxRuntime.jsxs(Group, { last: true, children: [
|
|
646
|
-
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "
|
|
647
|
-
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "
|
|
727
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Series legend", children: /* @__PURE__ */ jsxRuntime.jsx(Choice, { value: s.legendAnchor, options: ["right", "top-left", "top-right", "bottom-left", "bottom-right", "none"], onChange: (v) => set("legendAnchor", v) }) }),
|
|
728
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Panel title align", children: /* @__PURE__ */ jsxRuntime.jsx(Choice, { value: s.titleAlign, options: ["left", "center", "right"], onChange: (v) => set("titleAlign", v) }) }),
|
|
729
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Panel title bold", children: /* @__PURE__ */ jsxRuntime.jsx(Toggle, { value: s.titleBold, onChange: (v) => set("titleBold", v) }) }),
|
|
730
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Panel title color", children: /* @__PURE__ */ jsxRuntime.jsx(OptionalColor, { value: s.titleColor, onChange: (v) => set("titleColor", v), fallback: "#e2e8f0" }) }),
|
|
731
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Panel title bg", children: /* @__PURE__ */ jsxRuntime.jsx(OptionalColor, { value: s.titleBackground, onChange: (v) => set("titleBackground", v), fallback: "#1e293b" }) })
|
|
648
732
|
] })
|
|
649
733
|
] });
|
|
650
734
|
}
|
|
@@ -1369,6 +1453,7 @@ function ChartBuilder({ records, columns, stateId, onSave }) {
|
|
|
1369
1453
|
const [showTotals, setShowTotals] = react.useState(false);
|
|
1370
1454
|
const [style, setStyle] = react.useState(DEFAULT_CHART_STYLE);
|
|
1371
1455
|
const [showStyle, setShowStyle] = react.useState(false);
|
|
1456
|
+
const [showFields, setShowFields] = react.useState(false);
|
|
1372
1457
|
const [sqlRows, setSqlRows] = react.useState(null);
|
|
1373
1458
|
const [sqlLoading, setSqlLoading] = react.useState(false);
|
|
1374
1459
|
const [sqlError, setSqlError] = react.useState(null);
|
|
@@ -1654,8 +1739,23 @@ function ChartBuilder({ records, columns, stateId, onSave }) {
|
|
|
1654
1739
|
)
|
|
1655
1740
|
] }),
|
|
1656
1741
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pt-4 border-t border-midnight-border", children: [
|
|
1657
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1658
|
-
|
|
1742
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1743
|
+
"button",
|
|
1744
|
+
{
|
|
1745
|
+
onClick: () => setShowFields((s) => !s),
|
|
1746
|
+
className: "flex items-center gap-1 text-xs uppercase text-midnight-text-muted font-mono hover:text-midnight-text-body transition-colors",
|
|
1747
|
+
children: [
|
|
1748
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: `w-3 h-3 transition-transform ${showFields ? "" : "-rotate-90"}` }),
|
|
1749
|
+
"Available Fields",
|
|
1750
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-midnight-text-muted/60 normal-case", children: [
|
|
1751
|
+
"(",
|
|
1752
|
+
columns.length,
|
|
1753
|
+
")"
|
|
1754
|
+
] })
|
|
1755
|
+
]
|
|
1756
|
+
}
|
|
1757
|
+
),
|
|
1758
|
+
showFields && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-1 mt-2", children: columns.map((col) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-xs font-mono px-1 py-0.5", children: [
|
|
1659
1759
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-midnight-text-body", children: col.name }),
|
|
1660
1760
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-midnight-text-muted", children: col.type })
|
|
1661
1761
|
] }, col.name)) })
|
|
@@ -1685,6 +1785,15 @@ function ViewLoading() {
|
|
|
1685
1785
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center h-full text-xs text-midnight-text-muted", children: "Loading..." });
|
|
1686
1786
|
}
|
|
1687
1787
|
var SQL_CHART_TYPES = /* @__PURE__ */ new Set(["bar", "grouped-bar", "pie", "line", "scatter", "heatmap"]);
|
|
1788
|
+
var SQL_DATA_TYPES = /* @__PURE__ */ new Set(["table", "metric"]);
|
|
1789
|
+
var TABLE_PREVIEW_ROWS = 200;
|
|
1790
|
+
function tableSql(config) {
|
|
1791
|
+
const cols = config.columns?.length ? config.columns.map(qIdent).join(", ") : "*";
|
|
1792
|
+
return `SELECT ${cols} FROM data LIMIT ${TABLE_PREVIEW_ROWS}`;
|
|
1793
|
+
}
|
|
1794
|
+
function metricSql(config) {
|
|
1795
|
+
return `SELECT ${aggExpr(config.agg, config.column)} AS v FROM data`;
|
|
1796
|
+
}
|
|
1688
1797
|
function panelToChartConfig(chartType, config) {
|
|
1689
1798
|
const cfg = { chartType, xFields: [], yFields: [], filters: config.filters || [] };
|
|
1690
1799
|
switch (chartType) {
|
|
@@ -1709,11 +1818,23 @@ function panelToChartConfig(chartType, config) {
|
|
|
1709
1818
|
}
|
|
1710
1819
|
return cfg;
|
|
1711
1820
|
}
|
|
1712
|
-
function panelSql(
|
|
1821
|
+
function panelSql(type, config) {
|
|
1713
1822
|
if (config.sql) return config.sql;
|
|
1714
|
-
if (
|
|
1823
|
+
if (type === "table") return tableSql(config);
|
|
1824
|
+
if (type === "metric") return metricSql(config);
|
|
1825
|
+
if (SQL_CHART_TYPES.has(type)) return buildChartSQL(panelToChartConfig(type, config)) || "";
|
|
1715
1826
|
return "";
|
|
1716
1827
|
}
|
|
1828
|
+
function DataTable({ rows, columns }) {
|
|
1829
|
+
const colNames = columns?.length ? columns : rows[0] ? Object.keys(rows[0]) : [];
|
|
1830
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-auto h-full text-xs", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "w-full", children: [
|
|
1831
|
+
/* @__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)) })
|
|
1836
|
+
] }) });
|
|
1837
|
+
}
|
|
1717
1838
|
function SqlPanel({ panel }) {
|
|
1718
1839
|
const { runQuery, persistPanelData } = useDashboard();
|
|
1719
1840
|
const { canRefresh } = useCapabilities();
|
|
@@ -1805,6 +1926,8 @@ function SqlPanel({ panel }) {
|
|
|
1805
1926
|
const first = rows[0];
|
|
1806
1927
|
const v = first ? Object.values(first)[0] : 0;
|
|
1807
1928
|
chart = /* @__PURE__ */ jsxRuntime.jsx(MetricView, { value: Number(v) || 0, config: { column: config.column || "", agg: config.agg, label: config.label } });
|
|
1929
|
+
} else if (chartType === "table") {
|
|
1930
|
+
chart = /* @__PURE__ */ jsxRuntime.jsx(DataTable, { rows, columns: config.columns });
|
|
1808
1931
|
} else {
|
|
1809
1932
|
const shaped = shapeChartData(chartType, rows, { yFields: config.yFields || [] });
|
|
1810
1933
|
switch (chartType) {
|
|
@@ -1844,7 +1967,7 @@ function PanelContent({ panel, records, columns }) {
|
|
|
1844
1967
|
const { type } = panel;
|
|
1845
1968
|
const config = panel.config || {};
|
|
1846
1969
|
const effType = config.chartType || type;
|
|
1847
|
-
if (config.sql || SQL_CHART_TYPES.has(effType)) {
|
|
1970
|
+
if (config.sql || SQL_CHART_TYPES.has(effType) || SQL_DATA_TYPES.has(effType)) {
|
|
1848
1971
|
return /* @__PURE__ */ jsxRuntime.jsx(SqlPanel, { panel });
|
|
1849
1972
|
}
|
|
1850
1973
|
if (!records?.length && type !== "insight") {
|
|
@@ -1867,17 +1990,8 @@ function PanelContent({ panel, records, columns }) {
|
|
|
1867
1990
|
return /* @__PURE__ */ jsxRuntime.jsx(MetricView, { records, config: { column: config.column || "", agg: config.agg, label: config.label } });
|
|
1868
1991
|
case "insight":
|
|
1869
1992
|
return /* @__PURE__ */ jsxRuntime.jsx(InsightView, { config: { text: config.text } });
|
|
1870
|
-
case "table":
|
|
1871
|
-
|
|
1872
|
-
const colNames = cols?.map((c) => c.name) || (records && records[0] ? Object.keys(records[0]) : []);
|
|
1873
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-auto h-full text-xs", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "w-full", children: [
|
|
1874
|
-
/* @__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)) }) }),
|
|
1875
|
-
/* @__PURE__ */ jsxRuntime.jsx("tbody", { children: (records || []).slice(0, 50).map((r, i) => /* @__PURE__ */ jsxRuntime.jsx("tr", { className: "border-b border-dashed border-midnight-border hover:bg-midnight-raised", children: colNames.map((n) => {
|
|
1876
|
-
const cellValue = r[n];
|
|
1877
|
-
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);
|
|
1878
|
-
}) }, i)) })
|
|
1879
|
-
] }) });
|
|
1880
|
-
}
|
|
1993
|
+
case "table":
|
|
1994
|
+
return /* @__PURE__ */ jsxRuntime.jsx(DataTable, { rows: (records || []).slice(0, 50), columns: config.columns || columns?.map((c) => c.name) });
|
|
1881
1995
|
default:
|
|
1882
1996
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4 text-xs text-midnight-text-muted", children: [
|
|
1883
1997
|
"Unknown panel type: ",
|
|
@@ -1908,7 +2022,19 @@ function DashboardRenderer({ dashboard, records, columns }) {
|
|
|
1908
2022
|
},
|
|
1909
2023
|
children: [
|
|
1910
2024
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center justify-between px-3 py-1.5 border-b ${theme.border} bg-midnight-elevated`, children: [
|
|
1911
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2025
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2026
|
+
"span",
|
|
2027
|
+
{
|
|
2028
|
+
className: "flex-1 text-xs font-mono text-midnight-text-body truncate px-1",
|
|
2029
|
+
style: {
|
|
2030
|
+
textAlign: panel.config?.style?.titleAlign || "left",
|
|
2031
|
+
fontWeight: panel.config?.style?.titleBold ? 700 : void 0,
|
|
2032
|
+
background: panel.config?.style?.titleBackground || void 0,
|
|
2033
|
+
color: panel.config?.style?.titleColor || void 0
|
|
2034
|
+
},
|
|
2035
|
+
children: panel.title || panel.type
|
|
2036
|
+
}
|
|
2037
|
+
),
|
|
1912
2038
|
canEditPanels && removePanel && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1913
2039
|
"button",
|
|
1914
2040
|
{
|