@quantumwake/terminal-ux-dashboard-components 0.1.28 → 0.1.29

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
@@ -335,7 +335,7 @@ interface LineSerie {
335
335
  * the min–max spread of what it aggregates. y may be null for an
336
336
  * honest gap (the line breaks instead of bridging). */
337
337
  data: {
338
- x: string;
338
+ x: string | Date | number;
339
339
  y: number | null;
340
340
  lo?: number;
341
341
  hi?: number;
@@ -348,8 +348,22 @@ interface LineViewProps {
348
348
  /** Pre-shaped Nivo line series bypasses client-side processing. */
349
349
  data?: LineSerie[];
350
350
  style?: Partial<ChartStyle>;
351
- }
352
- declare function LineView({ records, xColumn, yColumn, data: presetData, style }: LineViewProps): react.JSX.Element;
351
+ /** 'point' (default): x values are CATEGORIES, placed at equal spacing
352
+ * in encounter order and ticked by sampling every Nth one — right for
353
+ * labels, wrong for timestamps (a missing bucket collapses, and the
354
+ * ticks land on arbitrary samples). 'time': x values are Dates (or
355
+ * epoch ms / ISO strings) on a continuous scale — gaps stretch, ticks
356
+ * land on ROUND times (d3's time ticks, capped by maxXTicks), and the
357
+ * series need not share buckets. */
358
+ xScale?: 'point' | 'time';
359
+ /** Time axis tick + tooltip formatter. Default: HH:MM:SS, local, 24h. */
360
+ xFormat?: (x: Date) => string;
361
+ /** Time axis: pin the visible domain — a live window keeps a steady
362
+ * width instead of re-fitting to whatever samples exist. Default: fit
363
+ * the data. */
364
+ xDomain?: [Date, Date];
365
+ }
366
+ declare function LineView({ records, xColumn, yColumn, data: presetData, style, xScale, xFormat, xDomain }: LineViewProps): react.JSX.Element;
353
367
 
354
368
  interface SparklineViewProps {
355
369
  /** The series, oldest first. */
package/dist/index.d.ts CHANGED
@@ -335,7 +335,7 @@ interface LineSerie {
335
335
  * the min–max spread of what it aggregates. y may be null for an
336
336
  * honest gap (the line breaks instead of bridging). */
337
337
  data: {
338
- x: string;
338
+ x: string | Date | number;
339
339
  y: number | null;
340
340
  lo?: number;
341
341
  hi?: number;
@@ -348,8 +348,22 @@ interface LineViewProps {
348
348
  /** Pre-shaped Nivo line series bypasses client-side processing. */
349
349
  data?: LineSerie[];
350
350
  style?: Partial<ChartStyle>;
351
- }
352
- declare function LineView({ records, xColumn, yColumn, data: presetData, style }: LineViewProps): react.JSX.Element;
351
+ /** 'point' (default): x values are CATEGORIES, placed at equal spacing
352
+ * in encounter order and ticked by sampling every Nth one — right for
353
+ * labels, wrong for timestamps (a missing bucket collapses, and the
354
+ * ticks land on arbitrary samples). 'time': x values are Dates (or
355
+ * epoch ms / ISO strings) on a continuous scale — gaps stretch, ticks
356
+ * land on ROUND times (d3's time ticks, capped by maxXTicks), and the
357
+ * series need not share buckets. */
358
+ xScale?: 'point' | 'time';
359
+ /** Time axis tick + tooltip formatter. Default: HH:MM:SS, local, 24h. */
360
+ xFormat?: (x: Date) => string;
361
+ /** Time axis: pin the visible domain — a live window keeps a steady
362
+ * width instead of re-fitting to whatever samples exist. Default: fit
363
+ * the data. */
364
+ xDomain?: [Date, Date];
365
+ }
366
+ declare function LineView({ records, xColumn, yColumn, data: presetData, style, xScale, xFormat, xDomain }: LineViewProps): react.JSX.Element;
353
367
 
354
368
  interface SparklineViewProps {
355
369
  /** The series, oldest first. */
package/dist/index.js CHANGED
@@ -648,7 +648,11 @@ function PieView({ records, groupColumn, data: presetData, style }) {
648
648
  }
649
649
  ) });
650
650
  }
