Fix hotkeys (#1468)
authorAjay Bura <32841439+ajbura@users.noreply.github.com>
Sat, 21 Oct 2023 07:14:33 +0000 (18:14 +1100)
committerGitHub <noreply@github.com>
Sat, 21 Oct 2023 07:14:33 +0000 (12:44 +0530)
* use hotkey using key instead of which (default)

* remove shift from block formatting hotkeys

* smartly exit formatting with backspace

* set markdown to off by default

* exit formatting with escape

src/app/components/editor/Toolbar.tsx
src/app/components/editor/autocomplete/AutocompleteMenu.tsx
src/app/components/editor/keyboard.ts
src/app/components/emoji-board/EmojiBoard.tsx
src/app/organisms/room/RoomInput.tsx
src/app/organisms/room/RoomTimeline.tsx
src/app/organisms/room/message/MessageEditor.tsx
src/app/state/settings.ts
src/app/utils/keyboard.ts

index 342dd1060adafdaf906521844618d15052299767..766a1d8394a49b7aaa27847307271b16e33abe0f 100644 (file)
@@ -261,33 +261,22 @@ export function Toolbar() {
             <BlockButton
               format={BlockType.BlockQuote}
               icon={Icons.BlockQuote}
-              tooltip={
-                <BtnTooltip text="Block Quote" shortCode={`${modKey} + ${KeySymbol.Shift} + '`} />
-              }
+              tooltip={<BtnTooltip text="Block Quote" shortCode={`${modKey} + '`} />}
             />
             <BlockButton
               format={BlockType.CodeBlock}
               icon={Icons.BlockCode}
-              tooltip={
-                <BtnTooltip text="Block Code" shortCode={`${modKey} + ${KeySymbol.Shift} + ;`} />
-              }
+              tooltip={<BtnTooltip text="Block Code" shortCode={`${modKey} + ;`} />}
             />
             <BlockButton
               format={BlockType.OrderedList}
               icon={Icons.OrderList}
-              tooltip={
-                <BtnTooltip text="Ordered List" shortCode={`${modKey} + ${KeySymbol.Shift} + 7`} />
-              }
+              tooltip={<BtnTooltip text="Ordered List" shortCode={`${modKey} + 7`} />}
             />
             <BlockButton
               format={BlockType.UnorderedList}
               icon={Icons.UnorderList}
-              tooltip={
-                <BtnTooltip
-                  text="Unordered List"
-                  shortCode={`${modKey} + ${KeySymbol.Shift} + 8`}
-                />
-              }
+              tooltip={<BtnTooltip text="Unordered List" shortCode={`${modKey} + 8`} />}
             />
             <HeadingBlockButton />
           </Box>
@@ -296,7 +285,9 @@ export function Toolbar() {
               <Line variant="SurfaceVariant" direction="Vertical" style={{ height: toRem(12) }} />
               <Box shrink="No" gap="100">
                 <ExitFormatting
-                  tooltip={<BtnTooltip text="Exit Formatting" shortCode={`${modKey} + E`} />}
+                  tooltip={
+                    <BtnTooltip text="Exit Formatting" shortCode={`Escape, ${modKey} + E`} />
+                  }
                 />
               </Box>
             </>
index e7c8df38828d84baa98a15b5b86b5e8e05016587..fc4327daf0e66a6b4f01838752d4fb944a3046fd 100644 (file)
@@ -1,6 +1,6 @@
 import React, { ReactNode } from 'react';
 import FocusTrap from 'focus-trap-react';
-import isHotkey from 'is-hotkey';
+import { isKeyHotkey } from 'is-hotkey';
 import { Header, Menu, Scroll, config } from 'folds';
 
 import * as css from './AutocompleteMenu.css';
@@ -22,8 +22,8 @@ export function AutocompleteMenu({ headerContent, requestClose, children }: Auto
             returnFocusOnDeactivate: false,
             clickOutsideDeactivates: true,
             allowOutsideClick: true,
-            isKeyForward: (evt: KeyboardEvent) => isHotkey('arrowdown', evt),
-            isKeyBackward: (evt: KeyboardEvent) => isHotkey('arrowup', evt),
+            isKeyForward: (evt: KeyboardEvent) => isKeyHotkey('arrowdown', evt),
+            isKeyBackward: (evt: KeyboardEvent) => isKeyHotkey('arrowup', evt),
           }}
         >
           <Menu className={css.AutocompleteMenu}>
index b6d4d692bd560c2aa26773ac4d87672a8590ed52..7031749e73f6488c481b165da529c5b70bdedefd 100644 (file)
@@ -1,6 +1,6 @@
-import { isHotkey } from 'is-hotkey';
+import { isKeyHotkey } from 'is-hotkey';
 import { KeyboardEvent } from 'react';
-import { Editor } from 'slate';
+import { Editor, Range } from 'slate';
 import { isAnyMarkActive, isBlockActive, removeAllMark, toggleBlock, toggleMark } from './utils';
 import { BlockType, MarkType } from './types';
 
@@ -15,10 +15,10 @@ export const INLINE_HOTKEYS: Record<string, MarkType> = {
 const INLINE_KEYS = Object.keys(INLINE_HOTKEYS);
 
 export const BLOCK_HOTKEYS: Record<string, BlockType> = {
-  'mod+shift+7': BlockType.OrderedList,
-  'mod+shift+8': BlockType.UnorderedList,
-  "mod+shift+'": BlockType.BlockQuote,
-  'mod+shift+;': BlockType.CodeBlock,
+  'mod+7': BlockType.OrderedList,
+  'mod+8': BlockType.UnorderedList,
+  "mod+'": BlockType.BlockQuote,
+  'mod+;': BlockType.CodeBlock,
 };
 const BLOCK_KEYS = Object.keys(BLOCK_HOTKEYS);
 
@@ -26,7 +26,36 @@ const BLOCK_KEYS = Object.keys(BLOCK_HOTKEYS);
  * @return boolean true if shortcut is toggled.
  */
 export const toggleKeyboardShortcut = (editor: Editor, event: KeyboardEvent<Element>): boolean => {
-  if (isHotkey('mod+e', event)) {
+  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)) {
     if (isAnyMarkActive(editor)) {
       removeAllMark(editor);
       return true;
@@ -40,7 +69,7 @@ export const toggleKeyboardShortcut = (editor: Editor, event: KeyboardEvent<Elem
   }
 
   const blockToggled = BLOCK_KEYS.find((hotkey) => {
-    if (isHotkey(hotkey, event)) {
+    if (isKeyHotkey(hotkey, event)) {
       event.preventDefault();
       toggleBlock(editor, BLOCK_HOTKEYS[hotkey]);
       return true;
@@ -52,7 +81,7 @@ export const toggleKeyboardShortcut = (editor: Editor, event: KeyboardEvent<Elem
   const inlineToggled = isBlockActive(editor, BlockType.CodeBlock)
     ? false
     : INLINE_KEYS.find((hotkey) => {
-        if (isHotkey(hotkey, event)) {
+        if (isKeyHotkey(hotkey, event)) {
           event.preventDefault();
           toggleMark(editor, INLINE_HOTKEYS[hotkey]);
           return true;
index 94ba14c1cfb9ae18f56c23843b48a2e69b76a7ce..52df9258216c63b14b7a3ad98709d562ef3b6f8b 100644 (file)
@@ -28,7 +28,7 @@ import {
   toRem,
 } from 'folds';
 import FocusTrap from 'focus-trap-react';
-import isHotkey from 'is-hotkey';
+import { isKeyHotkey } from 'is-hotkey';
 import classNames from 'classnames';
 import { MatrixClient, Room } from 'matrix-js-sdk';
 import { atom, useAtomValue, useSetAtom } from 'jotai';
@@ -769,9 +769,9 @@ export function EmojiBoard({
         clickOutsideDeactivates: true,
         allowOutsideClick: true,
         isKeyForward: (evt: KeyboardEvent) =>
-          !editableActiveElement() && isHotkey(['arrowdown', 'arrowright'], evt),
+          !editableActiveElement() && isKeyHotkey(['arrowdown', 'arrowright'], evt),
         isKeyBackward: (evt: KeyboardEvent) =>
-          !editableActiveElement() && isHotkey(['arrowup', 'arrowleft'], evt),
+          !editableActiveElement() && isKeyHotkey(['arrowup', 'arrowleft'], evt),
       }}
     >
       <EmojiBoardLayout
index a1fa6c26836ae88f72d3be4969277f175e481a50..f7b219be11be1921d6924bbb5ec714f3c4f96bd4 100644 (file)
@@ -9,7 +9,7 @@ import React, {
   useState,
 } from 'react';
 import { useAtom } from 'jotai';
-import isHotkey from 'is-hotkey';
+import { isKeyHotkey } from 'is-hotkey';
 import { EventType, IContent, MsgType, Room } from 'matrix-js-sdk';
 import { ReactEditor } from 'slate-react';
 import { Transforms, Editor } from 'slate';
@@ -319,11 +319,11 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
 
     const handleKeyDown: KeyboardEventHandler = useCallback(
       (evt) => {
-        if (enterForNewline ? isHotkey('shift+enter', evt) : isHotkey('enter', evt)) {
+        if (enterForNewline ? isKeyHotkey('shift+enter', evt) : isKeyHotkey('enter', evt)) {
           evt.preventDefault();
           submit();
         }
-        if (isHotkey('escape', evt)) {
+        if (isKeyHotkey('escape', evt)) {
           evt.preventDefault();
           setReplyDraft();
         }
@@ -333,7 +333,7 @@ export const RoomInput = forwardRef<HTMLDivElement, RoomInputProps>(
 
     const handleKeyUp: KeyboardEventHandler = useCallback(
       (evt) => {
-        if (isHotkey('escape', evt)) {
+        if (isKeyHotkey('escape', evt)) {
           evt.preventDefault();
           return;
         }
index 2a22245a17dec719d44bbba7897b774451884722..0852ecf7a4fde98600c9fcecef7ca09eff8c77e2 100644 (file)
@@ -43,7 +43,7 @@ import {
   config,
   toRem,
 } from 'folds';
-import isHotkey from 'is-hotkey';
+import { isKeyHotkey } from 'is-hotkey';
 import Linkify from 'linkify-react';
 import {
   decryptFile,
@@ -725,7 +725,7 @@ export function RoomTimeline({ room, eventId, roomInputRef, editor }: RoomTimeli
     useCallback(
       (evt) => {
         if (
-          isHotkey('arrowup', evt) &&
+          isKeyHotkey('arrowup', evt) &&
           editableActiveElement() &&
           document.activeElement?.getAttribute('data-editable-name') === 'RoomInput' &&
           isEmptyEditor(editor)
index f38cfbef74103473dfd6a0f5cbadaba505113f90..385042ec1ec8cb7df4e978ba4eddd68283549b03 100644 (file)
@@ -3,7 +3,7 @@ import { Box, Chip, Icon, IconButton, Icons, Line, PopOut, Spinner, Text, as, co
 import { Editor, Transforms } from 'slate';
 import { ReactEditor } from 'slate-react';
 import { IContent, MatrixEvent, RelationType, Room } from 'matrix-js-sdk';
-import isHotkey from 'is-hotkey';
+import { isKeyHotkey } from 'is-hotkey';
 import {
   AUTOCOMPLETE_PREFIXES,
   AutocompletePrefix,
@@ -120,11 +120,11 @@ export const MessageEditor = as<'div', MessageEditorProps>(
 
     const handleKeyDown: KeyboardEventHandler = useCallback(
       (evt) => {
-        if (enterForNewline ? isHotkey('shift+enter', evt) : isHotkey('enter', evt)) {
+        if (enterForNewline ? isKeyHotkey('shift+enter', evt) : isKeyHotkey('enter', evt)) {
           evt.preventDefault();
           handleSave();
         }
-        if (isHotkey('escape', evt)) {
+        if (isKeyHotkey('escape', evt)) {
           evt.preventDefault();
           onCancel();
         }
@@ -134,7 +134,7 @@ export const MessageEditor = as<'div', MessageEditorProps>(
 
     const handleKeyUp: KeyboardEventHandler = useCallback(
       (evt) => {
-        if (isHotkey('escape', evt)) {
+        if (isKeyHotkey('escape', evt)) {
           evt.preventDefault();
           return;
         }
index 7770a7586c7892db6ef3b780845466034e516fb5..26e3431dc6542a5c0bda74717b1aa06768484a43 100644 (file)
@@ -28,7 +28,7 @@ export interface Settings {
 const defaultSettings: Settings = {
   themeIndex: 0,
   useSystemTheme: true,
-  isMarkdown: true,
+  isMarkdown: false,
   editorToolbar: false,
   useSystemEmoji: false,
 
index 56eeb9fcb97f994b6e1940f778d0c08ba347b2c1..78aa252186ee71b640c4d73c72f1b833eb7e6f36 100644 (file)
@@ -1,4 +1,4 @@
-import isHotkey from 'is-hotkey';
+import { isKeyHotkey } from 'is-hotkey';
 import { KeyboardEventHandler } from 'react';
 
 export interface KeyboardEventLike {
@@ -12,14 +12,14 @@ export interface KeyboardEventLike {
 }
 
 export const onTabPress = (evt: KeyboardEventLike, callback: () => void) => {
-  if (isHotkey('tab', evt)) {
+  if (isKeyHotkey('tab', evt)) {
     evt.preventDefault();
     callback();
   }
 };
 
 export const preventScrollWithArrowKey: KeyboardEventHandler = (evt) => {
-  if (isHotkey(['arrowup', 'arrowright', 'arrowdown', 'arrowleft'], evt)) {
+  if (isKeyHotkey(['arrowup', 'arrowright', 'arrowdown', 'arrowleft'], evt)) {
     evt.preventDefault();
   }
 };