@quantumwake/terminal-ux-dashboard-components 0.1.12 → 0.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +301 -102
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +36 -14
- package/dist/index.d.ts +36 -14
- package/dist/index.js +301 -103
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
package/dist/index.cjs
CHANGED
|
@@ -13,11 +13,13 @@ var lucideReact = require('lucide-react');
|
|
|
13
13
|
var CodeMirror = require('@uiw/react-codemirror');
|
|
14
14
|
var langSql = require('@codemirror/lang-sql');
|
|
15
15
|
var autocomplete = require('@codemirror/autocomplete');
|
|
16
|
+
var GridLayout = require('react-grid-layout');
|
|
16
17
|
|
|
17
18
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
18
19
|
|
|
19
20
|
var PivotTableUI__default = /*#__PURE__*/_interopDefault(PivotTableUI);
|
|
20
21
|
var CodeMirror__default = /*#__PURE__*/_interopDefault(CodeMirror);
|
|
22
|
+
var GridLayout__default = /*#__PURE__*/_interopDefault(GridLayout);
|
|
21
23
|
|
|
22
24
|
// src/context/DashboardContext.tsx
|
|
23
25
|
var DashboardContext = react.createContext(null);
|
|
@@ -46,7 +48,9 @@ function useCapabilities() {
|
|
|
46
48
|
canAddPanel: !!c.addPanel,
|
|
47
49
|
canEditPanels: !!c.removePanel,
|
|
48
50
|
// Studio can recompute + persist a panel's precomputed result.
|
|
49
|
-
canRefresh: !!c.persistPanelData
|
|
51
|
+
canRefresh: !!c.persistPanelData,
|
|
52
|
+
// Studio can drag/resize panels and persist the grid layout.
|
|
53
|
+
canEditLayout: !!c.persistLayout
|
|
50
54
|
};
|
|
51
55
|
}
|
|
52
56
|
|
|
@@ -142,8 +146,10 @@ var buildChartSQL = ({
|
|
|
142
146
|
}
|
|
143
147
|
case "scatter": {
|
|
144
148
|
if (!x || !y) return null;
|
|
145
|
-
const
|
|
146
|
-
|
|
149
|
+
const nx = `TRY_CAST(${qIdent(x)} AS DOUBLE)`, ny = `TRY_CAST(${qIdent(y)} AS DOUBLE)`;
|
|
150
|
+
const extra = `${nx} IS NOT NULL AND ${ny} IS NOT NULL`;
|
|
151
|
+
const w = where ? `${where} AND ${extra}` : ` WHERE ${extra}`;
|
|
152
|
+
return `SELECT ${nx} AS x, ${ny} AS y FROM ${TABLE}${w} LIMIT ${MAX_POINTS}`;
|
|
147
153
|
}
|
|
148
154
|
case "heatmap": {
|
|
149
155
|
if (!x || !y) return null;
|
|
@@ -175,7 +181,10 @@ var shapeChartData = (chartType, rows, { yFields = [] } = {}) => {
|
|
|
175
181
|
case "line":
|
|
176
182
|
return [{ id: "series", data: rows.map((r) => ({ x: String(r.x), y: Number(r.y) || 0 })) }];
|
|
177
183
|
case "scatter":
|
|
178
|
-
return [{
|
|
184
|
+
return [{
|
|
185
|
+
id: "points",
|
|
186
|
+
data: rows.map((r) => ({ x: Number(r.x), y: Number(r.y) })).filter((p) => Number.isFinite(p.x) && Number.isFinite(p.y))
|
|
187
|
+
}];
|
|
179
188
|
case "heatmap": {
|
|
180
189
|
const rowKeys = [];
|
|
181
190
|
const colKeys = [];
|
|
@@ -217,10 +226,25 @@ var DEFAULT_CHART_STYLE = {
|
|
|
217
226
|
xLegendOffset: 46,
|
|
218
227
|
yLegendPosition: "middle",
|
|
219
228
|
yLegendOffset: -50,
|
|
229
|
+
// Custom axis title text + visibility.
|
|
230
|
+
xAxisLabel: "",
|
|
231
|
+
yAxisLabel: "",
|
|
232
|
+
showXLegend: true,
|
|
233
|
+
showYLegend: true,
|
|
234
|
+
// Axis title emphasis.
|
|
235
|
+
legendBold: false,
|
|
236
|
+
legendHighlight: "",
|
|
237
|
+
// Tick overflow handling.
|
|
238
|
+
xTickRotation: -35,
|
|
239
|
+
yTickRotation: 0,
|
|
240
|
+
tickTruncate: 0,
|
|
220
241
|
// Series legend placement (charts that have one: pie, grouped bar).
|
|
221
242
|
legendAnchor: "right",
|
|
222
|
-
// Panel title
|
|
223
|
-
titleAlign: "left"
|
|
243
|
+
// Panel title (rendered by DashboardRenderer's panel header).
|
|
244
|
+
titleAlign: "left",
|
|
245
|
+
titleBold: false,
|
|
246
|
+
titleBackground: "",
|
|
247
|
+
titleColor: ""
|
|
224
248
|
};
|
|
225
249
|
var LEGEND_ANCHORS = ["right", "top-left", "top-right", "bottom-left", "bottom-right", "none"];
|
|
226
250
|
var withStyleDefaults = (style) => ({
|
|
@@ -234,7 +258,17 @@ var buildNivoTheme = (style) => {
|
|
|
234
258
|
text: { fill: s.textColor, fontSize: s.fontSize },
|
|
235
259
|
axis: {
|
|
236
260
|
ticks: { text: { fill: s.textColor, fontSize: s.fontSize } },
|
|
237
|
-
legend: {
|
|
261
|
+
legend: {
|
|
262
|
+
text: {
|
|
263
|
+
fill: s.legendColor,
|
|
264
|
+
fontSize: s.fontSize + 2,
|
|
265
|
+
fontWeight: s.legendBold ? 700 : 400,
|
|
266
|
+
// A highlight is drawn as a text outline (halo) — the clean,
|
|
267
|
+
// SVG-native way to make an axis title stand out.
|
|
268
|
+
outlineWidth: s.legendHighlight ? 3 : 0,
|
|
269
|
+
outlineColor: s.legendHighlight || "transparent"
|
|
270
|
+
}
|
|
271
|
+
}
|
|
238
272
|
},
|
|
239
273
|
grid: { line: { stroke: s.gridColor } },
|
|
240
274
|
crosshair: { line: { stroke: s.textColor, strokeDasharray: "6 6" } },
|
|
@@ -263,14 +297,25 @@ var legendConfig = (style) => {
|
|
|
263
297
|
const ty = s.legendAnchor.includes("top") ? 10 : -10;
|
|
264
298
|
return { ...base, translateX: tx, translateY: ty };
|
|
265
299
|
};
|
|
266
|
-
var
|
|
300
|
+
var truncate = (v, n) => {
|
|
301
|
+
const str = String(v);
|
|
302
|
+
return n > 0 && str.length > n ? `${str.slice(0, n)}\u2026` : str;
|
|
303
|
+
};
|
|
304
|
+
var axisLegend = (style, axis, columnName, opts2 = {}) => {
|
|
267
305
|
const s = withStyleDefaults(style);
|
|
268
306
|
const isX = axis === "x";
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
307
|
+
const show = isX ? s.showXLegend : s.showYLegend;
|
|
308
|
+
const label = (isX ? s.xAxisLabel : s.yAxisLabel) || columnName;
|
|
309
|
+
const out = {};
|
|
310
|
+
if (show && label) {
|
|
311
|
+
out.legend = label;
|
|
312
|
+
out.legendPosition = isX ? s.xLegendPosition : s.yLegendPosition;
|
|
313
|
+
out.legendOffset = isX ? s.xLegendOffset : s.yLegendOffset;
|
|
314
|
+
}
|
|
315
|
+
out.tickRotation = isX ? s.xTickRotation : s.yTickRotation;
|
|
316
|
+
if (opts2.numeric) out.format = (v) => Number(v).toLocaleString();
|
|
317
|
+
else if (s.tickTruncate > 0) out.format = (v) => truncate(v, s.tickTruncate);
|
|
318
|
+
return out;
|
|
274
319
|
};
|
|
275
320
|
|
|
276
321
|
// src/dataShape.ts
|
|
@@ -334,8 +379,8 @@ function BarView({ records, groupColumn, valueColumn, aggFn = "count", data: pre
|
|
|
334
379
|
padding: 0.3,
|
|
335
380
|
colors: ["rgba(74, 222, 128, 0.8)"],
|
|
336
381
|
borderColor: { from: "color", modifiers: [["darker", 1.6]] },
|
|
337
|
-
axisBottom: { tickSize: 5, tickPadding: 5,
|
|
338
|
-
axisLeft: { tickSize: 5, tickPadding: 5,
|
|
382
|
+
axisBottom: { tickSize: 5, tickPadding: 5, ...axisLegend(style, "x", groupColumn) },
|
|
383
|
+
axisLeft: { tickSize: 5, tickPadding: 5, ...axisLegend(style, "y", valueColumn, { numeric: true }) },
|
|
339
384
|
labelSkipWidth: 12,
|
|
340
385
|
labelSkipHeight: 12,
|
|
341
386
|
labelTextColor: { from: "color", modifiers: [["darker", 3]] },
|
|
@@ -408,8 +453,8 @@ function LineView({ records, xColumn, yColumn, data: presetData, style }) {
|
|
|
408
453
|
pointBorderWidth: 2,
|
|
409
454
|
pointBorderColor: { from: "serieColor" },
|
|
410
455
|
enableGridX: false,
|
|
411
|
-
axisBottom: { tickSize: 5, tickPadding: 5,
|
|
412
|
-
axisLeft: { tickSize: 5, tickPadding: 5, ...axisLegend(style, "y", yColumn) },
|
|
456
|
+
axisBottom: { tickSize: 5, tickPadding: 5, ...axisLegend(style, "x", xColumn) },
|
|
457
|
+
axisLeft: { tickSize: 5, tickPadding: 5, ...axisLegend(style, "y", yColumn, { numeric: true }) },
|
|
413
458
|
useMesh: true,
|
|
414
459
|
theme: buildNivoTheme(style)
|
|
415
460
|
}
|
|
@@ -418,7 +463,7 @@ function LineView({ records, xColumn, yColumn, data: presetData, style }) {
|
|
|
418
463
|
function ScatterView({ records, xColumn, yColumn, data: presetData, style }) {
|
|
419
464
|
const computed = react.useMemo(() => {
|
|
420
465
|
if (presetData || !records) return [];
|
|
421
|
-
const points = records.
|
|
466
|
+
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
467
|
return [{ id: `${xColumn} vs ${yColumn}`, data: points }];
|
|
423
468
|
}, [records, xColumn, yColumn, presetData]);
|
|
424
469
|
const data = presetData || computed;
|
|
@@ -431,8 +476,8 @@ function ScatterView({ records, xColumn, yColumn, data: presetData, style }) {
|
|
|
431
476
|
yScale: { type: "linear", min: "auto", max: "auto" },
|
|
432
477
|
colors: ["rgba(167, 139, 250, 0.7)"],
|
|
433
478
|
nodeSize: 6,
|
|
434
|
-
axisBottom: { tickSize: 5, tickPadding: 5, ...axisLegend(style, "x", xColumn) },
|
|
435
|
-
axisLeft: { tickSize: 5, tickPadding: 5, ...axisLegend(style, "y", yColumn) },
|
|
479
|
+
axisBottom: { tickSize: 5, tickPadding: 5, ...axisLegend(style, "x", xColumn, { numeric: true }) },
|
|
480
|
+
axisLeft: { tickSize: 5, tickPadding: 5, ...axisLegend(style, "y", yColumn, { numeric: true }) },
|
|
436
481
|
useMesh: true,
|
|
437
482
|
theme: buildNivoTheme(style)
|
|
438
483
|
}
|
|
@@ -531,8 +576,8 @@ function HeatmapView({
|
|
|
531
576
|
{
|
|
532
577
|
data,
|
|
533
578
|
margin,
|
|
534
|
-
axisTop: { tickSize: 5, tickPadding: 5, tickRotation:
|
|
535
|
-
axisLeft: { tickSize: 5, tickPadding: 5, legend: rowColumn, legendPosition: s.yLegendPosition, legendOffset: -80 },
|
|
579
|
+
axisTop: { tickSize: 5, tickPadding: 5, tickRotation: s.xTickRotation, legend: s.showXLegend ? s.xAxisLabel || colColumn : "", legendPosition: s.xLegendPosition, legendOffset: -50 },
|
|
580
|
+
axisLeft: { tickSize: 5, tickPadding: 5, legend: s.showYLegend ? s.yAxisLabel || rowColumn : "", legendPosition: s.yLegendPosition, legendOffset: -80 },
|
|
536
581
|
axisRight: showTotals ? { tickSize: 5, tickPadding: 5, format: (id) => fmtNum(rowTotals[id]), legend: `${marginAgg} \u25B8`, legendOffset: 60 } : null,
|
|
537
582
|
axisBottom: showTotals ? { tickSize: 5, tickPadding: 5, tickRotation: -35, format: (id) => fmtNum(colTotals[id]) } : null,
|
|
538
583
|
colors: { type: "sequential", scheme: "blue_green", minValue: 0 },
|
|
@@ -576,11 +621,17 @@ function PivotView({ records }) {
|
|
|
576
621
|
)
|
|
577
622
|
] });
|
|
578
623
|
}
|
|
579
|
-
function
|
|
624
|
+
function Section({ title, children }) {
|
|
580
625
|
const { theme } = useDashboard();
|
|
581
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className:
|
|
582
|
-
/* @__PURE__ */ jsxRuntime.jsx("
|
|
583
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "
|
|
626
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `py-3 border-t ${theme.border} first:border-t-0 first:pt-0`, children: [
|
|
627
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[10px] uppercase tracking-wider text-midnight-text-muted/70 font-mono mb-2", children: title }),
|
|
628
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-1.5", children })
|
|
629
|
+
] });
|
|
630
|
+
}
|
|
631
|
+
function Row({ label, children }) {
|
|
632
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-[88px_1fr] items-center gap-2 min-h-[30px]", children: [
|
|
633
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-mono text-midnight-text-muted truncate", title: label, children: label }),
|
|
634
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-end", children })
|
|
584
635
|
] });
|
|
585
636
|
}
|
|
586
637
|
function ColorControl({ value, onChange }) {
|
|
@@ -591,61 +642,112 @@ function ColorControl({ value, onChange }) {
|
|
|
591
642
|
type: "color",
|
|
592
643
|
value: /^#/.test(value) ? value : "#94a3b8",
|
|
593
644
|
onChange: (e) => onChange(e.target.value),
|
|
594
|
-
className: `w-9 h-6 bg-transparent border ${theme.border} cursor-pointer`
|
|
645
|
+
className: `w-9 h-6 bg-transparent border ${theme.border} cursor-pointer p-0`
|
|
595
646
|
}
|
|
596
647
|
);
|
|
597
648
|
}
|
|
598
|
-
function
|
|
649
|
+
function SliderControl({ value, onChange, min, max, step = 1, unit = "" }) {
|
|
650
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 w-full", children: [
|
|
651
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
652
|
+
"input",
|
|
653
|
+
{
|
|
654
|
+
type: "range",
|
|
655
|
+
min,
|
|
656
|
+
max,
|
|
657
|
+
step,
|
|
658
|
+
value,
|
|
659
|
+
onChange: (e) => onChange(Number(e.target.value)),
|
|
660
|
+
className: "flex-1 h-1 accent-midnight-accent cursor-pointer"
|
|
661
|
+
}
|
|
662
|
+
),
|
|
663
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "w-10 shrink-0 text-right text-[11px] font-mono tabular-nums text-midnight-text-body", children: [
|
|
664
|
+
value,
|
|
665
|
+
unit
|
|
666
|
+
] })
|
|
667
|
+
] });
|
|
668
|
+
}
|
|
669
|
+
function TextControl({ value, onChange, placeholder }) {
|
|
599
670
|
const { theme } = useDashboard();
|
|
600
671
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
601
672
|
"input",
|
|
602
673
|
{
|
|
603
|
-
type: "
|
|
674
|
+
type: "text",
|
|
604
675
|
value,
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
676
|
+
placeholder,
|
|
677
|
+
onChange: (e) => onChange(e.target.value),
|
|
678
|
+
className: `w-full px-2 py-1 text-xs font-mono bg-midnight-surface border ${theme.border} text-midnight-text-body outline-none focus:border-midnight-accent transition-colors`
|
|
679
|
+
}
|
|
680
|
+
);
|
|
681
|
+
}
|
|
682
|
+
function Switch({ value, onChange }) {
|
|
683
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
684
|
+
"button",
|
|
685
|
+
{
|
|
686
|
+
type: "button",
|
|
687
|
+
role: "switch",
|
|
688
|
+
"aria-checked": value,
|
|
689
|
+
onClick: () => onChange(!value),
|
|
690
|
+
className: `relative inline-flex h-4 w-7 shrink-0 items-center rounded-full transition-colors ${value ? "bg-midnight-accent" : "bg-midnight-border"}`,
|
|
691
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: `inline-block h-3 w-3 transform rounded-full bg-white transition-transform ${value ? "translate-x-3.5" : "translate-x-0.5"}` })
|
|
609
692
|
}
|
|
610
693
|
);
|
|
611
694
|
}
|
|
695
|
+
function OptionalColor({ value, onChange, fallback = "#a78bfa" }) {
|
|
696
|
+
const on = !!value;
|
|
697
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
698
|
+
on && /* @__PURE__ */ jsxRuntime.jsx(ColorControl, { value, onChange }),
|
|
699
|
+
/* @__PURE__ */ jsxRuntime.jsx(Switch, { value: on, onChange: (v) => onChange(v ? fallback : "") })
|
|
700
|
+
] });
|
|
701
|
+
}
|
|
612
702
|
function Choice({ value, options, onChange }) {
|
|
613
703
|
const { theme } = useDashboard();
|
|
614
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
704
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
615
705
|
"select",
|
|
616
706
|
{
|
|
617
707
|
value,
|
|
618
708
|
onChange: (e) => onChange(e.target.value),
|
|
619
|
-
className: `w-full px-2 py-1 text-xs
|
|
709
|
+
className: `w-full px-2 py-1 text-xs font-mono bg-midnight-surface border ${theme.border} text-midnight-text-body outline-none focus:border-midnight-accent transition-colors`,
|
|
620
710
|
children: options.map((o) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: o, children: o }, o))
|
|
621
711
|
}
|
|
622
|
-
)
|
|
623
|
-
}
|
|
624
|
-
function Group({ children, last }) {
|
|
625
|
-
const { theme } = useDashboard();
|
|
626
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `space-y-2 pb-3 ${last ? "" : `mb-3 border-b ${theme.border}`}`, children });
|
|
712
|
+
);
|
|
627
713
|
}
|
|
628
714
|
function ChartStyleControls({ style, onChange }) {
|
|
629
715
|
const s = withStyleDefaults(style);
|
|
630
716
|
const set = (key, value) => onChange({ ...s, [key]: value });
|
|
631
717
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
632
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
718
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Section, { title: "Canvas", children: [
|
|
633
719
|
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Background", children: /* @__PURE__ */ jsxRuntime.jsx(ColorControl, { value: s.background, onChange: (v) => set("background", v) }) }),
|
|
634
|
-
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Text
|
|
635
|
-
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "
|
|
636
|
-
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "
|
|
637
|
-
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Font size", children: /* @__PURE__ */ jsxRuntime.jsx(NumberControl, { value: s.fontSize, min: 6, max: 24, onChange: (v) => set("fontSize", v) }) })
|
|
720
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Text", children: /* @__PURE__ */ jsxRuntime.jsx(ColorControl, { value: s.textColor, onChange: (v) => set("textColor", v) }) }),
|
|
721
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Grid", children: /* @__PURE__ */ jsxRuntime.jsx(ColorControl, { value: s.gridColor, onChange: (v) => set("gridColor", v) }) }),
|
|
722
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Font size", children: /* @__PURE__ */ jsxRuntime.jsx(SliderControl, { value: s.fontSize, min: 6, max: 24, unit: "px", onChange: (v) => set("fontSize", v) }) })
|
|
638
723
|
] }),
|
|
639
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
640
|
-
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "X title
|
|
641
|
-
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "X
|
|
642
|
-
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Y title
|
|
643
|
-
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Y
|
|
724
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Section, { title: "Axis titles", children: [
|
|
725
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "X title", children: /* @__PURE__ */ jsxRuntime.jsx(TextControl, { value: s.xAxisLabel, placeholder: "(column)", onChange: (v) => set("xAxisLabel", v) }) }),
|
|
726
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Show X", children: /* @__PURE__ */ jsxRuntime.jsx(Switch, { value: s.showXLegend, onChange: (v) => set("showXLegend", v) }) }),
|
|
727
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Y title", children: /* @__PURE__ */ jsxRuntime.jsx(TextControl, { value: s.yAxisLabel, placeholder: "(column)", onChange: (v) => set("yAxisLabel", v) }) }),
|
|
728
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Show Y", children: /* @__PURE__ */ jsxRuntime.jsx(Switch, { value: s.showYLegend, onChange: (v) => set("showYLegend", v) }) }),
|
|
729
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Color", children: /* @__PURE__ */ jsxRuntime.jsx(ColorControl, { value: s.legendColor, onChange: (v) => set("legendColor", v) }) }),
|
|
730
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Bold", children: /* @__PURE__ */ jsxRuntime.jsx(Switch, { value: s.legendBold, onChange: (v) => set("legendBold", v) }) }),
|
|
731
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Highlight", children: /* @__PURE__ */ jsxRuntime.jsx(OptionalColor, { value: s.legendHighlight, onChange: (v) => set("legendHighlight", v) }) })
|
|
644
732
|
] }),
|
|
645
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
646
|
-
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "
|
|
647
|
-
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "
|
|
648
|
-
|
|
733
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Section, { title: "Axis placement", children: [
|
|
734
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "X pos", children: /* @__PURE__ */ jsxRuntime.jsx(Choice, { value: s.xLegendPosition, options: ["start", "middle", "end"], onChange: (v) => set("xLegendPosition", v) }) }),
|
|
735
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "X offset", children: /* @__PURE__ */ jsxRuntime.jsx(SliderControl, { value: s.xLegendOffset, min: -80, max: 80, onChange: (v) => set("xLegendOffset", v) }) }),
|
|
736
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Y pos", children: /* @__PURE__ */ jsxRuntime.jsx(Choice, { value: s.yLegendPosition, options: ["start", "middle", "end"], onChange: (v) => set("yLegendPosition", v) }) }),
|
|
737
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Y offset", children: /* @__PURE__ */ jsxRuntime.jsx(SliderControl, { value: s.yLegendOffset, min: -80, max: 80, onChange: (v) => set("yLegendOffset", v) }) })
|
|
738
|
+
] }),
|
|
739
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Section, { title: "Ticks", children: [
|
|
740
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "X angle", children: /* @__PURE__ */ jsxRuntime.jsx(SliderControl, { value: s.xTickRotation, min: -90, max: 90, unit: "\xB0", onChange: (v) => set("xTickRotation", v) }) }),
|
|
741
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Y angle", children: /* @__PURE__ */ jsxRuntime.jsx(SliderControl, { value: s.yTickRotation, min: -90, max: 90, unit: "\xB0", onChange: (v) => set("yTickRotation", v) }) }),
|
|
742
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Truncate", children: /* @__PURE__ */ jsxRuntime.jsx(SliderControl, { value: s.tickTruncate, min: 0, max: 40, onChange: (v) => set("tickTruncate", v) }) })
|
|
743
|
+
] }),
|
|
744
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Section, { title: "Panel title", children: [
|
|
745
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Align", children: /* @__PURE__ */ jsxRuntime.jsx(Choice, { value: s.titleAlign, options: ["left", "center", "right"], onChange: (v) => set("titleAlign", v) }) }),
|
|
746
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Bold", children: /* @__PURE__ */ jsxRuntime.jsx(Switch, { value: s.titleBold, onChange: (v) => set("titleBold", v) }) }),
|
|
747
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Color", children: /* @__PURE__ */ jsxRuntime.jsx(OptionalColor, { value: s.titleColor, onChange: (v) => set("titleColor", v), fallback: "#e2e8f0" }) }),
|
|
748
|
+
/* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Background", children: /* @__PURE__ */ jsxRuntime.jsx(OptionalColor, { value: s.titleBackground, onChange: (v) => set("titleBackground", v), fallback: "#1e293b" }) })
|
|
749
|
+
] }),
|
|
750
|
+
/* @__PURE__ */ jsxRuntime.jsx(Section, { title: "Series legend", children: /* @__PURE__ */ jsxRuntime.jsx(Row, { label: "Position", children: /* @__PURE__ */ jsxRuntime.jsx(Choice, { value: s.legendAnchor, options: ["right", "top-left", "top-right", "bottom-left", "bottom-right", "none"], onChange: (v) => set("legendAnchor", v) }) }) })
|
|
649
751
|
] });
|
|
650
752
|
}
|
|
651
753
|
var AGGREGATES = [
|
|
@@ -1369,6 +1471,7 @@ function ChartBuilder({ records, columns, stateId, onSave }) {
|
|
|
1369
1471
|
const [showTotals, setShowTotals] = react.useState(false);
|
|
1370
1472
|
const [style, setStyle] = react.useState(DEFAULT_CHART_STYLE);
|
|
1371
1473
|
const [showStyle, setShowStyle] = react.useState(false);
|
|
1474
|
+
const [showFields, setShowFields] = react.useState(false);
|
|
1372
1475
|
const [sqlRows, setSqlRows] = react.useState(null);
|
|
1373
1476
|
const [sqlLoading, setSqlLoading] = react.useState(false);
|
|
1374
1477
|
const [sqlError, setSqlError] = react.useState(null);
|
|
@@ -1654,8 +1757,23 @@ function ChartBuilder({ records, columns, stateId, onSave }) {
|
|
|
1654
1757
|
)
|
|
1655
1758
|
] }),
|
|
1656
1759
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pt-4 border-t border-midnight-border", children: [
|
|
1657
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1658
|
-
|
|
1760
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1761
|
+
"button",
|
|
1762
|
+
{
|
|
1763
|
+
onClick: () => setShowFields((s) => !s),
|
|
1764
|
+
className: "flex items-center gap-1 text-xs uppercase text-midnight-text-muted font-mono hover:text-midnight-text-body transition-colors",
|
|
1765
|
+
children: [
|
|
1766
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: `w-3 h-3 transition-transform ${showFields ? "" : "-rotate-90"}` }),
|
|
1767
|
+
"Available Fields",
|
|
1768
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-midnight-text-muted/60 normal-case", children: [
|
|
1769
|
+
"(",
|
|
1770
|
+
columns.length,
|
|
1771
|
+
")"
|
|
1772
|
+
] })
|
|
1773
|
+
]
|
|
1774
|
+
}
|
|
1775
|
+
),
|
|
1776
|
+
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
1777
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-midnight-text-body", children: col.name }),
|
|
1660
1778
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-midnight-text-muted", children: col.type })
|
|
1661
1779
|
] }, col.name)) })
|
|
@@ -1685,6 +1803,15 @@ function ViewLoading() {
|
|
|
1685
1803
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center h-full text-xs text-midnight-text-muted", children: "Loading..." });
|
|
1686
1804
|
}
|
|
1687
1805
|
var SQL_CHART_TYPES = /* @__PURE__ */ new Set(["bar", "grouped-bar", "pie", "line", "scatter", "heatmap"]);
|
|
1806
|
+
var SQL_DATA_TYPES = /* @__PURE__ */ new Set(["table", "metric"]);
|
|
1807
|
+
var TABLE_PREVIEW_ROWS = 200;
|
|
1808
|
+
function tableSql(config) {
|
|
1809
|
+
const cols = config.columns?.length ? config.columns.map(qIdent).join(", ") : "*";
|
|
1810
|
+
return `SELECT ${cols} FROM data LIMIT ${TABLE_PREVIEW_ROWS}`;
|
|
1811
|
+
}
|
|
1812
|
+
function metricSql(config) {
|
|
1813
|
+
return `SELECT ${aggExpr(config.agg, config.column)} AS v FROM data`;
|
|
1814
|
+
}
|
|
1688
1815
|
function panelToChartConfig(chartType, config) {
|
|
1689
1816
|
const cfg = { chartType, xFields: [], yFields: [], filters: config.filters || [] };
|
|
1690
1817
|
switch (chartType) {
|
|
@@ -1709,11 +1836,23 @@ function panelToChartConfig(chartType, config) {
|
|
|
1709
1836
|
}
|
|
1710
1837
|
return cfg;
|
|
1711
1838
|
}
|
|
1712
|
-
function panelSql(
|
|
1839
|
+
function panelSql(type, config) {
|
|
1713
1840
|
if (config.sql) return config.sql;
|
|
1714
|
-
if (
|
|
1841
|
+
if (type === "table") return tableSql(config);
|
|
1842
|
+
if (type === "metric") return metricSql(config);
|
|
1843
|
+
if (SQL_CHART_TYPES.has(type)) return buildChartSQL(panelToChartConfig(type, config)) || "";
|
|
1715
1844
|
return "";
|
|
1716
1845
|
}
|
|
1846
|
+
function DataTable({ rows, columns }) {
|
|
1847
|
+
const colNames = columns?.length ? columns : rows[0] ? Object.keys(rows[0]) : [];
|
|
1848
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-auto h-full text-xs", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "w-full", children: [
|
|
1849
|
+
/* @__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)) }) }),
|
|
1850
|
+
/* @__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) => {
|
|
1851
|
+
const cellValue = r[n];
|
|
1852
|
+
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);
|
|
1853
|
+
}) }, i)) })
|
|
1854
|
+
] }) });
|
|
1855
|
+
}
|
|
1717
1856
|
function SqlPanel({ panel }) {
|
|
1718
1857
|
const { runQuery, persistPanelData } = useDashboard();
|
|
1719
1858
|
const { canRefresh } = useCapabilities();
|
|
@@ -1805,6 +1944,8 @@ function SqlPanel({ panel }) {
|
|
|
1805
1944
|
const first = rows[0];
|
|
1806
1945
|
const v = first ? Object.values(first)[0] : 0;
|
|
1807
1946
|
chart = /* @__PURE__ */ jsxRuntime.jsx(MetricView, { value: Number(v) || 0, config: { column: config.column || "", agg: config.agg, label: config.label } });
|
|
1947
|
+
} else if (chartType === "table") {
|
|
1948
|
+
chart = /* @__PURE__ */ jsxRuntime.jsx(DataTable, { rows, columns: config.columns });
|
|
1808
1949
|
} else {
|
|
1809
1950
|
const shaped = shapeChartData(chartType, rows, { yFields: config.yFields || [] });
|
|
1810
1951
|
switch (chartType) {
|
|
@@ -1844,7 +1985,7 @@ function PanelContent({ panel, records, columns }) {
|
|
|
1844
1985
|
const { type } = panel;
|
|
1845
1986
|
const config = panel.config || {};
|
|
1846
1987
|
const effType = config.chartType || type;
|
|
1847
|
-
if (config.sql || SQL_CHART_TYPES.has(effType)) {
|
|
1988
|
+
if (config.sql || SQL_CHART_TYPES.has(effType) || SQL_DATA_TYPES.has(effType)) {
|
|
1848
1989
|
return /* @__PURE__ */ jsxRuntime.jsx(SqlPanel, { panel });
|
|
1849
1990
|
}
|
|
1850
1991
|
if (!records?.length && type !== "insight") {
|
|
@@ -1867,17 +2008,8 @@ function PanelContent({ panel, records, columns }) {
|
|
|
1867
2008
|
return /* @__PURE__ */ jsxRuntime.jsx(MetricView, { records, config: { column: config.column || "", agg: config.agg, label: config.label } });
|
|
1868
2009
|
case "insight":
|
|
1869
2010
|
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
|
-
}
|
|
2011
|
+
case "table":
|
|
2012
|
+
return /* @__PURE__ */ jsxRuntime.jsx(DataTable, { rows: (records || []).slice(0, 50), columns: config.columns || columns?.map((c) => c.name) });
|
|
1881
2013
|
default:
|
|
1882
2014
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4 text-xs text-midnight-text-muted", children: [
|
|
1883
2015
|
"Unknown panel type: ",
|
|
@@ -1885,45 +2017,112 @@ function PanelContent({ panel, records, columns }) {
|
|
|
1885
2017
|
] });
|
|
1886
2018
|
}
|
|
1887
2019
|
}
|
|
1888
|
-
var
|
|
2020
|
+
var GRID_COLS = 12;
|
|
2021
|
+
var ROW_HEIGHT = 80;
|
|
2022
|
+
var GRID_MARGIN = 10;
|
|
2023
|
+
var DRAG_HANDLE = "panel-drag-handle";
|
|
2024
|
+
var RGL_CSS = `
|
|
2025
|
+
.react-grid-layout { position: relative; transition: height 200ms ease; }
|
|
2026
|
+
.react-grid-item { transition: all 200ms ease; transition-property: left, top, width, height; box-sizing: border-box; }
|
|
2027
|
+
.react-grid-item.cssTransforms { transition-property: transform, width, height; }
|
|
2028
|
+
.react-grid-item.resizing { transition: none; z-index: 3; will-change: width, height; }
|
|
2029
|
+
.react-grid-item.react-draggable-dragging { transition: none; z-index: 3; will-change: transform; }
|
|
2030
|
+
.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; }
|
|
2031
|
+
.react-grid-item > .react-resizable-handle { position: absolute; width: 18px; height: 18px; bottom: 0; right: 0; cursor: se-resize; }
|
|
2032
|
+
.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); }
|
|
2033
|
+
.${DRAG_HANDLE} { cursor: grab; }
|
|
2034
|
+
.react-grid-item.react-draggable-dragging .${DRAG_HANDLE} { cursor: grabbing; }
|
|
2035
|
+
`;
|
|
2036
|
+
var rglCssInjected = false;
|
|
2037
|
+
function useInjectRglCss() {
|
|
2038
|
+
react.useEffect(() => {
|
|
2039
|
+
if (rglCssInjected || typeof document === "undefined") return;
|
|
2040
|
+
const el = document.createElement("style");
|
|
2041
|
+
el.setAttribute("data-rgl", "dashboard-renderer");
|
|
2042
|
+
el.textContent = RGL_CSS;
|
|
2043
|
+
document.head.appendChild(el);
|
|
2044
|
+
rglCssInjected = true;
|
|
2045
|
+
}, []);
|
|
2046
|
+
}
|
|
2047
|
+
var GridLayoutWithWidth = GridLayout.WidthProvider(GridLayout__default.default);
|
|
2048
|
+
function buildLayout(panels) {
|
|
2049
|
+
let cx = 0, cy = 0, rowH = 0;
|
|
2050
|
+
return panels.map((p) => {
|
|
2051
|
+
const w = Math.min(Math.max(p.width || 6, 1), GRID_COLS);
|
|
2052
|
+
const h = Math.max(p.height || 4, 1);
|
|
2053
|
+
if (typeof p.x === "number" && typeof p.y === "number") {
|
|
2054
|
+
return { i: p.id, x: p.x, y: p.y, w, h, minW: 2, minH: 2 };
|
|
2055
|
+
}
|
|
2056
|
+
if (cx + w > GRID_COLS) {
|
|
2057
|
+
cx = 0;
|
|
2058
|
+
cy += rowH;
|
|
2059
|
+
rowH = 0;
|
|
2060
|
+
}
|
|
2061
|
+
const item = { i: p.id, x: cx, y: cy, w, h, minW: 2, minH: 2 };
|
|
2062
|
+
cx += w;
|
|
2063
|
+
rowH = Math.max(rowH, h);
|
|
2064
|
+
return item;
|
|
2065
|
+
});
|
|
2066
|
+
}
|
|
1889
2067
|
function DashboardRenderer({ dashboard, records, columns }) {
|
|
1890
|
-
const { theme, removePanel } = useDashboard();
|
|
1891
|
-
const { canEditPanels } = useCapabilities();
|
|
2068
|
+
const { theme, removePanel, persistLayout } = useDashboard();
|
|
2069
|
+
const { canEditPanels, canEditLayout } = useCapabilities();
|
|
2070
|
+
useInjectRglCss();
|
|
2071
|
+
const panels = dashboard?.panels ?? [];
|
|
2072
|
+
const layout = react.useMemo(() => buildLayout(panels), [panels]);
|
|
2073
|
+
const onLayoutChange = react.useCallback((next) => {
|
|
2074
|
+
if (!persistLayout) return;
|
|
2075
|
+
persistLayout(next.map((l) => ({ id: l.i, x: l.x, y: l.y, w: l.w, h: l.h })));
|
|
2076
|
+
}, [persistLayout]);
|
|
1892
2077
|
if (!dashboard) return null;
|
|
1893
2078
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4 p-2", children: [
|
|
1894
2079
|
dashboard.insights && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `border ${theme.border} bg-midnight-elevated px-4 py-3 flex items-start gap-3`, children: [
|
|
1895
2080
|
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Sparkles, { className: "w-5 h-5 text-midnight-accent shrink-0 mt-0.5" }),
|
|
1896
2081
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: `text-sm ${theme.text} leading-relaxed`, children: dashboard.insights })
|
|
1897
2082
|
] }),
|
|
1898
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
2083
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2084
|
+
GridLayoutWithWidth,
|
|
2085
|
+
{
|
|
2086
|
+
className: "layout",
|
|
2087
|
+
layout,
|
|
2088
|
+
cols: GRID_COLS,
|
|
2089
|
+
rowHeight: ROW_HEIGHT,
|
|
2090
|
+
margin: [GRID_MARGIN, GRID_MARGIN],
|
|
2091
|
+
isDraggable: canEditLayout,
|
|
2092
|
+
isResizable: canEditLayout,
|
|
2093
|
+
draggableHandle: `.${DRAG_HANDLE}`,
|
|
2094
|
+
onDragStop: onLayoutChange,
|
|
2095
|
+
onResizeStop: onLayoutChange,
|
|
2096
|
+
compactType: canEditLayout ? "vertical" : null,
|
|
2097
|
+
children: panels.map((panel) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `border ${theme.border} bg-midnight-surface flex flex-col overflow-hidden`, children: [
|
|
2098
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex items-center justify-between px-3 py-1.5 border-b ${theme.border} bg-midnight-elevated ${canEditLayout ? DRAG_HANDLE : ""}`, children: [
|
|
2099
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2100
|
+
"span",
|
|
2101
|
+
{
|
|
2102
|
+
className: "flex-1 text-xs font-mono text-midnight-text-body truncate px-1",
|
|
2103
|
+
style: {
|
|
2104
|
+
textAlign: panel.config?.style?.titleAlign || "left",
|
|
2105
|
+
fontWeight: panel.config?.style?.titleBold ? 700 : void 0,
|
|
2106
|
+
background: panel.config?.style?.titleBackground || void 0,
|
|
2107
|
+
color: panel.config?.style?.titleColor || void 0
|
|
2108
|
+
},
|
|
2109
|
+
children: panel.title || panel.type
|
|
2110
|
+
}
|
|
2111
|
+
),
|
|
2112
|
+
canEditPanels && removePanel && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2113
|
+
"button",
|
|
2114
|
+
{
|
|
2115
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
2116
|
+
onClick: () => removePanel(panel.id),
|
|
2117
|
+
className: "p-0.5 hover:bg-midnight-raised text-midnight-text-muted hover:text-midnight-text-body transition-colors",
|
|
2118
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "w-3 h-3" })
|
|
2119
|
+
}
|
|
2120
|
+
)
|
|
2121
|
+
] }),
|
|
2122
|
+
/* @__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 }) }) })
|
|
2123
|
+
] }, panel.id))
|
|
2124
|
+
}
|
|
2125
|
+
)
|
|
1927
2126
|
] });
|
|
1928
2127
|
}
|
|
1929
2128
|
function ProfileSummary({ profile, theme }) {
|