@eclipse-glsp/client
Version:
A sprotty-based client for GLSP
446 lines • 19.9 kB
JavaScript
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.KeyboardToolPalette = void 0;
/********************************************************************************
* Copyright (c) 2023-2024 Business Informatics Group (TU Wien) and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
const sprotty_1 = require("@eclipse-glsp/sprotty");
const inversify_1 = require("inversify");
const messages_1 = require("../../../base/messages");
const tool_1 = require("../../../base/tool-manager/tool");
const tool_palette_1 = require("../../tool-palette/tool-palette");
const delete_tool_1 = require("../../tools/deletion/delete-tool");
const marquee_mouse_tool_1 = require("../../tools/marquee-selection/marquee-mouse-tool");
const actions_1 = require("../actions");
const edge_autocomplete_palette_1 = require("../edge-autocomplete/edge-autocomplete-palette");
const diagram_navigation_tool_1 = require("../element-navigation/diagram-navigation-tool");
const constants_1 = require("../keyboard-grid/constants");
const toast_handler_1 = require("../toast/toast-handler");
const SEARCH_ICON_ID = 'search';
const SELECTION_TOOL_KEY = ['Digit1', 'Numpad1'];
const DELETION_TOOL_KEY = ['Digit2', 'Numpad2'];
const MARQUEE_TOOL_KEY = ['Digit3', 'Numpad3'];
const VALIDATION_TOOL_KEY = ['Digit4', 'Numpad4'];
const SEARCH_TOOL_KEY = ['Digit5', 'Numpad5'];
const SHOW_SHORTCUTS_CLASS = 'accessibility-show-shortcuts';
const AVAILABLE_KEYS = [
'KeyA',
'KeyB',
'KeyC',
'KeyD',
'KeyE',
'KeyF',
'KeyG',
'KeyH',
'KeyI',
'KeyJ',
'KeyK',
'KeyL',
'KeyM',
'KeyN',
'KeyO',
'KeyP',
'KeyQ',
'KeyR',
'KeyS',
'KeyT',
'KeyU',
'KeyV',
'KeyX',
'KeyY',
'KeyZ'
];
const HEADER_TOOL_KEYS = [SELECTION_TOOL_KEY, DELETION_TOOL_KEY, MARQUEE_TOOL_KEY, VALIDATION_TOOL_KEY, SEARCH_TOOL_KEY];
let KeyboardToolPalette = class KeyboardToolPalette extends tool_palette_1.ToolPalette {
constructor() {
super(...arguments);
this.keyboardIndexButtonMapping = new Map();
this.headerToolsButtonMapping = new Map();
}
get interactablePaletteItems() {
return this.paletteItems
.sort(tool_palette_1.compare)
.map(item => { var _a, _b; return (_b = (_a = item.children) === null || _a === void 0 ? void 0 : _a.sort(tool_palette_1.compare)) !== null && _b !== void 0 ? _b : [item]; })
.reduce((acc, val) => acc.concat(val), []);
}
initializeContents(_containerElement) {
this.containerElement.setAttribute('aria-label', messages_1.messages.tool_palette.label);
this.containerElement.tabIndex = 20;
this.containerElement.classList.add('accessibility-tool-palette');
this.addMinimizePaletteButton();
this.createHeader();
this.createBody();
this.lastActiveButton = this.defaultToolsButton;
this.containerElement.onkeyup = ev => {
this.clearToolOnEscape(ev);
if (this.isShortcutsVisible()) {
this.selectItemOnCharacter(ev);
this.triggerHeaderToolsByKey(ev);
}
};
}
handle(action) {
if (tool_palette_1.EnableToolPaletteAction.is(action)) {
const requestAction = sprotty_1.RequestContextActions.create({
contextId: tool_palette_1.ToolPalette.ID,
editorContext: {
selectedElementIds: []
}
});
this.actionDispatcher.requestUntil(requestAction).then(response => {
if (sprotty_1.SetContextActions.is(response)) {
this.paletteItems = response.actions.map(e => e);
this.actionDispatcher.dispatchAll([
sprotty_1.SetUIExtensionVisibilityAction.create({ extensionId: tool_palette_1.ToolPalette.ID, visible: !this.editorContext.isReadonly })
]);
}
});
}
else if (actions_1.FocusDomAction.is(action) && action.id === tool_palette_1.ToolPalette.ID) {
if (this.containerElement.contains(document.activeElement)) {
this.toggleShortcutVisibility();
}
else {
this.showShortcuts();
}
this.containerElement.focus();
}
else {
super.handle(action);
}
}
createBody() {
const bodyDiv = document.createElement('div');
bodyDiv.classList.add('palette-body');
const tabIndex = 21;
let toolButtonCounter = 0;
this.keyboardIndexButtonMapping.clear();
this.paletteItems.sort(tool_palette_1.compare).forEach(item => {
if (item.children) {
const group = (0, tool_palette_1.createToolGroup)(item);
item.children.sort(tool_palette_1.compare).forEach(child => {
const button = this.createKeyboardToolButton(child, tabIndex, toolButtonCounter);
group.appendChild(button);
this.keyboardIndexButtonMapping.set(toolButtonCounter, button);
toolButtonCounter++;
});
bodyDiv.appendChild(group);
}
else {
const button = this.createKeyboardToolButton(item, tabIndex, toolButtonCounter);
bodyDiv.appendChild(button);
this.keyboardIndexButtonMapping.set(toolButtonCounter, button);
toolButtonCounter++;
}
});
if (this.paletteItems.length === 0) {
const noResultsDiv = document.createElement('div');
noResultsDiv.innerText = messages_1.messages.tool_palette.no_items;
noResultsDiv.classList.add('tool-button');
bodyDiv.appendChild(noResultsDiv);
}
// Replace existing body to refresh filtered entries
if (this.bodyDiv) {
this.containerElement.removeChild(this.bodyDiv);
}
this.containerElement.appendChild(bodyDiv);
this.bodyDiv = bodyDiv;
}
createHeaderTools() {
this.headerToolsButtonMapping.clear();
let mappingIndex = 0;
const headerTools = document.createElement('div');
headerTools.classList.add('header-tools');
this.defaultToolsButton = this.createDefaultToolButton();
this.headerToolsButtonMapping.set(mappingIndex++, this.defaultToolsButton);
headerTools.appendChild(this.defaultToolsButton);
this.deleteToolButton = this.createMouseDeleteToolButton();
this.headerToolsButtonMapping.set(mappingIndex++, this.deleteToolButton);
headerTools.appendChild(this.deleteToolButton);
this.marqueeToolButton = this.createMarqueeToolButton();
this.headerToolsButtonMapping.set(mappingIndex++, this.marqueeToolButton);
headerTools.appendChild(this.marqueeToolButton);
this.validateToolButton = this.createValidateButton();
this.headerToolsButtonMapping.set(mappingIndex++, this.validateToolButton);
headerTools.appendChild(this.validateToolButton);
const resetViewportButton = this.createResetViewportButton();
this.headerToolsButtonMapping.set(mappingIndex++, resetViewportButton);
headerTools.appendChild(resetViewportButton);
if (this.gridManager) {
const toggleGridButton = this.createToggleGridButton();
this.headerToolsButtonMapping.set(mappingIndex++, toggleGridButton);
headerTools.appendChild(toggleGridButton);
}
if (this.debugManager) {
const toggleDebugButton = this.createToggleDebugButton();
this.headerToolsButtonMapping.set(mappingIndex++, toggleDebugButton);
headerTools.appendChild(toggleDebugButton);
}
// Create button for Search
this.searchToolButton = this.createSearchButton();
this.headerToolsButtonMapping.set(mappingIndex++, this.searchToolButton);
headerTools.appendChild(this.searchToolButton);
return headerTools;
}
createDefaultToolButton() {
const button = (0, tool_palette_1.createIcon)('inspect');
button.id = 'btn_default_tools';
button.title = messages_1.messages.tool_palette.selection_button;
button.onclick = this.onClickStaticToolButton(button);
button.appendChild(this.createKeyboardShotcut(SELECTION_TOOL_KEY[0]));
return button;
}
createMouseDeleteToolButton() {
const deleteToolButton = (0, tool_palette_1.createIcon)('chrome-close');
deleteToolButton.title = messages_1.messages.tool_palette.delete_button;
deleteToolButton.onclick = this.onClickStaticToolButton(deleteToolButton, delete_tool_1.MouseDeleteTool.ID);
deleteToolButton.appendChild(this.createKeyboardShotcut(DELETION_TOOL_KEY[0]));
return deleteToolButton;
}
createMarqueeToolButton() {
const marqueeToolButton = (0, tool_palette_1.createIcon)('screen-full');
marqueeToolButton.title = messages_1.messages.tool_palette.marquee_button;
const toastMessageAction = toast_handler_1.ShowToastMessageAction.createWithTimeout({
id: Symbol.for(diagram_navigation_tool_1.ElementNavigatorKeyListener.name),
message: messages_1.messages.tool_palette.marquee_message
});
marqueeToolButton.onclick = this.onClickStaticToolButton(marqueeToolButton, marquee_mouse_tool_1.MarqueeMouseTool.ID, toastMessageAction);
marqueeToolButton.appendChild(this.createKeyboardShotcut(MARQUEE_TOOL_KEY[0]));
return marqueeToolButton;
}
createValidateButton() {
const validateToolButton = (0, tool_palette_1.createIcon)('pass');
validateToolButton.title = messages_1.messages.tool_palette.validate_button;
validateToolButton.onclick = _event => {
const modelIds = [this.modelRootId];
this.actionDispatcher.dispatch(sprotty_1.RequestMarkersAction.create(modelIds));
};
validateToolButton.appendChild(this.createKeyboardShotcut(VALIDATION_TOOL_KEY[0]));
return validateToolButton;
}
onClickStaticToolButton(button, toolId, action) {
return (_ev) => {
if (!this.editorContext.isReadonly) {
const defaultAction = toolId ? tool_1.EnableToolsAction.create([toolId]) : tool_1.EnableDefaultToolsAction.create();
if (action) {
this.actionDispatcher.dispatchAll([defaultAction, action]);
}
else {
this.actionDispatcher.dispatchAll([defaultAction]);
}
this.changeActiveButton(button);
button.focus();
}
};
}
createSearchButton() {
const searchIcon = (0, tool_palette_1.createIcon)(SEARCH_ICON_ID);
searchIcon.onclick = _ev => {
const searchField = document.getElementById(this.containerElement.id + '_search_field');
if (searchField) {
if (searchField.style.display === 'none') {
searchField.style.display = '';
searchField.focus();
}
else {
searchField.style.display = 'none';
}
}
};
searchIcon.classList.add('search-icon');
searchIcon.title = messages_1.messages.tool_palette.search_button;
searchIcon.appendChild(this.createKeyboardShotcut(SEARCH_TOOL_KEY[0]));
return searchIcon;
}
createHeaderSearchField() {
const searchField = document.createElement('input');
searchField.classList.add('search-input');
searchField.tabIndex = 21;
searchField.id = this.containerElement.id + '_search_field';
searchField.type = 'text';
searchField.placeholder = messages_1.messages.tool_palette.search_placeholder;
searchField.style.display = 'none';
searchField.onkeyup = ev => {
this.requestFilterUpdate(this.searchField.value);
ev.stopPropagation();
if (searchField.value === '') {
this.focusToolPaletteOnEscape(ev);
}
else {
this.clearOnEscape(ev);
}
};
return searchField;
}
focusToolPaletteOnEscape(event) {
if ((0, sprotty_1.matchesKeystroke)(event, 'Escape')) {
this.containerElement.focus();
}
}
createKeyboardShotcut(keyShortcut) {
const hint = document.createElement('div');
hint.classList.add('key-shortcut');
let keyShortcutValue = keyShortcut.toString();
if (keyShortcut.includes('Key')) {
keyShortcutValue = keyShortcut.toString().substring(3);
}
else if (keyShortcut.includes('Digit')) {
keyShortcutValue = keyShortcut.toString().substring(5);
}
hint.innerHTML = keyShortcutValue;
return hint;
}
createKeyboardToolButton(item, tabIndex, buttonIndex) {
const button = document.createElement('div');
// add keyboard index
if (buttonIndex < AVAILABLE_KEYS.length) {
button.appendChild(this.createKeyboardShotcut(AVAILABLE_KEYS[buttonIndex]));
}
button.tabIndex = tabIndex;
button.classList.add('tool-button');
if (item.icon) {
button.appendChild((0, tool_palette_1.createIcon)(item.icon));
}
button.insertAdjacentText('beforeend', item.label);
button.onclick = this.onClickCreateToolButton(button, item);
button.onkeydown = ev => {
this.clickToolOnEnter(ev, button, item);
this.clearToolOnEscape(ev);
if ((0, sprotty_1.matchesKeystroke)(ev, 'ArrowDown')) {
if (buttonIndex + 1 > this.keyboardIndexButtonMapping.size - 1) {
this.selectItemViaArrowKey(this.keyboardIndexButtonMapping.get(0));
}
else {
this.selectItemViaArrowKey(this.keyboardIndexButtonMapping.get(buttonIndex + 1));
}
}
else if ((0, sprotty_1.matchesKeystroke)(ev, 'ArrowUp')) {
if (buttonIndex - 1 < 0) {
this.selectItemViaArrowKey(this.keyboardIndexButtonMapping.get(this.keyboardIndexButtonMapping.size - 1));
}
else {
this.selectItemViaArrowKey(this.keyboardIndexButtonMapping.get(buttonIndex - 1));
}
}
};
return button;
}
clickToolOnEnter(event, button, item) {
if ((0, sprotty_1.matchesKeystroke)(event, 'Enter')) {
if (!this.editorContext.isReadonly) {
this.actionDispatcher.dispatchAll(item.actions);
this.changeActiveButton(button);
this.selectItemOnCharacter(event);
}
}
}
selectItemOnCharacter(event) {
var _a;
let index = undefined;
const items = this.interactablePaletteItems;
const itemsCount = items.length < AVAILABLE_KEYS.length ? items.length : AVAILABLE_KEYS.length;
for (let i = 0; i < itemsCount; i++) {
const keycode = AVAILABLE_KEYS[i];
if ((0, sprotty_1.matchesKeystroke)(event, keycode)) {
index = i;
break;
}
}
if (index !== undefined) {
if (items[index].actions.some(a => a.kind === sprotty_1.TriggerNodeCreationAction.KIND)) {
this.actionDispatcher.dispatchAll([
...items[index].actions,
sprotty_1.SetUIExtensionVisibilityAction.create({
extensionId: constants_1.KeyboardNodeGridMetadata.ID,
visible: true,
contextElementsId: []
})
]);
}
else {
this.actionDispatcher.dispatchAll([
...items[index].actions,
sprotty_1.SetUIExtensionVisibilityAction.create({
extensionId: edge_autocomplete_palette_1.EdgeAutocompletePaletteMetadata.ID,
visible: true,
contextElementsId: []
})
]);
}
this.changeActiveButton(this.keyboardIndexButtonMapping.get(index));
(_a = this.keyboardIndexButtonMapping.get(index)) === null || _a === void 0 ? void 0 : _a.focus();
}
}
triggerHeaderToolsByKey(event) {
var _a;
let index = undefined;
for (let i = 0; i < HEADER_TOOL_KEYS.length; i++) {
for (let j = 0; j < HEADER_TOOL_KEYS[i].length; j++) {
const keycode = HEADER_TOOL_KEYS[i][j];
if ((0, sprotty_1.matchesKeystroke)(event, keycode)) {
event.stopPropagation();
event.preventDefault();
index = i;
break;
}
}
}
if (index !== undefined) {
(_a = this.headerToolsButtonMapping.get(index)) === null || _a === void 0 ? void 0 : _a.click();
}
}
selectItemViaArrowKey(currentButton) {
if (currentButton !== undefined) {
this.changeActiveButton(currentButton);
currentButton === null || currentButton === void 0 ? void 0 : currentButton.focus();
}
}
clearToolOnEscape(event) {
if ((0, sprotty_1.matchesKeystroke)(event, 'Escape')) {
if (event.target instanceof HTMLElement) {
event.target.blur();
}
this.actionDispatcher.dispatch(tool_1.EnableDefaultToolsAction.create());
}
}
toggleShortcutVisibility() {
if (this.isShortcutsVisible()) {
this.hideShortcuts();
}
else {
this.showShortcuts();
}
}
isShortcutsVisible() {
return this.containerElement.classList.contains(SHOW_SHORTCUTS_CLASS);
}
showShortcuts() {
this.containerElement.classList.add(SHOW_SHORTCUTS_CLASS);
}
hideShortcuts() {
this.containerElement.classList.remove(SHOW_SHORTCUTS_CLASS);
}
};
exports.KeyboardToolPalette = KeyboardToolPalette;
exports.KeyboardToolPalette = KeyboardToolPalette = __decorate([
(0, inversify_1.injectable)()
], KeyboardToolPalette);
//# sourceMappingURL=keyboard-tool-palette.js.map