@acorex/charts 20.7.60 → 20.7.61
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.
|
@@ -5,10 +5,10 @@ import { InjectionToken, input, output, viewChild, signal, inject, computed, eff
|
|
|
5
5
|
|
|
6
6
|
const AXLineChartDefaultConfig = {
|
|
7
7
|
margins: {
|
|
8
|
-
top:
|
|
9
|
-
right:
|
|
10
|
-
bottom:
|
|
11
|
-
left:
|
|
8
|
+
top: 12,
|
|
9
|
+
right: 12,
|
|
10
|
+
bottom: 8,
|
|
11
|
+
left: 8,
|
|
12
12
|
},
|
|
13
13
|
showXAxis: true,
|
|
14
14
|
showYAxis: true,
|
|
@@ -59,6 +59,7 @@ class AXLineChartComponent extends AXChartComponent {
|
|
|
59
59
|
svg;
|
|
60
60
|
chart;
|
|
61
61
|
xScale;
|
|
62
|
+
xScaleKind = 'linear';
|
|
62
63
|
yScale;
|
|
63
64
|
xAxis;
|
|
64
65
|
yAxis;
|
|
@@ -109,15 +110,18 @@ class AXLineChartComponent extends AXChartComponent {
|
|
|
109
110
|
}, ...(ngDevMode ? [{ debugName: "effectiveMessages" }] : []));
|
|
110
111
|
// Layout & Dimensions
|
|
111
112
|
MIN_DIMENSION = 100;
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
113
|
+
DEFAULT_MARGIN_TOP = 12;
|
|
114
|
+
DEFAULT_MARGIN_RIGHT = 12;
|
|
115
|
+
DEFAULT_MARGIN_BOTTOM = 8;
|
|
116
|
+
DEFAULT_MARGIN_LEFT = 8;
|
|
117
|
+
MIN_MARGIN_BOTTOM = 28;
|
|
118
|
+
MIN_MARGIN_LEFT = 36;
|
|
119
|
+
X_AXIS_TITLE_GAP = 10;
|
|
120
|
+
TICK_AREA_PADDING = 4;
|
|
121
|
+
AXIS_TICK_PADDING = 6;
|
|
122
|
+
ROTATION_TOLERANCE = 0.92;
|
|
123
|
+
FORCE_ROTATE_LABEL_LENGTH = 14;
|
|
124
|
+
Y_AXIS_TITLE_PADDING = 10;
|
|
121
125
|
// Styling & Visual
|
|
122
126
|
DEFAULT_LINE_WIDTH = 2;
|
|
123
127
|
DEFAULT_POINT_RADIUS = 4;
|
|
@@ -136,16 +140,178 @@ class AXLineChartComponent extends AXChartComponent {
|
|
|
136
140
|
POINT_ANIMATION_DELAY_RATIO = 0.5;
|
|
137
141
|
POINT_ANIMATION_DURATION_RATIO = 0.3;
|
|
138
142
|
// Text & Labels
|
|
139
|
-
AXIS_LABEL_FONT_SIZE = 14;
|
|
140
143
|
CHAR_WIDTH_RATIO = 0.6;
|
|
141
144
|
FONT_WIDTH_MULTIPLIER = 0.6;
|
|
142
145
|
MAX_LABEL_LENGTH = 20;
|
|
143
|
-
MIN_FONT_SIZE_X =
|
|
144
|
-
MAX_FONT_SIZE_X =
|
|
146
|
+
MIN_FONT_SIZE_X = 10;
|
|
147
|
+
MAX_FONT_SIZE_X = 16;
|
|
145
148
|
MIN_FONT_SIZE_Y = 11;
|
|
146
|
-
MAX_FONT_SIZE_Y =
|
|
147
|
-
FONT_PADDING =
|
|
148
|
-
|
|
149
|
+
MAX_FONT_SIZE_Y = 16;
|
|
150
|
+
FONT_PADDING = 6;
|
|
151
|
+
xAxisLabelLayout = {
|
|
152
|
+
fontSize: 12,
|
|
153
|
+
willRotate: false,
|
|
154
|
+
tickAreaHeight: 24,
|
|
155
|
+
displayedTickCount: 1,
|
|
156
|
+
};
|
|
157
|
+
/**
|
|
158
|
+
* Resolves X-axis tick font size, rotation, and reserved tick area height.
|
|
159
|
+
*/
|
|
160
|
+
resolveXAxisLabelLayout(chartWidth, categoricalLabels) {
|
|
161
|
+
if (!categoricalLabels || categoricalLabels.length === 0) {
|
|
162
|
+
const displayedTickCount = Math.min(12, Math.max(5, Math.floor(chartWidth / 80)));
|
|
163
|
+
const fontSize = this.getXAxisTickFontSize(chartWidth, displayedTickCount);
|
|
164
|
+
const tickAreaHeight = fontSize + this.TICK_AREA_PADDING + this.AXIS_TICK_PADDING;
|
|
165
|
+
return { fontSize, willRotate: false, tickAreaHeight, displayedTickCount };
|
|
166
|
+
}
|
|
167
|
+
const itemCount = categoricalLabels.length;
|
|
168
|
+
const longestLabel = categoricalLabels.reduce((a, b) => (a.length > b.length ? a : b), '');
|
|
169
|
+
const effectiveLength = this.getEffectiveXLabelLength(longestLabel, itemCount);
|
|
170
|
+
const step = itemCount > 1 ? chartWidth / (itemCount - 1) : chartWidth;
|
|
171
|
+
let fontSize = this.getXAxisTickFontSize(chartWidth, itemCount);
|
|
172
|
+
let effectiveWidth = effectiveLength * fontSize * this.CHAR_WIDTH_RATIO;
|
|
173
|
+
while (effectiveWidth > step * this.ROTATION_TOLERANCE && fontSize > this.MIN_FONT_SIZE_X) {
|
|
174
|
+
fontSize--;
|
|
175
|
+
effectiveWidth = effectiveLength * fontSize * this.CHAR_WIDTH_RATIO;
|
|
176
|
+
}
|
|
177
|
+
const willRotate = longestLabel.length >= this.FORCE_ROTATE_LABEL_LENGTH ||
|
|
178
|
+
effectiveWidth > step * this.ROTATION_TOLERANCE;
|
|
179
|
+
if (willRotate && effectiveWidth > step) {
|
|
180
|
+
while (effectiveWidth > step * 1.35 && fontSize > this.MIN_FONT_SIZE_X) {
|
|
181
|
+
fontSize--;
|
|
182
|
+
effectiveWidth = effectiveLength * fontSize * this.CHAR_WIDTH_RATIO;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
const tickAreaHeight = this.estimateXAxisTickAreaHeight(fontSize, effectiveWidth, willRotate);
|
|
186
|
+
let displayedTickCount = itemCount;
|
|
187
|
+
if (itemCount > this.VERY_MANY_ITEMS_THRESHOLD) {
|
|
188
|
+
displayedTickCount = Math.ceil(itemCount / 5);
|
|
189
|
+
}
|
|
190
|
+
else if (itemCount > this.MANY_ITEMS_THRESHOLD) {
|
|
191
|
+
displayedTickCount = Math.ceil(itemCount / 2);
|
|
192
|
+
}
|
|
193
|
+
return { fontSize, willRotate, tickAreaHeight, displayedTickCount };
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Returns truncated label length used for layout calculations.
|
|
197
|
+
*/
|
|
198
|
+
getEffectiveXLabelLength(label, itemCount) {
|
|
199
|
+
const maxLength = itemCount > this.MANY_ITEMS_THRESHOLD ? 10 : this.MAX_LABEL_LENGTH;
|
|
200
|
+
return Math.min(label.length, maxLength);
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Estimates vertical space required below the X-axis for tick labels.
|
|
204
|
+
*/
|
|
205
|
+
estimateXAxisTickAreaHeight(fontSize, effectiveLabelWidth, willRotate) {
|
|
206
|
+
if (willRotate) {
|
|
207
|
+
return effectiveLabelWidth * Math.SQRT1_2 + fontSize * Math.SQRT1_2 + this.TICK_AREA_PADDING + this.AXIS_TICK_PADDING;
|
|
208
|
+
}
|
|
209
|
+
return fontSize + this.TICK_AREA_PADDING + this.AXIS_TICK_PADDING;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Measures rendered X-axis tick label area using the axis group bounding box.
|
|
213
|
+
*/
|
|
214
|
+
measureXAxisTickAreaHeight(axisGroup, fontSize, fallbackHeight) {
|
|
215
|
+
if (!axisGroup) {
|
|
216
|
+
return fallbackHeight;
|
|
217
|
+
}
|
|
218
|
+
try {
|
|
219
|
+
const bbox = axisGroup.getBBox();
|
|
220
|
+
const measured = Math.max(0, bbox.y + bbox.height) + this.TICK_AREA_PADDING;
|
|
221
|
+
return Math.max(measured, fallbackHeight);
|
|
222
|
+
}
|
|
223
|
+
catch {
|
|
224
|
+
return fallbackHeight;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Measures rendered Y-axis tick label width using SVG bounding boxes.
|
|
229
|
+
*/
|
|
230
|
+
measureMaxYAxisTickLabelWidth(tickNodes, fontSize) {
|
|
231
|
+
try {
|
|
232
|
+
if (tickNodes.length > 0) {
|
|
233
|
+
return Math.max(...tickNodes.map((node) => node.getBBox().width)) + this.FONT_PADDING;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
// Fall through to estimation
|
|
238
|
+
}
|
|
239
|
+
return this.calculateMaxYAxisTickLabelWidth(fontSize);
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Calculates required bottom margin for X-axis ticks, optional rotation, and title.
|
|
243
|
+
*/
|
|
244
|
+
calculateRequiredBottomMargin(options, chartWidth, categoricalLabels) {
|
|
245
|
+
const baseBottom = options.margins?.bottom ?? this.DEFAULT_MARGIN_BOTTOM;
|
|
246
|
+
if (options.showXAxis === false) {
|
|
247
|
+
return Math.max(baseBottom, 8);
|
|
248
|
+
}
|
|
249
|
+
const layout = this.resolveXAxisLabelLayout(chartWidth, categoricalLabels);
|
|
250
|
+
this.xAxisLabelLayout.fontSize = layout.fontSize;
|
|
251
|
+
this.xAxisLabelLayout.willRotate = layout.willRotate;
|
|
252
|
+
this.xAxisLabelLayout.tickAreaHeight = layout.tickAreaHeight;
|
|
253
|
+
this.xAxisLabelLayout.displayedTickCount = layout.displayedTickCount;
|
|
254
|
+
const titleBlock = options.xAxisLabel
|
|
255
|
+
? this.X_AXIS_TITLE_GAP + this.getAxisTitleFontSize(layout.fontSize) + 2
|
|
256
|
+
: 0;
|
|
257
|
+
const rotatedBuffer = layout.willRotate ? 12 : 4;
|
|
258
|
+
return Math.max(baseBottom, this.MIN_MARGIN_BOTTOM, layout.tickAreaHeight + titleBlock + rotatedBuffer);
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Calculates required left margin for Y-axis ticks and title.
|
|
262
|
+
*/
|
|
263
|
+
calculateRequiredLeftMargin(options, chartHeight) {
|
|
264
|
+
const baseLeft = options.margins?.left ?? this.DEFAULT_MARGIN_LEFT;
|
|
265
|
+
if (options.showYAxis === false) {
|
|
266
|
+
return baseLeft;
|
|
267
|
+
}
|
|
268
|
+
const fontSize = this.getYAxisTickFontSize(chartHeight);
|
|
269
|
+
const tickLabelWidth = this.calculateMaxYAxisTickLabelWidth(fontSize);
|
|
270
|
+
const titleBlock = options.yAxisLabel
|
|
271
|
+
? this.Y_AXIS_TITLE_PADDING + this.getAxisTitleFontSize(fontSize) + 4
|
|
272
|
+
: 0;
|
|
273
|
+
return Math.max(baseLeft, this.MIN_MARGIN_LEFT, tickLabelWidth + this.AXIS_TICK_PADDING + titleBlock + 4);
|
|
274
|
+
}
|
|
275
|
+
createXAxisTitle(axesGroup, title, tickAreaHeight, tickFontSize) {
|
|
276
|
+
const titleFontSize = this.getAxisTitleFontSize(tickFontSize);
|
|
277
|
+
const titleY = Math.min(this.height + tickAreaHeight + this.X_AXIS_TITLE_GAP, this.height + this.margin.bottom - titleFontSize - 2);
|
|
278
|
+
axesGroup
|
|
279
|
+
.append('text')
|
|
280
|
+
.attr('class', 'ax-line-chart-axis-label ax-x-axis-label')
|
|
281
|
+
.attr('text-anchor', 'middle')
|
|
282
|
+
.attr('dominant-baseline', 'hanging')
|
|
283
|
+
.attr('x', this.width / 2)
|
|
284
|
+
.attr('y', titleY)
|
|
285
|
+
.attr('direction', 'ltr')
|
|
286
|
+
.attr('style', `
|
|
287
|
+
font-size: ${titleFontSize}px;
|
|
288
|
+
font-weight: 500;
|
|
289
|
+
fill: rgb(var(--ax-comp-line-chart-text-color));
|
|
290
|
+
pointer-events: none;
|
|
291
|
+
`)
|
|
292
|
+
.text(title);
|
|
293
|
+
}
|
|
294
|
+
createYAxisTitle(axesGroup, title, tickFontSize, maxTickLabelWidth) {
|
|
295
|
+
const titleFontSize = this.getAxisTitleFontSize(tickFontSize);
|
|
296
|
+
const labelY = -maxTickLabelWidth - this.Y_AXIS_TITLE_PADDING;
|
|
297
|
+
const labelX = -this.height / 2;
|
|
298
|
+
axesGroup
|
|
299
|
+
.append('text')
|
|
300
|
+
.attr('class', 'ax-line-chart-axis-label ax-y-axis-label')
|
|
301
|
+
.attr('text-anchor', 'middle')
|
|
302
|
+
.attr('dominant-baseline', 'middle')
|
|
303
|
+
.attr('transform', 'rotate(-90)')
|
|
304
|
+
.attr('x', labelX)
|
|
305
|
+
.attr('y', labelY)
|
|
306
|
+
.attr('direction', 'ltr')
|
|
307
|
+
.attr('style', `
|
|
308
|
+
font-size: ${titleFontSize}px;
|
|
309
|
+
font-weight: 500;
|
|
310
|
+
fill: rgb(var(--ax-comp-line-chart-text-color));
|
|
311
|
+
pointer-events: none;
|
|
312
|
+
`)
|
|
313
|
+
.text(title);
|
|
314
|
+
}
|
|
149
315
|
// Data & Performance
|
|
150
316
|
MAX_POINTS_TO_RENDER = 100;
|
|
151
317
|
POINT_COORDINATE_PRECISION = 10;
|
|
@@ -172,10 +338,41 @@ class AXLineChartComponent extends AXChartComponent {
|
|
|
172
338
|
return series.id || series.label || `series-${series.originalIndex}`;
|
|
173
339
|
}
|
|
174
340
|
/**
|
|
175
|
-
* Calculates
|
|
341
|
+
* Calculates adaptive X-axis tick font size based on chart width and label count.
|
|
342
|
+
*/
|
|
343
|
+
getXAxisTickFontSize(chartWidth, itemCount = 1) {
|
|
344
|
+
let size = chartWidth / 42;
|
|
345
|
+
if (itemCount > this.VERY_MANY_ITEMS_THRESHOLD) {
|
|
346
|
+
size *= 0.65;
|
|
347
|
+
}
|
|
348
|
+
else if (itemCount > this.MANY_ITEMS_THRESHOLD) {
|
|
349
|
+
size *= 0.8;
|
|
350
|
+
}
|
|
351
|
+
return Math.max(this.MIN_FONT_SIZE_X, Math.min(this.MAX_FONT_SIZE_X, Math.round(size)));
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Calculates adaptive Y-axis tick font size based on chart height.
|
|
355
|
+
*/
|
|
356
|
+
getYAxisTickFontSize(chartHeight) {
|
|
357
|
+
const size = chartHeight / 28;
|
|
358
|
+
return Math.max(this.MIN_FONT_SIZE_Y, Math.min(this.MAX_FONT_SIZE_Y, Math.round(size)));
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Calculates axis title font size relative to tick labels.
|
|
176
362
|
*/
|
|
177
|
-
|
|
178
|
-
return Math.
|
|
363
|
+
getAxisTitleFontSize(tickFontSize) {
|
|
364
|
+
return Math.min(15, Math.max(12, tickFontSize));
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Estimates inner chart dimensions before margins are finalized.
|
|
368
|
+
*/
|
|
369
|
+
estimateChartDimensions(containerWidth, containerHeight, options) {
|
|
370
|
+
const outerWidth = options.width ?? Math.max(containerWidth, 1);
|
|
371
|
+
const outerHeight = options.height ?? Math.max(containerHeight, 1);
|
|
372
|
+
return {
|
|
373
|
+
width: Math.max(outerWidth - this.margin.left - this.margin.right, this.MIN_DIMENSION),
|
|
374
|
+
height: Math.max(outerHeight - this.margin.top - this.margin.bottom, this.MIN_DIMENSION),
|
|
375
|
+
};
|
|
179
376
|
}
|
|
180
377
|
/**
|
|
181
378
|
* Creates a unique key for point coordinates (for overlap detection)
|
|
@@ -202,7 +399,7 @@ class AXLineChartComponent extends AXChartComponent {
|
|
|
202
399
|
/**
|
|
203
400
|
* Calculates maximum width needed for Y-axis tick labels
|
|
204
401
|
*/
|
|
205
|
-
calculateMaxYAxisTickLabelWidth() {
|
|
402
|
+
calculateMaxYAxisTickLabelWidth(fontSize) {
|
|
206
403
|
const allSeriesData = this._fullNormalizedData.filter((s) => !this.hiddenSeries.has(this.getSeriesIdentifier(s)));
|
|
207
404
|
let maxValue = 0;
|
|
208
405
|
let minValue = 0;
|
|
@@ -219,7 +416,7 @@ class AXLineChartComponent extends AXChartComponent {
|
|
|
219
416
|
// Check both max and min (for negative values)
|
|
220
417
|
const maxAbsValue = Math.max(Math.abs(maxValue), Math.abs(minValue));
|
|
221
418
|
const tickLabelText = Number.isFinite(maxAbsValue) ? formatLargeNumber(maxAbsValue) : '00000';
|
|
222
|
-
return tickLabelText.length *
|
|
419
|
+
return tickLabelText.length * fontSize * this.FONT_WIDTH_MULTIPLIER + this.FONT_PADDING;
|
|
223
420
|
}
|
|
224
421
|
ngOnInit() {
|
|
225
422
|
this.loadD3();
|
|
@@ -369,95 +566,54 @@ class AXLineChartComponent extends AXChartComponent {
|
|
|
369
566
|
this._tooltipVisible.set(false);
|
|
370
567
|
}
|
|
371
568
|
setupDimensions(containerElement, options) {
|
|
372
|
-
|
|
373
|
-
const
|
|
374
|
-
|
|
375
|
-
const minDim = Math.min(this.MIN_CONTAINER_DIMENSION, containerWidth, containerHeight);
|
|
569
|
+
const containerWidth = Math.max(containerElement.clientWidth, 1);
|
|
570
|
+
const containerHeight = Math.max(containerElement.clientHeight, 1);
|
|
571
|
+
this.calculateMargins(options, containerWidth, containerHeight);
|
|
376
572
|
if (options.width && options.height) {
|
|
377
573
|
this.width = options.width - this.margin.left - this.margin.right;
|
|
378
574
|
this.height = options.height - this.margin.top - this.margin.bottom;
|
|
379
575
|
}
|
|
380
576
|
else {
|
|
381
|
-
this.width =
|
|
382
|
-
this.height =
|
|
577
|
+
this.width = containerWidth - this.margin.left - this.margin.right;
|
|
578
|
+
this.height = containerHeight - this.margin.top - this.margin.bottom;
|
|
383
579
|
}
|
|
384
580
|
this.width = Math.max(this.width, this.MIN_DIMENSION);
|
|
385
581
|
this.height = Math.max(this.height, this.MIN_DIMENSION);
|
|
386
582
|
const totalWidth = this.width + this.margin.left + this.margin.right;
|
|
387
583
|
const totalHeight = this.height + this.margin.top + this.margin.bottom;
|
|
388
|
-
const viewBoxWidth = totalWidth + this.CHART_EDGE_PADDING * 2;
|
|
389
|
-
const viewBoxHeight = totalHeight + this.CHART_EDGE_PADDING * 2;
|
|
390
584
|
const svg = this.d3
|
|
391
585
|
.select(containerElement)
|
|
392
586
|
.append('svg')
|
|
393
587
|
.attr('width', '100%')
|
|
394
588
|
.attr('height', '100%')
|
|
395
|
-
.attr('viewBox', `0 0 ${
|
|
589
|
+
.attr('viewBox', `0 0 ${totalWidth} ${totalHeight}`)
|
|
396
590
|
.attr('preserveAspectRatio', 'xMidYMid meet');
|
|
397
591
|
this.svg = svg;
|
|
398
592
|
this.chart = this.svg
|
|
399
593
|
.append('g')
|
|
400
|
-
.attr('transform', `translate(${this.margin.left
|
|
594
|
+
.attr('transform', `translate(${this.margin.left},${this.margin.top})`);
|
|
401
595
|
}
|
|
402
|
-
calculateMargins(options, containerWidth) {
|
|
596
|
+
calculateMargins(options, containerWidth, containerHeight) {
|
|
403
597
|
this.margin = {
|
|
404
598
|
top: options.margins?.top ?? this.DEFAULT_MARGIN_TOP,
|
|
405
599
|
right: options.margins?.right ?? this.DEFAULT_MARGIN_RIGHT,
|
|
406
600
|
bottom: options.margins?.bottom ?? this.DEFAULT_MARGIN_BOTTOM,
|
|
407
601
|
left: options.margins?.left ?? this.DEFAULT_MARGIN_LEFT,
|
|
408
602
|
};
|
|
603
|
+
const estimatedDimensions = this.estimateChartDimensions(containerWidth, containerHeight, options);
|
|
409
604
|
const allDataPoints = this._fullNormalizedData.flatMap((series) => series.data);
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
const labelCount = allXValues.size;
|
|
416
|
-
if (labelCount > 0 && containerWidth > 0) {
|
|
417
|
-
const workingWidth = containerWidth - this.margin.left - this.margin.right;
|
|
418
|
-
if (workingWidth > 0) {
|
|
419
|
-
const availableWidthPerLabel = workingWidth / labelCount;
|
|
420
|
-
const labels = Array.from(allXValues);
|
|
421
|
-
const longestLabel = labels.reduce((a, b) => (a.length > b.length ? a : b), '');
|
|
422
|
-
const estimatedFontSize = this.calculateDynamicFontSize(workingWidth, 45, this.MIN_FONT_SIZE_X, this.MAX_FONT_SIZE_X);
|
|
423
|
-
// Account for label truncation in width calculation
|
|
424
|
-
const maxLabelLength = labelCount > this.MANY_ITEMS_THRESHOLD ? 10 : this.MAX_LABEL_LENGTH;
|
|
425
|
-
const effectiveLabelLength = Math.min(longestLabel.length, maxLabelLength);
|
|
426
|
-
const estimatedLongestLabelWidth = effectiveLabelLength * estimatedFontSize * this.CHAR_WIDTH_RATIO;
|
|
427
|
-
if (estimatedLongestLabelWidth > availableWidthPerLabel) {
|
|
428
|
-
// Calculate diagonal height when rotated -45 degrees
|
|
429
|
-
const diagonalHeight = estimatedLongestLabelWidth * Math.sin(Math.PI / 4);
|
|
430
|
-
const requiredExtraMargin = diagonalHeight + 15; // Extra padding for safety
|
|
431
|
-
this.margin.bottom += Math.min(this.MAX_EXTRA_MARGIN, requiredExtraMargin);
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
if (options.xAxisLabel) {
|
|
438
|
-
const xLabelLength = options.xAxisLabel.length;
|
|
439
|
-
const extraBottomMargin = Math.min(20, Math.max(10, xLabelLength * 0.8));
|
|
440
|
-
this.margin.bottom = Math.max(this.margin.bottom, 40 + extraBottomMargin);
|
|
441
|
-
}
|
|
442
|
-
if (options.yAxisLabel) {
|
|
443
|
-
// Calculate space needed for Y-axis: tick labels + padding + title
|
|
444
|
-
const maxTickLabelWidth = this.calculateMaxYAxisTickLabelWidth();
|
|
445
|
-
const yAxisTitleThickness = 20; // Height of rotated title text
|
|
446
|
-
const yAxisTitlePadding = this.Y_AXIS_PADDING;
|
|
447
|
-
const totalYAxisWidth = maxTickLabelWidth + yAxisTitlePadding + yAxisTitleThickness;
|
|
448
|
-
this.margin.left = Math.max(this.margin.left, totalYAxisWidth);
|
|
449
|
-
}
|
|
450
|
-
else if (options.showYAxis !== false) {
|
|
451
|
-
// Just tick labels, no title
|
|
452
|
-
const maxTickLabelWidth = this.calculateMaxYAxisTickLabelWidth();
|
|
453
|
-
this.margin.left = Math.max(this.margin.left, maxTickLabelWidth + 10);
|
|
454
|
-
}
|
|
455
|
-
if (options.showXAxis !== false) {
|
|
456
|
-
this.margin.bottom = Math.max(this.margin.bottom, this.MIN_MARGIN_BOTTOM);
|
|
457
|
-
}
|
|
458
|
-
if (options.showYAxis !== false) {
|
|
459
|
-
this.margin.left = Math.max(this.margin.left, this.MIN_MARGIN_LEFT);
|
|
605
|
+
const allNumericX = allDataPoints.length === 0 || allDataPoints.every((d) => typeof d.x === 'number');
|
|
606
|
+
let categoricalLabels = null;
|
|
607
|
+
if (!allNumericX) {
|
|
608
|
+
const visibleSeries = this._fullNormalizedData.filter((s) => !this.hiddenSeries.has(this.getSeriesIdentifier(s)));
|
|
609
|
+
categoricalLabels = [...new Set(visibleSeries.flatMap((series) => series.data.map((d) => String(d.x))))];
|
|
460
610
|
}
|
|
611
|
+
this.margin.bottom = this.calculateRequiredBottomMargin(options, estimatedDimensions.width, categoricalLabels);
|
|
612
|
+
this.margin.left = this.calculateRequiredLeftMargin(options, estimatedDimensions.height);
|
|
613
|
+
// Second pass: margins affect inner chart size, which affects label layout
|
|
614
|
+
const refinedDimensions = this.estimateChartDimensions(containerWidth, containerHeight, options);
|
|
615
|
+
this.margin.bottom = this.calculateRequiredBottomMargin(options, refinedDimensions.width, categoricalLabels);
|
|
616
|
+
this.margin.left = this.calculateRequiredLeftMargin(options, refinedDimensions.height);
|
|
461
617
|
}
|
|
462
618
|
setupScales(data) {
|
|
463
619
|
// Expects already filtered data for scales
|
|
@@ -472,20 +628,21 @@ class AXLineChartComponent extends AXChartComponent {
|
|
|
472
628
|
}
|
|
473
629
|
const allNumericX = allDataPoints.every((d) => typeof d.x === 'number');
|
|
474
630
|
if (allNumericX) {
|
|
631
|
+
this.xScaleKind = 'linear';
|
|
632
|
+
const xMin = this.d3.min(allDataPoints, (d) => d.x) ?? 0;
|
|
475
633
|
const xMax = this.d3.max(allDataPoints, (d) => d.x) ?? 0;
|
|
476
|
-
if (
|
|
477
|
-
// If all values are 0, show -1 to 1 range
|
|
634
|
+
if (xMin === xMax) {
|
|
478
635
|
this.xScale = this.d3
|
|
479
636
|
.scaleLinear()
|
|
480
|
-
.domain([-1, 1])
|
|
637
|
+
.domain([xMin - 1, xMax + 1])
|
|
481
638
|
.range([0, this.width]);
|
|
482
639
|
}
|
|
483
640
|
else {
|
|
484
|
-
|
|
485
|
-
this.xScale = this.d3.scaleLinear().domain([0, xMax]).range([0, this.width]);
|
|
641
|
+
this.xScale = this.d3.scaleLinear().domain([xMin, xMax]).range([0, this.width]);
|
|
486
642
|
}
|
|
487
643
|
}
|
|
488
644
|
else {
|
|
645
|
+
this.xScaleKind = 'point';
|
|
489
646
|
const xDomain = [];
|
|
490
647
|
const seenX = new Set();
|
|
491
648
|
for (const point of allDataPoints) {
|
|
@@ -496,40 +653,54 @@ class AXLineChartComponent extends AXChartComponent {
|
|
|
496
653
|
}
|
|
497
654
|
}
|
|
498
655
|
this.xScale = this.d3
|
|
499
|
-
.
|
|
656
|
+
.scalePoint()
|
|
500
657
|
.domain(xDomain)
|
|
501
658
|
.range([0, this.width])
|
|
502
|
-
.
|
|
503
|
-
.paddingOuter(0);
|
|
659
|
+
.padding(0);
|
|
504
660
|
}
|
|
505
661
|
const yAxisStartsAtZero = chartOptions.yAxisStartsAtZero !== false;
|
|
506
|
-
|
|
662
|
+
const dataYMin = this.d3.min(allDataPoints, (d) => d.y) ?? 0;
|
|
663
|
+
const dataYMax = this.d3.max(allDataPoints, (d) => d.y) ?? 0;
|
|
664
|
+
let yMin;
|
|
665
|
+
let yMax;
|
|
507
666
|
if (yAxisStartsAtZero) {
|
|
508
|
-
yMin = Math.min(0,
|
|
667
|
+
yMin = Math.min(0, dataYMin);
|
|
668
|
+
yMax = Math.max(0, dataYMax);
|
|
669
|
+
}
|
|
670
|
+
else {
|
|
671
|
+
yMin = dataYMin;
|
|
672
|
+
yMax = dataYMax;
|
|
509
673
|
}
|
|
510
|
-
const
|
|
511
|
-
const yRange = yMax - yMin;
|
|
674
|
+
const yRange = yMax - yMin || 1;
|
|
512
675
|
this.yScale = this.d3
|
|
513
676
|
.scaleLinear()
|
|
514
677
|
.domain([yMin, yMax + yRange * paddingMultiplier])
|
|
515
678
|
.nice()
|
|
516
679
|
.range([this.height, 0]);
|
|
680
|
+
if (yAxisStartsAtZero && dataYMin >= 0) {
|
|
681
|
+
this.yScale.domain([0, this.yScale.domain()[1]]);
|
|
682
|
+
}
|
|
517
683
|
}
|
|
518
684
|
createAxes(options) {
|
|
519
685
|
const showXAxis = options.showXAxis !== false;
|
|
520
686
|
const showYAxis = options.showYAxis !== false;
|
|
521
687
|
const showGrid = options.showGrid !== false;
|
|
522
|
-
const
|
|
688
|
+
const isPointScale = this.xScaleKind === 'point';
|
|
689
|
+
const pointScale = isPointScale ? this.xScale : null;
|
|
523
690
|
const isRtl = document.documentElement.dir === 'rtl' || document.body.dir === 'rtl';
|
|
524
691
|
const axesGroup = this.chart.append('g').attr('class', 'ax-line-chart-axes');
|
|
525
692
|
if (showXAxis) {
|
|
526
693
|
let xAxisGenerator;
|
|
527
694
|
let itemCount = 0;
|
|
528
|
-
if (
|
|
529
|
-
|
|
530
|
-
|
|
695
|
+
if (isPointScale && pointScale) {
|
|
696
|
+
itemCount = pointScale.domain().length;
|
|
697
|
+
const pointLayout = this.resolveXAxisLabelLayout(this.width, pointScale.domain());
|
|
698
|
+
this.xAxisLabelLayout.fontSize = pointLayout.fontSize;
|
|
699
|
+
this.xAxisLabelLayout.willRotate = pointLayout.willRotate;
|
|
700
|
+
this.xAxisLabelLayout.tickAreaHeight = pointLayout.tickAreaHeight;
|
|
701
|
+
this.xAxisLabelLayout.displayedTickCount = pointLayout.displayedTickCount;
|
|
531
702
|
// Smart tick reduction for many items (like bar chart)
|
|
532
|
-
let tickValues =
|
|
703
|
+
let tickValues = pointScale.domain();
|
|
533
704
|
if (itemCount > this.VERY_MANY_ITEMS_THRESHOLD) {
|
|
534
705
|
// Show every 5th tick for 50+ items
|
|
535
706
|
tickValues = tickValues.filter((_d, i) => i % 5 === 0);
|
|
@@ -539,7 +710,7 @@ class AXLineChartComponent extends AXChartComponent {
|
|
|
539
710
|
tickValues = tickValues.filter((_d, i) => i % 2 === 0);
|
|
540
711
|
}
|
|
541
712
|
xAxisGenerator = this.d3
|
|
542
|
-
.axisBottom(
|
|
713
|
+
.axisBottom(pointScale)
|
|
543
714
|
.tickValues(tickValues)
|
|
544
715
|
.tickSize(5)
|
|
545
716
|
.tickPadding(8)
|
|
@@ -582,21 +753,17 @@ class AXLineChartComponent extends AXChartComponent {
|
|
|
582
753
|
.attr('stroke', 'rgba(var(--ax-comp-line-chart-grid-lines-color), 0.2)')
|
|
583
754
|
.attr('stroke-dasharray', '2,2')
|
|
584
755
|
.attr('stroke-opacity', '0.5');
|
|
585
|
-
const dynamicXAxisTickFontSize =
|
|
756
|
+
const dynamicXAxisTickFontSize = isPointScale
|
|
757
|
+
? this.xAxisLabelLayout.fontSize
|
|
758
|
+
: this.getXAxisTickFontSize(this.width, itemCount);
|
|
586
759
|
const xAxisTicks = this.xAxis
|
|
587
760
|
.selectAll('text')
|
|
588
761
|
.style('font-size', `${dynamicXAxisTickFontSize}px`)
|
|
589
762
|
.style('font-weight', '400')
|
|
590
|
-
.style('fill', 'rgba(var(--ax-comp-line-chart-labels-color), 0.7)')
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
const step = this.xScale.step();
|
|
595
|
-
const longestLabel = this.xScale.domain().reduce((a, b) => (a.length > b.length ? a : b), '');
|
|
596
|
-
// Using char width ratio constant for better estimate
|
|
597
|
-
const estimatedLongestLabelWidth = longestLabel.length * dynamicXAxisTickFontSize * this.CHAR_WIDTH_RATIO;
|
|
598
|
-
if (estimatedLongestLabelWidth > step) {
|
|
599
|
-
labelsAreRotated = true;
|
|
763
|
+
.style('fill', 'rgba(var(--ax-comp-line-chart-labels-color), 0.7)')
|
|
764
|
+
.attr('direction', 'ltr');
|
|
765
|
+
if (isPointScale && pointScale && pointScale.domain().length > 0) {
|
|
766
|
+
if (this.xAxisLabelLayout.willRotate) {
|
|
600
767
|
xAxisTicks
|
|
601
768
|
.attr('transform', 'rotate(-45)')
|
|
602
769
|
.style('text-anchor', 'end')
|
|
@@ -604,24 +771,9 @@ class AXLineChartComponent extends AXChartComponent {
|
|
|
604
771
|
.attr('dy', '0.15em');
|
|
605
772
|
}
|
|
606
773
|
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
if (shouldShowXAxisTitle) {
|
|
611
|
-
axesGroup
|
|
612
|
-
.append('text')
|
|
613
|
-
.attr('class', 'ax-line-chart-axis-label ax-x-axis-label')
|
|
614
|
-
.attr('text-anchor', 'middle')
|
|
615
|
-
.attr('x', this.width / 2)
|
|
616
|
-
.attr('y', this.height + this.margin.bottom - 5)
|
|
617
|
-
.attr('direction', 'ltr')
|
|
618
|
-
.attr('style', `
|
|
619
|
-
font-size: 14px;
|
|
620
|
-
font-weight: 500;
|
|
621
|
-
fill: rgb(var(--ax-comp-line-chart-text-color));
|
|
622
|
-
pointer-events: none;
|
|
623
|
-
`)
|
|
624
|
-
.text(options.xAxisLabel);
|
|
774
|
+
const xTickAreaHeight = this.measureXAxisTickAreaHeight(this.xAxis.node(), dynamicXAxisTickFontSize, this.xAxisLabelLayout.tickAreaHeight);
|
|
775
|
+
if (options.xAxisLabel) {
|
|
776
|
+
this.createXAxisTitle(axesGroup, options.xAxisLabel, xTickAreaHeight, dynamicXAxisTickFontSize);
|
|
625
777
|
}
|
|
626
778
|
}
|
|
627
779
|
if (showYAxis) {
|
|
@@ -650,7 +802,7 @@ class AXLineChartComponent extends AXChartComponent {
|
|
|
650
802
|
.attr('stroke', 'rgba(var(--ax-comp-line-chart-grid-lines-color), 0.2)')
|
|
651
803
|
.attr('stroke-dasharray', '2,2')
|
|
652
804
|
.attr('stroke-opacity', '0.5');
|
|
653
|
-
const dynamicYAxisTickFontSize = this.
|
|
805
|
+
const dynamicYAxisTickFontSize = this.getYAxisTickFontSize(this.height);
|
|
654
806
|
const yTickTexts = this.yAxis.selectAll('text').attr('style', `
|
|
655
807
|
font-size: ${dynamicYAxisTickFontSize}px;
|
|
656
808
|
font-weight: 400;
|
|
@@ -659,31 +811,13 @@ class AXLineChartComponent extends AXChartComponent {
|
|
|
659
811
|
if (isRtl) {
|
|
660
812
|
yTickTexts.attr('text-anchor', 'start');
|
|
661
813
|
}
|
|
814
|
+
else {
|
|
815
|
+
yTickTexts.attr('text-anchor', 'end').attr('direction', 'ltr');
|
|
816
|
+
}
|
|
817
|
+
const yTickNodes = (yTickTexts.nodes() || []);
|
|
818
|
+
const maxTickLabelWidth = this.measureMaxYAxisTickLabelWidth(yTickNodes, dynamicYAxisTickFontSize);
|
|
662
819
|
if (options.yAxisLabel) {
|
|
663
|
-
|
|
664
|
-
// Position it to the left of the tick labels with proper spacing
|
|
665
|
-
const maxTickLabelWidth = this.calculateMaxYAxisTickLabelWidth();
|
|
666
|
-
const padding = this.Y_AXIS_PADDING;
|
|
667
|
-
// Position title to the left of the tick labels
|
|
668
|
-
const labelY = -maxTickLabelWidth - padding;
|
|
669
|
-
// Center the title vertically
|
|
670
|
-
const labelX = -this.height / 2;
|
|
671
|
-
axesGroup
|
|
672
|
-
.append('text')
|
|
673
|
-
.attr('class', 'ax-line-chart-axis-label ax-y-axis-label')
|
|
674
|
-
.attr('text-anchor', 'middle')
|
|
675
|
-
.attr('dominant-baseline', 'middle')
|
|
676
|
-
.attr('transform', 'rotate(-90)')
|
|
677
|
-
.attr('x', labelX)
|
|
678
|
-
.attr('y', labelY)
|
|
679
|
-
.attr('direction', 'ltr')
|
|
680
|
-
.attr('style', `
|
|
681
|
-
font-size: 14px;
|
|
682
|
-
font-weight: 500;
|
|
683
|
-
fill: rgb(var(--ax-comp-line-chart-text-color));
|
|
684
|
-
pointer-events: none;
|
|
685
|
-
`)
|
|
686
|
-
.text(options.yAxisLabel);
|
|
820
|
+
this.createYAxisTitle(axesGroup, options.yAxisLabel, dynamicYAxisTickFontSize, maxTickLabelWidth);
|
|
687
821
|
}
|
|
688
822
|
}
|
|
689
823
|
if (showGrid) {
|
|
@@ -716,7 +850,7 @@ class AXLineChartComponent extends AXChartComponent {
|
|
|
716
850
|
.axisBottom(this.xScale)
|
|
717
851
|
.tickSize(-this.height)
|
|
718
852
|
.tickFormat(() => '')
|
|
719
|
-
.tickValues(
|
|
853
|
+
.tickValues(isPointScale ? undefined : this.xScale.ticks()));
|
|
720
854
|
// Style the vertical grid path and lines
|
|
721
855
|
xGrid.selectAll('path').attr('stroke-width', '0');
|
|
722
856
|
xGrid
|
|
@@ -728,16 +862,14 @@ class AXLineChartComponent extends AXChartComponent {
|
|
|
728
862
|
}
|
|
729
863
|
}
|
|
730
864
|
}
|
|
865
|
+
getXPosition(point) {
|
|
866
|
+
if (this.xScaleKind === 'point') {
|
|
867
|
+
return this.xScale(String(point.x)) ?? 0;
|
|
868
|
+
}
|
|
869
|
+
return this.xScale(point.x);
|
|
870
|
+
}
|
|
731
871
|
renderLines(allSeriesData) {
|
|
732
|
-
const
|
|
733
|
-
const getX = (d) => {
|
|
734
|
-
if (isBandScale) {
|
|
735
|
-
return this.xScale(String(d.x)) + this.xScale.bandwidth() / 2;
|
|
736
|
-
}
|
|
737
|
-
else {
|
|
738
|
-
return this.xScale(d.x);
|
|
739
|
-
}
|
|
740
|
-
};
|
|
872
|
+
const getX = (d) => this.getXPosition(d);
|
|
741
873
|
const lineGenerator = this.d3
|
|
742
874
|
.line()
|
|
743
875
|
.x(getX)
|
|
@@ -1024,14 +1156,7 @@ class AXLineChartComponent extends AXChartComponent {
|
|
|
1024
1156
|
}
|
|
1025
1157
|
}
|
|
1026
1158
|
showCrosshairLines(dataPoint) {
|
|
1027
|
-
const
|
|
1028
|
-
let x;
|
|
1029
|
-
if (isBandScale) {
|
|
1030
|
-
x = this.xScale(String(dataPoint.x)) + this.xScale.bandwidth() / 2;
|
|
1031
|
-
}
|
|
1032
|
-
else {
|
|
1033
|
-
x = this.xScale(dataPoint.x);
|
|
1034
|
-
}
|
|
1159
|
+
const x = this.getXPosition(dataPoint);
|
|
1035
1160
|
const y = this.yScale(dataPoint.y);
|
|
1036
1161
|
let crosshairGroup = this.chart.select('.ax-line-chart-crosshair');
|
|
1037
1162
|
if (crosshairGroup.empty()) {
|
|
@@ -1191,11 +1316,11 @@ class AXLineChartComponent extends AXChartComponent {
|
|
|
1191
1316
|
}
|
|
1192
1317
|
}
|
|
1193
1318
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AXLineChartComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
1194
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "20.3.3", type: AXLineChartComponent, isStandalone: true, selector: "ax-line-chart", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pointClick: "pointClick" }, viewQueries: [{ propertyName: "chartContainerEl", first: true, predicate: ["chartContainer"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"ax-line-chart\" #chartContainer>\n <!-- Shared tooltip component -->\n <ax-chart-tooltip\n [visible]=\"tooltipVisible()\"\n [position]=\"tooltipPosition()\"\n [data]=\"tooltipData()\"\n [showPercentage]=\"false\"\n ></ax-chart-tooltip>\n</div>\n", styles: ["ax-line-chart{display:block;width:100%;height:100%;min-height:0;box-sizing:border-box;--ax-comp-line-chart-labels-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-line-chart-axis-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-line-chart-grid-lines-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-line-chart-bg-color: 0, 0, 0, 0;--ax-comp-line-chart-text-color: var(--ax-sys-color-on-lightest-surface)}ax-line-chart .ax-line-chart{width:100%;height:100%;position:relative;box-sizing:border-box;padding:clamp(.5rem
|
|
1319
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "20.3.3", type: AXLineChartComponent, isStandalone: true, selector: "ax-line-chart", inputs: { data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pointClick: "pointClick" }, viewQueries: [{ propertyName: "chartContainerEl", first: true, predicate: ["chartContainer"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<div class=\"ax-line-chart\" #chartContainer>\n <!-- Shared tooltip component -->\n <ax-chart-tooltip\n [visible]=\"tooltipVisible()\"\n [position]=\"tooltipPosition()\"\n [data]=\"tooltipData()\"\n [showPercentage]=\"false\"\n ></ax-chart-tooltip>\n</div>\n", styles: ["ax-line-chart{display:block;width:100%;height:100%;min-height:0;box-sizing:border-box;--ax-comp-line-chart-labels-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-line-chart-axis-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-line-chart-grid-lines-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-line-chart-bg-color: 0, 0, 0, 0;--ax-comp-line-chart-text-color: var(--ax-sys-color-on-lightest-surface)}ax-line-chart .ax-line-chart{width:100%;height:100%;position:relative;box-sizing:border-box;padding:clamp(.25rem,.6vw,.5rem);border-radius:.5rem;overflow:hidden;contain:layout style;color:rgba(var(--ax-comp-line-chart-text-color));background-color:rgb(var(--ax-comp-line-chart-bg-color))}ax-line-chart .ax-line-chart svg{display:block;width:100%;height:100%;max-width:100%;max-height:100%;overflow:hidden;flex-shrink:0}ax-line-chart .ax-line-chart svg g:has(text){font-family:inherit}ax-line-chart .ax-line-chart-no-data-message{text-align:center;background-color:rgb(var(--ax-comp-line-chart-bg-color));padding:1.5rem;border-radius:.5rem;border:1px solid rgba(var(--ax-sys-color-surface));width:80%;max-width:300px}ax-line-chart .ax-line-chart-no-data-message .ax-line-chart-no-data-icon{opacity:.6;margin-bottom:.75rem}ax-line-chart .ax-line-chart-no-data-message .ax-line-chart-no-data-text{font-size:1rem;font-weight:600;margin-bottom:.5rem}ax-line-chart .ax-line-chart-no-data-message .ax-line-chart-no-data-help{font-size:.8rem;opacity:.6}\n"], dependencies: [{ kind: "component", type: AXChartTooltipComponent, selector: "ax-chart-tooltip", inputs: ["data", "position", "visible", "showPercentage", "style"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
1195
1320
|
}
|
|
1196
1321
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AXLineChartComponent, decorators: [{
|
|
1197
1322
|
type: Component,
|
|
1198
|
-
args: [{ selector: 'ax-line-chart', encapsulation: ViewEncapsulation.None, imports: [AXChartTooltipComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"ax-line-chart\" #chartContainer>\n <!-- Shared tooltip component -->\n <ax-chart-tooltip\n [visible]=\"tooltipVisible()\"\n [position]=\"tooltipPosition()\"\n [data]=\"tooltipData()\"\n [showPercentage]=\"false\"\n ></ax-chart-tooltip>\n</div>\n", styles: ["ax-line-chart{display:block;width:100%;height:100%;min-height:0;box-sizing:border-box;--ax-comp-line-chart-labels-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-line-chart-axis-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-line-chart-grid-lines-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-line-chart-bg-color: 0, 0, 0, 0;--ax-comp-line-chart-text-color: var(--ax-sys-color-on-lightest-surface)}ax-line-chart .ax-line-chart{width:100%;height:100%;position:relative;box-sizing:border-box;padding:clamp(.5rem
|
|
1323
|
+
args: [{ selector: 'ax-line-chart', encapsulation: ViewEncapsulation.None, imports: [AXChartTooltipComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"ax-line-chart\" #chartContainer>\n <!-- Shared tooltip component -->\n <ax-chart-tooltip\n [visible]=\"tooltipVisible()\"\n [position]=\"tooltipPosition()\"\n [data]=\"tooltipData()\"\n [showPercentage]=\"false\"\n ></ax-chart-tooltip>\n</div>\n", styles: ["ax-line-chart{display:block;width:100%;height:100%;min-height:0;box-sizing:border-box;--ax-comp-line-chart-labels-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-line-chart-axis-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-line-chart-grid-lines-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-line-chart-bg-color: 0, 0, 0, 0;--ax-comp-line-chart-text-color: var(--ax-sys-color-on-lightest-surface)}ax-line-chart .ax-line-chart{width:100%;height:100%;position:relative;box-sizing:border-box;padding:clamp(.25rem,.6vw,.5rem);border-radius:.5rem;overflow:hidden;contain:layout style;color:rgba(var(--ax-comp-line-chart-text-color));background-color:rgb(var(--ax-comp-line-chart-bg-color))}ax-line-chart .ax-line-chart svg{display:block;width:100%;height:100%;max-width:100%;max-height:100%;overflow:hidden;flex-shrink:0}ax-line-chart .ax-line-chart svg g:has(text){font-family:inherit}ax-line-chart .ax-line-chart-no-data-message{text-align:center;background-color:rgb(var(--ax-comp-line-chart-bg-color));padding:1.5rem;border-radius:.5rem;border:1px solid rgba(var(--ax-sys-color-surface));width:80%;max-width:300px}ax-line-chart .ax-line-chart-no-data-message .ax-line-chart-no-data-icon{opacity:.6;margin-bottom:.75rem}ax-line-chart .ax-line-chart-no-data-message .ax-line-chart-no-data-text{font-size:1rem;font-weight:600;margin-bottom:.5rem}ax-line-chart .ax-line-chart-no-data-message .ax-line-chart-no-data-help{font-size:.8rem;opacity:.6}\n"] }]
|
|
1199
1324
|
}] });
|
|
1200
1325
|
|
|
1201
1326
|
/**
|