@internetstiftelsen/charts 0.7.1 → 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 +54 -8
- package/area.d.ts +0 -1
- package/area.js +2 -19
- package/bar.d.ts +0 -1
- package/bar.js +64 -136
- 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/types.d.ts +2 -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 +8 -4
- package/xy-chart.js +127 -127
package/donut-chart.js
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { arc, pie, select } from 'd3';
|
|
2
|
-
import { BaseChart } from './base-chart.js';
|
|
3
2
|
import { sanitizeForCSS } from './utils.js';
|
|
4
3
|
import { ChartValidator } from './validation.js';
|
|
4
|
+
import { RadialChartBase } from './radial-chart-base.js';
|
|
5
5
|
const HOVER_EXPAND_PX = 8;
|
|
6
6
|
const ANIMATION_DURATION_MS = 150;
|
|
7
|
-
|
|
8
|
-
const EDGE_MARGIN_PX = 10;
|
|
9
|
-
export class DonutChart extends BaseChart {
|
|
7
|
+
export class DonutChart extends RadialChartBase {
|
|
10
8
|
constructor(config) {
|
|
11
9
|
super(config);
|
|
12
10
|
Object.defineProperty(this, "innerRadiusRatio", {
|
|
@@ -58,8 +56,7 @@ export class DonutChart extends BaseChart {
|
|
|
58
56
|
this.cornerRadius = donut.cornerRadius ?? this.theme.donut.cornerRadius;
|
|
59
57
|
this.valueKey = config.valueKey ?? 'value';
|
|
60
58
|
this.labelKey = config.labelKey ?? 'name';
|
|
61
|
-
this.
|
|
62
|
-
this.prepareSegments();
|
|
59
|
+
this.initializeDataState();
|
|
63
60
|
}
|
|
64
61
|
validateDonutData() {
|
|
65
62
|
ChartValidator.validateDataKey(this.data, this.labelKey, 'DonutChart');
|
|
@@ -81,65 +78,34 @@ export class DonutChart extends BaseChart {
|
|
|
81
78
|
}));
|
|
82
79
|
}
|
|
83
80
|
addChild(component) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
this.update(this.data);
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
else if (type === 'title') {
|
|
98
|
-
this.title = component;
|
|
99
|
-
}
|
|
100
|
-
else if (type === 'donutCenterContent') {
|
|
101
|
-
this.centerContent = component;
|
|
81
|
+
if (this.tryRegisterComponent(component, [
|
|
82
|
+
{
|
|
83
|
+
type: 'donutCenterContent',
|
|
84
|
+
get: () => this.centerContent,
|
|
85
|
+
set: (entry) => {
|
|
86
|
+
this.centerContent = entry;
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
])) {
|
|
90
|
+
return this;
|
|
102
91
|
}
|
|
103
|
-
return
|
|
92
|
+
return super.addChild(component);
|
|
104
93
|
}
|
|
105
94
|
getExportComponents() {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
if (this.legend?.isInlineMode()) {
|
|
117
|
-
components.push(this.legend);
|
|
118
|
-
}
|
|
119
|
-
return components;
|
|
95
|
+
return [
|
|
96
|
+
...this.getBaseExportComponents({
|
|
97
|
+
title: true,
|
|
98
|
+
}),
|
|
99
|
+
...(this.centerContent ? [this.centerContent] : []),
|
|
100
|
+
...this.getBaseExportComponents({
|
|
101
|
+
tooltip: true,
|
|
102
|
+
legend: this.legend?.isInlineMode(),
|
|
103
|
+
}),
|
|
104
|
+
];
|
|
120
105
|
}
|
|
121
106
|
update(data) {
|
|
122
|
-
this.data = data;
|
|
123
|
-
this.validateDonutData();
|
|
124
|
-
this.prepareSegments();
|
|
125
107
|
super.update(data);
|
|
126
108
|
}
|
|
127
|
-
getLayoutComponents() {
|
|
128
|
-
const components = [];
|
|
129
|
-
if (this.title) {
|
|
130
|
-
components.push(this.title);
|
|
131
|
-
}
|
|
132
|
-
if (this.legend) {
|
|
133
|
-
components.push(this.legend);
|
|
134
|
-
}
|
|
135
|
-
return components;
|
|
136
|
-
}
|
|
137
|
-
prepareLayout() {
|
|
138
|
-
const svgNode = this.svg?.node();
|
|
139
|
-
if (svgNode && this.legend?.isInlineMode()) {
|
|
140
|
-
this.legend.estimateLayoutSpace(this.getLegendSeries(), this.theme, this.width, svgNode);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
109
|
createExportChart() {
|
|
144
110
|
return new DonutChart({
|
|
145
111
|
data: this.data,
|
|
@@ -156,81 +122,37 @@ export class DonutChart extends BaseChart {
|
|
|
156
122
|
}
|
|
157
123
|
applyComponentOverrides(overrides) {
|
|
158
124
|
const restoreBase = super.applyComponentOverrides(overrides);
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}
|
|
125
|
+
const restoreCenterContent = this.applySlotOverrides(overrides, [
|
|
126
|
+
{
|
|
127
|
+
type: 'donutCenterContent',
|
|
128
|
+
get: () => this.centerContent,
|
|
129
|
+
set: (component) => {
|
|
130
|
+
this.centerContent = component;
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
]);
|
|
169
134
|
return () => {
|
|
170
|
-
|
|
135
|
+
restoreCenterContent();
|
|
171
136
|
restoreBase();
|
|
172
137
|
};
|
|
173
138
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
: this.segments;
|
|
185
|
-
const cx = this.plotArea.left + this.plotArea.width / 2;
|
|
186
|
-
const cy = this.plotArea.top + this.plotArea.height / 2;
|
|
187
|
-
const outerRadius = Math.min(this.plotArea.width, this.plotArea.height) / 2;
|
|
188
|
-
const innerRadius = outerRadius * this.innerRadiusRatio;
|
|
189
|
-
const fontScale = this.resolveFontScale(outerRadius);
|
|
190
|
-
if (this.tooltip) {
|
|
191
|
-
this.tooltip.initialize(this.theme);
|
|
192
|
-
}
|
|
193
|
-
this.renderSegments(visibleSegments, cx, cy, innerRadius, outerRadius);
|
|
139
|
+
syncDerivedState() {
|
|
140
|
+
this.validateDonutData();
|
|
141
|
+
this.prepareSegments();
|
|
142
|
+
}
|
|
143
|
+
renderChart({ svg, plotGroup, plotArea, }) {
|
|
144
|
+
this.renderTitle(svg);
|
|
145
|
+
const visibleSegments = this.getVisibleRadialItems(this.segments);
|
|
146
|
+
const { cx, cy, outerRadius, innerRadius, fontScale } = this.getRadialLayout(plotArea, this.innerRadiusRatio);
|
|
147
|
+
this.initializeTooltip();
|
|
148
|
+
this.renderSegments(plotGroup, visibleSegments, cx, cy, innerRadius, outerRadius);
|
|
194
149
|
if (this.centerContent) {
|
|
195
|
-
this.centerContent.render(
|
|
196
|
-
}
|
|
197
|
-
if (this.legend?.isInlineMode()) {
|
|
198
|
-
const pos = this.layoutManager.getComponentPosition(this.legend);
|
|
199
|
-
this.legend.render(this.svg, this.getLegendSeries(), this.theme, this.width, pos.x, pos.y);
|
|
150
|
+
this.centerContent.render(svg, cx, cy, this.renderTheme, fontScale);
|
|
200
151
|
}
|
|
201
|
-
|
|
202
|
-
resolveFontScale(outerRadius) {
|
|
203
|
-
const plotHeight = Math.max(1, this.theme.height -
|
|
204
|
-
this.theme.margins.top -
|
|
205
|
-
this.theme.margins.bottom);
|
|
206
|
-
const referenceRadius = Math.max(1, plotHeight / 2);
|
|
207
|
-
const rawScale = outerRadius / referenceRadius;
|
|
208
|
-
return Math.max(0.5, Math.min(1, rawScale));
|
|
152
|
+
this.renderInlineLegend(svg);
|
|
209
153
|
}
|
|
210
154
|
getLegendSeries() {
|
|
211
|
-
return this.segments
|
|
212
|
-
return {
|
|
213
|
-
dataKey: segment.label,
|
|
214
|
-
fill: segment.color,
|
|
215
|
-
};
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
positionTooltip(event, tooltipDiv) {
|
|
219
|
-
const node = tooltipDiv.node();
|
|
220
|
-
if (!node)
|
|
221
|
-
return;
|
|
222
|
-
const rect = node.getBoundingClientRect();
|
|
223
|
-
let x = event.pageX + TOOLTIP_OFFSET_PX;
|
|
224
|
-
let y = event.pageY - rect.height / 2;
|
|
225
|
-
if (x + rect.width > window.innerWidth - EDGE_MARGIN_PX) {
|
|
226
|
-
x = event.pageX - rect.width - TOOLTIP_OFFSET_PX;
|
|
227
|
-
}
|
|
228
|
-
x = Math.max(EDGE_MARGIN_PX, x);
|
|
229
|
-
y = Math.max(EDGE_MARGIN_PX, Math.min(y, window.innerHeight +
|
|
230
|
-
window.scrollY -
|
|
231
|
-
rect.height -
|
|
232
|
-
EDGE_MARGIN_PX));
|
|
233
|
-
tooltipDiv.style('left', `${x}px`).style('top', `${y}px`);
|
|
155
|
+
return this.getRadialLegendSeries(this.segments);
|
|
234
156
|
}
|
|
235
157
|
buildTooltipContent(d, segments) {
|
|
236
158
|
const total = segments.reduce((sum, s) => sum + s.value, 0);
|
|
@@ -246,9 +168,7 @@ export class DonutChart extends BaseChart {
|
|
|
246
168
|
}
|
|
247
169
|
return `<strong>${d.data.label}</strong><br/>${d.data.value} (${percentage}%)`;
|
|
248
170
|
}
|
|
249
|
-
renderSegments(segments, cx, cy, innerRadius, outerRadius) {
|
|
250
|
-
if (!this.plotGroup || !this.svg)
|
|
251
|
-
return;
|
|
171
|
+
renderSegments(plotGroup, segments, cx, cy, innerRadius, outerRadius) {
|
|
252
172
|
const pieGenerator = pie()
|
|
253
173
|
.value((d) => d.value)
|
|
254
174
|
.padAngle(this.padAngle)
|
|
@@ -262,7 +182,7 @@ export class DonutChart extends BaseChart {
|
|
|
262
182
|
.outerRadius(outerRadius + HOVER_EXPAND_PX)
|
|
263
183
|
.cornerRadius(this.cornerRadius);
|
|
264
184
|
const pieData = pieGenerator(segments);
|
|
265
|
-
const segmentGroup =
|
|
185
|
+
const segmentGroup = plotGroup
|
|
266
186
|
.append('g')
|
|
267
187
|
.attr('class', 'donut-segments')
|
|
268
188
|
.attr('transform', `translate(${cx}, ${cy})`);
|
|
@@ -292,13 +212,13 @@ export class DonutChart extends BaseChart {
|
|
|
292
212
|
tooltipDiv
|
|
293
213
|
.style('visibility', 'visible')
|
|
294
214
|
.html(this.buildTooltipContent(d, segments));
|
|
295
|
-
this.
|
|
215
|
+
this.positionTooltipFromPointer(event, tooltipDiv);
|
|
296
216
|
}
|
|
297
217
|
})
|
|
298
218
|
.on('mousemove', (event) => {
|
|
299
219
|
const tooltipDiv = resolveTooltipDiv();
|
|
300
220
|
if (tooltipDiv && !tooltipDiv.empty()) {
|
|
301
|
-
this.
|
|
221
|
+
this.positionTooltipFromPointer(event, tooltipDiv);
|
|
302
222
|
}
|
|
303
223
|
})
|
|
304
224
|
.on('mouseleave', (event, d) => {
|
package/gauge-chart.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { DataItem, LegendSeries } from './types.js';
|
|
2
|
-
import { BaseChart, type BaseChartConfig } from './base-chart.js';
|
|
3
|
-
import type { ChartComponent
|
|
2
|
+
import { BaseChart, type BaseChartConfig, type BaseRenderContext } from './base-chart.js';
|
|
3
|
+
import type { ChartComponent } from './chart-interface.js';
|
|
4
4
|
export type GaugeSegment = {
|
|
5
5
|
from: number;
|
|
6
6
|
to: number;
|
|
@@ -35,10 +35,17 @@ export type GaugeLabelStyle = {
|
|
|
35
35
|
fontWeight?: number | string;
|
|
36
36
|
color?: string;
|
|
37
37
|
};
|
|
38
|
+
export type GaugeAnimationEasingPreset = 'linear' | 'ease-in' | 'ease-out' | 'ease-in-out' | 'bounce-out' | 'elastic-out';
|
|
39
|
+
export type GaugeAnimationConfig = {
|
|
40
|
+
show?: boolean;
|
|
41
|
+
duration?: number;
|
|
42
|
+
easing?: GaugeAnimationEasingPreset | `linear(${string})` | ((progress: number) => number);
|
|
43
|
+
};
|
|
38
44
|
export type GaugeConfig = {
|
|
39
45
|
value?: number;
|
|
40
46
|
min?: number;
|
|
41
47
|
max?: number;
|
|
48
|
+
animate?: boolean | GaugeAnimationConfig;
|
|
42
49
|
halfCircle?: boolean;
|
|
43
50
|
startAngle?: number;
|
|
44
51
|
endAngle?: number;
|
|
@@ -71,6 +78,7 @@ export declare class GaugeChart extends BaseChart {
|
|
|
71
78
|
private readonly targetValueKey;
|
|
72
79
|
private readonly minValue;
|
|
73
80
|
private readonly maxValue;
|
|
81
|
+
private readonly animation;
|
|
74
82
|
private readonly halfCircle;
|
|
75
83
|
private readonly startAngle;
|
|
76
84
|
private readonly endAngle;
|
|
@@ -91,11 +99,15 @@ export declare class GaugeChart extends BaseChart {
|
|
|
91
99
|
private segments;
|
|
92
100
|
private value;
|
|
93
101
|
private targetValue;
|
|
102
|
+
private lastRenderedValue;
|
|
94
103
|
constructor(config: GaugeChartConfig);
|
|
95
104
|
private normalizeNeedleConfig;
|
|
96
105
|
private normalizeMarkerConfig;
|
|
97
106
|
private getThemePaletteColor;
|
|
98
107
|
private normalizeTickConfig;
|
|
108
|
+
private normalizeAnimationConfig;
|
|
109
|
+
private resolveAnimationEasing;
|
|
110
|
+
private parseCssLinearEasing;
|
|
99
111
|
private normalizeTickLabelStyle;
|
|
100
112
|
private normalizeValueLabelStyle;
|
|
101
113
|
private validateGaugeConfig;
|
|
@@ -108,13 +120,13 @@ export declare class GaugeChart extends BaseChart {
|
|
|
108
120
|
private clampToDomain;
|
|
109
121
|
private prepareSegments;
|
|
110
122
|
private defaultFormat;
|
|
111
|
-
addChild(component: ChartComponent): this;
|
|
112
123
|
protected getExportComponents(): ChartComponent[];
|
|
113
124
|
update(data: DataItem[]): void;
|
|
114
|
-
protected getLayoutComponents(): LayoutAwareComponent[];
|
|
115
|
-
protected prepareLayout(): void;
|
|
116
125
|
protected createExportChart(): BaseChart;
|
|
117
|
-
protected
|
|
126
|
+
protected syncDerivedState(): void;
|
|
127
|
+
protected renderChart({ svg, plotGroup, plotArea, }: BaseRenderContext): void;
|
|
128
|
+
private resolveAnimationStartValue;
|
|
129
|
+
private shouldAnimateTransition;
|
|
118
130
|
private buildAriaLabel;
|
|
119
131
|
private findSegmentStatusLabel;
|
|
120
132
|
private getVisibleSegments;
|
|
@@ -135,6 +147,5 @@ export declare class GaugeChart extends BaseChart {
|
|
|
135
147
|
private resolveTooltipDiv;
|
|
136
148
|
private buildTooltipContent;
|
|
137
149
|
private positionTooltip;
|
|
138
|
-
private renderLegend;
|
|
139
150
|
protected getLegendSeries(): LegendSeries[];
|
|
140
151
|
}
|