wisemapping-frontend/packages/mindplot/src/components/DesignerKeyboard.ts

471 lines
12 KiB
TypeScript
Raw Normal View History

2021-07-16 16:41:58 +02:00
/*
2021-12-25 23:39:34 +01:00
* Copyright [2021] [wisemapping]
2021-07-16 16:41:58 +02:00
*
* Licensed under WiseMapping Public License, Version 1.0 (the "License").
* It is basically the Apache License, Version 2.0 (the "License") plus the
* "powered by wisemapping" text requirement on every single page;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the license at
*
* http://www.wisemapping.org/license
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
2021-12-05 18:25:16 +01:00
import $ from 'jquery';
import { $assert } from '@wisemapping/core-js';
2021-12-19 17:06:42 +01:00
import Keyboard from './Keyboard';
2022-02-04 07:19:18 +01:00
import { Designer } from '..';
import Topic from './Topic';
2021-07-16 16:41:58 +02:00
2022-02-13 04:15:51 +01:00
export type EventCallback = (event?: Event) => void;
2021-12-05 00:39:20 +01:00
class DesignerKeyboard extends Keyboard {
2022-02-10 04:26:44 +01:00
// eslint-disable-next-line no-use-before-define
static _instance: DesignerKeyboard;
2022-02-04 07:19:18 +01:00
2022-02-13 04:15:51 +01:00
static _disabled: boolean;
2022-02-04 07:19:18 +01:00
constructor(designer: Designer) {
super();
2021-10-05 02:05:34 +02:00
$assert(designer, 'designer can not be null');
this._registerEvents(designer);
2021-12-05 00:39:20 +01:00
}
2021-07-16 16:41:58 +02:00
2022-02-13 04:15:51 +01:00
addShortcut(shortcuts: string[] | string, callback: EventCallback): void {
super.addShortcut(shortcuts, (e: Event) => {
if (DesignerKeyboard.isDisabled()) {
return;
}
callback(e);
});
}
2022-02-04 07:19:18 +01:00
private _registerEvents(designer: Designer) {
2021-10-05 02:05:34 +02:00
// Try with the keyboard ..
const model = designer.getModel();
this.addShortcut(
2022-02-06 03:20:42 +01:00
['backspace'], (event: Event) => {
2021-10-05 02:05:34 +02:00
event.preventDefault();
event.stopPropagation();
designer.deleteSelectedEntities();
},
);
this.addShortcut(
2022-02-06 03:20:42 +01:00
['space'], (event: Event) => {
2021-10-05 02:05:34 +02:00
designer.shrinkSelectedBranch();
2022-02-06 03:20:42 +01:00
event.preventDefault();
event.stopPropagation();
2021-10-05 02:05:34 +02:00
},
);
this.addShortcut(
2022-02-06 03:20:42 +01:00
['f2'], (event: Event) => {
2021-10-05 02:05:34 +02:00
event.stopPropagation();
event.preventDefault();
const node = model.selectedTopic();
if (node) {
2022-02-06 06:28:35 +01:00
node.showTextEditor(node.getText());
2021-10-05 02:05:34 +02:00
}
},
);
this.addShortcut(
2022-02-06 03:20:42 +01:00
['del'], (event: Event) => {
2021-10-05 02:05:34 +02:00
designer.deleteSelectedEntities();
event.preventDefault();
event.stopPropagation();
},
);
this.addShortcut(
['enter'], () => {
designer.createSiblingForSelectedNode();
},
);
this.addShortcut(
2022-02-06 03:20:42 +01:00
['insert'], (event: Event) => {
2021-10-05 02:05:34 +02:00
designer.createChildForSelectedNode();
event.preventDefault();
event.stopPropagation();
},
);
this.addShortcut(
2022-02-06 03:20:42 +01:00
['tab'], (eventevent: Event) => {
2021-10-05 02:05:34 +02:00
designer.createChildForSelectedNode();
2022-02-10 04:26:44 +01:00
eventevent.preventDefault();
eventevent.stopPropagation();
2021-10-05 02:05:34 +02:00
},
);
this.addShortcut(
2022-02-06 03:20:42 +01:00
['meta+enter'], (eventevent: Event) => {
2022-02-10 04:26:44 +01:00
eventevent.preventDefault();
eventevent.stopPropagation();
2021-10-05 02:05:34 +02:00
designer.createChildForSelectedNode();
},
);
this.addShortcut(
2022-02-06 03:20:42 +01:00
['ctrl+z', 'meta+z'], (event: Event) => {
event.preventDefault();
2021-10-05 02:05:34 +02:00
event.stopPropagation();
designer.undo();
},
);
this.addShortcut(
2022-02-06 03:20:42 +01:00
['ctrl+c', 'meta+c'], (event: Event) => {
event.preventDefault();
2021-10-05 02:05:34 +02:00
event.stopPropagation();
designer.copyToClipboard();
},
);
this.addShortcut(
2022-02-06 03:20:42 +01:00
['ctrl+v', 'meta+v'], (event: Event) => {
event.preventDefault();
2021-10-05 02:05:34 +02:00
event.stopPropagation();
designer.pasteClipboard();
},
);
this.addShortcut(
2022-02-06 03:20:42 +01:00
['ctrl+shift+z', 'meta+shift+z', 'ctrl+y', 'meta+y'], (event: Event) => {
2021-10-05 02:05:34 +02:00
event.preventDefault();
event.stopPropagation();
designer.redo();
},
);
this.addShortcut(
2022-02-06 03:20:42 +01:00
['ctrl+a', 'meta+a'], (event: Event) => {
2021-10-05 02:05:34 +02:00
event.preventDefault();
event.stopPropagation();
designer.selectAll();
},
);
this.addShortcut(
2022-02-06 03:20:42 +01:00
['ctrl+b', 'meta+b'], (event: Event) => {
2021-10-05 02:05:34 +02:00
event.preventDefault();
event.stopPropagation();
2021-07-16 16:41:58 +02:00
2021-10-05 02:05:34 +02:00
designer.changeFontWeight();
},
);
this.addShortcut(
2022-02-06 03:20:42 +01:00
['ctrl+s', 'meta+s'], (event: Event) => {
2021-10-05 02:05:34 +02:00
event.preventDefault();
event.stopPropagation();
$(document).find('#save').trigger('click');
},
);
this.addShortcut(
2022-02-06 03:20:42 +01:00
['ctrl+i', 'meta+i'], (event: Event) => {
2021-10-05 02:05:34 +02:00
event.preventDefault();
event.stopPropagation();
2021-07-16 16:41:58 +02:00
2021-10-05 02:05:34 +02:00
designer.changeFontStyle();
},
);
this.addShortcut(
2022-02-06 03:20:42 +01:00
['ctrl+shift+a', 'meta+shift+a'], (event: Event) => {
2021-10-05 02:05:34 +02:00
event.preventDefault();
event.stopPropagation();
2021-07-16 16:41:58 +02:00
2021-10-05 02:05:34 +02:00
designer.deselectAll();
},
);
this.addShortcut(
2022-02-06 03:20:42 +01:00
['meta+=', 'ctrl+='], (event: Event) => {
2021-10-05 02:05:34 +02:00
event.preventDefault();
event.stopPropagation();
2021-07-16 16:41:58 +02:00
2021-10-05 02:05:34 +02:00
designer.zoomIn();
},
);
this.addShortcut(
2022-02-06 03:20:42 +01:00
['meta+-', 'ctrl+-'], (event: Event) => {
2021-10-05 02:05:34 +02:00
event.preventDefault();
event.stopPropagation();
2021-07-16 16:41:58 +02:00
2021-10-05 02:05:34 +02:00
designer.zoomOut();
},
);
const me = this;
this.addShortcut(
2022-02-06 03:20:42 +01:00
'right', (event: Event) => {
2021-10-05 02:05:34 +02:00
const node = model.selectedTopic();
if (node) {
if (node.isCentralTopic()) {
me._goToSideChild(designer, node, 'RIGHT');
} else if (node.getPosition().x < 0) {
me._goToParent(designer, node);
} else if (!node.areChildrenShrunken()) {
me._goToChild(designer, node);
}
} else {
const centralTopic = model.getCentralTopic();
me._goToNode(designer, centralTopic);
}
event.preventDefault();
event.stopPropagation();
},
);
this.addShortcut(
2022-02-06 03:20:42 +01:00
'left', (event: Event) => {
2021-10-05 02:05:34 +02:00
const node = model.selectedTopic();
if (node) {
if (node.isCentralTopic()) {
me._goToSideChild(designer, node, 'LEFT');
} else if (node.getPosition().x > 0) {
me._goToParent(designer, node);
} else if (!node.areChildrenShrunken()) {
me._goToChild(designer, node);
}
} else {
const centralTopic = model.getCentralTopic();
me._goToNode(designer, centralTopic);
}
event.preventDefault();
event.stopPropagation();
},
);
this.addShortcut(
2022-02-06 03:20:42 +01:00
'up', (event: Event) => {
2021-10-05 02:05:34 +02:00
const node = model.selectedTopic();
if (node) {
if (!node.isCentralTopic()) {
me._goToBrother(designer, node, 'UP');
}
} else {
const centralTopic = model.getCentralTopic();
me._goToNode(designer, centralTopic);
}
event.preventDefault();
event.stopPropagation();
},
);
this.addShortcut(
2022-02-06 03:20:42 +01:00
'down', (event: Event) => {
2021-10-05 02:05:34 +02:00
const node = model.selectedTopic();
if (node) {
if (!node.isCentralTopic()) {
me._goToBrother(designer, node, 'DOWN');
}
} else {
const centralTopic = model.getCentralTopic();
me._goToNode(designer, centralTopic);
}
event.preventDefault();
event.stopPropagation();
},
);
const excludes = ['esc', 'escape', 'f1', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'f10', 'f11', 'f12'];
2021-07-16 16:41:58 +02:00
2021-10-05 02:05:34 +02:00
$(document).on('keypress', (event) => {
2022-02-10 04:26:44 +01:00
let keyCode: number;
2022-02-13 04:15:51 +01:00
if (DesignerKeyboard.isDisabled()) {
return;
}
2021-10-05 02:05:34 +02:00
// Firefox doesn't skip special keys for keypress event...
2021-12-14 18:06:09 +01:00
if (event.key && excludes.includes(event.key.toLowerCase())) {
2021-10-05 02:05:34 +02:00
return;
}
// Sometimes Firefox doesn't contain keyCode value
2021-12-19 18:07:01 +01:00
if (event.key && event.keyCode === 0) {
2021-10-05 02:05:34 +02:00
keyCode = event.charCode;
} else {
keyCode = event.keyCode;
}
2021-07-16 16:41:58 +02:00
2022-02-10 04:26:44 +01:00
// eslint-disable-next-line @typescript-eslint/no-explicit-any
2022-02-04 07:19:18 +01:00
const jq: any = $;
const specialKey = jq.hotkeys.specialKeys[keyCode];
if (['enter', 'capslock'].indexOf(specialKey) === -1 && !jq.hotkeys.shiftNums[keyCode]) {
2021-10-05 02:05:34 +02:00
const nodes = designer.getModel().filterSelectedTopics();
if (nodes.length > 0) {
// If a modifier is press, the key selected must be ignored.
const pressKey = String.fromCharCode(keyCode);
if (event.ctrlKey || event.altKey || event.metaKey) {
return;
}
nodes[0].showTextEditor(pressKey);
event.stopPropagation();
2021-07-16 16:41:58 +02:00
}
2021-10-05 02:05:34 +02:00
}
});
2021-12-05 00:39:20 +01:00
}
2021-07-16 16:41:58 +02:00
2022-02-04 07:19:18 +01:00
private _goToBrother(designer: Designer, node: Topic, direction) {
2021-10-05 02:05:34 +02:00
const parent = node.getParent();
if (parent) {
const brothers = parent.getChildren();
2021-07-16 16:41:58 +02:00
2021-10-05 02:05:34 +02:00
let target = node;
const { y } = node.getPosition();
const { x } = node.getPosition();
let dist = null;
for (let i = 0; i < brothers.length; i++) {
const sameSide = (x * brothers[i].getPosition().x) >= 0;
2021-12-05 00:39:20 +01:00
if (brothers[i] !== node && sameSide) {
2021-10-05 02:05:34 +02:00
const brother = brothers[i];
const brotherY = brother.getPosition().y;
2021-12-05 00:39:20 +01:00
if (direction === 'DOWN' && brotherY > y) {
2021-10-05 02:05:34 +02:00
let distancia = y - brotherY;
if (distancia < 0) {
distancia *= (-1);
2021-07-16 16:41:58 +02:00
}
2021-10-05 02:05:34 +02:00
if (dist == null || dist > distancia) {
dist = distancia;
target = brothers[i];
}
2021-12-05 00:39:20 +01:00
} else if (direction === 'UP' && brotherY < y) {
2021-10-05 02:05:34 +02:00
let distance = y - brotherY;
if (distance < 0) {
distance *= (-1);
}
if (dist == null || dist > distance) {
dist = distance;
target = brothers[i];
}
}
2021-07-16 16:41:58 +02:00
}
2021-10-05 02:05:34 +02:00
}
this._goToNode(designer, target);
}
2021-12-05 00:39:20 +01:00
}
2021-07-16 16:41:58 +02:00
2022-02-04 07:19:18 +01:00
private _goToSideChild(designer: Designer, node: Topic, side: 'LEFT' | 'RIGHT') {
2021-10-05 02:05:34 +02:00
const children = node.getChildren();
if (children.length > 0) {
let target = children[0];
let top = null;
for (let i = 0; i < children.length; i++) {
const child = children[i];
const childY = child.getPosition().y;
2021-12-05 00:39:20 +01:00
if (side === 'LEFT' && child.getPosition().x < 0) {
2021-10-05 02:05:34 +02:00
if (top == null || childY < top) {
target = child;
top = childY;
}
2021-07-16 16:41:58 +02:00
}
2021-12-05 00:39:20 +01:00
if (side === 'RIGHT' && child.getPosition().x > 0) {
2021-10-05 02:05:34 +02:00
if (top == null || childY < top) {
target = child;
top = childY;
}
2021-07-16 16:41:58 +02:00
}
2021-10-05 02:05:34 +02:00
}
2021-07-16 16:41:58 +02:00
2021-10-05 02:05:34 +02:00
this._goToNode(designer, target);
}
2021-12-05 00:39:20 +01:00
}
2021-07-16 16:41:58 +02:00
2022-02-04 07:19:18 +01:00
private _goToParent(designer: Designer, node: Topic) {
2021-10-05 02:05:34 +02:00
const parent = node.getParent();
if (parent) {
this._goToNode(designer, parent);
2021-07-16 16:41:58 +02:00
}
2021-12-05 00:39:20 +01:00
}
2021-10-05 02:05:34 +02:00
2022-02-04 07:19:18 +01:00
private _goToChild(designer, node) {
2021-10-05 02:05:34 +02:00
const children = node.getChildren();
if (children.length > 0) {
let target = children[0];
let top = target.getPosition().y;
for (let i = 0; i < children.length; i++) {
const child = children[i];
if (child.getPosition().y < top) {
top = child.getPosition().y;
target = child;
}
}
this._goToNode(designer, target);
}
2021-12-05 00:39:20 +01:00
}
2021-10-05 02:05:34 +02:00
2022-02-04 07:19:18 +01:00
private _goToNode(designer: Designer, node: Topic) {
2021-10-05 02:05:34 +02:00
// First deselect all the nodes ...
designer.deselectAll();
// Give focus to the selected node....
node.setOnFocus(true);
2021-12-05 00:39:20 +01:00
}
2021-07-16 16:41:58 +02:00
2022-02-04 07:19:18 +01:00
static register = function register(designer: Designer) {
this._instance = new DesignerKeyboard(designer);
2022-02-13 04:15:51 +01:00
this._disabled = false;
};
static pause = function pause() {
this._disabled = true;
};
static resume = function resume() {
this._disabled = false;
};
static isDisabled = function isDisabled() {
return this._disabled;
2022-02-04 07:19:18 +01:00
};
2021-07-16 16:41:58 +02:00
2022-02-04 07:19:18 +01:00
static specialKeys = {
8: 'backspace',
9: 'tab',
10: 'return',
13: 'enter',
16: 'shift',
17: 'ctrl',
18: 'alt',
19: 'pause',
20: 'capslock',
27: 'esc',
32: 'space',
33: 'pageup',
34: 'pagedown',
35: 'end',
36: 'home',
37: 'left',
38: 'up',
39: 'right',
40: 'down',
45: 'insert',
46: 'del',
96: '0',
97: '1',
98: '2',
99: '3',
100: '4',
101: '5',
102: '6',
103: '7',
104: '8',
105: '9',
106: '*',
107: '+',
109: '-',
110: '.',
111: '/',
112: 'f1',
113: 'f2',
114: 'f3',
115: 'f4',
116: 'f5',
117: 'f6',
118: 'f7',
119: 'f8',
120: 'f9',
121: 'f10',
122: 'f11',
123: 'f12',
144: 'numlock',
145: 'scroll',
186: ';',
191: '/',
220: '\\',
222: "'",
224: 'meta',
};
2022-02-04 07:19:18 +01:00
static getInstance() {
return this._instance;
}
}
2021-07-16 16:41:58 +02:00
export default DesignerKeyboard;