From 82a3cf679550ce46802fcc73cb82e94e79dada08 Mon Sep 17 00:00:00 2001 From: Laurence Roberts Date: Thu, 25 Apr 2024 16:15:52 +0100 Subject: [PATCH 1/2] AG-11398 Add zoom independent axes option --- .../src/features/zoom/zoom.ts | 153 +++++++++++------- .../src/features/zoom/zoomModule.ts | 74 ++++----- .../src/features/zoom/zoomScroller.ts | 103 ++++++++---- .../src/features/zoom/zoomToolbar.ts | 90 ++++++++++- .../src/features/zoom/zoomTypes.ts | 1 + .../src/features/zoom/zoomUtils.ts | 6 +- .../ag-charts-types/src/chart/zoomOptions.ts | 5 + 7 files changed, 296 insertions(+), 136 deletions(-) diff --git a/packages/ag-charts-enterprise/src/features/zoom/zoom.ts b/packages/ag-charts-enterprise/src/features/zoom/zoom.ts index 32aaa891d9..ffc71a5a8e 100644 --- a/packages/ag-charts-enterprise/src/features/zoom/zoom.ts +++ b/packages/ag-charts-enterprise/src/features/zoom/zoom.ts @@ -111,6 +111,9 @@ export class Zoom extends _ModuleSupport.BaseModuleInstance implements _ModuleSu @Validate(BOOLEAN) public enableDoubleClickToReset = true; + @Validate(BOOLEAN) + private enableIndependentAxes = false; + @Validate(BOOLEAN) public enablePanning = true; @@ -178,10 +181,11 @@ export class Zoom extends _ModuleSupport.BaseModuleInstance implements _ModuleSu this.selector = new ZoomSelector(selectionRect); this.contextMenu = new ZoomContextMenu(ctx.contextMenuRegistry, ctx.zoomManager, this.updateZoom.bind(this)); this.toolbar = new ZoomToolbar( - this.ctx.toolbarManager, - this.ctx.zoomManager, + ctx.toolbarManager, + ctx.zoomManager, this.getResetZoom.bind(this), - this.updateZoom.bind(this) + this.updateZoom.bind(this), + this.updateAxisZoom.bind(this) ); const { Default, ZoomDrag, Animation } = _ModuleSupport.InteractionState; @@ -273,9 +277,9 @@ export class Zoom extends _ModuleSupport.BaseModuleInstance implements _ModuleSu const { x, y } = this.getResetZoom(); if (hoveredAxis) { - const { direction } = hoveredAxis; + const { id, direction } = hoveredAxis; const axisZoom = direction === ChartAxisDirection.X ? x : y; - this.updateAxisZoom(direction, axisZoom); + this.updateAxisZoom(id, direction, axisZoom); } else if (paddedRect?.containsPoint(event.offsetX, event.offsetY) && !event.preventZoomDblClick) { this.updateZoom({ x, y }); } @@ -352,7 +356,7 @@ export class Zoom extends _ModuleSupport.BaseModuleInstance implements _ModuleSu const anchor = direction === _ModuleSupport.ChartAxisDirection.X ? anchorPointX : anchorPointY; const axisZoom = zoomManager.getAxisZoom(axisId); const newZoom = axisDragger.update(event, direction, anchor, seriesRect, zoom, axisZoom); - this.updateAxisZoom(direction, newZoom); + this.updateAxisZoom(axisId, direction, newZoom); event.preventDefault(); break; @@ -416,75 +420,87 @@ export class Zoom extends _ModuleSupport.BaseModuleInstance implements _ModuleSu } private onNavZoom(event: _ModuleSupport.KeyNavEvent<'nav-zoom'>) { - const { enabled, enableScrolling, scroller, seriesRect } = this; + const { enabled, enableScrolling, scroller } = this; - if (!enabled || !enableScrolling || !seriesRect) return; + if (!enabled || !enableScrolling) return; event.preventDefault(); - this.updateZoom( - scroller.update( - { deltaY: -event.delta }, - this.getModuleProperties(this.isScaling()), - seriesRect, - this.getZoom() - ) - ); + this.updateZoom(scroller.updateDelta(event.delta, this.getModuleProperties(), this.getZoom())); } private onWheel(event: _ModuleSupport.PointerInteractionEvent<'wheel'>) { + const { enabled, enableAxisDragging, enablePanning, enableScrolling, hoveredAxis, paddedRect } = this; + + if (!enabled || !enableScrolling || !paddedRect) return; + + const isSeriesScrolling = paddedRect.containsPoint(event.offsetX, event.offsetY); + const isAxisScrolling = enableAxisDragging && hoveredAxis != null; + + const sourceEvent = event.sourceEvent as WheelEvent; + const { deltaX, deltaY } = sourceEvent; + const isHorizontalScrolling = deltaX != null && deltaY != null && Math.abs(deltaX) > Math.abs(deltaY); + + if (enablePanning && isHorizontalScrolling) { + this.onWheelPanning(event); + } else if (isSeriesScrolling || isAxisScrolling) { + this.onWheelScrolling(event); + } + } + + private onWheelPanning(event: _ModuleSupport.PointerInteractionEvent<'wheel'>) { + const { + scrollingStep, + scrollPanner, + seriesRect, + ctx: { zoomManager }, + } = this; + + if (!seriesRect) return; + + event.preventDefault(); + + const newZooms = scrollPanner.update(event, scrollingStep, seriesRect, zoomManager.getAxisZooms()); + for (const [axisId, { direction, zoom }] of Object.entries(newZooms)) { + this.updateAxisZoom(axisId, direction, zoom); + } + } + + private onWheelScrolling(event: _ModuleSupport.PointerInteractionEvent<'wheel'>) { const { - enabled, enableAxisDragging, - enablePanning, - enableScrolling, + enableIndependentAxes, hoveredAxis, - paddedRect, scroller, - scrollingStep, - scrollPanner, seriesRect, ctx: { zoomManager }, } = this; - if (!enabled || !enableScrolling || !paddedRect || !seriesRect) return; + if (!seriesRect) return; + + event.preventDefault(); - const isSeriesScrolling = paddedRect.containsPoint(event.offsetX, event.offsetY); const isAxisScrolling = enableAxisDragging && hoveredAxis != null; let isScalingX = this.isScalingX(); let isScalingY = this.isScalingY(); if (isAxisScrolling) { - isScalingX = hoveredAxis!.direction === _ModuleSupport.ChartAxisDirection.X; + isScalingX = hoveredAxis.direction === _ModuleSupport.ChartAxisDirection.X; isScalingY = !isScalingX; } - const sourceEvent = event.sourceEvent as WheelEvent; - const { deltaX, deltaY } = sourceEvent; - const isHorizontalScrolling = deltaX != null && deltaY != null && Math.abs(deltaX) > Math.abs(deltaY); - - if (enablePanning && isHorizontalScrolling) { - event.preventDefault(); + const props = this.getModuleProperties({ isScalingX, isScalingY }); - const newZooms = scrollPanner.update(event, scrollingStep, seriesRect, zoomManager.getAxisZooms()); - for (const { direction, zoom: newZoom } of Object.values(newZooms)) { - this.updateAxisZoom(direction, newZoom); + if (enableIndependentAxes) { + const newZooms = scroller.updateAxes(event, props, seriesRect, zoomManager.getAxisZooms()); + for (const [axisId, { direction, zoom }] of Object.entries(newZooms)) { + if (isAxisScrolling && hoveredAxis.id !== axisId) continue; + this.updateAxisZoom(axisId, direction, zoom); } - return; + } else { + const newZoom = scroller.update(event, props, seriesRect, this.getZoom()); + this.updateZoom(newZoom); } - - if (!isSeriesScrolling && !isAxisScrolling) return; - - event.preventDefault(); - - const newZoom = scroller.update( - event, - this.getModuleProperties({ isScalingX, isScalingY }), - seriesRect, - this.getZoom() - ); - - this.updateZoom(newZoom); } private onAxisLeave() { @@ -630,8 +646,8 @@ export class Zoom extends _ModuleSupport.BaseModuleInstance implements _ModuleSu const newZooms = panner.translateZooms(seriesRect, zoomManager.getAxisZooms(), event.deltaX, event.deltaY); - for (const { direction: panDirection, zoom: panZoom } of Object.values(newZooms)) { - this.updateAxisZoom(panDirection, panZoom); + for (const [axisId, { direction, zoom }] of Object.entries(newZooms)) { + this.updateAxisZoom(axisId, direction, zoom); } tooltipManager.updateTooltip(TOOLTIP_ID); @@ -661,10 +677,6 @@ export class Zoom extends _ModuleSupport.BaseModuleInstance implements _ModuleSu return this.shouldFlipXY ? this.axes === 'x' : this.axes === 'y'; } - private isScaling() { - return { isScalingX: this.isScalingX(), isScalingY: this.isScalingY() }; - } - private getAnchorPointX() { return this.shouldFlipXY ? this.anchorPointY : this.anchorPointX; } @@ -703,14 +715,36 @@ export class Zoom extends _ModuleSupport.BaseModuleInstance implements _ModuleSu } private updateAxisZoom( + axisId: string, direction: _ModuleSupport.ChartAxisDirection, - partialZoom: _ModuleSupport.ZoomState | undefined + axisZoom: _ModuleSupport.ZoomState | undefined ) { - if (!partialZoom) return; + const { + enableIndependentAxes, + minRatioX, + minRatioY, + ctx: { zoomManager }, + } = this; + + if (!axisZoom) return; + + const zoom = this.getZoom(); + + if (!enableIndependentAxes) { + zoom[direction] = axisZoom; + this.updateZoom(zoom); + return; + } + + const deltaAxis = axisZoom.max - axisZoom.min; + const deltaOld = zoom[direction].max - zoom[direction].min; + const minRatio = direction === ChartAxisDirection.X ? minRatioX : minRatioY; + + if (deltaAxis <= deltaOld && deltaAxis < minRatio) { + return; + } - const fullZoom = this.getZoom(); - fullZoom[direction] = partialZoom; - this.updateZoom(fullZoom); + zoomManager.updateAxisZoom('zoom', axisId, axisZoom); } private getZoom() { @@ -729,6 +763,7 @@ export class Zoom extends _ModuleSupport.BaseModuleInstance implements _ModuleSu anchorPointX: overrides?.anchorPointX ?? this.getAnchorPointX(), anchorPointY: overrides?.anchorPointY ?? this.getAnchorPointY(), enabled: overrides?.enabled ?? this.enabled, + independentAxes: overrides?.independentAxes ?? this.enableIndependentAxes, isScalingX: overrides?.isScalingX ?? this.isScalingX(), isScalingY: overrides?.isScalingY ?? this.isScalingY(), minRatioX: overrides?.minRatioX ?? this.minRatioX, diff --git a/packages/ag-charts-enterprise/src/features/zoom/zoomModule.ts b/packages/ag-charts-enterprise/src/features/zoom/zoomModule.ts index c3ab742395..e77ba01478 100644 --- a/packages/ag-charts-enterprise/src/features/zoom/zoomModule.ts +++ b/packages/ag-charts-enterprise/src/features/zoom/zoomModule.ts @@ -1,7 +1,43 @@ -import type { _ModuleSupport } from 'ag-charts-community'; +import type { AgZoomOptions, _ModuleSupport } from 'ag-charts-community'; import { Zoom } from './zoom'; +const buttons: AgZoomOptions['buttons'] = { + enabled: true, + buttons: [ + { + icon: 'zoom-out', + tooltip: 'toolbarZoomZoomOut', + value: 'zoom-out', + section: 'scale', + }, + { + icon: 'zoom-in', + tooltip: 'toolbarZoomZoomIn', + value: 'zoom-in', + section: 'scale', + }, + { + icon: 'pan-left', + tooltip: 'toolbarZoomPanLeft', + value: 'pan-left', + section: 'pan', + }, + { + icon: 'pan-right', + tooltip: 'toolbarZoomPanRight', + value: 'pan-right', + section: 'pan', + }, + { + icon: 'reset', + tooltip: 'toolbarZoomReset', + value: 'reset', + section: 'reset', + }, + ], +}; + export const ZoomModule: _ModuleSupport.Module = { type: 'root', optionsKey: 'zoom', @@ -14,41 +50,7 @@ export const ZoomModule: _ModuleSupport.Module = { anchorPointX: 'end', anchorPointY: 'middle', axes: 'x', - buttons: { - enabled: true, - buttons: [ - { - icon: 'zoom-out', - tooltip: 'toolbarZoomZoomOut', - value: 'zoom-out', - section: 'scale', - }, - { - icon: 'zoom-in', - tooltip: 'toolbarZoomZoomIn', - value: 'zoom-in', - section: 'scale', - }, - { - icon: 'pan-left', - tooltip: 'toolbarZoomPanLeft', - value: 'pan-left', - section: 'pan', - }, - { - icon: 'pan-right', - tooltip: 'toolbarZoomPanRight', - value: 'pan-right', - section: 'pan', - }, - { - icon: 'reset', - tooltip: 'toolbarZoomReset', - value: 'reset', - section: 'reset', - }, - ], - }, + buttons, enabled: false, enableAxisDragging: true, enableDoubleClickToReset: true, diff --git a/packages/ag-charts-enterprise/src/features/zoom/zoomScroller.ts b/packages/ag-charts-enterprise/src/features/zoom/zoomScroller.ts index 00ff1fc410..579327013d 100644 --- a/packages/ag-charts-enterprise/src/features/zoom/zoomScroller.ts +++ b/packages/ag-charts-enterprise/src/features/zoom/zoomScroller.ts @@ -1,42 +1,82 @@ -import type { _ModuleSupport, _Scene } from 'ag-charts-community'; +import { _ModuleSupport, _Scene } from 'ag-charts-community'; -import type { DefinedZoomState, ZoomProperties } from './zoomTypes'; +import type { AxisZoomStates, DefinedZoomState, ZoomProperties } from './zoomTypes'; import { + constrainAxis, constrainZoom, definedZoomState, dx, dy, pointToRatio, scaleZoomAxisWithAnchor, - scaleZoomAxisWithPoint, } from './zoomUtils'; export class ZoomScroller { + updateAxes( + event: _ModuleSupport.PointerInteractionEvent<'wheel'>, + props: ZoomProperties, + bbox: _Scene.BBox, + zooms: AxisZoomStates + ): AxisZoomStates { + const sourceEvent = event.sourceEvent as WheelEvent; + const newZooms: AxisZoomStates = {}; + const { anchorPointX, anchorPointY, isScalingX, isScalingY, scrollingStep } = props; + + // Convert the cursor position to coordinates as a ratio of 0 to 1 + const origin = pointToRatio( + bbox, + sourceEvent.offsetX ?? sourceEvent.clientX, + sourceEvent.offsetY ?? sourceEvent.clientY + ); + + for (const [axisId, { direction, zoom }] of Object.entries(zooms)) { + if (zoom == null) continue; + + let newZoom = { ...zoom }; + + const delta = scrollingStep * event.deltaY * (zoom.max - zoom.min); + if (direction === _ModuleSupport.ChartAxisDirection.X && isScalingX) { + newZoom.max += delta; + newZoom = scaleZoomAxisWithAnchor(newZoom, zoom, anchorPointX, origin.x); + } else if (direction === _ModuleSupport.ChartAxisDirection.Y && isScalingY) { + newZoom.max += delta; + newZoom = scaleZoomAxisWithAnchor(newZoom, zoom, anchorPointY, origin.y); + } else { + continue; + } + + newZooms[axisId] = { direction, zoom: constrainAxis(newZoom) }; + } + + return newZooms; + } + update( - event: { deltaY: number; sourceEvent?: Event }, + event: _ModuleSupport.PointerInteractionEvent<'wheel'>, props: ZoomProperties, bbox: _Scene.BBox, oldZoom: DefinedZoomState ): DefinedZoomState { - const sourceEvent = event.sourceEvent as WheelEvent | undefined; - + const sourceEvent = event.sourceEvent as WheelEvent; const { anchorPointX, anchorPointY, isScalingX, isScalingY, scrollingStep } = props; + const origin = pointToRatio( + bbox, + sourceEvent.offsetX ?? sourceEvent.clientX, + sourceEvent.offsetY ?? sourceEvent.clientY + ); + // Scale the zoom bounding box const dir = event.deltaY; let newZoom = definedZoomState(oldZoom); newZoom.x.max += isScalingX ? scrollingStep * dir * dx(oldZoom) : 0; newZoom.y.max += isScalingY ? scrollingStep * dir * dy(oldZoom) : 0; - if (sourceEvent && ((anchorPointX === 'pointer' && isScalingX) || (anchorPointY === 'pointer' && isScalingY))) { - newZoom = this.scaleZoomToPointer(sourceEvent, isScalingX, isScalingY, bbox, oldZoom, newZoom); - } else { - if (isScalingX) { - newZoom.x = scaleZoomAxisWithAnchor(newZoom.x, oldZoom.x, anchorPointX); - } - if (isScalingY) { - newZoom.y = scaleZoomAxisWithAnchor(newZoom.y, oldZoom.y, anchorPointY); - } + if (isScalingX) { + newZoom.x = scaleZoomAxisWithAnchor(newZoom.x, oldZoom.x, anchorPointX, origin.x); + } + if (isScalingY) { + newZoom.y = scaleZoomAxisWithAnchor(newZoom.y, oldZoom.y, anchorPointY, origin.y); } // Constrain the zoom bounding box to remain within the ultimate bounds of 0,0 and 1,1 @@ -45,24 +85,23 @@ export class ZoomScroller { return newZoom; } - private scaleZoomToPointer( - sourceEvent: WheelEvent, - isScalingX: boolean, - isScalingY: boolean, - bbox: _Scene.BBox, - oldZoom: DefinedZoomState, - newZoom: DefinedZoomState - ) { - // Convert the cursor position to coordinates as a ratio of 0 to 1 - const origin = pointToRatio( - bbox, - sourceEvent.offsetX ?? sourceEvent.clientX, - sourceEvent.offsetY ?? sourceEvent.clientY - ); + updateDelta(delta: number, props: ZoomProperties, oldZoom: DefinedZoomState): DefinedZoomState { + const { anchorPointX, anchorPointY, isScalingX, isScalingY, scrollingStep } = props; + + // Scale the zoom bounding box + let newZoom = definedZoomState(oldZoom); + newZoom.x.max += isScalingX ? scrollingStep * -delta * dx(oldZoom) : 0; + newZoom.y.max += isScalingY ? scrollingStep * -delta * dy(oldZoom) : 0; - // Translate the zoom bounding box such that the cursor remains over the same position as before - newZoom.x = isScalingX ? scaleZoomAxisWithPoint(newZoom.x, oldZoom.x, origin.x) : newZoom.x; - newZoom.y = isScalingY ? scaleZoomAxisWithPoint(newZoom.y, oldZoom.y, origin.y) : newZoom.y; + if (isScalingX) { + newZoom.x = scaleZoomAxisWithAnchor(newZoom.x, oldZoom.x, anchorPointX); + } + if (isScalingY) { + newZoom.y = scaleZoomAxisWithAnchor(newZoom.y, oldZoom.y, anchorPointY); + } + + // Constrain the zoom bounding box to remain within the ultimate bounds of 0,0 and 1,1 + newZoom = constrainZoom(newZoom); return newZoom; } diff --git a/packages/ag-charts-enterprise/src/features/zoom/zoomToolbar.ts b/packages/ag-charts-enterprise/src/features/zoom/zoomToolbar.ts index 81eeb7f47d..6ce06899d5 100644 --- a/packages/ag-charts-enterprise/src/features/zoom/zoomToolbar.ts +++ b/packages/ag-charts-enterprise/src/features/zoom/zoomToolbar.ts @@ -5,6 +5,7 @@ import { DEFAULT_ANCHOR_POINT_X, DEFAULT_ANCHOR_POINT_Y, UNIT, + constrainAxis, constrainZoom, definedZoomState, dx, @@ -16,14 +17,19 @@ import { unitZoomState, } from './zoomUtils'; -const { ToolbarManager } = _ModuleSupport; +const { ChartAxisDirection, ToolbarManager } = _ModuleSupport; export class ZoomToolbar { constructor( private readonly toolbarManager: _ModuleSupport.ToolbarManager, private readonly zoomManager: _ModuleSupport.ZoomManager, private readonly getResetZoom: () => DefinedZoomState, - private readonly updateZoom: (zoom: DefinedZoomState) => void + private readonly updateZoom: (zoom: DefinedZoomState) => void, + private readonly updateAxisZoom: ( + axisId: string, + direction: _ModuleSupport.ChartAxisDirection, + partialZoom: _ModuleSupport.ZoomState | undefined + ) => void ) {} public toggle(enabled: boolean | undefined, zoom: DefinedZoomState, props: ZoomProperties) { @@ -77,6 +83,76 @@ export class ZoomToolbar { private onButtonPressZoom(event: _ModuleSupport.ToolbarButtonPressedEvent, props: ZoomProperties) { if (!ToolbarManager.isGroup('zoom', event)) return; + if (props.independentAxes && event.value !== 'reset') { + const axisZooms = this.zoomManager.getAxisZooms(); + for (const [axisId, { direction, zoom }] of Object.entries(axisZooms)) { + if (zoom == null) continue; + this.onButtonPressZoomAxis(event, props, axisId, direction, zoom); + } + } else { + this.onButtonPressZoomUnified(event, props); + } + } + + private onButtonPressZoomAxis( + event: _ModuleSupport.ToolbarButtonPressedEvent, + props: ZoomProperties, + axisId: string, + direction: _ModuleSupport.ChartAxisDirection, + zoom: _ModuleSupport.ZoomState + ) { + if (!ToolbarManager.isGroup('zoom', event)) return; + + const { anchorPointX, anchorPointY, isScalingX, isScalingY, scrollingStep } = props; + + let newZoom = { ...zoom }; + const delta = zoom.max - zoom.min; + + switch (event.value) { + case 'pan-start': + newZoom.max = delta; + newZoom.min = 0; + break; + + case 'pan-end': + newZoom.min = newZoom.max - delta; + newZoom.max = UNIT.max; + break; + + case 'pan-left': + newZoom.min -= delta * scrollingStep; + newZoom.max -= delta * scrollingStep; + break; + + case 'pan-right': + newZoom.min += delta * scrollingStep; + newZoom.max += delta * scrollingStep; + break; + + case 'zoom-in': + case 'zoom-out': { + const isDirectionX = direction === ChartAxisDirection.X; + const isScalingDirection = (isDirectionX && isScalingX) || (!isDirectionX && isScalingY); + + let scale = event.value === 'zoom-in' ? 1 - scrollingStep : 1 + scrollingStep; + if (!isScalingDirection) scale = 1; + + const useAnchorPointX = anchorPointX === 'pointer' ? DEFAULT_ANCHOR_POINT_X : anchorPointX; + const useAnchorPointY = anchorPointY === 'pointer' ? DEFAULT_ANCHOR_POINT_Y : anchorPointY; + const useAnchorPoint = isDirectionX ? useAnchorPointX : useAnchorPointY; + + newZoom.max = newZoom.min + (newZoom.max - newZoom.min) * scale; + newZoom = scaleZoomAxisWithAnchor(newZoom, zoom, useAnchorPoint); + break; + } + } + + this.updateAxisZoom(axisId, direction, constrainAxis(newZoom)); + } + + private onButtonPressZoomUnified(event: _ModuleSupport.ToolbarButtonPressedEvent, props: ZoomProperties) { + if (!ToolbarManager.isGroup('zoom', event)) return; + const { anchorPointX, anchorPointY, isScalingX, isScalingY, scrollingStep } = props; const oldZoom = definedZoomState(this.zoomManager.getZoom()); @@ -98,16 +174,16 @@ export class ZoomToolbar { break; case 'pan-left': - case 'pan-right': { - const distance = scrollingStep * dx(zoom); - zoom = translateZoom(zoom, event.value === 'pan-left' ? -distance : distance, 0); + zoom = translateZoom(zoom, -dx(zoom) * scrollingStep, 0); + break; + + case 'pan-right': + zoom = translateZoom(zoom, dx(zoom) * scrollingStep, 0); break; - } case 'zoom-in': case 'zoom-out': { const scale = event.value === 'zoom-in' ? 1 - scrollingStep : 1 + scrollingStep; - const useAnchorPointX = anchorPointX === 'pointer' ? DEFAULT_ANCHOR_POINT_X : anchorPointX; const useAnchorPointY = anchorPointY === 'pointer' ? DEFAULT_ANCHOR_POINT_Y : anchorPointY; diff --git a/packages/ag-charts-enterprise/src/features/zoom/zoomTypes.ts b/packages/ag-charts-enterprise/src/features/zoom/zoomTypes.ts index 7f675d7040..744d2b3d07 100644 --- a/packages/ag-charts-enterprise/src/features/zoom/zoomTypes.ts +++ b/packages/ag-charts-enterprise/src/features/zoom/zoomTypes.ts @@ -23,6 +23,7 @@ export interface ZoomProperties { anchorPointX: AgZoomAnchorPoint; anchorPointY: AgZoomAnchorPoint; enabled: boolean; + independentAxes: boolean; isScalingX: boolean; isScalingY: boolean; minRatioX: number; diff --git a/packages/ag-charts-enterprise/src/features/zoom/zoomUtils.ts b/packages/ag-charts-enterprise/src/features/zoom/zoomUtils.ts index a2f5cbea14..54557e48b2 100644 --- a/packages/ag-charts-enterprise/src/features/zoom/zoomUtils.ts +++ b/packages/ag-charts-enterprise/src/features/zoom/zoomUtils.ts @@ -130,7 +130,9 @@ export function scaleZoomAxisWithPoint( oldState: _ModuleSupport.ZoomState, origin: number ) { - const scaledOrigin = origin * (1 - (oldState.max - oldState.min - (newState.max - newState.min))); + const newDelta = newState.max - newState.min; + const oldDelta = oldState.max - oldState.min; + const scaledOrigin = origin * (1 - (oldDelta - newDelta)); const translation = origin - scaledOrigin; const min = newState.min + translation; @@ -158,7 +160,7 @@ export function constrainZoom(zoom: DefinedZoomState): DefinedZoomState { return after; } -function constrainAxis(axis: { min: number; max: number }) { +export function constrainAxis(axis: { min: number; max: number }) { const size = axis.max - axis.min; let min = axis.max > UNIT.max ? UNIT.max - size : axis.min; diff --git a/packages/ag-charts-types/src/chart/zoomOptions.ts b/packages/ag-charts-types/src/chart/zoomOptions.ts index 939bc1a3cf..569dd625bb 100644 --- a/packages/ag-charts-types/src/chart/zoomOptions.ts +++ b/packages/ag-charts-types/src/chart/zoomOptions.ts @@ -71,6 +71,11 @@ export interface AgZoomOptions { * Default: `true` */ enableDoubleClickToReset?: boolean; + /** + * Set to `true` to enable independent axis zooming. + * Default: `false` + */ + enableIndependentAxes?: boolean; /** * Set to `true` to enable panning while zoomed. * From 64298c2bbd6363b901505c312c03fbe029166c82 Mon Sep 17 00:00:00 2001 From: Alan Treadway Date: Fri, 21 Jun 2024 10:18:12 +0100 Subject: [PATCH 2/2] AG-11398 - Hide independent zoom options and apply in FC preset. --- .../src/api/preset/candlestickVolumePreset.ts | 2 ++ packages/ag-charts-enterprise/src/features/zoom/zoom.ts | 2 +- packages/ag-charts-types/src/chart/zoomOptions.ts | 5 ----- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/ag-charts-community/src/api/preset/candlestickVolumePreset.ts b/packages/ag-charts-community/src/api/preset/candlestickVolumePreset.ts index cab97a8ae3..a6fea2bfc1 100644 --- a/packages/ag-charts-community/src/api/preset/candlestickVolumePreset.ts +++ b/packages/ag-charts-community/src/api/preset/candlestickVolumePreset.ts @@ -31,6 +31,8 @@ export function candlestickVolumePreset( return { zoom: { enabled: true, + // @ts-expect-error + enableIndependentAxes: true, }, toolbar: { ranges: { diff --git a/packages/ag-charts-enterprise/src/features/zoom/zoom.ts b/packages/ag-charts-enterprise/src/features/zoom/zoom.ts index ffc71a5a8e..5fb5ce8fe2 100644 --- a/packages/ag-charts-enterprise/src/features/zoom/zoom.ts +++ b/packages/ag-charts-enterprise/src/features/zoom/zoom.ts @@ -112,7 +112,7 @@ export class Zoom extends _ModuleSupport.BaseModuleInstance implements _ModuleSu public enableDoubleClickToReset = true; @Validate(BOOLEAN) - private enableIndependentAxes = false; + public enableIndependentAxes = false; @Validate(BOOLEAN) public enablePanning = true; diff --git a/packages/ag-charts-types/src/chart/zoomOptions.ts b/packages/ag-charts-types/src/chart/zoomOptions.ts index 569dd625bb..939bc1a3cf 100644 --- a/packages/ag-charts-types/src/chart/zoomOptions.ts +++ b/packages/ag-charts-types/src/chart/zoomOptions.ts @@ -71,11 +71,6 @@ export interface AgZoomOptions { * Default: `true` */ enableDoubleClickToReset?: boolean; - /** - * Set to `true` to enable independent axis zooming. - * Default: `false` - */ - enableIndependentAxes?: boolean; /** * Set to `true` to enable panning while zoomed. *