mirror of
https://github.com/penpot/penpot.git
synced 2025-06-06 03:11:39 +02:00
♻️ Improves shortcuts lifecycle management.
This commit is contained in:
parent
c33c3fb2fa
commit
4b7f82a9d9
6 changed files with 130 additions and 55 deletions
|
@ -5,14 +5,22 @@
|
||||||
;; Copyright (c) UXBOX Labs SL
|
;; Copyright (c) UXBOX Labs SL
|
||||||
|
|
||||||
(ns app.main.data.shortcuts
|
(ns app.main.data.shortcuts
|
||||||
|
(:refer-clojure :exclude [meta reset!])
|
||||||
(:require
|
(:require
|
||||||
["mousetrap" :as mousetrap]
|
["mousetrap" :as mousetrap]
|
||||||
|
[app.common.data :as d]
|
||||||
|
[app.common.spec :as us]
|
||||||
[app.config :as cfg]
|
[app.config :as cfg]
|
||||||
[app.util.logging :as log])
|
[app.util.logging :as log]
|
||||||
(:refer-clojure :exclude [meta]))
|
[cljs.spec.alpha :as s]
|
||||||
|
[potok.core :as ptk]))
|
||||||
|
|
||||||
(log/set-level! :warn)
|
(log/set-level! :warn)
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; Helpers
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
(def mac-command "\u2318")
|
(def mac-command "\u2318")
|
||||||
(def mac-option "\u2325")
|
(def mac-option "\u2325")
|
||||||
(def mac-delete "\u232B")
|
(def mac-delete "\u232B")
|
||||||
|
@ -44,30 +52,8 @@
|
||||||
[shortcut]
|
[shortcut]
|
||||||
(c-mod (a-mod shortcut)))
|
(c-mod (a-mod shortcut)))
|
||||||
|
|
||||||
(defn bind-shortcuts
|
(defn meta
|
||||||
([shortcuts-config]
|
[key]
|
||||||
(bind-shortcuts
|
|
||||||
shortcuts-config
|
|
||||||
mousetrap/bind
|
|
||||||
(fn [key cb]
|
|
||||||
(fn [event]
|
|
||||||
(log/debug :msg (str "Shortcut" key))
|
|
||||||
(.preventDefault event)
|
|
||||||
(cb event)))))
|
|
||||||
|
|
||||||
([shortcuts-config bind-fn cb-fn]
|
|
||||||
(doseq [[key {:keys [command disabled fn type]}] shortcuts-config]
|
|
||||||
(when-not disabled
|
|
||||||
(if (vector? command)
|
|
||||||
(doseq [cmd (seq command)]
|
|
||||||
(bind-fn cmd (cb-fn key fn) type))
|
|
||||||
(bind-fn command (cb-fn key fn) type))))))
|
|
||||||
|
|
||||||
(defn remove-shortcuts
|
|
||||||
[]
|
|
||||||
(mousetrap/reset))
|
|
||||||
|
|
||||||
(defn meta [key]
|
|
||||||
;; If the key is "+" we need to surround with quotes
|
;; If the key is "+" we need to surround with quotes
|
||||||
;; otherwise will not be very readable
|
;; otherwise will not be very readable
|
||||||
(let [key (if (and (not (cfg/check-platform? :macos))
|
(let [key (if (and (not (cfg/check-platform? :macos))
|
||||||
|
@ -80,37 +66,120 @@
|
||||||
"Ctrl+")
|
"Ctrl+")
|
||||||
key)))
|
key)))
|
||||||
|
|
||||||
(defn shift [key]
|
(defn shift
|
||||||
|
[key]
|
||||||
(str
|
(str
|
||||||
(if (cfg/check-platform? :macos)
|
(if (cfg/check-platform? :macos)
|
||||||
mac-shift
|
mac-shift
|
||||||
"Shift+")
|
"Shift+")
|
||||||
key))
|
key))
|
||||||
|
|
||||||
(defn alt [key]
|
(defn alt
|
||||||
|
[key]
|
||||||
(str
|
(str
|
||||||
(if (cfg/check-platform? :macos)
|
(if (cfg/check-platform? :macos)
|
||||||
mac-option
|
mac-option
|
||||||
"Alt+")
|
"Alt+")
|
||||||
key))
|
key))
|
||||||
|
|
||||||
(defn meta-shift [key]
|
(defn meta-shift
|
||||||
|
[key]
|
||||||
(-> key meta shift))
|
(-> key meta shift))
|
||||||
|
|
||||||
(defn meta-alt [key]
|
(defn meta-alt
|
||||||
|
[key]
|
||||||
(-> key meta alt))
|
(-> key meta alt))
|
||||||
|
|
||||||
(defn supr []
|
(defn supr
|
||||||
|
[]
|
||||||
(if (cfg/check-platform? :macos)
|
(if (cfg/check-platform? :macos)
|
||||||
mac-delete
|
mac-delete
|
||||||
"Supr"))
|
"Supr"))
|
||||||
|
|
||||||
(defn esc []
|
(defn esc
|
||||||
|
[]
|
||||||
(if (cfg/check-platform? :macos)
|
(if (cfg/check-platform? :macos)
|
||||||
mac-esc
|
mac-esc
|
||||||
"Escape"))
|
"Escape"))
|
||||||
|
|
||||||
(defn enter []
|
(defn enter
|
||||||
|
[]
|
||||||
(if (cfg/check-platform? :macos)
|
(if (cfg/check-platform? :macos)
|
||||||
mac-enter
|
mac-enter
|
||||||
"Enter"))
|
"Enter"))
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; Events
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
;; --- EVENT: push
|
||||||
|
|
||||||
|
(s/def ::tooltip ::us/string)
|
||||||
|
(s/def ::fn fn?)
|
||||||
|
|
||||||
|
(s/def ::command
|
||||||
|
(s/or :str ::us/string
|
||||||
|
:vec vector?))
|
||||||
|
|
||||||
|
(s/def ::shortcut
|
||||||
|
(s/keys :req-un [::command]
|
||||||
|
:opt-un [::fn
|
||||||
|
::tooltip]))
|
||||||
|
|
||||||
|
(s/def ::shortcuts
|
||||||
|
(s/map-of ::us/keyword
|
||||||
|
::shortcut))
|
||||||
|
|
||||||
|
(defn- wrap-cb
|
||||||
|
[key cb]
|
||||||
|
(fn [event]
|
||||||
|
(log/debug :msg (str "Shortcut" key))
|
||||||
|
(.preventDefault event)
|
||||||
|
(cb event)))
|
||||||
|
|
||||||
|
(defn- bind!
|
||||||
|
[shortcuts]
|
||||||
|
(->> shortcuts
|
||||||
|
(remove #(:disabled (second %)))
|
||||||
|
(run! (fn [[key {:keys [command fn type]}]]
|
||||||
|
(if (vector? command)
|
||||||
|
(run! #(mousetrap/bind % (wrap-cb key fn) type) command)
|
||||||
|
(mousetrap/bind command (wrap-cb key fn) type))))))
|
||||||
|
|
||||||
|
(defn- reset!
|
||||||
|
([]
|
||||||
|
(mousetrap/reset))
|
||||||
|
([shortcuts]
|
||||||
|
(mousetrap/reset)
|
||||||
|
(bind! shortcuts)))
|
||||||
|
|
||||||
|
(defn push-shortcuts
|
||||||
|
[key shortcuts]
|
||||||
|
(us/assert ::us/keyword key)
|
||||||
|
(us/assert ::shortcuts shortcuts)
|
||||||
|
(ptk/reify ::push-shortcuts
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(-> state
|
||||||
|
(update :shortcuts (fnil conj '()) [key shortcuts])))
|
||||||
|
|
||||||
|
ptk/EffectEvent
|
||||||
|
(effect [_ state stream]
|
||||||
|
(let [[key shortcuts] (peek (:shortcuts state))]
|
||||||
|
(reset! shortcuts)))))
|
||||||
|
|
||||||
|
(defn pop-shortcuts
|
||||||
|
[key]
|
||||||
|
(ptk/reify ::pop-shortcuts
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(update state :shortcuts (fn [shortcuts]
|
||||||
|
(let [current-key (first (peek shortcuts))]
|
||||||
|
(if (= key current-key)
|
||||||
|
(pop shortcuts)
|
||||||
|
shortcuts)))))
|
||||||
|
ptk/EffectEvent
|
||||||
|
(effect [_ state stream]
|
||||||
|
(let [[key* shortcuts] (peek (:shortcuts state))]
|
||||||
|
(when (not= key key*)
|
||||||
|
(reset! shortcuts))))))
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
:toggle-assets {:tooltip (ds/alt "I")
|
:toggle-assets {:tooltip (ds/alt "I")
|
||||||
:command (ds/a-mod "i")
|
:command (ds/a-mod "i")
|
||||||
:fn #(st/emit! (dw/go-to-layout :assets))}
|
:fn #(st/emit! (dw/go-to-layout :assets))}
|
||||||
|
|
||||||
:toggle-history {:tooltip (ds/alt "H")
|
:toggle-history {:tooltip (ds/alt "H")
|
||||||
:command (ds/a-mod "h")
|
:command (ds/a-mod "h")
|
||||||
:fn #(st/emit! (dw/go-to-layout :document-history))}
|
:fn #(st/emit! (dw/go-to-layout :document-history))}
|
||||||
|
@ -45,7 +45,7 @@
|
||||||
:toggle-rules {:tooltip (ds/meta-shift "R")
|
:toggle-rules {:tooltip (ds/meta-shift "R")
|
||||||
:command (ds/c-mod "shift+r")
|
:command (ds/c-mod "shift+r")
|
||||||
:fn #(st/emit! (dw/toggle-layout-flags :rules))}
|
:fn #(st/emit! (dw/toggle-layout-flags :rules))}
|
||||||
|
|
||||||
:select-all {:tooltip (ds/meta "A")
|
:select-all {:tooltip (ds/meta "A")
|
||||||
:command (ds/c-mod "a")
|
:command (ds/c-mod "a")
|
||||||
:fn #(st/emit! (dw/select-all))}
|
:fn #(st/emit! (dw/select-all))}
|
||||||
|
@ -73,7 +73,7 @@
|
||||||
:decrease-zoom {:tooltip "-"
|
:decrease-zoom {:tooltip "-"
|
||||||
:command "-"
|
:command "-"
|
||||||
:fn #(st/emit! (dw/decrease-zoom nil))}
|
:fn #(st/emit! (dw/decrease-zoom nil))}
|
||||||
|
|
||||||
:group {:tooltip (ds/meta "G")
|
:group {:tooltip (ds/meta "G")
|
||||||
:command (ds/c-mod "g")
|
:command (ds/c-mod "g")
|
||||||
:fn #(st/emit! dw/group-selected)}
|
:fn #(st/emit! dw/group-selected)}
|
||||||
|
@ -173,7 +173,8 @@
|
||||||
|
|
||||||
:paste {:tooltip (ds/meta "V")
|
:paste {:tooltip (ds/meta "V")
|
||||||
:disabled true
|
:disabled true
|
||||||
:command (ds/c-mod "v")}
|
:command (ds/c-mod "v")
|
||||||
|
:fn (constantly nil)}
|
||||||
|
|
||||||
:delete {:tooltip (ds/supr)
|
:delete {:tooltip (ds/supr)
|
||||||
:command ["del" "backspace"]
|
:command ["del" "backspace"]
|
||||||
|
|
|
@ -91,7 +91,7 @@
|
||||||
(events/unlistenByKey key1))))]
|
(events/unlistenByKey key1))))]
|
||||||
|
|
||||||
(mf/use-effect on-mount)
|
(mf/use-effect on-mount)
|
||||||
(hooks/use-shortcuts sc/shortcuts)
|
(hooks/use-shortcuts ::handoff sc/shortcuts)
|
||||||
|
|
||||||
[:div.handoff-layout {:class (dom/classnames :force-visible
|
[:div.handoff-layout {:class (dom/classnames :force-visible
|
||||||
(:show-thumbnails state))}
|
(:show-thumbnails state))}
|
||||||
|
|
|
@ -9,10 +9,11 @@
|
||||||
(:require
|
(:require
|
||||||
[app.common.spec :as us]
|
[app.common.spec :as us]
|
||||||
[app.main.data.shortcuts :as dsc]
|
[app.main.data.shortcuts :as dsc]
|
||||||
|
[app.main.store :as st]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.object :as obj]
|
|
||||||
[app.util.dom.dnd :as dnd]
|
[app.util.dom.dnd :as dnd]
|
||||||
[app.util.logging :as log]
|
[app.util.logging :as log]
|
||||||
|
[app.util.object :as obj]
|
||||||
[app.util.timers :as ts]
|
[app.util.timers :as ts]
|
||||||
[app.util.transit :as t]
|
[app.util.transit :as t]
|
||||||
[app.util.webapi :as wapi]
|
[app.util.webapi :as wapi]
|
||||||
|
@ -35,11 +36,13 @@
|
||||||
state))
|
state))
|
||||||
|
|
||||||
(defn use-shortcuts
|
(defn use-shortcuts
|
||||||
[shortcuts]
|
[key shortcuts]
|
||||||
(mf/use-effect
|
(mf/use-effect
|
||||||
|
#js [(str key) shortcuts]
|
||||||
(fn []
|
(fn []
|
||||||
(dsc/bind-shortcuts shortcuts)
|
(st/emit! (dsc/push-shortcuts key shortcuts))
|
||||||
(fn [] (dsc/remove-shortcuts)))))
|
(fn []
|
||||||
|
(st/emit! (dsc/pop-shortcuts key))))))
|
||||||
|
|
||||||
(defn invisible-image
|
(defn invisible-image
|
||||||
[]
|
[]
|
||||||
|
|
|
@ -237,7 +237,7 @@
|
||||||
(events/unlistenByKey key3))))]
|
(events/unlistenByKey key3))))]
|
||||||
|
|
||||||
(mf/use-effect on-mount)
|
(mf/use-effect on-mount)
|
||||||
(hooks/use-shortcuts sc/shortcuts)
|
(hooks/use-shortcuts ::viewer sc/shortcuts)
|
||||||
|
|
||||||
[:div.viewer-layout {:class (dom/classnames :force-visible
|
[:div.viewer-layout {:class (dom/classnames :force-visible
|
||||||
(:show-thumbnails state))}
|
(:show-thumbnails state))}
|
||||||
|
|
|
@ -153,18 +153,6 @@
|
||||||
(utils/update-transform render-node roots modifiers)
|
(utils/update-transform render-node roots modifiers)
|
||||||
(utils/remove-transform render-node roots))))))
|
(utils/remove-transform render-node roots))))))
|
||||||
|
|
||||||
(defn setup-shortcuts [path-editing? drawing-path?]
|
|
||||||
(mf/use-effect
|
|
||||||
(mf/deps path-editing? drawing-path?)
|
|
||||||
(fn []
|
|
||||||
(cond
|
|
||||||
(or drawing-path? path-editing?)
|
|
||||||
(dsc/bind-shortcuts psc/shortcuts)
|
|
||||||
|
|
||||||
:else
|
|
||||||
(dsc/bind-shortcuts wsc/shortcuts))
|
|
||||||
dsc/remove-shortcuts)))
|
|
||||||
|
|
||||||
(defn inside-vbox [vbox objects frame-id]
|
(defn inside-vbox [vbox objects frame-id]
|
||||||
(let [frame (get objects frame-id)]
|
(let [frame (get objects frame-id)]
|
||||||
|
|
||||||
|
@ -195,3 +183,17 @@
|
||||||
(:frame-id @hover))]
|
(:frame-id @hover))]
|
||||||
(when (not (contains? @active-frames frame-id))
|
(when (not (contains? @active-frames frame-id))
|
||||||
(swap! active-frames assoc frame-id true))))))
|
(swap! active-frames assoc frame-id true))))))
|
||||||
|
|
||||||
|
;; NOTE: this is executed on each page change, maybe we need to move
|
||||||
|
;; this shortcuts outside the viewport?
|
||||||
|
|
||||||
|
(defn setup-shortcuts
|
||||||
|
[path-editing? drawing-path?]
|
||||||
|
(hooks/use-shortcuts ::workspace wsc/shortcuts)
|
||||||
|
|
||||||
|
(mf/use-effect
|
||||||
|
(mf/deps path-editing? drawing-path?)
|
||||||
|
(fn []
|
||||||
|
(when (or drawing-path? path-editing?)
|
||||||
|
(st/emit! (dsc/push-shortcuts ::path psc/shortcuts))
|
||||||
|
(st/emitf (dsc/pop-shortcuts ::path))))))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue