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.
- package/README.md +8 -8
- package/dist/charts/area.js +8 -36
- package/dist/charts/bar.js +8 -26
- package/dist/charts/bubble.js +9 -7
- package/dist/charts/combo.js +17 -39
- package/dist/charts/dumbbell.js +15 -14
- package/dist/charts/funnel.js +12 -7
- package/dist/charts/heatmap.js +8 -6
- package/dist/charts/line.js +6 -27
- package/dist/charts/scatter.js +7 -5
- package/dist/cli.js +4 -3
- package/dist/components/big_value.d.ts +23 -1
- package/dist/components/big_value.js +84 -25
- package/dist/components/delta.d.ts +24 -1
- package/dist/components/delta.js +63 -17
- package/dist/components/table-interactivity.d.ts +69 -0
- package/dist/components/table-interactivity.js +216 -0
- package/dist/components/table.d.ts +6 -1
- package/dist/components/table.js +53 -12
- package/dist/core/chart-helpers.d.ts +59 -5
- package/dist/core/chart-helpers.js +84 -5
- package/dist/core/formatting.d.ts +61 -4
- package/dist/core/formatting.js +216 -17
- package/dist/core/lint-rules/registry.d.ts +4 -2
- package/dist/core/lint-rules/registry.js +6 -1
- package/dist/core/lint-rules/rules/index.d.ts +1 -0
- package/dist/core/lint-rules/rules/index.js +1 -0
- package/dist/core/lint-rules/rules/pct-scalar-gt-one.d.ts +13 -0
- package/dist/core/lint-rules/rules/pct-scalar-gt-one.js +46 -0
- package/dist/core/lint-rules/types.d.ts +12 -0
- package/dist/core/linter.d.ts +10 -2
- package/dist/core/linter.js +60 -12
- package/dist/layout/block-loader.d.ts +31 -0
- package/dist/layout/block-loader.js +143 -0
- package/dist/layout/layout-resolver.d.ts +33 -0
- package/dist/layout/layout-resolver.js +73 -0
- package/dist/layout/markdown-parser.d.ts +34 -0
- package/dist/layout/markdown-parser.js +395 -0
- package/dist/layout/parser-types.d.ts +116 -0
- package/dist/layout/parser-types.js +11 -0
- package/dist/layout/parser.d.ts +31 -22
- package/dist/layout/parser.js +118 -1006
- package/dist/layout/renderer.d.ts +33 -0
- package/dist/layout/renderer.js +450 -0
- package/dist/types.d.ts +1 -1
- package/package.json +6 -6
- 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
|
*/
|
package/dist/components/table.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
2
|
+
* Shared helpers for chart builders.
|
|
3
3
|
*
|
|
4
|
-
* Consolidates
|
|
5
|
-
*
|
|
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
|
|
2
|
+
* Shared helpers for chart builders.
|
|
3
3
|
*
|
|
4
|
-
* Consolidates
|
|
5
|
-
*
|
|
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
|