@internetstiftelsen/charts 0.9.0 → 0.9.2
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/LICENSE +21 -0
- package/README.md +44 -0
- package/{area.d.ts → dist/area.d.ts} +1 -1
- package/{bar.d.ts → dist/bar.d.ts} +3 -4
- package/{bar.js → dist/bar.js} +3 -11
- package/{base-chart.d.ts → dist/base-chart.d.ts} +30 -18
- package/{base-chart.js → dist/base-chart.js} +170 -50
- package/dist/chart-interface.d.ts +19 -0
- package/{donut-center-content.d.ts → dist/donut-center-content.d.ts} +1 -1
- package/{donut-chart.d.ts → dist/donut-chart.d.ts} +19 -4
- package/{donut-chart.js → dist/donut-chart.js} +140 -2
- package/{gauge-chart.d.ts → dist/gauge-chart.d.ts} +2 -2
- package/{gauge-chart.js → dist/gauge-chart.js} +2 -0
- package/{grid.d.ts → dist/grid.d.ts} +4 -4
- package/{grid.js → dist/grid.js} +15 -9
- package/{layout-manager.d.ts → dist/layout-manager.d.ts} +5 -5
- package/{legend.d.ts → dist/legend.d.ts} +3 -1
- package/{legend.js → dist/legend.js} +32 -0
- package/{line.d.ts → dist/line.d.ts} +1 -1
- package/{pie-chart.d.ts → dist/pie-chart.d.ts} +4 -11
- package/{pie-chart.js → dist/pie-chart.js} +23 -21
- package/{radial-chart-base.js → dist/radial-chart-base.js} +3 -1
- package/{theme.d.ts → dist/theme.d.ts} +2 -0
- package/{theme.js → dist/theme.js} +24 -29
- package/{title.d.ts → dist/title.d.ts} +1 -1
- package/{tooltip.d.ts → dist/tooltip.d.ts} +1 -1
- package/{tooltip.js → dist/tooltip.js} +239 -74
- package/{types.d.ts → dist/types.d.ts} +29 -12
- package/{utils.d.ts → dist/utils.d.ts} +0 -2
- package/{utils.js → dist/utils.js} +0 -5
- package/{word-cloud-chart.d.ts → dist/word-cloud-chart.d.ts} +1 -1
- package/{word-cloud-chart.js → dist/word-cloud-chart.js} +2 -0
- package/{x-axis.d.ts → dist/x-axis.d.ts} +2 -1
- package/{x-axis.js → dist/x-axis.js} +18 -14
- package/{xy-chart.d.ts → dist/xy-chart.d.ts} +8 -5
- package/{xy-chart.js → dist/xy-chart.js} +32 -6
- package/{y-axis.d.ts → dist/y-axis.d.ts} +1 -1
- package/{y-axis.js → dist/y-axis.js} +4 -4
- package/package.json +38 -36
- package/chart-interface.d.ts +0 -13
- /package/{area.js → dist/area.js} +0 -0
- /package/{chart-interface.js → dist/chart-interface.js} +0 -0
- /package/{donut-center-content.js → dist/donut-center-content.js} +0 -0
- /package/{export-image.d.ts → dist/export-image.d.ts} +0 -0
- /package/{export-image.js → dist/export-image.js} +0 -0
- /package/{export-pdf.d.ts → dist/export-pdf.d.ts} +0 -0
- /package/{export-pdf.js → dist/export-pdf.js} +0 -0
- /package/{export-tabular.d.ts → dist/export-tabular.d.ts} +0 -0
- /package/{export-tabular.js → dist/export-tabular.js} +0 -0
- /package/{export-xlsx.d.ts → dist/export-xlsx.d.ts} +0 -0
- /package/{export-xlsx.js → dist/export-xlsx.js} +0 -0
- /package/{grouped-data.d.ts → dist/grouped-data.d.ts} +0 -0
- /package/{grouped-data.js → dist/grouped-data.js} +0 -0
- /package/{grouped-tabular.d.ts → dist/grouped-tabular.d.ts} +0 -0
- /package/{grouped-tabular.js → dist/grouped-tabular.js} +0 -0
- /package/{layout-manager.js → dist/layout-manager.js} +0 -0
- /package/{line.js → dist/line.js} +0 -0
- /package/{radial-chart-base.d.ts → dist/radial-chart-base.d.ts} +0 -0
- /package/{scale-utils.d.ts → dist/scale-utils.d.ts} +0 -0
- /package/{scale-utils.js → dist/scale-utils.js} +0 -0
- /package/{title.js → dist/title.js} +0 -0
- /package/{types.js → dist/types.js} +0 -0
- /package/{validation.d.ts → dist/validation.d.ts} +0 -0
- /package/{validation.js → dist/validation.js} +0 -0
|
@@ -8,9 +8,9 @@ export const DEFAULT_COLOR_PALETTE = [
|
|
|
8
8
|
'#ff9fb4', // ruby-light
|
|
9
9
|
'#1f2a36', // cyberspace
|
|
10
10
|
];
|
|
11
|
+
export const DEFAULT_CHART_WIDTH = 928;
|
|
12
|
+
export const DEFAULT_CHART_HEIGHT = 600;
|
|
11
13
|
export const defaultTheme = {
|
|
12
|
-
width: 928,
|
|
13
|
-
height: 600,
|
|
14
14
|
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"',
|
|
15
15
|
margins: {
|
|
16
16
|
top: 20,
|
|
@@ -25,7 +25,7 @@ export const defaultTheme = {
|
|
|
25
25
|
colorPalette: [...DEFAULT_COLOR_PALETTE],
|
|
26
26
|
axis: {
|
|
27
27
|
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"',
|
|
28
|
-
fontSize:
|
|
28
|
+
fontSize: 14,
|
|
29
29
|
fontWeight: 'normal',
|
|
30
30
|
groupLabel: {
|
|
31
31
|
fontWeight: '700',
|
|
@@ -82,8 +82,6 @@ export const defaultTheme = {
|
|
|
82
82
|
},
|
|
83
83
|
};
|
|
84
84
|
export const newspaperTheme = {
|
|
85
|
-
width: 928,
|
|
86
|
-
height: 600,
|
|
87
85
|
fontFamily: 'Georgia, "Times New Roman", Times, serif',
|
|
88
86
|
margins: {
|
|
89
87
|
top: 20,
|
|
@@ -107,7 +105,7 @@ export const newspaperTheme = {
|
|
|
107
105
|
],
|
|
108
106
|
axis: {
|
|
109
107
|
fontFamily: 'Georgia, "Times New Roman", Times, serif',
|
|
110
|
-
fontSize:
|
|
108
|
+
fontSize: 13,
|
|
111
109
|
fontWeight: '600',
|
|
112
110
|
groupLabel: {
|
|
113
111
|
fontWeight: '700',
|
|
@@ -166,29 +164,26 @@ export const newspaperTheme = {
|
|
|
166
164
|
};
|
|
167
165
|
export const defaultResponsiveConfig = {
|
|
168
166
|
breakpoints: {
|
|
169
|
-
sm:
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
},
|
|
190
|
-
};
|
|
191
|
-
}
|
|
167
|
+
sm: {
|
|
168
|
+
maxWidth: 479,
|
|
169
|
+
theme: {
|
|
170
|
+
axis: { fontSize: 11 },
|
|
171
|
+
legend: { fontSize: 11 },
|
|
172
|
+
valueLabel: { fontSize: 10 },
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
md: {
|
|
176
|
+
minWidth: 480,
|
|
177
|
+
maxWidth: 767,
|
|
178
|
+
theme: {
|
|
179
|
+
axis: { fontSize: 12 },
|
|
180
|
+
legend: { fontSize: 12 },
|
|
181
|
+
valueLabel: { fontSize: 11 },
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
lg: {
|
|
185
|
+
minWidth: 768,
|
|
186
|
+
},
|
|
192
187
|
},
|
|
193
188
|
};
|
|
194
189
|
export const themes = {
|
|
@@ -14,7 +14,7 @@ export declare class Title implements LayoutAwareComponent<TitleConfigBase> {
|
|
|
14
14
|
private readonly marginBottom;
|
|
15
15
|
constructor(config: TitleConfig);
|
|
16
16
|
getExportConfig(): TitleConfigBase;
|
|
17
|
-
createExportComponent(override?: Partial<TitleConfigBase>): LayoutAwareComponent
|
|
17
|
+
createExportComponent(override?: Partial<TitleConfigBase>): LayoutAwareComponent<TitleConfigBase>;
|
|
18
18
|
/**
|
|
19
19
|
* Returns the space required by the title
|
|
20
20
|
*/
|
|
@@ -19,7 +19,7 @@ export declare class Tooltip implements ChartComponent<TooltipConfigBase> {
|
|
|
19
19
|
private tooltipDiv;
|
|
20
20
|
constructor(config?: TooltipConfig);
|
|
21
21
|
getExportConfig(): TooltipConfigBase;
|
|
22
|
-
createExportComponent(override?: Partial<TooltipConfigBase>): ChartComponent
|
|
22
|
+
createExportComponent(override?: Partial<TooltipConfigBase>): ChartComponent<TooltipConfigBase>;
|
|
23
23
|
initialize(theme: ChartTheme): void;
|
|
24
24
|
attachToArea(svg: Selection<SVGSVGElement, undefined, null, undefined>, data: DataItem[], series: (Line | Bar | Area)[], xKey: string, x: D3Scale, y: D3Scale, theme: ChartTheme, plotArea: PlotAreaBounds, parseValue: (value: unknown) => number, isHorizontal?: boolean, categoryScaleType?: ScaleType, resolveSeriesValue?: (series: Line | Bar | Area, dataPoint: DataItem, index: number) => number): void;
|
|
25
25
|
cleanup(): void;
|
|
@@ -93,8 +93,23 @@ export class Tooltip {
|
|
|
93
93
|
}
|
|
94
94
|
return parseValue(rawValue);
|
|
95
95
|
}) {
|
|
96
|
-
if (!this.tooltipDiv)
|
|
96
|
+
if (!this.tooltipDiv) {
|
|
97
97
|
return;
|
|
98
|
+
}
|
|
99
|
+
const normalizeFormatterValue = (value) => {
|
|
100
|
+
if (value === null ||
|
|
101
|
+
value === undefined ||
|
|
102
|
+
typeof value === 'string' ||
|
|
103
|
+
typeof value === 'number' ||
|
|
104
|
+
typeof value === 'boolean' ||
|
|
105
|
+
value instanceof Date) {
|
|
106
|
+
return value;
|
|
107
|
+
}
|
|
108
|
+
return String(value);
|
|
109
|
+
};
|
|
110
|
+
if (data.length === 0) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
98
113
|
const tooltip = this.tooltipDiv;
|
|
99
114
|
const formatter = this.formatter;
|
|
100
115
|
const labelFormatter = this.labelFormatter;
|
|
@@ -122,6 +137,72 @@ export class Tooltip {
|
|
|
122
137
|
const scaled = y(getCategoryScaleValue(yValue, categoryScaleType));
|
|
123
138
|
return (scaled || 0) + (y.bandwidth ? y.bandwidth() / 2 : 0);
|
|
124
139
|
};
|
|
140
|
+
const stripHtml = (content) => {
|
|
141
|
+
return content
|
|
142
|
+
.replace(/<br\s*\/?>/gi, '. ')
|
|
143
|
+
.replace(/<[^>]+>/g, ' ')
|
|
144
|
+
.replace(/\s+/g, ' ')
|
|
145
|
+
.trim();
|
|
146
|
+
};
|
|
147
|
+
const buildTooltipContent = (dataPoint) => {
|
|
148
|
+
if (customFormatter) {
|
|
149
|
+
return customFormatter(dataPoint, series);
|
|
150
|
+
}
|
|
151
|
+
const labelValue = dataPoint[xKey];
|
|
152
|
+
const label = labelFormatter
|
|
153
|
+
? labelFormatter(String(labelValue), dataPoint)
|
|
154
|
+
: String(labelValue);
|
|
155
|
+
let content = `<strong>${label}</strong><br/>`;
|
|
156
|
+
series.forEach((s) => {
|
|
157
|
+
const value = dataPoint[s.dataKey];
|
|
158
|
+
if (formatter) {
|
|
159
|
+
content +=
|
|
160
|
+
formatter(s.dataKey, normalizeFormatterValue(value), dataPoint) + '<br/>';
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
content += `${s.dataKey}: ${value}<br/>`;
|
|
164
|
+
});
|
|
165
|
+
return content;
|
|
166
|
+
};
|
|
167
|
+
const buildAccessibleLabel = (dataPoint) => {
|
|
168
|
+
if (customFormatter) {
|
|
169
|
+
return stripHtml(customFormatter(dataPoint, series));
|
|
170
|
+
}
|
|
171
|
+
const labelValue = dataPoint[xKey];
|
|
172
|
+
const label = labelFormatter
|
|
173
|
+
? labelFormatter(String(labelValue), dataPoint)
|
|
174
|
+
: String(labelValue);
|
|
175
|
+
const parts = [label];
|
|
176
|
+
series.forEach((s) => {
|
|
177
|
+
const value = dataPoint[s.dataKey];
|
|
178
|
+
if (formatter) {
|
|
179
|
+
parts.push(stripHtml(formatter(s.dataKey, normalizeFormatterValue(value), dataPoint)));
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
parts.push(`${s.dataKey}: ${value === null || value === undefined
|
|
183
|
+
? 'no data'
|
|
184
|
+
: value}`);
|
|
185
|
+
});
|
|
186
|
+
return parts.join('. ');
|
|
187
|
+
};
|
|
188
|
+
const isTooltipFocusTarget = (element) => {
|
|
189
|
+
return (element instanceof SVGElement &&
|
|
190
|
+
element.classList.contains('tooltip-focus-target'));
|
|
191
|
+
};
|
|
192
|
+
const dataPointPositions = data.map((dataPoint) => isHorizontal ? getYPosition(dataPoint) : getXPosition(dataPoint));
|
|
193
|
+
const getClosestIndexFromPointer = (mouseX, mouseY) => {
|
|
194
|
+
const pointerPosition = isHorizontal ? mouseY : mouseX;
|
|
195
|
+
let closestIndex = 0;
|
|
196
|
+
let minDistance = Math.abs(pointerPosition - dataPointPositions[0]);
|
|
197
|
+
for (let i = 1; i < dataPointPositions.length; i++) {
|
|
198
|
+
const distance = Math.abs(pointerPosition - dataPointPositions[i]);
|
|
199
|
+
if (distance < minDistance) {
|
|
200
|
+
minDistance = distance;
|
|
201
|
+
closestIndex = i;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return closestIndex;
|
|
205
|
+
};
|
|
125
206
|
// Create overlay rect for mouse tracking using plot area bounds
|
|
126
207
|
const overlay = svg
|
|
127
208
|
.append('rect')
|
|
@@ -130,6 +211,7 @@ export class Tooltip {
|
|
|
130
211
|
.attr('y', plotArea.top)
|
|
131
212
|
.attr('width', plotArea.width)
|
|
132
213
|
.attr('height', plotArea.height)
|
|
214
|
+
.attr('aria-hidden', 'true')
|
|
133
215
|
.style('fill', 'none')
|
|
134
216
|
.style('pointer-events', 'all');
|
|
135
217
|
const lineSeries = series.filter((s) => s.type === 'line' || s.type === 'area');
|
|
@@ -144,39 +226,23 @@ export class Tooltip {
|
|
|
144
226
|
.attr('fill', theme.line.point.color || seriesColor)
|
|
145
227
|
.attr('stroke', theme.line.point.strokeColor || seriesColor)
|
|
146
228
|
.attr('stroke-width', theme.line.point.strokeWidth)
|
|
229
|
+
.attr('aria-hidden', 'true')
|
|
147
230
|
.style('opacity', 0)
|
|
148
231
|
.style('pointer-events', 'none');
|
|
149
232
|
});
|
|
150
|
-
|
|
151
|
-
.
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
let dataPointPosition;
|
|
155
|
-
if (isHorizontal) {
|
|
156
|
-
const yPositions = data.map((d) => getYPosition(d));
|
|
157
|
-
let minDistance = Math.abs(mouseY - yPositions[0]);
|
|
158
|
-
for (let i = 1; i < yPositions.length; i++) {
|
|
159
|
-
const distance = Math.abs(mouseY - yPositions[i]);
|
|
160
|
-
if (distance < minDistance) {
|
|
161
|
-
minDistance = distance;
|
|
162
|
-
closestIndex = i;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
dataPointPosition = yPositions[closestIndex];
|
|
166
|
-
}
|
|
167
|
-
else {
|
|
168
|
-
const xPositions = data.map((d) => getXPosition(d));
|
|
169
|
-
let minDistance = Math.abs(mouseX - xPositions[0]);
|
|
170
|
-
for (let i = 1; i < xPositions.length; i++) {
|
|
171
|
-
const distance = Math.abs(mouseX - xPositions[i]);
|
|
172
|
-
if (distance < minDistance) {
|
|
173
|
-
minDistance = distance;
|
|
174
|
-
closestIndex = i;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
dataPointPosition = xPositions[closestIndex];
|
|
233
|
+
const clearVisualState = () => {
|
|
234
|
+
focusCircles.forEach((circle) => circle.style('opacity', 0));
|
|
235
|
+
if (!hasBarSeries) {
|
|
236
|
+
return;
|
|
178
237
|
}
|
|
238
|
+
barSeries.forEach((s) => {
|
|
239
|
+
const sanitizedKey = sanitizeForCSS(s.dataKey);
|
|
240
|
+
svg.selectAll(`.bar-${sanitizedKey}`).style('opacity', 1);
|
|
241
|
+
});
|
|
242
|
+
};
|
|
243
|
+
const showTooltipAtIndex = (closestIndex) => {
|
|
179
244
|
const dataPoint = data[closestIndex];
|
|
245
|
+
const dataPointPosition = dataPointPositions[closestIndex];
|
|
180
246
|
lineSeries.forEach((s, i) => {
|
|
181
247
|
const value = resolveSeriesValue(s, dataPoint, closestIndex);
|
|
182
248
|
if (!Number.isFinite(value)) {
|
|
@@ -185,16 +251,15 @@ export class Tooltip {
|
|
|
185
251
|
}
|
|
186
252
|
if (isHorizontal) {
|
|
187
253
|
focusCircles[i]
|
|
188
|
-
.attr('cx', x(value))
|
|
254
|
+
.attr('cx', x(value) ?? 0)
|
|
189
255
|
.attr('cy', dataPointPosition)
|
|
190
256
|
.style('opacity', 1);
|
|
257
|
+
return;
|
|
191
258
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
.style('opacity', 1);
|
|
197
|
-
}
|
|
259
|
+
focusCircles[i]
|
|
260
|
+
.attr('cx', dataPointPosition)
|
|
261
|
+
.attr('cy', y(value) ?? 0)
|
|
262
|
+
.style('opacity', 1);
|
|
198
263
|
});
|
|
199
264
|
if (hasBarSeries) {
|
|
200
265
|
barSeries.forEach((s) => {
|
|
@@ -202,29 +267,9 @@ export class Tooltip {
|
|
|
202
267
|
svg.selectAll(`.bar-${sanitizedKey}`).style('opacity', (_, i) => (i === closestIndex ? 1 : 0.5));
|
|
203
268
|
});
|
|
204
269
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
}
|
|
209
|
-
else {
|
|
210
|
-
const labelValue = dataPoint[xKey];
|
|
211
|
-
const label = labelFormatter
|
|
212
|
-
? labelFormatter(String(labelValue), dataPoint)
|
|
213
|
-
: String(labelValue);
|
|
214
|
-
content = `<strong>${label}</strong><br/>`;
|
|
215
|
-
series.forEach((s) => {
|
|
216
|
-
const value = dataPoint[s.dataKey];
|
|
217
|
-
if (formatter) {
|
|
218
|
-
content +=
|
|
219
|
-
formatter(s.dataKey, value, dataPoint) +
|
|
220
|
-
'<br/>';
|
|
221
|
-
}
|
|
222
|
-
else {
|
|
223
|
-
content += `${s.dataKey}: ${value}<br/>`;
|
|
224
|
-
}
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
tooltip.style('visibility', 'visible').html(content);
|
|
270
|
+
tooltip
|
|
271
|
+
.style('visibility', 'visible')
|
|
272
|
+
.html(buildTooltipContent(dataPoint));
|
|
228
273
|
const tooltipNode = tooltip.node();
|
|
229
274
|
const tooltipRect = tooltipNode.getBoundingClientRect();
|
|
230
275
|
const tooltipWidth = tooltipRect.width;
|
|
@@ -257,10 +302,7 @@ export class Tooltip {
|
|
|
257
302
|
const maxY = y(minValue);
|
|
258
303
|
const midY = (minY + maxY) / 2;
|
|
259
304
|
tooltipX =
|
|
260
|
-
svgRect.left +
|
|
261
|
-
window.scrollX +
|
|
262
|
-
dataPointPosition +
|
|
263
|
-
offsetX;
|
|
305
|
+
svgRect.left + window.scrollX + dataPointPosition + offsetX;
|
|
264
306
|
tooltipY =
|
|
265
307
|
svgRect.top + window.scrollY + midY - tooltipHeight / 2;
|
|
266
308
|
}
|
|
@@ -285,23 +327,146 @@ export class Tooltip {
|
|
|
285
327
|
}
|
|
286
328
|
}
|
|
287
329
|
tooltipX = Math.max(10, tooltipX);
|
|
288
|
-
tooltipY = Math.max(10, Math.min(tooltipY, window.innerHeight +
|
|
289
|
-
window.scrollY -
|
|
290
|
-
tooltipHeight -
|
|
291
|
-
10));
|
|
330
|
+
tooltipY = Math.max(10, Math.min(tooltipY, window.innerHeight + window.scrollY - tooltipHeight - 10));
|
|
292
331
|
tooltip
|
|
293
332
|
.style('left', `${tooltipX}px`)
|
|
294
333
|
.style('top', `${tooltipY}px`);
|
|
334
|
+
};
|
|
335
|
+
const hideTooltip = () => {
|
|
336
|
+
tooltip.style('visibility', 'hidden');
|
|
337
|
+
clearVisualState();
|
|
338
|
+
};
|
|
339
|
+
const getFocusTargetBounds = (index) => {
|
|
340
|
+
if (isHorizontal) {
|
|
341
|
+
if (y.bandwidth) {
|
|
342
|
+
const targetHeight = y.bandwidth();
|
|
343
|
+
return {
|
|
344
|
+
x: plotArea.left,
|
|
345
|
+
y: dataPointPositions[index] - targetHeight / 2,
|
|
346
|
+
width: plotArea.width,
|
|
347
|
+
height: targetHeight,
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
const top = index === 0
|
|
351
|
+
? plotArea.top
|
|
352
|
+
: (dataPointPositions[index - 1] +
|
|
353
|
+
dataPointPositions[index]) /
|
|
354
|
+
2;
|
|
355
|
+
const bottom = index === dataPointPositions.length - 1
|
|
356
|
+
? plotArea.bottom
|
|
357
|
+
: (dataPointPositions[index] +
|
|
358
|
+
dataPointPositions[index + 1]) /
|
|
359
|
+
2;
|
|
360
|
+
return {
|
|
361
|
+
x: plotArea.left,
|
|
362
|
+
y: top,
|
|
363
|
+
width: plotArea.width,
|
|
364
|
+
height: bottom - top,
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
if (x.bandwidth) {
|
|
368
|
+
const targetWidth = x.bandwidth();
|
|
369
|
+
return {
|
|
370
|
+
x: dataPointPositions[index] - targetWidth / 2,
|
|
371
|
+
y: plotArea.top,
|
|
372
|
+
width: targetWidth,
|
|
373
|
+
height: plotArea.height,
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
const left = index === 0
|
|
377
|
+
? plotArea.left
|
|
378
|
+
: (dataPointPositions[index - 1] +
|
|
379
|
+
dataPointPositions[index]) /
|
|
380
|
+
2;
|
|
381
|
+
const right = index === dataPointPositions.length - 1
|
|
382
|
+
? plotArea.right
|
|
383
|
+
: (dataPointPositions[index] +
|
|
384
|
+
dataPointPositions[index + 1]) /
|
|
385
|
+
2;
|
|
386
|
+
return {
|
|
387
|
+
x: left,
|
|
388
|
+
y: plotArea.top,
|
|
389
|
+
width: right - left,
|
|
390
|
+
height: plotArea.height,
|
|
391
|
+
};
|
|
392
|
+
};
|
|
393
|
+
overlay
|
|
394
|
+
.on('mousemove', (event) => {
|
|
395
|
+
const [mouseX, mouseY] = pointer(event, svg.node());
|
|
396
|
+
showTooltipAtIndex(getClosestIndexFromPointer(mouseX, mouseY));
|
|
295
397
|
})
|
|
296
398
|
.on('mouseout', () => {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
399
|
+
if (isTooltipFocusTarget(document.activeElement)) {
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
hideTooltip();
|
|
403
|
+
});
|
|
404
|
+
const focusTargets = svg
|
|
405
|
+
.append('g')
|
|
406
|
+
.attr('class', 'tooltip-focus-targets')
|
|
407
|
+
.selectAll('rect')
|
|
408
|
+
.data(data)
|
|
409
|
+
.join('rect')
|
|
410
|
+
.attr('class', 'tooltip-focus-target')
|
|
411
|
+
.attr('data-index', (_, i) => i)
|
|
412
|
+
.attr('x', (_, i) => getFocusTargetBounds(i).x)
|
|
413
|
+
.attr('y', (_, i) => getFocusTargetBounds(i).y)
|
|
414
|
+
.attr('width', (_, i) => getFocusTargetBounds(i).width)
|
|
415
|
+
.attr('height', (_, i) => getFocusTargetBounds(i).height)
|
|
416
|
+
.attr('tabindex', 0)
|
|
417
|
+
.attr('fill', 'transparent')
|
|
418
|
+
.attr('stroke', 'none')
|
|
419
|
+
.attr('stroke-width', 0)
|
|
420
|
+
.attr('aria-label', (dataPoint) => buildAccessibleLabel(dataPoint))
|
|
421
|
+
.style('pointer-events', 'none');
|
|
422
|
+
const focusTargetNodes = focusTargets.nodes();
|
|
423
|
+
focusTargets
|
|
424
|
+
.on('focus', function () {
|
|
425
|
+
const currentIndex = focusTargetNodes.indexOf(this);
|
|
426
|
+
if (currentIndex === -1) {
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
select(this).attr('stroke', '#111827').attr('stroke-width', 2);
|
|
430
|
+
showTooltipAtIndex(currentIndex);
|
|
431
|
+
})
|
|
432
|
+
.on('blur', function (event) {
|
|
433
|
+
select(this).attr('stroke', 'none').attr('stroke-width', 0);
|
|
434
|
+
if (isTooltipFocusTarget(event.relatedTarget)) {
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
hideTooltip();
|
|
438
|
+
})
|
|
439
|
+
.on('keydown', function (event) {
|
|
440
|
+
const currentIndex = focusTargetNodes.indexOf(this);
|
|
441
|
+
if (currentIndex === -1) {
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
let nextIndex = null;
|
|
445
|
+
switch (event.key) {
|
|
446
|
+
case 'ArrowRight':
|
|
447
|
+
case 'ArrowDown':
|
|
448
|
+
nextIndex = currentIndex + 1;
|
|
449
|
+
break;
|
|
450
|
+
case 'ArrowLeft':
|
|
451
|
+
case 'ArrowUp':
|
|
452
|
+
nextIndex = currentIndex - 1;
|
|
453
|
+
break;
|
|
454
|
+
case 'Home':
|
|
455
|
+
nextIndex = 0;
|
|
456
|
+
break;
|
|
457
|
+
case 'End':
|
|
458
|
+
nextIndex = focusTargetNodes.length - 1;
|
|
459
|
+
break;
|
|
460
|
+
default:
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
if (nextIndex === null ||
|
|
464
|
+
nextIndex < 0 ||
|
|
465
|
+
nextIndex >= focusTargetNodes.length) {
|
|
466
|
+
return;
|
|
304
467
|
}
|
|
468
|
+
event.preventDefault();
|
|
469
|
+
focusTargetNodes[nextIndex].focus();
|
|
305
470
|
});
|
|
306
471
|
}
|
|
307
472
|
cleanup() {
|
|
@@ -2,7 +2,7 @@ export type DeepPartial<T> = {
|
|
|
2
2
|
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
|
|
3
3
|
};
|
|
4
4
|
export type DataValue = string | number | boolean | Date | null | undefined;
|
|
5
|
-
export type DataItem = Record<string,
|
|
5
|
+
export type DataItem = Record<string, unknown>;
|
|
6
6
|
export type GroupedDataGroup = {
|
|
7
7
|
group: string;
|
|
8
8
|
data: DataItem[];
|
|
@@ -37,8 +37,6 @@ export type ExportHooks<TConfig = Record<string, unknown>> = {
|
|
|
37
37
|
};
|
|
38
38
|
export type ColorPalette = string[];
|
|
39
39
|
export type ChartTheme = {
|
|
40
|
-
width: number;
|
|
41
|
-
height: number;
|
|
42
40
|
fontFamily: string;
|
|
43
41
|
margins: {
|
|
44
42
|
top: number;
|
|
@@ -53,11 +51,11 @@ export type ChartTheme = {
|
|
|
53
51
|
colorPalette: ColorPalette;
|
|
54
52
|
axis: {
|
|
55
53
|
fontFamily: string;
|
|
56
|
-
fontSize:
|
|
54
|
+
fontSize: number;
|
|
57
55
|
fontWeight?: string;
|
|
58
56
|
groupLabel?: {
|
|
59
57
|
fontFamily?: string;
|
|
60
|
-
fontSize?:
|
|
58
|
+
fontSize?: number;
|
|
61
59
|
fontWeight?: string;
|
|
62
60
|
color?: string;
|
|
63
61
|
};
|
|
@@ -115,10 +113,22 @@ export type ChartTheme = {
|
|
|
115
113
|
};
|
|
116
114
|
};
|
|
117
115
|
};
|
|
118
|
-
export type
|
|
116
|
+
export type ResolvedChartTheme = ChartTheme & {
|
|
117
|
+
width: number;
|
|
118
|
+
height: number;
|
|
119
|
+
};
|
|
120
|
+
export type ResponsiveBreakpointConfig = {
|
|
121
|
+
minWidth?: number;
|
|
122
|
+
maxWidth?: number;
|
|
123
|
+
theme?: DeepPartial<ChartTheme>;
|
|
124
|
+
components?: ResponsiveComponentOverride[];
|
|
125
|
+
};
|
|
126
|
+
export type ResponsiveBreakpointDefinition = number | ResponsiveBreakpointConfig;
|
|
127
|
+
export type ResponsiveBreakpoints = Record<string, ResponsiveBreakpointDefinition>;
|
|
119
128
|
export type ResponsiveRenderContext = {
|
|
120
129
|
width: number;
|
|
121
130
|
height: number;
|
|
131
|
+
activeBreakpoints: string[];
|
|
122
132
|
breakpoint: string | null;
|
|
123
133
|
};
|
|
124
134
|
export type ResponsiveComponentSnapshot = {
|
|
@@ -179,7 +189,6 @@ export type BarConfigBase = {
|
|
|
179
189
|
dataKey: string;
|
|
180
190
|
fill?: string;
|
|
181
191
|
colorAdapter?: (data: DataItem, index: number) => string;
|
|
182
|
-
orientation?: 'vertical' | 'horizontal';
|
|
183
192
|
maxBarSize?: number;
|
|
184
193
|
valueLabel?: BarValueLabelConfig;
|
|
185
194
|
};
|
|
@@ -218,6 +227,8 @@ export declare function getSeriesColor(series: {
|
|
|
218
227
|
fill?: string;
|
|
219
228
|
}): string;
|
|
220
229
|
export type LabelOversizedBehavior = 'truncate' | 'wrap' | 'hide';
|
|
230
|
+
export type AxisTickValue = string | number | Date;
|
|
231
|
+
export type AxisTickFormatter = (value: AxisTickValue) => string;
|
|
221
232
|
export type XAxisConfigBase = {
|
|
222
233
|
display?: boolean;
|
|
223
234
|
dataKey?: string;
|
|
@@ -228,7 +239,7 @@ export type XAxisConfigBase = {
|
|
|
228
239
|
rotatedLabels?: boolean;
|
|
229
240
|
maxLabelWidth?: number;
|
|
230
241
|
oversizedBehavior?: LabelOversizedBehavior;
|
|
231
|
-
tickFormat?: string |
|
|
242
|
+
tickFormat?: string | AxisTickFormatter | null;
|
|
232
243
|
autoHideOverlapping?: boolean;
|
|
233
244
|
minLabelGap?: number;
|
|
234
245
|
preserveEndLabels?: boolean;
|
|
@@ -238,7 +249,7 @@ export type XAxisConfig = XAxisConfigBase & {
|
|
|
238
249
|
};
|
|
239
250
|
export type YAxisConfigBase = {
|
|
240
251
|
display?: boolean;
|
|
241
|
-
tickFormat?: string |
|
|
252
|
+
tickFormat?: string | AxisTickFormatter | null;
|
|
242
253
|
rotatedLabels?: boolean;
|
|
243
254
|
maxLabelWidth?: number;
|
|
244
255
|
oversizedBehavior?: LabelOversizedBehavior;
|
|
@@ -247,8 +258,8 @@ export type YAxisConfig = YAxisConfigBase & {
|
|
|
247
258
|
exportHooks?: ExportHooks<YAxisConfigBase>;
|
|
248
259
|
};
|
|
249
260
|
export type GridConfigBase = {
|
|
250
|
-
|
|
251
|
-
|
|
261
|
+
value?: boolean;
|
|
262
|
+
category?: boolean;
|
|
252
263
|
};
|
|
253
264
|
export type GridConfig = GridConfigBase & {
|
|
254
265
|
exportHooks?: ExportHooks<GridConfigBase>;
|
|
@@ -303,8 +314,14 @@ export type TitleConfig = TitleConfigBase & {
|
|
|
303
314
|
exportHooks?: ExportHooks<TitleConfigBase>;
|
|
304
315
|
};
|
|
305
316
|
export type ScaleType = 'band' | 'linear' | 'time' | 'log';
|
|
306
|
-
export type D3Scale = any;
|
|
307
317
|
export type ScaleDomainValue = string | number | Date;
|
|
318
|
+
export type D3Scale = {
|
|
319
|
+
(value: ScaleDomainValue): number | undefined;
|
|
320
|
+
domain(): ScaleDomainValue[];
|
|
321
|
+
range(): number[];
|
|
322
|
+
copy(): D3Scale;
|
|
323
|
+
bandwidth?(): number;
|
|
324
|
+
};
|
|
308
325
|
export type ScaleConfig = {
|
|
309
326
|
type: ScaleType;
|
|
310
327
|
domain?: ScaleDomainValue[];
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import { type ClassValue } from 'clsx';
|
|
2
1
|
import type { DeepPartial } from './types.js';
|
|
3
2
|
export { toChartData } from './grouped-tabular.js';
|
|
4
3
|
export type { GroupedStringParseOptions, ToChartDataOptions, } from './grouped-tabular.js';
|
|
5
|
-
export declare function cn(...inputs: ClassValue[]): string;
|
|
6
4
|
/**
|
|
7
5
|
* Sanitizes a string to be used as a CSS class name or ID.
|
|
8
6
|
* Removes or replaces invalid characters for CSS selectors.
|
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
import { clsx } from 'clsx';
|
|
2
|
-
import { twMerge } from 'tailwind-merge';
|
|
3
1
|
export { toChartData } from './grouped-tabular.js';
|
|
4
|
-
export function cn(...inputs) {
|
|
5
|
-
return twMerge(clsx(inputs));
|
|
6
|
-
}
|
|
7
2
|
/**
|
|
8
3
|
* Sanitizes a string to be used as a CSS class name or ID.
|
|
9
4
|
* Removes or replaces invalid characters for CSS selectors.
|
|
@@ -12,7 +12,7 @@ export type WordCloudConfig = {
|
|
|
12
12
|
rotation?: WordCloudRotationMode;
|
|
13
13
|
spiral?: WordCloudSpiral;
|
|
14
14
|
};
|
|
15
|
-
export type WordCloudChartConfig = Pick<BaseChartConfig, 'data' | 'theme' | 'responsive'> & {
|
|
15
|
+
export type WordCloudChartConfig = Pick<BaseChartConfig, 'data' | 'width' | 'height' | 'theme' | 'responsive'> & {
|
|
16
16
|
wordCloud?: WordCloudConfig;
|
|
17
17
|
};
|
|
18
18
|
export declare class WordCloudChart extends BaseChart {
|
|
@@ -115,6 +115,8 @@ export class WordCloudChart extends BaseChart {
|
|
|
115
115
|
createExportChart() {
|
|
116
116
|
return new WordCloudChart({
|
|
117
117
|
data: this.sourceData,
|
|
118
|
+
width: this.configuredWidth,
|
|
119
|
+
height: this.configuredHeight,
|
|
118
120
|
theme: this.theme,
|
|
119
121
|
responsive: this.responsiveConfig,
|
|
120
122
|
wordCloud: this.options,
|
|
@@ -22,10 +22,11 @@ export declare class XAxis implements LayoutAwareComponent<XAxisConfigBase> {
|
|
|
22
22
|
private readonly minLabelGap;
|
|
23
23
|
private readonly preserveEndLabels;
|
|
24
24
|
readonly exportHooks?: ExportHooks<XAxisConfigBase>;
|
|
25
|
+
private resolveFontSizeValue;
|
|
25
26
|
private resolveGroupLabelStyle;
|
|
26
27
|
constructor(config?: XAxisConfig);
|
|
27
28
|
getExportConfig(): XAxisConfigBase;
|
|
28
|
-
createExportComponent(override?: Partial<XAxisConfigBase>): LayoutAwareComponent
|
|
29
|
+
createExportComponent(override?: Partial<XAxisConfigBase>): LayoutAwareComponent<XAxisConfigBase>;
|
|
29
30
|
/**
|
|
30
31
|
* Returns the space required by the x-axis
|
|
31
32
|
*/
|