@classic-homes/charts-core 0.4.1 → 0.5.0
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.cjs +202 -7
- package/dist/index.d.cts +75 -4
- package/dist/index.d.ts +75 -4
- package/dist/index.js +195 -8
- package/dist/types/index.d.cts +36 -0
- package/dist/types/index.d.ts +36 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -424,6 +424,159 @@ function createTooltipFormatter(options = {}) {
|
|
|
424
424
|
};
|
|
425
425
|
}
|
|
426
426
|
|
|
427
|
+
// src/utils/forecast.ts
|
|
428
|
+
var DEFAULT_FORECAST_LABEL = "Forecast";
|
|
429
|
+
function forecastStart(source) {
|
|
430
|
+
const from = source.forecastFrom;
|
|
431
|
+
if (from == null || !Number.isFinite(from)) return void 0;
|
|
432
|
+
const index = Math.max(0, Math.floor(from));
|
|
433
|
+
return index < source.data.length ? index : void 0;
|
|
434
|
+
}
|
|
435
|
+
function hasForecast(series) {
|
|
436
|
+
return !!series?.some((s) => forecastStart(s) !== void 0);
|
|
437
|
+
}
|
|
438
|
+
function forecastTagLookup(series) {
|
|
439
|
+
if (!hasForecast(series)) return void 0;
|
|
440
|
+
return (dataIndex) => {
|
|
441
|
+
for (const s of series) {
|
|
442
|
+
const from = forecastStart(s);
|
|
443
|
+
if (from !== void 0 && dataIndex >= from) return s.forecastLabel || DEFAULT_FORECAST_LABEL;
|
|
444
|
+
}
|
|
445
|
+
return void 0;
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
function describeForecast(categories, series) {
|
|
449
|
+
let first;
|
|
450
|
+
for (const s of series) {
|
|
451
|
+
const from = forecastStart(s);
|
|
452
|
+
if (from === void 0 || first && first.index <= from) continue;
|
|
453
|
+
first = { index: from, label: s.forecastLabel || DEFAULT_FORECAST_LABEL };
|
|
454
|
+
}
|
|
455
|
+
if (!first) return void 0;
|
|
456
|
+
const category = categories[first.index] ?? "";
|
|
457
|
+
return `Values from ${category} onward are marked ${first.label.toLowerCase()}`;
|
|
458
|
+
}
|
|
459
|
+
var isPlainObject = (item) => typeof item === "object" && item !== null && !Array.isArray(item);
|
|
460
|
+
function blank(item) {
|
|
461
|
+
if (Array.isArray(item)) return [item[0], null];
|
|
462
|
+
return null;
|
|
463
|
+
}
|
|
464
|
+
function bare(item) {
|
|
465
|
+
return isPlainObject(item) && "value" in item ? item.value : item;
|
|
466
|
+
}
|
|
467
|
+
function withItem(item, extra) {
|
|
468
|
+
if (isPlainObject(item) && "value" in item) {
|
|
469
|
+
return {
|
|
470
|
+
...item,
|
|
471
|
+
...extra,
|
|
472
|
+
itemStyle: { ...item.itemStyle, ...extra.itemStyle }
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
return { value: item, ...extra };
|
|
476
|
+
}
|
|
477
|
+
function withForecastPoints(data, source, pointSize = 0) {
|
|
478
|
+
const from = forecastStart(source);
|
|
479
|
+
if (from === void 0) return [...data];
|
|
480
|
+
const size = Math.max(pointSize, 6) + 1;
|
|
481
|
+
return data.map(
|
|
482
|
+
(item, index) => index >= from ? withItem(item, { symbol: "emptyCircle", symbolSize: size }) : item
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
function withForecastBars(data, source, color) {
|
|
486
|
+
const from = forecastStart(source);
|
|
487
|
+
if (from === void 0) return [...data];
|
|
488
|
+
return data.map(
|
|
489
|
+
(item, index) => index >= from ? withItem(item, {
|
|
490
|
+
itemStyle: {
|
|
491
|
+
opacity: 0.45,
|
|
492
|
+
borderColor: color,
|
|
493
|
+
borderWidth: 1.5,
|
|
494
|
+
borderType: "dashed"
|
|
495
|
+
}
|
|
496
|
+
}) : item
|
|
497
|
+
);
|
|
498
|
+
}
|
|
499
|
+
function withForecastLines(series, sources, { colors: colors3 }) {
|
|
500
|
+
if (!hasForecast(sources)) return series;
|
|
501
|
+
const stackFrom = /* @__PURE__ */ new Map();
|
|
502
|
+
series.forEach((s, index) => {
|
|
503
|
+
const from = forecastStart(sources[index]);
|
|
504
|
+
if (s.stack == null || from === void 0) return;
|
|
505
|
+
stackFrom.set(s.stack, Math.min(stackFrom.get(s.stack) ?? Infinity, from));
|
|
506
|
+
});
|
|
507
|
+
const mains = [];
|
|
508
|
+
const companions = [];
|
|
509
|
+
series.forEach((s, index) => {
|
|
510
|
+
const from = s.stack != null ? stackFrom.get(s.stack) : forecastStart(sources[index]);
|
|
511
|
+
if (from === void 0) {
|
|
512
|
+
mains.push(s);
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
const color = colors3[index];
|
|
516
|
+
const itemStyle = { ...s.itemStyle, color };
|
|
517
|
+
if (from === 0) {
|
|
518
|
+
mains.push({
|
|
519
|
+
...s,
|
|
520
|
+
itemStyle,
|
|
521
|
+
lineStyle: { ...s.lineStyle, type: "dashed" },
|
|
522
|
+
...s.areaStyle && { areaStyle: fadedArea(s.areaStyle) }
|
|
523
|
+
});
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
const data = s.data ?? [];
|
|
527
|
+
const common = {
|
|
528
|
+
name: s.name,
|
|
529
|
+
type: "line",
|
|
530
|
+
smooth: s.smooth,
|
|
531
|
+
connectNulls: s.connectNulls,
|
|
532
|
+
showSymbol: false,
|
|
533
|
+
symbol: "none",
|
|
534
|
+
silent: true,
|
|
535
|
+
legendHoverLink: false,
|
|
536
|
+
tooltip: { show: false },
|
|
537
|
+
emphasis: { disabled: true },
|
|
538
|
+
itemStyle,
|
|
539
|
+
z: s.z
|
|
540
|
+
};
|
|
541
|
+
companions.push(
|
|
542
|
+
{
|
|
543
|
+
...common,
|
|
544
|
+
data: data.map((item, i) => i < from ? bare(item) : blank(item)),
|
|
545
|
+
stack: s.stack != null ? `${s.stack}\0actual` : void 0,
|
|
546
|
+
lineStyle: s.lineStyle,
|
|
547
|
+
areaStyle: s.areaStyle
|
|
548
|
+
},
|
|
549
|
+
{
|
|
550
|
+
...common,
|
|
551
|
+
data: data.map((item, i) => i >= from - 1 ? bare(item) : blank(item)),
|
|
552
|
+
stack: s.stack != null ? `${s.stack}\0forecast` : void 0,
|
|
553
|
+
lineStyle: { ...s.lineStyle, type: "dashed" },
|
|
554
|
+
areaStyle: s.areaStyle ? fadedArea(s.areaStyle) : void 0
|
|
555
|
+
}
|
|
556
|
+
);
|
|
557
|
+
mains.push({
|
|
558
|
+
...s,
|
|
559
|
+
itemStyle,
|
|
560
|
+
// Forecast points carry their own size; the rest keep theirs (none
|
|
561
|
+
// when the series hid its points)
|
|
562
|
+
showSymbol: true,
|
|
563
|
+
symbolSize: s.showSymbol === false ? 0 : s.symbolSize ?? 4,
|
|
564
|
+
lineStyle: { ...s.lineStyle, opacity: 0 },
|
|
565
|
+
areaStyle: void 0,
|
|
566
|
+
emphasis: {
|
|
567
|
+
...s.emphasis,
|
|
568
|
+
focus: "none",
|
|
569
|
+
lineStyle: { ...s.emphasis?.lineStyle, opacity: 0 }
|
|
570
|
+
},
|
|
571
|
+
z: (s.z ?? 2) + 1
|
|
572
|
+
});
|
|
573
|
+
});
|
|
574
|
+
return [...mains, ...companions];
|
|
575
|
+
}
|
|
576
|
+
function fadedArea(areaStyle) {
|
|
577
|
+
return { ...areaStyle, opacity: (areaStyle.opacity ?? 0.7) * 0.5 };
|
|
578
|
+
}
|
|
579
|
+
|
|
427
580
|
// src/utils/accessibility.ts
|
|
428
581
|
var isNumber = (value) => typeof value === "number" && Number.isFinite(value);
|
|
429
582
|
function seriesList(names) {
|
|
@@ -449,6 +602,8 @@ function describeTrends(title, kind, data, format) {
|
|
|
449
602
|
0
|
|
450
603
|
);
|
|
451
604
|
if (missing > 0) description += `. ${missing} values are missing`;
|
|
605
|
+
const forecast = describeForecast(data.categories, data.series);
|
|
606
|
+
if (forecast) description += `. ${forecast}`;
|
|
452
607
|
return description;
|
|
453
608
|
}
|
|
454
609
|
function generateLineChartDescription(title, data, format) {
|
|
@@ -484,6 +639,8 @@ function generateBarChartDescription(title, data, format) {
|
|
|
484
639
|
if (max && min) {
|
|
485
640
|
description += `. Highest value is ${formatValue(max.value, format)} for ${max.category}. Lowest value is ${formatValue(min.value, format)} for ${min.category}`;
|
|
486
641
|
}
|
|
642
|
+
const forecast = describeForecast(data.categories, data.series);
|
|
643
|
+
if (forecast) description += `. ${forecast}`;
|
|
487
644
|
return description;
|
|
488
645
|
}
|
|
489
646
|
function generateDotPlotDescription(title, data, format) {
|
|
@@ -573,14 +730,39 @@ function generateDataTable(headers, rows) {
|
|
|
573
730
|
}
|
|
574
731
|
|
|
575
732
|
// src/utils/merge.ts
|
|
576
|
-
var ALWAYS_REPLACE = ["
|
|
733
|
+
var ALWAYS_REPLACE = ["graphic"];
|
|
577
734
|
function countComponents(value) {
|
|
578
735
|
if (value == null || value === false) return 0;
|
|
579
736
|
if (Array.isArray(value)) return value.filter((item) => item != null).length;
|
|
580
737
|
return 1;
|
|
581
738
|
}
|
|
739
|
+
function isRecord(value) {
|
|
740
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
741
|
+
}
|
|
742
|
+
function sameShape(a, b) {
|
|
743
|
+
if (!isRecord(a) || !isRecord(b)) return !isRecord(a) && !isRecord(b);
|
|
744
|
+
const keys = Object.keys(a).filter((key) => a[key] !== void 0);
|
|
745
|
+
const other = Object.keys(b).filter((key) => b[key] !== void 0);
|
|
746
|
+
if (keys.length !== other.length) return false;
|
|
747
|
+
return keys.every(
|
|
748
|
+
(key) => key in b && b[key] !== void 0 && (key === "type" || key === "id" ? a[key] === b[key] : sameShape(a[key], b[key]))
|
|
749
|
+
);
|
|
750
|
+
}
|
|
751
|
+
function asList(value) {
|
|
752
|
+
if (value == null || value === false) return [];
|
|
753
|
+
return Array.isArray(value) ? value : [value];
|
|
754
|
+
}
|
|
755
|
+
function sameSeriesShape(prev, next) {
|
|
756
|
+
const before = asList(prev);
|
|
757
|
+
const after = asList(next);
|
|
758
|
+
return before.length === after.length && before.every((item, i) => sameShape(item, after[i]));
|
|
759
|
+
}
|
|
582
760
|
function getReplaceMerge(prev, next, isComponentType) {
|
|
583
761
|
const types = new Set(ALWAYS_REPLACE);
|
|
762
|
+
const prior = prev;
|
|
763
|
+
if (!prior || !sameSeriesShape(prior.series, next.series)) {
|
|
764
|
+
types.add("series");
|
|
765
|
+
}
|
|
584
766
|
if (prev) {
|
|
585
767
|
const before = prev;
|
|
586
768
|
const after = next;
|
|
@@ -592,7 +774,7 @@ function getReplaceMerge(prev, next, isComponentType) {
|
|
|
592
774
|
}
|
|
593
775
|
return [...types].filter((type) => isComponentType(type));
|
|
594
776
|
}
|
|
595
|
-
function
|
|
777
|
+
function isPlainObject2(value) {
|
|
596
778
|
if (value === null || typeof value !== "object") return false;
|
|
597
779
|
const proto = Object.getPrototypeOf(value);
|
|
598
780
|
return proto === Object.prototype || proto === null;
|
|
@@ -605,7 +787,7 @@ function mergeValue(base, override, topLevel = false) {
|
|
|
605
787
|
const length = Math.max(from.length, to.length);
|
|
606
788
|
return Array.from({ length }, (_, index) => mergeValue(from[index], to[index]));
|
|
607
789
|
}
|
|
608
|
-
if (
|
|
790
|
+
if (isPlainObject2(base) && isPlainObject2(override)) {
|
|
609
791
|
const merged = { ...base };
|
|
610
792
|
for (const key of Object.keys(override)) {
|
|
611
793
|
merged[key] = mergeValue(base[key], override[key]);
|
|
@@ -1013,12 +1195,17 @@ function nameLabelLookup(nodes) {
|
|
|
1013
1195
|
function hasPointLabels(series) {
|
|
1014
1196
|
return series.some((s) => s.labels?.some((text) => text != null && text !== ""));
|
|
1015
1197
|
}
|
|
1016
|
-
function buildTooltip(base, { show, formatter, valueFormatter, getLabel }) {
|
|
1198
|
+
function buildTooltip(base, { show, formatter, valueFormatter, getLabel, forecastTag }) {
|
|
1017
1199
|
if (!show) return void 0;
|
|
1018
|
-
if (formatter === void 0 && valueFormatter === void 0) return base;
|
|
1200
|
+
if (formatter === void 0 && valueFormatter === void 0 && !forecastTag) return base;
|
|
1019
1201
|
const tooltip = { ...base };
|
|
1020
|
-
if (valueFormatter) {
|
|
1021
|
-
|
|
1202
|
+
if (valueFormatter || forecastTag) {
|
|
1203
|
+
const format = (value) => typeof value === "number" ? valueFormatter ? valueFormatter(value) : value.toLocaleString() : String(value ?? "");
|
|
1204
|
+
tooltip.valueFormatter = (value, dataIndex) => {
|
|
1205
|
+
const text = format(value);
|
|
1206
|
+
const tag = dataIndex === void 0 ? void 0 : forecastTag?.(dataIndex);
|
|
1207
|
+
return tag && value != null ? `${text} (${tag})` : text;
|
|
1208
|
+
};
|
|
1022
1209
|
}
|
|
1023
1210
|
if (typeof formatter === "string") {
|
|
1024
1211
|
tooltip.formatter = formatter;
|
|
@@ -1562,6 +1749,7 @@ function downloadCsv(table, filename) {
|
|
|
1562
1749
|
);
|
|
1563
1750
|
}
|
|
1564
1751
|
|
|
1752
|
+
exports.DEFAULT_FORECAST_LABEL = DEFAULT_FORECAST_LABEL;
|
|
1565
1753
|
exports.THEME_DARK = THEME_DARK;
|
|
1566
1754
|
exports.THEME_LIGHT = THEME_LIGHT;
|
|
1567
1755
|
exports.abbreviateNumber = abbreviateNumber;
|
|
@@ -1589,9 +1777,12 @@ exports.computeDelta = computeDelta;
|
|
|
1589
1777
|
exports.createTooltipFormatter = createTooltipFormatter;
|
|
1590
1778
|
exports.darkChartColors = darkChartColors;
|
|
1591
1779
|
exports.darkTheme = darkTheme;
|
|
1780
|
+
exports.describeForecast = describeForecast;
|
|
1592
1781
|
exports.downloadCsv = downloadCsv;
|
|
1593
1782
|
exports.downloadFile = downloadFile;
|
|
1594
1783
|
exports.escapeHtml = escapeHtml;
|
|
1784
|
+
exports.forecastStart = forecastStart;
|
|
1785
|
+
exports.forecastTagLookup = forecastTagLookup;
|
|
1595
1786
|
exports.formatCurrency = formatCurrency;
|
|
1596
1787
|
exports.formatDate = formatDate;
|
|
1597
1788
|
exports.formatNumber = formatNumber;
|
|
@@ -1618,6 +1809,7 @@ exports.getChartColors = getChartColors;
|
|
|
1618
1809
|
exports.getLegendItems = getLegendItems;
|
|
1619
1810
|
exports.getReplaceMerge = getReplaceMerge;
|
|
1620
1811
|
exports.gridInset = gridInset;
|
|
1812
|
+
exports.hasForecast = hasForecast;
|
|
1621
1813
|
exports.hasPointLabels = hasPointLabels;
|
|
1622
1814
|
exports.heatmapTable = heatmapTable;
|
|
1623
1815
|
exports.histogramTable = histogramTable;
|
|
@@ -1649,6 +1841,9 @@ exports.valueLabelFormatter = valueLabelFormatter;
|
|
|
1649
1841
|
exports.waterfallLayout = waterfallLayout;
|
|
1650
1842
|
exports.waterfallTable = waterfallTable;
|
|
1651
1843
|
exports.withEndLabelInset = withEndLabelInset;
|
|
1844
|
+
exports.withForecastBars = withForecastBars;
|
|
1845
|
+
exports.withForecastLines = withForecastLines;
|
|
1846
|
+
exports.withForecastPoints = withForecastPoints;
|
|
1652
1847
|
exports.withPointLabels = withPointLabels;
|
|
1653
1848
|
exports.withReferenceMarks = withReferenceMarks;
|
|
1654
1849
|
exports.withZoomInset = withZoomInset;
|
package/dist/index.d.cts
CHANGED
|
@@ -125,8 +125,12 @@ declare function generateDataTable(headers: string[], rows: (string | number)[][
|
|
|
125
125
|
* rebuilds every component, so legend selection, dataZoom ranges and other
|
|
126
126
|
* interaction state reset on each data update. Instead this replaces only:
|
|
127
127
|
*
|
|
128
|
-
* - `
|
|
129
|
-
*
|
|
128
|
+
* - `graphic`, so removed centre labels disappear;
|
|
129
|
+
* - `series`, unless every series keeps its keys and type and only its
|
|
130
|
+
* values change. A replaced series is a new model, which ECharts animates
|
|
131
|
+
* in from scratch, so a data refresh with the same shape (often the same
|
|
132
|
+
* data) must merge to avoid re-animating the chart; a changed shape is
|
|
133
|
+
* replaced so removed series, mark lines and options disappear;
|
|
130
134
|
* - any component type that `next` has fewer of than `prev` (a hidden legend
|
|
131
135
|
* or tooltip, a dropped volume grid and its axes).
|
|
132
136
|
*
|
|
@@ -147,6 +151,71 @@ declare function getReplaceMerge(prev: object | null | undefined, next: object,
|
|
|
147
151
|
*/
|
|
148
152
|
declare function mergeChartOption<T extends object>(generated: T, override: object | undefined): T;
|
|
149
153
|
|
|
154
|
+
/**
|
|
155
|
+
* Forecast stretches: the tail of a series whose values are projections (or a
|
|
156
|
+
* period still in progress, such as the current month). Lines draw it dashed
|
|
157
|
+
* with hollow points, bars draw it faded with a dashed outline, and the
|
|
158
|
+
* tooltip tags its values.
|
|
159
|
+
*/
|
|
160
|
+
type AnyObject$1 = Record<string, any>;
|
|
161
|
+
declare const DEFAULT_FORECAST_LABEL = "Forecast";
|
|
162
|
+
/** The forecast fields a series may carry */
|
|
163
|
+
interface ForecastSource {
|
|
164
|
+
data: readonly unknown[];
|
|
165
|
+
forecastFrom?: number;
|
|
166
|
+
forecastLabel?: string;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Index of a series' first forecast point, or `undefined` when it has none
|
|
170
|
+
* (no `forecastFrom`, or one past the end of the data).
|
|
171
|
+
*/
|
|
172
|
+
declare function forecastStart(source: ForecastSource): number | undefined;
|
|
173
|
+
/** Whether any series has a forecast stretch */
|
|
174
|
+
declare function hasForecast(series: readonly ForecastSource[] | undefined): boolean;
|
|
175
|
+
/**
|
|
176
|
+
* Tooltip tag by data index: the label of the first series forecasting at
|
|
177
|
+
* that index. ECharts' `valueFormatter` only knows the index, so series that
|
|
178
|
+
* share an index share its tag.
|
|
179
|
+
*/
|
|
180
|
+
declare function forecastTagLookup(series: readonly ForecastSource[]): ((dataIndex: number) => string | undefined) | undefined;
|
|
181
|
+
/**
|
|
182
|
+
* The first category where any series' forecast starts, for accessible
|
|
183
|
+
* descriptions
|
|
184
|
+
*/
|
|
185
|
+
declare function describeForecast(categories: readonly string[], series: readonly ForecastSource[]): string | undefined;
|
|
186
|
+
/**
|
|
187
|
+
* Line forecast points: hollow, a little larger than the series' own points.
|
|
188
|
+
* Returns the data unchanged when the series has no forecast.
|
|
189
|
+
*/
|
|
190
|
+
declare function withForecastPoints(data: readonly unknown[], source: ForecastSource,
|
|
191
|
+
/** The series' own point size (0 when its points are hidden) */
|
|
192
|
+
pointSize?: number): unknown[];
|
|
193
|
+
/**
|
|
194
|
+
* Bar forecast items: faded, with a dashed outline in the series colour.
|
|
195
|
+
* Returns the data unchanged when the series has no forecast.
|
|
196
|
+
*/
|
|
197
|
+
declare function withForecastBars(data: readonly unknown[], source: ForecastSource, color: string): unknown[];
|
|
198
|
+
interface ForecastLineOptions {
|
|
199
|
+
/** Resolved colour per series (explicit colour, else the theme palette) */
|
|
200
|
+
colors: readonly string[];
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Draw line series' forecast stretches dashed.
|
|
204
|
+
*
|
|
205
|
+
* ECharts styles a line as a whole, so a series with a forecast keeps its
|
|
206
|
+
* full data for the tooltip, points and labels but hides its own line; two
|
|
207
|
+
* silent companions (same name, so the legend toggles them together) draw
|
|
208
|
+
* it: a solid one up to the last actual point, and a dashed one from there
|
|
209
|
+
* to the end. Series stacked together split at the same index (the earliest
|
|
210
|
+
* forecast in the stack) and stack in parallel groups, so the companions'
|
|
211
|
+
* totals match the series'.
|
|
212
|
+
*
|
|
213
|
+
* Series that forecast from their first point are simply dashed. Returns
|
|
214
|
+
* the series unchanged when none has a forecast. Apply reference marks
|
|
215
|
+
* before this, so they attach to the series rather than the companions.
|
|
216
|
+
*/
|
|
217
|
+
declare function withForecastLines<S extends AnyObject$1>(series: S[], sources: readonly ForecastSource[], { colors }: ForecastLineOptions): AnyObject$1[];
|
|
218
|
+
|
|
150
219
|
/**
|
|
151
220
|
* Builders for the label, axis, tooltip and legend parts of a chart option.
|
|
152
221
|
*
|
|
@@ -264,11 +333,13 @@ interface BuildTooltipOptions {
|
|
|
264
333
|
valueFormatter?: (value: number) => string;
|
|
265
334
|
/** Per-point label lookup, exposed to formatter functions as `label` */
|
|
266
335
|
getLabel?: PointLabelLookup;
|
|
336
|
+
/** Tag for forecast values by data index (see `forecastTagLookup`) */
|
|
337
|
+
forecastTag?: (dataIndex: number) => string | undefined;
|
|
267
338
|
}
|
|
268
339
|
/**
|
|
269
340
|
* Build the `tooltip` component. `base` is the chart's default tooltip.
|
|
270
341
|
*/
|
|
271
|
-
declare function buildTooltip(base: AnyObject, { show, formatter, valueFormatter, getLabel }: BuildTooltipOptions): AnyObject | undefined;
|
|
342
|
+
declare function buildTooltip(base: AnyObject, { show, formatter, valueFormatter, getLabel, forecastTag }: BuildTooltipOptions): AnyObject | undefined;
|
|
272
343
|
/**
|
|
273
344
|
* Format a value with `tooltipValueFormatter`, for charts whose default
|
|
274
345
|
* tooltip is custom HTML (where ECharts' `valueFormatter` does not apply)
|
|
@@ -673,4 +744,4 @@ declare function downloadFile(content: string, filename: string, mimeType?: stri
|
|
|
673
744
|
*/
|
|
674
745
|
declare function downloadCsv(table: ChartDataTable, filename: string): void;
|
|
675
746
|
|
|
676
|
-
export { type Accessor, type Aggregate, type AutoHeightOptions, AxisConfig, BarChartData, type BinValuesOptions, BoxPlotStats, type BuildDataLabelOptions, type BuildLegendOptions, type BuildTooltipOptions, BulletItem, CandlestickData, CategoryChartData, CategorySeriesData, ChartDataTable, ChartFormatter, type ChartFormatterOptions, type ChartFormatters, ChartLegendConfig, type Delta, type FromRecordsOptions, HeatmapData, HistogramBin, HistogramBucket, LabelFormatterParams, LegendItem, LineChartData, PieSliceData, type PointLabelLookup, RadarChartData, RadarIndicator, ReferenceBand, ReferenceLine, type ReferenceMarkOptions, SankeyData, ScatterChartData, type StackTotalOptions, type ToSliceDataOptions, TooltipFormatterParams, TreemapNode, ValueFormat, type WaterfallBar, WaterfallStep, abbreviateNumber, allSeriesEmpty, anchorCenterGraphic, barCornerRadius, binValues, boxPlotTable, boxplotStats, buildAxis, buildDataLabel, buildDataZoom, buildLegend, buildMarkArea, buildMarkLine, buildStackTotalSeries, buildTooltip, bulletTable, candlestickTable, categoryTable, chartFilename, chartFormatters, computeDelta, createTooltipFormatter, downloadCsv, downloadFile, escapeHtml, formatCurrency, formatDate, formatNumber, formatPercent, formatTableCell, formatTooltipValue, formatValue, fromRecords, generateAreaChartDescription, generateBarChartDescription, generateBoxPlotDescription, generateBulletDescription, generateComboChartDescription, generateDataTable, generateDotPlotDescription, generateHistogramDescription, generateLineChartDescription, generatePieChartDescription, generateScatterChartDescription, generateWaterfallDescription, getLegendItems, getReplaceMerge, gridInset, hasPointLabels, heatmapTable, histogramTable, itemLabelLookup, legendCenter, mergeChartOption, nameLabelLookup, niceStep, pairWithCategories, pointLabel, quantile, radarTable, resolveChartHeight, resolveValueFormatter, sankeyTable, scatterTable, seriesLabelLookup, sliceTable, stackTotals, tableToCsv, toPercentStack, toSliceData, treemapTable, trendLine, valueAxisConfig, valueLabelFormatter, waterfallLayout, waterfallTable, withEndLabelInset, withPointLabels, withReferenceMarks, withZoomInset };
|
|
747
|
+
export { type Accessor, type Aggregate, type AutoHeightOptions, AxisConfig, BarChartData, type BinValuesOptions, BoxPlotStats, type BuildDataLabelOptions, type BuildLegendOptions, type BuildTooltipOptions, BulletItem, CandlestickData, CategoryChartData, CategorySeriesData, ChartDataTable, ChartFormatter, type ChartFormatterOptions, type ChartFormatters, ChartLegendConfig, DEFAULT_FORECAST_LABEL, type Delta, type ForecastLineOptions, type ForecastSource, type FromRecordsOptions, HeatmapData, HistogramBin, HistogramBucket, LabelFormatterParams, LegendItem, LineChartData, PieSliceData, type PointLabelLookup, RadarChartData, RadarIndicator, ReferenceBand, ReferenceLine, type ReferenceMarkOptions, SankeyData, ScatterChartData, type StackTotalOptions, type ToSliceDataOptions, TooltipFormatterParams, TreemapNode, ValueFormat, type WaterfallBar, WaterfallStep, abbreviateNumber, allSeriesEmpty, anchorCenterGraphic, barCornerRadius, binValues, boxPlotTable, boxplotStats, buildAxis, buildDataLabel, buildDataZoom, buildLegend, buildMarkArea, buildMarkLine, buildStackTotalSeries, buildTooltip, bulletTable, candlestickTable, categoryTable, chartFilename, chartFormatters, computeDelta, createTooltipFormatter, describeForecast, downloadCsv, downloadFile, escapeHtml, forecastStart, forecastTagLookup, formatCurrency, formatDate, formatNumber, formatPercent, formatTableCell, formatTooltipValue, formatValue, fromRecords, generateAreaChartDescription, generateBarChartDescription, generateBoxPlotDescription, generateBulletDescription, generateComboChartDescription, generateDataTable, generateDotPlotDescription, generateHistogramDescription, generateLineChartDescription, generatePieChartDescription, generateScatterChartDescription, generateWaterfallDescription, getLegendItems, getReplaceMerge, gridInset, hasForecast, hasPointLabels, heatmapTable, histogramTable, itemLabelLookup, legendCenter, mergeChartOption, nameLabelLookup, niceStep, pairWithCategories, pointLabel, quantile, radarTable, resolveChartHeight, resolveValueFormatter, sankeyTable, scatterTable, seriesLabelLookup, sliceTable, stackTotals, tableToCsv, toPercentStack, toSliceData, treemapTable, trendLine, valueAxisConfig, valueLabelFormatter, waterfallLayout, waterfallTable, withEndLabelInset, withForecastBars, withForecastLines, withForecastPoints, withPointLabels, withReferenceMarks, withZoomInset };
|
package/dist/index.d.ts
CHANGED
|
@@ -125,8 +125,12 @@ declare function generateDataTable(headers: string[], rows: (string | number)[][
|
|
|
125
125
|
* rebuilds every component, so legend selection, dataZoom ranges and other
|
|
126
126
|
* interaction state reset on each data update. Instead this replaces only:
|
|
127
127
|
*
|
|
128
|
-
* - `
|
|
129
|
-
*
|
|
128
|
+
* - `graphic`, so removed centre labels disappear;
|
|
129
|
+
* - `series`, unless every series keeps its keys and type and only its
|
|
130
|
+
* values change. A replaced series is a new model, which ECharts animates
|
|
131
|
+
* in from scratch, so a data refresh with the same shape (often the same
|
|
132
|
+
* data) must merge to avoid re-animating the chart; a changed shape is
|
|
133
|
+
* replaced so removed series, mark lines and options disappear;
|
|
130
134
|
* - any component type that `next` has fewer of than `prev` (a hidden legend
|
|
131
135
|
* or tooltip, a dropped volume grid and its axes).
|
|
132
136
|
*
|
|
@@ -147,6 +151,71 @@ declare function getReplaceMerge(prev: object | null | undefined, next: object,
|
|
|
147
151
|
*/
|
|
148
152
|
declare function mergeChartOption<T extends object>(generated: T, override: object | undefined): T;
|
|
149
153
|
|
|
154
|
+
/**
|
|
155
|
+
* Forecast stretches: the tail of a series whose values are projections (or a
|
|
156
|
+
* period still in progress, such as the current month). Lines draw it dashed
|
|
157
|
+
* with hollow points, bars draw it faded with a dashed outline, and the
|
|
158
|
+
* tooltip tags its values.
|
|
159
|
+
*/
|
|
160
|
+
type AnyObject$1 = Record<string, any>;
|
|
161
|
+
declare const DEFAULT_FORECAST_LABEL = "Forecast";
|
|
162
|
+
/** The forecast fields a series may carry */
|
|
163
|
+
interface ForecastSource {
|
|
164
|
+
data: readonly unknown[];
|
|
165
|
+
forecastFrom?: number;
|
|
166
|
+
forecastLabel?: string;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Index of a series' first forecast point, or `undefined` when it has none
|
|
170
|
+
* (no `forecastFrom`, or one past the end of the data).
|
|
171
|
+
*/
|
|
172
|
+
declare function forecastStart(source: ForecastSource): number | undefined;
|
|
173
|
+
/** Whether any series has a forecast stretch */
|
|
174
|
+
declare function hasForecast(series: readonly ForecastSource[] | undefined): boolean;
|
|
175
|
+
/**
|
|
176
|
+
* Tooltip tag by data index: the label of the first series forecasting at
|
|
177
|
+
* that index. ECharts' `valueFormatter` only knows the index, so series that
|
|
178
|
+
* share an index share its tag.
|
|
179
|
+
*/
|
|
180
|
+
declare function forecastTagLookup(series: readonly ForecastSource[]): ((dataIndex: number) => string | undefined) | undefined;
|
|
181
|
+
/**
|
|
182
|
+
* The first category where any series' forecast starts, for accessible
|
|
183
|
+
* descriptions
|
|
184
|
+
*/
|
|
185
|
+
declare function describeForecast(categories: readonly string[], series: readonly ForecastSource[]): string | undefined;
|
|
186
|
+
/**
|
|
187
|
+
* Line forecast points: hollow, a little larger than the series' own points.
|
|
188
|
+
* Returns the data unchanged when the series has no forecast.
|
|
189
|
+
*/
|
|
190
|
+
declare function withForecastPoints(data: readonly unknown[], source: ForecastSource,
|
|
191
|
+
/** The series' own point size (0 when its points are hidden) */
|
|
192
|
+
pointSize?: number): unknown[];
|
|
193
|
+
/**
|
|
194
|
+
* Bar forecast items: faded, with a dashed outline in the series colour.
|
|
195
|
+
* Returns the data unchanged when the series has no forecast.
|
|
196
|
+
*/
|
|
197
|
+
declare function withForecastBars(data: readonly unknown[], source: ForecastSource, color: string): unknown[];
|
|
198
|
+
interface ForecastLineOptions {
|
|
199
|
+
/** Resolved colour per series (explicit colour, else the theme palette) */
|
|
200
|
+
colors: readonly string[];
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Draw line series' forecast stretches dashed.
|
|
204
|
+
*
|
|
205
|
+
* ECharts styles a line as a whole, so a series with a forecast keeps its
|
|
206
|
+
* full data for the tooltip, points and labels but hides its own line; two
|
|
207
|
+
* silent companions (same name, so the legend toggles them together) draw
|
|
208
|
+
* it: a solid one up to the last actual point, and a dashed one from there
|
|
209
|
+
* to the end. Series stacked together split at the same index (the earliest
|
|
210
|
+
* forecast in the stack) and stack in parallel groups, so the companions'
|
|
211
|
+
* totals match the series'.
|
|
212
|
+
*
|
|
213
|
+
* Series that forecast from their first point are simply dashed. Returns
|
|
214
|
+
* the series unchanged when none has a forecast. Apply reference marks
|
|
215
|
+
* before this, so they attach to the series rather than the companions.
|
|
216
|
+
*/
|
|
217
|
+
declare function withForecastLines<S extends AnyObject$1>(series: S[], sources: readonly ForecastSource[], { colors }: ForecastLineOptions): AnyObject$1[];
|
|
218
|
+
|
|
150
219
|
/**
|
|
151
220
|
* Builders for the label, axis, tooltip and legend parts of a chart option.
|
|
152
221
|
*
|
|
@@ -264,11 +333,13 @@ interface BuildTooltipOptions {
|
|
|
264
333
|
valueFormatter?: (value: number) => string;
|
|
265
334
|
/** Per-point label lookup, exposed to formatter functions as `label` */
|
|
266
335
|
getLabel?: PointLabelLookup;
|
|
336
|
+
/** Tag for forecast values by data index (see `forecastTagLookup`) */
|
|
337
|
+
forecastTag?: (dataIndex: number) => string | undefined;
|
|
267
338
|
}
|
|
268
339
|
/**
|
|
269
340
|
* Build the `tooltip` component. `base` is the chart's default tooltip.
|
|
270
341
|
*/
|
|
271
|
-
declare function buildTooltip(base: AnyObject, { show, formatter, valueFormatter, getLabel }: BuildTooltipOptions): AnyObject | undefined;
|
|
342
|
+
declare function buildTooltip(base: AnyObject, { show, formatter, valueFormatter, getLabel, forecastTag }: BuildTooltipOptions): AnyObject | undefined;
|
|
272
343
|
/**
|
|
273
344
|
* Format a value with `tooltipValueFormatter`, for charts whose default
|
|
274
345
|
* tooltip is custom HTML (where ECharts' `valueFormatter` does not apply)
|
|
@@ -673,4 +744,4 @@ declare function downloadFile(content: string, filename: string, mimeType?: stri
|
|
|
673
744
|
*/
|
|
674
745
|
declare function downloadCsv(table: ChartDataTable, filename: string): void;
|
|
675
746
|
|
|
676
|
-
export { type Accessor, type Aggregate, type AutoHeightOptions, AxisConfig, BarChartData, type BinValuesOptions, BoxPlotStats, type BuildDataLabelOptions, type BuildLegendOptions, type BuildTooltipOptions, BulletItem, CandlestickData, CategoryChartData, CategorySeriesData, ChartDataTable, ChartFormatter, type ChartFormatterOptions, type ChartFormatters, ChartLegendConfig, type Delta, type FromRecordsOptions, HeatmapData, HistogramBin, HistogramBucket, LabelFormatterParams, LegendItem, LineChartData, PieSliceData, type PointLabelLookup, RadarChartData, RadarIndicator, ReferenceBand, ReferenceLine, type ReferenceMarkOptions, SankeyData, ScatterChartData, type StackTotalOptions, type ToSliceDataOptions, TooltipFormatterParams, TreemapNode, ValueFormat, type WaterfallBar, WaterfallStep, abbreviateNumber, allSeriesEmpty, anchorCenterGraphic, barCornerRadius, binValues, boxPlotTable, boxplotStats, buildAxis, buildDataLabel, buildDataZoom, buildLegend, buildMarkArea, buildMarkLine, buildStackTotalSeries, buildTooltip, bulletTable, candlestickTable, categoryTable, chartFilename, chartFormatters, computeDelta, createTooltipFormatter, downloadCsv, downloadFile, escapeHtml, formatCurrency, formatDate, formatNumber, formatPercent, formatTableCell, formatTooltipValue, formatValue, fromRecords, generateAreaChartDescription, generateBarChartDescription, generateBoxPlotDescription, generateBulletDescription, generateComboChartDescription, generateDataTable, generateDotPlotDescription, generateHistogramDescription, generateLineChartDescription, generatePieChartDescription, generateScatterChartDescription, generateWaterfallDescription, getLegendItems, getReplaceMerge, gridInset, hasPointLabels, heatmapTable, histogramTable, itemLabelLookup, legendCenter, mergeChartOption, nameLabelLookup, niceStep, pairWithCategories, pointLabel, quantile, radarTable, resolveChartHeight, resolveValueFormatter, sankeyTable, scatterTable, seriesLabelLookup, sliceTable, stackTotals, tableToCsv, toPercentStack, toSliceData, treemapTable, trendLine, valueAxisConfig, valueLabelFormatter, waterfallLayout, waterfallTable, withEndLabelInset, withPointLabels, withReferenceMarks, withZoomInset };
|
|
747
|
+
export { type Accessor, type Aggregate, type AutoHeightOptions, AxisConfig, BarChartData, type BinValuesOptions, BoxPlotStats, type BuildDataLabelOptions, type BuildLegendOptions, type BuildTooltipOptions, BulletItem, CandlestickData, CategoryChartData, CategorySeriesData, ChartDataTable, ChartFormatter, type ChartFormatterOptions, type ChartFormatters, ChartLegendConfig, DEFAULT_FORECAST_LABEL, type Delta, type ForecastLineOptions, type ForecastSource, type FromRecordsOptions, HeatmapData, HistogramBin, HistogramBucket, LabelFormatterParams, LegendItem, LineChartData, PieSliceData, type PointLabelLookup, RadarChartData, RadarIndicator, ReferenceBand, ReferenceLine, type ReferenceMarkOptions, SankeyData, ScatterChartData, type StackTotalOptions, type ToSliceDataOptions, TooltipFormatterParams, TreemapNode, ValueFormat, type WaterfallBar, WaterfallStep, abbreviateNumber, allSeriesEmpty, anchorCenterGraphic, barCornerRadius, binValues, boxPlotTable, boxplotStats, buildAxis, buildDataLabel, buildDataZoom, buildLegend, buildMarkArea, buildMarkLine, buildStackTotalSeries, buildTooltip, bulletTable, candlestickTable, categoryTable, chartFilename, chartFormatters, computeDelta, createTooltipFormatter, describeForecast, downloadCsv, downloadFile, escapeHtml, forecastStart, forecastTagLookup, formatCurrency, formatDate, formatNumber, formatPercent, formatTableCell, formatTooltipValue, formatValue, fromRecords, generateAreaChartDescription, generateBarChartDescription, generateBoxPlotDescription, generateBulletDescription, generateComboChartDescription, generateDataTable, generateDotPlotDescription, generateHistogramDescription, generateLineChartDescription, generatePieChartDescription, generateScatterChartDescription, generateWaterfallDescription, getLegendItems, getReplaceMerge, gridInset, hasForecast, hasPointLabels, heatmapTable, histogramTable, itemLabelLookup, legendCenter, mergeChartOption, nameLabelLookup, niceStep, pairWithCategories, pointLabel, quantile, radarTable, resolveChartHeight, resolveValueFormatter, sankeyTable, scatterTable, seriesLabelLookup, sliceTable, stackTotals, tableToCsv, toPercentStack, toSliceData, treemapTable, trendLine, valueAxisConfig, valueLabelFormatter, waterfallLayout, waterfallTable, withEndLabelInset, withForecastBars, withForecastLines, withForecastPoints, withPointLabels, withReferenceMarks, withZoomInset };
|
package/dist/index.js
CHANGED
|
@@ -103,6 +103,159 @@ function createTooltipFormatter(options = {}) {
|
|
|
103
103
|
};
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
+
// src/utils/forecast.ts
|
|
107
|
+
var DEFAULT_FORECAST_LABEL = "Forecast";
|
|
108
|
+
function forecastStart(source) {
|
|
109
|
+
const from = source.forecastFrom;
|
|
110
|
+
if (from == null || !Number.isFinite(from)) return void 0;
|
|
111
|
+
const index = Math.max(0, Math.floor(from));
|
|
112
|
+
return index < source.data.length ? index : void 0;
|
|
113
|
+
}
|
|
114
|
+
function hasForecast(series) {
|
|
115
|
+
return !!series?.some((s) => forecastStart(s) !== void 0);
|
|
116
|
+
}
|
|
117
|
+
function forecastTagLookup(series) {
|
|
118
|
+
if (!hasForecast(series)) return void 0;
|
|
119
|
+
return (dataIndex) => {
|
|
120
|
+
for (const s of series) {
|
|
121
|
+
const from = forecastStart(s);
|
|
122
|
+
if (from !== void 0 && dataIndex >= from) return s.forecastLabel || DEFAULT_FORECAST_LABEL;
|
|
123
|
+
}
|
|
124
|
+
return void 0;
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function describeForecast(categories, series) {
|
|
128
|
+
let first;
|
|
129
|
+
for (const s of series) {
|
|
130
|
+
const from = forecastStart(s);
|
|
131
|
+
if (from === void 0 || first && first.index <= from) continue;
|
|
132
|
+
first = { index: from, label: s.forecastLabel || DEFAULT_FORECAST_LABEL };
|
|
133
|
+
}
|
|
134
|
+
if (!first) return void 0;
|
|
135
|
+
const category = categories[first.index] ?? "";
|
|
136
|
+
return `Values from ${category} onward are marked ${first.label.toLowerCase()}`;
|
|
137
|
+
}
|
|
138
|
+
var isPlainObject = (item) => typeof item === "object" && item !== null && !Array.isArray(item);
|
|
139
|
+
function blank(item) {
|
|
140
|
+
if (Array.isArray(item)) return [item[0], null];
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
function bare(item) {
|
|
144
|
+
return isPlainObject(item) && "value" in item ? item.value : item;
|
|
145
|
+
}
|
|
146
|
+
function withItem(item, extra) {
|
|
147
|
+
if (isPlainObject(item) && "value" in item) {
|
|
148
|
+
return {
|
|
149
|
+
...item,
|
|
150
|
+
...extra,
|
|
151
|
+
itemStyle: { ...item.itemStyle, ...extra.itemStyle }
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
return { value: item, ...extra };
|
|
155
|
+
}
|
|
156
|
+
function withForecastPoints(data, source, pointSize = 0) {
|
|
157
|
+
const from = forecastStart(source);
|
|
158
|
+
if (from === void 0) return [...data];
|
|
159
|
+
const size = Math.max(pointSize, 6) + 1;
|
|
160
|
+
return data.map(
|
|
161
|
+
(item, index) => index >= from ? withItem(item, { symbol: "emptyCircle", symbolSize: size }) : item
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
function withForecastBars(data, source, color) {
|
|
165
|
+
const from = forecastStart(source);
|
|
166
|
+
if (from === void 0) return [...data];
|
|
167
|
+
return data.map(
|
|
168
|
+
(item, index) => index >= from ? withItem(item, {
|
|
169
|
+
itemStyle: {
|
|
170
|
+
opacity: 0.45,
|
|
171
|
+
borderColor: color,
|
|
172
|
+
borderWidth: 1.5,
|
|
173
|
+
borderType: "dashed"
|
|
174
|
+
}
|
|
175
|
+
}) : item
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
function withForecastLines(series, sources, { colors }) {
|
|
179
|
+
if (!hasForecast(sources)) return series;
|
|
180
|
+
const stackFrom = /* @__PURE__ */ new Map();
|
|
181
|
+
series.forEach((s, index) => {
|
|
182
|
+
const from = forecastStart(sources[index]);
|
|
183
|
+
if (s.stack == null || from === void 0) return;
|
|
184
|
+
stackFrom.set(s.stack, Math.min(stackFrom.get(s.stack) ?? Infinity, from));
|
|
185
|
+
});
|
|
186
|
+
const mains = [];
|
|
187
|
+
const companions = [];
|
|
188
|
+
series.forEach((s, index) => {
|
|
189
|
+
const from = s.stack != null ? stackFrom.get(s.stack) : forecastStart(sources[index]);
|
|
190
|
+
if (from === void 0) {
|
|
191
|
+
mains.push(s);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
const color = colors[index];
|
|
195
|
+
const itemStyle = { ...s.itemStyle, color };
|
|
196
|
+
if (from === 0) {
|
|
197
|
+
mains.push({
|
|
198
|
+
...s,
|
|
199
|
+
itemStyle,
|
|
200
|
+
lineStyle: { ...s.lineStyle, type: "dashed" },
|
|
201
|
+
...s.areaStyle && { areaStyle: fadedArea(s.areaStyle) }
|
|
202
|
+
});
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
const data = s.data ?? [];
|
|
206
|
+
const common = {
|
|
207
|
+
name: s.name,
|
|
208
|
+
type: "line",
|
|
209
|
+
smooth: s.smooth,
|
|
210
|
+
connectNulls: s.connectNulls,
|
|
211
|
+
showSymbol: false,
|
|
212
|
+
symbol: "none",
|
|
213
|
+
silent: true,
|
|
214
|
+
legendHoverLink: false,
|
|
215
|
+
tooltip: { show: false },
|
|
216
|
+
emphasis: { disabled: true },
|
|
217
|
+
itemStyle,
|
|
218
|
+
z: s.z
|
|
219
|
+
};
|
|
220
|
+
companions.push(
|
|
221
|
+
{
|
|
222
|
+
...common,
|
|
223
|
+
data: data.map((item, i) => i < from ? bare(item) : blank(item)),
|
|
224
|
+
stack: s.stack != null ? `${s.stack}\0actual` : void 0,
|
|
225
|
+
lineStyle: s.lineStyle,
|
|
226
|
+
areaStyle: s.areaStyle
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
...common,
|
|
230
|
+
data: data.map((item, i) => i >= from - 1 ? bare(item) : blank(item)),
|
|
231
|
+
stack: s.stack != null ? `${s.stack}\0forecast` : void 0,
|
|
232
|
+
lineStyle: { ...s.lineStyle, type: "dashed" },
|
|
233
|
+
areaStyle: s.areaStyle ? fadedArea(s.areaStyle) : void 0
|
|
234
|
+
}
|
|
235
|
+
);
|
|
236
|
+
mains.push({
|
|
237
|
+
...s,
|
|
238
|
+
itemStyle,
|
|
239
|
+
// Forecast points carry their own size; the rest keep theirs (none
|
|
240
|
+
// when the series hid its points)
|
|
241
|
+
showSymbol: true,
|
|
242
|
+
symbolSize: s.showSymbol === false ? 0 : s.symbolSize ?? 4,
|
|
243
|
+
lineStyle: { ...s.lineStyle, opacity: 0 },
|
|
244
|
+
areaStyle: void 0,
|
|
245
|
+
emphasis: {
|
|
246
|
+
...s.emphasis,
|
|
247
|
+
focus: "none",
|
|
248
|
+
lineStyle: { ...s.emphasis?.lineStyle, opacity: 0 }
|
|
249
|
+
},
|
|
250
|
+
z: (s.z ?? 2) + 1
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
return [...mains, ...companions];
|
|
254
|
+
}
|
|
255
|
+
function fadedArea(areaStyle) {
|
|
256
|
+
return { ...areaStyle, opacity: (areaStyle.opacity ?? 0.7) * 0.5 };
|
|
257
|
+
}
|
|
258
|
+
|
|
106
259
|
// src/utils/accessibility.ts
|
|
107
260
|
var isNumber = (value) => typeof value === "number" && Number.isFinite(value);
|
|
108
261
|
function seriesList(names) {
|
|
@@ -128,6 +281,8 @@ function describeTrends(title, kind, data, format) {
|
|
|
128
281
|
0
|
|
129
282
|
);
|
|
130
283
|
if (missing > 0) description += `. ${missing} values are missing`;
|
|
284
|
+
const forecast = describeForecast(data.categories, data.series);
|
|
285
|
+
if (forecast) description += `. ${forecast}`;
|
|
131
286
|
return description;
|
|
132
287
|
}
|
|
133
288
|
function generateLineChartDescription(title, data, format) {
|
|
@@ -163,6 +318,8 @@ function generateBarChartDescription(title, data, format) {
|
|
|
163
318
|
if (max && min) {
|
|
164
319
|
description += `. Highest value is ${formatValue(max.value, format)} for ${max.category}. Lowest value is ${formatValue(min.value, format)} for ${min.category}`;
|
|
165
320
|
}
|
|
321
|
+
const forecast = describeForecast(data.categories, data.series);
|
|
322
|
+
if (forecast) description += `. ${forecast}`;
|
|
166
323
|
return description;
|
|
167
324
|
}
|
|
168
325
|
function generateDotPlotDescription(title, data, format) {
|
|
@@ -252,14 +409,39 @@ function generateDataTable(headers, rows) {
|
|
|
252
409
|
}
|
|
253
410
|
|
|
254
411
|
// src/utils/merge.ts
|
|
255
|
-
var ALWAYS_REPLACE = ["
|
|
412
|
+
var ALWAYS_REPLACE = ["graphic"];
|
|
256
413
|
function countComponents(value) {
|
|
257
414
|
if (value == null || value === false) return 0;
|
|
258
415
|
if (Array.isArray(value)) return value.filter((item) => item != null).length;
|
|
259
416
|
return 1;
|
|
260
417
|
}
|
|
418
|
+
function isRecord(value) {
|
|
419
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
420
|
+
}
|
|
421
|
+
function sameShape(a, b) {
|
|
422
|
+
if (!isRecord(a) || !isRecord(b)) return !isRecord(a) && !isRecord(b);
|
|
423
|
+
const keys = Object.keys(a).filter((key) => a[key] !== void 0);
|
|
424
|
+
const other = Object.keys(b).filter((key) => b[key] !== void 0);
|
|
425
|
+
if (keys.length !== other.length) return false;
|
|
426
|
+
return keys.every(
|
|
427
|
+
(key) => key in b && b[key] !== void 0 && (key === "type" || key === "id" ? a[key] === b[key] : sameShape(a[key], b[key]))
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
function asList(value) {
|
|
431
|
+
if (value == null || value === false) return [];
|
|
432
|
+
return Array.isArray(value) ? value : [value];
|
|
433
|
+
}
|
|
434
|
+
function sameSeriesShape(prev, next) {
|
|
435
|
+
const before = asList(prev);
|
|
436
|
+
const after = asList(next);
|
|
437
|
+
return before.length === after.length && before.every((item, i) => sameShape(item, after[i]));
|
|
438
|
+
}
|
|
261
439
|
function getReplaceMerge(prev, next, isComponentType) {
|
|
262
440
|
const types = new Set(ALWAYS_REPLACE);
|
|
441
|
+
const prior = prev;
|
|
442
|
+
if (!prior || !sameSeriesShape(prior.series, next.series)) {
|
|
443
|
+
types.add("series");
|
|
444
|
+
}
|
|
263
445
|
if (prev) {
|
|
264
446
|
const before = prev;
|
|
265
447
|
const after = next;
|
|
@@ -271,7 +453,7 @@ function getReplaceMerge(prev, next, isComponentType) {
|
|
|
271
453
|
}
|
|
272
454
|
return [...types].filter((type) => isComponentType(type));
|
|
273
455
|
}
|
|
274
|
-
function
|
|
456
|
+
function isPlainObject2(value) {
|
|
275
457
|
if (value === null || typeof value !== "object") return false;
|
|
276
458
|
const proto = Object.getPrototypeOf(value);
|
|
277
459
|
return proto === Object.prototype || proto === null;
|
|
@@ -284,7 +466,7 @@ function mergeValue(base, override, topLevel = false) {
|
|
|
284
466
|
const length = Math.max(from.length, to.length);
|
|
285
467
|
return Array.from({ length }, (_, index) => mergeValue(from[index], to[index]));
|
|
286
468
|
}
|
|
287
|
-
if (
|
|
469
|
+
if (isPlainObject2(base) && isPlainObject2(override)) {
|
|
288
470
|
const merged = { ...base };
|
|
289
471
|
for (const key of Object.keys(override)) {
|
|
290
472
|
merged[key] = mergeValue(base[key], override[key]);
|
|
@@ -692,12 +874,17 @@ function nameLabelLookup(nodes) {
|
|
|
692
874
|
function hasPointLabels(series) {
|
|
693
875
|
return series.some((s) => s.labels?.some((text) => text != null && text !== ""));
|
|
694
876
|
}
|
|
695
|
-
function buildTooltip(base, { show, formatter, valueFormatter, getLabel }) {
|
|
877
|
+
function buildTooltip(base, { show, formatter, valueFormatter, getLabel, forecastTag }) {
|
|
696
878
|
if (!show) return void 0;
|
|
697
|
-
if (formatter === void 0 && valueFormatter === void 0) return base;
|
|
879
|
+
if (formatter === void 0 && valueFormatter === void 0 && !forecastTag) return base;
|
|
698
880
|
const tooltip = { ...base };
|
|
699
|
-
if (valueFormatter) {
|
|
700
|
-
|
|
881
|
+
if (valueFormatter || forecastTag) {
|
|
882
|
+
const format = (value) => typeof value === "number" ? valueFormatter ? valueFormatter(value) : value.toLocaleString() : String(value ?? "");
|
|
883
|
+
tooltip.valueFormatter = (value, dataIndex) => {
|
|
884
|
+
const text = format(value);
|
|
885
|
+
const tag = dataIndex === void 0 ? void 0 : forecastTag?.(dataIndex);
|
|
886
|
+
return tag && value != null ? `${text} (${tag})` : text;
|
|
887
|
+
};
|
|
701
888
|
}
|
|
702
889
|
if (typeof formatter === "string") {
|
|
703
890
|
tooltip.formatter = formatter;
|
|
@@ -1241,4 +1428,4 @@ function downloadCsv(table, filename) {
|
|
|
1241
1428
|
);
|
|
1242
1429
|
}
|
|
1243
1430
|
|
|
1244
|
-
export { abbreviateNumber, allSeriesEmpty, anchorCenterGraphic, barCornerRadius, binValues, boxPlotTable, boxplotStats, buildAxis, buildDataLabel, buildDataZoom, buildLegend, buildMarkArea, buildMarkLine, buildStackTotalSeries, buildTooltip, bulletTable, candlestickTable, categoryTable, chartFilename, chartFormatters, computeDelta, createTooltipFormatter, downloadCsv, downloadFile, escapeHtml, formatCurrency, formatDate, formatNumber, formatPercent, formatTableCell, formatTooltipValue, formatValue, fromRecords, generateAreaChartDescription, generateBarChartDescription, generateBoxPlotDescription, generateBulletDescription, generateComboChartDescription, generateDataTable, generateDotPlotDescription, generateHistogramDescription, generateLineChartDescription, generatePieChartDescription, generateScatterChartDescription, generateWaterfallDescription, getLegendItems, getReplaceMerge, gridInset, hasPointLabels, heatmapTable, histogramTable, itemLabelLookup, legendCenter, mergeChartOption, nameLabelLookup, niceStep, pairWithCategories, pointLabel, quantile, radarTable, resolveChartHeight, resolveValueFormatter, sankeyTable, scatterTable, seriesLabelLookup, sliceTable, stackTotals, tableToCsv, toPercentStack, toSliceData, treemapTable, trendLine, valueAxisConfig, valueLabelFormatter, waterfallLayout, waterfallTable, withEndLabelInset, withPointLabels, withReferenceMarks, withZoomInset };
|
|
1431
|
+
export { DEFAULT_FORECAST_LABEL, abbreviateNumber, allSeriesEmpty, anchorCenterGraphic, barCornerRadius, binValues, boxPlotTable, boxplotStats, buildAxis, buildDataLabel, buildDataZoom, buildLegend, buildMarkArea, buildMarkLine, buildStackTotalSeries, buildTooltip, bulletTable, candlestickTable, categoryTable, chartFilename, chartFormatters, computeDelta, createTooltipFormatter, describeForecast, downloadCsv, downloadFile, escapeHtml, forecastStart, forecastTagLookup, formatCurrency, formatDate, formatNumber, formatPercent, formatTableCell, formatTooltipValue, formatValue, fromRecords, generateAreaChartDescription, generateBarChartDescription, generateBoxPlotDescription, generateBulletDescription, generateComboChartDescription, generateDataTable, generateDotPlotDescription, generateHistogramDescription, generateLineChartDescription, generatePieChartDescription, generateScatterChartDescription, generateWaterfallDescription, getLegendItems, getReplaceMerge, gridInset, hasForecast, hasPointLabels, heatmapTable, histogramTable, itemLabelLookup, legendCenter, mergeChartOption, nameLabelLookup, niceStep, pairWithCategories, pointLabel, quantile, radarTable, resolveChartHeight, resolveValueFormatter, sankeyTable, scatterTable, seriesLabelLookup, sliceTable, stackTotals, tableToCsv, toPercentStack, toSliceData, treemapTable, trendLine, valueAxisConfig, valueLabelFormatter, waterfallLayout, waterfallTable, withEndLabelInset, withForecastBars, withForecastLines, withForecastPoints, withPointLabels, withReferenceMarks, withZoomInset };
|
package/dist/types/index.d.cts
CHANGED
|
@@ -24,6 +24,15 @@ interface LineSeriesData {
|
|
|
24
24
|
* chart's `stacked` prop for this series.
|
|
25
25
|
*/
|
|
26
26
|
stack?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Index of the first forecast point: from here to the end of the series the
|
|
29
|
+
* values are projections (or a period still in progress). Lines draw that
|
|
30
|
+
* stretch dashed with hollow points, bars draw those bars faded with a
|
|
31
|
+
* dashed outline, and the tooltip tags those values with `forecastLabel`.
|
|
32
|
+
*/
|
|
33
|
+
forecastFrom?: number;
|
|
34
|
+
/** Tooltip tag for forecast values (defaults to 'Forecast') */
|
|
35
|
+
forecastLabel?: string;
|
|
27
36
|
}
|
|
28
37
|
interface BarChartData {
|
|
29
38
|
/** Category labels */
|
|
@@ -45,6 +54,15 @@ interface BarSeriesData {
|
|
|
45
54
|
* chart's `stacked` prop for this series.
|
|
46
55
|
*/
|
|
47
56
|
stack?: string;
|
|
57
|
+
/**
|
|
58
|
+
* Index of the first forecast point: from here to the end of the series the
|
|
59
|
+
* values are projections (or a period still in progress). Lines draw that
|
|
60
|
+
* stretch dashed with hollow points, bars draw those bars faded with a
|
|
61
|
+
* dashed outline, and the tooltip tags those values with `forecastLabel`.
|
|
62
|
+
*/
|
|
63
|
+
forecastFrom?: number;
|
|
64
|
+
/** Tooltip tag for forecast values (defaults to 'Forecast') */
|
|
65
|
+
forecastLabel?: string;
|
|
48
66
|
}
|
|
49
67
|
interface PieSliceData {
|
|
50
68
|
/** Slice name (shown in legend/tooltip) */
|
|
@@ -76,6 +94,15 @@ interface AreaSeriesData {
|
|
|
76
94
|
* chart's `stacked` prop for this series.
|
|
77
95
|
*/
|
|
78
96
|
stack?: string;
|
|
97
|
+
/**
|
|
98
|
+
* Index of the first forecast point: from here to the end of the series the
|
|
99
|
+
* values are projections (or a period still in progress). Lines draw that
|
|
100
|
+
* stretch dashed with hollow points, bars draw those bars faded with a
|
|
101
|
+
* dashed outline, and the tooltip tags those values with `forecastLabel`.
|
|
102
|
+
*/
|
|
103
|
+
forecastFrom?: number;
|
|
104
|
+
/** Tooltip tag for forecast values (defaults to 'Forecast') */
|
|
105
|
+
forecastLabel?: string;
|
|
79
106
|
}
|
|
80
107
|
interface ScatterChartData {
|
|
81
108
|
/** Data series */
|
|
@@ -216,6 +243,15 @@ interface CategorySeriesData {
|
|
|
216
243
|
labels?: (string | null | undefined)[];
|
|
217
244
|
/** Stack group id */
|
|
218
245
|
stack?: string;
|
|
246
|
+
/**
|
|
247
|
+
* Index of the first forecast point: from here to the end of the series the
|
|
248
|
+
* values are projections (or a period still in progress). Lines draw that
|
|
249
|
+
* stretch dashed with hollow points, bars draw those bars faded with a
|
|
250
|
+
* dashed outline, and the tooltip tags those values with `forecastLabel`.
|
|
251
|
+
*/
|
|
252
|
+
forecastFrom?: number;
|
|
253
|
+
/** Tooltip tag for forecast values (defaults to 'Forecast') */
|
|
254
|
+
forecastLabel?: string;
|
|
219
255
|
}
|
|
220
256
|
interface ComboChartData {
|
|
221
257
|
/** Category labels */
|
package/dist/types/index.d.ts
CHANGED
|
@@ -24,6 +24,15 @@ interface LineSeriesData {
|
|
|
24
24
|
* chart's `stacked` prop for this series.
|
|
25
25
|
*/
|
|
26
26
|
stack?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Index of the first forecast point: from here to the end of the series the
|
|
29
|
+
* values are projections (or a period still in progress). Lines draw that
|
|
30
|
+
* stretch dashed with hollow points, bars draw those bars faded with a
|
|
31
|
+
* dashed outline, and the tooltip tags those values with `forecastLabel`.
|
|
32
|
+
*/
|
|
33
|
+
forecastFrom?: number;
|
|
34
|
+
/** Tooltip tag for forecast values (defaults to 'Forecast') */
|
|
35
|
+
forecastLabel?: string;
|
|
27
36
|
}
|
|
28
37
|
interface BarChartData {
|
|
29
38
|
/** Category labels */
|
|
@@ -45,6 +54,15 @@ interface BarSeriesData {
|
|
|
45
54
|
* chart's `stacked` prop for this series.
|
|
46
55
|
*/
|
|
47
56
|
stack?: string;
|
|
57
|
+
/**
|
|
58
|
+
* Index of the first forecast point: from here to the end of the series the
|
|
59
|
+
* values are projections (or a period still in progress). Lines draw that
|
|
60
|
+
* stretch dashed with hollow points, bars draw those bars faded with a
|
|
61
|
+
* dashed outline, and the tooltip tags those values with `forecastLabel`.
|
|
62
|
+
*/
|
|
63
|
+
forecastFrom?: number;
|
|
64
|
+
/** Tooltip tag for forecast values (defaults to 'Forecast') */
|
|
65
|
+
forecastLabel?: string;
|
|
48
66
|
}
|
|
49
67
|
interface PieSliceData {
|
|
50
68
|
/** Slice name (shown in legend/tooltip) */
|
|
@@ -76,6 +94,15 @@ interface AreaSeriesData {
|
|
|
76
94
|
* chart's `stacked` prop for this series.
|
|
77
95
|
*/
|
|
78
96
|
stack?: string;
|
|
97
|
+
/**
|
|
98
|
+
* Index of the first forecast point: from here to the end of the series the
|
|
99
|
+
* values are projections (or a period still in progress). Lines draw that
|
|
100
|
+
* stretch dashed with hollow points, bars draw those bars faded with a
|
|
101
|
+
* dashed outline, and the tooltip tags those values with `forecastLabel`.
|
|
102
|
+
*/
|
|
103
|
+
forecastFrom?: number;
|
|
104
|
+
/** Tooltip tag for forecast values (defaults to 'Forecast') */
|
|
105
|
+
forecastLabel?: string;
|
|
79
106
|
}
|
|
80
107
|
interface ScatterChartData {
|
|
81
108
|
/** Data series */
|
|
@@ -216,6 +243,15 @@ interface CategorySeriesData {
|
|
|
216
243
|
labels?: (string | null | undefined)[];
|
|
217
244
|
/** Stack group id */
|
|
218
245
|
stack?: string;
|
|
246
|
+
/**
|
|
247
|
+
* Index of the first forecast point: from here to the end of the series the
|
|
248
|
+
* values are projections (or a period still in progress). Lines draw that
|
|
249
|
+
* stretch dashed with hollow points, bars draw those bars faded with a
|
|
250
|
+
* dashed outline, and the tooltip tags those values with `forecastLabel`.
|
|
251
|
+
*/
|
|
252
|
+
forecastFrom?: number;
|
|
253
|
+
/** Tooltip tag for forecast values (defaults to 'Forecast') */
|
|
254
|
+
forecastLabel?: string;
|
|
219
255
|
}
|
|
220
256
|
interface ComboChartData {
|
|
221
257
|
/** Category labels */
|