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)};
}
`;