diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d10979c..24bcaa49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p - Fix undo/redo not adding or removing graph elements or links after `DataDiagramModel.discardLayout()` call (e.g. by a reload from `useLoadedWorkspace()`). - Fix small input width overflow in `UnifiedSearch` when resized to the minimum size. - Fix `Navigator` to avoid animating expand/collapse transition on the initial mount. +- Fix unintended scroll instead of canvas zoom on mouse wheel event over non-scrollable elements rendered with `CanvasPlaceAt` (when `zoomOptions.requireCtrl` is `false`): + * Allow to explicitly prevent zoom on mouse wheel over a non-scrollable element with `data-reactodia-prevent-zoom` attribute. #### ⏱ Performance - Fix canvas panning optimization not being applied due to incorrect `z-index` value. diff --git a/src/diagram/canvasArea.tsx b/src/diagram/canvasArea.tsx index 48e6262b..21cd77d6 100644 --- a/src/diagram/canvasArea.tsx +++ b/src/diagram/canvasArea.tsx @@ -4,6 +4,7 @@ import cx from 'clsx'; import { delay } from '../coreUtils/async'; import { ColorSchemeApi } from '../coreUtils/colorScheme'; +import { findParentWithin } from '../coreUtils/dom'; import { EventObserver, Events, EventSource } from '../coreUtils/events'; import { @@ -27,7 +28,7 @@ import type { CommandBatch } from './history'; import { LinkLabelLayer, LinkLayer, LinkMarkers } from './linkLayer'; import type { DiagramModel } from './model'; import { - CanvasPlaceLayerContext, CanvasPlaceLayer, createPlaceLayerContext, + CanvasPlaceLayerContext, CanvasPlaceLayer, createPlaceLayerContext, isCanvasPlaceLayer, } from './placeLayer'; import { MutableRenderingState, RenderingLayer } from './renderingState'; @@ -458,18 +459,39 @@ class CanvasController implements CanvasApi { }; getWheelToScaleDelta = (e: WheelEvent): number | undefined => { - return this.shouldZoom(e) ? wheelToScaleDeltaDefault(e) : undefined; + switch (this.shouldZoom(e)) { + case 'zoom': + return wheelToScaleDeltaDefault(e); + case 'prevent': + return 0; + case 'default': + default: { + return undefined; + } + } }; - private shouldZoom(e: WheelEvent): boolean { + private shouldZoom(e: WheelEvent): 'zoom' | 'prevent' | 'default' { const {requireCtrl} = this.zoomOptions; const target = e.target; if (requireCtrl) { - return e.ctrlKey; + return e.ctrlKey ? 'zoom' : 'default'; } else if (e.ctrlKey) { - return true; + return 'zoom'; } - return this.isEventFromCellLayer(e) && target instanceof Node && ( + + if (this.paper.root && target instanceof window.Element) { + const layer = findParentWithin(target, this.paper.root, isCanvasPlaceLayer); + if (layer) { + return ( + hasScrollableParent(target, layer) ? 'default' : + findParentWithin(target, layer, doesPreventZoom) ? 'prevent' : + 'zoom' + ); + } + } + + const fromStaticCells = this.isEventFromCellLayer(e) && target instanceof Node && ( this.paper.root === target || this.paper.pane === target || this.paper.pane?.firstChild === target || @@ -479,6 +501,7 @@ class CanvasController implements CanvasApi { !hasScrollableParent(target, this.elementLayer.current) ) ); + return fromStaticCells ? 'zoom' : 'default'; } get metrics(): CanvasMetrics { @@ -725,3 +748,7 @@ function hasScrollableParent(target: Node, parent: Node | null): boolean { } return false; } + +function doesPreventZoom(target: globalThis.Element): boolean { + return target.hasAttribute('data-reactodia-prevent-zoom'); +} diff --git a/src/diagram/placeLayer.tsx b/src/diagram/placeLayer.tsx index 86c77ba6..8a87e700 100644 --- a/src/diagram/placeLayer.tsx +++ b/src/diagram/placeLayer.tsx @@ -48,6 +48,10 @@ export function CanvasPlaceLayer(props: CanvasPlaceLayerProps) { return
; } +export function isCanvasPlaceLayer(element: Element): boolean { + return element.hasAttribute('data-reactodia-place-layer'); +} + /** * Canvas layer to render widget components at, from the bottom to the top: * - `underlay` - placed under any diagram content; diff --git a/src/widgets/dialog.tsx b/src/widgets/dialog.tsx index 91c68caa..42d3a849 100644 --- a/src/widgets/dialog.tsx +++ b/src/widgets/dialog.tsx @@ -314,7 +314,8 @@ export class Dialog extends React.Component