@quantumwake/terminal-ux-dashboard-components 0.1.28 → 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/dist/index.d.cts CHANGED
@@ -248,6 +248,14 @@ type AggFn = 'count' | 'distinct' | 'sum' | 'avg' | 'min' | 'max' | 'median';
248
248
  declare const groupBy: (records: Row[], column: string) => Record<string, Row[]>;
249
249
  declare const aggregate: (records: Row[], column: string, fn: AggFn | string) => number;
250
250
 
251
+ /**
252
+ * Formats a byte count as a human-readable string: plain bytes under 1024
253
+ * ("999 B"), then KB/MB/GB/TB with one decimal place ("1.0 KB", "1.5 MB",
254
+ * "2.0 GB"), capped at TB (no PB+). `perSecond` appends "/s" — for
255
+ * throughput series (KB/s, MB/s, GB/s).
256
+ */
257
+ declare function formatBytes(value: number, perSecond?: boolean): string;
258
+
251
259
  type HeatStat = 'count' | 'sum' | 'avg' | 'min' | 'max' | 'median';
252
260
  type ColorScope = 'global' | 'row' | 'column' | 'block';
253
261
  type ColorMethod = 'linear' | 'rank';
@@ -306,8 +314,11 @@ interface BarViewProps {
306
314
  * many bars with long names (labels never collide). Default vertical. */
307
315
  layout?: 'vertical' | 'horizontal';
308
316
  style?: Partial<ChartStyle>;
317
+ /** Value-axis tick + tooltip + in-bar label formatter (e.g. formatBytes
318
+ * for a throughput series). Default: Number(value).toLocaleString(). */
319
+ yFormat?: (value: number) => string;
309
320
  }
310
- declare function BarView({ records, groupColumn, valueColumn, aggFn, data: presetData, layout, style }: BarViewProps): react.JSX.Element;
321
+ declare function BarView({ records, groupColumn, valueColumn, aggFn, data: presetData, layout, style, yFormat }: BarViewProps): react.JSX.Element;
311
322
 
