From 15b2b78ce98f1b2c7ba73452d72db54e62b0f4c2 Mon Sep 17 00:00:00 2001 From: KenTandrian Date: Tue, 3 Feb 2026 22:26:47 +0700 Subject: [PATCH] fix(client): implement custom tooltip positioner with type safety --- .../benchmark-comparator.component.ts | 8 +------- .../investment-chart.component.ts | 8 +------- apps/client/src/main.ts | 3 +++ libs/common/src/lib/chart-helper.ts | 4 ++-- libs/ui/src/lib/chart/chart.registry.ts | 19 +++++++++++++++++++ libs/ui/src/lib/chart/index.ts | 1 + .../lib/line-chart/line-chart.component.ts | 8 +------- 7 files changed, 28 insertions(+), 23 deletions(-) create mode 100644 libs/ui/src/lib/chart/chart.registry.ts create mode 100644 libs/ui/src/lib/chart/index.ts diff --git a/apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts b/apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts index b4565f6cd..5fd84e994 100644 --- a/apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts +++ b/apps/client/src/app/components/benchmark-comparator/benchmark-comparator.component.ts @@ -1,6 +1,5 @@ import { getTooltipOptions, - getTooltipPositionerMapTop, getVerticalHoverLinePlugin } from '@ghostfolio/common/chart-helper'; import { primaryColorRgb, secondaryColorRgb } from '@ghostfolio/common/config'; @@ -43,8 +42,7 @@ import { PointElement, TimeScale, Tooltip, - type TooltipOptions, - type TooltipPosition + type TooltipOptions } from 'chart.js'; import 'chartjs-adapter-date-fns'; import annotationPlugin from 'chartjs-plugin-annotation'; @@ -98,9 +96,6 @@ export class GfBenchmarkComparatorComponent implements OnChanges, OnDestroy { Tooltip ); - Tooltip.positioners['top'] = (_elements, position: TooltipPosition) => - getTooltipPositionerMapTop(this.chart, position); - addIcons({ arrowForwardOutline }); } @@ -264,7 +259,6 @@ export class GfBenchmarkComparatorComponent implements OnChanges, OnDestroy { unit: '%' }), mode: 'index', - // @ts-ignore position: 'top', xAlign: 'center', yAlign: 'bottom' diff --git a/apps/client/src/app/components/investment-chart/investment-chart.component.ts b/apps/client/src/app/components/investment-chart/investment-chart.component.ts index c4d2805eb..6b92e09de 100644 --- a/apps/client/src/app/components/investment-chart/investment-chart.component.ts +++ b/apps/client/src/app/components/investment-chart/investment-chart.component.ts @@ -1,6 +1,5 @@ import { getTooltipOptions, - getTooltipPositionerMapTop, getVerticalHoverLinePlugin, transformTickToAbbreviation } from '@ghostfolio/common/chart-helper'; @@ -37,8 +36,7 @@ import { PointElement, TimeScale, Tooltip, - type TooltipOptions, - type TooltipPosition + type TooltipOptions } from 'chart.js'; import 'chartjs-adapter-date-fns'; import annotationPlugin from 'chartjs-plugin-annotation'; @@ -82,9 +80,6 @@ export class GfInvestmentChartComponent implements OnChanges, OnDestroy { TimeScale, Tooltip ); - - Tooltip.positioners['top'] = (_elements, position: TooltipPosition) => - getTooltipPositionerMapTop(this.chart, position); } public ngOnChanges() { @@ -301,7 +296,6 @@ export class GfInvestmentChartComponent implements OnChanges, OnDestroy { unit: this.isInPercent ? '%' : undefined }), mode: 'index', - // @ts-ignore position: 'top', xAlign: 'center', yAlign: 'bottom' diff --git a/apps/client/src/main.ts b/apps/client/src/main.ts index f596de5f4..45901baec 100644 --- a/apps/client/src/main.ts +++ b/apps/client/src/main.ts @@ -1,5 +1,6 @@ import { InfoResponse } from '@ghostfolio/common/interfaces'; import { filterGlobalPermissions } from '@ghostfolio/common/permissions'; +import { registerChartConfiguration } from '@ghostfolio/ui/chart'; import { GF_ENVIRONMENT } from '@ghostfolio/ui/environment'; import { GfNotificationModule } from '@ghostfolio/ui/notifications'; @@ -58,6 +59,8 @@ import { environment } from './environments/environment'; enableProdMode(); } + registerChartConfiguration(); + await bootstrapApplication(GfAppComponent, { providers: [ authInterceptorProviders, diff --git a/libs/common/src/lib/chart-helper.ts b/libs/common/src/lib/chart-helper.ts index 2c34442c0..626b6a1c3 100644 --- a/libs/common/src/lib/chart-helper.ts +++ b/libs/common/src/lib/chart-helper.ts @@ -92,8 +92,8 @@ export function getTooltipOptions({ }; } -export function getTooltipPositionerMapTop( - chart: Chart, +export function getTooltipPositionerMapTop( + chart: Chart, position: TooltipPosition ) { if (!position || !chart?.chartArea) { diff --git a/libs/ui/src/lib/chart/chart.registry.ts b/libs/ui/src/lib/chart/chart.registry.ts new file mode 100644 index 000000000..af82aad4c --- /dev/null +++ b/libs/ui/src/lib/chart/chart.registry.ts @@ -0,0 +1,19 @@ +import { getTooltipPositionerMapTop } from '@ghostfolio/common/chart-helper'; + +import { Tooltip, TooltipPositionerFunction, ChartType } from 'chart.js'; + +declare module 'chart.js' { + interface TooltipPositionerMap { + top: TooltipPositionerFunction; + } +} + +export function registerChartConfiguration() { + if (Tooltip.positioners['top']) { + return; + } + + Tooltip.positioners.top = function (_elements, eventPosition) { + return getTooltipPositionerMapTop(this.chart, eventPosition); + }; +} diff --git a/libs/ui/src/lib/chart/index.ts b/libs/ui/src/lib/chart/index.ts new file mode 100644 index 000000000..2a3d3b358 --- /dev/null +++ b/libs/ui/src/lib/chart/index.ts @@ -0,0 +1 @@ +export * from './chart.registry'; diff --git a/libs/ui/src/lib/line-chart/line-chart.component.ts b/libs/ui/src/lib/line-chart/line-chart.component.ts index 0d1a709b5..6980b6309 100644 --- a/libs/ui/src/lib/line-chart/line-chart.component.ts +++ b/libs/ui/src/lib/line-chart/line-chart.component.ts @@ -1,6 +1,5 @@ import { getTooltipOptions, - getTooltipPositionerMapTop, getVerticalHoverLinePlugin } from '@ghostfolio/common/chart-helper'; import { primaryColorRgb, secondaryColorRgb } from '@ghostfolio/common/config'; @@ -35,8 +34,7 @@ import { PointElement, TimeScale, Tooltip, - type TooltipOptions, - type TooltipPosition + type TooltipOptions } from 'chart.js'; import 'chartjs-adapter-date-fns'; import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; @@ -87,9 +85,6 @@ export class GfLineChartComponent TimeScale, Tooltip ); - - Tooltip.positioners['top'] = (_elements, position: TooltipPosition) => - getTooltipPositionerMapTop(this.chart, position); } public ngAfterViewInit() { @@ -337,7 +332,6 @@ export class GfLineChartComponent unit: this.unit }), mode: 'index', - // @ts-ignore position: 'top', xAlign: 'center', yAlign: 'bottom'