Skip to content

Commit

Permalink
Pull request #5356: Feature/DXCF-5676 web add ability to customise ne…
Browse files Browse the repository at this point in the history
…ws 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
  • Loading branch information
Keelaro1 authored and dxcity committed Dec 11, 2024
1 parent 860c399 commit 2e0f806
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 82 deletions.
7 changes: 1 addition & 6 deletions src/chart/chart.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -1510,12 +1511,6 @@ export interface YAxisLabelsColors extends Record<YAxisLabelType, Record<string,
prePostMarket: YAxisPrePostMarketLabelColorConfig;
prevDayClose: YAxisLabelColorConfig;
}
//#endregion

export interface CustomIcon {
normal: string;
hover: string;
}

export interface ChartConfigComponentsEventsIcons {
earnings?: CustomIcon;
Expand Down
72 changes: 72 additions & 0 deletions src/chart/components/events/events-custom-icons.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* 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/.
*/
import { Point } from '../../inputlisteners/canvas-input-listener.component';
export interface CustomIcon {
normal: string;
hover: string;
}

export interface CustomIconImage {
img: HTMLImageElement;
svgHeight: number;
}

export const getIconHash = (type: string, state: keyof CustomIcon) => `${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<string, CustomIconImage>,
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);
}
};
92 changes: 21 additions & 71 deletions src/chart/components/events/events.drawer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,31 @@
*/
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,
rhombus: 6,
'rhombus-large': 8,
};

const getIconHash = (type: EventType, state: keyof CustomIcon) => `${type}_${state}`;
const iconTypes: Array<keyof ChartConfigComponentsEventsIcons> = [
'earnings',
'dividends',
'splits',
'conference-calls',
];

export class EventsDrawer implements Drawer {
// cache of created icons
private customIcons: Record<string, CreatedCustomIcon> = {};
private customIcons: Record<string, CustomIconImage> = {};

constructor(
private canvasModel: CanvasModel,
Expand All @@ -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,
};
}

/**
Expand Down Expand Up @@ -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);
}
Expand All @@ -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.
Expand Down
5 changes: 0 additions & 5 deletions src/chart/components/pane/pane.component.ts
Original file line number Diff line number Diff line change
@@ -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.
Expand Down

0 comments on commit 2e0f806

Please sign in to comment.