@acorex/charts 19.14.0-next.2 → 19.14.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/bar-chart/lib/bar-chart.component.d.ts +2 -6
- package/bar-chart/lib/bar-chart.type.d.ts +13 -6
- package/donut-chart/lib/donut-chart.component.d.ts +5 -0
- package/donut-chart/lib/donut-chart.type.d.ts +57 -0
- package/fesm2022/acorex-charts-bar-chart.mjs +87 -67
- package/fesm2022/acorex-charts-bar-chart.mjs.map +1 -1
- package/fesm2022/acorex-charts-chart-tooltip.mjs.map +1 -1
- package/fesm2022/acorex-charts-donut-chart.mjs +128 -68
- package/fesm2022/acorex-charts-donut-chart.mjs.map +1 -1
- package/fesm2022/acorex-charts-gauge-chart.mjs +261 -110
- package/fesm2022/acorex-charts-gauge-chart.mjs.map +1 -1
- package/fesm2022/acorex-charts-hierarchy-chart.mjs +11 -14
- package/fesm2022/acorex-charts-hierarchy-chart.mjs.map +1 -1
- package/fesm2022/acorex-charts-line-chart.mjs +204 -66
- package/fesm2022/acorex-charts-line-chart.mjs.map +1 -1
- package/fesm2022/acorex-charts.mjs.map +1 -1
- package/gauge-chart/lib/gauge-chart.component.d.ts +41 -13
- package/gauge-chart/lib/gauge-chart.type.d.ts +36 -5
- package/hierarchy-chart/lib/hierarchy-chart.component.d.ts +0 -3
- package/hierarchy-chart/lib/hierarchy-chart.type.d.ts +41 -13
- package/line-chart/lib/line-chart.component.d.ts +1 -0
- package/line-chart/lib/line-chart.type.d.ts +12 -0
- package/package.json +1 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { AXChartTooltipComponent } from '@acorex/charts/chart-tooltip';
|
|
1
2
|
import { CommonModule } from '@angular/common';
|
|
2
3
|
import * as i0 from '@angular/core';
|
|
3
|
-
import { InjectionToken, inject, input, viewChild, signal, computed, afterNextRender, effect, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
4
|
+
import { InjectionToken, inject, ChangeDetectorRef, input, viewChild, signal, computed, afterNextRender, effect, ChangeDetectionStrategy, ViewEncapsulation, Component } from '@angular/core';
|
|
4
5
|
import { AX_GLOBAL_CONFIG } from '@acorex/core/config';
|
|
5
6
|
import { set } from 'lodash-es';
|
|
6
7
|
|
|
@@ -9,12 +10,8 @@ const AXGaugeChartDefaultConfig = {
|
|
|
9
10
|
maxValue: 100,
|
|
10
11
|
gaugeWidth: 22,
|
|
11
12
|
cornerRadius: 5,
|
|
12
|
-
backgroundColor: 'transparent',
|
|
13
|
-
baseColor: '#e2e8f0',
|
|
14
13
|
showValue: true,
|
|
15
|
-
|
|
16
|
-
valueFontSize: 24,
|
|
17
|
-
labelFontSize: 16,
|
|
14
|
+
showTooltip: true,
|
|
18
15
|
animationDuration: 750,
|
|
19
16
|
animationEasing: 'cubic-out',
|
|
20
17
|
};
|
|
@@ -39,6 +36,8 @@ function gaugeChartConfig(config = {}) {
|
|
|
39
36
|
* Renders a semi-circular gauge chart with animated needle and thresholds
|
|
40
37
|
*/
|
|
41
38
|
class AXGaugeChartComponent {
|
|
39
|
+
// Inject ChangeDetectorRef
|
|
40
|
+
cdr = inject(ChangeDetectorRef);
|
|
42
41
|
// Inputs
|
|
43
42
|
/** Chart value input */
|
|
44
43
|
value = input(0);
|
|
@@ -56,6 +55,17 @@ class AXGaugeChartComponent {
|
|
|
56
55
|
_prevValue = signal(null);
|
|
57
56
|
// Track previous options to detect changes
|
|
58
57
|
_prevOptionsString = '';
|
|
58
|
+
// Tooltip signals
|
|
59
|
+
_tooltipVisible = signal(false);
|
|
60
|
+
_tooltipPosition = signal({ x: 0, y: 0 });
|
|
61
|
+
_tooltipData = signal({
|
|
62
|
+
title: '',
|
|
63
|
+
value: '',
|
|
64
|
+
});
|
|
65
|
+
// Expose tooltip signals as read-only for the template
|
|
66
|
+
tooltipVisible = this._tooltipVisible.asReadonly();
|
|
67
|
+
tooltipPosition = this._tooltipPosition.asReadonly();
|
|
68
|
+
tooltipData = this._tooltipData.asReadonly();
|
|
59
69
|
// Inject configuration
|
|
60
70
|
configToken = inject(AX_GAUGE_CHART_CONFIG);
|
|
61
71
|
// Computed configuration options
|
|
@@ -127,12 +137,8 @@ class AXGaugeChartComponent {
|
|
|
127
137
|
const options = this.effectiveOptions();
|
|
128
138
|
// Store options string for comparison
|
|
129
139
|
this._prevOptionsString = JSON.stringify(options);
|
|
130
|
-
|
|
131
|
-
const baseColor = options.baseColor;
|
|
140
|
+
// Use design tokens instead of removed properties
|
|
132
141
|
const showValue = options.showValue;
|
|
133
|
-
const valueColor = options.valueColor;
|
|
134
|
-
const valueFontSize = options.valueFontSize;
|
|
135
|
-
const labelFontSize = options.labelFontSize;
|
|
136
142
|
const minValue = options.minValue;
|
|
137
143
|
const maxValue = options.maxValue;
|
|
138
144
|
const thresholds = options.thresholds ?? [];
|
|
@@ -167,9 +173,9 @@ class AXGaugeChartComponent {
|
|
|
167
173
|
const innerRadius = radius - gaugeWidth;
|
|
168
174
|
const outerRadius = radius;
|
|
169
175
|
// Create gradient definitions
|
|
170
|
-
this.createGradients(svg, thresholds
|
|
176
|
+
this.createGradients(svg, thresholds);
|
|
171
177
|
// Draw the background arc
|
|
172
|
-
this.drawBackgroundArc(chartGroup, innerRadius, outerRadius, cornerRadius
|
|
178
|
+
this.drawBackgroundArc(chartGroup, innerRadius, outerRadius, cornerRadius);
|
|
173
179
|
// Draw the threshold arcs if thresholds exist
|
|
174
180
|
if (thresholds.length > 0) {
|
|
175
181
|
this.drawThresholds(chartGroup, innerRadius, outerRadius, minValue, maxValue, thresholds, cornerRadius);
|
|
@@ -177,11 +183,13 @@ class AXGaugeChartComponent {
|
|
|
177
183
|
// Draw tick marks
|
|
178
184
|
this.drawTicks(chartGroup, outerRadius, minValue, maxValue);
|
|
179
185
|
// Draw the dial/needle with animation
|
|
180
|
-
this.drawDial(chartGroup, radius, this.value(), minValue, maxValue,
|
|
186
|
+
this.drawDial(chartGroup, radius, this.value(), minValue, maxValue, animationDuration);
|
|
181
187
|
// Draw the value display (after the dial so it's on top)
|
|
182
188
|
if (showValue) {
|
|
183
|
-
this.drawValueDisplay(chartGroup, this.value(), label, radius
|
|
189
|
+
this.drawValueDisplay(chartGroup, this.value(), label, radius);
|
|
184
190
|
}
|
|
191
|
+
// Hide tooltip initially
|
|
192
|
+
this._tooltipVisible.set(false);
|
|
185
193
|
}
|
|
186
194
|
/**
|
|
187
195
|
* Updates the chart when options change
|
|
@@ -214,69 +222,17 @@ class AXGaugeChartComponent {
|
|
|
214
222
|
const angleInDegrees = this.radiansToDegrees(angle - Math.PI / 2);
|
|
215
223
|
svg
|
|
216
224
|
.select('.gauge-needle')
|
|
225
|
+
.attr('fill', 'rgb(var(--ax-comp-gauge-chart-needle-color))')
|
|
217
226
|
.transition()
|
|
218
227
|
.duration(animationDuration)
|
|
219
228
|
.ease(easingFunction)
|
|
220
229
|
.attr('transform', `rotate(${angleInDegrees})`);
|
|
221
230
|
}
|
|
222
231
|
/**
|
|
223
|
-
*
|
|
232
|
+
* Draw the background arc
|
|
224
233
|
*/
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
this.svgElement.remove();
|
|
228
|
-
this.svgElement = null;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
/**
|
|
232
|
-
* Creates gradient definitions for thresholds
|
|
233
|
-
*/
|
|
234
|
-
createGradients(svg, thresholds, baseColor, valueColor) {
|
|
235
|
-
const defs = svg.append('defs');
|
|
236
|
-
// Create a radial gradient for the background
|
|
237
|
-
const bgGradient = defs
|
|
238
|
-
.append('radialGradient')
|
|
239
|
-
.attr('id', 'gauge-bg-gradient')
|
|
240
|
-
.attr('cx', '50%')
|
|
241
|
-
.attr('cy', '50%')
|
|
242
|
-
.attr('r', '50%')
|
|
243
|
-
.attr('gradientUnits', 'userSpaceOnUse');
|
|
244
|
-
if (thresholds.length === 0) {
|
|
245
|
-
// Default gradient when no thresholds are provided
|
|
246
|
-
bgGradient
|
|
247
|
-
.append('stop')
|
|
248
|
-
.attr('offset', '0%')
|
|
249
|
-
.attr('stop-color', this.d3.color(valueColor)?.brighter(0.5).toString() || valueColor);
|
|
250
|
-
bgGradient.append('stop').attr('offset', '100%').attr('stop-color', valueColor);
|
|
251
|
-
}
|
|
252
|
-
else {
|
|
253
|
-
bgGradient
|
|
254
|
-
.append('stop')
|
|
255
|
-
.attr('offset', '0%')
|
|
256
|
-
.attr('stop-color', this.d3.color(baseColor)?.brighter(0.5).toString() || baseColor);
|
|
257
|
-
bgGradient.append('stop').attr('offset', '100%').attr('stop-color', baseColor);
|
|
258
|
-
// Create gradients for each threshold
|
|
259
|
-
thresholds.forEach((threshold, i) => {
|
|
260
|
-
const gradient = defs
|
|
261
|
-
.append('linearGradient')
|
|
262
|
-
.attr('id', `threshold-gradient-${i}`)
|
|
263
|
-
.attr('gradientUnits', 'userSpaceOnUse')
|
|
264
|
-
.attr('x1', '-1')
|
|
265
|
-
.attr('y1', '0')
|
|
266
|
-
.attr('x2', '1')
|
|
267
|
-
.attr('y2', '0');
|
|
268
|
-
gradient
|
|
269
|
-
.append('stop')
|
|
270
|
-
.attr('offset', '0%')
|
|
271
|
-
.attr('stop-color', this.d3.color(threshold.color)?.brighter(0.5).toString() || threshold.color);
|
|
272
|
-
gradient.append('stop').attr('offset', '100%').attr('stop-color', threshold.color);
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
/**
|
|
277
|
-
* Draws the background arc
|
|
278
|
-
*/
|
|
279
|
-
drawBackgroundArc(chartGroup, innerRadius, outerRadius, cornerRadius, backgroundColor) {
|
|
234
|
+
drawBackgroundArc(chartGroup, innerRadius, outerRadius, cornerRadius) {
|
|
235
|
+
// Create arc for the background
|
|
280
236
|
const backgroundArc = this.d3
|
|
281
237
|
.arc()
|
|
282
238
|
.innerRadius(innerRadius)
|
|
@@ -284,21 +240,37 @@ class AXGaugeChartComponent {
|
|
|
284
240
|
.startAngle(-Math.PI / 2) // Start from bottom (-90 degrees)
|
|
285
241
|
.endAngle(Math.PI / 2) // End at top (90 degrees)
|
|
286
242
|
.cornerRadius(cornerRadius);
|
|
287
|
-
|
|
243
|
+
// Draw background arc
|
|
244
|
+
const backgroundPath = chartGroup
|
|
288
245
|
.append('path')
|
|
289
|
-
.attr('d', backgroundArc
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
startAngle: -Math.PI / 2,
|
|
293
|
-
endAngle: Math.PI / 2,
|
|
294
|
-
}))
|
|
295
|
-
.attr('fill', backgroundColor === 'transparent' ? 'url(#gauge-bg-gradient)' : backgroundColor)
|
|
246
|
+
.attr('d', backgroundArc)
|
|
247
|
+
.attr('class', 'gauge-arc gauge-base')
|
|
248
|
+
.attr('fill', 'url(#gauge-bg-gradient)')
|
|
296
249
|
.attr('filter', 'drop-shadow(0px 2px 3px rgba(0,0,0,0.1))');
|
|
250
|
+
// Add tooltip for single arc if no thresholds
|
|
251
|
+
if (!this.effectiveOptions().thresholds || this.effectiveOptions().thresholds.length === 0) {
|
|
252
|
+
backgroundPath
|
|
253
|
+
.style('cursor', 'pointer')
|
|
254
|
+
.on('mouseenter', (event) => {
|
|
255
|
+
if (!this.effectiveOptions().showTooltip)
|
|
256
|
+
return;
|
|
257
|
+
this.showSingleRangeTooltip(event);
|
|
258
|
+
})
|
|
259
|
+
.on('mousemove', (event) => {
|
|
260
|
+
if (this._tooltipVisible()) {
|
|
261
|
+
this.updateTooltipPosition(event);
|
|
262
|
+
}
|
|
263
|
+
})
|
|
264
|
+
.on('mouseleave', () => {
|
|
265
|
+
this.hideTooltip();
|
|
266
|
+
});
|
|
267
|
+
}
|
|
297
268
|
}
|
|
298
269
|
/**
|
|
299
|
-
*
|
|
270
|
+
* Draw the thresholds arcs
|
|
300
271
|
*/
|
|
301
272
|
drawThresholds(chartGroup, innerRadius, outerRadius, minValue, maxValue, thresholds, cornerRadius) {
|
|
273
|
+
// Create arc generator
|
|
302
274
|
const arc = this.d3
|
|
303
275
|
.arc()
|
|
304
276
|
.innerRadius(innerRadius)
|
|
@@ -312,7 +284,10 @@ class AXGaugeChartComponent {
|
|
|
312
284
|
let previousEndAngle = this.scaleValueToColorAngle(minValue, minValue, maxValue);
|
|
313
285
|
sortedThresholds.forEach((threshold, i) => {
|
|
314
286
|
const endAngle = angles[i];
|
|
315
|
-
|
|
287
|
+
// Skip if end angle is not greater than start angle
|
|
288
|
+
if (endAngle <= previousEndAngle)
|
|
289
|
+
return;
|
|
290
|
+
const arcPath = chartGroup
|
|
316
291
|
.append('path')
|
|
317
292
|
.attr('d', arc({
|
|
318
293
|
innerRadius,
|
|
@@ -320,15 +295,34 @@ class AXGaugeChartComponent {
|
|
|
320
295
|
startAngle: previousEndAngle,
|
|
321
296
|
endAngle,
|
|
322
297
|
}))
|
|
323
|
-
.attr('fill', `url(#threshold-gradient-${i})`)
|
|
324
|
-
.attr('class', 'threshold-arc')
|
|
325
|
-
.attr('data-value', threshold.value)
|
|
298
|
+
.attr('fill', threshold.color || `url(#threshold-gradient-${i})`)
|
|
299
|
+
.attr('class', 'gauge-arc threshold-arc')
|
|
300
|
+
.attr('data-value', threshold.value)
|
|
301
|
+
.style('cursor', 'pointer');
|
|
302
|
+
// Add tooltip interaction
|
|
303
|
+
if (this.effectiveOptions().showTooltip) {
|
|
304
|
+
// Convert angles back to values for tooltip
|
|
305
|
+
const startValue = this.angleToValue(previousEndAngle, minValue, maxValue);
|
|
306
|
+
const endValue = threshold.value;
|
|
307
|
+
arcPath
|
|
308
|
+
.on('mouseenter', (event) => {
|
|
309
|
+
this.showThresholdTooltip(event, startValue, endValue, threshold.color, threshold.label);
|
|
310
|
+
})
|
|
311
|
+
.on('mousemove', (event) => {
|
|
312
|
+
if (this._tooltipVisible()) {
|
|
313
|
+
this.updateTooltipPosition(event);
|
|
314
|
+
}
|
|
315
|
+
})
|
|
316
|
+
.on('mouseleave', () => {
|
|
317
|
+
this.hideTooltip();
|
|
318
|
+
});
|
|
319
|
+
}
|
|
326
320
|
// Update the previous end angle for the next threshold
|
|
327
321
|
previousEndAngle = endAngle;
|
|
328
322
|
});
|
|
329
323
|
// Draw the last segment if the last threshold is less than the max value
|
|
330
324
|
if (previousEndAngle < Math.PI / 2) {
|
|
331
|
-
chartGroup
|
|
325
|
+
const lastArc = chartGroup
|
|
332
326
|
.append('path')
|
|
333
327
|
.attr('d', arc({
|
|
334
328
|
innerRadius,
|
|
@@ -337,7 +331,154 @@ class AXGaugeChartComponent {
|
|
|
337
331
|
endAngle: Math.PI / 2,
|
|
338
332
|
}))
|
|
339
333
|
.attr('fill', 'url(#gauge-bg-gradient)')
|
|
340
|
-
.attr('class', 'threshold-arc')
|
|
334
|
+
.attr('class', 'gauge-arc threshold-arc')
|
|
335
|
+
.style('cursor', 'pointer');
|
|
336
|
+
// Add tooltip for the last segment
|
|
337
|
+
if (this.effectiveOptions().showTooltip) {
|
|
338
|
+
const startValue = this.angleToValue(previousEndAngle, minValue, maxValue);
|
|
339
|
+
const endValue = maxValue;
|
|
340
|
+
const lastThreshold = thresholds[thresholds.length - 1];
|
|
341
|
+
lastArc
|
|
342
|
+
.on('mouseenter', (event) => {
|
|
343
|
+
this.showThresholdTooltip(event, startValue, endValue, lastThreshold.color, lastThreshold.label);
|
|
344
|
+
})
|
|
345
|
+
.on('mousemove', (event) => {
|
|
346
|
+
if (this._tooltipVisible()) {
|
|
347
|
+
this.updateTooltipPosition(event);
|
|
348
|
+
}
|
|
349
|
+
})
|
|
350
|
+
.on('mouseleave', () => {
|
|
351
|
+
this.hideTooltip();
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Shows tooltip for a threshold arc
|
|
358
|
+
*/
|
|
359
|
+
showThresholdTooltip(event, startValue, endValue, color, label) {
|
|
360
|
+
// Create tooltip data
|
|
361
|
+
this._tooltipData.set({
|
|
362
|
+
title: label || 'Range',
|
|
363
|
+
value: `${startValue.toLocaleString()} - ${endValue.toLocaleString()}`,
|
|
364
|
+
color: color,
|
|
365
|
+
});
|
|
366
|
+
// Show tooltip
|
|
367
|
+
this._tooltipVisible.set(true);
|
|
368
|
+
this.updateTooltipPosition(event);
|
|
369
|
+
this.cdr.detectChanges();
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Shows tooltip for the entire range when no thresholds are defined
|
|
373
|
+
*/
|
|
374
|
+
showSingleRangeTooltip(event) {
|
|
375
|
+
const options = this.effectiveOptions();
|
|
376
|
+
if (!options.showTooltip)
|
|
377
|
+
return;
|
|
378
|
+
this.updateTooltipPosition(event);
|
|
379
|
+
this._tooltipData.set({
|
|
380
|
+
title: options.label || 'Range',
|
|
381
|
+
value: `${options.minValue.toLocaleString()} - ${options.maxValue.toLocaleString()}`,
|
|
382
|
+
color: 'rgb(var(--ax-comp-gauge-chart-track-color))',
|
|
383
|
+
});
|
|
384
|
+
this._tooltipVisible.set(true);
|
|
385
|
+
this.cdr.detectChanges();
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Updates the tooltip position based on the mouse event
|
|
389
|
+
*/
|
|
390
|
+
updateTooltipPosition(event) {
|
|
391
|
+
const container = this.chartContainerEl().nativeElement.getBoundingClientRect();
|
|
392
|
+
const x = event.clientX - container.left;
|
|
393
|
+
const y = event.clientY - container.top;
|
|
394
|
+
// Get container dimensions to check if we're near the edge
|
|
395
|
+
const containerWidth = container.width;
|
|
396
|
+
const containerHeight = container.height;
|
|
397
|
+
// Tooltip dimensions approximation (can't get exact dimensions without rendering)
|
|
398
|
+
const tooltipWidth = 150; // Approximate width of tooltip
|
|
399
|
+
const tooltipHeight = 80; // Approximate height of tooltip
|
|
400
|
+
// Calculate position with edge detection
|
|
401
|
+
let tooltipX = x + 8; // fix overlap with cursor
|
|
402
|
+
const tooltipY = y - 12; // fix overlap with cursor
|
|
403
|
+
// Check if we're too close to the right edge
|
|
404
|
+
const rightEdgeDistance = containerWidth - x;
|
|
405
|
+
if (rightEdgeDistance < tooltipWidth) {
|
|
406
|
+
// Place tooltip to the left of the cursor with no gap
|
|
407
|
+
tooltipX = x - tooltipWidth + 70; // Overlap more with cursor position
|
|
408
|
+
}
|
|
409
|
+
// Check if we're too close to the bottom edge
|
|
410
|
+
// const bottomEdgeDistance = containerHeight - y;
|
|
411
|
+
// if (bottomEdgeDistance < tooltipHeight + 10) {
|
|
412
|
+
// // Move tooltip up if near bottom edge
|
|
413
|
+
// tooltipY = y - tooltipHeight;
|
|
414
|
+
// }
|
|
415
|
+
this._tooltipPosition.set({
|
|
416
|
+
x: tooltipX,
|
|
417
|
+
y: tooltipY,
|
|
418
|
+
});
|
|
419
|
+
this.cdr.detectChanges();
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Hides the tooltip
|
|
423
|
+
*/
|
|
424
|
+
hideTooltip() {
|
|
425
|
+
this._tooltipVisible.set(false);
|
|
426
|
+
this.cdr.detectChanges();
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Cleans up chart resources
|
|
430
|
+
*/
|
|
431
|
+
cleanupChart() {
|
|
432
|
+
if (this.svgElement) {
|
|
433
|
+
this.svgElement.remove();
|
|
434
|
+
this.svgElement = null;
|
|
435
|
+
}
|
|
436
|
+
this._tooltipVisible.set(false);
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Creates gradient definitions for thresholds
|
|
440
|
+
*/
|
|
441
|
+
createGradients(svg, thresholds) {
|
|
442
|
+
const defs = svg.append('defs');
|
|
443
|
+
// Create a radial gradient for the background
|
|
444
|
+
const bgGradient = defs
|
|
445
|
+
.append('radialGradient')
|
|
446
|
+
.attr('id', 'gauge-bg-gradient')
|
|
447
|
+
.attr('cx', '50%')
|
|
448
|
+
.attr('cy', '50%')
|
|
449
|
+
.attr('r', '50%')
|
|
450
|
+
.attr('gradientUnits', 'userSpaceOnUse');
|
|
451
|
+
// Get CSS variable for track color
|
|
452
|
+
if (thresholds.length === 0) {
|
|
453
|
+
// Default gradient when no thresholds are provided
|
|
454
|
+
bgGradient.append('stop').attr('offset', '0%').attr('stop-color', 'rgb(var(--ax-comp-gauge-chart-track-color))');
|
|
455
|
+
bgGradient
|
|
456
|
+
.append('stop')
|
|
457
|
+
.attr('offset', '100%')
|
|
458
|
+
.attr('stop-color', 'rgba(var(--ax-comp-gauge-chart-track-color), 0.8)');
|
|
459
|
+
}
|
|
460
|
+
else {
|
|
461
|
+
bgGradient.append('stop').attr('offset', '0%').attr('stop-color', 'rgb(var(--ax-comp-gauge-chart-track-color))');
|
|
462
|
+
bgGradient
|
|
463
|
+
.append('stop')
|
|
464
|
+
.attr('offset', '100%')
|
|
465
|
+
.attr('stop-color', 'rgba(var(--ax-comp-gauge-chart-track-color), 0.8)');
|
|
466
|
+
// Create gradients for each threshold
|
|
467
|
+
thresholds.forEach((threshold, i) => {
|
|
468
|
+
const gradient = defs
|
|
469
|
+
.append('linearGradient')
|
|
470
|
+
.attr('id', `threshold-gradient-${i}`)
|
|
471
|
+
.attr('gradientUnits', 'userSpaceOnUse')
|
|
472
|
+
.attr('x1', '-1')
|
|
473
|
+
.attr('y1', '0')
|
|
474
|
+
.attr('x2', '1')
|
|
475
|
+
.attr('y2', '0');
|
|
476
|
+
gradient
|
|
477
|
+
.append('stop')
|
|
478
|
+
.attr('offset', '0%')
|
|
479
|
+
.attr('stop-color', this.d3.color(threshold.color)?.brighter(0.5).toString() || threshold.color);
|
|
480
|
+
gradient.append('stop').attr('offset', '100%').attr('stop-color', threshold.color);
|
|
481
|
+
});
|
|
341
482
|
}
|
|
342
483
|
}
|
|
343
484
|
/**
|
|
@@ -375,7 +516,7 @@ class AXGaugeChartComponent {
|
|
|
375
516
|
.attr('y1', y1)
|
|
376
517
|
.attr('x2', x2)
|
|
377
518
|
.attr('y2', y2)
|
|
378
|
-
.attr('stroke', '
|
|
519
|
+
.attr('stroke', 'rgba(var(--ax-comp-gauge-chart-text-color), 0.5)')
|
|
379
520
|
.attr('stroke-width', 2);
|
|
380
521
|
// Add tick label
|
|
381
522
|
tickGroup
|
|
@@ -384,8 +525,7 @@ class AXGaugeChartComponent {
|
|
|
384
525
|
.attr('y', labelY)
|
|
385
526
|
.attr('text-anchor', 'middle')
|
|
386
527
|
.attr('dominant-baseline', 'middle')
|
|
387
|
-
.attr('fill', '
|
|
388
|
-
.attr('class', 'gauge-min-max')
|
|
528
|
+
.attr('fill', 'rgba(var(--ax-comp-gauge-chart-text-color), 0.7)')
|
|
389
529
|
.style('font-size', '12px')
|
|
390
530
|
.text(tick.toLocaleString());
|
|
391
531
|
});
|
|
@@ -393,37 +533,40 @@ class AXGaugeChartComponent {
|
|
|
393
533
|
/**
|
|
394
534
|
* Draws the value and label text in the center
|
|
395
535
|
*/
|
|
396
|
-
drawValueDisplay(chartGroup, value, label, radius
|
|
397
|
-
|
|
398
|
-
|
|
536
|
+
drawValueDisplay(chartGroup, value, label, radius) {
|
|
537
|
+
// Calculate appropriate font sizes based on chart dimensions
|
|
538
|
+
const chartContainerWidth = this.chartContainerEl().nativeElement.clientWidth;
|
|
539
|
+
const baseFontSize = Math.max(1.4, Math.min(2.4, chartContainerWidth / 200)); // Scale between 1.4rem and 2.4rem
|
|
540
|
+
const subTextFontSize = baseFontSize * 0.5;
|
|
541
|
+
// Create group for the value display
|
|
542
|
+
const valueGroup = chartGroup.append('g').attr('class', 'gauge-value-display').attr('text-anchor', 'middle');
|
|
543
|
+
// Add value display
|
|
399
544
|
valueGroup
|
|
400
545
|
.append('text')
|
|
401
|
-
.attr('x', 0)
|
|
402
|
-
.attr('y', radius * 0.3)
|
|
403
|
-
.attr('text-anchor', 'middle')
|
|
404
|
-
.attr('dominant-baseline', 'middle')
|
|
405
|
-
.attr('fill', valueColor)
|
|
406
546
|
.attr('class', 'gauge-value')
|
|
407
|
-
.
|
|
547
|
+
.attr('x', 0)
|
|
548
|
+
.attr('y', radius * 0.25)
|
|
549
|
+
.style('font-size', `${baseFontSize}rem`)
|
|
550
|
+
.style('font-weight', '600')
|
|
551
|
+
.style('fill', 'rgb(var(--ax-comp-gauge-chart-text-color))')
|
|
408
552
|
.text(value.toLocaleString());
|
|
409
|
-
//
|
|
553
|
+
// Add label display (if provided)
|
|
410
554
|
if (label) {
|
|
411
555
|
valueGroup
|
|
412
556
|
.append('text')
|
|
413
|
-
.attr('x', 0)
|
|
414
|
-
.attr('y', radius * 0.45)
|
|
415
|
-
.attr('text-anchor', 'middle')
|
|
416
|
-
.attr('dominant-baseline', 'middle')
|
|
417
|
-
.attr('fill', '#64748b')
|
|
418
557
|
.attr('class', 'gauge-label')
|
|
419
|
-
.
|
|
558
|
+
.attr('x', 0)
|
|
559
|
+
.attr('y', radius * 0.35)
|
|
560
|
+
.style('font-size', `${subTextFontSize}rem`)
|
|
561
|
+
.style('fill', 'rgb(var(--ax-comp-gauge-chart-text-color))')
|
|
562
|
+
.style('opacity', '0.8')
|
|
420
563
|
.text(label);
|
|
421
564
|
}
|
|
422
565
|
}
|
|
423
566
|
/**
|
|
424
567
|
* Draws the dial/needle pointing to the current value with animation
|
|
425
568
|
*/
|
|
426
|
-
drawDial(chartGroup, radius, value, minValue, maxValue,
|
|
569
|
+
drawDial(chartGroup, radius, value, minValue, maxValue, animationDuration) {
|
|
427
570
|
// Clamp value to min/max range
|
|
428
571
|
const clampedValue = Math.max(minValue, Math.min(maxValue, value));
|
|
429
572
|
// Calculate angle for needle based on value
|
|
@@ -435,8 +578,8 @@ class AXGaugeChartComponent {
|
|
|
435
578
|
const needlePath = dialGroup
|
|
436
579
|
.append('path')
|
|
437
580
|
.attr('d', this.createNeedlePath(radius))
|
|
438
|
-
.attr('fill', valueColor)
|
|
439
581
|
.attr('class', 'gauge-needle')
|
|
582
|
+
.attr('fill', 'rgb(var(--ax-comp-gauge-chart-needle-color))')
|
|
440
583
|
.attr('transform', `rotate(${this.radiansToDegrees(-Math.PI)})`); // Start at -180 degrees (left)
|
|
441
584
|
// Add a center circle
|
|
442
585
|
dialGroup
|
|
@@ -444,7 +587,7 @@ class AXGaugeChartComponent {
|
|
|
444
587
|
.attr('cx', 0)
|
|
445
588
|
.attr('cy', 0)
|
|
446
589
|
.attr('r', radius * 0.08)
|
|
447
|
-
.attr('fill',
|
|
590
|
+
.attr('fill', 'rgb(var(--ax-comp-gauge-chart-needle-color))')
|
|
448
591
|
.attr('stroke', '#fff')
|
|
449
592
|
.attr('stroke-width', 2);
|
|
450
593
|
// Add a smaller white center
|
|
@@ -514,6 +657,14 @@ class AXGaugeChartComponent {
|
|
|
514
657
|
const angleRange = Math.PI; // 180 degrees in radians
|
|
515
658
|
return ((value - min) / valueRange) * angleRange - Math.PI / 2;
|
|
516
659
|
}
|
|
660
|
+
/**
|
|
661
|
+
* Converts an angle back to a value
|
|
662
|
+
*/
|
|
663
|
+
angleToValue(angle, min, max) {
|
|
664
|
+
const valueRange = max - min;
|
|
665
|
+
const angleRange = Math.PI;
|
|
666
|
+
return min + ((angle + Math.PI / 2) / angleRange) * valueRange;
|
|
667
|
+
}
|
|
517
668
|
/**
|
|
518
669
|
* Converts radians to degrees
|
|
519
670
|
*/
|
|
@@ -521,11 +672,11 @@ class AXGaugeChartComponent {
|
|
|
521
672
|
return radians * (180 / Math.PI);
|
|
522
673
|
}
|
|
523
674
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXGaugeChartComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
524
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "19.2.10", type: AXGaugeChartComponent, isStandalone: true, selector: "ax-gauge-chart", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "chartContainerEl", first: true, predicate: ["chartContainer"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"ax-gauge-chart\" #chartContainer></div>\n", styles: ["
|
|
675
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "19.2.10", type: AXGaugeChartComponent, isStandalone: true, selector: "ax-gauge-chart", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "chartContainerEl", first: true, predicate: ["chartContainer"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"ax-gauge-chart\" #chartContainer></div>\n<ax-chart-tooltip [data]=\"tooltipData()\" [position]=\"tooltipPosition()\" [visible]=\"tooltipVisible()\"></ax-chart-tooltip>\n", styles: ["ax-gauge-chart{display:block;width:100%;height:100%;min-height:200px;--ax-comp-gauge-chart-bg-color: var(--ax-sys-color-lightest-surface);--ax-comp-gauge-chart-text-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-gauge-chart-track-color: var(--ax-sys-color-dark-surface);--ax-comp-gauge-chart-needle-color: var(--ax-sys-color-primary-500)}.ax-gauge-chart{position:relative;width:100%;height:100%;display:flex;align-items:center;justify-content:center;overflow:visible;color:rgb(var(--ax-comp-gauge-chart-text-color));background-color:rgb(var(--ax-comp-gauge-chart-bg-color));border-radius:.5rem}svg{display:block;width:100%;height:100%;max-width:100%;max-height:100%;overflow:visible}.ax-gauge-chart-no-data-message{display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:1rem;width:100%;height:100%;color:rgb(var(--ax-comp-gauge-chart-text-color));background-color:rgb(var(--ax-comp-gauge-chart-bg-color))}.ax-gauge-chart-no-data-icon{margin-bottom:.75rem;color:rgba(var(--ax-comp-gauge-chart-text-color),.6)}.ax-gauge-chart-no-data-text{font-weight:600;color:rgb(var(--ax-comp-gauge-chart-text-color))}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: AXChartTooltipComponent, selector: "ax-chart-tooltip", inputs: ["data", "position", "visible", "showPercentage", "style"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
525
676
|
}
|
|
526
677
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.10", ngImport: i0, type: AXGaugeChartComponent, decorators: [{
|
|
527
678
|
type: Component,
|
|
528
|
-
args: [{ selector: 'ax-gauge-chart',
|
|
679
|
+
args: [{ selector: 'ax-gauge-chart', encapsulation: ViewEncapsulation.None, imports: [CommonModule, AXChartTooltipComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"ax-gauge-chart\" #chartContainer></div>\n<ax-chart-tooltip [data]=\"tooltipData()\" [position]=\"tooltipPosition()\" [visible]=\"tooltipVisible()\"></ax-chart-tooltip>\n", styles: ["ax-gauge-chart{display:block;width:100%;height:100%;min-height:200px;--ax-comp-gauge-chart-bg-color: var(--ax-sys-color-lightest-surface);--ax-comp-gauge-chart-text-color: var(--ax-sys-color-on-lightest-surface);--ax-comp-gauge-chart-track-color: var(--ax-sys-color-dark-surface);--ax-comp-gauge-chart-needle-color: var(--ax-sys-color-primary-500)}.ax-gauge-chart{position:relative;width:100%;height:100%;display:flex;align-items:center;justify-content:center;overflow:visible;color:rgb(var(--ax-comp-gauge-chart-text-color));background-color:rgb(var(--ax-comp-gauge-chart-bg-color));border-radius:.5rem}svg{display:block;width:100%;height:100%;max-width:100%;max-height:100%;overflow:visible}.ax-gauge-chart-no-data-message{display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:1rem;width:100%;height:100%;color:rgb(var(--ax-comp-gauge-chart-text-color));background-color:rgb(var(--ax-comp-gauge-chart-bg-color))}.ax-gauge-chart-no-data-icon{margin-bottom:.75rem;color:rgba(var(--ax-comp-gauge-chart-text-color),.6)}.ax-gauge-chart-no-data-text{font-weight:600;color:rgb(var(--ax-comp-gauge-chart-text-color))}\n"] }]
|
|
529
680
|
}], ctorParameters: () => [] });
|
|
530
681
|
|
|
531
682
|
/**
|