@internetstiftelsen/charts 0.11.0 → 0.13.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/dist/text.d.ts ADDED
@@ -0,0 +1,40 @@
1
+ import { type Selection } from 'd3';
2
+ import type { ChartTheme, ExportHooks, TextAlign, TextConfig, TextConfigBase, TextPosition } from './types.js';
3
+ import type { ComponentSpace, LayoutAwareComponent } from './chart-interface.js';
4
+ type TextComponentType = 'text' | 'title';
5
+ type ResolvedTextStyle = {
6
+ fontSize: number;
7
+ fontWeight: string;
8
+ fontFamily: string;
9
+ color: string;
10
+ lineHeight: number;
11
+ marginTop: number;
12
+ marginBottom: number;
13
+ };
14
+ export declare class Text implements LayoutAwareComponent<TextConfigBase> {
15
+ readonly type: TextComponentType;
16
+ readonly display: boolean;
17
+ readonly text: string;
18
+ readonly position: TextPosition;
19
+ readonly variant: string;
20
+ readonly align: TextAlign;
21
+ readonly exportHooks?: ExportHooks<TextConfigBase>;
22
+ protected readonly fontSize?: number;
23
+ protected readonly fontWeight?: string;
24
+ protected readonly fontFamily?: string;
25
+ protected readonly color?: string;
26
+ protected readonly lineHeight?: number;
27
+ protected readonly marginTop?: number;
28
+ protected readonly marginBottom?: number;
29
+ constructor(config: TextConfig, componentType?: TextComponentType);
30
+ getExportConfig(): TextConfigBase;
31
+ createExportComponent(override?: Partial<TextConfigBase>): LayoutAwareComponent<TextConfigBase>;
32
+ getRequiredSpace(theme?: ChartTheme): ComponentSpace;
33
+ render(svg: Selection<SVGSVGElement, undefined, null, undefined>, theme: ChartTheme, width: number, x?: number, y?: number): void;
34
+ protected resolveStyle(theme?: ChartTheme): ResolvedTextStyle;
35
+ private resolveStyleValue;
36
+ private getLines;
37
+ private resolveTextPosition;
38
+ private resolveClassName;
39
+ }
40
+ export {};
package/dist/text.js ADDED
@@ -0,0 +1,217 @@
1
+ import { mergeDeep, sanitizeForCSS } from './utils.js';
2
+ export class Text {
3
+ constructor(config, componentType = 'text') {
4
+ Object.defineProperty(this, "type", {
5
+ enumerable: true,
6
+ configurable: true,
7
+ writable: true,
8
+ value: void 0
9
+ });
10
+ Object.defineProperty(this, "display", {
11
+ enumerable: true,
12
+ configurable: true,
13
+ writable: true,
14
+ value: void 0
15
+ });
16
+ Object.defineProperty(this, "text", {
17
+ enumerable: true,
18
+ configurable: true,
19
+ writable: true,
20
+ value: void 0
21
+ });
22
+ Object.defineProperty(this, "position", {
23
+ enumerable: true,
24
+ configurable: true,
25
+ writable: true,
26
+ value: void 0
27
+ });
28
+ Object.defineProperty(this, "variant", {
29
+ enumerable: true,
30
+ configurable: true,
31
+ writable: true,
32
+ value: void 0
33
+ });
34
+ Object.defineProperty(this, "align", {
35
+ enumerable: true,
36
+ configurable: true,
37
+ writable: true,
38
+ value: void 0
39
+ });
40
+ Object.defineProperty(this, "exportHooks", {
41
+ enumerable: true,
42
+ configurable: true,
43
+ writable: true,
44
+ value: void 0
45
+ });
46
+ Object.defineProperty(this, "fontSize", {
47
+ enumerable: true,
48
+ configurable: true,
49
+ writable: true,
50
+ value: void 0
51
+ });
52
+ Object.defineProperty(this, "fontWeight", {
53
+ enumerable: true,
54
+ configurable: true,
55
+ writable: true,
56
+ value: void 0
57
+ });
58
+ Object.defineProperty(this, "fontFamily", {
59
+ enumerable: true,
60
+ configurable: true,
61
+ writable: true,
62
+ value: void 0
63
+ });
64
+ Object.defineProperty(this, "color", {
65
+ enumerable: true,
66
+ configurable: true,
67
+ writable: true,
68
+ value: void 0
69
+ });
70
+ Object.defineProperty(this, "lineHeight", {
71
+ enumerable: true,
72
+ configurable: true,
73
+ writable: true,
74
+ value: void 0
75
+ });
76
+ Object.defineProperty(this, "marginTop", {
77
+ enumerable: true,
78
+ configurable: true,
79
+ writable: true,
80
+ value: void 0
81
+ });
82
+ Object.defineProperty(this, "marginBottom", {
83
+ enumerable: true,
84
+ configurable: true,
85
+ writable: true,
86
+ value: void 0
87
+ });
88
+ this.type = componentType;
89
+ this.display = config.display ?? true;
90
+ this.text = config.text;
91
+ this.position = config.position ?? 'top';
92
+ this.variant = config.variant ?? 'caption';
93
+ this.align = config.align ?? 'center';
94
+ this.fontSize = config.fontSize;
95
+ this.fontWeight = config.fontWeight;
96
+ this.fontFamily = config.fontFamily;
97
+ this.color = config.color;
98
+ this.lineHeight = config.lineHeight;
99
+ this.marginTop = config.marginTop;
100
+ this.marginBottom = config.marginBottom;
101
+ this.exportHooks = config.exportHooks;
102
+ }
103
+ getExportConfig() {
104
+ return {
105
+ display: this.display,
106
+ text: this.text,
107
+ position: this.position,
108
+ variant: this.variant,
109
+ align: this.align,
110
+ fontSize: this.fontSize,
111
+ fontWeight: this.fontWeight,
112
+ fontFamily: this.fontFamily,
113
+ color: this.color,
114
+ lineHeight: this.lineHeight,
115
+ marginTop: this.marginTop,
116
+ marginBottom: this.marginBottom,
117
+ };
118
+ }
119
+ createExportComponent(override) {
120
+ const merged = mergeDeep(this.getExportConfig(), override);
121
+ return new Text({
122
+ ...merged,
123
+ exportHooks: this.exportHooks,
124
+ }, this.type);
125
+ }
126
+ getRequiredSpace(theme) {
127
+ if (!this.display) {
128
+ return {
129
+ width: 0,
130
+ height: 0,
131
+ position: this.position,
132
+ };
133
+ }
134
+ const style = this.resolveStyle(theme);
135
+ const lineCount = this.getLines().length;
136
+ const textHeight = style.fontSize +
137
+ Math.max(0, lineCount - 1) * style.fontSize * style.lineHeight;
138
+ return {
139
+ width: 0,
140
+ height: style.marginTop + textHeight + style.marginBottom,
141
+ position: this.position,
142
+ };
143
+ }
144
+ render(svg, theme, width, x = 0, y = 0) {
145
+ if (!this.display) {
146
+ return;
147
+ }
148
+ const style = this.resolveStyle(theme);
149
+ const { textX, textAnchor } = this.resolveTextPosition(theme, width);
150
+ const textGroup = svg
151
+ .append('g')
152
+ .attr('class', this.resolveClassName())
153
+ .attr('transform', `translate(${x}, ${y})`);
154
+ const text = textGroup
155
+ .append('text')
156
+ .attr('x', textX)
157
+ .attr('text-anchor', textAnchor)
158
+ .attr('font-size', `${style.fontSize}px`)
159
+ .attr('font-weight', style.fontWeight)
160
+ .attr('font-family', style.fontFamily)
161
+ .attr('fill', style.color);
162
+ this.getLines().forEach((line, index) => {
163
+ const tspan = text.append('tspan').attr('x', textX).text(line);
164
+ if (index === 0) {
165
+ tspan.attr('y', style.marginTop + style.fontSize);
166
+ return;
167
+ }
168
+ tspan.attr('dy', `${style.lineHeight}em`);
169
+ });
170
+ }
171
+ resolveStyle(theme) {
172
+ const variant = theme?.text.variants[this.variant] ?? {};
173
+ return {
174
+ fontSize: this.resolveStyleValue(this.fontSize, variant.fontSize, 12),
175
+ fontWeight: this.resolveStyleValue(this.fontWeight, variant.fontWeight, 'normal'),
176
+ fontFamily: this.fontFamily ??
177
+ variant.fontFamily ??
178
+ theme?.fontFamily ??
179
+ '',
180
+ color: this.resolveStyleValue(this.color, variant.color, '#1f2a36'),
181
+ lineHeight: this.resolveStyleValue(this.lineHeight, variant.lineHeight, 1.2),
182
+ marginTop: this.resolveStyleValue(this.marginTop, variant.marginTop, 0),
183
+ marginBottom: this.resolveStyleValue(this.marginBottom, variant.marginBottom, 0),
184
+ };
185
+ }
186
+ resolveStyleValue(configured, variant, fallback) {
187
+ return configured ?? variant ?? fallback;
188
+ }
189
+ getLines() {
190
+ return this.text.split('\n');
191
+ }
192
+ resolveTextPosition(theme, width) {
193
+ if (this.align === 'left') {
194
+ return {
195
+ textX: theme.margins.left,
196
+ textAnchor: 'start',
197
+ };
198
+ }
199
+ if (this.align === 'right') {
200
+ return {
201
+ textX: width - theme.margins.right,
202
+ textAnchor: 'end',
203
+ };
204
+ }
205
+ return {
206
+ textX: width / 2,
207
+ textAnchor: 'middle',
208
+ };
209
+ }
210
+ resolveClassName() {
211
+ const classes = ['text', `text--${sanitizeForCSS(this.variant)}`];
212
+ if (this.type === 'title') {
213
+ classes.unshift('title');
214
+ }
215
+ return classes.join(' ');
216
+ }
217
+ }
package/dist/theme.js CHANGED
@@ -40,6 +40,34 @@ export const defaultTheme = {
40
40
  itemSpacingX: 20,
41
41
  itemSpacingY: 8,
42
42
  },
