feat: keyboard shortcuts for primary actions (#2030)
This commit is contained in:
12
packages/ui-services/src/Keyboard/KeyboardCommandHandler.ts
Normal file
12
packages/ui-services/src/Keyboard/KeyboardCommandHandler.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { KeyboardCommand } from './KeyboardCommands'
|
||||
|
||||
export type KeyboardCommandHandler = {
|
||||
command: KeyboardCommand
|
||||
onKeyDown?: (event: KeyboardEvent) => boolean | void
|
||||
onKeyUp?: (event: KeyboardEvent) => boolean | void
|
||||
element?: HTMLElement
|
||||
elements?: HTMLElement[]
|
||||
notElement?: HTMLElement
|
||||
notElementIds?: string[]
|
||||
notTags?: string[]
|
||||
}
|
||||
26
packages/ui-services/src/Keyboard/KeyboardCommands.ts
Normal file
26
packages/ui-services/src/Keyboard/KeyboardCommands.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
export type KeyboardCommand = symbol
|
||||
|
||||
function createKeyboardCommand(type: string): KeyboardCommand {
|
||||
return Symbol(type)
|
||||
}
|
||||
|
||||
export const TOGGLE_LIST_PANE_KEYBOARD_COMMAND = createKeyboardCommand('TOGGLE_LIST_PANE_KEYBOARD_COMMAND')
|
||||
export const TOGGLE_NAVIGATION_PANE_KEYBOARD_COMMAND = createKeyboardCommand('TOGGLE_NAVIGATION_PANE_KEYBOARD_COMMAND')
|
||||
export const CREATE_NEW_NOTE_KEYBOARD_COMMAND = createKeyboardCommand('CREATE_NEW_NOTE_KEYBOARD_COMMAND')
|
||||
export const NEXT_LIST_ITEM_KEYBOARD_COMMAND = createKeyboardCommand('NEXT_LIST_ITEM_KEYBOARD_COMMAND')
|
||||
export const PREVIOUS_LIST_ITEM_KEYBOARD_COMMAND = createKeyboardCommand('PREVIOUS_LIST_ITEM_KEYBOARD_COMMAND')
|
||||
export const SEARCH_KEYBOARD_COMMAND = createKeyboardCommand('SEARCH_KEYBOARD_COMMAND')
|
||||
export const CANCEL_SEARCH_COMMAND = createKeyboardCommand('CANCEL_SEARCH_COMMAND')
|
||||
export const SELECT_ALL_ITEMS_KEYBOARD_COMMAND = createKeyboardCommand('SELECT_ALL_ITEMS_KEYBOARD_COMMAND')
|
||||
export const SHOW_HIDDEN_OPTIONS_KEYBOARD_COMMAND = createKeyboardCommand('SHOW_HIDDEN_OPTIONS_KEYBOARD_COMMAND')
|
||||
export const DELETE_NOTE_KEYBOARD_COMMAND = createKeyboardCommand('DELETE_NOTE_KEYBOARD_COMMAND')
|
||||
export const TAB_COMMAND = createKeyboardCommand('PLAIN_EDITOR_INSERT_TAB_KEYBOARD_COMMAND')
|
||||
export const ESCAPE_COMMAND = createKeyboardCommand('ESCAPE_COMMAND')
|
||||
export const TOGGLE_FOCUS_MODE_COMMAND = createKeyboardCommand('TOGGLE_FOCUS_MODE_COMMAND')
|
||||
export const CHANGE_EDITOR_COMMAND = createKeyboardCommand('CHANGE_EDITOR_COMMAND')
|
||||
export const FOCUS_TAGS_INPUT_COMMAND = createKeyboardCommand('FOCUS_TAGS_INPUT_COMMAND')
|
||||
export const CREATE_NEW_TAG_COMMAND = createKeyboardCommand('CREATE_NEW_TAG_COMMAND')
|
||||
export const OPEN_NOTE_HISTORY_COMMAND = createKeyboardCommand('OPEN_NOTE_HISTORY_COMMAND')
|
||||
export const CAPTURE_SAVE_COMMAND = createKeyboardCommand('CAPTURE_SAVE_COMMAND')
|
||||
export const STAR_NOTE_COMMAND = createKeyboardCommand('STAR_NOTE_COMMAND')
|
||||
export const PIN_NOTE_COMMAND = createKeyboardCommand('PIN_NOTE_COMMAND')
|
||||
12
packages/ui-services/src/Keyboard/KeyboardKey.ts
Normal file
12
packages/ui-services/src/Keyboard/KeyboardKey.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
export enum KeyboardKey {
|
||||
Tab = 'Tab',
|
||||
Backspace = 'Backspace',
|
||||
Up = 'ArrowUp',
|
||||
Down = 'ArrowDown',
|
||||
Left = 'ArrowLeft',
|
||||
Right = 'ArrowRight',
|
||||
Enter = 'Enter',
|
||||
Escape = 'Escape',
|
||||
Home = 'Home',
|
||||
End = 'End',
|
||||
}
|
||||
4
packages/ui-services/src/Keyboard/KeyboardKeyEvent.ts
Normal file
4
packages/ui-services/src/Keyboard/KeyboardKeyEvent.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export enum KeyboardKeyEvent {
|
||||
Down = 'KeyEventDown',
|
||||
Up = 'KeyEventUp',
|
||||
}
|
||||
7
packages/ui-services/src/Keyboard/KeyboardModifier.ts
Normal file
7
packages/ui-services/src/Keyboard/KeyboardModifier.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export enum KeyboardModifier {
|
||||
Shift = 'Shift',
|
||||
Ctrl = 'Control',
|
||||
/** ⌘ key on Mac, ⊞ key on Windows */
|
||||
Meta = 'Meta',
|
||||
Alt = 'Alt',
|
||||
}
|
||||
203
packages/ui-services/src/Keyboard/KeyboardService.ts
Normal file
203
packages/ui-services/src/Keyboard/KeyboardService.ts
Normal file
@@ -0,0 +1,203 @@
|
||||
import { Environment, Platform } from '@standardnotes/snjs'
|
||||
import { removeFromArray } from '@standardnotes/utils'
|
||||
import { eventMatchesKeyAndModifiers } from './eventMatchesKeyAndModifiers'
|
||||
import { KeyboardCommand } from './KeyboardCommands'
|
||||
import { KeyboardKeyEvent } from './KeyboardKeyEvent'
|
||||
import { KeyboardModifier } from './KeyboardModifier'
|
||||
import { KeyboardCommandHandler } from './KeyboardCommandHandler'
|
||||
import { KeyboardShortcut, PlatformedKeyboardShortcut } from './KeyboardShortcut'
|
||||
import { getKeyboardShortcuts } from './getKeyboardShortcuts'
|
||||
|
||||
export class KeyboardService {
|
||||
readonly activeModifiers = new Set<KeyboardModifier>()
|
||||
private commandHandlers: KeyboardCommandHandler[] = []
|
||||
private commandMap = new Map<KeyboardCommand, KeyboardShortcut>()
|
||||
|
||||
constructor(private platform: Platform, environment: Environment) {
|
||||
window.addEventListener('keydown', this.handleKeyDown)
|
||||
window.addEventListener('keyup', this.handleKeyUp)
|
||||
window.addEventListener('blur', this.handleWindowBlur)
|
||||
|
||||
const shortcuts = getKeyboardShortcuts(platform, environment)
|
||||
for (const shortcut of shortcuts) {
|
||||
this.registerShortcut(shortcut)
|
||||
}
|
||||
}
|
||||
|
||||
get isMac() {
|
||||
return this.platform === Platform.MacDesktop || this.platform === Platform.MacWeb
|
||||
}
|
||||
|
||||
public deinit() {
|
||||
this.commandHandlers.length = 0
|
||||
window.removeEventListener('keydown', this.handleKeyDown)
|
||||
window.removeEventListener('keyup', this.handleKeyUp)
|
||||
window.removeEventListener('blur', this.handleWindowBlur)
|
||||
;(this.handleKeyDown as unknown) = undefined
|
||||
;(this.handleKeyUp as unknown) = undefined
|
||||
;(this.handleWindowBlur as unknown) = undefined
|
||||
}
|
||||
|
||||
private addActiveModifier = (modifier: KeyboardModifier | undefined): void => {
|
||||
if (!modifier) {
|
||||
return
|
||||
}
|
||||
|
||||
switch (modifier) {
|
||||
case KeyboardModifier.Meta: {
|
||||
if (this.isMac) {
|
||||
this.activeModifiers.add(modifier)
|
||||
}
|
||||
break
|
||||
}
|
||||
case KeyboardModifier.Ctrl: {
|
||||
if (!this.isMac) {
|
||||
this.activeModifiers.add(modifier)
|
||||
}
|
||||
break
|
||||
}
|
||||
default: {
|
||||
this.activeModifiers.add(modifier)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private removeActiveModifier = (modifier: KeyboardModifier | undefined): void => {
|
||||
if (!modifier) {
|
||||
return
|
||||
}
|
||||
|
||||
this.activeModifiers.delete(modifier)
|
||||
}
|
||||
|
||||
public cancelAllKeyboardModifiers = (): void => {
|
||||
this.activeModifiers.clear()
|
||||
}
|
||||
|
||||
public handleComponentKeyDown = (modifier: KeyboardModifier | undefined): void => {
|
||||
this.addActiveModifier(modifier)
|
||||
}
|
||||
|
||||
public handleComponentKeyUp = (modifier: KeyboardModifier | undefined): void => {
|
||||
this.removeActiveModifier(modifier)
|
||||
}
|
||||
|
||||
private handleKeyDown = (event: KeyboardEvent): void => {
|
||||
this.updateAllModifiersFromEvent(event)
|
||||
|
||||
this.handleKeyboardEvent(event, KeyboardKeyEvent.Down)
|
||||
}
|
||||
|
||||
private handleKeyUp = (event: KeyboardEvent): void => {
|
||||
this.updateAllModifiersFromEvent(event)
|
||||
|
||||
this.handleKeyboardEvent(event, KeyboardKeyEvent.Up)
|
||||
}
|
||||
|
||||
private updateAllModifiersFromEvent(event: KeyboardEvent): void {
|
||||
for (const modifier of Object.values(KeyboardModifier)) {
|
||||
if (event.getModifierState(modifier)) {
|
||||
this.addActiveModifier(modifier)
|
||||
} else {
|
||||
this.removeActiveModifier(modifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleWindowBlur = (): void => {
|
||||
for (const modifier of this.activeModifiers) {
|
||||
this.activeModifiers.delete(modifier)
|
||||
}
|
||||
}
|
||||
|
||||
private handleKeyboardEvent(event: KeyboardEvent, keyEvent: KeyboardKeyEvent): void {
|
||||
for (const command of this.commandMap.keys()) {
|
||||
const shortcut = this.commandMap.get(command)
|
||||
if (!shortcut) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (eventMatchesKeyAndModifiers(event, shortcut)) {
|
||||
if (shortcut.preventDefault) {
|
||||
event.preventDefault()
|
||||
}
|
||||
this.handleCommand(command, event, keyEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private handleCommand(command: KeyboardCommand, event: KeyboardEvent, keyEvent: KeyboardKeyEvent): void {
|
||||
const target = event.target as HTMLElement
|
||||
|
||||
for (const observer of this.commandHandlers) {
|
||||
if (observer.command !== command) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (observer.element && event.target !== observer.element) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (observer.elements && !observer.elements.includes(target)) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (observer.notElement && observer.notElement === event.target) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (observer.notElementIds && observer.notElementIds.includes(target.id)) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (observer.notTags && observer.notTags.includes(target.tagName)) {
|
||||
continue
|
||||
}
|
||||
|
||||
const callback = keyEvent === KeyboardKeyEvent.Down ? observer.onKeyDown : observer.onKeyUp
|
||||
if (callback) {
|
||||
const exclusive = callback(event)
|
||||
if (exclusive) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
registerShortcut(shortcut: KeyboardShortcut): void {
|
||||
this.commandMap.set(shortcut.command, shortcut)
|
||||
}
|
||||
|
||||
addCommandHandler(observer: KeyboardCommandHandler): () => void {
|
||||
this.commandHandlers.push(observer)
|
||||
|
||||
const thislessObservers = this.commandHandlers
|
||||
return () => {
|
||||
observer.onKeyDown = undefined
|
||||
observer.onKeyDown = undefined
|
||||
removeFromArray(thislessObservers, observer)
|
||||
}
|
||||
}
|
||||
|
||||
addCommandHandlers(handlers: KeyboardCommandHandler[]): () => void {
|
||||
const disposers = handlers.map((handler) => this.addCommandHandler(handler))
|
||||
return () => {
|
||||
for (const disposer of disposers) {
|
||||
disposer()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
keyboardShortcutForCommand(command: KeyboardCommand): PlatformedKeyboardShortcut | undefined {
|
||||
const shortcut = this.commandMap.get(command)
|
||||
if (!shortcut) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return {
|
||||
platform: this.platform,
|
||||
...shortcut,
|
||||
}
|
||||
}
|
||||
}
|
||||
20
packages/ui-services/src/Keyboard/KeyboardShortcut.ts
Normal file
20
packages/ui-services/src/Keyboard/KeyboardShortcut.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { Platform } from '@standardnotes/snjs'
|
||||
import { KeyboardCommand } from './KeyboardCommands'
|
||||
import { KeyboardKey } from './KeyboardKey'
|
||||
import { KeyboardModifier } from './KeyboardModifier'
|
||||
|
||||
export type KeyboardShortcut = {
|
||||
command: KeyboardCommand
|
||||
modifiers?: KeyboardModifier[]
|
||||
key?: KeyboardKey | string
|
||||
/**
|
||||
* Alternative to using key, if the key can be affected by alt + shift. For example, if you want alt + shift + n,
|
||||
* use code 'KeyN' instead of key 'n', as the modifiers would turn n into '˜' on Mac.
|
||||
*/
|
||||
code?: string
|
||||
preventDefault?: boolean
|
||||
}
|
||||
|
||||
export type PlatformedKeyboardShortcut = KeyboardShortcut & {
|
||||
platform: Platform
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { KeyboardShortcut } from './KeyboardShortcut'
|
||||
import { modifiersForEvent } from './modifiersForEvent'
|
||||
|
||||
export function eventMatchesKeyAndModifiers(event: KeyboardEvent, shortcut: KeyboardShortcut): boolean {
|
||||
const eventModifiers = modifiersForEvent(event)
|
||||
const shortcutModifiers = shortcut.modifiers ?? []
|
||||
|
||||
if (eventModifiers.length !== shortcutModifiers.length) {
|
||||
return false
|
||||
}
|
||||
|
||||
for (const modifier of shortcutModifiers) {
|
||||
if (!eventModifiers.includes(modifier)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if (!shortcut.key && !shortcut.code) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (shortcut.key) {
|
||||
return shortcut.key.toLowerCase() === event.key.toLowerCase()
|
||||
} else {
|
||||
return shortcut.code === event.code
|
||||
}
|
||||
}
|
||||
136
packages/ui-services/src/Keyboard/getKeyboardShortcuts.ts
Normal file
136
packages/ui-services/src/Keyboard/getKeyboardShortcuts.ts
Normal file
@@ -0,0 +1,136 @@
|
||||
import { Environment, Platform } from '@standardnotes/snjs'
|
||||
import { isMacPlatform } from './platformCheck'
|
||||
import {
|
||||
CREATE_NEW_NOTE_KEYBOARD_COMMAND,
|
||||
TOGGLE_LIST_PANE_KEYBOARD_COMMAND,
|
||||
TOGGLE_NAVIGATION_PANE_KEYBOARD_COMMAND,
|
||||
NEXT_LIST_ITEM_KEYBOARD_COMMAND,
|
||||
PREVIOUS_LIST_ITEM_KEYBOARD_COMMAND,
|
||||
SEARCH_KEYBOARD_COMMAND,
|
||||
SELECT_ALL_ITEMS_KEYBOARD_COMMAND,
|
||||
SHOW_HIDDEN_OPTIONS_KEYBOARD_COMMAND,
|
||||
DELETE_NOTE_KEYBOARD_COMMAND,
|
||||
TAB_COMMAND,
|
||||
ESCAPE_COMMAND,
|
||||
CANCEL_SEARCH_COMMAND,
|
||||
TOGGLE_FOCUS_MODE_COMMAND,
|
||||
CHANGE_EDITOR_COMMAND,
|
||||
FOCUS_TAGS_INPUT_COMMAND,
|
||||
CREATE_NEW_TAG_COMMAND,
|
||||
OPEN_NOTE_HISTORY_COMMAND,
|
||||
CAPTURE_SAVE_COMMAND,
|
||||
STAR_NOTE_COMMAND,
|
||||
PIN_NOTE_COMMAND,
|
||||
} from './KeyboardCommands'
|
||||
import { KeyboardKey } from './KeyboardKey'
|
||||
import { KeyboardModifier } from './KeyboardModifier'
|
||||
import { KeyboardShortcut } from './KeyboardShortcut'
|
||||
|
||||
export function getKeyboardShortcuts(platform: Platform, _environment: Environment): KeyboardShortcut[] {
|
||||
const isMac = isMacPlatform(platform)
|
||||
|
||||
const primaryModifier = isMac ? KeyboardModifier.Meta : KeyboardModifier.Ctrl
|
||||
|
||||
return [
|
||||
{
|
||||
command: TOGGLE_LIST_PANE_KEYBOARD_COMMAND,
|
||||
key: 'l',
|
||||
modifiers: [primaryModifier, KeyboardModifier.Shift],
|
||||
},
|
||||
{
|
||||
command: TOGGLE_NAVIGATION_PANE_KEYBOARD_COMMAND,
|
||||
key: 'e',
|
||||
modifiers: [primaryModifier, KeyboardModifier.Shift],
|
||||
},
|
||||
{
|
||||
command: CREATE_NEW_NOTE_KEYBOARD_COMMAND,
|
||||
code: 'KeyN',
|
||||
modifiers: [KeyboardModifier.Alt, KeyboardModifier.Shift],
|
||||
},
|
||||
{
|
||||
command: NEXT_LIST_ITEM_KEYBOARD_COMMAND,
|
||||
key: KeyboardKey.Down,
|
||||
},
|
||||
{
|
||||
command: PREVIOUS_LIST_ITEM_KEYBOARD_COMMAND,
|
||||
key: KeyboardKey.Up,
|
||||
},
|
||||
{
|
||||
command: SEARCH_KEYBOARD_COMMAND,
|
||||
code: 'KeyF',
|
||||
modifiers: [KeyboardModifier.Alt, KeyboardModifier.Shift],
|
||||
},
|
||||
{
|
||||
command: CANCEL_SEARCH_COMMAND,
|
||||
key: KeyboardKey.Escape,
|
||||
},
|
||||
{
|
||||
command: SELECT_ALL_ITEMS_KEYBOARD_COMMAND,
|
||||
key: 'a',
|
||||
modifiers: [primaryModifier],
|
||||
},
|
||||
{
|
||||
command: SHOW_HIDDEN_OPTIONS_KEYBOARD_COMMAND,
|
||||
modifiers: [KeyboardModifier.Alt],
|
||||
},
|
||||
{
|
||||
command: DELETE_NOTE_KEYBOARD_COMMAND,
|
||||
key: KeyboardKey.Backspace,
|
||||
modifiers: [primaryModifier],
|
||||
},
|
||||
{
|
||||
command: TAB_COMMAND,
|
||||
key: KeyboardKey.Tab,
|
||||
},
|
||||
{
|
||||
command: ESCAPE_COMMAND,
|
||||
key: KeyboardKey.Escape,
|
||||
},
|
||||
{
|
||||
command: TOGGLE_FOCUS_MODE_COMMAND,
|
||||
key: 'f',
|
||||
modifiers: [primaryModifier, KeyboardModifier.Shift],
|
||||
},
|
||||
{
|
||||
command: CHANGE_EDITOR_COMMAND,
|
||||
key: '/',
|
||||
modifiers: [primaryModifier, KeyboardModifier.Shift],
|
||||
preventDefault: true,
|
||||
},
|
||||
{
|
||||
command: FOCUS_TAGS_INPUT_COMMAND,
|
||||
code: 'KeyT',
|
||||
modifiers: [primaryModifier, KeyboardModifier.Alt],
|
||||
preventDefault: true,
|
||||
},
|
||||
{
|
||||
command: CREATE_NEW_TAG_COMMAND,
|
||||
code: 'KeyN',
|
||||
modifiers: [primaryModifier, KeyboardModifier.Alt],
|
||||
},
|
||||
{
|
||||
command: OPEN_NOTE_HISTORY_COMMAND,
|
||||
key: 'h',
|
||||
modifiers: [primaryModifier, KeyboardModifier.Shift],
|
||||
preventDefault: true,
|
||||
},
|
||||
{
|
||||
command: CAPTURE_SAVE_COMMAND,
|
||||
key: 's',
|
||||
modifiers: [primaryModifier],
|
||||
preventDefault: true,
|
||||
},
|
||||
{
|
||||
command: STAR_NOTE_COMMAND,
|
||||
key: 's',
|
||||
modifiers: [primaryModifier, KeyboardModifier.Shift],
|
||||
preventDefault: true,
|
||||
},
|
||||
{
|
||||
command: PIN_NOTE_COMMAND,
|
||||
key: 'p',
|
||||
modifiers: [primaryModifier, KeyboardModifier.Shift],
|
||||
preventDefault: true,
|
||||
},
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { Platform } from '@standardnotes/snjs'
|
||||
import { KeyboardModifier } from './KeyboardModifier'
|
||||
|
||||
function isMacPlatform(platform: Platform) {
|
||||
return platform === Platform.MacDesktop || platform === Platform.MacWeb
|
||||
}
|
||||
|
||||
export function keyboardCharacterForModifier(modifier: KeyboardModifier, platform: Platform) {
|
||||
const isMac = isMacPlatform(platform)
|
||||
|
||||
if (modifier === KeyboardModifier.Meta) {
|
||||
return isMac ? '⌘' : '⊞'
|
||||
} else if (modifier === KeyboardModifier.Ctrl) {
|
||||
return isMac ? '⌃' : 'Ctrl'
|
||||
} else if (modifier === KeyboardModifier.Alt) {
|
||||
return isMac ? '⌥' : 'Alt'
|
||||
} else if (modifier === KeyboardModifier.Shift) {
|
||||
return isMac ? '⇧' : 'Shift'
|
||||
} else {
|
||||
return KeyboardModifier[modifier]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { isMacPlatform } from '@standardnotes/ui-services'
|
||||
import { keyboardCharacterForModifier } from './keyboardCharacterForModifier'
|
||||
import { PlatformedKeyboardShortcut } from './KeyboardShortcut'
|
||||
|
||||
function stringForCode(code = ''): string {
|
||||
return code.replace('Key', '').replace('Digit', '')
|
||||
}
|
||||
|
||||
export function keyboardStringForShortcut(shortcut: PlatformedKeyboardShortcut | undefined) {
|
||||
if (!shortcut) {
|
||||
return ''
|
||||
}
|
||||
|
||||
const key = shortcut.key?.toUpperCase() || stringForCode(shortcut.code)
|
||||
|
||||
if (!shortcut.modifiers || shortcut.modifiers.length === 0) {
|
||||
return key
|
||||
}
|
||||
|
||||
const modifierCharacters = shortcut.modifiers.map((modifier) =>
|
||||
keyboardCharacterForModifier(modifier, shortcut.platform),
|
||||
)
|
||||
|
||||
if (isMacPlatform(shortcut.platform)) {
|
||||
return `${modifierCharacters.join('')}${key}`
|
||||
} else {
|
||||
return `${modifierCharacters.join('+')}+${key}`
|
||||
}
|
||||
}
|
||||
18
packages/ui-services/src/Keyboard/modifiersForEvent.ts
Normal file
18
packages/ui-services/src/Keyboard/modifiersForEvent.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { KeyboardModifier } from './KeyboardModifier'
|
||||
|
||||
export function modifiersForEvent(event: KeyboardEvent): KeyboardModifier[] {
|
||||
const allModifiers = Object.values(KeyboardModifier)
|
||||
const eventModifiers = allModifiers.filter((modifier) => {
|
||||
// For a modifier like ctrlKey, must check both event.ctrlKey and event.key.
|
||||
// That's because on keyup, event.ctrlKey would be false, but event.key == Control would be true.
|
||||
const matches =
|
||||
((event.ctrlKey || event.key === KeyboardModifier.Ctrl) && modifier === KeyboardModifier.Ctrl) ||
|
||||
((event.metaKey || event.key === KeyboardModifier.Meta) && modifier === KeyboardModifier.Meta) ||
|
||||
((event.altKey || event.key === KeyboardModifier.Alt) && modifier === KeyboardModifier.Alt) ||
|
||||
((event.shiftKey || event.key === KeyboardModifier.Shift) && modifier === KeyboardModifier.Shift)
|
||||
|
||||
return matches
|
||||
})
|
||||
|
||||
return eventModifiers
|
||||
}
|
||||
9
packages/ui-services/src/Keyboard/platformCheck.ts
Normal file
9
packages/ui-services/src/Keyboard/platformCheck.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { Platform } from '@standardnotes/snjs'
|
||||
|
||||
export function isMacPlatform(platform: Platform) {
|
||||
return platform === Platform.MacDesktop || platform === Platform.MacWeb
|
||||
}
|
||||
|
||||
export function isWindowsPlatform(platform: Platform) {
|
||||
return platform === Platform.WindowsDesktop || platform === Platform.WindowsWeb
|
||||
}
|
||||
Reference in New Issue
Block a user