Files
modeling-app/src/lib/hotkeyWrapper.ts
2024-08-22 21:39:57 -04:00

78 lines
2.7 KiB
TypeScript

import { Options, useHotkeys } from 'react-hotkeys-hook'
import { useEffect } from 'react'
import { codeManager } from './singletons'
import { Platform } from './utils'
// Hotkey wrapper wraps hotkeys for the app (outside of the editor)
// with hotkeys inside the editor.
// This way we can have hotkeys defined in one place and not have to worry about
// conflicting hotkeys, or them only being implemented for the app but not
// inside the editor.
// TODO: would be nice if this didn't have to be a react hook. It's not needed
// for the code mirror stuff but but it is needed for the useHotkeys hook.
export default function useHotkeyWrapper(
hotkey: string[],
callback: () => void,
additionalOptions?: Options
) {
const defaultOptions = { preventDefault: true }
const options = { ...defaultOptions, ...additionalOptions }
useHotkeys(hotkey, callback, options)
useEffect(() => {
for (const key of hotkey) {
const keybinding = mapHotkeyToCodeMirrorHotkey(key)
codeManager.registerHotkey(keybinding, callback)
}
})
}
// Convert hotkey to code mirror hotkey
// See: https://codemirror.net/docs/ref/#view.KeyBinding
function mapHotkeyToCodeMirrorHotkey(hotkey: string): string {
return hotkey
.replaceAll('+', '-')
.replaceAll(' ', '')
.replaceAll('mod', 'Mod')
.replaceAll('meta', 'Meta')
.replaceAll('ctrl', 'Ctrl')
.replaceAll('shift', 'Shift')
.replaceAll('alt', 'Alt')
}
const LOWER_CASE_LETTER = /[a-z]/
const WHITESPACE = /\s+/g
// Convert hotkey to display text.
export function hotkeyDisplay(hotkey: string, platform: Platform): string {
const isMac = platform === 'macos'
const isWindows = platform === 'windows'
const meta = isWindows ? 'Win' : 'Meta'
const outputSeparator = isMac ? '' : '+'
const display = hotkey
// Capitalize letters. We want Ctrl K, not Ctrl k, since Shift should be
// shown as a separate modifier.
.split('+')
.map((word) => {
if (word.length === 1 && LOWER_CASE_LETTER.test(word)) {
return word.toUpperCase()
}
return word
})
.join(outputSeparator)
// Collapse multiple spaces into one.
.replaceAll(WHITESPACE, ' ')
.replaceAll('mod', isMac ? '⌘' : 'Ctrl')
.replaceAll('meta', isMac ? '⌘' : meta)
// Note: This is *not* the ASCII caret character. It's "UP ARROWHEAD".
// Unicode: U+2303.
.replaceAll('ctrl', isMac ? '⌃' : 'Ctrl')
// Note: This is technically the wrong arrow for shift, but it's more
// visible and recognizable. May want to change this in the future.
//
// The correct arrow is ⇧ "UPWARDS WHITE ARROW" Unicode: U+21E7
.replaceAll('shift', isMac ? '⬆' : 'Shift')
.replaceAll('alt', isMac ? '⌥' : 'Alt')
return display
}