Browse Source

enhance (wip): customizable shortcuts

Konstantinos Kaloutas 2 years ago
parent
commit
96aed148b4

+ 7 - 1
src/main/frontend/extensions/tldraw.cljs

@@ -10,6 +10,7 @@
             [frontend.handler.route :as route-handler]
             [frontend.handler.whiteboard :as whiteboard-handler]
             [frontend.handler.history :as history]
+            [frontend.modules.shortcut.data-helper :as shortcut-helper]
             [frontend.rum :as r]
             [frontend.search :as search]
             [frontend.state :as state]
@@ -75,13 +76,18 @@
   (apply whiteboard/references-count
          (map (fn [k] (js->clj (gobj/get props k) {:keywordize-keys true})) ["id" "className" "options"])))
 
+(rum/defc keyboard-shortcut
+  [props]
+  [(ui/render-keyboard-shortcut (shortcut-helper/gen-shortcut-seq (keyword (gobj/get props "action"))))])
+
 (def tldraw-renderers {:Page page-cp
                        :Block block-cp
                        :Breadcrumb breadcrumb
                        :Tweet tweet
                        :PageName page-name-link
                        :BacklinksCount references-count
-                       :BlockReference block-reference})
+                       :BlockReference block-reference
+                       :KeyboardShortcut keyboard-shortcut})
 
 (def undo (fn [] (history/undo! nil)))
 (def redo (fn [] (history/redo! nil)))

+ 20 - 2
src/main/frontend/handler/editor.cljs

@@ -3159,6 +3159,11 @@
       :else
       (js/document.execCommand "copy"))))
 
