@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.
Files changed (45) hide show
  1. package/README.md +64 -0
  2. package/dist/area.d.ts +9 -1
  3. package/dist/area.js +174 -38
  4. package/dist/bar.d.ts +9 -1
  5. package/dist/bar.js +130 -47
  6. package/dist/base-chart.js +11 -1
  7. package/dist/donut-chart.js +3 -18
  8. package/dist/gauge-chart.d.ts +3 -4
  9. package/dist/gauge-chart.js +7 -53
  10. package/dist/lazy-mount.d.ts +13 -0
  11. package/dist/lazy-mount.js +90 -0
  12. package/dist/line.d.ts +9 -1
  13. package/dist/line.js +141 -23
  14. package/dist/pie-chart.js +5 -29
  15. package/dist/radial-chart-base.d.ts +4 -3
  16. package/dist/radial-chart-base.js +27 -12
  17. package/dist/scatter.d.ts +5 -1
  18. package/dist/scatter.js +89 -8
  19. package/dist/theme.js +17 -0
  20. package/dist/tooltip.d.ts +55 -3
  21. package/dist/tooltip.js +950 -137
  22. package/dist/types.d.ts +20 -0
  23. package/dist/xy-animation.d.ts +3 -0
  24. package/dist/xy-animation.js +2 -0
  25. package/dist/xy-chart.d.ts +11 -1
  26. package/dist/xy-chart.js +107 -10
  27. package/dist/xy-motion/config.d.ts +2 -0
  28. package/dist/xy-motion/config.js +177 -0
  29. package/dist/xy-motion/driver.d.ts +9 -0
  30. package/dist/xy-motion/driver.js +10 -0
  31. package/dist/xy-motion/helpers.d.ts +17 -0
  32. package/dist/xy-motion/helpers.js +105 -0
  33. package/dist/xy-motion/live-state.d.ts +8 -0
  34. package/dist/xy-motion/live-state.js +240 -0
  35. package/dist/xy-motion/noop-xy-motion-driver.d.ts +9 -0
  36. package/dist/xy-motion/noop-xy-motion-driver.js +15 -0
  37. package/dist/xy-motion/types.d.ts +85 -0
  38. package/dist/xy-motion/types.js +1 -0
  39. package/dist/xy-motion/xy-motion-driver.d.ts +19 -0
  40. package/dist/xy-motion/xy-motion-driver.js +130 -0
  41. package/docs/components.md +36 -0
  42. package/docs/getting-started.md +35 -0
  43. package/docs/theming.md +14 -0
  44. package/docs/xy-chart.md +67 -1
  45. 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): void;
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 validData = data.filter(hasValidValue);
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', getXPosition)
86
- .attr('cy', (d) => y(parseValue(d[this.dataKey])) || 0)
87
- .attr('r', pointSize)
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 (this.valueLabel?.show) {
92
- this.renderValueLabels(plotGroup, validData, y, parseValue, theme, getXPosition, pointSize);
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
- readonly id = "iisChartTooltip";
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: (Line | Bar | Area | Scatter)[], xKey: string, x: D3Scale, y: D3Scale, theme: ChartTheme, plotArea: PlotAreaBounds, parseValue: (value: unknown) => number, isHorizontal?: boolean, categoryScaleType?: ScaleType, resolveSeriesValue?: (series: Line | Bar | Area | Scatter, dataPoint: DataItem, index: number) => number): void;
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 {};