UNPKG

@blockly/keyboard-navigation

Version:
100 lines (90 loc) 3.39 kB
import {ShortcutRegistry, Msg} from 'blockly'; import {keyNames} from './keynames'; const isMacPlatform = navigator.platform.startsWith('Mac'); /** * Find the primary shortcut for this platform and return it as single string * in a short user facing format. * * @param action The action name, e.g. "cut". * @returns The formatted shortcut. */ export function getShortActionShortcut(action: string): string { const parts = getActionShortcutsAsKeys(action, shortModifierNames)[0]; return parts.join(isMacPlatform ? ' ' : ' + '); } /** * Find the relevant shortcuts for the given action for the current platform. * Keys are returned in a long user facing format. * * @param action The action name, e.g. "cut". * @returns The formatted shortcuts as individual keys. */ export function getLongActionShortcutsAsKeys(action: string): string[][] { return getActionShortcutsAsKeys(action, longModifierNames); } const longModifierNames: Record<string, string> = { 'Control': Msg['CONTROL_KEY'], 'Meta': Msg['COMMAND_KEY'], 'Alt': isMacPlatform ? Msg['OPTION_KEY'] : Msg['ALT_KEY'], }; const shortModifierNames: Record<string, string> = { 'Control': Msg['CONTROL_KEY'], 'Meta': '⌘', 'Alt': isMacPlatform ? '⌥' : Msg['ALT_KEY'], }; /** * Find the relevant shortcuts for the given action for the current platform. * Keys are returned in a user facing format. * * This could be considerably simpler if we only bound shortcuts relevant to the * current platform or tagged them with a platform. * * @param action The action name, e.g. "cut". * @param modifierNames The names to use for the Meta/Control/Alt modifiers. * @returns The formatted shortcuts. */ function getActionShortcutsAsKeys( action: string, modifierNames: Record<string, string>, ): string[][] { const shortcuts = ShortcutRegistry.registry.getKeyCodesByShortcutName(action); // See ShortcutRegistry.createSerializedKey for the starting format. const named = shortcuts.map((shortcut) => { return shortcut .split('+') .map((maybeNumeric) => keyNames[maybeNumeric] ?? maybeNumeric) .map((k) => upperCaseFirst(modifierNames[k] ?? k)); }); const command = modifierNames['Meta']; const option = modifierNames['Alt']; const control = modifierNames['Control']; // Needed to prefer Command to Option where we've bound Alt. named.sort((a, b) => { const aValue = a.includes(command) ? 1 : 0; const bValue = b.includes(command) ? 1 : 0; return bValue - aValue; }); let currentPlatform = named.filter((shortcut) => { const isMacShortcut = shortcut.includes(command) || shortcut.includes(option); return isMacShortcut === isMacPlatform; }); currentPlatform = currentPlatform.length === 0 ? named : currentPlatform; // If there are modifiers return only one shortcut on the assumption they are // intended for different platforms. Otherwise assume they are alternatives. const hasModifiers = currentPlatform.some((shortcut) => shortcut.some( (key) => command === key || option === key || control === key, ), ); return hasModifiers ? [currentPlatform[0]] : currentPlatform; } /** * Convert the first character to uppercase. * * @param str String. * @returns The string in title case. */ export function upperCaseFirst(str: string) { return str.charAt(0).toUpperCase() + str.substring(1); }