+(defn whiteboard?
+  []
+  (and (state/whiteboard-route?)
+       (.closest (.-activeElement js/document) ".logseq-tldraw")))
+
 (defn shortcut-cut
   "shortcut cut action:
   * when in selection mode, cut selected blocks
@@ -3172,14 +3177,24 @@
     (and (state/editing?) (util/input-text-selected?
                            (gdom/getElement (state/get-edit-input-id))))
     (keydown-backspace-handler true e)
+    
+    (whiteboard?)
+    (.cut (state/active-tldraw-app))
 
     :else
     nil))
 
 (defn delete-selection
   [e]
-  (when (state/selection?)
-    (shortcut-delete-selection e)))
+  (cond
+    (state/selection?)
+    (shortcut-delete-selection e)
+
+    (whiteboard?)
+    (.deleteShapes (.-api ^js (state/active-tldraw-app)))
+
+    :else
+    nil))
 
 (defn editor-delete
   [_state e]
@@ -3608,6 +3623,9 @@
         edit-block (state/get-edit-block)
         target-element (.-nodeName (.-target e))]
     (cond
+      (whiteboard?)
+      (.selectAll (.-api ^js (state/active-tldraw-app)))
+      
       ;; editing block fully selected
       (and edit-block edit-input
            (= (util/get-selected-text) (.-value edit-input)))

+ 53 - 4
src/main/frontend/modules/shortcut/config.cljs

@@ -71,6 +71,39 @@
    :pdf/find                     {:binding "alt+f"
                                   :fn      pdf-utils/open-finder}
 
+   :whiteboard/reset-zoom        {:binding "shift+0"
+                                  :fn      #(.resetZoom (.-api ^js (state/active-tldraw-app)))}
+
+   :whiteboard/zoom-to-fit       {:binding "shift+1"
+                                  :fn      #(.zoomToFit (.-api ^js (state/active-tldraw-app)))}
+
+   :whiteboard/zoom-to-selection {:binding "shift+2"
+                                  :fn      #(.zoomToSelection (.-api ^js (state/active-tldraw-app)))}
+
+   :whiteboard/collapse          {:binding "mod+up"
+                                  :fn      #(.setCollapsed (.-api ^js (state/active-tldraw-app)) true)}
+
+   :whiteboard/expand            {:binding "mod+down"
+                                  :fn      #(.setCollapsed (.-api ^js (state/active-tldraw-app)) false)}
+
+   :whiteboard/zoom-out          {:binding "mod+-"
+                                  :fn      #(.zoomOut (.-api ^js (state/active-tldraw-app)) false)}
+
+   :whiteboard/zoom-in           {:binding "mod+="
+                                  :fn      #(.zoomIn (.-api ^js (state/active-tldraw-app)) false)}
+
+   :whiteboard/send-backward     {:binding "["
+                                  :fn      #(.sendBackward (state/active-tldraw-app))}
+
+   :whiteboard/send-to-back      {:binding "shift+["
+                                  :fn      #(.sendToBack (state/active-tldraw-app))}
+
+   :whiteboard/bring-forward     {:binding "]"
+                                  :fn      #(.bringForward (state/active-tldraw-app))}
+
+   :whiteboard/bring-to-front    {:binding "shift+]"
+                                  :fn      #(.bringToFront (state/active-tldraw-app))}
+
    :whiteboard/lock              {:binding "mod+l"
                                   :fn      #(.setLocked (state/active-tldraw-app) true)}
 
@@ -83,7 +116,7 @@
    :whiteboard/ungroup           {:binding "mod+shift+g"
                                   :fn      #(.unGroup (.-api ^js (state/active-tldraw-app)))}
 
-   :whiteboard/toggle-grid       {:binding "mod+shift+g"
+   :whiteboard/toggle-grid       {:binding "shift g"
                                   :fn      #(.toggleGrid (.-api ^js (state/active-tldraw-app)))}
 
    :auto-complete/complete       {:binding "enter"
@@ -523,10 +556,21 @@
         (with-meta {:before m/enable-when-not-editing-mode!}))
 
     :shortcut.handler/whiteboard
-    (-> (build-category-map [:whiteboard/lock
+    (-> (build-category-map [:whiteboard/reset-zoom
+                             :whiteboard/zoom-to-fit
+                             :whiteboard/zoom-to-selection
+                             :whiteboard/collapse
+                             :whiteboard/expand
+                             :whiteboard/send-backward
+                             :whiteboard/send-to-back
+                             :whiteboard/bring-forward
+                             :whiteboard/bring-to-front
+                             :whiteboard/select-all
+                             :whiteboard/lock
                              :whiteboard/unlock
                              :whiteboard/group
-                             :whiteboard/ungroup])
+                             :whiteboard/ungroup
+                             :whiteboard/toggle-grid])
         (with-meta {:before m/enable-when-not-editing-mode!}))
 
     :shortcut.handler/auto-complete
@@ -781,7 +825,12 @@
     :ui/toggle-contents]
 
    :shortcut.category/whiteboard
-   [:whiteboard/lock
+   [:whiteboard/reset-zoom
+    :whiteboard/zoom-to-fit
+    :whiteboard/zoom-to-selection
+    :whiteboard/collapse
+    :whiteboard/expand
+    :whiteboard/lock
     :whiteboard/unlock
     :whiteboard/group
     :whiteboard/ungroup]

+ 15 - 4
src/main/frontend/modules/shortcut/dicts.cljc

@@ -79,10 +79,21 @@
    :editor/zoom-in                 "Zoom in editing block / Forwards otherwise"
    :editor/zoom-out                "Zoom out editing block / Backwards otherwise"
    :editor/toggle-undo-redo-mode   "Toggle undo redo mode (global or page only)"
-   :whiteboard/lock                "Lock selected elements"
-   :whiteboard/unlock              "Unlock selected elements"
-   :whiteboard/group               "Group selected elements"
-   :whiteboard/ungroup             "Ungroup selected elements"
+   :whiteboard/reset-zoom          "Reset zoom"
+   :whiteboard/zoom-to-fit         "Zoom to fit"
+   :whiteboard/zoom-to-selection   "Zoom to fit selection"
+   :whiteboard/collapse            "Collapse selected portals"
+   :whiteboard/expand              "Expand selected portals"
+   :whiteboard/zoom-out            "Zoom out"
+   :whiteboard/zoom-in             "Zoom in"
+   :whiteboard/send-backward       "Move backward"
+   :whiteboard/send-to-back        "Move to back"
+   :whiteboard/bring-forward       "Move forward"
+   :whiteboard/bring-to-front      "Move to front"
+   :whiteboard/lock                "Lock selection"
+   :whiteboard/unlock              "Unlock selection"
+   :whiteboard/group               "Group selection"
+   :whiteboard/ungroup             "Ungroup selection"
    :whiteboard/toggle-grid         "Toggle the canvas grid"
    :ui/toggle-brackets             "Toggle whether to display brackets"
    :go/search-in-page              "Search blocks in the current page"

+ 23 - 58
tldraw/apps/tldraw-logseq/src/components/ContextMenu/ContextMenu.tsx

@@ -20,7 +20,8 @@ export const ContextMenu = observer(function ContextMenu({
   collisionRef,
 }: ContextMenuProps) {
   const app = useApp()
-  const { handlers } = React.useContext(LogseqContext)
+  const { handlers, renderers } = React.useContext(LogseqContext)
+  const KeyboardShortcut = renderers?.KeyboardShortcut
   const rContent = React.useRef<HTMLDivElement>(null)
 
   const runAndTransition = (f: Function) => {
@@ -32,6 +33,14 @@ export const ContextMenu = observer(function ContextMenu({
     return isDev()
   }, [])
 
+  function shortcut(action: string) {
+    return (
+      <div className="tl-menu-right-slot">
+        <KeyboardShortcut action={action} />
+      </div>
+    )
+  }
+
   return (
     <ReactContextMenu.Root
       onOpenChange={open => {
@@ -138,11 +147,7 @@ export const ContextMenu = observer(function ContextMenu({
                 onClick={() => runAndTransition(app.api.zoomToSelection)}
               >
                 Zoom to fit
-                <div className="tl-menu-right-slot">
-                  <span className="keyboard-shortcut">
-                    <code>⇧</code> <code>2</code>
-                  </span>
-                </div>
+                {shortcut("whiteboard/zoom-to-fit")}
               </ReactContextMenu.Item>
               <ReactContextMenu.Separator className="menu-separator" />
             </>
@@ -159,11 +164,7 @@ export const ContextMenu = observer(function ContextMenu({
                   >
                     <TablerIcon className="tl-menu-icon" name="ungroup" />
                     Ungroup
-                    <div className="tl-menu-right-slot">
-                      <span className="keyboard-shortcut">
-                        <code>{MOD_KEY}</code> <code>⇧</code> <code>G</code>
-                      </span>
-                    </div>
+                    {shortcut("whiteboard/ungroup")}
                   </ReactContextMenu.Item>
                 )}
                 {app.selectedShapesArray.length > 1 &&
@@ -174,11 +175,7 @@ export const ContextMenu = observer(function ContextMenu({
                     >
                       <TablerIcon className="tl-menu-icon" name="group" />
                       Group
-                      <div className="tl-menu-right-slot">
-                        <span className="keyboard-shortcut">
-                          <code>{MOD_KEY}</code> <code>G</code>
-                        </span>
-                      </div>
+                      {shortcut("whiteboard/group")}
                     </ReactContextMenu.Item>
                   )}
                 <ReactContextMenu.Separator className="menu-separator" />
@@ -193,11 +190,7 @@ export const ContextMenu = observer(function ContextMenu({
                 >
                   <TablerIcon className="tl-menu-icon" name="cut" />
                   Cut
-                  <div className="tl-menu-right-slot">
-                    <span className="keyboard-shortcut">
-                      <code>{MOD_KEY}</code> <code>X</code>
-                    </span>
-                  </div>
+                  {shortcut("editor/cut")}
                 </ReactContextMenu.Item>
               )}
               <ReactContextMenu.Item
@@ -206,11 +199,7 @@ export const ContextMenu = observer(function ContextMenu({
               >
                 <TablerIcon className="tl-menu-icon" name="copy" />
                 Copy
-                <div className="tl-menu-right-slot">
-                  <span className="keyboard-shortcut">
-                    <code>{MOD_KEY}</code> <code>C</code>
-                  </span>
-                </div>
+                {shortcut("editor/copy")}
               </ReactContextMenu.Item>
             </>
           )}
@@ -223,7 +212,7 @@ export const ContextMenu = observer(function ContextMenu({
               Paste
               <div className="tl-menu-right-slot">
                 <span className="keyboard-shortcut">
-                  <code>{MOD_KEY}</code> <code>V</code>
+                  <code>{MOD_KEY}</code> <code>v</code>
                 </span>
               </div>
             </ReactContextMenu.Item>
@@ -236,7 +225,7 @@ export const ContextMenu = observer(function ContextMenu({
               Paste as link
               <div className="tl-menu-right-slot">
                 <span className="keyboard-shortcut">
-                  <code>{MOD_KEY}</code> <code>⇧</code> <code>V</code>
+                  <code>{MOD_KEY}</code> <code>⇧</code> <code>v</code>
                 </span>
               </div>
             </ReactContextMenu.Item>
@@ -272,11 +261,7 @@ export const ContextMenu = observer(function ContextMenu({
             onClick={() => runAndTransition(app.api.selectAll)}
           >
             Select all
-            <div className="tl-menu-right-slot">
-              <span className="keyboard-shortcut">
-                <code>{MOD_KEY}</code> <code>A</code>
-              </span>
-            </div>
+            {shortcut("editor/select-parent")}
           </ReactContextMenu.Item>
           {app.selectedShapes?.size > 1 && (
             <ReactContextMenu.Item
@@ -293,11 +278,7 @@ export const ContextMenu = observer(function ContextMenu({
             >
               <TablerIcon className="tl-menu-icon" name="lock" />
               Lock
-              <div className="tl-menu-right-slot">
-                <span className="keyboard-shortcut">
-                <code>{MOD_KEY}</code> <code>L</code>
-                </span>
-              </div>
+              {shortcut("whiteboard/lock")}
             </ReactContextMenu.Item>
           )}
           {app.selectedShapes?.size > 0 && app.selectedShapesArray?.some(s => s.props.isLocked) && (
@@ -307,11 +288,7 @@ export const ContextMenu = observer(function ContextMenu({
             >
               <TablerIcon className="tl-menu-icon" name="lock-open" />
               Unlock
-              <div className="tl-menu-right-slot">
-                <span className="keyboard-shortcut">
-                <code>{MOD_KEY}</code> <code>⇧</code> <code>L</code>
-                </span>
-              </div>
+              {shortcut("whiteboard/unlock")}
             </ReactContextMenu.Item>
           )}
           {app.selectedShapes?.size > 0 &&
@@ -324,11 +301,7 @@ export const ContextMenu = observer(function ContextMenu({
                 >
                   <TablerIcon className="tl-menu-icon" name="backspace" />
                   Delete
-                  <div className="tl-menu-right-slot">
-                    <span className="keyboard-shortcut">
-                      <code>Del</code>
-                    </span>
-                  </div>
+                  {shortcut("editor/delete")}
                 </ReactContextMenu.Item>
                 {app.selectedShapes?.size > 1 && !app.readOnly && (
                   <>
@@ -357,22 +330,14 @@ export const ContextMenu = observer(function ContextMenu({
                       onClick={() => runAndTransition(app.bringToFront)}
                     >
                       Move to front
-                      <div className="tl-menu-right-slot">
-                        <span className="keyboard-shortcut">
-                          <code>⇧</code> <code>]</code>
-                        </span>
-                      </div>
+                      {shortcut("whiteboard/bring-to-front")}
                     </ReactContextMenu.Item>
                     <ReactContextMenu.Item
                       className="tl-menu-item"
                       onClick={() => runAndTransition(app.sendToBack)}
                     >
                       Move to back
-                      <div className="tl-menu-right-slot">
-                        <span className="keyboard-shortcut">
-                          <code>⇧</code> <code>[</code>
-                        </span>
-                      </div>
+                      {shortcut("whiteboard/send-to-back")}
                     </ReactContextMenu.Item>
                   </>
                 )}

+ 3 - 0
tldraw/apps/tldraw-logseq/src/lib/logseq-context.ts

@@ -37,6 +37,9 @@ export interface LogseqContextValue {
         renderFn?: (open?: boolean, count?: number) => React.ReactNode
       }
     }>
+    KeyboardShortcut: React.FC<{
+      action: string
+    }>
   }
   handlers: {
     search: (

+ 0 - 10
tldraw/packages/core/src/lib/TLApi/TLApi.ts

@@ -196,16 +196,6 @@ export class TLApi<S extends TLShape = TLShape, K extends TLEventMap = TLEventMa
     return this
   }
 
-  save = () => {
-    this.app.save()
-    return this
-  }
-
-  saveAs = () => {
-    this.app.save()
-    return this
-  }
-
   undo = () => {
     this.app.undo()
     return this

+ 0 - 95
tldraw/packages/core/src/lib/TLApp/TLApp.ts

@@ -22,7 +22,6 @@ import {
   createNewLineBinding,
   dedupe,
   isNonNullable,
-  KeyUtils,
   uniqueId,
 } from '../../utils'
 import type { TLShape, TLShapeConstructor, TLShapeModel } from '../shapes'
@@ -92,94 +91,6 @@ export class TLApp<
 
   Tools: TLToolConstructor<S, K>[] = []
 
-  initKeyboardShortcuts() {
-    const ownShortcuts: TLShortcut<S, K>[] = [
-      {
-        keys: 'shift+0',
-        fn: () => this.api.resetZoom(),
-      },
-      {
-        keys: 'shift+1',
-        fn: () => this.api.zoomToFit(),
-      },
-      {
-        keys: 'shift+2',
-        fn: () => this.api.zoomToSelection(),
-      },
-      {
-        keys: 'mod+up',
-        fn: () => this.api.setCollapsed(true),
-      },
-      {
-        keys: 'mod+down',
-        fn: () => this.api.setCollapsed(false),
-      },
-      {
-        keys: 'mod+-',
-        fn: () => this.api.zoomOut(),
-      },
-      {
-        keys: 'mod+=',
-        fn: () => this.api.zoomIn(),
-      },
-      {
-        keys: 'mod+x',
-        fn: () => this.cut(),
-      },
-      {
-        keys: '[',
-        fn: () => this.sendBackward(),
-      },
-      {
-        keys: 'shift+[',
-        fn: () => this.sendToBack(),
-      },
-      {
-        keys: ']',
-        fn: () => this.bringForward(),
-      },
-      {
-        keys: 'shift+]',
-        fn: () => this.bringToFront(),
-      },
-      {
-        keys: 'mod+a',
-        fn: () => {
-          const { selectedTool } = this
-          if (selectedTool.id !== 'select') {
-            this.selectTool('select')
-          }
-          this.api.selectAll()
-        },
-      },
-      {
-        keys: 'mod+shift+s',
-        fn: () => {
-          this.saveAs()
-          this.notify('saveAs', null)
-        },
-      },
-      {
-        keys: 'mod+shift+v',
-        fn: (_, __, e) => {
-          if (!this.editingShape) {
-            e.preventDefault()
-            this.paste(undefined, true)
-          }
-        },
-      },
-      {
-        keys: ['del', 'backspace'],
-        fn: () => {
-          if (!this.editingShape) {
-            this.api.deleteShapes()
-            this.selectedTool.transition('idle')
-          }
-        },
-      },
-    ]
-  }
-
   /* --------------------- History -------------------- */
 
   history = new TLHistory<S, K>(this)
@@ -226,12 +137,6 @@ export class TLApp<
     return this
   }
 
-  saveAs = (): this => {
-    // todo
-    this.notify('saveAs', null)
-    return this
-  }
-
   @computed get serialized(): TLDocumentModel<S> {
     return {
       // currentPageId: this.currentPageId,

+ 0 - 10
tldraw/packages/core/src/types/types.ts

@@ -163,14 +163,6 @@ export type TLSubscriptionEvent =
       event: 'persist'
       info: { replace: boolean }
     }
-  | {
-      event: 'save'
-      info: null
-    }
-  | {
-      event: 'saveAs'
-      info: null
-    }
   | {
       event: 'undo'
       info: null
@@ -260,8 +252,6 @@ export interface TLCallbacks<
 > {
   onMount: TLCallback<S, K, R, 'mount'>
   onPersist: TLCallback<S, K, R, 'persist'>
-  onSave: TLCallback<S, K, R, 'save'>
-  onSaveAs: TLCallback<S, K, R, 'saveAs'>
   onError: TLCallback<S, K, R, 'error'>
 }
 

+ 1 - 5
tldraw/packages/react/src/hooks/useSetup.ts

@@ -10,8 +10,6 @@ export function useSetup<
 >(app: R, props: TLAppPropsWithApp<S, R> | TLAppPropsWithoutApp<S, R>) {
   const {
     onPersist,
-    onSave,
-    onSaveAs,
     onError,
     onMount,
     onCreateAssets,
@@ -43,8 +41,6 @@ export function useSetup<
   React.useLayoutEffect(() => {
     const unsubs: (() => void)[] = []
     if (onPersist) unsubs.push(app.subscribe('persist', onPersist))
-    if (onSave) unsubs.push(app.subscribe('save', onSave))
-    if (onSaveAs) unsubs.push(app.subscribe('saveAs', onSaveAs))
     if (onError) unsubs.push(app.subscribe('error', onError))
     if (onCreateShapes) unsubs.push(app.subscribe('create-shapes', onCreateShapes))
     if (onCreateAssets) unsubs.push(app.subscribe('create-assets', onCreateAssets))
@@ -56,5 +52,5 @@ export function useSetup<
     if (onCanvasDBClick) unsubs.push(app.subscribe('canvas-dbclick', onCanvasDBClick))
     // Kind of unusual, is this the right pattern?
     return () => unsubs.forEach(unsub => unsub())
-  }, [app, onPersist, onSave, onSaveAs, onError])
+  }, [app, onPersist, onError])
 }

+ 0 - 2
tldraw/packages/react/src/types/TLReactSubscriptions.tsx

@@ -31,8 +31,6 @@ export interface TLReactCallbacks<
 > {
   onMount: TLReactCallback<S, R, 'mount'>
   onPersist: TLReactCallback<S, R, 'persist'>
-  onSave: TLReactCallback<S, R, 'save'>
-  onSaveAs: TLReactCallback<S, R, 'saveAs'>
   onError: TLReactCallback<S, R, 'error'>
   onCreateShapes: TLReactCallback<S, R, 'create-shapes'>
   onCreateAssets: TLReactCallback<S, R, 'create-assets'>