43
+ text: {
44
+ variants: {
45
+ title: {
46
+ fontSize: 18,
47
+ fontWeight: 'bold',
48
+ color: '#1f2a36',
49
+ lineHeight: 1.2,
50
+ marginTop: 10,
51
+ marginBottom: 15,
52
+ },
53
+ subtitle: {
54
+ fontSize: 14,
55
+ fontWeight: 'normal',
56
+ color: '#4b5563',
57
+ lineHeight: 1.35,
58
+ marginTop: 0,
59
+ marginBottom: 12,
60
+ },
61
+ caption: {
62
+ fontSize: 12,
63
+ fontWeight: 'normal',
64
+ color: '#6b7280',
65
+ lineHeight: 1.3,
66
+ marginTop: 8,
67
+ marginBottom: 0,
68
+ },
69
+ },
70
+ },
43
71
  tooltip: {
44
72
  background: '#ffffff',
45
73
  border: '#dddddd',
@@ -128,10 +156,38 @@ export const newspaperTheme = {
128
156
  itemSpacingX: 20,
129
157
  itemSpacingY: 8,
130
158
  },
159
+ text: {
160
+ variants: {
161
+ title: {
162
+ fontSize: 18,
163
+ fontWeight: 'bold',
164
+ color: '#1a1a1a',
165
+ lineHeight: 1.2,
166
+ marginTop: 10,
167
+ marginBottom: 15,
168
+ },
169
+ subtitle: {
170
+ fontSize: 14,
171
+ fontWeight: 'normal',
172
+ color: '#4a4a4a',
173
+ lineHeight: 1.35,
174
+ marginTop: 0,
175
+ marginBottom: 12,
176
+ },
177
+ caption: {
178
+ fontSize: 12,
179
+ fontWeight: 'normal',
180
+ color: '#6b6b6b',
181
+ lineHeight: 1.3,
182
+ marginTop: 8,
183
+ marginBottom: 0,
184
+ },
185
+ },
186
+ },
131
187
  tooltip: {
132
- background: '#ffffff',
133
- border: '#2c2c2c',
134
- color: '#1a1a1a',
188
+ background: '#111111',
189
+ border: '#111111',
190
+ color: '#ffffff',
135
191
  fontFamily: 'Georgia, "Times New Roman", Times, serif',
136
192
  fontSize: 11,
137
193
  fontWeight: 'normal',
package/dist/title.d.ts CHANGED
@@ -1,23 +1,17 @@
1
1
  import { type Selection } from 'd3';
2
- import type { TitleConfig, ChartTheme, ExportHooks, TitleConfigBase } from './types.js';
3
- import type { LayoutAwareComponent, ComponentSpace } from './chart-interface.js';
2
+ import type { ChartTheme, ExportHooks, TitleConfig, TitleConfigBase } from './types.js';
3
+ import type { ComponentSpace, LayoutAwareComponent } from './chart-interface.js';
4
4
  export declare class Title implements LayoutAwareComponent<TitleConfigBase> {
5
5
  readonly type: "title";
6
6
  readonly display: boolean;
7
7
  readonly text: string;
8
+ readonly position: "top";
9
+ readonly variant = "title";
8
10
  readonly exportHooks?: ExportHooks<TitleConfigBase>;
9
- private readonly fontSize;
10
- private readonly fontWeight;
11
- private readonly fontFamily?;
12
- private readonly align;
13
- private readonly marginTop;
14
- private readonly marginBottom;
11
+ private readonly textComponent;
15
12
  constructor(config: TitleConfig);
16
13
  getExportConfig(): TitleConfigBase;
17
14
  createExportComponent(override?: Partial<TitleConfigBase>): LayoutAwareComponent<TitleConfigBase>;
18
- /**
19
- * Returns the space required by the title
20
- */
21
- getRequiredSpace(): ComponentSpace;
15
+ getRequiredSpace(theme?: ChartTheme): ComponentSpace;
22
16
  render(svg: Selection<SVGSVGElement, undefined, null, undefined>, theme: ChartTheme, width: number, x?: number, y?: number): void;
23
17
  }
package/dist/title.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { Text } from './text.js';
1
2
  import { mergeDeep } from './utils.js';
2
3
  export class Title {
3
4
  constructor(config) {
@@ -19,68 +20,53 @@ export class Title {
19
20
  writable: true,
20
21
  value: void 0
21
22
  });
22
- Object.defineProperty(this, "exportHooks", {
23
- enumerable: true,
24
- configurable: true,
25
- writable: true,
26
- value: void 0
27
- });
28
- Object.defineProperty(this, "fontSize", {
29
- enumerable: true,
30
- configurable: true,
31
- writable: true,
32
- value: void 0
33
- });
34
- Object.defineProperty(this, "fontWeight", {
35
- enumerable: true,
36
- configurable: true,
37
- writable: true,
38
- value: void 0
39
- });
40
- Object.defineProperty(this, "fontFamily", {
23
+ Object.defineProperty(this, "position", {
41
24
  enumerable: true,
42
25
  configurable: true,
43
26
  writable: true,
44
- value: void 0
27
+ value: 'top'
45
28
  });
46
- Object.defineProperty(this, "align", {
29
+ Object.defineProperty(this, "variant", {
47
30
  enumerable: true,
48
31
  configurable: true,
49
32
  writable: true,
50
- value: void 0
33
+ value: 'title'
51
34
  });
52
- Object.defineProperty(this, "marginTop", {
35
+ Object.defineProperty(this, "exportHooks", {
53
36
  enumerable: true,
54
37
  configurable: true,
55
38
  writable: true,
56
39
  value: void 0
57
40
  });
58
- Object.defineProperty(this, "marginBottom", {
41
+ Object.defineProperty(this, "textComponent", {
59
42
  enumerable: true,
60
43
  configurable: true,
61
44
  writable: true,
62
45
  value: void 0
63
46
  });
64
- this.display = config.display ?? true;
65
- this.text = config.text;
66
- this.fontSize = config.fontSize ?? 18;
67
- this.fontWeight = config.fontWeight ?? 'bold';
68
- this.fontFamily = config.fontFamily;
69
- this.align = config.align ?? 'center';
70
- this.marginTop = config.marginTop ?? 10;
71
- this.marginBottom = config.marginBottom ?? 15;
47
+ this.textComponent = new Text({
48
+ ...config,
49
+ position: 'top',
50
+ variant: 'title',
51
+ exportHooks: config.exportHooks,
52
+ }, 'title');
53
+ this.display = this.textComponent.display;
54
+ this.text = this.textComponent.text;
72
55
  this.exportHooks = config.exportHooks;
73
56
  }
74
57
  getExportConfig() {
58
+ const config = this.textComponent.getExportConfig();
75
59
  return {
76
- display: this.display,
77
- text: this.text,
78
- fontSize: this.fontSize,
79
- fontWeight: this.fontWeight,
80
- fontFamily: this.fontFamily,
81
- align: this.align,
82
- marginTop: this.marginTop,
83
- marginBottom: this.marginBottom,
60
+ display: config.display,
61
+ text: config.text,
62
+ fontSize: config.fontSize,
63
+ fontWeight: config.fontWeight,
64
+ fontFamily: config.fontFamily,
65
+ align: config.align,
66
+ color: config.color,
67
+ lineHeight: config.lineHeight,
68
+ marginTop: config.marginTop,
69
+ marginBottom: config.marginBottom,
84
70
  };
85
71
  }
86
72
  createExportComponent(override) {
@@ -90,49 +76,10 @@ export class Title {
90
76
  exportHooks: this.exportHooks,
91
77
  });
92
78
  }
93
- /**
94
- * Returns the space required by the title
95
- */
96
- getRequiredSpace() {
97
- if (!this.display) {
98
- return {
99
- width: 0,
100
- height: 0,
101
- position: 'top',
102
- };
103
- }
104
- return {
105
- width: 0, // Title spans full width
106
- height: this.marginTop + this.fontSize + this.marginBottom,
107
- position: 'top',
108
- };
79
+ getRequiredSpace(theme) {
80
+ return this.textComponent.getRequiredSpace(theme);
109
81
  }
110
82
  render(svg, theme, width, x = 0, y = 0) {
111
- if (!this.display) {
112
- return;
113
- }
114
- const titleGroup = svg
115
- .append('g')
116
- .attr('class', 'title')
117
- .attr('transform', `translate(${x}, ${y})`);
118
- let textX = width / 2;
119
- let textAnchor = 'middle';
120
- if (this.align === 'left') {
121
- textX = theme.margins.left;
122
- textAnchor = 'start';
123
- }
124
- else if (this.align === 'right') {
125
- textX = width - theme.margins.right;
126
- textAnchor = 'end';
127
- }
128
- titleGroup
129
- .append('text')
130
- .attr('x', textX)
131
- .attr('y', this.marginTop + this.fontSize)
132
- .attr('text-anchor', textAnchor)
133
- .attr('font-size', `${this.fontSize}px`)
134
- .attr('font-weight', this.fontWeight)
135
- .attr('font-family', this.fontFamily || theme.fontFamily)
136
- .text(this.text);
83
+ this.textComponent.render(svg, theme, width, x, y);
137
84
  }
138
85
  }
package/dist/tooltip.d.ts CHANGED
@@ -49,6 +49,9 @@ export declare class Tooltip implements ChartComponent<TooltipConfigBase> {
49
49
  private hideTooltipElement;
50
50
  private setTooltipMarkup;
51
51
  private appendTooltipConnector;
52
+ private appendTooltipArrow;
53
+ private appendTooltipArrowTriangle;
54
+ private resolveTooltipArrowPosition;
52
55
  private resolveBarTooltipAnchor;
53
56
  private resolvePointTooltipAnchor;
54
57
  private getSplitTooltip;
@@ -64,9 +67,8 @@ export declare class Tooltip implements ChartComponent<TooltipConfigBase> {
64
67
  private getAnchoredTooltipPosition;
65
68
  private resolveTooltipConnectorLayout;
66
69
  private resolveTooltipBoxArrowPosition;
67
- private resolveTooltipArrowBaseMaskPath;
68
70
  private resolveTooltipConnectorPath;
69
- private resolveTooltipBoxArrow;
71
+ private resolveTooltipArrowTip;
70
72
  private hasFiniteNumbers;
71
73
  private resolveSplitTooltipCollisions;
72
74
  private getOppositeSideArrowEdge;