import styled from '@emotion/styled'; import space from 'sentry/styles/space'; import {getKeyCode} from 'sentry/utils/getKeyCode'; const macModifiers = { 18: '⌥', 17: '⌃', 91: '⌘', }; const normalModifiers = { 18: 'ALT', 17: 'CTRL', }; const genericGlyphs = { 16: '⇧', 8: '⌫', 37: '←', 38: '↑', 39: '→', 40: '↓', 107: '+', }; const keyToDisplay = ( key: string, isMac: boolean ): {label: React.ReactNode; specificToOs: 'macos' | 'generic'} => { const keyCode = getKeyCode(key); // Not a special key if (!keyCode) { return {label: {key.toUpperCase()}, specificToOs: 'generic'}; } const modifierMap = isMac ? macModifiers : normalModifiers; const keyStr = modifierMap[keyCode] ?? genericGlyphs[keyCode] ?? key.toUpperCase(); const specificToOs = keyCode === getKeyCode('command') ? 'macos' : 'generic'; return {label: {keyStr}, specificToOs}; }; type Props = { /** * Pass key combinations in with + as the separator. * For example: `'command+option+x'` * * Pass an array of strings for fallback key combos when the first one contains a key that does not exist on that os (non-mac): * `['command+option+x', 'ctrl+shift+x']` * (does not have to be the same combo) */ value: string[] | string; forcePlatform?: 'macos' | 'generic'; }; const HotkeysLabel = ({value, forcePlatform}: Props) => { // Split by commas and then split by +, but allow escaped /+ const hotkeySets = (Array.isArray(value) ? value : [value]).map(o => o.trim().split('+') ); const isMac = forcePlatform ? forcePlatform === 'macos' : window?.navigator?.platform?.toLowerCase().startsWith('mac') ?? false; // If we're not using mac find the first key set that is generic. // Otherwise show whatever the first hotkey is. const finalKeySet = hotkeySets .map(keySet => keySet.map(key => keyToDisplay(key, isMac))) .find(keySet => !isMac ? keySet.every(key => key.specificToOs === 'generic') : true ); // No key available for the OS. Don't show a hotkey if (finalKeySet === undefined) { return null; } return {finalKeySet.map(key => key.label)}; }; export default HotkeysLabel; const Key = styled('span')` font-size: ${p => p.theme.fontSizeMedium}; `; const HotkeysContainer = styled('div')` font-family: ${p => p.theme.text.family}; display: flex; flex-direction: row; align-items: center; > * { margin-right: ${space(0.5)}; } `;