@internetstiftelsen/charts 0.10.1 → 0.11.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 +64 -0
- package/dist/area.d.ts +9 -1
- package/dist/area.js +174 -38
- package/dist/bar.d.ts +9 -1
- package/dist/bar.js +130 -47
- package/dist/base-chart.js +11 -1
- package/dist/donut-chart.js +3 -18
- package/dist/gauge-chart.d.ts +3 -4
- package/dist/gauge-chart.js +7 -53
- package/dist/lazy-mount.d.ts +13 -0
- package/dist/lazy-mount.js +90 -0
- package/dist/line.d.ts +9 -1
- package/dist/line.js +141 -23
- package/dist/pie-chart.js +5 -29
- package/dist/radial-chart-base.d.ts +4 -3
- package/dist/radial-chart-base.js +27 -12
- package/dist/scatter.d.ts +5 -1
- package/dist/scatter.js +89 -8
- package/dist/theme.js +17 -0
- package/dist/tooltip.d.ts +55 -3
- package/dist/tooltip.js +950 -137
- package/dist/types.d.ts +20 -0
- package/dist/xy-animation.d.ts +3 -0
- package/dist/xy-animation.js +2 -0
- package/dist/xy-chart.d.ts +11 -1
- package/dist/xy-chart.js +107 -10
- package/dist/xy-motion/config.d.ts +2 -0
- package/dist/xy-motion/config.js +177 -0
- package/dist/xy-motion/driver.d.ts +9 -0
- package/dist/xy-motion/driver.js +10 -0
- package/dist/xy-motion/helpers.d.ts +17 -0
- package/dist/xy-motion/helpers.js +105 -0
- package/dist/xy-motion/live-state.d.ts +8 -0
- package/dist/xy-motion/live-state.js +240 -0
- package/dist/xy-motion/noop-xy-motion-driver.d.ts +9 -0
- package/dist/xy-motion/noop-xy-motion-driver.js +15 -0
- package/dist/xy-motion/types.d.ts +85 -0
- package/dist/xy-motion/types.js +1 -0
- package/dist/xy-motion/xy-motion-driver.d.ts +19 -0
- package/dist/xy-motion/xy-motion-driver.js +130 -0
- package/docs/components.md +36 -0
- package/docs/getting-started.md +35 -0
- package/docs/theming.md +14 -0
- package/docs/xy-chart.md +67 -1
- package/package.json +1 -1
package/dist/scatter.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ChartTheme, D3Scale, DataItem, ExportHooks, LineValueLabelConfig, ScaleType, ScatterConfig, ScatterConfigBase } from './types.js';
|
|
2
2
|
import type { ChartComponent } from './chart-interface.js';
|
|
3
3
|
import type { Selection } from 'd3';
|
|
4
|
+
import type { XYPointAnimationContext, XYPointSnapshot, XYSeriesRenderResult } from './xy-motion/types.js';
|
|
4
5
|
export declare class Scatter implements ChartComponent<ScatterConfigBase> {
|
|
5
6
|
readonly type: "scatter";
|
|
6
7
|
readonly dataKey: string;
|
|
@@ -11,6 +12,9 @@ export declare class Scatter implements ChartComponent<ScatterConfigBase> {
|
|
|
11
12
|
constructor(config: ScatterConfig);
|
|
12
13
|
getExportConfig(): ScatterConfigBase;
|
|
13
14
|
createExportComponent(override?: Partial<ScatterConfigBase>): ChartComponent<ScatterConfigBase>;
|
|
14
|
-
render(plotGroup: Selection<SVGGElement, undefined, null, undefined>, data: DataItem[], xKey: string, x: D3Scale, y: D3Scale, parseValue: (value: unknown) => number, xScaleType: ScaleType | undefined, theme: ChartTheme):
|
|
15
|
+
render(plotGroup: Selection<SVGGElement, undefined, null, undefined>, data: DataItem[], xKey: string, x: D3Scale, y: D3Scale, parseValue: (value: unknown) => number, xScaleType: ScaleType | undefined, theme: ChartTheme, animation?: XYPointAnimationContext): XYSeriesRenderResult<XYPointSnapshot>;
|
|
16
|
+
private buildAnimatedData;
|
|
17
|
+
private createSnapshot;
|
|
18
|
+
private renderPoints;
|
|
15
19
|
private renderValueLabels;
|
|
16
20
|
}
|
package/dist/scatter.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { mergeDeep, sanitizeForCSS } from './utils.js';
|
|
2
2
|
import { getScalePosition } from './scale-utils.js';
|
|
3
|
+
import { buildXYDatumSnapshotKeys, createTransitionCompletionPromise, getEnterStaggerTiming, } from './xy-motion/helpers.js';
|
|
3
4
|
export class Scatter {
|
|
4
5
|
constructor(config) {
|
|
5
6
|
Object.defineProperty(this, "type", {
|
|
@@ -59,7 +60,7 @@ export class Scatter {
|
|
|
59
60
|
exportHooks: this.exportHooks,
|
|
60
61
|
});
|
|
61
62
|
}
|
|
62
|
-
render(plotGroup, data, xKey, x, y, parseValue, xScaleType = 'band', theme) {
|
|
63
|
+
render(plotGroup, data, xKey, x, y, parseValue, xScaleType = 'band', theme, animation) {
|
|
63
64
|
const getXPosition = (d) => {
|
|
64
65
|
return (getScalePosition(x, d[xKey], xScaleType) +
|
|
65
66
|
(x.bandwidth ? x.bandwidth() / 2 : 0));
|
|
@@ -71,26 +72,106 @@ export class Scatter {
|
|
|
71
72
|
}
|
|
72
73
|
return Number.isFinite(parseValue(value));
|
|
73
74
|
};
|
|
74
|
-
const
|
|
75
|
+
const snapshotKeys = buildXYDatumSnapshotKeys(data, xKey);
|
|
76
|
+
const validData = data.flatMap((entry, index) => {
|
|
77
|
+
if (!hasValidValue(entry)) {
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
return [
|
|
81
|
+
{
|
|
82
|
+
data: entry,
|
|
83
|
+
snapshotKey: snapshotKeys[index] ?? String(index),
|
|
84
|
+
x: getXPosition(entry),
|
|
85
|
+
y: y(parseValue(entry[this.dataKey])) ?? 0,
|
|
86
|
+
},
|
|
87
|
+
];
|
|
88
|
+
});
|
|
89
|
+
const animatedData = this.buildAnimatedData(validData, animation);
|
|
75
90
|
const pointSize = this.pointSize ?? theme.line.point.size;
|
|
76
91
|
const pointStrokeWidth = theme.line.point.strokeWidth;
|
|
77
92
|
const pointStrokeColor = theme.line.point.strokeColor || this.stroke;
|
|
78
93
|
const pointColor = theme.line.point.color || this.stroke;
|
|
79
94
|
const sanitizedKey = sanitizeForCSS(this.dataKey);
|
|
80
|
-
plotGroup
|
|
95
|
+
const transitions = this.renderPoints(plotGroup, validData, animatedData, sanitizedKey, pointSize, pointColor, pointStrokeColor, pointStrokeWidth, animation);
|
|
96
|
+
const snapshot = this.createSnapshot(validData);
|
|
97
|
+
if (this.valueLabel?.show) {
|
|
98
|
+
this.renderValueLabels(plotGroup, validData.map((entry) => entry.data), y, parseValue, theme, getXPosition, pointSize);
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
snapshot,
|
|
102
|
+
transitions,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
buildAnimatedData(validData, animation) {
|
|
106
|
+
return validData.map((entry) => {
|
|
107
|
+
if (!animation) {
|
|
108
|
+
return entry;
|
|
109
|
+
}
|
|
110
|
+
const previousSnapshot = animation.previousSnapshot?.get(entry.snapshotKey);
|
|
111
|
+
return {
|
|
112
|
+
...entry,
|
|
113
|
+
x: previousSnapshot?.x ?? entry.x,
|
|
114
|
+
y: previousSnapshot?.y ?? animation.baselineY,
|
|
115
|
+
};
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
createSnapshot(validData) {
|
|
119
|
+
const snapshot = new Map();
|
|
120
|
+
validData.forEach((entry) => {
|
|
121
|
+
snapshot.set(entry.snapshotKey, {
|
|
122
|
+
x: entry.x,
|
|
123
|
+
y: entry.y,
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
return snapshot;
|
|
127
|
+
}
|
|
128
|
+
renderPoints(plotGroup, validData, animatedData, sanitizedKey, pointSize, pointColor, pointStrokeColor, pointStrokeWidth, animation) {
|
|
129
|
+
const isInitialAnimation = animation?.mode === 'initial';
|
|
130
|
+
const circles = plotGroup
|
|
81
131
|
.selectAll(`.scatter-point-${sanitizedKey}`)
|
|
82
132
|
.data(validData)
|
|
83
133
|
.join('circle')
|
|
84
134
|
.attr('class', `scatter-point-${sanitizedKey}`)
|
|
85
|
-
.attr('cx',
|
|
86
|
-
|
|
87
|
-
|
|
135
|
+
.attr('cx', (_, index) => (isInitialAnimation
|
|
136
|
+
? validData[index]?.x
|
|
137
|
+
: animation
|
|
138
|
+
? animatedData[index]?.x
|
|
139
|
+
: validData[index]?.x) ?? 0)
|
|
140
|
+
.attr('cy', (_, index) => (isInitialAnimation
|
|
141
|
+
? (validData[index]?.y ?? 0) - pointSize * 2
|
|
142
|
+
: animation
|
|
143
|
+
? animatedData[index]?.y
|
|
144
|
+
: validData[index]?.y) ?? 0)
|
|
145
|
+
.attr('r', isInitialAnimation ? 0 : pointSize)
|
|
146
|
+
.attr('opacity', isInitialAnimation ? 0 : 1)
|
|
88
147
|
.attr('fill', pointColor)
|
|
89
148
|
.attr('stroke', pointStrokeColor)
|
|
90
149
|
.attr('stroke-width', pointStrokeWidth);
|
|
91
|
-
if (
|
|
92
|
-
|
|
150
|
+
if (!animation) {
|
|
151
|
+
return [];
|
|
152
|
+
}
|
|
153
|
+
const transition = circles.transition();
|
|
154
|
+
if (isInitialAnimation) {
|
|
155
|
+
transition
|
|
156
|
+
.delay((_, index) => {
|
|
157
|
+
return getEnterStaggerTiming(index, validData.length, animation.duration).delay;
|
|
158
|
+
})
|
|
159
|
+
.duration((_, index) => {
|
|
160
|
+
return getEnterStaggerTiming(index, validData.length, animation.duration).duration;
|
|
161
|
+
})
|
|
162
|
+
.ease(animation.easing)
|
|
163
|
+
.attr('cy', (_, index) => validData[index]?.y ?? 0)
|
|
164
|
+
.attr('r', pointSize)
|
|
165
|
+
.attr('opacity', 1);
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
transition
|
|
169
|
+
.duration(animation.duration)
|
|
170
|
+
.ease(animation.easing)
|
|
171
|
+
.attr('cx', (_, index) => validData[index]?.x ?? 0)
|
|
172
|
+
.attr('cy', (_, index) => validData[index]?.y ?? 0);
|
|
93
173
|
}
|
|
174
|
+
return [createTransitionCompletionPromise(transition)];
|
|
94
175
|
}
|
|
95
176
|
renderValueLabels(plotGroup, data, y, parseValue, theme, getXPosition, pointSize) {
|
|
96
177
|
const config = this.valueLabel;
|
package/dist/theme.js
CHANGED
|
@@ -40,6 +40,14 @@ export const defaultTheme = {
|
|
|
40
40
|
itemSpacingX: 20,
|
|
41
41
|
itemSpacingY: 8,
|
|
42
42
|
},
|
|
43
|
+
tooltip: {
|
|
44
|
+
background: '#ffffff',
|
|
45
|
+
border: '#dddddd',
|
|
46
|
+
color: '#1f2a36',
|
|
47
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"',
|
|
48
|
+
fontSize: 12,
|
|
49
|
+
fontWeight: 'normal',
|
|
50
|
+
},
|
|
43
51
|
line: {
|
|
44
52
|
strokeWidth: 4,
|
|
45
53
|
point: {
|
|
@@ -120,6 +128,14 @@ export const newspaperTheme = {
|
|
|
120
128
|
itemSpacingX: 20,
|
|
121
129
|
itemSpacingY: 8,
|
|
122
130
|
},
|
|
131
|
+
tooltip: {
|
|
132
|
+
background: '#ffffff',
|
|
133
|
+
border: '#2c2c2c',
|
|
134
|
+
color: '#1a1a1a',
|
|
135
|
+
fontFamily: 'Georgia, "Times New Roman", Times, serif',
|
|
136
|
+
fontSize: 11,
|
|
137
|
+
fontWeight: 'normal',
|
|
138
|
+
},
|
|
123
139
|
line: {
|
|
124
140
|
strokeWidth: 2.5,
|
|
125
141
|
point: {
|
|
@@ -169,6 +185,7 @@ export const defaultResponsiveConfig = {
|
|
|
169
185
|
theme: {
|
|
170
186
|
axis: { fontSize: 11 },
|
|
171
187
|
legend: { fontSize: 11 },
|
|
188
|
+
tooltip: { fontSize: 11 },
|
|
172
189
|
valueLabel: { fontSize: 10 },
|
|
173
190
|
},
|
|
174
191
|
},
|
package/dist/tooltip.d.ts
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
import { type Selection } from 'd3';
|
|
2
|
-
import type { TooltipConfig, DataItem, DataValue, D3Scale, ChartTheme, ExportHooks, TooltipConfigBase, ScaleType } from './types.js';
|
|
2
|
+
import type { TooltipConfig, DataItem, DataValue, D3Scale, ChartTheme, ExportHooks, TooltipConfigBase, ScaleType, TooltipMode, TooltipPosition, TooltipBarAnchorPosition, TooltipTransitionConfig } from './types.js';
|
|
3
3
|
import type { ChartComponent } from './chart-interface.js';
|
|
4
4
|
import type { Line } from './line.js';
|
|
5
5
|
import type { Bar } from './bar.js';
|
|
6
6
|
import type { Area } from './area.js';
|
|
7
7
|
import type { Scatter } from './scatter.js';
|
|
8
8
|
import type { PlotAreaBounds } from './layout-manager.js';
|
|
9
|
+
type XYTooltipSeries = Line | Bar | Area | Scatter;
|
|
9
10
|
export declare class Tooltip implements ChartComponent<TooltipConfigBase> {
|
|
10
|
-
|
|
11
|
+
private static nextTooltipId;
|
|
12
|
+
readonly id: string;
|
|
11
13
|
readonly type: "tooltip";
|
|
14
|
+
readonly mode: TooltipMode;
|
|
15
|
+
readonly position: TooltipPosition;
|
|
16
|
+
readonly barAnchorPosition: TooltipBarAnchorPosition;
|
|
17
|
+
readonly transition: Required<TooltipTransitionConfig>;
|
|
12
18
|
readonly formatter?: (dataKey: string, value: DataValue, data: DataItem) => string;
|
|
13
19
|
readonly labelFormatter?: (label: string, data: DataItem) => string;
|
|
14
20
|
readonly customFormatter?: (data: DataItem, series: {
|
|
@@ -17,11 +23,57 @@ export declare class Tooltip implements ChartComponent<TooltipConfigBase> {
|
|
|
17
23
|
fill?: string;
|
|
18
24
|
}[]) => string;
|
|
19
25
|
readonly exportHooks?: ExportHooks<TooltipConfigBase>;
|
|
26
|
+
private readonly splitTooltipOwner;
|
|
27
|
+
private readonly tooltipStyleKeys;
|
|
20
28
|
private tooltipDiv;
|
|
29
|
+
private tooltipTheme;
|
|
21
30
|
constructor(config?: TooltipConfig);
|
|
22
31
|
getExportConfig(): TooltipConfigBase;
|
|
23
32
|
createExportComponent(override?: Partial<TooltipConfigBase>): ChartComponent<TooltipConfigBase>;
|
|
24
33
|
initialize(theme: ChartTheme): void;
|
|
25
|
-
attachToArea(svg: Selection<SVGSVGElement, undefined, null, undefined>, data: DataItem[], series:
|
|
34
|
+
attachToArea(svg: Selection<SVGSVGElement, undefined, null, undefined>, data: DataItem[], series: XYTooltipSeries[], xKey: string, x: D3Scale, y: D3Scale, theme: ChartTheme, plotArea: PlotAreaBounds, parseValue: (value: unknown) => number, isHorizontal?: boolean, categoryScaleType?: ScaleType, resolveSeriesValue?: (series: XYTooltipSeries, dataPoint: DataItem, index: number) => number): void;
|
|
35
|
+
setContent(content: string): void;
|
|
36
|
+
getBounds(): DOMRect | null;
|
|
37
|
+
showAt(left: number, top: number): void;
|
|
38
|
+
hide(): void;
|
|
26
39
|
cleanup(): void;
|
|
40
|
+
private applyTooltipStylesIfNeeded;
|
|
41
|
+
private getTooltipStyleKey;
|
|
42
|
+
private writeTooltipStyles;
|
|
43
|
+
private measureTooltip;
|
|
44
|
+
private renderTooltipWithConnector;
|
|
45
|
+
private renderTooltipWithoutConnector;
|
|
46
|
+
private showTooltipAt;
|
|
47
|
+
private showTooltipSelection;
|
|
48
|
+
private hideTooltipSelection;
|
|
49
|
+
private hideTooltipElement;
|
|
50
|
+
private setTooltipMarkup;
|
|
51
|
+
private appendTooltipConnector;
|
|
52
|
+
private resolveBarTooltipAnchor;
|
|
53
|
+
private resolvePointTooltipAnchor;
|
|
54
|
+
private getSplitTooltip;
|
|
55
|
+
private hideSplitTooltips;
|
|
56
|
+
private removeSplitTooltips;
|
|
57
|
+
private removeRootTooltip;
|
|
58
|
+
private resolveTooltipArrowEdge;
|
|
59
|
+
private resolveSidePlacementArrowEdge;
|
|
60
|
+
private resolveVerticalPlacementArrowEdge;
|
|
61
|
+
private resolveSharedTooltipTarget;
|
|
62
|
+
private resolveSplitTooltipTarget;
|
|
63
|
+
private getTooltipConnectorOffset;
|
|
64
|
+
private getAnchoredTooltipPosition;
|
|
65
|
+
private resolveTooltipConnectorLayout;
|
|
66
|
+
private resolveTooltipBoxArrowPosition;
|
|
67
|
+
private resolveTooltipArrowBaseMaskPath;
|
|
68
|
+
private resolveTooltipConnectorPath;
|
|
69
|
+
private resolveTooltipBoxArrow;
|
|
70
|
+
private hasFiniteNumbers;
|
|
71
|
+
private resolveSplitTooltipCollisions;
|
|
72
|
+
private getOppositeSideArrowEdge;
|
|
73
|
+
private getOppositeVerticalArrowEdge;
|
|
74
|
+
private flipTooltipIfItReducesCollisions;
|
|
75
|
+
private countSplitTooltipCollisions;
|
|
76
|
+
private doSplitTooltipLayoutsOverlap;
|
|
77
|
+
private resolveSplitTooltipPositions;
|
|
27
78
|
}
|
|
79
|
+
export {};
|