feat: Added "Keyboard shortcuts" help dialog. Can be opened by pressing Shift + ?

This commit is contained in:
Aman Harwara
2024-01-27 15:25:49 +05:30
parent 289849724a
commit ff3c45ba35
27 changed files with 312 additions and 27 deletions

View File

@@ -1,7 +1,10 @@
import { KeyboardCommand } from './KeyboardCommands'
import { KeyboardShortcutCategory } from './KeyboardShortcut'
export type KeyboardCommandHandler = {
command: KeyboardCommand
category?: KeyboardShortcutCategory
description?: string
onKeyDown?: (event: KeyboardEvent, data?: unknown) => boolean | void
onKeyUp?: (event: KeyboardEvent, data?: unknown) => boolean | void
element?: HTMLElement

View File

@@ -39,3 +39,5 @@ export const SUPER_EXPORT_MARKDOWN = createKeyboardCommand('SUPER_EXPORT_MARKDOW
export const OPEN_PREFERENCES_COMMAND = createKeyboardCommand('OPEN_PREFERENCES_COMMAND')
export const CHANGE_EDITOR_WIDTH_COMMAND = createKeyboardCommand('CHANGE_EDITOR_WIDTH_COMMAND')
export const TOGGLE_KEYBOARD_SHORTCUTS_MODAL = createKeyboardCommand('TOGGLE_KEYBOARD_SHORTCUTS_MODAL')

View File

@@ -1,3 +1,6 @@
import { Platform } from '@standardnotes/models'
import { isMacPlatform } from './platformCheck'
export enum KeyboardModifier {
Shift = 'Shift',
Ctrl = 'Control',
@@ -5,3 +8,7 @@ export enum KeyboardModifier {
Meta = 'Meta',
Alt = 'Alt',
}
export function getPrimaryModifier(platform: Platform): KeyboardModifier {
return isMacPlatform(platform) ? KeyboardModifier.Meta : KeyboardModifier.Ctrl
}

View File

@@ -4,7 +4,7 @@ import { KeyboardCommand } from './KeyboardCommands'
import { KeyboardKeyEvent } from './KeyboardKeyEvent'
import { KeyboardModifier } from './KeyboardModifier'
import { KeyboardCommandHandler } from './KeyboardCommandHandler'
import { KeyboardShortcut, PlatformedKeyboardShortcut } from './KeyboardShortcut'
import { KeyboardShortcut, KeyboardShortcutHelpItem, PlatformedKeyboardShortcut } from './KeyboardShortcut'
import { getKeyboardShortcuts } from './getKeyboardShortcuts'
export class KeyboardService {
@@ -12,6 +12,8 @@ export class KeyboardService {
private commandHandlers = new Set<KeyboardCommandHandler>()
private commandMap = new Map<KeyboardCommand, KeyboardShortcut>()
private keyboardShortcutHelpItems = new Set<KeyboardShortcutHelpItem>()
constructor(
private platform: Platform,
environment: Environment,
@@ -190,10 +192,18 @@ export class KeyboardService {
addCommandHandler(observer: KeyboardCommandHandler): () => void {
this.commandHandlers.add(observer)
const helpItem = this.getKeyboardShortcutHelpItemForHandler(observer)
if (helpItem) {
this.keyboardShortcutHelpItems.add(helpItem)
}
return () => {
observer.onKeyDown = undefined
observer.onKeyDown = undefined
this.commandHandlers.delete(observer)
if (helpItem) {
this.keyboardShortcutHelpItems.delete(helpItem)
}
}
}
@@ -217,4 +227,48 @@ export class KeyboardService {
...shortcut,
}
}
getKeyboardShortcutHelpItemForHandler(handler: KeyboardCommandHandler): KeyboardShortcutHelpItem | undefined {
const shortcut = this.keyboardShortcutForCommand(handler.command)
if (!shortcut || !handler.category || !handler.description) {
return undefined
}
return {
...shortcut,
category: handler.category,
description: handler.description,
}
}
/**
* Register help item for a keyboard shortcut that is handled outside of the KeyboardService,
* for example by a library like Lexical.
*/
registerExternalKeyboardShortcutHelpItem(item: KeyboardShortcutHelpItem): () => void {
this.keyboardShortcutHelpItems.add(item)
return () => {
this.keyboardShortcutHelpItems.delete(item)
}
}
/**
* Register help item for a keyboard shortcut that is handled outside of the KeyboardService,
* for example by a library like Lexical.
*/
registerExternalKeyboardShortcutHelpItems(items: KeyboardShortcutHelpItem[]): () => void {
const disposers = items.map((item) => this.registerExternalKeyboardShortcutHelpItem(item))
return () => {
for (const disposer of disposers) {
disposer()
}
}
}
getRegisteredKeyboardShorcutHelpItems(): KeyboardShortcutHelpItem[] {
return Array.from(this.keyboardShortcutHelpItems)
}
}

View File

@@ -18,3 +18,10 @@ export type KeyboardShortcut = {
export type PlatformedKeyboardShortcut = KeyboardShortcut & {
platform: Platform
}
export type KeyboardShortcutCategory = 'General' | 'Notes list' | 'Current note' | 'Super notes' | 'Formatting'
export type KeyboardShortcutHelpItem = Omit<PlatformedKeyboardShortcut, 'command'> & {
category: KeyboardShortcutCategory
description: string
}

View File

@@ -1,5 +1,4 @@
import { Environment, Platform } from '@standardnotes/models'
import { isMacPlatform } from './platformCheck'
import {
CREATE_NEW_NOTE_KEYBOARD_COMMAND,
TOGGLE_LIST_PANE_KEYBOARD_COMMAND,
@@ -31,15 +30,14 @@ import {
SUPER_SEARCH_TOGGLE_REPLACE_MODE,
CHANGE_EDITOR_WIDTH_COMMAND,
SUPER_TOGGLE_TOOLBAR,
TOGGLE_KEYBOARD_SHORTCUTS_MODAL,
} from './KeyboardCommands'
import { KeyboardKey } from './KeyboardKey'
import { KeyboardModifier } from './KeyboardModifier'
import { KeyboardModifier, getPrimaryModifier } 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
const primaryModifier = getPrimaryModifier(platform)
return [
{
@@ -195,5 +193,11 @@ export function getKeyboardShortcuts(platform: Platform, _environment: Environme
modifiers: [primaryModifier, KeyboardModifier.Shift],
preventDefault: true,
},
{
command: TOGGLE_KEYBOARD_SHORTCUTS_MODAL,
key: '?',
preventDefault: true,
modifiers: [KeyboardModifier.Shift],
},
]
}

View File

@@ -0,0 +1,20 @@
export function keyboardCharacterForKeyOrCode(keyOrCode: string) {
if (keyOrCode.startsWith('Digit')) {
return keyOrCode.replace('Digit', '')
}
if (keyOrCode.startsWith('Key')) {
return keyOrCode.replace('Key', '')
}
switch (keyOrCode) {
case 'ArrowDown':
return '↓'
case 'ArrowUp':
return '↑'
case 'ArrowLeft':
return '←'
case 'ArrowRight':
return '→'
default:
return keyOrCode
}
}

View File

@@ -10,6 +10,7 @@ export * from './Keyboard/KeyboardCommands'
export * from './Keyboard/platformCheck'
export * from './Keyboard/KeyboardKey'
export * from './Keyboard/KeyboardModifier'
export * from './Keyboard/keyboardCharacterForKey'
export * from './Keyboard/keyboardCharacterForModifier'
export * from './Keyboard/keyboardStringForShortcut'
export * from './Route/Params/DemoParams'