@acorex/charts 20.1.4 → 20.1.6
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.
|
@@ -200,7 +200,7 @@ class AXLineChartComponent extends NXComponent {
|
|
|
200
200
|
return;
|
|
201
201
|
const containerElement = this.chartContainerEl().nativeElement;
|
|
202
202
|
const allSeriesData = this._fullNormalizedData;
|
|
203
|
-
this.d3.select(containerElement).
|
|
203
|
+
this.d3.select(containerElement).select('svg').remove();
|
|
204
204
|
if (allSeriesData.length === 0 || allSeriesData.every((series) => !series.data || series.data.length === 0)) {
|
|
205
205
|
this.showNoDataMessage(containerElement);
|
|
206
206
|
return;
|
|
@@ -243,7 +243,7 @@ class AXLineChartComponent extends NXComponent {
|
|
|
243
243
|
this._tooltipVisible.set(false);
|
|
244
244
|
}
|
|
245
245
|
setupDimensions(containerElement, options) {
|
|
246
|
-
this.calculateMargins(options);
|
|
246
|
+
this.calculateMargins(options, containerElement.clientWidth);
|
|
247
247
|
const containerWidth = containerElement.clientWidth;
|
|
248
248
|
const containerHeight = containerElement.clientHeight;
|
|
249
249
|
const minDim = Math.min(200, containerWidth, containerHeight);
|
|
@@ -276,13 +276,36 @@ class AXLineChartComponent extends NXComponent {
|
|
|
276
276
|
this.svg = svg;
|
|
277
277
|
this.chart = this.svg.append('g').attr('transform', `translate(${this.margin.left},${this.margin.top})`);
|
|
278
278
|
}
|
|
279
|
-
calculateMargins(options) {
|
|
279
|
+
calculateMargins(options, containerWidth) {
|
|
280
280
|
this.margin = {
|
|
281
281
|
top: options.margins?.top ?? 20,
|
|
282
282
|
right: options.margins?.right ?? 25,
|
|
283
283
|
bottom: options.margins?.bottom ?? 40,
|
|
284
284
|
left: options.margins?.left ?? 50,
|
|
285
285
|
};
|
|
286
|
+
const allDataPoints = this._fullNormalizedData.flatMap((series) => series.data);
|
|
287
|
+
if (allDataPoints.length > 0) {
|
|
288
|
+
const allNumericX = allDataPoints.every((d) => typeof d.x === 'number');
|
|
289
|
+
if (!allNumericX) {
|
|
290
|
+
const visibleSeries = this._fullNormalizedData.filter((s) => !this.hiddenSeries.has(s.id || s.label || `series-${s.originalIndex}`));
|
|
291
|
+
const allXValues = new Set(visibleSeries.flatMap((series) => series.data.map((d) => String(d.x))));
|
|
292
|
+
const labelCount = allXValues.size;
|
|
293
|
+
if (labelCount > 0 && containerWidth > 0) {
|
|
294
|
+
const workingWidth = containerWidth - this.margin.left - this.margin.right;
|
|
295
|
+
if (workingWidth > 0) {
|
|
296
|
+
const availableWidthPerLabel = workingWidth / labelCount;
|
|
297
|
+
const labels = Array.from(allXValues);
|
|
298
|
+
const longestLabel = labels.reduce((a, b) => (a.length > b.length ? a : b), '');
|
|
299
|
+
const estimatedFontSize = Math.max(11, Math.min(15, Math.round(workingWidth / 45)));
|
|
300
|
+
const estimatedLongestLabelWidth = longestLabel.length * estimatedFontSize * 0.6;
|
|
301
|
+
if (estimatedLongestLabelWidth > availableWidthPerLabel) {
|
|
302
|
+
const requiredExtraMargin = estimatedLongestLabelWidth * Math.sin(Math.PI / 4) + 10;
|
|
303
|
+
this.margin.bottom += Math.min(60, requiredExtraMargin);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
286
309
|
if (options.xAxisLabel) {
|
|
287
310
|
const xLabelLength = options.xAxisLabel.length;
|
|
288
311
|
const extraBottomMargin = Math.min(20, Math.max(10, xLabelLength * 0.8));
|
|
@@ -312,7 +335,6 @@ class AXLineChartComponent extends NXComponent {
|
|
|
312
335
|
allDataPoints.push({ x: 0, y: 0 }); // Default fallback to prevent crash with empty domain
|
|
313
336
|
}
|
|
314
337
|
const allNumericX = allDataPoints.every((d) => typeof d.x === 'number');
|
|
315
|
-
const allDates = !allNumericX && allDataPoints.every((d) => !isNaN(new Date(d.x).getTime()));
|
|
316
338
|
if (allNumericX) {
|
|
317
339
|
const xMin = this.d3.min(allDataPoints, (d) => d.x) ?? 0;
|
|
318
340
|
const xMax = this.d3.max(allDataPoints, (d) => d.x) ?? 0;
|
|
@@ -326,20 +348,6 @@ class AXLineChartComponent extends NXComponent {
|
|
|
326
348
|
this.xScale = this.d3.scaleLinear().domain([xMin, xMax]).range([0, this.width]);
|
|
327
349
|
}
|
|
328
350
|
}
|
|
329
|
-
else if (allDates) {
|
|
330
|
-
const xMin = this.d3.min(allDataPoints, (d) => new Date(d.x)) ?? new Date();
|
|
331
|
-
const xMax = this.d3.max(allDataPoints, (d) => new Date(d.x)) ?? new Date();
|
|
332
|
-
if (xMin.getTime() === xMax.getTime()) {
|
|
333
|
-
const oneDayMs = 86400000;
|
|
334
|
-
this.xScale = this.d3
|
|
335
|
-
.scaleTime()
|
|
336
|
-
.domain([new Date(xMin.getTime() - oneDayMs), new Date(xMax.getTime() + oneDayMs)])
|
|
337
|
-
.range([0, this.width]);
|
|
338
|
-
}
|
|
339
|
-
else {
|
|
340
|
-
this.xScale = this.d3.scaleTime().domain([xMin, xMax]).range([0, this.width]);
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
351
|
else {
|
|
344
352
|
this.xScale = this.d3
|
|
345
353
|
.scaleBand()
|
|
@@ -382,13 +390,27 @@ class AXLineChartComponent extends NXComponent {
|
|
|
382
390
|
.attr('stroke-dasharray', '2,2')
|
|
383
391
|
.attr('stroke-opacity', '0.5');
|
|
384
392
|
const dynamicXAxisTickFontSize = Math.max(11, Math.min(15, Math.round(this.width / 45)));
|
|
385
|
-
this.xAxis
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
393
|
+
const xAxisTicks = this.xAxis
|
|
394
|
+
.selectAll('text')
|
|
395
|
+
.style('font-size', `${dynamicXAxisTickFontSize}px`)
|
|
396
|
+
.style('font-weight', '400')
|
|
397
|
+
.style('fill', 'rgba(var(--ax-comp-line-chart-labels-color), 0.7)');
|
|
398
|
+
// Automatically rotate labels if they are likely to overlap
|
|
399
|
+
if (isBandScale && this.xScale.domain().length > 0) {
|
|
400
|
+
const step = this.xScale.step();
|
|
401
|
+
const longestLabel = this.xScale.domain().reduce((a, b) => (a.length > b.length ? a : b), '');
|
|
402
|
+
// Using 0.6 as a better estimate for char width-to-height ratio
|
|
403
|
+
const estimatedLongestLabelWidth = longestLabel.length * dynamicXAxisTickFontSize * 0.6;
|
|
404
|
+
if (estimatedLongestLabelWidth > step) {
|
|
405
|
+
xAxisTicks
|
|
406
|
+
.attr('transform', 'rotate(-45)')
|
|
407
|
+
.style('text-anchor', 'end')
|
|
408
|
+
.attr('dx', '-0.8em')
|
|
409
|
+
.attr('dy', '0.15em');
|
|
410
|
+
}
|
|
411
|
+
}
|
|
390
412
|
if (options.xAxisLabel) {
|
|
391
|
-
const labelY = this.height + this.margin.bottom * 0.
|
|
413
|
+
const labelY = this.height + this.margin.bottom * 0.7;
|
|
392
414
|
axesGroup
|
|
393
415
|
.append('text')
|
|
394
416
|
.attr('class', 'ax-line-chart-axis-label ax-x-axis-label')
|
|
@@ -496,7 +518,7 @@ class AXLineChartComponent extends NXComponent {
|
|
|
496
518
|
return this.xScale(String(d.x)) + this.xScale.bandwidth() / 2;
|
|
497
519
|
}
|
|
498
520
|
else {
|
|
499
|
-
return this.xScale(
|
|
521
|
+
return this.xScale(d.x);
|
|
500
522
|
}
|
|
501
523
|
};
|
|
502
524
|
const lineGenerator = this.d3
|
|
@@ -797,7 +819,7 @@ class AXLineChartComponent extends NXComponent {
|
|
|
797
819
|
x = this.xScale(String(dataPoint.x)) + this.xScale.bandwidth() / 2;
|
|
798
820
|
}
|
|
799
821
|
else {
|
|
800
|
-
x = this.xScale(
|
|
822
|
+
x = this.xScale(dataPoint.x);
|
|
801
823
|
}
|
|
802
824
|
const y = this.yScale(dataPoint.y);
|
|
803
825
|
let crosshairGroup = this.chart.select('.ax-line-chart-crosshair');
|