@classic-homes/charts-core 0.4.2 → 0.6.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 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) {
@@ -617,7 +774,7 @@ function getReplaceMerge(prev, next, isComponentType) {
617
774
  }
618
775
  return [...types].filter((type) => isComponentType(type));
619
776
  }
620
- function isPlainObject(value) {
777
+ function isPlainObject2(value) {
621
778
  if (value === null || typeof value !== "object") return false;
622
779
  const proto = Object.getPrototypeOf(value);
623
780
  return proto === Object.prototype || proto === null;
@@ -630,7 +787,7 @@ function mergeValue(base, override, topLevel = false) {
630
787
  const length = Math.max(from.length, to.length);
631
788
  return Array.from({ length }, (_, index) => mergeValue(from[index], to[index]));
632
789
  }
633
- if (isPlainObject(base) && isPlainObject(override)) {
790
+ if (isPlainObject2(base) && isPlainObject2(override)) {
634
791
  const merged = { ...base };
635
792
  for (const key of Object.keys(override)) {
636
793
  merged[key] = mergeValue(base[key], override[key]);
@@ -649,6 +806,66 @@ function mergeChartOption(generated, override) {
649
806
  return merged;
650
807
  }
651
808
 
809
+ // src/utils/ghost.ts
810
+ var GHOST_LINE_OPACITY = 0.6;
811
+ var GHOST_BAR_OPACITY = 0.35;
812
+ var GHOST_STACK = "\0ghost";
813
+ function isGhostSeries(source) {
814
+ return source?.variant === "ghost";
815
+ }
816
+ function hasGhostSeries(series) {
817
+ return !!series?.some(isGhostSeries);
818
+ }
819
+ function withoutGhostSeries(series) {
820
+ return series.filter((s) => !isGhostSeries(s));
821
+ }
822
+ function ghostStackId(stack, source) {
823
+ if (stack === void 0 || !isGhostSeries(source) || stack.endsWith(GHOST_STACK)) return stack;
824
+ return `${stack}${GHOST_STACK}`;
825
+ }
826
+ var isPlainObject3 = (item) => typeof item === "object" && item !== null && !Array.isArray(item);
827
+ function withoutItemLabels(data) {
828
+ if (!Array.isArray(data)) return data;
829
+ return data.map(
830
+ (item) => isPlainObject3(item) && item.label ? { ...item, label: { ...item.label, show: false } } : item
831
+ );
832
+ }
833
+ function withGhostSeries(series, sources) {
834
+ if (!hasGhostSeries(sources)) return series;
835
+ return series.map((s, index) => {
836
+ const source = sources[index];
837
+ if (!isGhostSeries(source)) return s;
838
+ const common = {
839
+ ...s,
840
+ stack: ghostStackId(s.stack, source),
841
+ data: withoutItemLabels(s.data),
842
+ label: { ...s.label, show: false }
843
+ };
844
+ if (s.type === "bar") {
845
+ return {
846
+ ...common,
847
+ itemStyle: { ...s.itemStyle, opacity: GHOST_BAR_OPACITY },
848
+ emphasis: {
849
+ ...s.emphasis,
850
+ itemStyle: { ...s.emphasis?.itemStyle, opacity: GHOST_BAR_OPACITY + 0.2 }
851
+ }
852
+ };
853
+ }
854
+ return {
855
+ ...common,
856
+ showSymbol: false,
857
+ areaStyle: void 0,
858
+ lineStyle: { ...s.lineStyle, type: "dashed", opacity: GHOST_LINE_OPACITY },
859
+ itemStyle: { ...s.itemStyle, opacity: GHOST_LINE_OPACITY },
860
+ emphasis: {
861
+ ...s.emphasis,
862
+ areaStyle: void 0
863
+ },
864
+ z: (s.z ?? 2) - 1
865
+ };
866
+ });
867
+ }
868
+
652
869
  // src/utils/stats.ts
653
870
  var isFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value);
654
871
  var clean = (value) => Number(value.toPrecision(12));
@@ -801,20 +1018,26 @@ function waterfallLayout(steps) {
801
1018
  }
802
1019
  function toPercentStack(series) {
803
1020
  const length = Math.max(0, ...series.map((s) => s.data.length));
804
- const totals = Array.from(
1021
+ const sumOf = (ghost) => Array.from(
805
1022
  { length },
806
1023
  (_, index) => series.reduce((sum, s) => {
1024
+ if (s.variant === "ghost" !== ghost) return sum;
807
1025
  const value = s.data[index];
808
1026
  return sum + (isFiniteNumber(value) ? Math.abs(value) : 0);
809
1027
  }, 0)
810
1028
  );
811
- return series.map((s) => ({
812
- ...s,
813
- data: s.data.map((value, index) => {
814
- if (!isFiniteNumber(value)) return null;
815
- return totals[index] === 0 ? 0 : clean(value / totals[index] * 100);
816
- })
817
- }));
1029
+ const totals = sumOf(false);
1030
+ const ghostTotals = sumOf(true);
1031
+ return series.map((s) => {
1032
+ const sums = s.variant === "ghost" ? ghostTotals : totals;
1033
+ return {
1034
+ ...s,
1035
+ data: s.data.map((value, index) => {
1036
+ if (!isFiniteNumber(value)) return null;
1037
+ return sums[index] === 0 ? 0 : clean(value / sums[index] * 100);
1038
+ })
1039
+ };
1040
+ });
818
1041
  }
819
1042
  function stackTotals(series, stacked) {
820
1043
  const length = Math.max(0, ...series.map((s) => s.data.length));
@@ -1038,12 +1261,17 @@ function nameLabelLookup(nodes) {
1038
1261
  function hasPointLabels(series) {
1039
1262
  return series.some((s) => s.labels?.some((text) => text != null && text !== ""));
1040
1263
  }
1041
- function buildTooltip(base, { show, formatter, valueFormatter, getLabel }) {
1264
+ function buildTooltip(base, { show, formatter, valueFormatter, getLabel, forecastTag }) {
1042
1265
  if (!show) return void 0;
1043
- if (formatter === void 0 && valueFormatter === void 0) return base;
1266
+ if (formatter === void 0 && valueFormatter === void 0 && !forecastTag) return base;
1044
1267
  const tooltip = { ...base };
1045
- if (valueFormatter) {
1046
- tooltip.valueFormatter = (value) => typeof value === "number" ? valueFormatter(value) : String(value ?? "");
1268
+ if (valueFormatter || forecastTag) {
1269
+ const format = (value) => typeof value === "number" ? valueFormatter ? valueFormatter(value) : value.toLocaleString() : String(value ?? "");
1270
+ tooltip.valueFormatter = (value, dataIndex) => {
1271
+ const text = format(value);
1272
+ const tag = dataIndex === void 0 ? void 0 : forecastTag?.(dataIndex);
1273
+ return tag && value != null ? `${text} (${tag})` : text;
1274
+ };
1047
1275
  }
1048
1276
  if (typeof formatter === "string") {
1049
1277
  tooltip.formatter = formatter;
@@ -1587,6 +1815,9 @@ function downloadCsv(table, filename) {
1587
1815
  );
1588
1816
  }
1589
1817
 
1818
+ exports.DEFAULT_FORECAST_LABEL = DEFAULT_FORECAST_LABEL;
1819
+ exports.GHOST_BAR_OPACITY = GHOST_BAR_OPACITY;
1820
+ exports.GHOST_LINE_OPACITY = GHOST_LINE_OPACITY;
1590
1821
  exports.THEME_DARK = THEME_DARK;
1591
1822
  exports.THEME_LIGHT = THEME_LIGHT;
1592
1823
  exports.abbreviateNumber = abbreviateNumber;
@@ -1614,9 +1845,12 @@ exports.computeDelta = computeDelta;
1614
1845
  exports.createTooltipFormatter = createTooltipFormatter;
1615
1846
  exports.darkChartColors = darkChartColors;
1616
1847
  exports.darkTheme = darkTheme;
1848
+ exports.describeForecast = describeForecast;
1617
1849
  exports.downloadCsv = downloadCsv;
1618
1850
  exports.downloadFile = downloadFile;
1619
1851
  exports.escapeHtml = escapeHtml;
1852
+ exports.forecastStart = forecastStart;
1853
+ exports.forecastTagLookup = forecastTagLookup;
1620
1854
  exports.formatCurrency = formatCurrency;
1621
1855
  exports.formatDate = formatDate;
1622
1856
  exports.formatNumber = formatNumber;
@@ -1642,10 +1876,14 @@ exports.generateWaterfallDescription = generateWaterfallDescription;
1642
1876
  exports.getChartColors = getChartColors;
1643
1877
  exports.getLegendItems = getLegendItems;
1644
1878
  exports.getReplaceMerge = getReplaceMerge;
1879
+ exports.ghostStackId = ghostStackId;
1645
1880
  exports.gridInset = gridInset;
1881
+ exports.hasForecast = hasForecast;
1882
+ exports.hasGhostSeries = hasGhostSeries;
1646
1883
  exports.hasPointLabels = hasPointLabels;
1647
1884
  exports.heatmapTable = heatmapTable;
1648
1885
  exports.histogramTable = histogramTable;
1886
+ exports.isGhostSeries = isGhostSeries;
1649
1887
  exports.itemLabelLookup = itemLabelLookup;
1650
1888
  exports.legendCenter = legendCenter;
1651
1889
  exports.lightChartColors = lightChartColors;
@@ -1674,6 +1912,11 @@ exports.valueLabelFormatter = valueLabelFormatter;
1674
1912
  exports.waterfallLayout = waterfallLayout;
1675
1913
  exports.waterfallTable = waterfallTable;
1676
1914
  exports.withEndLabelInset = withEndLabelInset;
1915
+ exports.withForecastBars = withForecastBars;
1916
+ exports.withForecastLines = withForecastLines;
1917
+ exports.withForecastPoints = withForecastPoints;
1918
+ exports.withGhostSeries = withGhostSeries;
1677
1919
  exports.withPointLabels = withPointLabels;
1678
1920
  exports.withReferenceMarks = withReferenceMarks;
1679
1921
  exports.withZoomInset = withZoomInset;
1922
+ exports.withoutGhostSeries = withoutGhostSeries;
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { ChartSemanticColors, EChartsTheme, THEME_DARK, THEME_LIGHT, colorWithAlpha, darkChartColors, darkTheme, generateDarkTheme, generateLightTheme, getChartColors, lightChartColors, lightTheme } from './theme/index.cjs';
2
2
  import { ValueFormat, CategoryChartData, BarChartData, BoxPlotStats, BulletItem, HistogramBucket, LineChartData, PieSliceData, ScatterChartData, WaterfallStep, ChartFormatter, LabelFormatterParams, TooltipFormatterParams, AxisConfig, ChartLegendConfig, ReferenceBand, ReferenceLine, LegendItem, HistogramBin, CategorySeriesData, ChartDataTable, CandlestickData, HeatmapData, RadarChartData, RadarIndicator, SankeyData, TreemapNode } from './types/index.cjs';
3
- export { AreaChartData, AreaChartProps, AreaSeriesData, AxisChartProps, BarChartProps, BarSeriesData, BaseChartProps, BaseEventParams, BoxPlotChartProps, BoxPlotGroup, BulletChartProps, CandlestickChartProps, CandlestickEventParams, CandlestickItem, ChartEventHandlers, ChartExportConfig, ChartLegendPosition, ComboChartData, ComboChartProps, ComboSeriesData, DataPointEventParams, DonutChartProps, DotPlotChartData, DotPlotChartProps, DotPlotSeriesData, FunnelChartProps, FunnelSliceData, GaugeChartProps, HeatmapChartProps, HeatmapEventParams, HistogramChartProps, LabelChartProps, LegendChartProps, LegendRenderProps, LineChartProps, LineSeriesData, PieChartProps, RadarChartProps, RadarSeriesData, ReferenceChartProps, RowHeightProps, SankeyChartProps, SankeyEventParams, SankeyLink, SankeyNode, ScatterChartProps, ScatterSeriesData, SliceEventParams, SparklineProps, StatCardProps, StatDelta, TreemapChartProps, TreemapEventParams, TreemapLevel, ValueFormatPreset, WaterfallChartProps, ZoomChartProps } from './types/index.cjs';
3
+ export { AreaChartData, AreaChartProps, AreaSeriesData, AxisChartProps, BarChartProps, BarSeriesData, BaseChartProps, BaseEventParams, BoxPlotChartProps, BoxPlotGroup, BulletChartProps, CandlestickChartProps, CandlestickEventParams, CandlestickItem, ChartEventHandlers, ChartExportConfig, ChartLegendPosition, ComboChartData, ComboChartProps, ComboSeriesData, DataPointEventParams, DeltaTextProps, DonutChartProps, DotPlotChartData, DotPlotChartProps, DotPlotSeriesData, FunnelChartProps, FunnelSliceData, GaugeChartProps, HeatmapChartProps, HeatmapEventParams, HistogramChartProps, LabelChartProps, LegendChartProps, LegendRenderProps, LineChartProps, LineSeriesData, PieChartProps, RadarChartProps, RadarSeriesData, ReferenceChartProps, RowHeightProps, SankeyChartProps, SankeyEventParams, SankeyLink, SankeyNode, ScatterChartProps, ScatterSeriesData, SliceEventParams, SparklineProps, StatCardProps, StatDelta, TreemapChartProps, TreemapEventParams, TreemapLevel, ValueFormatPreset, WaterfallChartProps, ZoomChartProps } from './types/index.cjs';
4
4
  import 'echarts';
5
5
  import 'echarts/core';
6
6
 
@@ -151,6 +151,113 @@ declare function getReplaceMerge(prev: object | null | undefined, next: object,
151
151
  */
152
152
  declare function mergeChartOption<T extends object>(generated: T, override: object | undefined): T;
153
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$2 = 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$2>(series: S[], sources: readonly ForecastSource[], { colors }: ForecastLineOptions): AnyObject$2[];
218
+
219
+ /**
220
+ * Ghost series: a faded reference drawn beside the real data, such as the
221
+ * prior period in a period-over-period comparison. Lines and areas draw
222
+ * dashed and faded with no fill or points; bars draw faded beside the solid
223
+ * ones (in a parallel stack group) rather than stacked onto them. Ghost
224
+ * series carry no value labels and are left out of bar totals.
225
+ */
226
+ type AnyObject$1 = Record<string, any>;
227
+ /** The variant field a series may carry */
228
+ interface GhostSource {
229
+ variant?: 'ghost';
230
+ }
231
+ /** Opacity of a ghost line, its points and its legend icon */
232
+ declare const GHOST_LINE_OPACITY = 0.6;
233
+ /** Opacity of ghost bars */
234
+ declare const GHOST_BAR_OPACITY = 0.35;
235
+ /** Whether a series is a ghost (faded reference) series */
236
+ declare function isGhostSeries(source: GhostSource | undefined): boolean;
237
+ /** Whether any series is a ghost series */
238
+ declare function hasGhostSeries(series: readonly GhostSource[] | undefined): boolean;
239
+ /** The series that are not ghosts (for totals and value labels) */
240
+ declare function withoutGhostSeries<S extends GhostSource>(series: readonly S[]): S[];
241
+ /**
242
+ * A series' stack id, moved into a parallel group for ghost series so they
243
+ * stack among themselves (side by side with the real stack) and never onto
244
+ * real series. Unstacked series stay unstacked.
245
+ */
246
+ declare function ghostStackId(stack: string | undefined, source: GhostSource | undefined): string | undefined;
247
+ /**
248
+ * Restyle the ECharts series built for ghost sources (matched by index):
249
+ *
250
+ * - line (and area) series: dashed, faded line and points, no area fill,
251
+ * no points by default, drawn under the real series
252
+ * - bar series: same colour at {@link GHOST_BAR_OPACITY}, in a parallel
253
+ * stack group so they pair side by side with the real bars
254
+ *
255
+ * Ghost series lose their value labels (series and per-point). Series
256
+ * without a ghost source are returned unchanged. Apply after building the
257
+ * series and before reference marks, forecast lines or totals are added.
258
+ */
259
+ declare function withGhostSeries<S extends AnyObject$1>(series: S[], sources: readonly (GhostSource | undefined)[]): S[];
260
+
154
261
  /**
155
262
  * Builders for the label, axis, tooltip and legend parts of a chart option.
156
263
  *
@@ -268,11 +375,13 @@ interface BuildTooltipOptions {
268
375
  valueFormatter?: (value: number) => string;
269
376
  /** Per-point label lookup, exposed to formatter functions as `label` */
270
377
  getLabel?: PointLabelLookup;
378
+ /** Tag for forecast values by data index (see `forecastTagLookup`) */
379
+ forecastTag?: (dataIndex: number) => string | undefined;
271
380
  }
272
381
  /**
273
382
  * Build the `tooltip` component. `base` is the chart's default tooltip.
274
383
  */
275
- declare function buildTooltip(base: AnyObject, { show, formatter, valueFormatter, getLabel }: BuildTooltipOptions): AnyObject | undefined;
384
+ declare function buildTooltip(base: AnyObject, { show, formatter, valueFormatter, getLabel, forecastTag }: BuildTooltipOptions): AnyObject | undefined;
276
385
  /**
277
386
  * Format a value with `tooltipValueFormatter`, for charts whose default
278
387
  * tooltip is custom HTML (where ECharts' `valueFormatter` does not apply)
@@ -448,10 +557,12 @@ interface WaterfallBar {
448
557
  declare function waterfallLayout(steps: WaterfallStep[]): WaterfallBar[];
449
558
  /**
450
559
  * Convert stacked series to each category's share of 100%. Missing values
451
- * stay missing; a category whose values sum to zero gets zeros.
560
+ * stay missing; a category whose values sum to zero gets zeros. Ghost
561
+ * series (`variant: 'ghost'`) share their own 100%, apart from the rest.
452
562
  */
453
563
  declare function toPercentStack<S extends {
454
564
  data: (number | null)[];
565
+ variant?: 'ghost';
455
566
  }>(series: S[]): S[];
456
567
  /**
457
568
  * Each stack's total by category, keyed by stack id. A series stacks under
@@ -677,4 +788,4 @@ declare function downloadFile(content: string, filename: string, mimeType?: stri
677
788
  */
678
789
  declare function downloadCsv(table: ChartDataTable, filename: string): void;
679
790
 
680
- 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 };
791
+ 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, GHOST_BAR_OPACITY, GHOST_LINE_OPACITY, type GhostSource, 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, ghostStackId, gridInset, hasForecast, hasGhostSeries, hasPointLabels, heatmapTable, histogramTable, isGhostSeries, 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, withGhostSeries, withPointLabels, withReferenceMarks, withZoomInset, withoutGhostSeries };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { ChartSemanticColors, EChartsTheme, THEME_DARK, THEME_LIGHT, colorWithAlpha, darkChartColors, darkTheme, generateDarkTheme, generateLightTheme, getChartColors, lightChartColors, lightTheme } from './theme/index.js';
2
2
  import { ValueFormat, CategoryChartData, BarChartData, BoxPlotStats, BulletItem, HistogramBucket, LineChartData, PieSliceData, ScatterChartData, WaterfallStep, ChartFormatter, LabelFormatterParams, TooltipFormatterParams, AxisConfig, ChartLegendConfig, ReferenceBand, ReferenceLine, LegendItem, HistogramBin, CategorySeriesData, ChartDataTable, CandlestickData, HeatmapData, RadarChartData, RadarIndicator, SankeyData, TreemapNode } from './types/index.js';
3
- export { AreaChartData, AreaChartProps, AreaSeriesData, AxisChartProps, BarChartProps, BarSeriesData, BaseChartProps, BaseEventParams, BoxPlotChartProps, BoxPlotGroup, BulletChartProps, CandlestickChartProps, CandlestickEventParams, CandlestickItem, ChartEventHandlers, ChartExportConfig, ChartLegendPosition, ComboChartData, ComboChartProps, ComboSeriesData, DataPointEventParams, DonutChartProps, DotPlotChartData, DotPlotChartProps, DotPlotSeriesData, FunnelChartProps, FunnelSliceData, GaugeChartProps, HeatmapChartProps, HeatmapEventParams, HistogramChartProps, LabelChartProps, LegendChartProps, LegendRenderProps, LineChartProps, LineSeriesData, PieChartProps, RadarChartProps, RadarSeriesData, ReferenceChartProps, RowHeightProps, SankeyChartProps, SankeyEventParams, SankeyLink, SankeyNode, ScatterChartProps, ScatterSeriesData, SliceEventParams, SparklineProps, StatCardProps, StatDelta, TreemapChartProps, TreemapEventParams, TreemapLevel, ValueFormatPreset, WaterfallChartProps, ZoomChartProps } from './types/index.js';
3
+ export { AreaChartData, AreaChartProps, AreaSeriesData, AxisChartProps, BarChartProps, BarSeriesData, BaseChartProps, BaseEventParams, BoxPlotChartProps, BoxPlotGroup, BulletChartProps, CandlestickChartProps, CandlestickEventParams, CandlestickItem, ChartEventHandlers, ChartExportConfig, ChartLegendPosition, ComboChartData, ComboChartProps, ComboSeriesData, DataPointEventParams, DeltaTextProps, DonutChartProps, DotPlotChartData, DotPlotChartProps, DotPlotSeriesData, FunnelChartProps, FunnelSliceData, GaugeChartProps, HeatmapChartProps, HeatmapEventParams, HistogramChartProps, LabelChartProps, LegendChartProps, LegendRenderProps, LineChartProps, LineSeriesData, PieChartProps, RadarChartProps, RadarSeriesData, ReferenceChartProps, RowHeightProps, SankeyChartProps, SankeyEventParams, SankeyLink, SankeyNode, ScatterChartProps, ScatterSeriesData, SliceEventParams, SparklineProps, StatCardProps, StatDelta, TreemapChartProps, TreemapEventParams, TreemapLevel, ValueFormatPreset, WaterfallChartProps, ZoomChartProps } from './types/index.js';
4
4
  import 'echarts';
5
5
  import 'echarts/core';
6
6
 
@@ -151,6 +151,113 @@ declare function getReplaceMerge(prev: object | null | undefined, next: object,
151
151
  */
152
152
  declare function mergeChartOption<T extends object>(generated: T, override: object | undefined): T;
153
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$2 = 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$2>(series: S[], sources: readonly ForecastSource[], { colors }: ForecastLineOptions): AnyObject$2[];
218
+
219
+ /**
220
+ * Ghost series: a faded reference drawn beside the real data, such as the
221
+ * prior period in a period-over-period comparison. Lines and areas draw
222
+ * dashed and faded with no fill or points; bars draw faded beside the solid
223
+ * ones (in a parallel stack group) rather than stacked onto them. Ghost
224
+ * series carry no value labels and are left out of bar totals.
225
+ */
226
+ type AnyObject$1 = Record<string, any>;
227
+ /** The variant field a series may carry */
228
+ interface GhostSource {
229
+ variant?: 'ghost';
230
+ }
231
+ /** Opacity of a ghost line, its points and its legend icon */
232
+ declare const GHOST_LINE_OPACITY = 0.6;
233
+ /** Opacity of ghost bars */
234
+ declare const GHOST_BAR_OPACITY = 0.35;
235
+ /** Whether a series is a ghost (faded reference) series */
236
+ declare function isGhostSeries(source: GhostSource | undefined): boolean;
237
+ /** Whether any series is a ghost series */
238
+ declare function hasGhostSeries(series: readonly GhostSource[] | undefined): boolean;
239
+ /** The series that are not ghosts (for totals and value labels) */
240
+ declare function withoutGhostSeries<S extends GhostSource>(series: readonly S[]): S[];
241
+ /**
242
+ * A series' stack id, moved into a parallel group for ghost series so they
243
+ * stack among themselves (side by side with the real stack) and never onto
244
+ * real series. Unstacked series stay unstacked.
245
+ */
246
+ declare function ghostStackId(stack: string | undefined, source: GhostSource | undefined): string | undefined;
247
+ /**
248
+ * Restyle the ECharts series built for ghost sources (matched by index):
249
+ *
250
+ * - line (and area) series: dashed, faded line and points, no area fill,
251
+ * no points by default, drawn under the real series
252
+ * - bar series: same colour at {@link GHOST_BAR_OPACITY}, in a parallel
253
+ * stack group so they pair side by side with the real bars
254
+ *
255
+ * Ghost series lose their value labels (series and per-point). Series
256
+ * without a ghost source are returned unchanged. Apply after building the
257
+ * series and before reference marks, forecast lines or totals are added.
258
+ */
259
+ declare function withGhostSeries<S extends AnyObject$1>(series: S[], sources: readonly (GhostSource | undefined)[]): S[];
260
+
154
261
  /**
155
262
  * Builders for the label, axis, tooltip and legend parts of a chart option.
156
263
  *
@@ -268,11 +375,13 @@ interface BuildTooltipOptions {
268
375
  valueFormatter?: (value: number) => string;
269
376
  /** Per-point label lookup, exposed to formatter functions as `label` */
270
377
  getLabel?: PointLabelLookup;
378
+ /** Tag for forecast values by data index (see `forecastTagLookup`) */
379
+ forecastTag?: (dataIndex: number) => string | undefined;
271
380
  }
272
381
  /**
273
382
  * Build the `tooltip` component. `base` is the chart's default tooltip.
274
383
  */
275
- declare function buildTooltip(base: AnyObject, { show, formatter, valueFormatter, getLabel }: BuildTooltipOptions): AnyObject | undefined;
384
+ declare function buildTooltip(base: AnyObject, { show, formatter, valueFormatter, getLabel, forecastTag }: BuildTooltipOptions): AnyObject | undefined;
276
385
  /**
277
386
  * Format a value with `tooltipValueFormatter`, for charts whose default
278
387
  * tooltip is custom HTML (where ECharts' `valueFormatter` does not apply)
@@ -448,10 +557,12 @@ interface WaterfallBar {
448
557
  declare function waterfallLayout(steps: WaterfallStep[]): WaterfallBar[];
449
558
  /**
450
559
  * Convert stacked series to each category's share of 100%. Missing values
451
- * stay missing; a category whose values sum to zero gets zeros.
560
+ * stay missing; a category whose values sum to zero gets zeros. Ghost
561
+ * series (`variant: 'ghost'`) share their own 100%, apart from the rest.
452
562
  */
453
563
  declare function toPercentStack<S extends {
454
564
  data: (number | null)[];
565
+ variant?: 'ghost';
455
566
  }>(series: S[]): S[];
456
567
  /**
457
568
  * Each stack's total by category, keyed by stack id. A series stacks under
@@ -677,4 +788,4 @@ declare function downloadFile(content: string, filename: string, mimeType?: stri
677
788
  */
678
789
  declare function downloadCsv(table: ChartDataTable, filename: string): void;
679
790
 
680
- 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 };
791
+ 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, GHOST_BAR_OPACITY, GHOST_LINE_OPACITY, type GhostSource, 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, ghostStackId, gridInset, hasForecast, hasGhostSeries, hasPointLabels, heatmapTable, histogramTable, isGhostSeries, 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, withGhostSeries, withPointLabels, withReferenceMarks, withZoomInset, withoutGhostSeries };
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) {
@@ -296,7 +453,7 @@ function getReplaceMerge(prev, next, isComponentType) {
296
453
  }
297
454
  return [...types].filter((type) => isComponentType(type));
298
455
  }
299
- function isPlainObject(value) {
456
+ function isPlainObject2(value) {
300
457
  if (value === null || typeof value !== "object") return false;
301
458
  const proto = Object.getPrototypeOf(value);
302
459
  return proto === Object.prototype || proto === null;
@@ -309,7 +466,7 @@ function mergeValue(base, override, topLevel = false) {
309
466
  const length = Math.max(from.length, to.length);
310
467
  return Array.from({ length }, (_, index) => mergeValue(from[index], to[index]));
311
468
  }
312
- if (isPlainObject(base) && isPlainObject(override)) {
469
+ if (isPlainObject2(base) && isPlainObject2(override)) {
313
470
  const merged = { ...base };
314
471
  for (const key of Object.keys(override)) {
315
472
  merged[key] = mergeValue(base[key], override[key]);
@@ -328,6 +485,66 @@ function mergeChartOption(generated, override) {
328
485
  return merged;
329
486
  }
330
487
 
488
+ // src/utils/ghost.ts
489
+ var GHOST_LINE_OPACITY = 0.6;
490
+ var GHOST_BAR_OPACITY = 0.35;
491
+ var GHOST_STACK = "\0ghost";
492
+ function isGhostSeries(source) {
493
+ return source?.variant === "ghost";
494
+ }
495
+ function hasGhostSeries(series) {
496
+ return !!series?.some(isGhostSeries);
497
+ }
498
+ function withoutGhostSeries(series) {
499
+ return series.filter((s) => !isGhostSeries(s));
500
+ }
501
+ function ghostStackId(stack, source) {
502
+ if (stack === void 0 || !isGhostSeries(source) || stack.endsWith(GHOST_STACK)) return stack;
503
+ return `${stack}${GHOST_STACK}`;
504
+ }
505
+ var isPlainObject3 = (item) => typeof item === "object" && item !== null && !Array.isArray(item);
506
+ function withoutItemLabels(data) {
507
+ if (!Array.isArray(data)) return data;
508
+ return data.map(
509
+ (item) => isPlainObject3(item) && item.label ? { ...item, label: { ...item.label, show: false } } : item
510
+ );
511
+ }
512
+ function withGhostSeries(series, sources) {
513
+ if (!hasGhostSeries(sources)) return series;
514
+ return series.map((s, index) => {
515
+ const source = sources[index];
516
+ if (!isGhostSeries(source)) return s;
517
+ const common = {
518
+ ...s,
519
+ stack: ghostStackId(s.stack, source),
520
+ data: withoutItemLabels(s.data),
521
+ label: { ...s.label, show: false }
522
+ };
523
+ if (s.type === "bar") {
524
+ return {
525
+ ...common,
526
+ itemStyle: { ...s.itemStyle, opacity: GHOST_BAR_OPACITY },
527
+ emphasis: {
528
+ ...s.emphasis,
529
+ itemStyle: { ...s.emphasis?.itemStyle, opacity: GHOST_BAR_OPACITY + 0.2 }
530
+ }
531
+ };
532
+ }
533
+ return {
534
+ ...common,
535
+ showSymbol: false,
536
+ areaStyle: void 0,
537
+ lineStyle: { ...s.lineStyle, type: "dashed", opacity: GHOST_LINE_OPACITY },
538
+ itemStyle: { ...s.itemStyle, opacity: GHOST_LINE_OPACITY },
539
+ emphasis: {
540
+ ...s.emphasis,
541
+ areaStyle: void 0
542
+ },
543
+ z: (s.z ?? 2) - 1
544
+ };
545
+ });
546
+ }
547
+
331
548
  // src/utils/stats.ts
332
549
  var isFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value);
333
550
  var clean = (value) => Number(value.toPrecision(12));
@@ -480,20 +697,26 @@ function waterfallLayout(steps) {
480
697
  }
481
698
  function toPercentStack(series) {
482
699
  const length = Math.max(0, ...series.map((s) => s.data.length));
483
- const totals = Array.from(
700
+ const sumOf = (ghost) => Array.from(
484
701
  { length },
485
702
  (_, index) => series.reduce((sum, s) => {
703
+ if (s.variant === "ghost" !== ghost) return sum;
486
704
  const value = s.data[index];
487
705
  return sum + (isFiniteNumber(value) ? Math.abs(value) : 0);
488
706
  }, 0)
489
707
  );
490
- return series.map((s) => ({
491
- ...s,
492
- data: s.data.map((value, index) => {
493
- if (!isFiniteNumber(value)) return null;
494
- return totals[index] === 0 ? 0 : clean(value / totals[index] * 100);
495
- })
496
- }));
708
+ const totals = sumOf(false);
709
+ const ghostTotals = sumOf(true);
710
+ return series.map((s) => {
711
+ const sums = s.variant === "ghost" ? ghostTotals : totals;
712
+ return {
713
+ ...s,
714
+ data: s.data.map((value, index) => {
715
+ if (!isFiniteNumber(value)) return null;
716
+ return sums[index] === 0 ? 0 : clean(value / sums[index] * 100);
717
+ })
718
+ };
719
+ });
497
720
  }
498
721
  function stackTotals(series, stacked) {
499
722
  const length = Math.max(0, ...series.map((s) => s.data.length));
@@ -717,12 +940,17 @@ function nameLabelLookup(nodes) {
717
940
  function hasPointLabels(series) {
718
941
  return series.some((s) => s.labels?.some((text) => text != null && text !== ""));
719
942
  }
720
- function buildTooltip(base, { show, formatter, valueFormatter, getLabel }) {
943
+ function buildTooltip(base, { show, formatter, valueFormatter, getLabel, forecastTag }) {
721
944
  if (!show) return void 0;
722
- if (formatter === void 0 && valueFormatter === void 0) return base;
945
+ if (formatter === void 0 && valueFormatter === void 0 && !forecastTag) return base;
723
946
  const tooltip = { ...base };
724
- if (valueFormatter) {
725
- tooltip.valueFormatter = (value) => typeof value === "number" ? valueFormatter(value) : String(value ?? "");
947
+ if (valueFormatter || forecastTag) {
948
+ const format = (value) => typeof value === "number" ? valueFormatter ? valueFormatter(value) : value.toLocaleString() : String(value ?? "");
949
+ tooltip.valueFormatter = (value, dataIndex) => {
950
+ const text = format(value);
951
+ const tag = dataIndex === void 0 ? void 0 : forecastTag?.(dataIndex);
952
+ return tag && value != null ? `${text} (${tag})` : text;
953
+ };
726
954
  }
727
955
  if (typeof formatter === "string") {
728
956
  tooltip.formatter = formatter;
@@ -1266,4 +1494,4 @@ function downloadCsv(table, filename) {
1266
1494
  );
1267
1495
  }
1268
1496
 
1269
- 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 };
1497
+ export { DEFAULT_FORECAST_LABEL, GHOST_BAR_OPACITY, GHOST_LINE_OPACITY, 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, ghostStackId, gridInset, hasForecast, hasGhostSeries, hasPointLabels, heatmapTable, histogramTable, isGhostSeries, 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, withGhostSeries, withPointLabels, withReferenceMarks, withZoomInset, withoutGhostSeries };
@@ -24,6 +24,22 @@ 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;
36
+ /**
37
+ * Draw the series as a faded reference (e.g. the prior period beside the
38
+ * current one): lines and areas dashed and faded with no fill or points,
39
+ * bars faded beside the solid ones rather than stacked onto them. Ghost
40
+ * series carry no value labels and are left out of bar totals.
41
+ */
42
+ variant?: 'ghost';
27
43
  }
28
44
  interface BarChartData {
29
45
  /** Category labels */
@@ -45,6 +61,22 @@ interface BarSeriesData {
45
61
  * chart's `stacked` prop for this series.
46
62
  */
47
63
  stack?: string;
64
+ /**
65
+ * Index of the first forecast point: from here to the end of the series the
66
+ * values are projections (or a period still in progress). Lines draw that
67
+ * stretch dashed with hollow points, bars draw those bars faded with a
68
+ * dashed outline, and the tooltip tags those values with `forecastLabel`.
69
+ */
70
+ forecastFrom?: number;
71
+ /** Tooltip tag for forecast values (defaults to 'Forecast') */
72
+ forecastLabel?: string;
73
+ /**
74
+ * Draw the series as a faded reference (e.g. the prior period beside the
75
+ * current one): lines and areas dashed and faded with no fill or points,
76
+ * bars faded beside the solid ones rather than stacked onto them. Ghost
77
+ * series carry no value labels and are left out of bar totals.
78
+ */
79
+ variant?: 'ghost';
48
80
  }
49
81
  interface PieSliceData {
50
82
  /** Slice name (shown in legend/tooltip) */
@@ -76,6 +108,22 @@ interface AreaSeriesData {
76
108
  * chart's `stacked` prop for this series.
77
109
  */
78
110
  stack?: string;
111
+ /**
112
+ * Index of the first forecast point: from here to the end of the series the
113
+ * values are projections (or a period still in progress). Lines draw that
114
+ * stretch dashed with hollow points, bars draw those bars faded with a
115
+ * dashed outline, and the tooltip tags those values with `forecastLabel`.
116
+ */
117
+ forecastFrom?: number;
118
+ /** Tooltip tag for forecast values (defaults to 'Forecast') */
119
+ forecastLabel?: string;
120
+ /**
121
+ * Draw the series as a faded reference (e.g. the prior period beside the
122
+ * current one): lines and areas dashed and faded with no fill or points,
123
+ * bars faded beside the solid ones rather than stacked onto them. Ghost
124
+ * series carry no value labels and are left out of bar totals.
125
+ */
126
+ variant?: 'ghost';
79
127
  }
80
128
  interface ScatterChartData {
81
129
  /** Data series */
@@ -216,6 +264,22 @@ interface CategorySeriesData {
216
264
  labels?: (string | null | undefined)[];
217
265
  /** Stack group id */
218
266
  stack?: string;
267
+ /**
268
+ * Index of the first forecast point: from here to the end of the series the
269
+ * values are projections (or a period still in progress). Lines draw that
270
+ * stretch dashed with hollow points, bars draw those bars faded with a
271
+ * dashed outline, and the tooltip tags those values with `forecastLabel`.
272
+ */
273
+ forecastFrom?: number;
274
+ /** Tooltip tag for forecast values (defaults to 'Forecast') */
275
+ forecastLabel?: string;
276
+ /**
277
+ * Draw the series as a faded reference (e.g. the prior period beside the
278
+ * current one): lines and areas dashed and faded with no fill or points,
279
+ * bars faded beside the solid ones rather than stacked onto them. Ghost
280
+ * series carry no value labels and are left out of bar totals.
281
+ */
282
+ variant?: 'ghost';
219
283
  }
220
284
  interface ComboChartData {
221
285
  /** Category labels */
@@ -1162,6 +1226,34 @@ interface StatDelta {
1162
1226
  /** Text after the change, e.g. 'vs last year' */
1163
1227
  label?: string;
1164
1228
  }
1229
+ /**
1230
+ * Delta text: the change between two values as a coloured ▲/▼ with the
1231
+ * change and a "vs previous" label (the change line of `StatCard`, for KPI
1232
+ * rows, table cells and other inline use). Renders nothing when either value
1233
+ * is missing.
1234
+ */
1235
+ interface DeltaTextProps {
1236
+ /** The current value */
1237
+ current: number | null | undefined;
1238
+ /** The value being compared against (e.g. last year's) */
1239
+ previous: number | null | undefined;
1240
+ /**
1241
+ * How the change is shown: 'percent' (+12%), 'absolute' (+12, using
1242
+ * `valueFormat`) or 'both' (defaults to 'percent'). A change from zero
1243
+ * has no percentage, so it shows the absolute change.
1244
+ */
1245
+ display?: 'percent' | 'absolute' | 'both';
1246
+ /** Which direction is good, for colouring (defaults to 'up'); 'none' stays neutral */
1247
+ goodWhen?: 'up' | 'down' | 'none';
1248
+ /** How absolute changes and the previous value are written */
1249
+ valueFormat?: ValueFormat;
1250
+ /** Locale for `valueFormat` and the percentage (defaults to 'en-US') */
1251
+ locale?: string;
1252
+ /** Text after the change (defaults to 'vs <previous>'; '' hides it) */
1253
+ label?: string;
1254
+ /** Custom CSS class */
1255
+ class?: string;
1256
+ }
1165
1257
  /**
1166
1258
  * Stat card: a headline metric with its change and trend
1167
1259
  */
@@ -1196,4 +1288,4 @@ interface StatCardProps {
1196
1288
  class?: string;
1197
1289
  }
1198
1290
 
1199
- export type { AreaChartData, AreaChartProps, AreaSeriesData, AxisChartProps, AxisConfig, BarChartData, BarChartProps, BarSeriesData, BaseChartProps, BaseEventParams, BoxPlotChartProps, BoxPlotGroup, BoxPlotStats, BulletChartProps, BulletItem, CandlestickChartProps, CandlestickData, CandlestickEventParams, CandlestickItem, CategoryChartData, CategorySeriesData, ChartDataTable, ChartEventHandlers, ChartExportConfig, ChartFormatter, ChartLegendConfig, ChartLegendPosition, ComboChartData, ComboChartProps, ComboSeriesData, DataPointEventParams, DonutChartProps, DotPlotChartData, DotPlotChartProps, DotPlotSeriesData, FunnelChartProps, FunnelSliceData, GaugeChartProps, HeatmapChartProps, HeatmapData, HeatmapEventParams, HistogramBin, HistogramBucket, HistogramChartProps, LabelChartProps, LabelFormatterParams, LegendChartProps, LegendItem, LegendRenderProps, LineChartData, LineChartProps, LineSeriesData, PieChartProps, PieSliceData, RadarChartData, RadarChartProps, RadarIndicator, RadarSeriesData, ReferenceBand, ReferenceChartProps, ReferenceLine, RowHeightProps, SankeyChartProps, SankeyData, SankeyEventParams, SankeyLink, SankeyNode, ScatterChartData, ScatterChartProps, ScatterSeriesData, SliceEventParams, SparklineProps, StatCardProps, StatDelta, TooltipFormatterParams, TreemapChartProps, TreemapEventParams, TreemapLevel, TreemapNode, ValueFormat, ValueFormatPreset, WaterfallChartProps, WaterfallStep, ZoomChartProps };
1291
+ export type { AreaChartData, AreaChartProps, AreaSeriesData, AxisChartProps, AxisConfig, BarChartData, BarChartProps, BarSeriesData, BaseChartProps, BaseEventParams, BoxPlotChartProps, BoxPlotGroup, BoxPlotStats, BulletChartProps, BulletItem, CandlestickChartProps, CandlestickData, CandlestickEventParams, CandlestickItem, CategoryChartData, CategorySeriesData, ChartDataTable, ChartEventHandlers, ChartExportConfig, ChartFormatter, ChartLegendConfig, ChartLegendPosition, ComboChartData, ComboChartProps, ComboSeriesData, DataPointEventParams, DeltaTextProps, DonutChartProps, DotPlotChartData, DotPlotChartProps, DotPlotSeriesData, FunnelChartProps, FunnelSliceData, GaugeChartProps, HeatmapChartProps, HeatmapData, HeatmapEventParams, HistogramBin, HistogramBucket, HistogramChartProps, LabelChartProps, LabelFormatterParams, LegendChartProps, LegendItem, LegendRenderProps, LineChartData, LineChartProps, LineSeriesData, PieChartProps, PieSliceData, RadarChartData, RadarChartProps, RadarIndicator, RadarSeriesData, ReferenceBand, ReferenceChartProps, ReferenceLine, RowHeightProps, SankeyChartProps, SankeyData, SankeyEventParams, SankeyLink, SankeyNode, ScatterChartData, ScatterChartProps, ScatterSeriesData, SliceEventParams, SparklineProps, StatCardProps, StatDelta, TooltipFormatterParams, TreemapChartProps, TreemapEventParams, TreemapLevel, TreemapNode, ValueFormat, ValueFormatPreset, WaterfallChartProps, WaterfallStep, ZoomChartProps };
@@ -24,6 +24,22 @@ 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;
36
+ /**
37
+ * Draw the series as a faded reference (e.g. the prior period beside the
38
+ * current one): lines and areas dashed and faded with no fill or points,
39
+ * bars faded beside the solid ones rather than stacked onto them. Ghost
40
+ * series carry no value labels and are left out of bar totals.
41
+ */
42
+ variant?: 'ghost';
27
43
  }
28
44
  interface BarChartData {
29
45
  /** Category labels */
@@ -45,6 +61,22 @@ interface BarSeriesData {
45
61
  * chart's `stacked` prop for this series.
46
62
  */
47
63
  stack?: string;
64
+ /**
65
+ * Index of the first forecast point: from here to the end of the series the
66
+ * values are projections (or a period still in progress). Lines draw that
67
+ * stretch dashed with hollow points, bars draw those bars faded with a
68
+ * dashed outline, and the tooltip tags those values with `forecastLabel`.
69
+ */
70
+ forecastFrom?: number;
71
+ /** Tooltip tag for forecast values (defaults to 'Forecast') */
72
+ forecastLabel?: string;
73
+ /**
74
+ * Draw the series as a faded reference (e.g. the prior period beside the
75
+ * current one): lines and areas dashed and faded with no fill or points,
76
+ * bars faded beside the solid ones rather than stacked onto them. Ghost
77
+ * series carry no value labels and are left out of bar totals.
78
+ */
79
+ variant?: 'ghost';
48
80
  }
49
81
  interface PieSliceData {
50
82
  /** Slice name (shown in legend/tooltip) */
@@ -76,6 +108,22 @@ interface AreaSeriesData {
76
108
  * chart's `stacked` prop for this series.
77
109
  */
78
110
  stack?: string;
111
+ /**
112
+ * Index of the first forecast point: from here to the end of the series the
113
+ * values are projections (or a period still in progress). Lines draw that
114
+ * stretch dashed with hollow points, bars draw those bars faded with a
115
+ * dashed outline, and the tooltip tags those values with `forecastLabel`.
116
+ */
117
+ forecastFrom?: number;
118
+ /** Tooltip tag for forecast values (defaults to 'Forecast') */
119
+ forecastLabel?: string;
120
+ /**
121
+ * Draw the series as a faded reference (e.g. the prior period beside the
122
+ * current one): lines and areas dashed and faded with no fill or points,
123
+ * bars faded beside the solid ones rather than stacked onto them. Ghost
124
+ * series carry no value labels and are left out of bar totals.
125
+ */
126
+ variant?: 'ghost';
79
127
  }
80
128
  interface ScatterChartData {
81
129
  /** Data series */
@@ -216,6 +264,22 @@ interface CategorySeriesData {
216
264
  labels?: (string | null | undefined)[];
217
265
  /** Stack group id */
218
266
  stack?: string;
267
+ /**
268
+ * Index of the first forecast point: from here to the end of the series the
269
+ * values are projections (or a period still in progress). Lines draw that
270
+ * stretch dashed with hollow points, bars draw those bars faded with a
271
+ * dashed outline, and the tooltip tags those values with `forecastLabel`.
272
+ */
273
+ forecastFrom?: number;
274
+ /** Tooltip tag for forecast values (defaults to 'Forecast') */
275
+ forecastLabel?: string;
276
+ /**
277
+ * Draw the series as a faded reference (e.g. the prior period beside the
278
+ * current one): lines and areas dashed and faded with no fill or points,
279
+ * bars faded beside the solid ones rather than stacked onto them. Ghost
280
+ * series carry no value labels and are left out of bar totals.
281
+ */
282
+ variant?: 'ghost';
219
283
  }
220
284
  interface ComboChartData {
221
285
  /** Category labels */
@@ -1162,6 +1226,34 @@ interface StatDelta {
1162
1226
  /** Text after the change, e.g. 'vs last year' */
1163
1227
  label?: string;
1164
1228
  }
1229
+ /**
1230
+ * Delta text: the change between two values as a coloured ▲/▼ with the
1231
+ * change and a "vs previous" label (the change line of `StatCard`, for KPI
1232
+ * rows, table cells and other inline use). Renders nothing when either value
1233
+ * is missing.
1234
+ */
1235
+ interface DeltaTextProps {
1236
+ /** The current value */
1237
+ current: number | null | undefined;
1238
+ /** The value being compared against (e.g. last year's) */
1239
+ previous: number | null | undefined;
1240
+ /**
1241
+ * How the change is shown: 'percent' (+12%), 'absolute' (+12, using
1242
+ * `valueFormat`) or 'both' (defaults to 'percent'). A change from zero
1243
+ * has no percentage, so it shows the absolute change.
1244
+ */
1245
+ display?: 'percent' | 'absolute' | 'both';
1246
+ /** Which direction is good, for colouring (defaults to 'up'); 'none' stays neutral */
1247
+ goodWhen?: 'up' | 'down' | 'none';
1248
+ /** How absolute changes and the previous value are written */
1249
+ valueFormat?: ValueFormat;
1250
+ /** Locale for `valueFormat` and the percentage (defaults to 'en-US') */
1251
+ locale?: string;
1252
+ /** Text after the change (defaults to 'vs <previous>'; '' hides it) */
1253
+ label?: string;
1254
+ /** Custom CSS class */
1255
+ class?: string;
1256
+ }
1165
1257
  /**
1166
1258
  * Stat card: a headline metric with its change and trend
1167
1259
  */
@@ -1196,4 +1288,4 @@ interface StatCardProps {
1196
1288
  class?: string;
1197
1289
  }
1198
1290
 
1199
- export type { AreaChartData, AreaChartProps, AreaSeriesData, AxisChartProps, AxisConfig, BarChartData, BarChartProps, BarSeriesData, BaseChartProps, BaseEventParams, BoxPlotChartProps, BoxPlotGroup, BoxPlotStats, BulletChartProps, BulletItem, CandlestickChartProps, CandlestickData, CandlestickEventParams, CandlestickItem, CategoryChartData, CategorySeriesData, ChartDataTable, ChartEventHandlers, ChartExportConfig, ChartFormatter, ChartLegendConfig, ChartLegendPosition, ComboChartData, ComboChartProps, ComboSeriesData, DataPointEventParams, DonutChartProps, DotPlotChartData, DotPlotChartProps, DotPlotSeriesData, FunnelChartProps, FunnelSliceData, GaugeChartProps, HeatmapChartProps, HeatmapData, HeatmapEventParams, HistogramBin, HistogramBucket, HistogramChartProps, LabelChartProps, LabelFormatterParams, LegendChartProps, LegendItem, LegendRenderProps, LineChartData, LineChartProps, LineSeriesData, PieChartProps, PieSliceData, RadarChartData, RadarChartProps, RadarIndicator, RadarSeriesData, ReferenceBand, ReferenceChartProps, ReferenceLine, RowHeightProps, SankeyChartProps, SankeyData, SankeyEventParams, SankeyLink, SankeyNode, ScatterChartData, ScatterChartProps, ScatterSeriesData, SliceEventParams, SparklineProps, StatCardProps, StatDelta, TooltipFormatterParams, TreemapChartProps, TreemapEventParams, TreemapLevel, TreemapNode, ValueFormat, ValueFormatPreset, WaterfallChartProps, WaterfallStep, ZoomChartProps };
1291
+ export type { AreaChartData, AreaChartProps, AreaSeriesData, AxisChartProps, AxisConfig, BarChartData, BarChartProps, BarSeriesData, BaseChartProps, BaseEventParams, BoxPlotChartProps, BoxPlotGroup, BoxPlotStats, BulletChartProps, BulletItem, CandlestickChartProps, CandlestickData, CandlestickEventParams, CandlestickItem, CategoryChartData, CategorySeriesData, ChartDataTable, ChartEventHandlers, ChartExportConfig, ChartFormatter, ChartLegendConfig, ChartLegendPosition, ComboChartData, ComboChartProps, ComboSeriesData, DataPointEventParams, DeltaTextProps, DonutChartProps, DotPlotChartData, DotPlotChartProps, DotPlotSeriesData, FunnelChartProps, FunnelSliceData, GaugeChartProps, HeatmapChartProps, HeatmapData, HeatmapEventParams, HistogramBin, HistogramBucket, HistogramChartProps, LabelChartProps, LabelFormatterParams, LegendChartProps, LegendItem, LegendRenderProps, LineChartData, LineChartProps, LineSeriesData, PieChartProps, PieSliceData, RadarChartData, RadarChartProps, RadarIndicator, RadarSeriesData, ReferenceBand, ReferenceChartProps, ReferenceLine, RowHeightProps, SankeyChartProps, SankeyData, SankeyEventParams, SankeyLink, SankeyNode, ScatterChartData, ScatterChartProps, ScatterSeriesData, SliceEventParams, SparklineProps, StatCardProps, StatDelta, TooltipFormatterParams, TreemapChartProps, TreemapEventParams, TreemapLevel, TreemapNode, ValueFormat, ValueFormatPreset, WaterfallChartProps, WaterfallStep, ZoomChartProps };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@classic-homes/charts-core",
3
- "version": "0.4.2",
3
+ "version": "0.6.0",
4
4
  "description": "Core utilities and theme for Classic Theme charts",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",