This commit is contained in:
@@ -26,6 +26,16 @@ import UniqueConstraintSchema from '../../../../../browser/server_groups/servers
|
||||
import PrimaryKeySchema from '../../../../../browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/static/js/primary_key.ui';
|
||||
import { boundingBoxFromPolygons } from '@projectstorm/geometry';
|
||||
|
||||
/* Modifier keys as recorded by the canvas event bus, mapped to the matching
|
||||
* flag on mouse events. */
|
||||
const MODIFIER_KEYS = {
|
||||
shift: 'shiftKey',
|
||||
control: 'ctrlKey',
|
||||
alt: 'altKey',
|
||||
meta: 'metaKey',
|
||||
};
|
||||
const MOUSE_EVENT_TYPES = ['mousedown', 'mousemove', 'mouseup', 'wheel'];
|
||||
|
||||
export default class ERDCore {
|
||||
constructor() {
|
||||
this._cache = {};
|
||||
@@ -38,7 +48,40 @@ export default class ERDCore {
|
||||
}
|
||||
|
||||
createEngine(options) {
|
||||
return createEngine(options);
|
||||
let engine = createEngine(options);
|
||||
this.releaseStaleModifierKeysOnMouseEvents(engine.getActionEventBus());
|
||||
return engine;
|
||||
}
|
||||
|
||||
/*
|
||||
* The canvas event bus only learns that a key was released from its keyup
|
||||
* event. If that never reaches the page (for example, Shift is released
|
||||
* whilst focus is elsewhere), it believes Shift is still held and stays in
|
||||
* the selecting state, where every drag draws a selection box, and tables
|
||||
* can no longer be moved nor the canvas panned. Mouse events carry the real
|
||||
* modifier state, so release any key the event says is no longer held
|
||||
* before the event is handled.
|
||||
*/
|
||||
releaseStaleModifierKeysOnMouseEvents(eventBus) {
|
||||
const fireAction = eventBus.fireAction.bind(eventBus);
|
||||
eventBus.fireAction = (actionEvent)=>{
|
||||
const event = actionEvent?.event;
|
||||
if(MOUSE_EVENT_TYPES.includes(event?.type)) {
|
||||
for(const key of eventBus.getKeys()) {
|
||||
if(MODIFIER_KEYS[key] && event[MODIFIER_KEYS[key]] === false) {
|
||||
fireAction({
|
||||
event: {
|
||||
type: 'keyup',
|
||||
key: key,
|
||||
preventDefault: () => {/*This is intentional (SonarQube)*/},
|
||||
stopPropagation: () => {/*This is intentional (SonarQube)*/},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
fireAction(actionEvent);
|
||||
};
|
||||
}
|
||||
|
||||
initializeEngine() {
|
||||
|
||||
62
web/regression/javascript/erd/erd_modifier_keys_spec.js
Normal file
62
web/regression/javascript/erd/erd_modifier_keys_spec.js
Normal file
@@ -0,0 +1,62 @@
|
||||
/////////////////////////////////////////////////////////////
|
||||
//
|
||||
// pgAdmin 4 - PostgreSQL Tools
|
||||
//
|
||||
// Copyright (C) 2013 - 2026, The pgAdmin Development Team
|
||||
// This software is released under the PostgreSQL Licence
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
import ERDCore from 'pgadmin.tools.erd/erd_tool/ERDCore';
|
||||
|
||||
describe('ERDCore modifier keys', ()=>{
|
||||
let engine, canvas;
|
||||
|
||||
const fire = (event)=>{
|
||||
engine.getActionEventBus().fireAction({
|
||||
event: {
|
||||
preventDefault: ()=>{/* intentional */},
|
||||
stopPropagation: ()=>{/* intentional */},
|
||||
target: canvas,
|
||||
...event,
|
||||
},
|
||||
});
|
||||
};
|
||||
const currentState = ()=>engine.getStateMachine().getCurrentState().getOptions().name;
|
||||
|
||||
beforeEach(()=>{
|
||||
engine = new ERDCore().getEngine();
|
||||
canvas = document.createElement('div');
|
||||
document.body.appendChild(canvas);
|
||||
engine.setCanvas(canvas);
|
||||
});
|
||||
|
||||
afterEach(()=>{
|
||||
canvas.remove();
|
||||
});
|
||||
|
||||
it('draws a selection box when dragging with Shift held', ()=>{
|
||||
fire({type: 'keydown', key: 'Shift'});
|
||||
fire({type: 'mousedown', clientX: 10, clientY: 10, buttons: 1, shiftKey: true});
|
||||
expect(currentState()).toBe('selection-box');
|
||||
});
|
||||
|
||||
it('pans the canvas once Shift is released, even if the keyup was missed', ()=>{
|
||||
fire({type: 'keydown', key: 'Shift'});
|
||||
fire({type: 'mousedown', clientX: 10, clientY: 10, buttons: 1, shiftKey: true});
|
||||
fire({type: 'mousemove', clientX: 50, clientY: 50, buttons: 1, shiftKey: true});
|
||||
fire({type: 'mouseup', clientX: 50, clientY: 50, buttons: 0, shiftKey: true});
|
||||
expect(currentState()).toBe('selecting');
|
||||
|
||||
/* No keyup for Shift arrives, but the next mouse event says it is up */
|
||||
fire({type: 'mousedown', clientX: 10, clientY: 10, buttons: 1, shiftKey: false});
|
||||
expect(engine.getActionEventBus().getKeys()).not.toContain('shift');
|
||||
expect(currentState()).toBe('drag-canvas');
|
||||
});
|
||||
|
||||
it('leaves keys alone for events without modifier flags', ()=>{
|
||||
fire({type: 'keydown', key: 'Shift'});
|
||||
fire({type: 'mousedown', clientX: 10, clientY: 10, buttons: 1});
|
||||
expect(engine.getActionEventBus().getKeys()).toContain('shift');
|
||||
expect(currentState()).toBe('selection-box');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user