651
- function LineView({ records, xColumn, yColumn, data: presetData, style }) {
651
+ var fmtClock = (d) => d.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", second: "2-digit", hourCycle: "h23" });
652
+ var toDate = (x) => x instanceof Date ? x : new Date(x);
653
+ function LineView({ records, xColumn, yColumn, data: presetData, style, xScale = "point", xFormat, xDomain }) {
654
+ const timed = xScale === "time";
655
+ const clock = xFormat ?? fmtClock;
652
656
  const computed = useMemo(() => {
653
657
  if (presetData || !records) return [];
654
658
  const sorted = [...records].filter((r) => r[xColumn] != null && r[yColumn] != null).sort((a, b) => {
@@ -661,7 +665,11 @@ function LineView({ records, xColumn, yColumn, data: presetData, style }) {
661
665
  data: sorted.map((r) => ({ x: String(r[xColumn]), y: Number(r[yColumn]) || 0 }))
662
666
  }];
663
667
  }, [records, xColumn, yColumn, presetData]);
664
- const data = presetData || computed;
668
+ const shaped = presetData || computed;
669
+ const data = useMemo(
670
+ () => timed ? shaped.map((serie) => ({ ...serie, data: serie.data.map((d) => ({ ...d, x: toDate(d.x) })) })) : shaped,
671
+ [shaped, timed]
672
+ );
665
673
  const s = withStyleDefaults(style);
666
674
  const { frameClass, frameStyle, margin } = chartSizing(style, { top: 20, right: 20, bottom: 60, left: 60 });
667
675
  const colorById = new Map(data.map((serie, i) => [
@@ -669,7 +677,14 @@ function LineView({ records, xColumn, yColumn, data: presetData, style }) {
669
677
  serie.color ?? (s.seriesColors.length || data.length > 1 ? seriesColor(i, s) : "rgba(96, 165, 250, 0.9)")
670
678
  ]));
671
679
  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);
680
+ const tickValues = timed ? void 0 : thinTicks(longest.data.map((d) => d.x), s.maxXTicks);
681
+ const axisBottom = (() => {
682
+ const base = makeAxis(style, "x", xColumn);
683
+ if (!timed) return { ...base, ...tickValues ? { tickValues } : {} };
684
+ delete base.renderTick;
685
+ return { ...base, tickRotation: s.xTickRotation, format: clock, ...s.maxXTicks > 0 ? { tickValues: s.maxXTicks } : {} };
686
+ })();
687
+ const nivoXScale = timed ? { type: "time", format: "native", precision: "millisecond", useUTC: false, min: xDomain?.[0] ?? "auto", max: xDomain?.[1] ?? "auto" } : { type: "point" };
673
688
  let yScale = { type: "linear", min: "auto", max: "auto" };
674
689
  if (s.yFromZero) {
675
690
  const dataMax = Math.max(0, ...data.flatMap((serie) => serie.data.map((d) => Math.max(d.y ?? 0, d.hi ?? d.y ?? 0))));
@@ -700,7 +715,8 @@ function LineView({ records, xColumn, yColumn, data: presetData, style }) {
700
715
  {
701
716
  data,
702
717
  margin,
703
- xScale: { type: "point" },
718
+ xScale: nivoXScale,
719
+ xFormat: timed ? (v) => clock(v) : void 0,
704
720
  yScale,
705
721
  curve: "monotoneX",
706
722
  enableArea: s.areaOpacity > 0,
@@ -711,7 +727,7 @@ function LineView({ records, xColumn, yColumn, data: presetData, style }) {
711
727
  pointBorderWidth: 2,
712
728
  pointBorderColor: { from: "serieColor" },
713
729
  enableGridX: false,
714
- axisBottom: { ...makeAxis(style, "x", xColumn), ...tickValues ? { tickValues } : {} },
730
+ axisBottom,
715
731
  axisLeft: makeAxis(style, "y", yColumn, { numeric: true }),
716
732
  useMesh: true,
717
733
  layers: ["grid", "markers", "axes", "areas", bandsLayer, linesLayer, "crosshair", "slices", "mesh", "legends"],