From 359a6befdcf1e05dc834e912c7e0ec22efbb672f Mon Sep 17 00:00:00 2001 From: Oli Legat Date: Fri, 10 May 2024 14:09:47 +0100 Subject: [PATCH 01/10] AG-11385 Move focusIndicator ownership into ChartContext Also add stubbed ProxyInteractionService class which will be responsive for creating hidden DOM elements that represent the chart for a11y. --- .../ag-charts-community/src/chart/chartContext.ts | 10 +++++++++- .../src/chart/dom/proxyInteractionService.ts | 15 +++++++++++++++ .../src/chart/interaction/regionManager.ts | 8 ++------ 3 files changed, 26 insertions(+), 7 deletions(-) create mode 100644 packages/ag-charts-community/src/chart/dom/proxyInteractionService.ts diff --git a/packages/ag-charts-community/src/chart/chartContext.ts b/packages/ag-charts-community/src/chart/chartContext.ts index aa7384eb7d..b0460126ff 100644 --- a/packages/ag-charts-community/src/chart/chartContext.ts +++ b/packages/ag-charts-community/src/chart/chartContext.ts @@ -7,6 +7,8 @@ import { AnnotationManager } from './annotation/annotationManager'; import type { ChartService } from './chartService'; import { DataService } from './data/dataService'; import { DOMManager } from './dom/domManager'; +import { FocusIndicator } from './dom/focusIndicator'; +import { ProxyInteractionService } from './dom/proxyInteractionService'; import { AnimationManager } from './interaction/animationManager'; import { AriaAnnouncementService } from './interaction/ariaAnnouncementServices'; import { ChartEventManager } from './interaction/chartEventManager'; @@ -45,9 +47,11 @@ export class ChartContext implements ModuleContext { contextMenuRegistry: ContextMenuRegistry; cursorManager: CursorManager; domManager: DOMManager; + focusIndicator: FocusIndicator; highlightManager: HighlightManager; interactionManager: InteractionManager; keyNavManager: KeyNavManager; + proxyInteractionService: ProxyInteractionService; regionManager: RegionManager; seriesStateManager: SeriesStateManager; syncManager: SyncManager; @@ -82,7 +86,9 @@ export class ChartContext implements ModuleContext { this.highlightManager = new HighlightManager(); this.interactionManager = new InteractionManager(chart.keyboard, this.domManager); this.keyNavManager = new KeyNavManager(this.interactionManager, this.domManager); - this.regionManager = new RegionManager(this.interactionManager, this.keyNavManager, this.domManager); + this.focusIndicator = new FocusIndicator(this.domManager); + this.regionManager = new RegionManager(this.interactionManager, this.keyNavManager, this.focusIndicator); + this.proxyInteractionService = new ProxyInteractionService(this.domManager, this.focusIndicator); this.toolbarManager = new ToolbarManager(); this.gestureDetector = new GestureDetector(this.domManager); this.layoutService = new LayoutService(); @@ -101,7 +107,9 @@ export class ChartContext implements ModuleContext { destroy() { // chart.ts handles the destruction of the scene and zoomManager. this.tooltipManager.destroy(); + this.proxyInteractionService.destroy(); this.regionManager.destroy(); + this.focusIndicator.destroy(); this.keyNavManager.destroy(); this.interactionManager.destroy(); this.animationManager.stop(); diff --git a/packages/ag-charts-community/src/chart/dom/proxyInteractionService.ts b/packages/ag-charts-community/src/chart/dom/proxyInteractionService.ts new file mode 100644 index 0000000000..030f16f951 --- /dev/null +++ b/packages/ag-charts-community/src/chart/dom/proxyInteractionService.ts @@ -0,0 +1,15 @@ +import type { DOMManager } from './domManager'; +import type { FocusIndicator } from './focusIndicator'; + +// TODO: stub interface +export class ProxyInteractionService { + constructor( + private readonly domManager: DOMManager, + private readonly focusIndicator: FocusIndicator + ) {} + + public destroy() { + this.domManager; + this.focusIndicator; + } +} diff --git a/packages/ag-charts-community/src/chart/interaction/regionManager.ts b/packages/ag-charts-community/src/chart/interaction/regionManager.ts index 5ed10386b1..1090b0a920 100644 --- a/packages/ag-charts-community/src/chart/interaction/regionManager.ts +++ b/packages/ag-charts-community/src/chart/interaction/regionManager.ts @@ -1,7 +1,6 @@ import type { BBox } from '../../scene/bbox'; import { Listeners } from '../../util/listeners'; -import type { DOMManager } from '../dom/domManager'; -import { FocusIndicator } from '../dom/focusIndicator'; +import type { FocusIndicator } from '../dom/focusIndicator'; import { buildConsumable } from './consumableEvent'; import type { InteractionManager, PointerInteractionEvent, PointerInteractionTypes } from './interactionManager'; import { InteractionState, POINTER_INTERACTION_TYPES } from './interactionManager'; @@ -67,7 +66,6 @@ function addHandler( export class RegionManager { private currentTabIndex = 0; - public readonly focusIndicator: FocusIndicator; private currentRegion?: Region; private isDragging = false; @@ -80,7 +78,7 @@ export class RegionManager { constructor( private readonly interactionManager: InteractionManager, private readonly keyNavManager: KeyNavManager, - domManager: DOMManager + private readonly focusIndicator: FocusIndicator ) { this.destroyFns.push( ...POINTER_INTERACTION_TYPES.map((eventName) => @@ -93,8 +91,6 @@ export class RegionManager { this.keyNavManager.addListener('nav-hori', this.onNav.bind(this)), this.keyNavManager.addListener('submit', this.onNav.bind(this)) ); - - this.focusIndicator = new FocusIndicator(domManager); } public destroy() { From ecb88d902234cfc8ac06f93394bca32693f85a38 Mon Sep 17 00:00:00 2001 From: Oli Legat Date: Fri, 10 May 2024 14:52:40 +0100 Subject: [PATCH 02/10] AG-11385 Move nearest.ts into util/ This file is generic enough and doesn't need to know about the scene. --- .../src/chart/series/cartesian/quadtreeUtil.ts | 2 +- packages/ag-charts-community/src/chart/series/series.ts | 2 +- packages/ag-charts-community/src/integrated-charts-scene.ts | 2 -- packages/ag-charts-community/src/module-support.ts | 1 + packages/ag-charts-community/src/scene/bbox.ts | 4 ++-- packages/ag-charts-community/src/scene/shape/line.ts | 2 +- packages/ag-charts-community/src/scene/shape/path.ts | 2 +- packages/ag-charts-community/src/scene/shape/rect.ts | 2 +- packages/ag-charts-community/src/scene/util/quadtree.ts | 2 +- packages/ag-charts-community/src/{scene => util}/nearest.ts | 0 .../src/features/error-bar/errorBarNode.ts | 6 +++--- .../src/series/box-plot/boxPlotGroup.ts | 4 ++-- .../src/series/candlestick/candlestickGroup.ts | 2 +- .../src/series/treemap/treemapSeries.ts | 2 +- 14 files changed, 16 insertions(+), 17 deletions(-) rename packages/ag-charts-community/src/{scene => util}/nearest.ts (100%) diff --git a/packages/ag-charts-community/src/chart/series/cartesian/quadtreeUtil.ts b/packages/ag-charts-community/src/chart/series/cartesian/quadtreeUtil.ts index d21f359e60..1693bf02ec 100644 --- a/packages/ag-charts-community/src/chart/series/cartesian/quadtreeUtil.ts +++ b/packages/ag-charts-community/src/chart/series/cartesian/quadtreeUtil.ts @@ -1,9 +1,9 @@ import type { Group } from '../../../scene/group'; -import type { DistantObject } from '../../../scene/nearest'; import type { Node } from '../../../scene/node'; import type { Point } from '../../../scene/point'; import type { QuadtreeNearest } from '../../../scene/util/quadtree'; import { Logger } from '../../../util/logger'; +import type { DistantObject } from '../../../util/nearest'; import type { SeriesNodePickMatch } from '../series'; import type { SeriesNodeDatum } from '../seriesTypes'; diff --git a/packages/ag-charts-community/src/chart/series/series.ts b/packages/ag-charts-community/src/chart/series/series.ts index 2a7eba5d80..875381dfd4 100644 --- a/packages/ag-charts-community/src/chart/series/series.ts +++ b/packages/ag-charts-community/src/chart/series/series.ts @@ -11,13 +11,13 @@ import type { ScaleType } from '../../scale/scale'; import type { BBox } from '../../scene/bbox'; import { Group } from '../../scene/group'; import type { ZIndexSubOrder } from '../../scene/layersManager'; -import { type DistantObject, nearestSquared } from '../../scene/nearest'; import type { Node } from '../../scene/node'; import type { Point } from '../../scene/point'; import type { PlacedLabel, PointLabelDatum } from '../../scene/util/labelPlacement'; import { createId } from '../../util/id'; import { jsonDiff } from '../../util/json'; import { Listeners } from '../../util/listeners'; +import { type DistantObject, nearestSquared } from '../../util/nearest'; import { clamp } from '../../util/number'; import { mergeDefaults } from '../../util/object'; import type { TypedEvent } from '../../util/observable'; diff --git a/packages/ag-charts-community/src/integrated-charts-scene.ts b/packages/ag-charts-community/src/integrated-charts-scene.ts index b2a143a5f4..b33aca7bf8 100644 --- a/packages/ag-charts-community/src/integrated-charts-scene.ts +++ b/packages/ag-charts-community/src/integrated-charts-scene.ts @@ -38,8 +38,6 @@ export { Tooltip, toTooltipHtml } from './chart/tooltip/tooltip'; export type { TooltipMeta } from './chart/tooltip/tooltip'; export { BBox } from './scene/bbox'; export { SectorBox } from './scene/sectorBox'; -export type { DistantObject, NearestResult } from './scene/nearest'; -export { nearestSquared, nearestSquaredInContainer } from './scene/nearest'; export { HdpiCanvas } from './scene/canvas/hdpiCanvas'; export { Image } from './scene/image'; export { ExtendedPath2D } from './scene/extendedPath2D'; diff --git a/packages/ag-charts-community/src/module-support.ts b/packages/ag-charts-community/src/module-support.ts index a37e7f7870..66ad3cd957 100644 --- a/packages/ag-charts-community/src/module-support.ts +++ b/packages/ag-charts-community/src/module-support.ts @@ -6,6 +6,7 @@ export { extractDecoratedProperties, isDecoratedObject, listDecoratedProperties export * from './util/dom'; export * from './util/deprecation'; export * from './util/json'; +export * from './util/nearest'; export * from './util/number'; export * from './util/object'; export * from './util/properties'; diff --git a/packages/ag-charts-community/src/scene/bbox.ts b/packages/ag-charts-community/src/scene/bbox.ts index 823d4aab2c..2535b22762 100644 --- a/packages/ag-charts-community/src/scene/bbox.ts +++ b/packages/ag-charts-community/src/scene/bbox.ts @@ -1,7 +1,7 @@ import { type Interpolating, interpolate } from '../util/interpolating'; +import type { DistantObject, NearestResult } from '../util/nearest'; +import { nearestSquared } from '../util/nearest'; import { clamp } from '../util/number'; -import type { DistantObject, NearestResult } from './nearest'; -import { nearestSquared } from './nearest'; // For small data structs like a bounding box, objects are superior to arrays // in terms of performance (by 3-4% in Chrome 71, Safari 12 and by 20% in Firefox 64). diff --git a/packages/ag-charts-community/src/scene/shape/line.ts b/packages/ag-charts-community/src/scene/shape/line.ts index 0782f2de06..574107805c 100644 --- a/packages/ag-charts-community/src/scene/shape/line.ts +++ b/packages/ag-charts-community/src/scene/shape/line.ts @@ -1,6 +1,6 @@ import { lineDistanceSquared } from '../../util/distance'; +import type { DistantObject } from '../../util/nearest'; import { BBox } from '../bbox'; -import type { DistantObject } from '../nearest'; import type { NodeOptions, RenderContext } from '../node'; import { RedrawType, SceneChangeDetection } from '../node'; import { Shape } from './shape'; diff --git a/packages/ag-charts-community/src/scene/shape/path.ts b/packages/ag-charts-community/src/scene/shape/path.ts index ba719ffde5..8483f6dd4d 100644 --- a/packages/ag-charts-community/src/scene/shape/path.ts +++ b/packages/ag-charts-community/src/scene/shape/path.ts @@ -1,5 +1,5 @@ +import type { DistantObject } from '../../util//nearest'; import { ExtendedPath2D } from '../extendedPath2D'; -import type { DistantObject } from '../nearest'; import type { RenderContext } from '../node'; import { RedrawType, SceneChangeDetection } from '../node'; import { Shape } from './shape'; diff --git a/packages/ag-charts-community/src/scene/shape/rect.ts b/packages/ag-charts-community/src/scene/shape/rect.ts index 22b69dac76..e967f21ea0 100644 --- a/packages/ag-charts-community/src/scene/shape/rect.ts +++ b/packages/ag-charts-community/src/scene/shape/rect.ts @@ -1,6 +1,6 @@ +import type { DistantObject } from '../../util/nearest'; import { BBox } from '../bbox'; import { ExtendedPath2D } from '../extendedPath2D'; -import type { DistantObject } from '../nearest'; import { Path, ScenePathChangeDetection } from './path'; import { Shape } from './shape'; diff --git a/packages/ag-charts-community/src/scene/util/quadtree.ts b/packages/ag-charts-community/src/scene/util/quadtree.ts index cf38ab8af1..7afa086da6 100644 --- a/packages/ag-charts-community/src/scene/util/quadtree.ts +++ b/packages/ag-charts-community/src/scene/util/quadtree.ts @@ -1,5 +1,5 @@ +import { type DistantObject, type NearestResult, nearestSquared } from '../../util/nearest'; import { BBox } from '../bbox'; -import { type DistantObject, type NearestResult, nearestSquared } from '../nearest'; type QuadtreeNearestResult = NearestResult>; diff --git a/packages/ag-charts-community/src/scene/nearest.ts b/packages/ag-charts-community/src/util/nearest.ts similarity index 100% rename from packages/ag-charts-community/src/scene/nearest.ts rename to packages/ag-charts-community/src/util/nearest.ts diff --git a/packages/ag-charts-enterprise/src/features/error-bar/errorBarNode.ts b/packages/ag-charts-enterprise/src/features/error-bar/errorBarNode.ts index 8afbbf6076..a17661fa8d 100644 --- a/packages/ag-charts-enterprise/src/features/error-bar/errorBarNode.ts +++ b/packages/ag-charts-enterprise/src/features/error-bar/errorBarNode.ts @@ -1,10 +1,10 @@ import type { AgErrorBarFormatterParams, AgErrorBarOptions, AgErrorBarThemeableOptions } from 'ag-charts-community'; import { _ModuleSupport, _Scene } from 'ag-charts-community'; -const { partialAssign, mergeDefaults } = _ModuleSupport; +const { nearestSquaredInContainer, partialAssign, mergeDefaults } = _ModuleSupport; const { BBox } = _Scene; type BBox = _Scene.BBox; -type NearestResult = _Scene.NearestResult; +type NearestResult = _ModuleSupport.NearestResult; export type ErrorBarNodeDatum = _ModuleSupport.CartesianSeriesNodeDatum & _ModuleSupport.ErrorBoundSeriesNodeDatum; export type ErrorBarStylingOptions = Omit; @@ -243,7 +243,7 @@ export class ErrorBarGroup extends _Scene.Group { } nearestSquared(x: number, y: number): _ModuleSupport.PickNodeDatumResult { - const { nearest, distanceSquared } = _Scene.nearestSquaredInContainer(x, y, this); + const { nearest, distanceSquared } = nearestSquaredInContainer(x, y, this); if (nearest !== undefined && !isNaN(distanceSquared)) { return { datum: nearest.datum, distanceSquared }; } diff --git a/packages/ag-charts-enterprise/src/series/box-plot/boxPlotGroup.ts b/packages/ag-charts-enterprise/src/series/box-plot/boxPlotGroup.ts index 053d87d6f2..975ac3e657 100644 --- a/packages/ag-charts-enterprise/src/series/box-plot/boxPlotGroup.ts +++ b/packages/ag-charts-enterprise/src/series/box-plot/boxPlotGroup.ts @@ -14,7 +14,7 @@ enum GroupTags { Cap, } -export class BoxPlotGroup extends Group implements _Scene.DistantObject { +export class BoxPlotGroup extends Group implements _ModuleSupport.DistantObject { constructor() { super(); this.append([ @@ -163,7 +163,7 @@ export class BoxPlotGroup extends Group implements _Scene.DistantObject { distanceSquared(x: number, y: number): number { const nodes = Selection.selectByClass<_Scene.Rect | _Scene.Line>(this, Rect, Line); - return _Scene.nearestSquared(x, y, nodes).distanceSquared; + return _ModuleSupport.nearestSquared(x, y, nodes).distanceSquared; } get midPoint(): { x: number; y: number } { diff --git a/packages/ag-charts-enterprise/src/series/candlestick/candlestickGroup.ts b/packages/ag-charts-enterprise/src/series/candlestick/candlestickGroup.ts index 51d030f5bb..c81907eb5a 100644 --- a/packages/ag-charts-enterprise/src/series/candlestick/candlestickGroup.ts +++ b/packages/ag-charts-enterprise/src/series/candlestick/candlestickGroup.ts @@ -41,7 +41,7 @@ export abstract class CandlestickBaseGroup distanceSquared(x: number, y: number): number { const nodes = _Scene.Selection.selectByClass<_Scene.Rect | _Scene.Line>(this, _Scene.Rect, _Scene.Line); - return _Scene.nearestSquared(x, y, nodes).distanceSquared; + return _ModuleSupport.nearestSquared(x, y, nodes).distanceSquared; } get midPoint(): { x: number; y: number } { diff --git a/packages/ag-charts-enterprise/src/series/treemap/treemapSeries.ts b/packages/ag-charts-enterprise/src/series/treemap/treemapSeries.ts index 0de79e2629..3392b0d717 100644 --- a/packages/ag-charts-enterprise/src/series/treemap/treemapSeries.ts +++ b/packages/ag-charts-enterprise/src/series/treemap/treemapSeries.ts @@ -79,7 +79,7 @@ const verticalAlignFactors: Record = { bottom: 1, }; -class DistantGroup extends _Scene.Group implements _Scene.DistantObject { +class DistantGroup extends _Scene.Group implements _ModuleSupport.DistantObject { distanceSquared(x: number, y: number): number { return this.computeBBox().distanceSquared(x, y); } From 355bcfb5f0cbda1d7db03ed7f8c87963d7eadc3a Mon Sep 17 00:00:00 2001 From: Oli Legat Date: Fri, 10 May 2024 14:59:46 +0100 Subject: [PATCH 03/10] AG-11385 Extract interfaces out of BBox The BBox class is quite complex, but a lot of functionalities only need small subset of this class. Examples: - RegionManager only needs `containPoint` and `width * height` (area). - FocusIndicator only needs `x, y, width, height`. - QuadtreeNodeExact only needs `collidesBBox` and `containsPoint`. - QuadtreeNodeNearest only needs `distanceSquared` and `containsPoint`. This cleanup will hopefully make code maintanance easier. --- .../src/chart/interaction/regionManager.ts | 13 +++++-------- packages/ag-charts-community/src/scene/bbox.ts | 3 ++- .../ag-charts-community/src/util/bboxinterface.ts | 14 ++++++++++++++ 3 files changed, 21 insertions(+), 9 deletions(-) create mode 100644 packages/ag-charts-community/src/util/bboxinterface.ts diff --git a/packages/ag-charts-community/src/chart/interaction/regionManager.ts b/packages/ag-charts-community/src/chart/interaction/regionManager.ts index 1090b0a920..7bc59fe488 100644 --- a/packages/ag-charts-community/src/chart/interaction/regionManager.ts +++ b/packages/ag-charts-community/src/chart/interaction/regionManager.ts @@ -1,4 +1,4 @@ -import type { BBox } from '../../scene/bbox'; +import type { BBoxContainsTester, BBoxProvider, BBoxValues } from '../../util/bboxinterface'; import { Listeners } from '../../util/listeners'; import type { FocusIndicator } from '../dom/focusIndicator'; import { buildConsumable } from './consumableEvent'; @@ -27,13 +27,10 @@ type TypeInfo = { [K in PointerInteractionTypes]: PointerInteractionEvent } & type RegionEvent = PointerInteractionEvent | KeyNavEvent; type RegionHandler = (event: RegionEvent) => void; +type RegionBBoxProvider = BBoxProvider; class RegionListeners extends Listeners {} -interface BBoxProvider { - getCachedBBox(): BBox; -} - type Region = { readonly properties: RegionProperties; readonly listeners: RegionListeners; @@ -41,7 +38,7 @@ type Region = { export interface RegionProperties { readonly name: RegionName; - readonly bboxproviders: BBoxProvider[]; + readonly bboxproviders: RegionBBoxProvider[]; canInteraction(): boolean; } @@ -111,7 +108,7 @@ export class RegionManager { return this.makeObserver(region); } - public addRegion(name: RegionName, bboxprovider: BBoxProvider, ...extraProviders: BBoxProvider[]) { + public addRegion(name: RegionName, bboxprovider: RegionBBoxProvider, ...extraProviders: RegionBBoxProvider[]) { return this.addRegionFromProperties({ name, bboxproviders: [bboxprovider, ...extraProviders], @@ -330,7 +327,7 @@ export class RegionManager { this.dispatch(focusedRegion, event); } - public updateFocusIndicatorRect(rect?: { x: number; y: number; width: number; height: number }) { + public updateFocusIndicatorRect(rect?: BBoxValues) { this.focusIndicator.updateBBox(rect); } } diff --git a/packages/ag-charts-community/src/scene/bbox.ts b/packages/ag-charts-community/src/scene/bbox.ts index 2535b22762..e2d9fb4d14 100644 --- a/packages/ag-charts-community/src/scene/bbox.ts +++ b/packages/ag-charts-community/src/scene/bbox.ts @@ -1,3 +1,4 @@ +import type { BBoxContainsTester, BBoxValues } from '../util/bboxinterface'; import { type Interpolating, interpolate } from '../util/interpolating'; import type { DistantObject, NearestResult } from '../util/nearest'; import { nearestSquared } from '../util/nearest'; @@ -20,7 +21,7 @@ type Padding = { type ShrinkOrGrowPosition = 'top' | 'left' | 'bottom' | 'right' | 'vertical' | 'horizontal'; -export class BBox implements DistantObject, Interpolating { +export class BBox implements BBoxValues, BBoxContainsTester, DistantObject, Interpolating { x: number; y: number; width: number; diff --git a/packages/ag-charts-community/src/util/bboxinterface.ts b/packages/ag-charts-community/src/util/bboxinterface.ts new file mode 100644 index 0000000000..22b942b1c4 --- /dev/null +++ b/packages/ag-charts-community/src/util/bboxinterface.ts @@ -0,0 +1,14 @@ +export interface BBoxValues { + x: number; + y: number; + width: number; + height: number; +} + +export interface BBoxContainsTester { + containsPoint(x: number, y: number): boolean; +} + +export interface BBoxProvider { + getCachedBBox(): T; +} From f4baa650ef379a98124a84e6bdb39082ba715d25 Mon Sep 17 00:00:00 2001 From: Oli Legat Date: Fri, 10 May 2024 15:58:40 +0100 Subject: [PATCH 04/10] AG-11385 Initial ProxyInteractionService implementation --- .../src/chart/dom/focusIndicator.ts | 9 ++- .../src/chart/dom/proxyInteractionService.ts | 70 ++++++++++++++++++- packages/ag-charts-community/src/util/dom.ts | 9 +++ 3 files changed, 81 insertions(+), 7 deletions(-) diff --git a/packages/ag-charts-community/src/chart/dom/focusIndicator.ts b/packages/ag-charts-community/src/chart/dom/focusIndicator.ts index c3723a7465..0d1d4bd829 100644 --- a/packages/ag-charts-community/src/chart/dom/focusIndicator.ts +++ b/packages/ag-charts-community/src/chart/dom/focusIndicator.ts @@ -1,3 +1,5 @@ +import type { BBoxValues } from '../../util/bboxinterface'; +import { setElementBBox } from '../../util/dom'; import type { DOMManager } from './domManager'; import * as focusStyles from './focusStyles'; @@ -16,16 +18,13 @@ export class FocusIndicator { this.domManager.removeChild('canvas-overlay', focusStyles.block); } - public updateBBox(rect?: { x: number; y: number; width: number; height: number }) { + public updateBBox(rect?: BBoxValues) { if (rect == null) { this.element.classList.add(focusStyles.modifiers.hidden); return; } this.element.classList.remove(focusStyles.modifiers.hidden); - this.element.style.width = `${rect.width}px`; - this.element.style.height = `${rect.height}px`; - this.element.style.left = `${rect.x}px`; - this.element.style.top = `${rect.y}px`; + setElementBBox(this.element, rect); } } diff --git a/packages/ag-charts-community/src/chart/dom/proxyInteractionService.ts b/packages/ag-charts-community/src/chart/dom/proxyInteractionService.ts index 030f16f951..a41376ce44 100644 --- a/packages/ag-charts-community/src/chart/dom/proxyInteractionService.ts +++ b/packages/ag-charts-community/src/chart/dom/proxyInteractionService.ts @@ -1,15 +1,81 @@ +import { Debug } from '../../sparklines-util'; +import type { BBoxProvider, BBoxValues } from '../../util/bboxinterface'; +import { createElement, setElementBBox } from '../../util/dom'; +import type { UpdateService } from '../updateService'; import type { DOMManager } from './domManager'; import type { FocusIndicator } from './focusIndicator'; -// TODO: stub interface +type ProxyType = 'button'; + +type ProxyParams = { + type: T; + id: string; + textContext: string; + parent: HTMLElement; + bboxprovider: BBoxProvider; + onclick?: (ev: MouseEvent) => void; +}; + +type ProxyReturnMap = { + button: HTMLButtonElement; +}; + +type ProxyNode = { + element: HTMLElement; + bboxprovider: BBoxProvider; +}; + export class ProxyInteractionService { + // This debug option make the proxies button partially transparent instead of fully transparent. + // To enabled this option, set window.agChartsDebug = ['showDOMProxies']. + private readonly debugShowDOMProxies: boolean = Debug.check('showDOMProxies'); + + private nodes: ProxyNode[] = []; + constructor( + updateService: UpdateService, private readonly domManager: DOMManager, private readonly focusIndicator: FocusIndicator - ) {} + ) { + updateService.addListener('update-complete', () => this.update()); + } public destroy() { this.domManager; this.focusIndicator; } + + private update() { + for (const { element, bboxprovider } of this.nodes) { + setElementBBox(element, bboxprovider.getCachedBBox()); + } + } + + createProxyElement(params: ProxyParams): ProxyReturnMap[T] | undefined { + const { type, id, parent, bboxprovider, textContext, onclick } = params; + switch (type) { + case 'button': + const newButton = createElement('button'); + + newButton.id = id; + newButton.textContent = textContext; + newButton.style.pointerEvents = 'none'; + newButton.style.opacity = this.debugShowDOMProxies ? '0.25' : '0'; + newButton.addEventListener('focus', (_event: FocusEvent): any => { + newButton.style.setProperty('pointerEvents', null); + this.focusIndicator.updateBBox(bboxprovider.getCachedBBox()); + }); + newButton.addEventListener('blur', (_event: FocusEvent): any => { + newButton.style.pointerEvents = 'none'; + this.focusIndicator.updateBBox(undefined); + }); + if (onclick) { + newButton.addEventListener('click', onclick); + } + + this.nodes.push({ element: newButton, bboxprovider }); + parent.appendChild(newButton); + return newButton; + } + } } diff --git a/packages/ag-charts-community/src/util/dom.ts b/packages/ag-charts-community/src/util/dom.ts index 2405c4a28d..49c451b3a2 100644 --- a/packages/ag-charts-community/src/util/dom.ts +++ b/packages/ag-charts-community/src/util/dom.ts @@ -1,3 +1,5 @@ +import type { BBoxValues } from './bboxinterface'; + const verifiedGlobals = {} as { document: Document; window: Window }; if (typeof window !== 'undefined') { @@ -70,3 +72,10 @@ export function setDocument(document: Document) { export function setWindow(window: Window) { verifiedGlobals.window = window; } + +export function setElementBBox(element: HTMLElement, bbox: BBoxValues) { + element.style.width = `${bbox.width}px`; + element.style.height = `${bbox.height}px`; + element.style.left = `${bbox.x}px`; + element.style.top = `${bbox.y}px`; +} From d71573ce486702d8cec8de58e84f88aeccb5828b Mon Sep 17 00:00:00 2001 From: Oli Legat Date: Fri, 10 May 2024 16:22:16 +0100 Subject: [PATCH 05/10] AG-11385 Fix build --- packages/ag-charts-community/src/chart/chartContext.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ag-charts-community/src/chart/chartContext.ts b/packages/ag-charts-community/src/chart/chartContext.ts index b0460126ff..fe54892e06 100644 --- a/packages/ag-charts-community/src/chart/chartContext.ts +++ b/packages/ag-charts-community/src/chart/chartContext.ts @@ -88,11 +88,11 @@ export class ChartContext implements ModuleContext { this.keyNavManager = new KeyNavManager(this.interactionManager, this.domManager); this.focusIndicator = new FocusIndicator(this.domManager); this.regionManager = new RegionManager(this.interactionManager, this.keyNavManager, this.focusIndicator); - this.proxyInteractionService = new ProxyInteractionService(this.domManager, this.focusIndicator); this.toolbarManager = new ToolbarManager(); this.gestureDetector = new GestureDetector(this.domManager); this.layoutService = new LayoutService(); this.updateService = new UpdateService(updateCallback); + this.proxyInteractionService = new ProxyInteractionService(this.updateService, this.domManager, this.focusIndicator); this.seriesStateManager = new SeriesStateManager(); this.callbackCache = new CallbackCache(); From d7b6d3b5c25f247ba243075c8afb91497c68af1e Mon Sep 17 00:00:00 2001 From: Oli Legat Date: Fri, 10 May 2024 17:08:40 +0100 Subject: [PATCH 06/10] AG-11385 nx format --- packages/ag-charts-community/src/chart/chartContext.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/ag-charts-community/src/chart/chartContext.ts b/packages/ag-charts-community/src/chart/chartContext.ts index fe54892e06..49296df85b 100644 --- a/packages/ag-charts-community/src/chart/chartContext.ts +++ b/packages/ag-charts-community/src/chart/chartContext.ts @@ -92,7 +92,11 @@ export class ChartContext implements ModuleContext { this.gestureDetector = new GestureDetector(this.domManager); this.layoutService = new LayoutService(); this.updateService = new UpdateService(updateCallback); - this.proxyInteractionService = new ProxyInteractionService(this.updateService, this.domManager, this.focusIndicator); + this.proxyInteractionService = new ProxyInteractionService( + this.updateService, + this.domManager, + this.focusIndicator + ); this.seriesStateManager = new SeriesStateManager(); this.callbackCache = new CallbackCache(); From f26e67272716c5dad094edd9aba1c146b190e320 Mon Sep 17 00:00:00 2001 From: Oli Legat Date: Fri, 10 May 2024 17:13:40 +0100 Subject: [PATCH 07/10] AG-11385 Fix Sonar errors --- .../src/chart/chartContext.ts | 6 +-- .../src/chart/dom/focusIndicator.ts | 2 +- .../src/chart/dom/proxyInteractionService.ts | 46 +++++++++---------- 3 files changed, 24 insertions(+), 30 deletions(-) diff --git a/packages/ag-charts-community/src/chart/chartContext.ts b/packages/ag-charts-community/src/chart/chartContext.ts index 49296df85b..969c2d1d5f 100644 --- a/packages/ag-charts-community/src/chart/chartContext.ts +++ b/packages/ag-charts-community/src/chart/chartContext.ts @@ -92,11 +92,7 @@ export class ChartContext implements ModuleContext { this.gestureDetector = new GestureDetector(this.domManager); this.layoutService = new LayoutService(); this.updateService = new UpdateService(updateCallback); - this.proxyInteractionService = new ProxyInteractionService( - this.updateService, - this.domManager, - this.focusIndicator - ); + this.proxyInteractionService = new ProxyInteractionService(this.updateService, this.focusIndicator); this.seriesStateManager = new SeriesStateManager(); this.callbackCache = new CallbackCache(); diff --git a/packages/ag-charts-community/src/chart/dom/focusIndicator.ts b/packages/ag-charts-community/src/chart/dom/focusIndicator.ts index 0d1d4bd829..09b539077d 100644 --- a/packages/ag-charts-community/src/chart/dom/focusIndicator.ts +++ b/packages/ag-charts-community/src/chart/dom/focusIndicator.ts @@ -18,7 +18,7 @@ export class FocusIndicator { this.domManager.removeChild('canvas-overlay', focusStyles.block); } - public updateBBox(rect?: BBoxValues) { + public updateBBox(rect: BBoxValues | undefined) { if (rect == null) { this.element.classList.add(focusStyles.modifiers.hidden); return; diff --git a/packages/ag-charts-community/src/chart/dom/proxyInteractionService.ts b/packages/ag-charts-community/src/chart/dom/proxyInteractionService.ts index a41376ce44..d6983134b3 100644 --- a/packages/ag-charts-community/src/chart/dom/proxyInteractionService.ts +++ b/packages/ag-charts-community/src/chart/dom/proxyInteractionService.ts @@ -31,18 +31,17 @@ export class ProxyInteractionService { private readonly debugShowDOMProxies: boolean = Debug.check('showDOMProxies'); private nodes: ProxyNode[] = []; + private readonly destroyFns: (() => void)[]; constructor( updateService: UpdateService, - private readonly domManager: DOMManager, private readonly focusIndicator: FocusIndicator ) { - updateService.addListener('update-complete', () => this.update()); + this.destroyFns = [updateService.addListener('update-complete', () => this.update())]; } public destroy() { - this.domManager; - this.focusIndicator; + this.destroyFns.forEach((d) => d()); } private update() { @@ -53,29 +52,28 @@ export class ProxyInteractionService { createProxyElement(params: ProxyParams): ProxyReturnMap[T] | undefined { const { type, id, parent, bboxprovider, textContext, onclick } = params; - switch (type) { - case 'button': - const newButton = createElement('button'); + if (type === 'button') { + const newButton = createElement('button'); - newButton.id = id; - newButton.textContent = textContext; + newButton.id = id; + newButton.textContent = textContext; + newButton.style.pointerEvents = 'none'; + newButton.style.opacity = this.debugShowDOMProxies ? '0.25' : '0'; + newButton.addEventListener('focus', (_event: FocusEvent): any => { + newButton.style.setProperty('pointerEvents', null); + this.focusIndicator.updateBBox(bboxprovider.getCachedBBox()); + }); + newButton.addEventListener('blur', (_event: FocusEvent): any => { newButton.style.pointerEvents = 'none'; - newButton.style.opacity = this.debugShowDOMProxies ? '0.25' : '0'; - newButton.addEventListener('focus', (_event: FocusEvent): any => { - newButton.style.setProperty('pointerEvents', null); - this.focusIndicator.updateBBox(bboxprovider.getCachedBBox()); - }); - newButton.addEventListener('blur', (_event: FocusEvent): any => { - newButton.style.pointerEvents = 'none'; - this.focusIndicator.updateBBox(undefined); - }); - if (onclick) { - newButton.addEventListener('click', onclick); - } + this.focusIndicator.updateBBox(undefined); + }); + if (onclick) { + newButton.addEventListener('click', onclick); + } - this.nodes.push({ element: newButton, bboxprovider }); - parent.appendChild(newButton); - return newButton; + this.nodes.push({ element: newButton, bboxprovider }); + parent.appendChild(newButton); + return newButton; } } } From 99ddcd2ba62cd21257476dec919930cdcf60de82 Mon Sep 17 00:00:00 2001 From: Oli Legat Date: Fri, 10 May 2024 17:18:30 +0100 Subject: [PATCH 08/10] AG-11385 Unused import --- .../ag-charts-community/src/chart/dom/proxyInteractionService.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/ag-charts-community/src/chart/dom/proxyInteractionService.ts b/packages/ag-charts-community/src/chart/dom/proxyInteractionService.ts index d6983134b3..3f17c74749 100644 --- a/packages/ag-charts-community/src/chart/dom/proxyInteractionService.ts +++ b/packages/ag-charts-community/src/chart/dom/proxyInteractionService.ts @@ -2,7 +2,6 @@ import { Debug } from '../../sparklines-util'; import type { BBoxProvider, BBoxValues } from '../../util/bboxinterface'; import { createElement, setElementBBox } from '../../util/dom'; import type { UpdateService } from '../updateService'; -import type { DOMManager } from './domManager'; import type { FocusIndicator } from './focusIndicator'; type ProxyType = 'button'; From fd470b4f1c5c417cf1bd9b4054f3be083a6abdc2 Mon Sep 17 00:00:00 2001 From: Oli Legat Date: Fri, 10 May 2024 17:23:59 +0100 Subject: [PATCH 09/10] AG-11385 Fix broken import --- .../src/chart/dom/proxyInteractionService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ag-charts-community/src/chart/dom/proxyInteractionService.ts b/packages/ag-charts-community/src/chart/dom/proxyInteractionService.ts index 3f17c74749..b8373e951a 100644 --- a/packages/ag-charts-community/src/chart/dom/proxyInteractionService.ts +++ b/packages/ag-charts-community/src/chart/dom/proxyInteractionService.ts @@ -1,5 +1,5 @@ -import { Debug } from '../../sparklines-util'; import type { BBoxProvider, BBoxValues } from '../../util/bboxinterface'; +import { Debug } from '../../util/debug'; import { createElement, setElementBBox } from '../../util/dom'; import type { UpdateService } from '../updateService'; import type { FocusIndicator } from './focusIndicator'; From 037c39c69a226cba356ae97eb08ecb0a71f051fd Mon Sep 17 00:00:00 2001 From: Oli Legat Date: Fri, 10 May 2024 17:27:04 +0100 Subject: [PATCH 10/10] AG-11385 Fix lint error --- .../src/chart/dom/proxyInteractionService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ag-charts-community/src/chart/dom/proxyInteractionService.ts b/packages/ag-charts-community/src/chart/dom/proxyInteractionService.ts index b8373e951a..f6e6f6de57 100644 --- a/packages/ag-charts-community/src/chart/dom/proxyInteractionService.ts +++ b/packages/ag-charts-community/src/chart/dom/proxyInteractionService.ts @@ -29,7 +29,7 @@ export class ProxyInteractionService { // To enabled this option, set window.agChartsDebug = ['showDOMProxies']. private readonly debugShowDOMProxies: boolean = Debug.check('showDOMProxies'); - private nodes: ProxyNode[] = []; + private readonly nodes: ProxyNode[] = []; private readonly destroyFns: (() => void)[]; constructor(