@internetstiftelsen/charts 0.8.0 → 0.9.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/README.md +53 -8
- package/area.d.ts +0 -1
- package/area.js +2 -19
- package/bar.d.ts +0 -1
- package/bar.js +5 -22
- package/base-chart.d.ts +54 -5
- package/base-chart.js +260 -73
- package/donut-chart.d.ts +7 -9
- package/donut-chart.js +51 -131
- package/gauge-chart.d.ts +18 -7
- package/gauge-chart.js +315 -106
- package/line.js +3 -25
- package/package.json +3 -1
- package/pie-chart.d.ts +7 -11
- package/pie-chart.js +30 -153
- package/radial-chart-base.d.ts +25 -0
- package/radial-chart-base.js +77 -0
- package/scale-utils.d.ts +3 -0
- package/scale-utils.js +14 -0
- package/utils.d.ts +7 -0
- package/utils.js +24 -0
- package/word-cloud-chart.d.ts +32 -0
- package/word-cloud-chart.js +199 -0
- package/xy-chart.d.ts +6 -4
- package/xy-chart.js +77 -126
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import cloud from 'd3-cloud';
|
|
2
|
+
import { scaleSqrt } from 'd3';
|
|
3
|
+
import { BaseChart, } from './base-chart.js';
|
|
4
|
+
import { isGroupedData } from './grouped-data.js';
|
|
5
|
+
const DEFAULT_OPTIONS = {
|
|
6
|
+
maxWords: 75,
|
|
7
|
+
minWordLength: 1,
|
|
8
|
+
minValue: 1,
|
|
9
|
+
minFontSize: 3,
|
|
10
|
+
maxFontSize: 20,
|
|
11
|
+
padding: 1,
|
|
12
|
+
rotation: undefined,
|
|
13
|
+
spiral: 'archimedean',
|
|
14
|
+
};
|
|
15
|
+
const GROUPED_DATA_ERROR = 'WordCloudChart: grouped datasets are not supported; provide a flat array of rows instead';
|
|
16
|
+
function createPreparedWords(data, plotArea, options, colors) {
|
|
17
|
+
const counts = new Map();
|
|
18
|
+
data.forEach((row) => {
|
|
19
|
+
const word = row.word.trim();
|
|
20
|
+
const count = row.count;
|
|
21
|
+
if (!word ||
|
|
22
|
+
word.length < options.minWordLength ||
|
|
23
|
+
!Number.isFinite(count) ||
|
|
24
|
+
count < options.minValue) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
counts.set(word, (counts.get(word) ?? 0) + count);
|
|
28
|
+
});
|
|
29
|
+
if (counts.size === 0) {
|
|
30
|
+
throw new Error('WordCloudChart: no valid words remain after filtering; adjust minWordLength or minValue, or provide valid rows');
|
|
31
|
+
}
|
|
32
|
+
const words = Array.from(counts.entries())
|
|
33
|
+
.map(([text, value]) => ({ text, value }))
|
|
34
|
+
.sort((left, right) => right.value - left.value)
|
|
35
|
+
.slice(0, options.maxWords);
|
|
36
|
+
const baseDimension = Math.max(1, Math.min(plotArea.width, plotArea.height));
|
|
37
|
+
const minFontSize = (baseDimension * options.minFontSize) / 100;
|
|
38
|
+
const maxFontSize = (baseDimension * options.maxFontSize) / 100;
|
|
39
|
+
const maxValue = words[0].value;
|
|
40
|
+
const minValue = words[words.length - 1].value;
|
|
41
|
+
const fontScale = maxValue === minValue
|
|
42
|
+
? null
|
|
43
|
+
: scaleSqrt()
|
|
44
|
+
.domain([minValue, maxValue])
|
|
45
|
+
.range([minFontSize, maxFontSize]);
|
|
46
|
+
return words.map((word, index) => {
|
|
47
|
+
return {
|
|
48
|
+
text: word.text,
|
|
49
|
+
value: word.value,
|
|
50
|
+
size: fontScale ? fontScale(word.value) : maxFontSize,
|
|
51
|
+
color: colors[index % colors.length] ?? '#000000',
|
|
52
|
+
};
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
export class WordCloudChart extends BaseChart {
|
|
56
|
+
constructor(config) {
|
|
57
|
+
super(config);
|
|
58
|
+
Object.defineProperty(this, "options", {
|
|
59
|
+
enumerable: true,
|
|
60
|
+
configurable: true,
|
|
61
|
+
writable: true,
|
|
62
|
+
value: void 0
|
|
63
|
+
});
|
|
64
|
+
Object.defineProperty(this, "layout", {
|
|
65
|
+
enumerable: true,
|
|
66
|
+
configurable: true,
|
|
67
|
+
writable: true,
|
|
68
|
+
value: null
|
|
69
|
+
});
|
|
70
|
+
Object.defineProperty(this, "layoutRunId", {
|
|
71
|
+
enumerable: true,
|
|
72
|
+
configurable: true,
|
|
73
|
+
writable: true,
|
|
74
|
+
value: 0
|
|
75
|
+
});
|
|
76
|
+
Object.defineProperty(this, "resolvePendingReady", {
|
|
77
|
+
enumerable: true,
|
|
78
|
+
configurable: true,
|
|
79
|
+
writable: true,
|
|
80
|
+
value: null
|
|
81
|
+
});
|
|
82
|
+
const wordCloud = config.wordCloud ?? {};
|
|
83
|
+
this.options = {
|
|
84
|
+
maxWords: wordCloud.maxWords ?? DEFAULT_OPTIONS.maxWords,
|
|
85
|
+
minWordLength: wordCloud.minWordLength ?? DEFAULT_OPTIONS.minWordLength,
|
|
86
|
+
minValue: wordCloud.minValue ?? DEFAULT_OPTIONS.minValue,
|
|
87
|
+
minFontSize: wordCloud.minFontSize ?? DEFAULT_OPTIONS.minFontSize,
|
|
88
|
+
maxFontSize: wordCloud.maxFontSize ?? DEFAULT_OPTIONS.maxFontSize,
|
|
89
|
+
padding: wordCloud.padding ?? DEFAULT_OPTIONS.padding,
|
|
90
|
+
rotation: wordCloud.rotation,
|
|
91
|
+
spiral: wordCloud.spiral ?? DEFAULT_OPTIONS.spiral,
|
|
92
|
+
};
|
|
93
|
+
this.initializeDataState();
|
|
94
|
+
}
|
|
95
|
+
destroy() {
|
|
96
|
+
this.layoutRunId += 1;
|
|
97
|
+
this.stopLayout();
|
|
98
|
+
this.setReadyPromise(Promise.resolve());
|
|
99
|
+
super.destroy();
|
|
100
|
+
}
|
|
101
|
+
validateSourceData(data) {
|
|
102
|
+
if (isGroupedData(data)) {
|
|
103
|
+
throw new Error(GROUPED_DATA_ERROR);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
renderChart({ svg, plotArea }) {
|
|
107
|
+
this.stopLayout();
|
|
108
|
+
this.renderTitle(svg);
|
|
109
|
+
const words = createPreparedWords(this.data, plotArea, this.options, this.renderTheme.colorPalette);
|
|
110
|
+
this.setReadyPromise(new Promise((resolve) => {
|
|
111
|
+
this.resolvePendingReady = resolve;
|
|
112
|
+
this.startLayout(words, plotArea, ++this.layoutRunId, resolve);
|
|
113
|
+
}));
|
|
114
|
+
}
|
|
115
|
+
createExportChart() {
|
|
116
|
+
return new WordCloudChart({
|
|
117
|
+
data: this.sourceData,
|
|
118
|
+
theme: this.theme,
|
|
119
|
+
responsive: this.responsiveConfig,
|
|
120
|
+
wordCloud: this.options,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
startLayout(words, plotArea, runId, resolve) {
|
|
124
|
+
const layout = cloud()
|
|
125
|
+
.words(words.map((word) => ({ ...word })))
|
|
126
|
+
.size([
|
|
127
|
+
Math.max(1, Math.floor(plotArea.width)),
|
|
128
|
+
Math.max(1, Math.floor(plotArea.height)),
|
|
129
|
+
])
|
|
130
|
+
.padding(this.options.padding)
|
|
131
|
+
.spiral(this.options.spiral)
|
|
132
|
+
.font(this.renderTheme.fontFamily)
|
|
133
|
+
.fontWeight(this.renderTheme.valueLabel.fontWeight)
|
|
134
|
+
.fontSize((word) => word.size)
|
|
135
|
+
.text((word) => word.text)
|
|
136
|
+
.on('end', (placedWords) => {
|
|
137
|
+
this.layout = null;
|
|
138
|
+
if (runId !== this.layoutRunId ||
|
|
139
|
+
!this.plotGroup ||
|
|
140
|
+
!this.plotArea) {
|
|
141
|
+
this.finishReady(resolve);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
if (placedWords.length < words.length) {
|
|
145
|
+
console.warn(`[Chart Warning] WordCloudChart: rendered ${placedWords.length} of ${words.length} words within the available area; reduce maxWords or font sizes to fit more words`);
|
|
146
|
+
}
|
|
147
|
+
this.renderWords(this.plotGroup, this.plotArea, placedWords);
|
|
148
|
+
this.finishReady(resolve);
|
|
149
|
+
});
|
|
150
|
+
if (this.options.rotation === 'none') {
|
|
151
|
+
layout.rotate(0);
|
|
152
|
+
}
|
|
153
|
+
else if (this.options.rotation === 'right-angle') {
|
|
154
|
+
layout.rotate((_word, index) => (index % 2 === 0 ? 0 : 90));
|
|
155
|
+
}
|
|
156
|
+
this.layout = layout;
|
|
157
|
+
layout.start();
|
|
158
|
+
}
|
|
159
|
+
renderWords(plotGroup, plotArea, words) {
|
|
160
|
+
plotGroup.attr('transform', `translate(${plotArea.left}, ${plotArea.top})`);
|
|
161
|
+
plotGroup
|
|
162
|
+
.append('rect')
|
|
163
|
+
.attr('class', 'word-cloud-viewport')
|
|
164
|
+
.attr('x', 0)
|
|
165
|
+
.attr('y', 0)
|
|
166
|
+
.attr('width', plotArea.width)
|
|
167
|
+
.attr('height', plotArea.height)
|
|
168
|
+
.attr('fill', 'transparent')
|
|
169
|
+
.attr('stroke', 'none')
|
|
170
|
+
.attr('pointer-events', 'none');
|
|
171
|
+
plotGroup
|
|
172
|
+
.append('g')
|
|
173
|
+
.attr('class', 'word-cloud')
|
|
174
|
+
.attr('transform', `translate(${plotArea.width / 2}, ${plotArea.height / 2})`)
|
|
175
|
+
.selectAll('text')
|
|
176
|
+
.data(words)
|
|
177
|
+
.join('text')
|
|
178
|
+
.attr('class', 'word-cloud-word')
|
|
179
|
+
.attr('text-anchor', 'middle')
|
|
180
|
+
.style('font-family', this.renderTheme.fontFamily)
|
|
181
|
+
.style('font-weight', String(this.renderTheme.valueLabel.fontWeight))
|
|
182
|
+
.style('font-size', (word) => `${word.size}px`)
|
|
183
|
+
.style('fill', (word) => word.color)
|
|
184
|
+
.attr('transform', (word) => `translate(${word.x ?? 0}, ${word.y ?? 0}) rotate(${word.rotate ?? 0})`)
|
|
185
|
+
.text((word) => word.text);
|
|
186
|
+
}
|
|
187
|
+
stopLayout() {
|
|
188
|
+
if (this.layout) {
|
|
189
|
+
this.layout.stop();
|
|
190
|
+
this.layout = null;
|
|
191
|
+
}
|
|
192
|
+
this.resolvePendingReady?.();
|
|
193
|
+
this.resolvePendingReady = null;
|
|
194
|
+
}
|
|
195
|
+
finishReady(resolve) {
|
|
196
|
+
this.resolvePendingReady = null;
|
|
197
|
+
resolve();
|
|
198
|
+
}
|
|
199
|
+
}
|
package/xy-chart.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BaseChart, type BaseChartConfig } from './base-chart.js';
|
|
1
|
+
import { BaseChart, type BaseChartConfig, type BaseLayoutContext, type BaseRenderContext } from './base-chart.js';
|
|
2
2
|
import type { ChartComponent } from './chart-interface.js';
|
|
3
3
|
import { type AreaStackConfig, type BarStackConfig, type LegendSeries } from './types.js';
|
|
4
4
|
export type XYChartConfig = BaseChartConfig & {
|
|
@@ -16,14 +16,16 @@ export declare class XYChart extends BaseChart {
|
|
|
16
16
|
protected getExportComponents(): ChartComponent[];
|
|
17
17
|
protected createExportChart(): BaseChart;
|
|
18
18
|
protected applyComponentOverrides(overrides: Map<ChartComponent, ChartComponent>): () => void;
|
|
19
|
-
|
|
20
|
-
protected
|
|
21
|
-
protected renderChart(): void;
|
|
19
|
+
protected prepareLayout(context: BaseLayoutContext): void;
|
|
20
|
+
protected renderChart({ svg, plotGroup, plotArea, }: BaseRenderContext): void;
|
|
22
21
|
private getXKey;
|
|
23
22
|
protected getLegendSeries(): LegendSeries[];
|
|
24
23
|
private getCategoryScaleType;
|
|
25
24
|
private getVisibleSeries;
|
|
26
25
|
private getDisplaySeries;
|
|
26
|
+
private resolveSeriesDefaults;
|
|
27
|
+
private shouldReplaceSeriesColor;
|
|
28
|
+
private cloneSeriesWithOverride;
|
|
27
29
|
private setupScales;
|
|
28
30
|
private isHorizontalOrientation;
|
|
29
31
|
private collectSeriesValues;
|
package/xy-chart.js
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { max, min, scaleBand, scaleLinear, scaleLog, scaleTime, } from 'd3';
|
|
2
|
-
import { BaseChart } from './base-chart.js';
|
|
2
|
+
import { BaseChart, } from './base-chart.js';
|
|
3
3
|
import { ChartValidator } from './validation.js';
|
|
4
4
|
import { GROUPED_GAP_TICK_PREFIX, GROUPED_GROUP_LABEL_KEY, } from './grouped-data.js';
|
|
5
|
+
const DEFAULT_SERIES_COLOR = '#8884d8';
|
|
6
|
+
function isXYSeries(component) {
|
|
7
|
+
return (component.type === 'line' ||
|
|
8
|
+
component.type === 'bar' ||
|
|
9
|
+
component.type === 'area');
|
|
10
|
+
}
|
|
5
11
|
export class XYChart extends BaseChart {
|
|
6
12
|
constructor(config) {
|
|
7
13
|
super(config);
|
|
@@ -41,82 +47,26 @@ export class XYChart extends BaseChart {
|
|
|
41
47
|
this.areaStackMode = config.areaStack?.mode ?? 'none';
|
|
42
48
|
}
|
|
43
49
|
addChild(component) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const defaultColor = '#8884d8';
|
|
48
|
-
const colorIndex = this.series.length % this.theme.colorPalette.length;
|
|
49
|
-
const newColor = this.theme.colorPalette[colorIndex];
|
|
50
|
-
if (type === 'line') {
|
|
51
|
-
const lineSeries = series;
|
|
52
|
-
const currentColor = lineSeries.stroke;
|
|
53
|
-
if (!currentColor || currentColor === defaultColor) {
|
|
54
|
-
lineSeries.stroke = newColor;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
else if (type === 'bar') {
|
|
58
|
-
const barSeries = series;
|
|
59
|
-
const currentColor = barSeries.fill;
|
|
60
|
-
if (!currentColor || currentColor === defaultColor) {
|
|
61
|
-
barSeries.fill = newColor;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
else {
|
|
65
|
-
const areaSeries = series;
|
|
66
|
-
const isUsingDefaultColor = (!areaSeries.fill || areaSeries.fill === defaultColor) &&
|
|
67
|
-
(!areaSeries.stroke || areaSeries.stroke === defaultColor);
|
|
68
|
-
if (isUsingDefaultColor) {
|
|
69
|
-
areaSeries.fill = newColor;
|
|
70
|
-
areaSeries.stroke = newColor;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
this.series.push(series);
|
|
74
|
-
}
|
|
75
|
-
else if (type === 'xAxis') {
|
|
76
|
-
this.xAxis = component;
|
|
77
|
-
}
|
|
78
|
-
else if (type === 'yAxis') {
|
|
79
|
-
this.yAxis = component;
|
|
80
|
-
}
|
|
81
|
-
else if (type === 'grid') {
|
|
82
|
-
this.grid = component;
|
|
83
|
-
}
|
|
84
|
-
else if (type === 'tooltip') {
|
|
85
|
-
this.tooltip = component;
|
|
50
|
+
if (isXYSeries(component)) {
|
|
51
|
+
this.series.push(this.resolveSeriesDefaults(component));
|
|
52
|
+
return this;
|
|
86
53
|
}
|
|
87
|
-
|
|
88
|
-
this.legend = component;
|
|
89
|
-
this.legend.setToggleCallback(() => {
|
|
90
|
-
this.rerender();
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
else if (type === 'title') {
|
|
94
|
-
this.title = component;
|
|
95
|
-
}
|
|
96
|
-
return this;
|
|
54
|
+
return super.addChild(component);
|
|
97
55
|
}
|
|
98
56
|
getExportComponents() {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}
|
|
113
|
-
if (this.tooltip) {
|
|
114
|
-
components.push(this.tooltip);
|
|
115
|
-
}
|
|
116
|
-
if (this.legend) {
|
|
117
|
-
components.push(this.legend);
|
|
118
|
-
}
|
|
119
|
-
return components;
|
|
57
|
+
return [
|
|
58
|
+
...this.getBaseExportComponents({
|
|
59
|
+
title: true,
|
|
60
|
+
grid: true,
|
|
61
|
+
}),
|
|
62
|
+
...this.series,
|
|
63
|
+
...this.getBaseExportComponents({
|
|
64
|
+
xAxis: true,
|
|
65
|
+
yAxis: true,
|
|
66
|
+
tooltip: true,
|
|
67
|
+
legend: true,
|
|
68
|
+
}),
|
|
69
|
+
];
|
|
120
70
|
}
|
|
121
71
|
createExportChart() {
|
|
122
72
|
return new XYChart({
|
|
@@ -136,34 +86,16 @@ export class XYChart extends BaseChart {
|
|
|
136
86
|
}
|
|
137
87
|
applyComponentOverrides(overrides) {
|
|
138
88
|
const restoreBase = super.applyComponentOverrides(overrides);
|
|
139
|
-
|
|
140
|
-
return restoreBase;
|
|
141
|
-
}
|
|
142
|
-
const previousSeries = [...this.series];
|
|
143
|
-
this.series.forEach((series, index) => {
|
|
144
|
-
const override = overrides.get(series);
|
|
145
|
-
if (override &&
|
|
146
|
-
(override.type === 'line' ||
|
|
147
|
-
override.type === 'bar' ||
|
|
148
|
-
override.type === 'area')) {
|
|
149
|
-
this.series[index] = override;
|
|
150
|
-
}
|
|
151
|
-
});
|
|
89
|
+
const restoreSeries = this.applyArrayComponentOverrides(this.series, overrides, isXYSeries);
|
|
152
90
|
return () => {
|
|
153
|
-
|
|
91
|
+
restoreSeries();
|
|
154
92
|
restoreBase();
|
|
155
93
|
};
|
|
156
94
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
this.update(this.sourceData);
|
|
162
|
-
}
|
|
163
|
-
prepareLayout() {
|
|
164
|
-
const svgNode = this.svg?.node();
|
|
95
|
+
prepareLayout(context) {
|
|
96
|
+
super.prepareLayout(context);
|
|
165
97
|
this.xAxis?.clearEstimatedSpace?.();
|
|
166
|
-
if (
|
|
98
|
+
if (this.xAxis) {
|
|
167
99
|
const xKey = this.getXKey();
|
|
168
100
|
const labelKey = this.xAxis.labelKey;
|
|
169
101
|
const labels = this.data.map((item) => {
|
|
@@ -172,16 +104,10 @@ export class XYChart extends BaseChart {
|
|
|
172
104
|
}
|
|
173
105
|
return item[xKey];
|
|
174
106
|
});
|
|
175
|
-
this.xAxis.estimateLayoutSpace?.(labels, this.
|
|
176
|
-
}
|
|
177
|
-
if (svgNode && this.legend?.isInlineMode()) {
|
|
178
|
-
this.legend.estimateLayoutSpace(this.getLegendSeries(), this.theme, this.width, svgNode);
|
|
107
|
+
this.xAxis.estimateLayoutSpace?.(labels, this.renderTheme, context.svgNode);
|
|
179
108
|
}
|
|
180
109
|
}
|
|
181
|
-
renderChart() {
|
|
182
|
-
if (!this.plotArea) {
|
|
183
|
-
throw new Error('Plot area not calculated');
|
|
184
|
-
}
|
|
110
|
+
renderChart({ svg, plotGroup, plotArea, }) {
|
|
185
111
|
this.series.forEach((series) => {
|
|
186
112
|
const typeName = series.type === 'line'
|
|
187
113
|
? 'Line'
|
|
@@ -209,32 +135,26 @@ export class XYChart extends BaseChart {
|
|
|
209
135
|
const categoryScaleType = this.getCategoryScaleType();
|
|
210
136
|
const visibleSeries = this.getVisibleSeries();
|
|
211
137
|
this.setupScales();
|
|
212
|
-
|
|
213
|
-
const titlePos = this.layoutManager.getComponentPosition(this.title);
|
|
214
|
-
this.title.render(this.svg, this.theme, this.width, titlePos.x, titlePos.y);
|
|
215
|
-
}
|
|
138
|
+
this.renderTitle(svg);
|
|
216
139
|
if (this.grid && this.x && this.y) {
|
|
217
|
-
this.grid.render(
|
|
140
|
+
this.grid.render(plotGroup, this.x, this.y, this.renderTheme);
|
|
218
141
|
}
|
|
219
142
|
this.renderSeries(visibleSeries);
|
|
220
143
|
if (this.x && this.y) {
|
|
221
144
|
if (this.xAxis) {
|
|
222
|
-
this.xAxis.render(
|
|
145
|
+
this.xAxis.render(svg, this.x, this.renderTheme, plotArea.bottom, this.data);
|
|
223
146
|
}
|
|
224
147
|
if (this.yAxis) {
|
|
225
|
-
this.yAxis.render(
|
|
148
|
+
this.yAxis.render(svg, this.y, this.renderTheme, plotArea.left);
|
|
226
149
|
}
|
|
227
150
|
}
|
|
228
151
|
if (this.tooltip && this.x && this.y) {
|
|
229
152
|
const visibleAreaSeries = visibleSeries.filter((series) => series.type === 'area');
|
|
230
153
|
const areaStackingContextBySeries = this.computeAreaStackingContexts(this.data, xKey, visibleAreaSeries);
|
|
231
|
-
this.tooltip.initialize(this.
|
|
232
|
-
this.tooltip.attachToArea(
|
|
233
|
-
}
|
|
234
|
-
if (this.legend?.isInlineMode()) {
|
|
235
|
-
const legendPos = this.layoutManager.getComponentPosition(this.legend);
|
|
236
|
-
this.legend.render(this.svg, this.getLegendSeries(), this.theme, this.width, legendPos.x, legendPos.y);
|
|
154
|
+
this.tooltip.initialize(this.renderTheme);
|
|
155
|
+
this.tooltip.attachToArea(svg, this.data, visibleSeries, xKey, this.x, this.y, this.renderTheme, plotArea, this.parseValue.bind(this), this.isHorizontalOrientation(), categoryScaleType, (series, dataPoint) => this.getSeriesTooltipValue(series, dataPoint, xKey, areaStackingContextBySeries));
|
|
237
156
|
}
|
|
157
|
+
this.renderInlineLegend(svg);
|
|
238
158
|
}
|
|
239
159
|
getXKey() {
|
|
240
160
|
if (this.xAxis?.dataKey) {
|
|
@@ -261,11 +181,9 @@ export class XYChart extends BaseChart {
|
|
|
261
181
|
return this.scaleConfig.x?.type || 'band';
|
|
262
182
|
}
|
|
263
183
|
getVisibleSeries() {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
}
|
|
268
|
-
return displaySeries.filter((series) => this.legend.isSeriesVisible(series.dataKey));
|
|
184
|
+
return this.filterVisibleItems(this.getDisplaySeries(), (series) => {
|
|
185
|
+
return series.dataKey;
|
|
186
|
+
});
|
|
269
187
|
}
|
|
270
188
|
getDisplaySeries() {
|
|
271
189
|
if (!this.barStackReverseSeries) {
|
|
@@ -288,6 +206,39 @@ export class XYChart extends BaseChart {
|
|
|
288
206
|
return nextBar;
|
|
289
207
|
});
|
|
290
208
|
}
|
|
209
|
+
resolveSeriesDefaults(series) {
|
|
210
|
+
const colorIndex = this.series.length % this.theme.colorPalette.length;
|
|
211
|
+
const paletteColor = this.theme.colorPalette[colorIndex];
|
|
212
|
+
if (series.type === 'line') {
|
|
213
|
+
return this.cloneSeriesWithOverride(series, {
|
|
214
|
+
stroke: this.shouldReplaceSeriesColor(series.stroke)
|
|
215
|
+
? paletteColor
|
|
216
|
+
: series.stroke,
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
if (series.type === 'bar') {
|
|
220
|
+
return this.cloneSeriesWithOverride(series, {
|
|
221
|
+
fill: this.shouldReplaceSeriesColor(series.fill)
|
|
222
|
+
? paletteColor
|
|
223
|
+
: series.fill,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
const shouldUsePaletteColor = this.shouldReplaceSeriesColor(series.fill) &&
|
|
227
|
+
this.shouldReplaceSeriesColor(series.stroke);
|
|
228
|
+
return this.cloneSeriesWithOverride(series, shouldUsePaletteColor
|
|
229
|
+
? {
|
|
230
|
+
fill: paletteColor,
|
|
231
|
+
stroke: paletteColor,
|
|
232
|
+
}
|
|
233
|
+
: {});
|
|
234
|
+
}
|
|
235
|
+
shouldReplaceSeriesColor(color) {
|
|
236
|
+
return !color || color === DEFAULT_SERIES_COLOR;
|
|
237
|
+
}
|
|
238
|
+
cloneSeriesWithOverride(series, override) {
|
|
239
|
+
const exportable = series;
|
|
240
|
+
return exportable.createExportComponent(override);
|
|
241
|
+
}
|
|
291
242
|
setupScales() {
|
|
292
243
|
const xKey = this.getXKey();
|
|
293
244
|
const isHorizontal = this.isHorizontalOrientation();
|
|
@@ -555,13 +506,13 @@ export class XYChart extends BaseChart {
|
|
|
555
506
|
gap: this.barStackGap,
|
|
556
507
|
nextLayerData,
|
|
557
508
|
};
|
|
558
|
-
series.render(this.plotGroup, this.data, xKey, this.x, this.y, this.parseValue, categoryScaleType, this.
|
|
509
|
+
series.render(this.plotGroup, this.data, xKey, this.x, this.y, this.parseValue, categoryScaleType, this.renderTheme, stackingContext);
|
|
559
510
|
});
|
|
560
511
|
areaSeries.forEach((series) => {
|
|
561
|
-
series.render(this.plotGroup, this.data, xKey, this.x, this.y, this.parseValue, categoryScaleType, this.
|
|
512
|
+
series.render(this.plotGroup, this.data, xKey, this.x, this.y, this.parseValue, categoryScaleType, this.renderTheme, areaStackingContextBySeries.get(series), areaValueLabelLayer ?? undefined);
|
|
562
513
|
});
|
|
563
514
|
lineSeries.forEach((series) => {
|
|
564
|
-
series.render(this.plotGroup, this.data, xKey, this.x, this.y, this.parseValue, categoryScaleType, this.
|
|
515
|
+
series.render(this.plotGroup, this.data, xKey, this.x, this.y, this.parseValue, categoryScaleType, this.renderTheme);
|
|
565
516
|
});
|
|
566
517
|
if (areaValueLabelLayer) {
|
|
567
518
|
areaValueLabelLayer.raise();
|