Chandranil Bakshi 1 day ago
committed by GitHub
parent
commit
589a1f863b
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 2
      apps/client/src/app/components/home-overview/home-overview.html
  2. 2
      libs/ui/src/lib/line-chart/line-chart.component.stories.ts
  3. 224
      libs/ui/src/lib/line-chart/line-chart.component.ts

2
apps/client/src/app/components/home-overview/home-overview.html

@ -69,6 +69,8 @@
unit="%" unit="%"
[class.pr-3]="deviceType() === 'mobile'" [class.pr-3]="deviceType() === 'mobile'"
[colorScheme]="user()?.settings?.colorScheme" [colorScheme]="user()?.settings?.colorScheme"
[dataDecimation]="true"
[dataDecimationThreshold]="100"
[hidden]="historicalDataItems()?.length === 0" [hidden]="historicalDataItems()?.length === 0"
[historicalDataItems]="historicalDataItems()" [historicalDataItems]="historicalDataItems()"
[isAnimated]="user()?.settings?.dateRange === '1d' ? false : true" [isAnimated]="user()?.settings?.dateRange === '1d' ? false : true"

2
libs/ui/src/lib/line-chart/line-chart.component.stories.ts

@ -19,6 +19,8 @@ type Story = StoryObj<GfLineChartComponent>;
export const Simple: Story = { export const Simple: Story = {
args: { args: {
dataDecimation: true,
dataDecimationThreshold: 1,
historicalDataItems: [ historicalDataItems: [
{ {
date: '2017-01-01', date: '2017-01-01',

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

@ -26,11 +26,14 @@ import {
import { import {
type AnimationsSpec, type AnimationsSpec,
Chart, Chart,
type ChartData,
Decimation,
Filler, Filler,
LinearScale, LinearScale,
LineController, LineController,
LineElement, LineElement,
PointElement, PointElement,
type Point,
TimeScale, TimeScale,
Tooltip, Tooltip,
type TooltipOptions type TooltipOptions
@ -54,6 +57,8 @@ export class GfLineChartComponent
@Input() benchmarkLabel = ''; @Input() benchmarkLabel = '';
@Input() colorScheme: ColorScheme; @Input() colorScheme: ColorScheme;
@Input() currency: string; @Input() currency: string;
@Input() dataDecimation = false;
@Input() dataDecimationThreshold: number;
@Input() historicalDataItems: LineChartItem[]; @Input() historicalDataItems: LineChartItem[];
@Input() isAnimated = false; @Input() isAnimated = false;
@Input() label: string; @Input() label: string;
@ -71,13 +76,14 @@ export class GfLineChartComponent
@ViewChild('chartCanvas') chartCanvas: ElementRef<HTMLCanvasElement>; @ViewChild('chartCanvas') chartCanvas: ElementRef<HTMLCanvasElement>;
public chart: Chart<'line'>; public chart: Chart<'line', (number | Point | null)[], string>;
public isLoading = true; public isLoading = true;
private readonly ANIMATION_DURATION = 1200; private readonly ANIMATION_DURATION = 1200;
public constructor(private changeDetectorRef: ChangeDetectorRef) { public constructor(private changeDetectorRef: ChangeDetectorRef) {
Chart.register( Chart.register(
Decimation,
Filler, Filler,
LineController, LineController,
LineElement, LineElement,
@ -118,14 +124,27 @@ export class GfLineChartComponent
private initialize() { private initialize() {
this.isLoading = true; this.isLoading = true;
const benchmarkPrices: number[] = []; const benchmarkPrices: (number | Point | null)[] = [];
const labels: string[] = []; const labels: string[] = [];
const marketPrices: number[] = []; const marketPrices: (number | Point | null)[] = [];
this.historicalDataItems?.forEach((historicalDataItem, index) => { this.historicalDataItems?.forEach((historicalDataItem, index) => {
benchmarkPrices.push(this.benchmarkDataItems?.[index]?.value); const label = historicalDataItem.date;
labels.push(historicalDataItem.date); const timestamp = new Date(historicalDataItem.date).getTime();
marketPrices.push(historicalDataItem.value); const benchmarkValue = this.benchmarkDataItems?.[index]?.value;
const marketValue = historicalDataItem.value;
benchmarkPrices.push(
this.dataDecimation
? { x: timestamp, y: benchmarkValue ?? null }
: benchmarkValue
);
labels.push(label);
marketPrices.push(
this.dataDecimation
? { x: timestamp, y: marketValue ?? null }
: marketValue
);
}); });
const gradient = this.chartCanvas?.nativeElement const gradient = this.chartCanvas?.nativeElement
@ -148,7 +167,7 @@ export class GfLineChartComponent
gradient.addColorStop(1, getBackgroundColor(this.colorScheme)); gradient.addColorStop(1, getBackgroundColor(this.colorScheme));
} }
const data = { const data: ChartData<'line', (number | Point | null)[], string> = {
labels, labels,
datasets: [ datasets: [
{ {
@ -181,6 +200,11 @@ export class GfLineChartComponent
if (this.chart) { if (this.chart) {
this.chart.data = data; this.chart.data = data;
this.chart.options.plugins ??= {}; this.chart.options.plugins ??= {};
this.chart.options.parsing = this.dataDecimation ? false : undefined;
this.chart.options.plugins.decimation = {
enabled: this.dataDecimation,
threshold: this.dataDecimationThreshold
};
this.chart.options.plugins.tooltip = this.chart.options.plugins.tooltip =
this.getTooltipPluginConfiguration(); this.getTooltipPluginConfiguration();
this.chart.options.animations = this.isAnimated this.chart.options.animations = this.isAnimated
@ -189,106 +213,114 @@ export class GfLineChartComponent
this.chart.update(); this.chart.update();
} else { } else {
this.chart = new Chart(this.chartCanvas.nativeElement, { this.chart = new Chart<'line', (number | Point | null)[], string>(
data, this.chartCanvas.nativeElement,
options: { {
animations: this.isAnimated ? animations : undefined, data,
aspectRatio: 16 / 9, options: {
elements: { animations: this.isAnimated ? animations : undefined,
point: { aspectRatio: 16 / 9,
hoverBackgroundColor: getBackgroundColor(this.colorScheme), elements: {
hoverRadius: 2 point: {
} hoverBackgroundColor: getBackgroundColor(this.colorScheme),
}, hoverRadius: 2
interaction: { intersect: false, mode: 'index' }, }
plugins: {
legend: {
align: 'start',
display: this.showLegend,
position: 'bottom'
}, },
tooltip: this.getTooltipPluginConfiguration(), interaction: { intersect: false, mode: 'index' },
verticalHoverLine: { parsing: this.dataDecimation ? false : undefined,
color: `rgba(${getTextColor(this.colorScheme)}, 0.1)` plugins: {
} decimation: {
}, enabled: this.dataDecimation,
scales: { threshold: this.dataDecimationThreshold
x: {
border: {
color: `rgba(${getTextColor(this.colorScheme)}, 0.1)`
},
display: this.showXAxis,
grid: {
display: false
}, },
time: { legend: {
tooltipFormat: getDateFormatString(this.locale), align: 'start',
unit: 'year' display: this.showLegend,
position: 'bottom'
}, },
type: 'time' tooltip: this.getTooltipPluginConfiguration(),
verticalHoverLine: {
color: `rgba(${getTextColor(this.colorScheme)}, 0.1)`
}
}, },
y: { scales: {
border: { x: {
width: 0 border: {
}, color: `rgba(${getTextColor(this.colorScheme)}, 0.1)`
display: this.showYAxis, },
grid: { display: this.showXAxis,
color: ({ scale, tick }) => { grid: {
if ( display: false
tick.value === 0 || },
tick.value === scale.max || time: {
tick.value === scale.min || tooltipFormat: getDateFormatString(this.locale),
tick.value === this.yMax || unit: 'year'
tick.value === this.yMin },
) { type: 'time'
return `rgba(${getTextColor(this.colorScheme)}, 0.1)`;
}
return 'transparent';
}
}, },
max: this.yMax, y: {
min: this.yMin, border: {
position: 'right', width: 0
ticks: { },
callback: (tickValue, index, ticks) => { display: this.showYAxis,
if (index === 0 || index === ticks.length - 1) { grid: {
// Only print last and first legend entry color: ({ scale, tick }) => {
if (
if (index === 0 && this.yMinLabel) { tick.value === 0 ||
return this.yMinLabel; tick.value === scale.max ||
} tick.value === scale.min ||
tick.value === this.yMax ||
if (index === ticks.length - 1 && this.yMaxLabel) { tick.value === this.yMin
return this.yMaxLabel; ) {
} return `rgba(${getTextColor(this.colorScheme)}, 0.1)`;
if (typeof tickValue === 'number') {
return tickValue.toLocaleString(this.locale, {
maximumFractionDigits: 2,
minimumFractionDigits: 2
});
} }
return tickValue; return 'transparent';
} }
},
max: this.yMax,
min: this.yMin,
position: 'right',
ticks: {
callback: (tickValue, index, ticks) => {
if (index === 0 || index === ticks.length - 1) {
// Only print last and first legend entry
if (index === 0 && this.yMinLabel) {
return this.yMinLabel;
}
if (index === ticks.length - 1 && this.yMaxLabel) {
return this.yMaxLabel;
}
if (typeof tickValue === 'number') {
return tickValue.toLocaleString(this.locale, {
maximumFractionDigits: 2,
minimumFractionDigits: 2
});
}
return tickValue;
}
return ''; return '';
},
display: this.showYAxis,
mirror: true,
z: 1
}, },
display: this.showYAxis, type: 'linear'
mirror: true, }
z: 1 },
}, spanGaps: true
type: 'linear'
}
}, },
spanGaps: true plugins: [
}, getVerticalHoverLinePlugin(this.chartCanvas, this.colorScheme)
plugins: [ ],
getVerticalHoverLinePlugin(this.chartCanvas, this.colorScheme) type: 'line'
], }
type: 'line' );
});
} }
} }

Loading…
Cancel
Save