312
323
  interface PieDatum {
313
324
  id: string;
@@ -335,7 +346,7 @@ interface LineSerie {
335
346
  * the min–max spread of what it aggregates. y may be null for an
336
347
  * honest gap (the line breaks instead of bridging). */
337
348
  data: {
338
- x: string;
349
+ x: string | Date | number;
339
350
  y: number | null;
340
351
  lo?: number;
341
352
  hi?: number;
@@ -348,8 +359,25 @@ interface LineViewProps {
348
359
  /** Pre-shaped Nivo line series bypasses client-side processing. */
349
360
  data?: LineSerie[];
350
361
  style?: Partial<ChartStyle>;
351
- }
352
- declare function LineView({ records, xColumn, yColumn, data: presetData, style }: LineViewProps): react.JSX.Element;
362
+ /** 'point' (default): x values are CATEGORIES, placed at equal spacing
363
+ * in encounter order and ticked by sampling every Nth one — right for
364
+ * labels, wrong for timestamps (a missing bucket collapses, and the
365
+ * ticks land on arbitrary samples). 'time': x values are Dates (or
366
+ * epoch ms / ISO strings) on a continuous scale — gaps stretch, ticks
367
+ * land on ROUND times (d3's time ticks, capped by maxXTicks), and the
368
+ * series need not share buckets. */
369
+ xScale?: 'point' | 'time';
370
+ /** Time axis tick + tooltip formatter. Default: HH:MM:SS, local, 24h. */
371
+ xFormat?: (x: Date) => string;
372
+ /** Time axis: pin the visible domain — a live window keeps a steady
373
+ * width instead of re-fitting to whatever samples exist. Default: fit
374
+ * the data. */
375
+ xDomain?: [Date, Date];
376
+ /** Y-axis tick + tooltip value formatter (e.g. formatBytes for a
377
+ * throughput series). Default: Number(value).toLocaleString(). */
378
+ yFormat?: (value: number) => string;
379
+ }
380
+ declare function LineView({ records, xColumn, yColumn, data: presetData, style, xScale, xFormat, xDomain, yFormat }: LineViewProps): react.JSX.Element;
353
381
 
354
382
  interface SparklineViewProps {
355
383
  /** The series, oldest first. */
@@ -361,10 +389,15 @@ interface SparklineViewProps {
361
389
  /** Show the CURRENT (last) value as a readout over the mark — a trend
362
390
  * shape without its number answers "which way", never "how much". */
363
391
  showValue?: boolean;
364
- /** Formats the readout (e.g. v => `${v.toFixed(1)} ms`). Default: locale. */
392
+ /** Formats the readout (e.g. v => `${v.toFixed(1)} ms`). Default: locale.
393
+ * Prefer yFormat in new code — kept for existing callers. */
365
394
  format?: (v: number) => string;
395
+ /** Same as `format` — named to match LineView/BarView's yFormat prop
396
+ * (e.g. formatBytes for a throughput sparkline). `format` wins if both
397
+ * are given. */
398
+ yFormat?: (v: number) => string;
366
399
  }
367
- declare function SparklineView({ data, height, color, showValue, format }: SparklineViewProps): react.JSX.Element;
400
+ declare function SparklineView({ data, height, color, showValue, format, yFormat }: SparklineViewProps): react.JSX.Element;
368
401
 
369
402
  interface ScatterSerie {
370
403
  id: string;
@@ -661,4 +694,4 @@ interface DataExplorerProps {
661
694
  }
662
695
  declare function DataExplorer({ records, columns, states, activeStateId, profile, dashboard, dashboardId, savedDashboards, loading, analyzing, defaultMode, tabs, onSelectState, onRefreshStates, }: DataExplorerProps): react.JSX.Element;
663
696
 
664
- export { type AggFn, type BarDatum, BarView, type BarViewProps, ChartBuilder, type ChartBuilderColumn, type ChartBuilderProps, type ChartConfig, type ChartField, type ChartFilter, type ChartMargin, type ChartPanel, type ChartStyle, ChartStyleControls, type ChartStyleControlsProps, type ChartType, type ColorMethod, type ColorOptions, type ColorScope, DEFAULT_CHART_STYLE, DEFAULT_SERIES_COLORS, type Dashboard$1 as Dashboard, type DashboardCapabilities, type DashboardContextValue, type DashboardPanel$1 as DashboardPanel, DashboardProvider, type DashboardProviderProps, DashboardRenderer, type DashboardRendererProps, type DashboardTheme, DataExplorer, type DataExplorerProps, HEAT_STATS, type HeatCell, type HeatPlusDatum, type HeatStat, HeatmapPlusView, type HeatmapPlusViewProps, type HeatmapSerie, HeatmapView, type HeatmapViewProps, type InsightConfig, InsightView, type InsightViewProps, LEGEND_ANCHORS, type LegendAnchor, type LegendPosition, type LineSerie, LineView, type LineViewProps, type MetricConfig, MetricView, type MetricViewProps, PanelChart, type PanelChartProps, type PanelInput, type PanelLayout, type PanelRef, type PieDatum, PieView, type PieViewProps, PivotView, type PivotViewProps, type QueryResult, type Row, SERIES_OVERFLOW_COLOR, type SavedDashboard, type ScatterSerie, ScatterView, type ScatterViewProps, SparklineView, type SparklineViewProps, SqlConsole, type SqlConsoleColumn, type SqlConsoleProps, type TitleAlign, aggExpr, aggregate, axisLegend, buildChartSQL, buildNivoTheme, chartSizing, compileWhere, groupBy, heatColor, heatLabelColor, legendConfig, normalizeCells, qIdent, qLit, seriesColor, shapeChartData, thinTicks, useCapabilities, useDashboard, withStyleDefaults };
697
+ export { type AggFn, type BarDatum, BarView, type BarViewProps, ChartBuilder, type ChartBuilderColumn, type ChartBuilderProps, type ChartConfig, type ChartField, type ChartFilter, type ChartMargin, type ChartPanel, type ChartStyle, ChartStyleControls, type ChartStyleControlsProps, type ChartType, type ColorMethod, type ColorOptions, type ColorScope, DEFAULT_CHART_STYLE, DEFAULT_SERIES_COLORS, type Dashboard$1 as Dashboard, type DashboardCapabilities, type DashboardContextValue, type DashboardPanel$1 as DashboardPanel, DashboardProvider, type DashboardProviderProps, DashboardRenderer, type DashboardRendererProps, type DashboardTheme, DataExplorer, type DataExplorerProps, HEAT_STATS, type HeatCell, type HeatPlusDatum, type HeatStat, HeatmapPlusView, type HeatmapPlusViewProps, type HeatmapSerie, HeatmapView, type HeatmapViewProps, type InsightConfig, InsightView, type InsightViewProps, LEGEND_ANCHORS, type LegendAnchor, type LegendPosition, type LineSerie, LineView, type LineViewProps, type MetricConfig, MetricView, type MetricViewProps, PanelChart, type PanelChartProps, type PanelInput, type PanelLayout, type PanelRef, type PieDatum, PieView, type PieViewProps, PivotView, type PivotViewProps, type QueryResult, type Row, SERIES_OVERFLOW_COLOR, type SavedDashboard, type ScatterSerie, ScatterView, type ScatterViewProps, SparklineView, type SparklineViewProps, SqlConsole, type SqlConsoleColumn, type SqlConsoleProps, type TitleAlign, aggExpr, aggregate, axisLegend, buildChartSQL, buildNivoTheme, chartSizing, compileWhere, formatBytes, groupBy, heatColor, heatLabelColor, legendConfig, normalizeCells, qIdent, qLit, seriesColor, shapeChartData, thinTicks, useCapabilities, useDashboard, withStyleDefaults };
package/dist/index.d.ts CHANGED
@@ -248,6 +248,14 @@ type AggFn = 'count' | 'distinct' | 'sum' | 'avg' | 'min' | 'max' | 'median';
248
248
  declare const groupBy: (records: Row[], column: string) => Record<string, Row[]>;
249
249
  declare const aggregate: (records: Row[], column: string, fn: AggFn | string) => number;
250
250
 
251
+ /**
252
+ * Formats a byte count as a human-readable string: plain bytes under 1024
253
+ * ("999 B"), then KB/MB/GB/TB with one decimal place ("1.0 KB", "1.5 MB",
254
+ * "2.0 GB"), capped at TB (no PB+). `perSecond` appends "/s" — for
255
+ * throughput series (KB/s, MB/s, GB/s).
256
+ */
257
+ declare function formatBytes(value: number, perSecond?: boolean): string;
258
+
251
259
  type HeatStat = 'count' | 'sum' | 'avg' | 'min' | 'max' | 'median';
252
260
  type ColorScope = 'global' | 'row' | 'column' | 'block';
253
261
  type ColorMethod = 'linear' | 'rank';
@@ -306,8 +314,11 @@ interface BarViewProps {
306
314
  * many bars with long names (labels never collide). Default vertical. */
307
315
  layout?: 'vertical' | 'horizontal';
308
316
  style?: Partial<ChartStyle>;
317
+ /** Value-axis tick + tooltip + in-bar label formatter (e.g. formatBytes
318
+ * for a throughput series). Default: Number(value).toLocaleString(). */
319
+ yFormat?: (value: number) => string;
309
320
  }
310
- declare function BarView({ records, groupColumn, valueColumn, aggFn, data: presetData, layout, style }: BarViewProps): react.JSX.Element;
321
+ declare function BarView({ records, groupColumn, valueColumn, aggFn, data: presetData, layout, style, yFormat }: BarViewProps): react.JSX.Element;
311
322
 
312
323
  interface PieDatum {
313
324
  id: string;
@@ -335,7 +346,7 @@ interface LineSerie {
335
346
  * the min–max spread of what it aggregates. y may be null for an
336
347
  * honest gap (the line breaks instead of bridging). */
337
348
  data: {
338
- x: string;
349
+ x: string | Date | number;
339
350
  y: number | null;
340
351
  lo?: number;
341
352
  hi?: number;
@@ -348,8 +359,25 @@ interface LineViewProps {
348
359
  /** Pre-shaped Nivo line series bypasses client-side processing. */
349
360
  data?: LineSerie[];
350
361
  style?: Partial<ChartStyle>;
351
- }
352
- declare function LineView({ records, xColumn, yColumn, data: presetData, style }: LineViewProps): react.JSX.Element;
362
+ /** 'point' (default): x values are CATEGORIES, placed at equal spacing
363
+ * in encounter order and ticked by sampling every Nth one — right for
364
+ * labels, wrong for timestamps (a missing bucket collapses, and the
365
+ * ticks land on arbitrary samples). 'time': x values are Dates (or
366
+ * epoch ms / ISO strings) on a continuous scale — gaps stretch, ticks
367
+ * land on ROUND times (d3's time ticks, capped by maxXTicks), and the
368
+ * series need not share buckets. */
369
+ xScale?: 'point' | 'time';
370
+ /** Time axis tick + tooltip formatter. Default: HH:MM:SS, local, 24h. */
371
+ xFormat?: (x: Date) => string;
372
+ /** Time axis: pin the visible domain — a live window keeps a steady
373
+ * width instead of re-fitting to whatever samples exist. Default: fit
374
+ * the data. */
375
+ xDomain?: [Date, Date];
376
+ /** Y-axis tick + tooltip value formatter (e.g. formatBytes for a
377
+ * throughput series). Default: Number(value).toLocaleString(). */
378
+ yFormat?: (value: number) => string;
379
+ }
380
+ declare function LineView({ records, xColumn, yColumn, data: presetData, style, xScale, xFormat, xDomain, yFormat }: LineViewProps): react.JSX.Element;
353
381
 
354
382
  interface SparklineViewProps {
355
383
  /** The series, oldest first. */
@@ -361,10 +389,15 @@ interface SparklineViewProps {
361
389
  /** Show the CURRENT (last) value as a readout over the mark — a trend
362
390
  * shape without its number answers "which way", never "how much". */
363
391
  showValue?: boolean;
364
- /** Formats the readout (e.g. v => `${v.toFixed(1)} ms`). Default: locale. */
392
+ /** Formats the readout (e.g. v => `${v.toFixed(1)} ms`). Default: locale.
393
+ * Prefer yFormat in new code — kept for existing callers. */
365
394
  format?: (v: number) => string;
395
+ /** Same as `format` — named to match LineView/BarView's yFormat prop
396
+ * (e.g. formatBytes for a throughput sparkline). `format` wins if both
397
+ * are given. */
398
+ yFormat?: (v: number) => string;
366
399
  }
367
- declare function SparklineView({ data, height, color, showValue, format }: SparklineViewProps): react.JSX.Element;
400
+ declare function SparklineView({ data, height, color, showValue, format, yFormat }: SparklineViewProps): react.JSX.Element;
368
401
 
369
402
  interface ScatterSerie {
370
403
  id: string;
@@ -661,4 +694,4 @@ interface DataExplorerProps {
661
694
  }
662
695
  declare function DataExplorer({ records, columns, states, activeStateId, profile, dashboard, dashboardId, savedDashboards, loading, analyzing, defaultMode, tabs, onSelectState, onRefreshStates, }: DataExplorerProps): react.JSX.Element;
663
696
 
664
- export { type AggFn, type BarDatum, BarView, type BarViewProps, ChartBuilder, type ChartBuilderColumn, type ChartBuilderProps, type ChartConfig, type ChartField, type ChartFilter, type ChartMargin, type ChartPanel, type ChartStyle, ChartStyleControls, type ChartStyleControlsProps, type ChartType, type ColorMethod, type ColorOptions, type ColorScope, DEFAULT_CHART_STYLE, DEFAULT_SERIES_COLORS, type Dashboard$1 as Dashboard, type DashboardCapabilities, type DashboardContextValue, type DashboardPanel$1 as DashboardPanel, DashboardProvider, type DashboardProviderProps, DashboardRenderer, type DashboardRendererProps, type DashboardTheme, DataExplorer, type DataExplorerProps, HEAT_STATS, type HeatCell, type HeatPlusDatum, type HeatStat, HeatmapPlusView, type HeatmapPlusViewProps, type HeatmapSerie, HeatmapView, type HeatmapViewProps, type InsightConfig, InsightView, type InsightViewProps, LEGEND_ANCHORS, type LegendAnchor, type LegendPosition, type LineSerie, LineView, type LineViewProps, type MetricConfig, MetricView, type MetricViewProps, PanelChart, type PanelChartProps, type PanelInput, type PanelLayout, type PanelRef, type PieDatum, PieView, type PieViewProps, PivotView, type PivotViewProps, type QueryResult, type Row, SERIES_OVERFLOW_COLOR, type SavedDashboard, type ScatterSerie, ScatterView, type ScatterViewProps, SparklineView, type SparklineViewProps, SqlConsole, type SqlConsoleColumn, type SqlConsoleProps, type TitleAlign, aggExpr, aggregate, axisLegend, buildChartSQL, buildNivoTheme, chartSizing, compileWhere, groupBy, heatColor, heatLabelColor, legendConfig, normalizeCells, qIdent, qLit, seriesColor, shapeChartData, thinTicks, useCapabilities, useDashboard, withStyleDefaults };
697
+ export { type AggFn, type BarDatum, BarView, type BarViewProps, ChartBuilder, type ChartBuilderColumn, type ChartBuilderProps, type ChartConfig, type ChartField, type ChartFilter, type ChartMargin, type ChartPanel, type ChartStyle, ChartStyleControls, type ChartStyleControlsProps, type ChartType, type ColorMethod, type ColorOptions, type ColorScope, DEFAULT_CHART_STYLE, DEFAULT_SERIES_COLORS, type Dashboard$1 as Dashboard, type DashboardCapabilities, type DashboardContextValue, type DashboardPanel$1 as DashboardPanel, DashboardProvider, type DashboardProviderProps, DashboardRenderer, type DashboardRendererProps, type DashboardTheme, DataExplorer, type DataExplorerProps, HEAT_STATS, type HeatCell, type HeatPlusDatum, type HeatStat, HeatmapPlusView, type HeatmapPlusViewProps, type HeatmapSerie, HeatmapView, type HeatmapViewProps, type InsightConfig, InsightView, type InsightViewProps, LEGEND_ANCHORS, type LegendAnchor, type LegendPosition, type LineSerie, LineView, type LineViewProps, type MetricConfig, MetricView, type MetricViewProps, PanelChart, type PanelChartProps, type PanelInput, type PanelLayout, type PanelRef, type PieDatum, PieView, type PieViewProps, PivotView, type PivotViewProps, type QueryResult, type Row, SERIES_OVERFLOW_COLOR, type SavedDashboard, type ScatterSerie, ScatterView, type ScatterViewProps, SparklineView, type SparklineViewProps, SqlConsole, type SqlConsoleColumn, type SqlConsoleProps, type TitleAlign, aggExpr, aggregate, axisLegend, buildChartSQL, buildNivoTheme, chartSizing, compileWhere, formatBytes, groupBy, heatColor, heatLabelColor, legendConfig, normalizeCells, qIdent, qLit, seriesColor, shapeChartData, thinTicks, useCapabilities, useDashboard, withStyleDefaults };
package/dist/index.js CHANGED
@@ -428,6 +428,23 @@ var aggregate = (records, column, fn) => {
428
428
  return records.length;
429
429
  }
430
430
  };
431
+
432
+ // src/format.ts
433
+ var BYTE_UNITS = ["KB", "MB", "GB", "TB"];
434
+ function formatBytes(value, perSecond = false) {
435
+ const suffix = perSecond ? "/s" : "";
436
+ if (!Number.isFinite(value)) return `\u2014 B${suffix}`;
437
+ const sign = value < 0 ? "-" : "";
438
+ const abs = Math.abs(value);
439
+ if (abs < 1024) return `${sign}${Math.round(abs)} B${suffix}`;
440
+ let scaled = abs / 1024;
441
+ let unit = 0;
442
+ while (scaled >= 1024 && unit < BYTE_UNITS.length - 1) {
443
+ scaled /= 1024;
444
+ unit++;
445
+ }
446
+ return `${sign}${scaled.toFixed(1)} ${BYTE_UNITS[unit]}${suffix}`;
447
+ }
431
448
  var HEAT_STATS = ["count", "sum", "avg", "min", "max", "median"];
432
449
  var cellKey = (row, col) => `${row}\0${col}`;
433
450
  var normalizePartition = (cells, method, vmin, vmax) => {
@@ -500,8 +517,8 @@ function MetricView({ records, config, value: presetValue }) {
500
517
  function InsightView({ config }) {
501
518
  return /* @__PURE__ */ jsx("div", { className: "h-full p-4 overflow-auto", children: /* @__PURE__ */ jsx("p", { className: "text-sm text-midnight-text-body leading-relaxed whitespace-pre-wrap", children: config.text }) });
502
519
  }
503
- function formatVal(value, s, numeric) {
504
- let str = numeric ? Number(value).toLocaleString() : String(value);
520
+ function formatVal(value, s, numeric, custom) {
521
+ let str = custom ? custom(value) : numeric ? Number(value).toLocaleString() : String(value);
505
522
  if (s.tickTruncate > 0 && str.length > s.tickTruncate) str = `${str.slice(0, s.tickTruncate)}\u2026`;
506
523
  return str;
507
524
  }
@@ -550,7 +567,7 @@ function makeAxis(style, axis, columnName, opts2 = {}) {
550
567
  }
551
568
  if (s.tickWrap) {
552
569
  out.renderTick = (tick) => {
553
- const lines = wrapText(formatVal(tick.value, s, numeric), s.tickWrapWidth);
570
+ const lines = wrapText(formatVal(tick.value, s, numeric, opts2.format), s.tickWrapWidth);
554
571
  const firstDy = isX ? "0" : `${-((lines.length - 1) * 0.55)}em`;
555
572
  return /* @__PURE__ */ jsxs("g", { transform: `translate(${tick.x},${tick.y})`, children: [
556
573
  /* @__PURE__ */ jsx("line", { x2: isX ? 0 : -5, y2: isX ? 5 : 0, style: { stroke: s.textColor, strokeWidth: 1, opacity: 0.3 } }),
@@ -568,13 +585,13 @@ function makeAxis(style, axis, columnName, opts2 = {}) {
568
585
  };
569
586
  } else {
570
587
  out.tickRotation = rotate;
571
- out.format = (v) => formatVal(v, s, numeric);
588
+ out.format = (v) => formatVal(v, s, numeric, opts2.format);
572
589
  }
573
590
  const maxTicks = isX ? s.maxXTicks : s.maxYTicks;
574
591
  if (numeric && maxTicks > 0) out.tickValues = maxTicks;
575
592
  return out;
576
593
  }
577
- function BarView({ records, groupColumn, valueColumn, aggFn = "count", data: presetData, layout = "vertical", style }) {
594
+ function BarView({ records, groupColumn, valueColumn, aggFn = "count", data: presetData, layout = "vertical", style, yFormat }) {
578
595
  const computed = useMemo(() => {
579
596
  if (presetData || !records) return [];
580
597
  const groups = groupBy(records, groupColumn);
@@ -585,8 +602,9 @@ function BarView({ records, groupColumn, valueColumn, aggFn = "count", data: pre
585
602
  const { frameClass, frameStyle, margin } = chartSizing(style, { top: 20, right: 20, bottom: 60, left: 60 });
586
603
  const horizontal = layout === "horizontal";
587
604
  const plotted = horizontal ? [...data].reverse() : data;
588
- const axisBottom = horizontal ? makeAxis(style, "x", valueColumn, { numeric: true }) : makeAxis(style, "x", groupColumn);
589
- const axisLeft = horizontal ? makeAxis(style, "y", groupColumn) : makeAxis(style, "y", valueColumn, { numeric: true });
605
+ const valueFormat = yFormat ? (v) => yFormat(Number(v)) : void 0;
606
+ const axisBottom = horizontal ? makeAxis(style, "x", valueColumn, { numeric: true, format: valueFormat }) : makeAxis(style, "x", groupColumn);
607
+ const axisLeft = horizontal ? makeAxis(style, "y", groupColumn) : makeAxis(style, "y", valueColumn, { numeric: true, format: valueFormat });
590
608
  return /* @__PURE__ */ jsx("div", { className: frameClass, style: frameStyle, children: /* @__PURE__ */ jsx(
591
609
  ResponsiveBar,
592
610
  {
@@ -600,6 +618,7 @@ function BarView({ records, groupColumn, valueColumn, aggFn = "count", data: pre
600
618
  borderColor: { from: "color", modifiers: [["darker", 1.6]] },
601
619
  axisBottom,
602
620
  axisLeft,
621
+ valueFormat,
603
622
  enableLabel: s.barLabels,
604
623
  labelSkipWidth: 12,
605
624
  labelSkipHeight: 12,
@@ -648,7 +667,11 @@ function PieView({ records, groupColumn, data: presetData, style }) {
648
667
  }
649
668
  ) });
650
669
  }
651
- function LineView({ records, xColumn, yColumn, data: presetData, style }) {
670
+ var fmtClock = (d) => d.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", second: "2-digit", hourCycle: "h23" });
671
+ var toDate = (x) => x instanceof Date ? x : new Date(x);
672
+ function LineView({ records, xColumn, yColumn, data: presetData, style, xScale = "point", xFormat, xDomain, yFormat }) {
673
+ const timed = xScale === "time";
674
+ const clock = xFormat ?? fmtClock;
652
675
  const computed = useMemo(() => {
653
676
  if (presetData || !records) return [];
654
677
  const sorted = [...records].filter((r) => r[xColumn] != null && r[yColumn] != null).sort((a, b) => {
@@ -661,15 +684,27 @@ function LineView({ records, xColumn, yColumn, data: presetData, style }) {
661
684
  data: sorted.map((r) => ({ x: String(r[xColumn]), y: Number(r[yColumn]) || 0 }))
662
685
  }];
663
686
  }, [records, xColumn, yColumn, presetData]);
664
- const data = presetData || computed;
687
+ const shaped = presetData || computed;
688
+ const data = useMemo(
689
+ () => timed ? shaped.map((serie) => ({ ...serie, data: serie.data.map((d) => ({ ...d, x: toDate(d.x) })) })) : shaped,
690
+ [shaped, timed]
691
+ );
665
692
  const s = withStyleDefaults(style);
666
693
  const { frameClass, frameStyle, margin } = chartSizing(style, { top: 20, right: 20, bottom: 60, left: 60 });
694
+ const yTickFormat = yFormat ? (v) => yFormat(Number(v)) : void 0;
667
695
  const colorById = new Map(data.map((serie, i) => [
668
696
  serie.id,
669
697
  serie.color ?? (s.seriesColors.length || data.length > 1 ? seriesColor(i, s) : "rgba(96, 165, 250, 0.9)")
670
698
  ]));
671
699
  const longest = data.reduce((a, b) => b.data.length > a.data.length ? b : a, { id: "", data: [] });
672
- const tickValues = thinTicks(longest.data.map((d) => d.x), s.maxXTicks);
700
+ const tickValues = timed ? void 0 : thinTicks(longest.data.map((d) => d.x), s.maxXTicks);
701
+ const axisBottom = (() => {
702
+ const base = makeAxis(style, "x", xColumn);
703
+ if (!timed) return { ...base, ...tickValues ? { tickValues } : {} };
704
+ delete base.renderTick;
705
+ return { ...base, tickRotation: s.xTickRotation, format: clock, ...s.maxXTicks > 0 ? { tickValues: s.maxXTicks } : {} };
706
+ })();
707
+ const nivoXScale = timed ? { type: "time", format: "native", precision: "millisecond", useUTC: false, min: xDomain?.[0] ?? "auto", max: xDomain?.[1] ?? "auto" } : { type: "point" };
673
708
  let yScale = { type: "linear", min: "auto", max: "auto" };
674
709
  if (s.yFromZero) {
675
710
  const dataMax = Math.max(0, ...data.flatMap((serie) => serie.data.map((d) => Math.max(d.y ?? 0, d.hi ?? d.y ?? 0))));
@@ -700,7 +735,8 @@ function LineView({ records, xColumn, yColumn, data: presetData, style }) {
700
735
  {
701
736
  data,
702
737
  margin,
703
- xScale: { type: "point" },
738
+ xScale: nivoXScale,
739
+ xFormat: timed ? (v) => clock(v) : void 0,
704
740
  yScale,
705
741
  curve: "monotoneX",
706
742
  enableArea: s.areaOpacity > 0,
@@ -711,8 +747,9 @@ function LineView({ records, xColumn, yColumn, data: presetData, style }) {
711
747
  pointBorderWidth: 2,
712
748
  pointBorderColor: { from: "serieColor" },
713
749
  enableGridX: false,
714
- axisBottom: { ...makeAxis(style, "x", xColumn), ...tickValues ? { tickValues } : {} },
715
- axisLeft: makeAxis(style, "y", yColumn, { numeric: true }),
750
+ axisBottom,
751
+ axisLeft: makeAxis(style, "y", yColumn, { numeric: true, format: yTickFormat }),
752
+ yFormat: yTickFormat,
716
753
  useMesh: true,
717
754
  layers: ["grid", "markers", "axes", "areas", bandsLayer, linesLayer, "crosshair", "slices", "mesh", "legends"],
718
755
  animate: s.animate,
@@ -729,7 +766,7 @@ function niceCeil(v) {
729
766
  }
730
767
  return 10 * mag;
731
768
  }
732
- function SparklineView({ data, height = 56, color = "#3987e5", showValue = false, format }) {
769
+ function SparklineView({ data, height = 56, color = "#3987e5", showValue = false, format, yFormat }) {
733
770
  const gradientId = useId();
734
771
  const width = 260;
735
772
  const pts = data && data.length ? data : [0];
@@ -739,7 +776,8 @@ function SparklineView({ data, height = 56, color = "#3987e5", showValue = false
739
776
  const line = pts.map((v, i) => `${i === 0 ? "M" : "L"} ${xy(v, i)[0].toFixed(1)} ${xy(v, i)[1].toFixed(1)}`).join(" ");
740
777
  const area = `${line} L ${width} ${height} L 0 ${height} Z`;
741
778
  const current = pts[pts.length - 1];
742
- const readout = format ? format(current) : current.toLocaleString(void 0, { maximumFractionDigits: 1 });
779
+ const fmt = format ?? yFormat;
780
+ const readout = fmt ? fmt(current) : current.toLocaleString(void 0, { maximumFractionDigits: 1 });
743
781
  return /* @__PURE__ */ jsxs("div", { className: "relative", children: [
744
782
  /* @__PURE__ */ jsxs("svg", { viewBox: `0 0 ${width} ${height}`, height, className: "w-full", preserveAspectRatio: "none", children: [
745
783
  /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs("linearGradient", { id: gradientId, x1: "0", y1: "0", x2: "0", y2: "1", children: [
@@ -3043,6 +3081,6 @@ function DataExplorer({
3043
3081
  ] });
3044
3082
  }
3045
3083
 
3046
- export { BarView, ChartBuilder, ChartStyleControls, DEFAULT_CHART_STYLE, DEFAULT_SERIES_COLORS, DashboardProvider, DashboardRenderer, DataExplorer, HEAT_STATS, HeatmapPlusView, HeatmapView, InsightView, LEGEND_ANCHORS, LineView, MetricView, PanelChart, PieView, PivotView, SERIES_OVERFLOW_COLOR, ScatterView, SparklineView, SqlConsole, aggExpr, aggregate, axisLegend, buildChartSQL, buildNivoTheme, chartSizing, compileWhere, groupBy, heatColor, heatLabelColor, legendConfig, normalizeCells, qIdent, qLit, seriesColor, shapeChartData, thinTicks, useCapabilities, useDashboard, withStyleDefaults };
3084
+ export { BarView, ChartBuilder, ChartStyleControls, DEFAULT_CHART_STYLE, DEFAULT_SERIES_COLORS, DashboardProvider, DashboardRenderer, DataExplorer, HEAT_STATS, HeatmapPlusView, HeatmapView, InsightView, LEGEND_ANCHORS, LineView, MetricView, PanelChart, PieView, PivotView, SERIES_OVERFLOW_COLOR, ScatterView, SparklineView, SqlConsole, aggExpr, aggregate, axisLegend, buildChartSQL, buildNivoTheme, chartSizing, compileWhere, formatBytes, groupBy, heatColor, heatLabelColor, legendConfig, normalizeCells, qIdent, qLit, seriesColor, shapeChartData, thinTicks, useCapabilities, useDashboard, withStyleDefaults };
3047
3085
  //# sourceMappingURL=index.js.map
3048
3086
  //# sourceMappingURL=index.js.map