mviz 1.6.4 → 1.6.7

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.
Files changed (47) hide show
  1. package/README.md +8 -8
  2. package/dist/charts/area.js +8 -36
  3. package/dist/charts/bar.js +8 -26
  4. package/dist/charts/bubble.js +9 -7
  5. package/dist/charts/combo.js +17 -39
  6. package/dist/charts/dumbbell.js +15 -14
  7. package/dist/charts/funnel.js +12 -7
  8. package/dist/charts/heatmap.js +8 -6
  9. package/dist/charts/line.js +6 -27
  10. package/dist/charts/scatter.js +7 -5
  11. package/dist/cli.js +4 -3
  12. package/dist/components/big_value.d.ts +23 -1
  13. package/dist/components/big_value.js +84 -25
  14. package/dist/components/delta.d.ts +24 -1
  15. package/dist/components/delta.js +63 -17
  16. package/dist/components/table-interactivity.d.ts +69 -0
  17. package/dist/components/table-interactivity.js +216 -0
  18. package/dist/components/table.d.ts +6 -1
  19. package/dist/components/table.js +53 -12
  20. package/dist/core/chart-helpers.d.ts +59 -5
  21. package/dist/core/chart-helpers.js +84 -5
  22. package/dist/core/formatting.d.ts +61 -4
  23. package/dist/core/formatting.js +216 -17
  24. package/dist/core/lint-rules/registry.d.ts +4 -2
  25. package/dist/core/lint-rules/registry.js +6 -1
  26. package/dist/core/lint-rules/rules/index.d.ts +1 -0
  27. package/dist/core/lint-rules/rules/index.js +1 -0
  28. package/dist/core/lint-rules/rules/pct-scalar-gt-one.d.ts +13 -0
  29. package/dist/core/lint-rules/rules/pct-scalar-gt-one.js +46 -0
  30. package/dist/core/lint-rules/types.d.ts +12 -0
  31. package/dist/core/linter.d.ts +10 -2
  32. package/dist/core/linter.js +60 -12
  33. package/dist/layout/block-loader.d.ts +31 -0
  34. package/dist/layout/block-loader.js +143 -0
  35. package/dist/layout/layout-resolver.d.ts +33 -0
  36. package/dist/layout/layout-resolver.js +73 -0
  37. package/dist/layout/markdown-parser.d.ts +34 -0
  38. package/dist/layout/markdown-parser.js +395 -0
  39. package/dist/layout/parser-types.d.ts +116 -0
  40. package/dist/layout/parser-types.js +11 -0
  41. package/dist/layout/parser.d.ts +31 -22
  42. package/dist/layout/parser.js +118 -1006
  43. package/dist/layout/renderer.d.ts +33 -0
  44. package/dist/layout/renderer.js +450 -0
  45. package/dist/types.d.ts +1 -1
  46. package/package.json +6 -6
  47. package/schema/mviz.v1.schema.json +402 -33
@@ -65,6 +65,11 @@ export declare function computeHeatmapRanges(columns: ColumnDef[], data: Record<
65
65
  min: number;
66
66
  max: number;
67
67
  }>;
68
+ /**
69
+ * Compute per-column pctMultiply for pct-formatted columns. A column maps to `false` when
70
+ * its values are already in percent units (max |v| > 1); otherwise defaults to `true`.
71
+ */
72
+ export declare function computePctMultiplyRanges(columns: ColumnDef[], data: Record<string, unknown>[]): Map<string, boolean>;
68
73
  /**
69
74
  * Compute min/max ranges for dumbbell sparkline columns
70
75
  */
@@ -81,7 +86,7 @@ export declare function formatCell(value: unknown, colDef: ColumnDef, themeColor
81
86
  }>, dumbbellRanges: Map<string, {
82
87
  min: number;
83
88
  max: number;
84
- }>, defaultHeatmapColors: string[], customPalette?: readonly string[], currency?: CurrencyConfig): CellResult;
89
+ }>, defaultHeatmapColors: string[], customPalette?: readonly string[], currency?: CurrencyConfig, pctMultiplyByColumn?: Map<string, boolean>): CellResult;
85
90
  /**
86
91
  * Generate a styled data table
87
92
  */
