Files
cinny/src/app/components/editor/keyboard.ts
T

93 lines
2.8 KiB
TypeScript
Raw Normal View History

2023-10-21 18:14:33 +11:00
import { isKeyHotkey } from 'is-hotkey';
2023-06-12 21:15:23 +10:00
import { KeyboardEvent } from 'react';
2023-10-21 18:14:33 +11:00
import { Editor, Range } from 'slate';
2023-10-18 13:15:30 +11:00
import { isAnyMarkActive, isBlockActive, removeAllMark, toggleBlock, toggleMark } from './utils';
import { BlockType, MarkType } from './types';
2023-06-12 21:15:23 +10:00
export const INLINE_HOTKEYS: Record<string, MarkType> = {
'mod+b': MarkType.Bold,
'mod+i': MarkType.Italic,
'mod+u': MarkType.Underline,
'mod+shift+u': MarkType.StrikeThrough,
'mod+[': MarkType.Code,
'mod+h': MarkType.Spoiler,
};
const INLINE_KEYS = Object.keys(INLINE_HOTKEYS);
export const BLOCK_HOTKEYS: Record<string, BlockType> = {
2023-10-21 18:14:33 +11:00
'mod+7': BlockType.OrderedList,
'mod+8': BlockType.UnorderedList,
"mod+'": BlockType.BlockQuote,
'mod+;': BlockType.CodeBlock,
2023-06-12 21:15:23 +10:00
};
const BLOCK_KEYS = Object.keys(BLOCK_HOTKEYS);
2023-06-14 03:47:18 +10:00
/**
* @return boolean true if shortcut is toggled.
*/
export const toggleKeyboardShortcut = (editor: Editor, event: KeyboardEvent<Element>): boolean => {
2023-10-21 18:14:33 +11:00
if (isKeyHotkey('backspace', event) && editor.selection && Range.isCollapsed(editor.selection)) {
const startPoint = Range.start(editor.selection);
if (startPoint.offset !== 0) return false;
const [parentNode, parentPath] = Editor.parent(editor, startPoint);
if (Editor.isEditor(parentNode)) return false;
if (parentNode.type === BlockType.Heading) {
toggleBlock(editor, BlockType.Paragraph);
return true;
}
if (
parentNode.type === BlockType.CodeLine ||
parentNode.type === BlockType.QuoteLine ||
parentNode.type === BlockType.ListItem
) {
// exit formatting only when line block
// is first of last of it's parent
const parentLocation = { at: parentPath };
const [previousNode] = Editor.previous(editor, parentLocation) ?? [];
const [nextNode] = Editor.next(editor, parentLocation) ?? [];
if (!previousNode || !nextNode) {
toggleBlock(editor, BlockType.Paragraph);
return true;
}
}
}
if (isKeyHotkey('mod+e', event) || isKeyHotkey('escape', event)) {
2023-06-14 03:47:18 +10:00
if (isAnyMarkActive(editor)) {
removeAllMark(editor);
return true;
}
2023-06-14 03:47:18 +10:00
if (!isBlockActive(editor, BlockType.Paragraph)) {
toggleBlock(editor, BlockType.Paragraph);
return true;
}
return false;
}
const blockToggled = BLOCK_KEYS.find((hotkey) => {
2023-10-21 18:14:33 +11:00
if (isKeyHotkey(hotkey, event)) {
2023-06-12 21:15:23 +10:00
event.preventDefault();
toggleBlock(editor, BLOCK_HOTKEYS[hotkey]);
2023-06-14 03:47:18 +10:00
return true;
2023-06-12 21:15:23 +10:00
}
2023-06-14 03:47:18 +10:00
return false;
2023-06-12 21:15:23 +10:00
});
2023-06-14 03:47:18 +10:00
if (blockToggled) return true;
2023-06-12 21:15:23 +10:00
2023-06-14 03:47:18 +10:00
const inlineToggled = isBlockActive(editor, BlockType.CodeBlock)
? false
: INLINE_KEYS.find((hotkey) => {
2023-10-21 18:14:33 +11:00
if (isKeyHotkey(hotkey, event)) {
2023-06-14 03:47:18 +10:00
event.preventDefault();
toggleMark(editor, INLINE_HOTKEYS[hotkey]);
return true;
}
return false;
});
return !!inlineToggled;
2023-06-12 21:15:23 +10:00
};