Browse Source

fix(ui): line chart type safety

pull/6277/head
KenTandrian 7 days ago
parent
commit
9d85808cdd
  1. 57
      libs/ui/src/lib/line-chart/line-chart.component.ts

57
libs/ui/src/lib/line-chart/line-chart.component.ts

@ -1,6 +1,5 @@
import { import {
getTooltipOptions, getTooltipOptions,
getTooltipPositionerMapTop,
getVerticalHoverLinePlugin getVerticalHoverLinePlugin
} from '@ghostfolio/common/chart-helper'; } from '@ghostfolio/common/chart-helper';
import { primaryColorRgb, secondaryColorRgb } from '@ghostfolio/common/config'; import { primaryColorRgb, secondaryColorRgb } from '@ghostfolio/common/config';
@ -19,12 +18,14 @@ import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
ChangeDetectorRef, ChangeDetectorRef,
Component, Component,
type ElementRef,
Input, Input,
OnChanges, OnChanges,
OnDestroy, OnDestroy,
ViewChild ViewChild
} from '@angular/core'; } from '@angular/core';
import { import {
type AnimationsSpec,
Chart, Chart,
Filler, Filler,
LinearScale, LinearScale,
@ -33,11 +34,13 @@ import {
PointElement, PointElement,
TimeScale, TimeScale,
Tooltip, Tooltip,
TooltipPosition type TooltipOptions
} from 'chart.js'; } from 'chart.js';
import 'chartjs-adapter-date-fns'; import 'chartjs-adapter-date-fns';
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
import { registerChartConfiguration } from '../chart';
@Component({ @Component({
changeDetection: ChangeDetectionStrategy.OnPush, changeDetection: ChangeDetectionStrategy.OnPush,
imports: [CommonModule, NgxSkeletonLoaderModule], imports: [CommonModule, NgxSkeletonLoaderModule],
@ -67,7 +70,7 @@ export class GfLineChartComponent
@Input() yMin: number; @Input() yMin: number;
@Input() yMinLabel: string; @Input() yMinLabel: string;
@ViewChild('chartCanvas') chartCanvas; @ViewChild('chartCanvas') chartCanvas: ElementRef<HTMLCanvasElement>;
public chart: Chart<'line'>; public chart: Chart<'line'>;
public isLoading = true; public isLoading = true;
@ -85,8 +88,7 @@ export class GfLineChartComponent
Tooltip Tooltip
); );
Tooltip.positioners['top'] = (_elements, position: TooltipPosition) => registerChartConfiguration();
getTooltipPositionerMapTop(this.chart, position);
} }
public ngAfterViewInit() { public ngAfterViewInit() {
@ -117,9 +119,9 @@ export class GfLineChartComponent
private initialize() { private initialize() {
this.isLoading = true; this.isLoading = true;
const benchmarkPrices = []; const benchmarkPrices: number[] = [];
const labels: string[] = []; const labels: string[] = [];
const marketPrices = []; const marketPrices: number[] = [];
this.historicalDataItems?.forEach((historicalDataItem, index) => { this.historicalDataItems?.forEach((historicalDataItem, index) => {
benchmarkPrices.push(this.benchmarkDataItems?.[index]?.value); benchmarkPrices.push(this.benchmarkDataItems?.[index]?.value);
@ -129,11 +131,14 @@ export class GfLineChartComponent
const gradient = this.chartCanvas?.nativeElement const gradient = this.chartCanvas?.nativeElement
?.getContext('2d') ?.getContext('2d')
.createLinearGradient( ?.createLinearGradient(
0, 0,
0, 0,
0, 0,
(this.chartCanvas.nativeElement.parentNode.offsetHeight * 4) / 5 ((this.chartCanvas.nativeElement.parentNode as HTMLElement)
.offsetHeight *
4) /
5
); );
if (gradient && this.showGradient) { if (gradient && this.showGradient) {
@ -169,27 +174,25 @@ export class GfLineChartComponent
}; };
if (this.chartCanvas) { if (this.chartCanvas) {
const animations = {
x: this.getAnimationConfigurationForAxis({ labels, axis: 'x' }),
y: this.getAnimationConfigurationForAxis({ labels, axis: 'y' })
};
if (this.chart) { if (this.chart) {
this.chart.data = data; this.chart.data = data;
this.chart.options.plugins ??= {};
this.chart.options.plugins.tooltip = this.chart.options.plugins.tooltip =
this.getTooltipPluginConfiguration() as unknown; this.getTooltipPluginConfiguration();
this.chart.options.animation = this.chart.options.animations = this.isAnimated
this.isAnimated && ? animations
({ : undefined;
x: this.getAnimationConfigurationForAxis({ labels, axis: 'x' }),
y: this.getAnimationConfigurationForAxis({ labels, axis: 'y' })
} as unknown);
this.chart.update(); this.chart.update();
} else { } else {
this.chart = new Chart(this.chartCanvas.nativeElement, { this.chart = new Chart(this.chartCanvas.nativeElement, {
data, data,
options: { options: {
animation: animations: this.isAnimated ? animations : undefined,
this.isAnimated &&
({
x: this.getAnimationConfigurationForAxis({ labels, axis: 'x' }),
y: this.getAnimationConfigurationForAxis({ labels, axis: 'y' })
} as unknown),
aspectRatio: 16 / 9, aspectRatio: 16 / 9,
elements: { elements: {
point: { point: {
@ -208,7 +211,7 @@ export class GfLineChartComponent
verticalHoverLine: { verticalHoverLine: {
color: `rgba(${getTextColor(this.colorScheme)}, 0.1)` color: `rgba(${getTextColor(this.colorScheme)}, 0.1)`
} }
} as unknown, },
scales: { scales: {
x: { x: {
border: { border: {
@ -298,7 +301,7 @@ export class GfLineChartComponent
}: { }: {
axis: 'x' | 'y'; axis: 'x' | 'y';
labels: string[]; labels: string[];
}) { }): Partial<AnimationsSpec<'line'>[string]> {
const delayBetweenPoints = this.ANIMATION_DURATION / labels.length; const delayBetweenPoints = this.ANIMATION_DURATION / labels.length;
return { return {
@ -308,7 +311,7 @@ export class GfLineChartComponent
} }
context[`${axis}Started`] = true; context[`${axis}Started`] = true;
return context.index * delayBetweenPoints; return context.dataIndex * delayBetweenPoints;
}, },
duration: delayBetweenPoints, duration: delayBetweenPoints,
easing: 'linear', easing: 'linear',
@ -317,7 +320,7 @@ export class GfLineChartComponent
}; };
} }
private getTooltipPluginConfiguration() { private getTooltipPluginConfiguration(): Partial<TooltipOptions<'line'>> {
return { return {
...getTooltipOptions({ ...getTooltipOptions({
colorScheme: this.colorScheme, colorScheme: this.colorScheme,
@ -326,7 +329,7 @@ export class GfLineChartComponent
unit: this.unit unit: this.unit
}), }),
mode: 'index', mode: 'index',
position: 'top' as unknown, position: 'top',
xAlign: 'center', xAlign: 'center',
yAlign: 'bottom' yAlign: 'bottom'
}; };

Loading…
Cancel
Save