Skip to content

Commit

Permalink
[FEAT] Allow to zoom the BPMN diagram with mouse wheel (#734)
Browse files Browse the repository at this point in the history
Add zoom support with <kbd>CTRL</kbd> + mouse wheel for Linux and Windows.
Minimum support for macOS.
  • Loading branch information
tbouffard authored Oct 15, 2020
1 parent 9f73128 commit c48462b
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 10 deletions.
47 changes: 42 additions & 5 deletions src/component/mxgraph/MxGraphConfigurator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ import ShapeConfigurator from './config/ShapeConfigurator';
import MarkerConfigurator from './config/MarkerConfigurator';
import MxClientConfigurator from './config/MxClientConfigurator';
import { BpmnVisualizationOptions } from '../BpmnVisualization';
import { mxgraph } from 'ts-mxgraph';
// TODO unable to load mxClient from [email protected]
declare const mxClient: typeof mxgraph.mxClient;

/**
* Configure the mxGraph graph that can be used by the lib
Expand All @@ -29,20 +32,21 @@ import { BpmnVisualizationOptions } from '../BpmnVisualization';
export default class MxGraphConfigurator {
private readonly graph: mxGraph;

constructor(container: HTMLElement) {
constructor(readonly container: HTMLElement) {
this.graph = new mxGraph(container);
}

public configure(options?: BpmnVisualizationOptions): mxGraph {
this.configureGraph(options);
this.configureGraph();
this.configureMouseNavigationSupport(options);
new StyleConfigurator(this.graph).configureStyles();
new ShapeConfigurator().configureShapes();
new MarkerConfigurator().configureMarkers();
new MxClientConfigurator().configureMxCodec();
return this.graph;
}

private configureGraph(options?: BpmnVisualizationOptions): void {
private configureGraph(): void {
this.graph.setCellsLocked(true);
this.graph.setCellsSelectable(false);
this.graph.setEdgeLabelsMovable(false);
Expand All @@ -56,15 +60,48 @@ export default class MxGraphConfigurator {
// Disable folding for container mxCell (pool, lane, sub process, call activity) because we don't need it.
// This also prevents requesting unavailable images (see #185) as we don't override mxGraph folding default images.
this.graph.foldingEnabled = false;
}

private configureMouseNavigationSupport(options?: BpmnVisualizationOptions): void {
const mouseNavigationSupport = options?.mouseNavigationSupport;
// Pan configuration
if (options?.mouseNavigationSupport) {
if (mouseNavigationSupport) {
this.graph.panningHandler.useLeftButtonForPanning = true;
this.graph.panningHandler.ignoreCell = true; // ok here as we cannot select cells
this.graph.setPanning(true);
} else {
this.graph.setPanning(false);
this.graph.panningHandler.setPinchEnabled(false); // ensure gesture support is disabled (pan and zoom)
this.graph.panningHandler.setPinchEnabled(false); // ensure gesture support is disabled (zoom only for now!)
}

this.configureMouseEvent(mouseNavigationSupport);
}

private configureMouseEvent(activated = false): void {
if (!activated) {
return;
}

mxEvent.addMouseWheelListener((event: Event, up: boolean) => {
// TODO review type: this hack is due to the introduction of mxgraph-type-definitions
const evt = (event as unknown) as MouseEvent;
if (mxEvent.isConsumed((evt as unknown) as mxMouseEvent)) {
return;
}
// only the ctrl key or the meta key on mac
const isZoomWheelEvent = (evt.ctrlKey || (mxClient.IS_MAC && evt.metaKey)) && !evt.altKey && !evt.shiftKey;
if (isZoomWheelEvent) {
this.zoom(up);
mxEvent.consume(evt);
}
}, this.container);
}

private zoom(zoomIn: boolean): void {
if (zoomIn) {
this.graph.zoomIn();
} else {
this.graph.zoomOut();
}
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
48 changes: 43 additions & 5 deletions test/e2e/bpmn.navigation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,30 @@
* limitations under the License.
*/
import { BpmnDiagramPreparation, ImageSnapshotConfigurator, ImageSnapshotThresholdConfig, PageTester } from './helpers/visu-utils';
import { Mouse } from 'puppeteer';
// FIXME - to be fixed when new release of puppeteer comes out
// wheel is added in version @types/puppeteer 2.1.5 but for some reason not in 3.0.2
// perhaps will be soon available in 3.0.3
// @see https://github.com/puppeteer/puppeteer/pull/6141/files
interface MouseWheelOptions {
/**
* X delta in CSS pixels for mouse wheel event (default: 0). Positive values emulate a scroll up and negative values a scroll down event.
* @default 0
*/
deltaX?: number;
/**
* Y delta in CSS pixels for mouse wheel event (default: 0). Positive values emulate a scroll right and negative values a scroll left event.
* @default 0
*/
deltaY?: number;
}
interface MouseWithWheel extends Mouse {
/**
* Dispatches a `mousewheel` event.
* @param options The mouse wheel options.
*/
wheel(options?: MouseWheelOptions): Promise<void>;
}

describe('diagram navigation', () => {
const imageSnapshotConfigurator = new ImageSnapshotConfigurator(
Expand All @@ -38,15 +62,18 @@ describe('diagram navigation', () => {

const pageTester = new PageTester(bpmnDiagramPreparation, 'bpmn-viewport', 'BPMN Visualization - Diagram Navigation');

it('mouse panning', async () => {
const fileName = 'simple-2_start_events-1_task';
const fileName = 'simple-2_start_events-1_task';
let viewportCenterX: number;
let viewportCenterY: number;
beforeEach(async () => {
const bpmnViewportElementHandle = await pageTester.expectBpmnDiagramToBeDisplayed(fileName);

const bounding_box = await bpmnViewportElementHandle.boundingBox();
viewportCenterX = bounding_box.x + bounding_box.width / 2;
viewportCenterY = bounding_box.y + bounding_box.height / 2;
});

it('mouse panning', async () => {
// simulate mouse panning
const viewportCenterX = bounding_box.x + bounding_box.width / 2;
const viewportCenterY = bounding_box.y + bounding_box.height / 2;
await page.mouse.move(viewportCenterX, viewportCenterY);
await page.mouse.down();
await page.mouse.move(viewportCenterX + 150, viewportCenterY + 40);
Expand All @@ -55,4 +82,15 @@ describe('diagram navigation', () => {
const image = await page.screenshot({ fullPage: true });
expect(image).toMatchImageSnapshot(imageSnapshotConfigurator.getConfig(fileName));
});

it.each(['zoom in', 'zoom out'])(`ctrl + mouse: %s`, async (zoom: string) => {
const deltaX = zoom === 'zoom in' ? -100 : 100;
// simulate mouse+ctrl zoom
await page.mouse.move(viewportCenterX, viewportCenterY);
await page.keyboard.down('Control');
await (<MouseWithWheel>page.mouse).wheel({ deltaX: deltaX });

const image = await page.screenshot({ fullPage: true });
expect(image).toMatchImageSnapshot(imageSnapshotConfigurator.getConfig(fileName));
});
});

0 comments on commit c48462b

Please sign in to comment.