From 2e0f806a86cb65ebd0194f75475e0b4c1cf41627 Mon Sep 17 00:00:00 2001 From: Anton Vorobev Date: Mon, 25 Nov 2024 07:56:19 +0000 Subject: [PATCH] Pull request #5356: Feature/DXCF-5676 web add ability to customise news bubbles Merge in DXCHARTS/dxchart5 from feature/DXCF-5676-web-add-ability-to-customise-news-bubbles to master * commit 'a2bb67cd0613683dd0118a760fa3cd3068b4cf9a': [DXCF-5676] [Web] add ability to customise news bubbles // pr fix [DXCF-5676] [Web] add ability to customise news bubbles // init [DXCF-5676] [Web] add ability to customise news bubbles // pr fix [DXCF-5676] [Web] add ability to customise news bubbles // pr fix [DXCF-5676] [Web] add ability to customise news bubbles // init [DXCF-5676] [Web] add ability to customise news bubbles // init [DXCF-5676] [Web] add ability to customise news bubbles // init [DXCF-5676] [Web] add ability to customise news bubbles // init [DXCF-5676] [Web] add ability to customise news bubbles // init GitOrigin-RevId: b1155115002d48f507cc3b84e1d55628abc80b87 --- src/chart/chart.config.ts | 7 +- .../components/events/events-custom-icons.ts | 72 +++++++++++++++ src/chart/components/events/events.drawer.ts | 92 +++++-------------- src/chart/components/pane/pane.component.ts | 5 - 4 files changed, 94 insertions(+), 82 deletions(-) create mode 100644 src/chart/components/events/events-custom-icons.ts diff --git a/src/chart/chart.config.ts b/src/chart/chart.config.ts index 4e1e0f7..1dad62a 100644 --- a/src/chart/chart.config.ts +++ b/src/chart/chart.config.ts @@ -15,6 +15,7 @@ import { DateTimeFormatter, TimeFormatterConfig } from './model/date-time.format import { DEFAULT_MERGE_OPTIONS, merge, MergeOptions } from './utils/merge.utils'; import { DeepPartial } from './utils/object.utils'; import { Candle, defaultSortCandles } from './model/candle.model'; +import { CustomIcon } from './components/events/events-custom-icons'; export const MAIN_FONT = 'Open Sans Semibold, sans-serif'; @@ -1510,12 +1511,6 @@ export interface YAxisLabelsColors extends Record `${type}_${state}`; + +/** + * Creates a custom icon for a given event type. + * @param {string} type - The type of the event. + * @param {CustomIcon} [icon] - The custom icon object containing the normal and hover images. + * @returns {void} + */ +export const createCustomIcon = (type: string, icon?: CustomIcon) => { + if (icon) { + const normal = createIconImage(icon.normal); + const hover = createIconImage(icon.hover); + + return { type, normal, hover }; + } +}; + +/** + * Creates an icon image from a string containing SVG data. + * @param {string} iconString - The string containing SVG data. + * @returns {CustomIconImage} An object containing an Image object and the height of the SVG element. + */ +export const createIconImage = (iconString: string): CustomIconImage => { + const parser = new DOMParser(); + const svgSelector = parser.parseFromString(iconString, 'text/html').querySelector('svg'); + let svgHeight = 0; + if (svgSelector) { + svgHeight = parseInt(svgSelector.getAttribute('height') ?? '', 10); + } + const svg64 = btoa(iconString); + const b64Start = 'data:image/svg+xml;base64,'; + const image64 = b64Start + svg64; + const img = new Image(); + img.src = image64; + + return { + img, + svgHeight, + }; +}; + +export const drawCustomSvgIcon = ( + ctx: CanvasRenderingContext2D, + icons: Record, + point: Point, + type: string, + isHovered: boolean, +) => { + if (isHovered) { + const hover = icons[getIconHash(type, 'hover')]; + ctx.drawImage(hover.img, point.x - hover.svgHeight / 2, point.y - hover.svgHeight / 2); + } else { + const normal = icons[getIconHash(type, 'normal')]; + ctx.drawImage(normal.img, point.x - normal.svgHeight / 2, point.y - normal.svgHeight / 2); + } +}; diff --git a/src/chart/components/events/events.drawer.ts b/src/chart/components/events/events.drawer.ts index 7ff7d5f..4cc5966 100644 --- a/src/chart/components/events/events.drawer.ts +++ b/src/chart/components/events/events.drawer.ts @@ -5,17 +5,14 @@ */ import { Bounds } from '../../model/bounds.model'; import { CanvasBoundsContainer, CanvasElement } from '../../canvas/canvas-bounds-container'; -import { CustomIcon, EventColors, FullChartConfig } from '../../chart.config'; +import { ChartConfigComponentsEventsIcons, FullChartConfig, EventColors } from '../../chart.config'; import { CanvasModel } from '../../model/canvas.model'; import { Drawer } from '../../drawers/drawing-manager'; import { DateTimeFormatter } from '../../model/date-time.formatter'; import { ChartModel } from '../chart/chart.model'; -import { EconomicEvent, EventsModel, EventType, EventWithId } from './events.model'; - -interface CreatedCustomIcon { - img: HTMLImageElement; - svgHeight: number; -} +import { EconomicEvent, EventsModel, EventWithId } from './events.model'; +import { createCustomIcon, CustomIconImage, drawCustomSvgIcon, getIconHash } from './events-custom-icons'; +import { Point } from '../../inputlisteners/canvas-input-listener.component'; const eventsSizesDict = { 'rhombus-small': 4, @@ -23,11 +20,16 @@ const eventsSizesDict = { 'rhombus-large': 8, }; -const getIconHash = (type: EventType, state: keyof CustomIcon) => `${type}_${state}`; +const iconTypes: Array = [ + 'earnings', + 'dividends', + 'splits', + 'conference-calls', +]; export class EventsDrawer implements Drawer { // cache of created icons - private customIcons: Record = {}; + private customIcons: Record = {}; constructor( private canvasModel: CanvasModel, @@ -39,49 +41,14 @@ export class EventsDrawer implements Drawer { ) { const iconsConfig = this.config.components.events.icons; if (iconsConfig) { - this.createCustomIcon('earnings', iconsConfig.earnings); - this.createCustomIcon('dividends', iconsConfig.dividends); - this.createCustomIcon('splits', iconsConfig.splits); - this.createCustomIcon('conference-calls', iconsConfig['conference-calls']); - } - } - - /** - * Creates a custom icon for a given event type. - * @param {EventType} type - The type of the event. - * @param {CustomIcon} [icon] - The custom icon object containing the normal and hover images. - * @returns {void} - */ - createCustomIcon(type: EventType, icon?: CustomIcon) { - if (icon) { - const normal = this.createIconImage(icon.normal); - const hover = this.createIconImage(icon.hover); - this.customIcons[getIconHash(type, 'normal')] = normal; - this.customIcons[getIconHash(type, 'hover')] = hover; - } - } - - /** - * Creates an icon image from a string containing SVG data. - * @param {string} iconString - The string containing SVG data. - * @returns {Object} An object containing an Image object and the height of the SVG element. - */ - createIconImage(iconString: string) { - const parser = new DOMParser(); - const svgSelector = parser.parseFromString(iconString, 'text/html').querySelector('svg'); - let svgHeight = 0; - if (svgSelector) { - svgHeight = parseInt(svgSelector.getAttribute('height') ?? '', 10); + iconTypes.forEach(type => { + const customIcon = createCustomIcon(type, iconsConfig[type]); + if (customIcon) { + this.customIcons[getIconHash(customIcon.type, 'normal')] = customIcon.normal; + this.customIcons[getIconHash(customIcon.type, 'hover')] = customIcon.hover; + } + }); } - const svg64 = btoa(iconString); - const b64Start = 'data:image/svg+xml;base64,'; - const image64 = b64Start + svg64; - const img = new Image(); - img.src = image64; - return { - img, - svgHeight, - }; } /** @@ -112,7 +79,9 @@ export class EventsDrawer implements Drawer { // check custom icon in cache if (this.customIcons[getIconHash(event.type, 'hover')] !== undefined) { - this.drawCustomSvgEvent(ctx, x, bounds, event); + const point: Point = { x, y: bounds.y + bounds.height / 2 }; + const isHovered = this.model.hoveredEvent.getValue() === event; + drawCustomSvgIcon(ctx, this.customIcons, point, event.type, isHovered); } else { this.drawDefaultEvent(ctx, x, bounds, event, colors); } @@ -139,25 +108,6 @@ export class EventsDrawer implements Drawer { ctx.restore(); } - /** - * Draws a custom SVG event on a canvas context. - * @param {CanvasRenderingContext2D} ctx - The canvas context to draw on. - * @param {number} x - The x coordinate of the event. - * @param {Bounds} bounds - The bounds of the event. - * @param {EventWithId} event - The event to draw. - * @returns {void} - */ - drawCustomSvgEvent(ctx: CanvasRenderingContext2D, x: number, bounds: Bounds, event: EventWithId) { - const y = bounds.y + bounds.height / 2; - const normal = this.customIcons[getIconHash(event.type, 'normal')]; - const hover = this.customIcons[getIconHash(event.type, 'hover')]; - if (this.model.hoveredEvent.getValue() === event) { - ctx.drawImage(hover.img, x - hover.svgHeight / 2, y - hover.svgHeight / 2); - } else { - ctx.drawImage(normal.img, x - normal.svgHeight / 2, y - normal.svgHeight / 2); - } - } - /** * Draws a default event on a canvas context. * @param {CanvasRenderingContext2D} ctx - The canvas context to draw on. diff --git a/src/chart/components/pane/pane.component.ts b/src/chart/components/pane/pane.component.ts index 408dd10..bd783da 100644 --- a/src/chart/components/pane/pane.component.ts +++ b/src/chart/components/pane/pane.component.ts @@ -1,8 +1,3 @@ -/* - * Copyright (C) 2019 - 2024 Devexperts Solutions IE Limited - * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. - * If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/. - */ /* * Copyright (C) 2019 - 2024 Devexperts Solutions IE Limited * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.