@rowsncolumns/charts 28.1.8 → 28.1.11
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/cjs/basic-chart/basic-chart.d.ts.map +1 -1
- package/dist/cjs/basic-chart/basic-chart.js +538 -430
- package/dist/cjs/basic-chart/helpers.d.ts.map +1 -1
- package/dist/cjs/basic-chart/helpers.js +2 -0
- package/dist/cjs/plotly/basic-chart/basic-chart.d.ts.map +1 -1
- package/dist/cjs/plotly/basic-chart/basic-chart.js +333 -184
- package/dist/cjs/plotly/basic-chart/helpers.d.ts.map +1 -1
- package/dist/cjs/plotly/basic-chart/helpers.js +2 -0
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/cjs/utils/bubble.d.ts +34 -1
- package/dist/cjs/utils/bubble.d.ts.map +1 -1
- package/dist/cjs/utils/bubble.js +39 -13
- package/dist/cjs/utils/echarts-overlays.d.ts +46 -0
- package/dist/cjs/utils/echarts-overlays.d.ts.map +1 -0
- package/dist/cjs/utils/echarts-overlays.js +134 -0
- package/dist/cjs/utils/hierarchy.d.ts +23 -1
- package/dist/cjs/utils/hierarchy.d.ts.map +1 -1
- package/dist/cjs/utils/hierarchy.js +179 -55
- package/dist/cjs/utils/overrides.d.ts.map +1 -1
- package/dist/cjs/utils/overrides.js +15 -3
- package/dist/cjs/utils/trendline.d.ts +7 -1
- package/dist/cjs/utils/trendline.d.ts.map +1 -1
- package/dist/cjs/utils/trendline.js +82 -18
- package/dist/esm/basic-chart/basic-chart.d.ts.map +1 -1
- package/dist/esm/basic-chart/basic-chart.js +381 -251
- package/dist/esm/basic-chart/helpers.d.ts.map +1 -1
- package/dist/esm/basic-chart/helpers.js +2 -0
- package/dist/esm/plotly/basic-chart/basic-chart.d.ts.map +1 -1
- package/dist/esm/plotly/basic-chart/basic-chart.js +253 -58
- package/dist/esm/plotly/basic-chart/helpers.d.ts.map +1 -1
- package/dist/esm/plotly/basic-chart/helpers.js +2 -0
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/esm/utils/bubble.d.ts +34 -1
- package/dist/esm/utils/bubble.d.ts.map +1 -1
- package/dist/esm/utils/bubble.js +35 -11
- package/dist/esm/utils/echarts-overlays.d.ts +46 -0
- package/dist/esm/utils/echarts-overlays.d.ts.map +1 -0
- package/dist/esm/utils/echarts-overlays.js +135 -0
- package/dist/esm/utils/hierarchy.d.ts +23 -1
- package/dist/esm/utils/hierarchy.d.ts.map +1 -1
- package/dist/esm/utils/hierarchy.js +168 -50
- package/dist/esm/utils/overrides.d.ts.map +1 -1
- package/dist/esm/utils/overrides.js +15 -3
- package/dist/esm/utils/trendline.d.ts +7 -1
- package/dist/esm/utils/trendline.d.ts.map +1 -1
- package/dist/esm/utils/trendline.js +67 -14
- package/package.json +3 -3
package/dist/esm/utils/bubble.js
CHANGED
|
@@ -12,22 +12,45 @@ export const toNumericValue = (value) => {
|
|
|
12
12
|
}
|
|
13
13
|
return null;
|
|
14
14
|
};
|
|
15
|
-
|
|
15
|
+
/** The largest bubble's diameter at bubbleScale 100: a quarter of the plot area's smaller side. */
|
|
16
|
+
export const BUBBLE_MAX_DIAMETER_FRACTION = 0.25;
|
|
17
|
+
/** Fallback plot side when the chart has no measured size yet. */
|
|
18
|
+
const DEFAULT_PLOT_MIN_SIDE = 240;
|
|
19
|
+
/** A positive size never collapses below this diameter. */
|
|
20
|
+
const MIN_BUBBLE_DIAMETER = 2;
|
|
21
|
+
/**
|
|
22
|
+
* Pixel diameter per size value, scaled from ZERO like Excel: the largest
|
|
23
|
+
* absolute value gets the maximum diameter and every other bubble's area
|
|
24
|
+
* (or width) is its share of that value. Scaling from the minimum value
|
|
25
|
+
* instead made the ratio between bubbles depend on the spread of the
|
|
26
|
+
* data (100 and 101 drew as the smallest and largest bubble), and a
|
|
27
|
+
* linear diameter made the perceived area grow with the square of the
|
|
28
|
+
* value (rowsncolumns/spreadsheet#1068).
|
|
29
|
+
*/
|
|
30
|
+
export const createBubbleSizeScale = (values, options = {}) => {
|
|
31
|
+
const maxDiameter = bubbleMaxDiameter(options);
|
|
16
32
|
const valid = values.filter((value) => Number.isFinite(value));
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
const min = Math.min(...valid);
|
|
21
|
-
const max = Math.max(...valid);
|
|
22
|
-
const range = max - min || 1;
|
|
33
|
+
const maxAbs = valid.reduce((max, value) => Math.max(max, Math.abs(value)), 0);
|
|
34
|
+
const byWidth = options.sizeRepresents === "width";
|
|
23
35
|
return (value) => {
|
|
24
|
-
if (!Number.isFinite(value)) {
|
|
25
|
-
return
|
|
36
|
+
if (!Number.isFinite(value) || maxAbs === 0) {
|
|
37
|
+
return maxDiameter;
|
|
26
38
|
}
|
|
27
|
-
const
|
|
28
|
-
|
|
39
|
+
const share = Math.abs(value) / maxAbs;
|
|
40
|
+
const diameter = maxDiameter * (byWidth ? share : Math.sqrt(share));
|
|
41
|
+
return share > 0 ? Math.max(MIN_BUBBLE_DIAMETER, diameter) : 0;
|
|
29
42
|
};
|
|
30
43
|
};
|
|
44
|
+
/** The diameter of the largest bubble for these options. */
|
|
45
|
+
export const bubbleMaxDiameter = (options = {}) => {
|
|
46
|
+
const plotMinSide = typeof options.plotMinSide === "number" && options.plotMinSide > 0
|
|
47
|
+
? options.plotMinSide
|
|
48
|
+
: DEFAULT_PLOT_MIN_SIDE;
|
|
49
|
+
const scale = typeof options.bubbleScale === "number" && Number.isFinite(options.bubbleScale)
|
|
50
|
+
? Math.min(300, Math.max(0, options.bubbleScale)) / 100
|
|
51
|
+
: 1;
|
|
52
|
+
return plotMinSide * BUBBLE_MAX_DIAMETER_FRACTION * scale;
|
|
53
|
+
};
|
|
31
54
|
/**
|
|
32
55
|
* Build the X-coordinate index for a series. Numeric values map to
|
|
33
56
|
* their own coordinate; string values become categorical (deduped into
|
|
@@ -107,6 +130,7 @@ export const prepareBubbleSeriesData = ({ xValues, labelValues, sizeValues, seri
|
|
|
107
130
|
value: [coordinate.coordinate, y, sizeValue],
|
|
108
131
|
name: label,
|
|
109
132
|
xLabel: coordinateLabel,
|
|
133
|
+
index: i,
|
|
110
134
|
});
|
|
111
135
|
}
|
|
112
136
|
const name = serie.name && serie.name.trim()
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
type OverlaySeriesSpec = {
|
|
2
|
+
color?: string | null;
|
|
3
|
+
useSecondaryAxis?: boolean | null;
|
|
4
|
+
trendlines?: Array<{
|
|
5
|
+
type?: any;
|
|
6
|
+
order?: number | null;
|
|
7
|
+
period?: number | null;
|
|
8
|
+
name?: string | null;
|
|
9
|
+
color?: string | null;
|
|
10
|
+
forecastForward?: number | null;
|
|
11
|
+
forecastBackward?: number | null;
|
|
12
|
+
}> | null;
|
|
13
|
+
errorBars?: {
|
|
14
|
+
direction?: "x" | "y" | null;
|
|
15
|
+
barType?: "both" | "plus" | "minus" | null;
|
|
16
|
+
valueType?: any;
|
|
17
|
+
value?: number | null;
|
|
18
|
+
noEndCap?: boolean | null;
|
|
19
|
+
plusRange?: any;
|
|
20
|
+
minusRange?: any;
|
|
21
|
+
} | null;
|
|
22
|
+
};
|
|
23
|
+
export type EchartsOverlayOptions<TRange = unknown> = {
|
|
24
|
+
series: OverlaySeriesSpec;
|
|
25
|
+
/** The series' values (Y on an XY chart), index-aligned with `xValues`. */
|
|
26
|
+
values: (number | null | undefined)[];
|
|
27
|
+
/**
|
|
28
|
+
* Scatter / bubble: the real X coordinate per point. Omitted for a
|
|
29
|
+
* category chart, where the point index is the category coordinate.
|
|
30
|
+
*/
|
|
31
|
+
xValues?: (number | null | undefined)[] | null;
|
|
32
|
+
/** Horizontal bar: the category index is the y coordinate. */
|
|
33
|
+
horizontal?: boolean;
|
|
34
|
+
dataLabel: string;
|
|
35
|
+
/** The data series' own colour — overlays default to it. */
|
|
36
|
+
seriesColor: string;
|
|
37
|
+
/** Reads a custom error bar's plus / minus range. */
|
|
38
|
+
resolveRange: (range: TRange) => unknown[] | null | undefined;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Build the overlay series for one data series. Returns an empty array
|
|
42
|
+
* when the series has neither trendlines nor error bars.
|
|
43
|
+
*/
|
|
44
|
+
export declare const buildEchartsOverlaySeries: <TRange = unknown>({ series, values, xValues, horizontal, dataLabel, seriesColor, resolveRange, }: EchartsOverlayOptions<TRange>) => any[];
|
|
45
|
+
export {};
|
|
46
|
+
//# sourceMappingURL=echarts-overlays.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"echarts-overlays.d.ts","sourceRoot":"","sources":["../../../utils/echarts-overlays.ts"],"names":[],"mappings":"AAkBA,KAAK,iBAAiB,GAAG;IACvB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,gBAAgB,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAClC,UAAU,CAAC,EACP,KAAK,CAAC;QACJ,IAAI,CAAC,EAAE,GAAG,CAAC;QACX,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACvB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACrB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAChC,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAClC,CAAC,GACF,IAAI,CAAC;IACT,SAAS,CAAC,EAAE;QACV,SAAS,CAAC,EAAE,GAAG,GAAG,GAAG,GAAG,IAAI,CAAC;QAC7B,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;QAC3C,SAAS,CAAC,EAAE,GAAG,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,QAAQ,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;QAC1B,SAAS,CAAC,EAAE,GAAG,CAAC;QAChB,UAAU,CAAC,EAAE,GAAG,CAAC;KAClB,GAAG,IAAI,CAAC;CACV,CAAC;AAEF,MAAM,MAAM,qBAAqB,CAAC,MAAM,GAAG,OAAO,IAAI;IACpD,MAAM,EAAE,iBAAiB,CAAC;IAC1B,2EAA2E;IAC3E,MAAM,EAAE,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,CAAC;IACtC;;;OAGG;IACH,OAAO,CAAC,EAAE,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,GAAG,IAAI,CAAC;IAC/C,8DAA8D;IAC9D,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,4DAA4D;IAC5D,WAAW,EAAE,MAAM,CAAC;IACpB,qDAAqD;IACrD,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,EAAE,GAAG,IAAI,GAAG,SAAS,CAAC;CAC/D,CAAC;AAIF;;;GAGG;AACH,eAAO,MAAM,yBAAyB,GAAI,MAAM,GAAG,OAAO,EAAE,gFAQzD,qBAAqB,CAAC,MAAM,CAAC,KAAG,GAAG,EA4HrC,CAAC"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trendline and error-bar overlay series for the echarts renderer.
|
|
3
|
+
*
|
|
4
|
+
* One data series may carry trendlines (`<c:trendline>`) and error bars
|
|
5
|
+
* (`<c:errBars>`); both are drawn as extra echarts series appended after
|
|
6
|
+
* the data series. The same builder serves every cartesian layout:
|
|
7
|
+
*
|
|
8
|
+
* - category charts (line / column / area): the point index is the
|
|
9
|
+
* category coordinate, values sit on the value axis;
|
|
10
|
+
* - horizontal bars: the same, with the axes swapped — the category
|
|
11
|
+
* index is the y coordinate (rowsncolumns/spreadsheet#1061 drew the
|
|
12
|
+
* overlays on the swapped axes, outside the plot);
|
|
13
|
+
* - scatter / bubble: real (x, y) coordinates per point, and the
|
|
14
|
+
* trendline is a regression of Y on X (#1067).
|
|
15
|
+
*/
|
|
16
|
+
import { computeErrorBars, resolveCustomErrorBarValues } from "./error-bars";
|
|
17
|
+
import { computeTrendline } from "./trendline";
|
|
18
|
+
const ERROR_BAR_CAP_HALF = 5;
|
|
19
|
+
/**
|
|
20
|
+
* Build the overlay series for one data series. Returns an empty array
|
|
21
|
+
* when the series has neither trendlines nor error bars.
|
|
22
|
+
*/
|
|
23
|
+
export const buildEchartsOverlaySeries = ({ series, values, xValues, horizontal = false, dataLabel, seriesColor, resolveRange, }) => {
|
|
24
|
+
if (values.length === 0)
|
|
25
|
+
return [];
|
|
26
|
+
const overlays = [];
|
|
27
|
+
const isXY = Array.isArray(xValues);
|
|
28
|
+
const useSecondary = series.useSecondaryAxis ? { yAxisIndex: 1 } : {};
|
|
29
|
+
// Trendlines — one overlay line series per spec.
|
|
30
|
+
for (const tl of series.trendlines ?? []) {
|
|
31
|
+
const result = computeTrendline(values, {
|
|
32
|
+
type: tl.type,
|
|
33
|
+
order: tl.order,
|
|
34
|
+
period: tl.period,
|
|
35
|
+
forecastForward: tl.forecastForward,
|
|
36
|
+
forecastBackward: tl.forecastBackward,
|
|
37
|
+
}, isXY ? xValues : undefined);
|
|
38
|
+
if (!result || result.points.length < 2)
|
|
39
|
+
continue;
|
|
40
|
+
const tlName = tl.name ??
|
|
41
|
+
`${dataLabel ? dataLabel + " " : ""}${tl.type ?? "linear"} trend`;
|
|
42
|
+
overlays.push({
|
|
43
|
+
type: "line",
|
|
44
|
+
name: tlName,
|
|
45
|
+
// On a category chart the index x overlays the category axis even
|
|
46
|
+
// when forecasting extends beyond the data range; a horizontal bar
|
|
47
|
+
// keeps its categories on y.
|
|
48
|
+
data: result.points.map((p) => (horizontal ? [p.y, p.x] : [p.x, p.y])),
|
|
49
|
+
showSymbol: false,
|
|
50
|
+
smooth: tl.type === "poly",
|
|
51
|
+
lineStyle: {
|
|
52
|
+
color: tl.color ?? seriesColor,
|
|
53
|
+
type: "dashed",
|
|
54
|
+
width: 2,
|
|
55
|
+
},
|
|
56
|
+
itemStyle: { color: tl.color ?? seriesColor },
|
|
57
|
+
tooltip: { show: false },
|
|
58
|
+
z: 5,
|
|
59
|
+
...useSecondary,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
// Error bars — a custom series of whiskers. valueType "custom" reads
|
|
63
|
+
// per-point magnitudes from the spec's plus/minus ranges, resolved
|
|
64
|
+
// through the same extractor the series values use.
|
|
65
|
+
if (series.errorBars) {
|
|
66
|
+
const direction = series.errorBars.direction ?? "y";
|
|
67
|
+
// An X error bar on an XY chart sizes from the X values (a percentage
|
|
68
|
+
// or deviation of X); a category chart has only value-axis bars, so a
|
|
69
|
+
// horizontal bar's whisker runs along x whatever the direction says.
|
|
70
|
+
const alongX = isXY ? direction === "x" : horizontal || direction === "x";
|
|
71
|
+
const magnitudeSource = isXY && alongX ? xValues : values;
|
|
72
|
+
const magnitudes = computeErrorBars(magnitudeSource, resolveCustomErrorBarValues(series.errorBars, resolveRange));
|
|
73
|
+
if (magnitudes) {
|
|
74
|
+
const noEndCap = series.errorBars.noEndCap ?? false;
|
|
75
|
+
const errorData = [];
|
|
76
|
+
for (let i = 0; i < values.length; i++) {
|
|
77
|
+
const v = values[i];
|
|
78
|
+
const y = typeof v === "number" && Number.isFinite(v) ? v : null;
|
|
79
|
+
const x = isXY ? xValues?.[i] : i;
|
|
80
|
+
if (y === null || typeof x !== "number" || !Number.isFinite(x)) {
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
// [a, b] is the point's (x, y) data coordinate; a horizontal bar
|
|
84
|
+
// keeps its value on x and its category on y.
|
|
85
|
+
const [a, b] = !isXY && horizontal ? [y, x] : [x, y];
|
|
86
|
+
errorData.push([a, b, magnitudes.plus[i] ?? 0, magnitudes.minus[i] ?? 0]);
|
|
87
|
+
}
|
|
88
|
+
overlays.push({
|
|
89
|
+
type: "custom",
|
|
90
|
+
name: `${dataLabel ? dataLabel + " " : ""}error`,
|
|
91
|
+
data: errorData,
|
|
92
|
+
z: 6,
|
|
93
|
+
tooltip: { show: false },
|
|
94
|
+
...useSecondary,
|
|
95
|
+
renderItem: (_params, api) => {
|
|
96
|
+
const a = api.value(0);
|
|
97
|
+
const b = api.value(1);
|
|
98
|
+
const plus = api.value(2);
|
|
99
|
+
const minus = api.value(3);
|
|
100
|
+
const center = api.coord([a, b]);
|
|
101
|
+
const hi = alongX ? api.coord([a + plus, b]) : api.coord([a, b + plus]);
|
|
102
|
+
const lo = alongX ? api.coord([a - minus, b]) : api.coord([a, b - minus]);
|
|
103
|
+
const stroke = { stroke: seriesColor, lineWidth: 1 };
|
|
104
|
+
const children = [
|
|
105
|
+
{
|
|
106
|
+
type: "line",
|
|
107
|
+
shape: alongX
|
|
108
|
+
? { x1: lo[0], y1: center[1], x2: hi[0], y2: center[1] }
|
|
109
|
+
: { x1: center[0], y1: lo[1], x2: center[0], y2: hi[1] },
|
|
110
|
+
style: stroke,
|
|
111
|
+
},
|
|
112
|
+
];
|
|
113
|
+
if (!noEndCap) {
|
|
114
|
+
const cap = ERROR_BAR_CAP_HALF;
|
|
115
|
+
children.push({
|
|
116
|
+
type: "line",
|
|
117
|
+
shape: alongX
|
|
118
|
+
? { x1: hi[0], y1: center[1] - cap, x2: hi[0], y2: center[1] + cap }
|
|
119
|
+
: { x1: center[0] - cap, y1: hi[1], x2: center[0] + cap, y2: hi[1] },
|
|
120
|
+
style: stroke,
|
|
121
|
+
}, {
|
|
122
|
+
type: "line",
|
|
123
|
+
shape: alongX
|
|
124
|
+
? { x1: lo[0], y1: center[1] - cap, x2: lo[0], y2: center[1] + cap }
|
|
125
|
+
: { x1: center[0] - cap, y1: lo[1], x2: center[0] + cap, y2: lo[1] },
|
|
126
|
+
style: stroke,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
return { type: "group", children };
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return overlays;
|
|
135
|
+
};
|
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
import type { ChartSpec, SheetRange, TreemapChart } from "@rowsncolumns/spreadsheet";
|
|
2
2
|
type Extractor = (range: SheetRange, hiddenStrategy?: ChartSpec["hiddenDimensionStrategy"]) => (string | number | boolean | null | undefined)[];
|
|
3
3
|
export type HierarchyRow = {
|
|
4
|
+
/** The node this row defines — the innermost filled level cell. */
|
|
4
5
|
label: string;
|
|
6
|
+
/** The immediate parent (the last entry of `path`), null for a root. */
|
|
5
7
|
parent: string | null;
|
|
6
8
|
value: number | null;
|
|
9
|
+
/**
|
|
10
|
+
* The level names above the node, top level first, after Excel's
|
|
11
|
+
* fill-down of blank level cells. Absent on a legacy adjacency row
|
|
12
|
+
* (`parent` alone, naming another row's label).
|
|
13
|
+
*/
|
|
14
|
+
path?: string[];
|
|
7
15
|
};
|
|
8
16
|
export type HierarchyNode = {
|
|
9
17
|
name: string;
|
|
18
|
+
/** Unique within the tree: the node's path joined with "/". */
|
|
10
19
|
id?: string;
|
|
11
20
|
value?: number | null;
|
|
12
21
|
children?: HierarchyNode[];
|
|
@@ -19,10 +28,23 @@ type ExtractOptions = {
|
|
|
19
28
|
export declare const getHierarchyHeaderTitle: (spec: TreemapChart, options: ExtractOptions) => string | undefined;
|
|
20
29
|
export declare const extractHierarchyRows: (spec: TreemapChart, options: ExtractOptions) => HierarchyRow[];
|
|
21
30
|
export declare const buildHierarchyTree: (rows: HierarchyRow[]) => HierarchyNode[];
|
|
22
|
-
export
|
|
31
|
+
export type PlotlyHierarchy = {
|
|
32
|
+
/** Node names, as drawn. */
|
|
23
33
|
labels: string[];
|
|
34
|
+
/** Unique node ids (the path joined with "/"); `parents` reference these. */
|
|
35
|
+
ids: string[];
|
|
36
|
+
/** Parent id per node, "" for a root. */
|
|
24
37
|
parents: string[];
|
|
25
38
|
values: number[];
|
|
39
|
+
/** Depth per node, 0 for a root. */
|
|
40
|
+
depths: number[];
|
|
26
41
|
};
|
|
42
|
+
export declare const toPlotlyHierarchy: (rows: HierarchyRow[]) => PlotlyHierarchy;
|
|
43
|
+
/**
|
|
44
|
+
* One solid colour per top-level category, inherited by every descendant
|
|
45
|
+
* (Excel colours a treemap / sunburst by its first level), in tree order.
|
|
46
|
+
* Returns the colour per node of `toPlotlyHierarchy`'s flat arrays.
|
|
47
|
+
*/
|
|
48
|
+
export declare const hierarchyNodeColors: (hierarchy: Pick<PlotlyHierarchy, "ids" | "parents">, palette: string[]) => string[];
|
|
27
49
|
export {};
|
|
28
50
|
//# sourceMappingURL=hierarchy.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hierarchy.d.ts","sourceRoot":"","sources":["../../../utils/hierarchy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,SAAS,EACT,UAAU,EACV,YAAY,EACb,MAAM,2BAA2B,CAAC;AAEnC,KAAK,SAAS,GAAG,CACf,KAAK,EAAE,UAAU,EACjB,cAAc,CAAC,EAAE,SAAS,CAAC,yBAAyB,CAAC,KAClD,CAAC,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,CAAC;AAEtD,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"hierarchy.d.ts","sourceRoot":"","sources":["../../../utils/hierarchy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,SAAS,EACT,UAAU,EACV,YAAY,EACb,MAAM,2BAA2B,CAAC;AAEnC,KAAK,SAAS,GAAG,CACf,KAAK,EAAE,UAAU,EACjB,cAAc,CAAC,EAAE,SAAS,CAAC,yBAAyB,CAAC,KAClD,CAAC,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,CAAC;AAEtD,MAAM,MAAM,YAAY,GAAG;IACzB,mEAAmE;IACnE,KAAK,EAAE,MAAM,CAAC;IACd,wEAAwE;IACxE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,+DAA+D;IAC/D,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,CAAC,EAAE,aAAa,EAAE,CAAC;CAC5B,CAAC;AAEF,KAAK,cAAc,GAAG;IACpB,wBAAwB,EAAE,SAAS,CAAC;IACpC,wBAAwB,EAAE,SAAS,CAAC;IACpC,uBAAuB,CAAC,EAAE,SAAS,CAAC,yBAAyB,CAAC,CAAC;CAChE,CAAC;AA0CF,eAAO,MAAM,uBAAuB,GAClC,MAAM,YAAY,EAClB,SAAS,cAAc,KACtB,MAAM,GAAG,SAgBX,CAAC;AAyCF,eAAO,MAAM,oBAAoB,GAC/B,MAAM,YAAY,EAClB,SAAS,cAAc,KACtB,YAAY,EAiFd,CAAC;AAiDF,eAAO,MAAM,kBAAkB,GAAI,MAAM,YAAY,EAAE,KAAG,aAAa,EAmDtE,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,4BAA4B;IAC5B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,6EAA6E;IAC7E,GAAG,EAAE,MAAM,EAAE,CAAC;IACd,yCAAyC;IACzC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,oCAAoC;IACpC,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,MAAM,YAAY,EAAE,KAAG,eAwBxD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,GAC9B,WAAW,IAAI,CAAC,eAAe,EAAE,KAAK,GAAG,SAAS,CAAC,EACnD,SAAS,MAAM,EAAE,KAChB,MAAM,EAYR,CAAC"}
|
|
@@ -16,10 +16,6 @@ const toLabelString = (value) => {
|
|
|
16
16
|
return "";
|
|
17
17
|
return `${value}`.trim();
|
|
18
18
|
};
|
|
19
|
-
const toParentLabel = (value) => {
|
|
20
|
-
const label = toLabelString(value);
|
|
21
|
-
return label === "" ? null : label;
|
|
22
|
-
};
|
|
23
19
|
const toNumericValue = (value) => {
|
|
24
20
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
25
21
|
return value;
|
|
@@ -50,71 +46,178 @@ export const getHierarchyHeaderTitle = (spec, options) => {
|
|
|
50
46
|
}
|
|
51
47
|
return undefined;
|
|
52
48
|
};
|
|
49
|
+
/**
|
|
50
|
+
* Lay the rows out as Excel does: one column per level (`parentLabels`
|
|
51
|
+
* entries, top level first) and the label column as the innermost level.
|
|
52
|
+
* Returns, per row, the filled level cells up to the row's leaf.
|
|
53
|
+
*
|
|
54
|
+
* A blank level cell BEFORE the row's first filled cell means "same as the
|
|
55
|
+
* row above" (Excel writes a parent only on the first row of its group).
|
|
56
|
+
* A blank AFTER the last filled cell ends the branch there — that cell is
|
|
57
|
+
* the row's leaf, so a shorter branch (`Services | Training | <blank>`)
|
|
58
|
+
* is a leaf of the second level, not `Services > Training > <previous
|
|
59
|
+
* item>` as plain per-column fill-down would make it.
|
|
60
|
+
*/
|
|
61
|
+
const layoutHierarchyLevels = (levelColumns, rowCount) => {
|
|
62
|
+
const filled = [];
|
|
63
|
+
let above = levelColumns.map(() => "");
|
|
64
|
+
for (let row = 0; row < rowCount; row++) {
|
|
65
|
+
const cells = levelColumns.map((column) => column[row] ?? "");
|
|
66
|
+
const firstFilled = cells.findIndex((cell) => cell !== "");
|
|
67
|
+
if (firstFilled === -1) {
|
|
68
|
+
// An entirely blank row defines nothing (and inherits nothing).
|
|
69
|
+
filled.push([]);
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
const effective = cells.map((cell, level) => level < firstFilled ? above[level] ?? "" : cell);
|
|
73
|
+
let lastFilled = effective.length - 1;
|
|
74
|
+
while (lastFilled >= 0 && effective[lastFilled] === "")
|
|
75
|
+
lastFilled--;
|
|
76
|
+
// The row's own levels end at its last filled cell; the cells past it
|
|
77
|
+
// stay blank for the rows below (they inherit up to the leaf only).
|
|
78
|
+
above = effective.map((cell, level) => (level <= lastFilled ? cell : ""));
|
|
79
|
+
filled.push(effective.slice(0, lastFilled + 1).filter((cell) => cell !== ""));
|
|
80
|
+
}
|
|
81
|
+
return filled;
|
|
82
|
+
};
|
|
53
83
|
export const extractHierarchyRows = (spec, options) => {
|
|
54
84
|
const { getDomainValuesFromRange, getSeriesValuesFromRange } = options;
|
|
55
85
|
const hiddenStrategy = options.hiddenDimensionStrategy;
|
|
56
86
|
const skipHeaders = spec.useFirstRowAsHeaders !== false;
|
|
57
87
|
const labels = flattenChartData(spec.labels, getDomainValuesFromRange, hiddenStrategy, skipHeaders).map(toLabelString);
|
|
58
|
-
|
|
88
|
+
// Each `parentLabels` entry is one hierarchy level (top level first).
|
|
89
|
+
const parentLevels = (spec.parentLabels ?? []).map((entry) => flattenChartData([entry], getDomainValuesFromRange, hiddenStrategy, skipHeaders).map(toLabelString));
|
|
59
90
|
const sizes = flattenChartData(spec.sizeData, getSeriesValuesFromRange, hiddenStrategy, skipHeaders);
|
|
91
|
+
// A single parent column whose values name other rows' labels is a flat
|
|
92
|
+
// adjacency list (Google Sheets' label / parent-label model): every row
|
|
93
|
+
// is its own node and a blank parent means a root. Excel's level layout
|
|
94
|
+
// never lists a level name in the label column.
|
|
95
|
+
const labelSet = new Set(labels.filter((label) => label !== ""));
|
|
96
|
+
const isAdjacencyList = parentLevels.length === 1 &&
|
|
97
|
+
parentLevels[0].some((parent) => parent !== "" && labelSet.has(parent));
|
|
60
98
|
const rows = [];
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
const
|
|
64
|
-
|
|
99
|
+
const seenPaths = new Set();
|
|
100
|
+
if (isAdjacencyList) {
|
|
101
|
+
const parents = parentLevels[0];
|
|
102
|
+
for (let i = 0; i < labels.length; i++) {
|
|
103
|
+
const label = labels[i];
|
|
104
|
+
if (!label)
|
|
105
|
+
continue;
|
|
106
|
+
if (seenPaths.has(label)) {
|
|
107
|
+
throw new Error(`Duplicate hierarchy label detected: ${label}`);
|
|
108
|
+
}
|
|
109
|
+
seenPaths.add(label);
|
|
110
|
+
const parent = parents[i] ?? "";
|
|
111
|
+
rows.push({
|
|
112
|
+
label,
|
|
113
|
+
parent: parent === "" ? null : parent,
|
|
114
|
+
value: toNumericValue(sizes[i]),
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
return rows;
|
|
118
|
+
}
|
|
119
|
+
const rowCount = Math.max(labels.length, ...parentLevels.map((level) => level.length));
|
|
120
|
+
const levels = layoutHierarchyLevels([...parentLevels, labels], rowCount);
|
|
121
|
+
for (let i = 0; i < rowCount; i++) {
|
|
122
|
+
const cells = levels[i];
|
|
123
|
+
if (!cells || cells.length === 0)
|
|
65
124
|
continue;
|
|
66
|
-
|
|
125
|
+
const label = cells[cells.length - 1];
|
|
126
|
+
const path = cells.slice(0, -1);
|
|
127
|
+
const key = [...path, label].join("\u0000");
|
|
128
|
+
if (seenPaths.has(key)) {
|
|
67
129
|
throw new Error(`Duplicate hierarchy label detected: ${label}`);
|
|
68
130
|
}
|
|
69
|
-
|
|
131
|
+
seenPaths.add(key);
|
|
70
132
|
rows.push({
|
|
71
133
|
label,
|
|
72
|
-
parent:
|
|
134
|
+
parent: path.length ? path[path.length - 1] : null,
|
|
73
135
|
value: toNumericValue(sizes[i]),
|
|
136
|
+
path,
|
|
74
137
|
});
|
|
75
138
|
}
|
|
76
139
|
return rows;
|
|
77
140
|
};
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
141
|
+
const PATH_SEPARATOR = "/";
|
|
142
|
+
/**
|
|
143
|
+
* The full path of every row, top level first, ending with the row's own
|
|
144
|
+
* label. A row carrying `path` (Excel's level layout) is placed under it;
|
|
145
|
+
* a legacy adjacency row names its parent, which is another row's label,
|
|
146
|
+
* so its path is that row's path plus itself (chained up to a root).
|
|
147
|
+
*/
|
|
148
|
+
const resolveRowPaths = (rows) => {
|
|
149
|
+
const byLabel = new Map();
|
|
150
|
+
rows.forEach((row) => {
|
|
151
|
+
if (row.label && !row.path && !byLabel.has(row.label)) {
|
|
152
|
+
byLabel.set(row.label, row);
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
const memo = new Map();
|
|
156
|
+
const resolve = (row, visiting) => {
|
|
157
|
+
const cached = memo.get(row);
|
|
158
|
+
if (cached)
|
|
159
|
+
return cached;
|
|
160
|
+
let path;
|
|
161
|
+
if (row.path) {
|
|
162
|
+
path = [...row.path, row.label];
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
const parentName = row.parent && row.parent !== row.label ? row.parent : null;
|
|
166
|
+
const parentRow = parentName ? byLabel.get(parentName) : undefined;
|
|
167
|
+
if (parentRow && parentRow !== row && !visiting.has(parentRow)) {
|
|
168
|
+
visiting.add(row);
|
|
169
|
+
path = [...resolve(parentRow, visiting), row.label];
|
|
170
|
+
visiting.delete(row);
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
path = parentName ? [parentName, row.label] : [row.label];
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
memo.set(row, path);
|
|
177
|
+
return path;
|
|
87
178
|
};
|
|
88
|
-
|
|
89
|
-
return node;
|
|
179
|
+
return rows.map((row) => resolve(row, new Set()));
|
|
90
180
|
};
|
|
91
181
|
export const buildHierarchyTree = (rows) => {
|
|
92
|
-
const
|
|
93
|
-
|
|
182
|
+
const roots = new Map();
|
|
183
|
+
const nodeAt = (path) => {
|
|
184
|
+
let level = roots;
|
|
185
|
+
let node;
|
|
186
|
+
path.forEach((name, depth) => {
|
|
187
|
+
const existing = level.get(name);
|
|
188
|
+
if (existing) {
|
|
189
|
+
node = existing;
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
node = {
|
|
193
|
+
name,
|
|
194
|
+
id: path.slice(0, depth + 1).join(PATH_SEPARATOR),
|
|
195
|
+
value: 0,
|
|
196
|
+
children: new Map(),
|
|
197
|
+
};
|
|
198
|
+
level.set(name, node);
|
|
199
|
+
}
|
|
200
|
+
level = node.children;
|
|
201
|
+
});
|
|
202
|
+
return node;
|
|
203
|
+
};
|
|
204
|
+
const paths = resolveRowPaths(rows);
|
|
205
|
+
rows.forEach((row, index) => {
|
|
94
206
|
if (!row.label)
|
|
95
207
|
return;
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
labelNode.value += numericValue;
|
|
99
|
-
const parentName = row.parent && row.parent !== row.label ? row.parent : null;
|
|
100
|
-
if (parentName) {
|
|
101
|
-
const parentNode = getOrCreateNode(nodes, parentName);
|
|
102
|
-
parentNode.children.add(row.label);
|
|
103
|
-
if (labelNode.parent == null) {
|
|
104
|
-
labelNode.parent = parentName;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
208
|
+
const node = nodeAt(paths[index]);
|
|
209
|
+
node.value += row.value ?? 0;
|
|
107
210
|
});
|
|
108
211
|
const buildNode = (internal) => {
|
|
109
212
|
const children = internal.children.size > 0
|
|
110
|
-
? Array.from(internal.children).map(
|
|
213
|
+
? Array.from(internal.children.values()).map(buildNode)
|
|
111
214
|
: undefined;
|
|
112
215
|
const value = children && children.length > 0
|
|
113
216
|
? children.reduce((sum, child) => sum + (child.value ?? 0), 0)
|
|
114
217
|
: internal.value;
|
|
115
218
|
const node = {
|
|
116
219
|
name: internal.name,
|
|
117
|
-
id: internal.
|
|
220
|
+
id: internal.id,
|
|
118
221
|
value,
|
|
119
222
|
};
|
|
120
223
|
if (children && children.length > 0) {
|
|
@@ -122,29 +225,44 @@ export const buildHierarchyTree = (rows) => {
|
|
|
122
225
|
}
|
|
123
226
|
return node;
|
|
124
227
|
};
|
|
125
|
-
|
|
126
|
-
nodes.forEach((internal) => {
|
|
127
|
-
if (internal.parent == null) {
|
|
128
|
-
roots.push(buildNode(internal));
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
return roots;
|
|
228
|
+
return Array.from(roots.values()).map(buildNode);
|
|
132
229
|
};
|
|
133
230
|
export const toPlotlyHierarchy = (rows) => {
|
|
134
231
|
const tree = buildHierarchyTree(rows);
|
|
135
232
|
const labels = [];
|
|
233
|
+
const ids = [];
|
|
136
234
|
const parents = [];
|
|
137
235
|
const values = [];
|
|
138
|
-
const
|
|
236
|
+
const depths = [];
|
|
237
|
+
const traverse = (nodes, parentId, depth) => {
|
|
139
238
|
nodes.forEach((node) => {
|
|
140
239
|
labels.push(node.name);
|
|
141
|
-
|
|
240
|
+
ids.push(node.id ?? node.name);
|
|
241
|
+
parents.push(parentId);
|
|
142
242
|
values.push(node.value ?? 0);
|
|
243
|
+
depths.push(depth);
|
|
143
244
|
if (node.children) {
|
|
144
|
-
traverse(node.children, node.name);
|
|
245
|
+
traverse(node.children, node.id ?? node.name, depth + 1);
|
|
145
246
|
}
|
|
146
247
|
});
|
|
147
248
|
};
|
|
148
|
-
traverse(tree,
|
|
149
|
-
return { labels, parents, values };
|
|
249
|
+
traverse(tree, "", 0);
|
|
250
|
+
return { labels, ids, parents, values, depths };
|
|
251
|
+
};
|
|
252
|
+
/**
|
|
253
|
+
* One solid colour per top-level category, inherited by every descendant
|
|
254
|
+
* (Excel colours a treemap / sunburst by its first level), in tree order.
|
|
255
|
+
* Returns the colour per node of `toPlotlyHierarchy`'s flat arrays.
|
|
256
|
+
*/
|
|
257
|
+
export const hierarchyNodeColors = (hierarchy, palette) => {
|
|
258
|
+
const colorById = new Map();
|
|
259
|
+
let rootIndex = 0;
|
|
260
|
+
return hierarchy.ids.map((id, index) => {
|
|
261
|
+
const parentId = hierarchy.parents[index];
|
|
262
|
+
const color = parentId === ""
|
|
263
|
+
? palette[rootIndex++ % Math.max(1, palette.length)] ?? ""
|
|
264
|
+
: colorById.get(parentId) ?? "";
|
|
265
|
+
colorById.set(id, color);
|
|
266
|
+
return color;
|
|
267
|
+
});
|
|
150
268
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"overrides.d.ts","sourceRoot":"","sources":["../../../utils/overrides.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAWrD,oEAAoE;AACpE,eAAO,MAAM,4BAA4B,GAAI,KAAK,GAAG,oCASpD,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,QAAQ,qBAAqB,EAC7B,MAAM,GAAG,KACR,qBA6CF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAC7B,MAAM,GAAG,EACT,MAAM,GAAG,EACT,sBAAsB,MAAM,EAC5B,SAAS,MAAM,QA4BhB,CAAC;AAgBF,eAAO,MAAM,kBAAkB,GAAI,MAAM,GAAG,EAAE,SAAS,MAAM,MAAM,QAAQ,GAAG,
|
|
1
|
+
{"version":3,"file":"overrides.d.ts","sourceRoot":"","sources":["../../../utils/overrides.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAWrD,oEAAoE;AACpE,eAAO,MAAM,4BAA4B,GAAI,KAAK,GAAG,oCASpD,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,QAAQ,qBAAqB,EAC7B,MAAM,GAAG,KACR,qBA6CF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAC7B,MAAM,GAAG,EACT,MAAM,GAAG,EACT,sBAAsB,MAAM,EAC5B,SAAS,MAAM,QA4BhB,CAAC;AAgBF,eAAO,MAAM,kBAAkB,GAAI,MAAM,GAAG,EAAE,SAAS,MAAM,MAAM,QAAQ,GAAG,WAoD7E,CAAC;AAgCF;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,GAAI,CAAC,EACvC,YAAY,CAAC,GAAG,IAAI,GAAG,SAAS,EAChC,QAAQ;IAAE,UAAU,CAAC,EAAE,CAAC,GAAG,IAAI,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,KACnD,CAAC,GAAG,IAAI,GAAG,SAA6C,CAAC;AAE5D,eAAO,MAAM,sBAAsB,GACjC,MAAM,GAAG,EACT,aAAa,MAAM,EACnB,SAAS,MAAM;;;iCAlG0D,GAAG;aA+G7E,CAAC"}
|
|
@@ -126,13 +126,22 @@ export const dataLabelFormatter = (opts, locale) => (params) => {
|
|
|
126
126
|
if (opts.showPercent && params.percent !== undefined) {
|
|
127
127
|
parts.push(`${params.percent}%`);
|
|
128
128
|
}
|
|
129
|
+
// The value shows by default unless another field was asked for instead:
|
|
130
|
+
// an importer records only the flags that are ON, so `<c:showVal val="0"/>`
|
|
131
|
+
// + `<c:showBubbleSize val="1"/>` arrives as `{ showBubbleSize: true }` and
|
|
132
|
+
// must label the bubble with its size alone (#1068 / #1079).
|
|
129
133
|
const showVal = opts.showValue !== false &&
|
|
130
134
|
!opts.showCategoryName &&
|
|
131
135
|
!opts.showPercent &&
|
|
132
|
-
!opts.showSeriesName
|
|
136
|
+
!opts.showSeriesName &&
|
|
137
|
+
!opts.showBubbleSize
|
|
133
138
|
? true
|
|
134
139
|
: !!opts.showValue;
|
|
135
|
-
|
|
140
|
+
// A scatter / bubble point's value is its [x, y, size] tuple: the label's
|
|
141
|
+
// value is Y and the bubble size is the third entry (#1068).
|
|
142
|
+
const isPointTuple = Array.isArray(params.value);
|
|
143
|
+
const pointValue = isPointTuple ? params.value[1] : params.value;
|
|
144
|
+
if (showVal && pointValue !== undefined) {
|
|
136
145
|
// PERCENT_STACKED stores the raw value on the data object so the
|
|
137
146
|
// label can still surface the dollar amount instead of the
|
|
138
147
|
// normalized percentage. Plain stacked / clustered have just a
|
|
@@ -140,7 +149,10 @@ export const dataLabelFormatter = (opts, locale) => (params) => {
|
|
|
140
149
|
const originalValue = typeof params.data === "object" && params.data
|
|
141
150
|
? params.data.originalValue
|
|
142
151
|
: undefined;
|
|
143
|
-
parts.push(formatLabelValue(
|
|
152
|
+
parts.push(formatLabelValue(pointValue, originalValue, opts.numberFormatPattern, locale));
|
|
153
|
+
}
|
|
154
|
+
if (opts.showBubbleSize && isPointTuple && params.value.length > 2) {
|
|
155
|
+
parts.push(formatLabelValue(params.value[2], undefined, opts.numberFormatPattern, locale));
|
|
144
156
|
}
|
|
145
157
|
return parts.join(": ");
|
|
146
158
|
};
|
|
@@ -43,6 +43,12 @@ export type TrendlineResult = {
|
|
|
43
43
|
* @param values Raw y-values; the 1-based category position is used as x
|
|
44
44
|
* for the fit, the 0-based index is reported in the points.
|
|
45
45
|
* @param spec Trendline parameters (type/order/period/forecast).
|
|
46
|
+
* @param xValues Scatter / bubble only — the series' real X values, index-
|
|
47
|
+
* aligned with `values`. The fit then runs on (x, y) pairs
|
|
48
|
+
* (a regression of Y on X, what Excel draws on an XY chart;
|
|
49
|
+
* the point index is wrong for unevenly spaced X), the
|
|
50
|
+
* forecast extends the X range by that many X units, and the
|
|
51
|
+
* points carry real X coordinates sampled across the range.
|
|
46
52
|
*/
|
|
47
53
|
export declare const computeTrendline: (values: (number | null | undefined)[], spec?: {
|
|
48
54
|
type?: TrendlineType | null;
|
|
@@ -50,5 +56,5 @@ export declare const computeTrendline: (values: (number | null | undefined)[], s
|
|
|
50
56
|
period?: number | null;
|
|
51
57
|
forecastForward?: number | null;
|
|
52
58
|
forecastBackward?: number | null;
|
|
53
|
-
}) => TrendlineResult | null;
|
|
59
|
+
}, xValues?: (number | null | undefined)[] | null) => TrendlineResult | null;
|
|
54
60
|
//# sourceMappingURL=trendline.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trendline.d.ts","sourceRoot":"","sources":["../../../utils/trendline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,MAAM,MAAM,aAAa,GACrB,QAAQ,GACR,KAAK,GACL,KAAK,GACL,MAAM,GACN,OAAO,GACP,WAAW,CAAC;AAEhB,6FAA6F;AAC7F,MAAM,MAAM,cAAc,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtD,MAAM,MAAM,eAAe,GAAG;IAC5B,sEAAsE;IACtE,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,uEAAuE;IACvE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB,CAAC;
|
|
1
|
+
{"version":3,"file":"trendline.d.ts","sourceRoot":"","sources":["../../../utils/trendline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,MAAM,MAAM,aAAa,GACrB,QAAQ,GACR,KAAK,GACL,KAAK,GACL,MAAM,GACN,OAAO,GACP,WAAW,CAAC;AAEhB,6FAA6F;AAC7F,MAAM,MAAM,cAAc,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtD,MAAM,MAAM,eAAe,GAAG;IAC5B,sEAAsE;IACtE,MAAM,EAAE,cAAc,EAAE,CAAC;IACzB,uEAAuE;IACvE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB,CAAC;AAkKF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,gBAAgB,GAC3B,QAAQ,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,EACrC,OAAM;IACJ,IAAI,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,EACN,UAAU,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,GAAG,IAAI,KAC7C,eAAe,GAAG,IA+HpB,CAAC"}
|