@@ -2,9 +2,10 @@
2
2
  * Data table component
3
3
  */
4
4
  import { COLORS, PALETTE, FONT_STACK, SPARKLINE_WIDTH, SPARKLINE_HEIGHT, DUMBBELL_DOT_RADIUS, getThemeColors, getHeatmapColors, getSparklineColors, } from '../core/themes.js';
5
- import { formatNumber, inferFormat, resolveCurrency } from '../core/formatting.js';
5
+ import { formatNumber, inferFormat, resolveCurrency, shouldPctMultiply } from '../core/formatting.js';
6
6
  import { calculateHeatmapColorWithContrast } from '../core/colors.js';
7
7
  import { registerComponent } from './registry.js';
8
+ import { parseTableInteractivity, sortableHeaderAttrs, cellSortAttr, buildTableChrome, sharedTableScript, sharedTableCss, tableInitCall, } from './table-interactivity.js';
8
9
  /**
9
10
  * Extract raw value and style overrides from cell data
10
11
  */
@@ -269,6 +270,26 @@ export function computeHeatmapRanges(columns, data) {
269
270
  }
270
271
  return ranges;
271
272
  }
273
+ /**
274
+ * Compute per-column pctMultiply for pct-formatted columns. A column maps to `false` when
275
+ * its values are already in percent units (max |v| > 1); otherwise defaults to `true`.
276
+ */
277
+ export function computePctMultiplyRanges(columns, data) {
278
+ const map = new Map();
279
+ for (const col of columns) {
280
+ const fmt = col.fmt;
281
+ if (fmt !== 'pct' && fmt !== 'pct0' && fmt !== 'pct1')
282
+ continue;
283
+ const values = [];
284
+ for (const row of data) {
285
+ const { value: rawVal } = extractCellValue(row[col.id], col);
286
+ if (typeof rawVal === 'number')
287
+ values.push(rawVal);
288
+ }
289
+ map.set(col.id, shouldPctMultiply(fmt, values));
290
+ }
291
+ return map;
292
+ }
272
293
  /**
273
294
  * Compute min/max ranges for dumbbell sparkline columns
274
295
  */
