UNPKG

@eclipse-glsp/client

Version:

A sprotty-based client for GLSP

446 lines 19.9 kB
"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-2025 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)('eraser'); 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