@quantumwake/terminal-ux-dashboard-components 0.1.29 → 0.1.30
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/README.md +25 -0
- package/dist/index.cjs +34 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -5
- package/dist/index.d.ts +24 -5
- package/dist/index.js +34 -12
- package/dist/index.js.map +1 -1
- package/package.json +8 -2
package/README.md
CHANGED
|
@@ -49,10 +49,35 @@ SQL service `/query`; a read-only host against DuckDB-WASM `read_parquet`).
|
|
|
49
49
|
- ⏳ Components (landing in tranches): views/, `SqlConsole`,
|
|
50
50
|
`ChartBuilder`, `DashboardRenderer`, `DataExplorer`
|
|
51
51
|
|
|
52
|
+
## Value formatting (`yFormat`, `formatBytes`)
|
|
53
|
+
|
|
54
|
+
`LineView`, `BarView` and `SparklineView` accept a `yFormat?: (value: number) =>
|
|
55
|
+
string` prop that formats every numeric value rendered for the user — y-axis
|
|
56
|
+
tick labels, hover/tooltip values, in-bar labels (`BarView`), and the
|
|
57
|
+
latest-value readout (`SparklineView`). It's unset by default, so nothing
|
|
58
|
+
changes for existing callers: ticks keep `Number(value).toLocaleString()` and
|
|
59
|
+
the tooltip/label keep Nivo's own default formatting.
|
|
60
|
+
|
|
61
|
+
`BarView`'s value axis follows `layout` — vertical puts it on the left,
|
|
62
|
+
horizontal on the bottom — `yFormat` applies to whichever axis that is.
|
|
63
|
+
|
|
64
|
+
```tsx
|
|
65
|
+
import { LineView, BarView, formatBytes } from '@quantumwake/terminal-ux-dashboard-components';
|
|
66
|
+
|
|
67
|
+
<LineView records={records} xColumn="t" yColumn="bytes_per_sec" yFormat={(v) => formatBytes(v, true)} />
|
|
68
|
+
<BarView records={records} groupColumn="host" valueColumn="bytes" yFormat={formatBytes} />
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
`formatBytes(value, perSecond?)` is the shared unit rule for byte-ish series:
|
|
72
|
+
B under 1024, then KB/MB/GB/TB with one decimal place (`"1.5 MB"`), capped at
|
|
73
|
+
TB. Pass `perSecond: true` to append `/s` for a throughput series
|
|
74
|
+
(`"2.0 GB/s"`).
|
|
75
|
+
|
|
52
76
|
## Build
|
|
53
77
|
|
|
54
78
|
```
|
|
55
79
|
npm install
|
|
56
80
|
npm run build # tsup → dist (esm + cjs + d.ts)
|
|
57
81
|
npm run lint # tsc --noEmit
|
|
82
|
+
npm run test # vitest run
|
|
58
83
|
```
|
package/dist/index.cjs
CHANGED
|
@@ -436,6 +436,23 @@ var aggregate = (records, column, fn) => {
|
|
|
436
436
|
return records.length;
|
|
437
437
|
}
|
|
438
438
|
};
|
|
439
|
+
|
|
440
|
+
// src/format.ts
|
|
441
|
+
var BYTE_UNITS = ["KB", "MB", "GB", "TB"];
|
|
442
|
+
function formatBytes(value, perSecond = false) {
|
|
443
|
+
const suffix = perSecond ? "/s" : "";
|
|
444
|
+
if (!Number.isFinite(value)) return `\u2014 B${suffix}`;
|
|
445
|
+
const sign = value < 0 ? "-" : "";
|
|
446
|
+
const abs = Math.abs(value);
|
|
447
|
+
if (abs < 1024) return `${sign}${Math.round(abs)} B${suffix}`;
|
|
448
|
+
let scaled = abs / 1024;
|
|
449
|
+
let unit = 0;
|
|
450
|
+
while (scaled >= 1024 && unit < BYTE_UNITS.length - 1) {
|
|
451
|
+
scaled /= 1024;
|
|
452
|
+
unit++;
|
|
453
|
+
}
|
|
454
|
+
return `${sign}${scaled.toFixed(1)} ${BYTE_UNITS[unit]}${suffix}`;
|
|
455
|
+
}
|
|
439
456
|
var HEAT_STATS = ["count", "sum", "avg", "min", "max", "median"];
|
|
440
457
|
var cellKey = (row, col) => `${row}\0${col}`;
|
|
441
458
|
var normalizePartition = (cells, method, vmin, vmax) => {
|
|
@@ -508,8 +525,8 @@ function MetricView({ records, config, value: presetValue }) {
|
|
|
508
525
|
function InsightView({ config }) {
|
|
509
526
|
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 }) });
|
|
510
527
|
}
|
|
511
|
-
function formatVal(value, s, numeric) {
|
|
512
|
-
let str = numeric ? Number(value).toLocaleString() : String(value);
|
|
528
|
+
function formatVal(value, s, numeric, custom) {
|
|
529
|
+
let str = custom ? custom(value) : numeric ? Number(value).toLocaleString() : String(value);
|
|
513
530
|
if (s.tickTruncate > 0 && str.length > s.tickTruncate) str = `${str.slice(0, s.tickTruncate)}\u2026`;
|
|
514
531
|
return str;
|
|
515
532
|
}
|
|
@@ -558,7 +575,7 @@ function makeAxis(style, axis, columnName, opts2 = {}) {
|
|
|
558
575
|
}
|
|
559
576
|
if (s.tickWrap) {
|
|
560
577
|
out.renderTick = (tick) => {
|
|
561
|
-
const lines = wrapText(formatVal(tick.value, s, numeric), s.tickWrapWidth);
|
|
578
|
+
const lines = wrapText(formatVal(tick.value, s, numeric, opts2.format), s.tickWrapWidth);
|
|
562
579
|
const firstDy = isX ? "0" : `${-((lines.length - 1) * 0.55)}em`;
|
|
563
580
|
return /* @__PURE__ */ jsxRuntime.jsxs("g", { transform: `translate(${tick.x},${tick.y})`, children: [
|
|
564
581
|
/* @__PURE__ */ jsxRuntime.jsx("line", { x2: isX ? 0 : -5, y2: isX ? 5 : 0, style: { stroke: s.textColor, strokeWidth: 1, opacity: 0.3 } }),
|
|
@@ -576,13 +593,13 @@ function makeAxis(style, axis, columnName, opts2 = {}) {
|
|
|
576
593
|
};
|
|
577
594
|
} else {
|
|
578
595
|
out.tickRotation = rotate;
|
|
579
|
-
out.format = (v) => formatVal(v, s, numeric);
|
|
596
|
+
out.format = (v) => formatVal(v, s, numeric, opts2.format);
|
|
580
597
|
}
|
|
581
598
|
const maxTicks = isX ? s.maxXTicks : s.maxYTicks;
|
|
582
599
|
if (numeric && maxTicks > 0) out.tickValues = maxTicks;
|
|
583
600
|
return out;
|
|
584
601
|
}
|
|
585
|
-
function BarView({ records, groupColumn, valueColumn, aggFn = "count", data: presetData, layout = "vertical", style }) {
|
|
602
|
+
function BarView({ records, groupColumn, valueColumn, aggFn = "count", data: presetData, layout = "vertical", style, yFormat }) {
|
|
586
603
|
const computed = react.useMemo(() => {
|
|
587
604
|
if (presetData || !records) return [];
|
|
588
605
|
const groups = groupBy(records, groupColumn);
|
|
@@ -593,8 +610,9 @@ function BarView({ records, groupColumn, valueColumn, aggFn = "count", data: pre
|
|
|
593
610
|
const { frameClass, frameStyle, margin } = chartSizing(style, { top: 20, right: 20, bottom: 60, left: 60 });
|
|
594
611
|
const horizontal = layout === "horizontal";
|
|
595
612
|
const plotted = horizontal ? [...data].reverse() : data;
|
|
596
|
-
const
|
|
597
|
-
const
|
|
613
|
+
const valueFormat = yFormat ? (v) => yFormat(Number(v)) : void 0;
|
|
614
|
+
const axisBottom = horizontal ? makeAxis(style, "x", valueColumn, { numeric: true, format: valueFormat }) : makeAxis(style, "x", groupColumn);
|
|
615
|
+
const axisLeft = horizontal ? makeAxis(style, "y", groupColumn) : makeAxis(style, "y", valueColumn, { numeric: true, format: valueFormat });
|
|
598
616
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: frameClass, style: frameStyle, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
599
617
|
bar.ResponsiveBar,
|
|
600
618
|
{
|
|
@@ -608,6 +626,7 @@ function BarView({ records, groupColumn, valueColumn, aggFn = "count", data: pre
|
|
|
608
626
|
borderColor: { from: "color", modifiers: [["darker", 1.6]] },
|
|
609
627
|
axisBottom,
|
|
610
628
|
axisLeft,
|
|
629
|
+
valueFormat,
|
|
611
630
|
enableLabel: s.barLabels,
|
|
612
631
|
labelSkipWidth: 12,
|
|
613
632
|
labelSkipHeight: 12,
|
|
@@ -658,7 +677,7 @@ function PieView({ records, groupColumn, data: presetData, style }) {
|
|
|
658
677
|
}
|
|
659
678
|
var fmtClock = (d) => d.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", second: "2-digit", hourCycle: "h23" });
|
|
660
679
|
var toDate = (x) => x instanceof Date ? x : new Date(x);
|
|
661
|
-
function LineView({ records, xColumn, yColumn, data: presetData, style, xScale = "point", xFormat, xDomain }) {
|
|
680
|
+
function LineView({ records, xColumn, yColumn, data: presetData, style, xScale = "point", xFormat, xDomain, yFormat }) {
|
|
662
681
|
const timed = xScale === "time";
|
|
663
682
|
const clock = xFormat ?? fmtClock;
|
|
664
683
|
const computed = react.useMemo(() => {
|
|
@@ -680,6 +699,7 @@ function LineView({ records, xColumn, yColumn, data: presetData, style, xScale =
|
|
|
680
699
|
);
|
|
681
700
|
const s = withStyleDefaults(style);
|
|
682
701
|
const { frameClass, frameStyle, margin } = chartSizing(style, { top: 20, right: 20, bottom: 60, left: 60 });
|
|
702
|
+
const yTickFormat = yFormat ? (v) => yFormat(Number(v)) : void 0;
|
|
683
703
|
const colorById = new Map(data.map((serie, i) => [
|
|
684
704
|
serie.id,
|
|
685
705
|
serie.color ?? (s.seriesColors.length || data.length > 1 ? seriesColor(i, s) : "rgba(96, 165, 250, 0.9)")
|
|
@@ -736,7 +756,8 @@ function LineView({ records, xColumn, yColumn, data: presetData, style, xScale =
|
|
|
736
756
|
pointBorderColor: { from: "serieColor" },
|
|
737
757
|
enableGridX: false,
|
|
738
758
|
axisBottom,
|
|
739
|
-
axisLeft: makeAxis(style, "y", yColumn, { numeric: true }),
|
|
759
|
+
axisLeft: makeAxis(style, "y", yColumn, { numeric: true, format: yTickFormat }),
|
|
760
|
+
yFormat: yTickFormat,
|
|
740
761
|
useMesh: true,
|
|
741
762
|
layers: ["grid", "markers", "axes", "areas", bandsLayer, linesLayer, "crosshair", "slices", "mesh", "legends"],
|
|
742
763
|
animate: s.animate,
|
|
@@ -753,7 +774,7 @@ function niceCeil(v) {
|
|
|
753
774
|
}
|
|
754
775
|
return 10 * mag;
|
|
755
776
|
}
|
|
756
|
-
function SparklineView({ data, height = 56, color = "#3987e5", showValue = false, format }) {
|
|
777
|
+
function SparklineView({ data, height = 56, color = "#3987e5", showValue = false, format, yFormat }) {
|
|
757
778
|
const gradientId = react.useId();
|
|
758
779
|
const width = 260;
|
|
759
780
|
const pts = data && data.length ? data : [0];
|
|
@@ -763,7 +784,8 @@ function SparklineView({ data, height = 56, color = "#3987e5", showValue = false
|
|
|
763
784
|
const line = pts.map((v, i) => `${i === 0 ? "M" : "L"} ${xy(v, i)[0].toFixed(1)} ${xy(v, i)[1].toFixed(1)}`).join(" ");
|
|
764
785
|
const area = `${line} L ${width} ${height} L 0 ${height} Z`;
|
|
765
786
|
const current = pts[pts.length - 1];
|
|
766
|
-
const
|
|
787
|
+
const fmt = format ?? yFormat;
|
|
788
|
+
const readout = fmt ? fmt(current) : current.toLocaleString(void 0, { maximumFractionDigits: 1 });
|
|
767
789
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
|
|
768
790
|
/* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: `0 0 ${width} ${height}`, height, className: "w-full", preserveAspectRatio: "none", children: [
|
|
769
791
|
/* @__PURE__ */ jsxRuntime.jsx("defs", { children: /* @__PURE__ */ jsxRuntime.jsxs("linearGradient", { id: gradientId, x1: "0", y1: "0", x2: "0", y2: "1", children: [
|
|
@@ -3096,6 +3118,7 @@ exports.buildChartSQL = buildChartSQL;
|
|
|
3096
3118
|
exports.buildNivoTheme = buildNivoTheme;
|
|
3097
3119
|
exports.chartSizing = chartSizing;
|
|
3098
3120
|
exports.compileWhere = compileWhere;
|
|
3121
|
+
exports.formatBytes = formatBytes;
|
|
3099
3122
|
exports.groupBy = groupBy;
|
|
3100
3123
|
exports.heatColor = heatColor;
|
|
3101
3124
|
exports.heatLabelColor = heatLabelColor;
|