@@ -334,7 +355,7 @@ function formatSparklineCell(rawValue, colDef, colId, themeColors, dumbbellRange
334
355
  /**
335
356
  * Format a cell value and return content with optional styling
336
357
  */
337
- export function formatCell(value, colDef, themeColors, heatmapRanges, dumbbellRanges, defaultHeatmapColors, customPalette, currency) {
358
+ export function formatCell(value, colDef, themeColors, heatmapRanges, dumbbellRanges, defaultHeatmapColors, customPalette, currency, pctMultiplyByColumn) {
338
359
  const colType = colDef.type;
339
360
  const colId = colDef.id;
340
361
  const palette = customPalette ?? PALETTE;
@@ -370,7 +391,8 @@ export function formatCell(value, colDef, themeColors, heatmapRanges, dumbbellRa
370
391
  formatted = rawValue;
371
392
  }
372
393
  else {
373
- formatted = formatNumber(rawValue, fmt, false, currency);
394
+ const pctMultiply = pctMultiplyByColumn?.get(colId) ?? true;
395
+ formatted = formatNumber(rawValue, fmt, false, currency, pctMultiply);
374
396
  }
375
397
  // Apply bold/italic styling
376
398
  if (styles.bold) {
@@ -381,6 +403,7 @@ export function formatCell(value, colDef, themeColors, heatmapRanges, dumbbellRa
381
403
  }
382
404
  return { content: formatted, bgColor, textColor };
383
405
  }
406
+ let tableIdCounter = 0;
384
407
  /**
385
408
  * Parse and validate table specification
386
409
  */
@@ -406,8 +429,11 @@ function parseTableConfig(spec) {
406
429
  stripeBg: colors.border + (theme === 'light' ? '30' : '40'),
407
430
  heatmapRanges: computeHeatmapRanges(columns, data),
408
431
  dumbbellRanges: computeDumbbellRanges(columns, data),
432
+ pctMultiplyByColumn: computePctMultiplyRanges(columns, data),
409
433
  defaultHeatmapColors: getHeatmapColors(theme),
410
434
  currency: resolveCurrency(spec.currency),
435
+ interactivity: parseTableInteractivity(spec),
436
+ tableId: spec.id ?? `mviz-table-${++tableIdCounter}`,
411
437
  };
412
438
  }
413
439
  /**
@@ -426,17 +452,22 @@ function buildHeaderRow(cfg, inlineStyles = false) {
426
452
  cells.push('<th class="row-num">#</th>');
427
453
  }
428
454
  }
429
- for (const col of cfg.columns) {
455
+ const visualOffset = cfg.showRowNumbers ? 1 : 0;
456
+ for (let i = 0; i < cfg.columns.length; i++) {
457
+ const col = cfg.columns[i];
430
458
  const colTitle = col.title ?? col.id;
431
459
  const align = col.align ?? 'left';
460
+ const sortIdx = i + visualOffset;
461
+ const sortAttrs = sortableHeaderAttrs(sortIdx, cfg.interactivity.sortable);
462
+ const sortInd = cfg.interactivity.sortable ? '<span class="mviz-sort-ind"></span>' : '';
432
463
  if (inlineStyles) {
433
- cells.push(`<th style="font-family: ${FONT_STACK}; font-size: 10px; font-weight: bold; ` +
464
+ cells.push(`<th${sortAttrs} style="font-family: ${FONT_STACK}; font-size: 10px; font-weight: bold; ` +
434
465
  `color: ${cfg.colors.textSecondary}; text-transform: uppercase; ` +
435
466
  `letter-spacing: 0.03em; padding: ${cfg.padding}; ` +
436
- `border-bottom: 1px solid ${cfg.colors.text}; text-align: ${align};">${colTitle}</th>`);
467
+ `border-bottom: 1px solid ${cfg.colors.text}; text-align: ${align};">${colTitle}${sortInd}</th>`);
437
468
  }
438
469
  else {
439
- cells.push(`<th style="text-align: ${align};">${colTitle}</th>`);
470
+ cells.push(`<th${sortAttrs} style="text-align: ${align};">${colTitle}${sortInd}</th>`);
440
471
  }
441
472
  }
442
473
  return `<tr>${cells.join('')}</tr>`;
@@ -464,18 +495,21 @@ function buildDataRows(cfg, inlineStyles = false) {
464
495
  for (const col of cfg.columns) {
465
496
  const value = rowData[col.id];
466
497
  const align = col.align ?? 'left';
467
- const cellResult = formatCell(value, col, cfg.colors, cfg.heatmapRanges, cfg.dumbbellRanges, cfg.defaultHeatmapColors, undefined, cfg.currency);
498
+ const cellResult = formatCell(value, col, cfg.colors, cfg.heatmapRanges, cfg.dumbbellRanges, cfg.defaultHeatmapColors, undefined, cfg.currency, cfg.pctMultiplyByColumn);
499
+ // Unwrap `{value, bold, italic}` overrides so numeric sort sees the real value.
500
+ const sortRaw = extractCellValue(value, col).value;
501
+ const sortAttr = cfg.interactivity.sortable ? cellSortAttr(sortRaw, col.fmt) : '';
468
502
  if (inlineStyles) {
469
503
  const border = isLast ? `border-bottom: 1px solid ${cfg.colors.text};` : '';
470
504
  const cellBg = cellResult.bgColor ?? rowBg;
471
505
  const cellText = cellResult.textColor ?? cfg.colors.text;
472
- cells.push(`<td style="padding: ${cfg.padding}; color: ${cellText}; ` +
506
+ cells.push(`<td${sortAttr} style="padding: ${cfg.padding}; color: ${cellText}; ` +
473
507
  `${border} text-align: ${align}; background: ${cellBg};">${cellResult.content}</td>`);
474
508
  }
475
509
  else {
476
510
  const bgStyle = cellResult.bgColor ? ` background-color: ${cellResult.bgColor};` : '';
477
511
  const textStyle = cellResult.textColor ? ` color: ${cellResult.textColor};` : '';
478
- cells.push(`<td style="text-align: ${align};${bgStyle}${textStyle}">${cellResult.content}</td>`);
512
+ cells.push(`<td${sortAttr} style="text-align: ${align};${bgStyle}${textStyle}">${cellResult.content}</td>`);
479
513
  }
480
514
  }
481
515
  if (inlineStyles) {
@@ -556,9 +590,11 @@ function renderEmbeddedTable(cfg) {
556
590
  const tableClass = 'data-table' + (cfg.striped ? ' table-striped' : '') + (cfg.compact ? ' table-compact' : '');
557
591
  const headerRow = buildHeaderRow(cfg, true);
558
592
  const dataRows = buildDataRows(cfg, true);
593
+ const chrome = buildTableChrome(cfg.tableId, cfg.interactivity, cfg.colors);
559
594
  return `<div style="background: ${cfg.colors.background}; font-family: ${FONT_STACK};">
560
595
  ${titleHtml}
561
- <table class="${tableClass}" style="width: 100%; border-collapse: collapse; font-size: 12px;">
596
+ ${chrome.toolbarHtml}
597
+ <table id="${cfg.tableId}" class="${tableClass}" style="width: 100%; border-collapse: collapse; font-size: 12px;">
562
598
  <thead>${headerRow}</thead>
563
599
  <tbody>${dataRows.join('')}</tbody>
564
600
  </table>
@@ -572,6 +608,7 @@ function renderStandaloneTable(cfg) {
572
608
  const tableClass = 'data-table' + (cfg.striped ? ' table-striped' : '') + (cfg.compact ? ' table-compact' : '');
573
609
  const headerRow = buildHeaderRow(cfg, false);
574
610
  const dataRows = buildDataRows(cfg, false);
611
+ const chrome = buildTableChrome(cfg.tableId, cfg.interactivity, cfg.colors);
575
612
  return `<!DOCTYPE html>
576
613
  <html lang="en">
577
614
  <head>
@@ -579,18 +616,22 @@ function renderStandaloneTable(cfg) {
579
616
  <title>${cfg.title}</title>
580
617
  <style>
581
618
  ${generateTableCss(cfg)}
619
+ ${sharedTableCss()}
582
620
  </style>
583
621
  </head>
584
622
  <body>
585
623
  <div class="container">
586
624
  <h2>${titleUpper}</h2>
587
- <table class="${tableClass}">
625
+ ${chrome.toolbarHtml}
626
+ <table id="${cfg.tableId}" class="${tableClass}">
588
627
  <thead>${headerRow}</thead>
589
628
  <tbody>${dataRows.join('')}</tbody>
590
629
  </table>
591
630
  </div>
592
631
  <script>
593
632
  ${generateSparklineScript()}
633
+ ${sharedTableScript()}
634
+ ${tableInitCall(cfg.tableId)}
594
635
  </script>
595
636
  </body>
596
637
  </html>`;
@@ -1,10 +1,14 @@
1
1
  /**
2
- * Shared helpers for scatter-family charts (scatter, bubble).
2
+ * Shared helpers for chart builders.
3
3
  *
4
- * Consolidates series-grouping, label config, and legend patterns
5
- * that were duplicated across scatter.ts and bubble.ts.
4
+ * Consolidates patterns that were duplicated across multiple chart builders:
5
+ * - scatter-family series grouping/labels (scatter, bubble)
6
+ * - value-axis format context (bar, line, area, combo)
7
+ * - x-axis label config (line, area, combo, bar)
8
+ * - yMin/yMax application (bar, line, area, combo)
9
+ * - multi-series legend (bar, area, combo)
6
10
  */
7
- import type { DataPoint, ThemeColors } from '../types.js';
11
+ import type { ChartSpec, CurrencyConfig, DataPoint, FormatType, ThemeColors, AxisType } from '../types.js';
8
12
  /**
9
13
  * Build ECharts label config for persistent point labels.
10
14
  * @param labelIndex - Index of the label value in the point array
@@ -15,7 +19,57 @@ export declare function buildPointLabelConfig(labelIndex: number, colors: ThemeC
15
19
  */
16
20
  export declare function groupDataBySeries<T>(data: DataPoint[], seriesField: string, buildPoint: (d: DataPoint) => T): Map<string, T[]>;
17
21
  /**
18
- * Build ECharts legend config for multi-series charts.
22
+ * Build ECharts legend config for scatter-style multi-series charts.
23
+ * Uses small text and no item-width override.
19
24
  */
20
25
  export declare function buildSeriesLegend(legendData: string[], colors: ThemeColors): Record<string, unknown>;
26
+ /**
27
+ * Build ECharts legend config for bar/area/combo style charts.
28
+ * Uses narrow line-style swatches matching the rest of the chart palette.
29
+ */
30
+ export declare function buildMultiSeriesLegend(legendData: string[], colors: ThemeColors): Record<string, unknown>;
31
+ /**
32
+ * Resolved format context for a chart's value axis.
33
+ *
34
+ * Wraps the recurring (format, currency, pctMultiply, formatter) tuple
35
+ * so chart builders don't have to re-derive each piece individually.
36
+ */
37
+ export interface ValueAxisFormatContext {
38
+ valueFormat: FormatType;
39
+ currency: CurrencyConfig;
40
+ pctMultiply: boolean;
41
+ axisFormatter: {
42
+ _js_: string;
43
+ } | null;
44
+ labelFormatter?: {
45
+ _js_: string;
46
+ } | null;
47
+ }
48
+ export interface BuildValueAxisFormatContextOptions {
49
+ /** Override the inferred format (e.g. combo's secondaryFormat). */
50
+ formatOverride?: FormatType;
51
+ /** Also build a label formatter (used by bar). */
52
+ withLabelFormatter?: boolean;
53
+ }
54
+ /**
55
+ * Resolve format/currency/pctMultiply and produce axis (and optionally label)
56
+ * formatters for a chart's value axis.
57
+ *
58
+ * Used by bar, line, area, and combo. Combo calls this twice (once per axis).
59
+ */
60
+ export declare function buildValueAxisFormatContext(spec: ChartSpec, data: DataPoint[], yKeys: string[], opts?: BuildValueAxisFormatContextOptions): ValueAxisFormatContext;
61
+ /**
62
+ * Build an ECharts xAxis label config based on the resolved axis type.
63
+ *
64
+ * - `category`: 45° rotation with truncation for long labels
65
+ * - `time`: month-day formatter ("Jan 5")
66
+ * - `value`: just hideOverlap
67
+ */
68
+ export declare function buildXAxisLabelConfig(xAxisType: AxisType): Record<string, unknown>;
69
+ /**
70
+ * Apply yMin/yMax from a spec onto a value-axis object in place.
71
+ * The caller passes the right axis object (xAxis for horizontal bars,
72
+ * yAxis or yAxis[0] for everything else).
73
+ */
74
+ export declare function applyValueAxisRange(axisObj: Record<string, unknown>, spec: ChartSpec): void;
21
75
  //# sourceMappingURL=chart-helpers.d.ts.map
@@ -1,10 +1,15 @@
1
1
  /**
2
- * Shared helpers for scatter-family charts (scatter, bubble).
2
+ * Shared helpers for chart builders.
3
3
  *
4
- * Consolidates series-grouping, label config, and legend patterns
5
- * that were duplicated across scatter.ts and bubble.ts.
4
+ * Consolidates patterns that were duplicated across multiple chart builders:
5
+ * - scatter-family series grouping/labels (scatter, bubble)
6
+ * - value-axis format context (bar, line, area, combo)
7
+ * - x-axis label config (line, area, combo, bar)
8
+ * - yMin/yMax application (bar, line, area, combo)
9
+ * - multi-series legend (bar, area, combo)
6
10
  */
7
- import { FONT_SIZE_TINY } from './themes.js';
11
+ import { FONT_SIZE_TINY, FONT_SIZE_XXS, LEGEND_ITEM_WIDTH, LEGEND_ITEM_HEIGHT, LEGEND_ITEM_GAP, } from './themes.js';
12
+ import { inferFormat, resolveCurrency, shouldPctMultiply, collectNumericFieldValues, getAxisFormatterJs, getLabelFormatterJs, } from './formatting.js';
8
13
  /**
9
14
  * Build ECharts label config for persistent point labels.
10
15
  * @param labelIndex - Index of the label value in the point array
@@ -33,7 +38,8 @@ export function groupDataBySeries(data, seriesField, buildPoint) {
33
38
  return groups;
34
39
  }
35
40
  /**
36
- * Build ECharts legend config for multi-series charts.
41
+ * Build ECharts legend config for scatter-style multi-series charts.
42
+ * Uses small text and no item-width override.
37
43
  */
38
44
  export function buildSeriesLegend(legendData, colors) {
39
45
  return {
@@ -43,4 +49,77 @@ export function buildSeriesLegend(legendData, colors) {
43
49
  textStyle: { color: colors.textSecondary, fontSize: FONT_SIZE_TINY },
44
50
  };
45
51
  }
52
+ /**
53
+ * Build ECharts legend config for bar/area/combo style charts.
54
+ * Uses narrow line-style swatches matching the rest of the chart palette.
55
+ */
56
+ export function buildMultiSeriesLegend(legendData, colors) {
57
+ return {
58
+ data: legendData,
59
+ top: 0,
60
+ textStyle: { color: colors.textSecondary, fontSize: FONT_SIZE_XXS },
61
+ itemWidth: LEGEND_ITEM_WIDTH,
62
+ itemHeight: LEGEND_ITEM_HEIGHT,
63
+ itemGap: LEGEND_ITEM_GAP,
64
+ };
65
+ }
66
+ /**
67
+ * Resolve format/currency/pctMultiply and produce axis (and optionally label)
68
+ * formatters for a chart's value axis.
69
+ *
70
+ * Used by bar, line, area, and combo. Combo calls this twice (once per axis).
71
+ */
72
+ export function buildValueAxisFormatContext(spec, data, yKeys, opts = {}) {
73
+ const firstYKey = yKeys[0] ?? 'value';
74
+ const sampleValue = data.length > 0 && data[0] ? data[0][firstYKey] ?? 0 : 0;
75
+ const valueFormat = opts.formatOverride ?? spec.format ?? inferFormat(firstYKey, sampleValue);
76
+ const currency = resolveCurrency(spec.currency);
77
+ const pctMultiply = shouldPctMultiply(valueFormat, collectNumericFieldValues(data, yKeys));
78
+ const axisFormatter = getAxisFormatterJs(valueFormat, currency.symbol, currency.locale, pctMultiply);
79
+ const ctx = { valueFormat, currency, pctMultiply, axisFormatter };
80
+ if (opts.withLabelFormatter) {
81
+ ctx.labelFormatter = getLabelFormatterJs(valueFormat, currency.symbol, currency.locale, pctMultiply);
82
+ }
83
+ return ctx;
84
+ }
85
+ /**
86
+ * Month-day formatter used for time-axis labels (e.g. "Jan 5").
87
+ * Extracted to a constant so all chart builders share the same JS source.
88
+ */
89
+ const TIME_AXIS_MONTH_DAY_FORMATTER = {
90
+ _js_: `function(value) {
91
+ var d = new Date(value);
92
+ var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
93
+ return months[d.getMonth()] + ' ' + d.getDate();
94
+ }`,
95
+ };
96
+ /**
97
+ * Build an ECharts xAxis label config based on the resolved axis type.
98
+ *
99
+ * - `category`: 45° rotation with truncation for long labels
100
+ * - `time`: month-day formatter ("Jan 5")
101
+ * - `value`: just hideOverlap
102
+ */
103
+ export function buildXAxisLabelConfig(xAxisType) {
104
+ if (xAxisType === 'category') {
105
+ return { interval: 0, rotate: 45, overflow: 'truncate', ellipsis: '...', width: 100 };
106
+ }
107
+ if (xAxisType === 'time') {
108
+ return { hideOverlap: true, formatter: TIME_AXIS_MONTH_DAY_FORMATTER };
109
+ }
110
+ return { hideOverlap: true };
111
+ }
112
+ /**
113
+ * Apply yMin/yMax from a spec onto a value-axis object in place.
114
+ * The caller passes the right axis object (xAxis for horizontal bars,
115
+ * yAxis or yAxis[0] for everything else).
116
+ */
117
+ export function applyValueAxisRange(axisObj, spec) {
118
+ if (spec.yMin !== undefined) {
119
+ axisObj.min = spec.yMin;
120
+ }
121
+ if (spec.yMax !== undefined) {
122
+ axisObj.max = spec.yMax;
123
+ }
124
+ }
46
125
  //# sourceMappingURL=chart-helpers.js.map
@@ -21,8 +21,65 @@ export declare function localeFixed(value: number, decimals: number, locale: str
21
21
  * Format a number according to the specified format type
22
22
  * @param showSign - If true, prefix positive numbers with +
23
23
  * @param currency - Optional currency config for currency formats (defaults to USD)
24
+ * @param pctMultiply - For pct formats: multiply the value by 100 before formatting.
25
+ * Default true. Set false when the caller has already determined that the series
26
+ * values are in percentage units (via detectPctMultiply).
24
27
  */
25
- export declare function formatNumber(value: number | null | undefined, format?: FormatType, showSign?: boolean, currency?: CurrencyConfig): string;
28
+ export declare function formatNumber(value: number | null | undefined, format?: FormatType, showSign?: boolean, currency?: CurrencyConfig, pctMultiply?: boolean): string;
29
+ /**
30
+ * Convert a cell value to a numeric (or string) sort key based on the
31
+ * declared column format. The result is what the table emits as
32
+ * `data-sort` so the client-side compare always sees comparable values.
33
+ *
34
+ * - `date` → epoch ms (accepts Date, ISO string, or numeric ms)
35
+ * - `duration` → total seconds (accepts number-of-seconds or `1m7s` style)
36
+ * - currency / pct / num / auto → underlying numeric value
37
+ *
38
+ * Returns null when the value can't be coerced for the declared format —
39
+ * the caller falls back to its untyped heuristic chain.
40
+ */
41
+ export declare function formatSortKey(value: unknown, format?: FormatType): number | string | null;
42
+ /**
43
+ * Parse a duration string like `"10s"`, `"1m7s"`, `"25m39s"`, `"1h2m3s"`
44
+ * into total seconds. Returns null when the input doesn't match.
45
+ * Exported for the untyped fallback path in `cellSortAttr`.
46
+ *
47
+ * When `requireSeconds` is true, only strings that contain an `s` segment
48
+ * match — bare `5m` or `1h` are rejected. The untyped fallback uses this
49
+ * stricter form because a bare magnitude like `5m` is ambiguous (5 minutes
50
+ * or 5 million?); the lenient form is for columns that explicitly declare
51
+ * `fmt: "duration"`.
52
+ */
53
+ export declare function parseDurationString(s: string, options?: {
54
+ requireSeconds?: boolean;
55
+ }): number | null;
56
+ /**
57
+ * Parse an ISO-8601 date string (`YYYY-MM-DD` with optional time/timezone)
58
+ * into epoch ms. Returns null for non-ISO strings so they fall back to
59
+ * locale sort instead of mis-parsing through `Date.parse`.
60
+ */
61
+ export declare function parseIsoDateString(s: string): number | null;
62
+ /**
63
+ * Detect whether a series of values needs to be multiplied by 100 for percent formatting.
64
+ * Returns true when the series looks fractional (max |v| <= 1). Returns false when any
65
+ * value exceeds 1, indicating the series is already in percentage units (e.g., [15, 22, 38]).
66
+ *
67
+ * Empty / all-null series default to true (fractional), matching the legacy behavior.
68
+ */
69
+ export declare function detectPctMultiply(values: Array<number | null | undefined>): boolean;
70
+ /**
71
+ * Series-aware pctMultiply for a chart/table column.
72
+ *
73
+ * For non-pct formats, always returns true (no effect — the flag is unused for those).
74
+ * For pct formats, returns false when the series contains a value > 1, so callers can
75
+ * format already-percent data like [15, 22, 38] as 15%, 22%, 38% instead of 1500% etc.
76
+ */
77
+ export declare function shouldPctMultiply(format: FormatType | null | undefined, values: Array<number | null | undefined>): boolean;
78
+ /**
79
+ * Pull numeric values out of a data array for one or more field names.
80
+ * Used by charts to sample a y-series for pct auto-detection.
81
+ */
82
+ export declare function collectNumericFieldValues(data: Array<Record<string, unknown>> | undefined, fields: string[]): number[];
26
83
  /**
27
84
  * Infer format type from field name and sample value
28
85
  */
@@ -50,14 +107,14 @@ export declare function parseNumericString(value: string): number | string;
50
107
  * @param currencySymbol - Symbol to use for currency formats (defaults to '$')
51
108
  * @param locale - Locale for number formatting (defaults to 'en-US')
52
109
  */
53
- export declare function formatValue(fmt: FormatType | null, currencySymbol?: string, locale?: string): string;
110
+ export declare function formatValue(fmt: FormatType | null, currencySymbol?: string, locale?: string, pctMultiply?: boolean): string;
54
111
  /**
55
112
  * Generate an ECharts label formatter function based on format type.
56
113
  * Returns a dict with _js_ key containing the formatter function.
57
114
  * @param currencySymbol - Symbol to use for currency formats (defaults to '$')
58
115
  * @param locale - Locale for number formatting (defaults to 'en-US')
59
116
  */
60
- export declare function getLabelFormatterJs(fmt: FormatType | null | undefined, currencySymbol?: string, locale?: string): {
117
+ export declare function getLabelFormatterJs(fmt: FormatType | null | undefined, currencySymbol?: string, locale?: string, pctMultiply?: boolean): {
61
118
  _js_: string;
62
119
  } | null;
63
120
  /**
@@ -65,7 +122,7 @@ export declare function getLabelFormatterJs(fmt: FormatType | null | undefined,
65
122
  * @param currencySymbol - Symbol to use for currency formats (defaults to '$')
66
123
  * @param locale - Locale for number formatting (defaults to 'en-US')
67
124
  */
68
- export declare function getAxisFormatterJs(fmt: FormatType | null | undefined, currencySymbol?: string, locale?: string): {
125
+ export declare function getAxisFormatterJs(fmt: FormatType | null | undefined, currencySymbol?: string, locale?: string, pctMultiply?: boolean): {
69
126
  _js_: string;
70
127
  } | null;
71
128
  //# sourceMappingURL=formatting.d.ts.map