cats-charts 0.0.83 → 0.0.84
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/fesm2022/cats-charts.mjs +159 -19
- package/fesm2022/cats-charts.mjs.map +1 -1
- package/index.d.ts +30 -1
- package/package.json +1 -1
package/fesm2022/cats-charts.mjs
CHANGED
|
@@ -5073,6 +5073,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.16", ngImpo
|
|
|
5073
5073
|
}] } });
|
|
5074
5074
|
|
|
5075
5075
|
echarts.use([GaugeChart$1, GraphicComponent, TooltipComponent, TitleComponent, CanvasRenderer]);
|
|
5076
|
+
const DEFAULT_GAUGE_CENTER = ['50%', '84%'];
|
|
5077
|
+
const DEFAULT_GAUGE_RADIUS = '140%';
|
|
5076
5078
|
class GaugeChart {
|
|
5077
5079
|
config = {};
|
|
5078
5080
|
theme;
|
|
@@ -5080,6 +5082,7 @@ class GaugeChart {
|
|
|
5080
5082
|
chartEvent = new EventEmitter();
|
|
5081
5083
|
chartOption = {};
|
|
5082
5084
|
chartInstance;
|
|
5085
|
+
chartResizeObserver;
|
|
5083
5086
|
previousReactiveSignature = '';
|
|
5084
5087
|
ngOnChanges(changes) {
|
|
5085
5088
|
if (changes['theme'] && this.chartInstance) {
|
|
@@ -5099,7 +5102,7 @@ class GaugeChart {
|
|
|
5099
5102
|
}
|
|
5100
5103
|
buildOption() {
|
|
5101
5104
|
if (this.config.option) {
|
|
5102
|
-
this.chartOption = this.config.option;
|
|
5105
|
+
this.chartOption = this.applyTargetPointerSizing(this.config.option);
|
|
5103
5106
|
if (this.chartInstance) {
|
|
5104
5107
|
this.chartInstance.setOption(this.chartOption, true);
|
|
5105
5108
|
}
|
|
@@ -5146,8 +5149,8 @@ class GaugeChart {
|
|
|
5146
5149
|
max: displayMax,
|
|
5147
5150
|
startAngle: 180,
|
|
5148
5151
|
endAngle: 0,
|
|
5149
|
-
center: [
|
|
5150
|
-
radius:
|
|
5152
|
+
center: [...DEFAULT_GAUGE_CENTER],
|
|
5153
|
+
radius: DEFAULT_GAUGE_RADIUS,
|
|
5151
5154
|
splitNumber,
|
|
5152
5155
|
progress: {
|
|
5153
5156
|
show: progressShow,
|
|
@@ -5170,6 +5173,9 @@ class GaugeChart {
|
|
|
5170
5173
|
pointer: {
|
|
5171
5174
|
show: false,
|
|
5172
5175
|
},
|
|
5176
|
+
title: {
|
|
5177
|
+
show: false,
|
|
5178
|
+
},
|
|
5173
5179
|
anchor: {
|
|
5174
5180
|
show: false,
|
|
5175
5181
|
},
|
|
@@ -5196,7 +5202,18 @@ class GaugeChart {
|
|
|
5196
5202
|
distance: this.config.midValuePosition === 'inside' ? -36 : 10,
|
|
5197
5203
|
color: '#111827',
|
|
5198
5204
|
fontSize: 10,
|
|
5205
|
+
align: 'center',
|
|
5206
|
+
verticalAlign: 'middle',
|
|
5199
5207
|
formatter: (value) => this.axisLabelFormatter(value, displayMin, displayMax, showMidValue),
|
|
5208
|
+
rich: {
|
|
5209
|
+
gaugeLabel: {
|
|
5210
|
+
padding: showMidValue ? [0, 0, 0, 0] : [16, 0, 0, 0],
|
|
5211
|
+
color: '#434a51',
|
|
5212
|
+
fontWeight: 600,
|
|
5213
|
+
align: 'center',
|
|
5214
|
+
verticalAlign: 'middle',
|
|
5215
|
+
},
|
|
5216
|
+
},
|
|
5200
5217
|
},
|
|
5201
5218
|
detail: {
|
|
5202
5219
|
offsetCenter: [0, '-10%'],
|
|
@@ -5227,6 +5244,7 @@ class GaugeChart {
|
|
|
5227
5244
|
gaugeSeries['min'] = displayMin;
|
|
5228
5245
|
gaugeSeries['max'] = displayMax;
|
|
5229
5246
|
gaugeSeries['data'] = [{ value: displayValue, name: this.config.name ?? 'Value' }];
|
|
5247
|
+
const targetPointer = this.calculateTargetPointerGeometry(gaugeSeries);
|
|
5230
5248
|
const generatedChartOption = {
|
|
5231
5249
|
backgroundColor: this.config.backgroundColor ?? 'transparent',
|
|
5232
5250
|
title: {
|
|
@@ -5251,8 +5269,8 @@ class GaugeChart {
|
|
|
5251
5269
|
max: displayMax,
|
|
5252
5270
|
startAngle: 180,
|
|
5253
5271
|
endAngle: 0,
|
|
5254
|
-
center: ['
|
|
5255
|
-
radius: '
|
|
5272
|
+
center: gaugeSeries['center'] ?? [...DEFAULT_GAUGE_CENTER],
|
|
5273
|
+
radius: gaugeSeries['radius'] ?? DEFAULT_GAUGE_RADIUS,
|
|
5256
5274
|
axisLine: {
|
|
5257
5275
|
lineStyle: {
|
|
5258
5276
|
width: 0,
|
|
@@ -5262,9 +5280,7 @@ class GaugeChart {
|
|
|
5262
5280
|
pointer: {
|
|
5263
5281
|
show: true,
|
|
5264
5282
|
icon: 'rect',
|
|
5265
|
-
|
|
5266
|
-
length: '18%',
|
|
5267
|
-
offsetCenter: [0, '-91%'],
|
|
5283
|
+
...targetPointer,
|
|
5268
5284
|
itemStyle: {
|
|
5269
5285
|
color: '#374151',
|
|
5270
5286
|
},
|
|
@@ -5273,6 +5289,7 @@ class GaugeChart {
|
|
|
5273
5289
|
axisTick: { show: false },
|
|
5274
5290
|
splitLine: { show: false },
|
|
5275
5291
|
axisLabel: { show: false },
|
|
5292
|
+
title: { show: false },
|
|
5276
5293
|
detail: { show: false },
|
|
5277
5294
|
data: [{ value: displayTarget, name: 'Target' }],
|
|
5278
5295
|
},
|
|
@@ -5289,6 +5306,57 @@ class GaugeChart {
|
|
|
5289
5306
|
this.chartInstance = ec;
|
|
5290
5307
|
this.buildOption();
|
|
5291
5308
|
this.bindEvents();
|
|
5309
|
+
this.observeChartResize();
|
|
5310
|
+
}
|
|
5311
|
+
/**
|
|
5312
|
+
* Calculates all target-marker geometry in one place. A gauge pointer icon
|
|
5313
|
+
* is drawn from `offsetCenter - length` through `offsetCenter`, so the
|
|
5314
|
+
* offset must account for the inner edge while length covers the band and
|
|
5315
|
+
* both edge overflows. Use the widest band because progress and axisLine
|
|
5316
|
+
* may be configured independently.
|
|
5317
|
+
*/
|
|
5318
|
+
calculateTargetPointerGeometry(bandSeries) {
|
|
5319
|
+
const barWidth = this.resolveGaugeWidth(bandSeries, 38, true);
|
|
5320
|
+
const edgeOverflow = Math.max(6, Math.round(barWidth * 0.25));
|
|
5321
|
+
const pointerLength = barWidth + edgeOverflow * 2;
|
|
5322
|
+
const radius = this.resolveGaugeRadius(bandSeries['radius']);
|
|
5323
|
+
if (radius === undefined) {
|
|
5324
|
+
return {
|
|
5325
|
+
width: 10,
|
|
5326
|
+
length: pointerLength,
|
|
5327
|
+
offsetCenter: [0, '-91%'],
|
|
5328
|
+
};
|
|
5329
|
+
}
|
|
5330
|
+
return {
|
|
5331
|
+
width: 10,
|
|
5332
|
+
length: pointerLength,
|
|
5333
|
+
offsetCenter: [0, -(radius - barWidth - edgeOverflow)],
|
|
5334
|
+
};
|
|
5335
|
+
}
|
|
5336
|
+
resolveGaugeRadius(radiusOption) {
|
|
5337
|
+
if (!this.chartInstance)
|
|
5338
|
+
return undefined;
|
|
5339
|
+
const radius = this.toFiniteNumber(radiusOption);
|
|
5340
|
+
if (radius !== undefined)
|
|
5341
|
+
return radius;
|
|
5342
|
+
if (typeof radiusOption !== 'string' || !radiusOption.endsWith('%'))
|
|
5343
|
+
return undefined;
|
|
5344
|
+
const percentage = this.toFiniteNumber(radiusOption.slice(0, -1));
|
|
5345
|
+
if (percentage === undefined)
|
|
5346
|
+
return undefined;
|
|
5347
|
+
return (Math.min(this.chartInstance.getWidth(), this.chartInstance.getHeight()) / 2) * (percentage / 100);
|
|
5348
|
+
}
|
|
5349
|
+
observeChartResize() {
|
|
5350
|
+
if (typeof ResizeObserver === 'undefined' || !this.chartInstance)
|
|
5351
|
+
return;
|
|
5352
|
+
this.chartResizeObserver?.disconnect();
|
|
5353
|
+
this.chartResizeObserver = new ResizeObserver(() => {
|
|
5354
|
+
if (this.chartInstance.isDisposed())
|
|
5355
|
+
return;
|
|
5356
|
+
this.chartInstance.resize();
|
|
5357
|
+
this.buildOption();
|
|
5358
|
+
});
|
|
5359
|
+
this.chartResizeObserver.observe(this.chartInstance.getDom());
|
|
5292
5360
|
}
|
|
5293
5361
|
bindEvents() {
|
|
5294
5362
|
if (!this.chartInstance)
|
|
@@ -5312,6 +5380,42 @@ class GaugeChart {
|
|
|
5312
5380
|
return undefined;
|
|
5313
5381
|
return series.find((seriesOption) => seriesOption?.type === 'gauge') ?? series[0];
|
|
5314
5382
|
}
|
|
5383
|
+
/**
|
|
5384
|
+
* Custom ECharts options can provide a second, transparent gauge series for
|
|
5385
|
+
* the target marker. Apply the same automatic marker sizing used by the
|
|
5386
|
+
* generated option without changing any of the caller's other settings.
|
|
5387
|
+
*/
|
|
5388
|
+
applyTargetPointerSizing(option) {
|
|
5389
|
+
if (!Array.isArray(option['series']))
|
|
5390
|
+
return option;
|
|
5391
|
+
const bandSeries = option['series'].find((seriesOption) => seriesOption?.['type'] === 'gauge' && !this.isTargetPointerSeries(seriesOption));
|
|
5392
|
+
if (!bandSeries)
|
|
5393
|
+
return option;
|
|
5394
|
+
const targetPointer = this.calculateTargetPointerGeometry(bandSeries);
|
|
5395
|
+
let changed = false;
|
|
5396
|
+
const series = option['series'].map((seriesOption) => {
|
|
5397
|
+
if (!this.isTargetPointerSeries(seriesOption))
|
|
5398
|
+
return seriesOption;
|
|
5399
|
+
changed = true;
|
|
5400
|
+
return {
|
|
5401
|
+
...seriesOption,
|
|
5402
|
+
center: bandSeries['center'] ?? seriesOption['center'] ?? [...DEFAULT_GAUGE_CENTER],
|
|
5403
|
+
radius: bandSeries['radius'] ?? seriesOption['radius'] ?? DEFAULT_GAUGE_RADIUS,
|
|
5404
|
+
pointer: {
|
|
5405
|
+
...seriesOption['pointer'],
|
|
5406
|
+
...targetPointer,
|
|
5407
|
+
},
|
|
5408
|
+
};
|
|
5409
|
+
});
|
|
5410
|
+
return changed ? { ...option, series } : option;
|
|
5411
|
+
}
|
|
5412
|
+
isTargetPointerSeries(seriesOption) {
|
|
5413
|
+
const axisLineWidth = this.toFiniteNumber(seriesOption?.['axisLine']?.['lineStyle']?.['width']);
|
|
5414
|
+
return (seriesOption?.['type'] === 'gauge' &&
|
|
5415
|
+
seriesOption?.['pointer']?.['show'] === true &&
|
|
5416
|
+
seriesOption?.['progress']?.['show'] !== true &&
|
|
5417
|
+
(seriesOption?.['axisLine']?.['show'] === false || axisLineWidth === 0));
|
|
5418
|
+
}
|
|
5315
5419
|
resolveStyleSeriesOption(style) {
|
|
5316
5420
|
if (!style)
|
|
5317
5421
|
return {};
|
|
@@ -5326,10 +5430,16 @@ class GaugeChart {
|
|
|
5326
5430
|
...(style.anchor ? { anchor: style.anchor } : {}),
|
|
5327
5431
|
};
|
|
5328
5432
|
}
|
|
5329
|
-
resolveGaugeWidth(seriesOption, fallback) {
|
|
5433
|
+
resolveGaugeWidth(seriesOption, fallback, visibleOnly = false) {
|
|
5330
5434
|
const axisLineWidth = this.toFiniteNumber(seriesOption?.['axisLine']?.['lineStyle']?.['width']);
|
|
5331
5435
|
const progressWidth = this.toFiniteNumber(seriesOption?.['progress']?.['width']);
|
|
5332
|
-
|
|
5436
|
+
const configuredWidths = [
|
|
5437
|
+
...(!visibleOnly || seriesOption?.['axisLine']?.['show'] !== false ? [axisLineWidth] : []),
|
|
5438
|
+
...(!visibleOnly || seriesOption?.['progress']?.['show'] === true ? [progressWidth] : []),
|
|
5439
|
+
].filter((width) => width !== undefined);
|
|
5440
|
+
// Cover whichever configured band is thicker. Fall back only when neither
|
|
5441
|
+
// width is supplied, so a deliberately thin gauge is not enlarged.
|
|
5442
|
+
return this.clamp(configuredWidths.length ? Math.max(...configuredWidths) : fallback, 1, 80);
|
|
5333
5443
|
}
|
|
5334
5444
|
resolveProgressVisibility(seriesOption, conditionalEnabled) {
|
|
5335
5445
|
const configuredProgressShow = seriesOption?.['progress']?.['show'];
|
|
@@ -5380,9 +5490,11 @@ class GaugeChart {
|
|
|
5380
5490
|
const rounded = this.config.midValueType === 'percentage' ? Math.round(value) : this.niceNumber(value);
|
|
5381
5491
|
if (!showMidValue && value !== min && value !== max)
|
|
5382
5492
|
return '';
|
|
5383
|
-
|
|
5493
|
+
const label = this.config.midValueType === 'percentage'
|
|
5384
5494
|
? `${rounded}%`
|
|
5385
5495
|
: this.formatNumber(rounded, this.config.style?.compactNumber !== false, 0);
|
|
5496
|
+
const endpointOffset = showMidValue ? '' : '\n';
|
|
5497
|
+
return `{gaugeLabel|${endpointOffset}${label}}`;
|
|
5386
5498
|
}
|
|
5387
5499
|
resolveMetricValue() {
|
|
5388
5500
|
const configured = this.toFiniteNumber(this.config.value);
|
|
@@ -5408,7 +5520,7 @@ class GaugeChart {
|
|
|
5408
5520
|
const conditional = this.config.style?.conditionalFormatting;
|
|
5409
5521
|
if (conditional?.enabled) {
|
|
5410
5522
|
return conditional.type === 'gradient'
|
|
5411
|
-
? this.resolveGradientStops()
|
|
5523
|
+
? this.resolveGradientStops(min, max)
|
|
5412
5524
|
: this.resolveRuleStops(min, max, conditional.rules ?? []);
|
|
5413
5525
|
}
|
|
5414
5526
|
const barColor = this.config.style?.barColor;
|
|
@@ -5446,7 +5558,7 @@ class GaugeChart {
|
|
|
5446
5558
|
return this.withOpacity(matchedRule.color, matchedRule.opacity ?? 100);
|
|
5447
5559
|
}
|
|
5448
5560
|
if (conditional?.enabled && conditional.type === 'gradient') {
|
|
5449
|
-
const stops = this.resolveGradientStops();
|
|
5561
|
+
const stops = this.resolveGradientStops(min, max);
|
|
5450
5562
|
const ratio = this.clamp((value - min) / (max - min || 1), 0, 1);
|
|
5451
5563
|
const stop = stops.find(([position]) => ratio <= position) ?? stops[stops.length - 1];
|
|
5452
5564
|
if (stop)
|
|
@@ -5502,7 +5614,17 @@ class GaugeChart {
|
|
|
5502
5614
|
const secondValue = this.ruleValueToNumber(rule.valueTo, rule.unit, min, max);
|
|
5503
5615
|
if (firstValue === undefined)
|
|
5504
5616
|
return false;
|
|
5505
|
-
|
|
5617
|
+
const firstMatches = this.matchesRuleCondition(value, firstValue, rule.operator, secondValue);
|
|
5618
|
+
if (!firstMatches)
|
|
5619
|
+
return false;
|
|
5620
|
+
if (rule.secondCondition) {
|
|
5621
|
+
const second = this.ruleValueToNumber(rule.secondCondition.value, rule.secondCondition.unit, min, max);
|
|
5622
|
+
return second !== undefined && this.matchesRuleCondition(value, second, rule.secondCondition.operator);
|
|
5623
|
+
}
|
|
5624
|
+
return true;
|
|
5625
|
+
}
|
|
5626
|
+
matchesRuleCondition(value, firstValue, operator, secondValue) {
|
|
5627
|
+
switch (operator) {
|
|
5506
5628
|
case '>=':
|
|
5507
5629
|
return value >= firstValue;
|
|
5508
5630
|
case '>':
|
|
@@ -5519,7 +5641,7 @@ class GaugeChart {
|
|
|
5519
5641
|
return false;
|
|
5520
5642
|
}
|
|
5521
5643
|
}
|
|
5522
|
-
resolveGradientStops() {
|
|
5644
|
+
resolveGradientStops(min, max) {
|
|
5523
5645
|
const points = [...(this.config.style?.conditionalFormatting?.gradientPoints ?? [])]
|
|
5524
5646
|
.sort((a, b) => a.position - b.position);
|
|
5525
5647
|
const normalized = points.length
|
|
@@ -5532,10 +5654,27 @@ class GaugeChart {
|
|
|
5532
5654
|
const stylePoints = this.config.style?.conditionalFormatting?.reverseGradient
|
|
5533
5655
|
? [...normalized].reverse()
|
|
5534
5656
|
: normalized;
|
|
5535
|
-
|
|
5536
|
-
this.
|
|
5537
|
-
|
|
5538
|
-
|
|
5657
|
+
const stops = normalized.map((point, index) => {
|
|
5658
|
+
const from = this.gradientValueToRatio(point.valueFrom, point.unit, min ?? 0, max ?? 1);
|
|
5659
|
+
const position = from ?? this.clamp(point.position / 100, 0, 1);
|
|
5660
|
+
return [
|
|
5661
|
+
position,
|
|
5662
|
+
this.withOpacity(stylePoints[index]?.color ?? this.config.colors?.[index] ?? '#4378ea', stylePoints[index]?.opacity ?? 100),
|
|
5663
|
+
];
|
|
5664
|
+
});
|
|
5665
|
+
normalized.forEach((point, index) => {
|
|
5666
|
+
const to = this.gradientValueToRatio(point.valueTo, point.unit, min ?? 0, max ?? 1);
|
|
5667
|
+
if (to !== undefined && stops[index])
|
|
5668
|
+
stops[index][0] = Math.max(stops[index][0], to);
|
|
5669
|
+
});
|
|
5670
|
+
return stops.sort((a, b) => a[0] - b[0]);
|
|
5671
|
+
}
|
|
5672
|
+
gradientValueToRatio(value, unit, min, max) {
|
|
5673
|
+
const numeric = this.toFiniteNumber(value);
|
|
5674
|
+
if (numeric === undefined)
|
|
5675
|
+
return undefined;
|
|
5676
|
+
const actual = unit === 'percentage' ? min + (this.clamp(numeric, 0, 100) / 100) * (max - min) : numeric;
|
|
5677
|
+
return this.clamp((actual - min) / ((max - min) || 1), 0, 1);
|
|
5539
5678
|
}
|
|
5540
5679
|
ruleValueToNumber(value, unit, min, max) {
|
|
5541
5680
|
const numericValue = this.toFiniteNumber(value);
|
|
@@ -5649,6 +5788,7 @@ class GaugeChart {
|
|
|
5649
5788
|
return Math.min(Math.max(value, min), max);
|
|
5650
5789
|
}
|
|
5651
5790
|
ngOnDestroy() {
|
|
5791
|
+
this.chartResizeObserver?.disconnect();
|
|
5652
5792
|
if (this.chartInstance) {
|
|
5653
5793
|
this.chartInstance.off();
|
|
5654
5794
|
this.chartInstance.dispose();
|