| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128 |
- (ns frontend.modules.shortcut.core
- (:require [clojure.string :as str]
- [frontend.handler.notification :as notification]
- [frontend.modules.shortcut.data-helper :as dh]
- [frontend.util :as util]
- [goog.events :as events]
- [goog.ui.KeyboardShortcutHandler.EventType :as EventType]
- [lambdaisland.glogi :as log]
- [medley.core :as medley])
- (:import [goog.events KeyCodes]
- [goog.ui KeyboardShortcutHandler]))
- (def *installed (atom {}))
- (def global-keys #js
- [KeyCodes/TAB
- KeyCodes/ENTER
- KeyCodes/BACKSPACE KeyCodes/DELETE
- KeyCodes/UP KeyCodes/LEFT KeyCodes/DOWN KeyCodes/RIGHT])
- (defn install-shortcut!
- [handler-id {:keys [set-global-keys?
- prevent-default?
- skip-installed?
- state]
- :or {set-global-keys? true
- prevent-default? false
- skip-installed? false}}]
- (let [shortcut-map (dh/shortcut-map handler-id state)
- handler (new KeyboardShortcutHandler js/window)]
- ;; set arrows enter, tab to global
- (when set-global-keys?
- (.setGlobalKeys handler global-keys))
- (.setAlwaysPreventDefault handler prevent-default?)
- ;; register shortcuts
- ;; TODO add try catch for register conflicts
- (doseq [[id _] shortcut-map]
- ;; (log/info :shortcut/install-shortcut {:id id :shortcut (dh/shortcut-binding id)})
- (doseq [k (dh/shortcut-binding id)]
- (try
- (.registerShortcut handler (util/keyname id) k)
- (catch js/Object e
- (log/error :shortcut/register-shortcut {:id id
- :binding k
- :error e})
- (notification/show! (str/join " " [id k (.-message e)]) :error false)))))
- (let [f (fn [e]
- (let [dispatch-fn (get shortcut-map (keyword (.-identifier e)))]
- ;; trigger fn
- (dispatch-fn e)))
- install-id (medley/random-uuid)
- data {install-id
- {:group handler-id
- :dispatch-fn f
- :handler handler}}]
- (events/listen handler EventType/SHORTCUT_TRIGGERED f)
- (when-not skip-installed?
- (swap! *installed merge data))
- install-id)))
- (defn install-shortcuts!
- []
- (install-shortcut! :shortcut.handler/misc {:skip-installed? true})
- (->> [:shortcut.handler/editor-global
- :shortcut.handler/global-non-editing-only
- :shortcut.handler/global-prevent-default]
- (map #(install-shortcut! % {}))
- doall))
- (defn uninstall-shortcut! [install-id]
- (let [handler
- (-> (get @*installed install-id)
- :handler)]
- (.dispose ^js handler)
- (swap! *installed dissoc install-id)))
- (defn- uninstall-shortcut-aux!
- [state handler-id]
- (some-> (get state :shortcut-key)
- uninstall-shortcut!))
- (defn- install-shortcut-aux!
- [state handler-id]
- (let [install-id (-> handler-id
- (install-shortcut! {:state state}))]
- (assoc state :shortcut-key install-id)))
- (defn mixin [handler-id]
- {:did-mount
- (fn [state]
- (install-shortcut-aux! state handler-id))
- :did-remount (fn [old-state new-state]
- ;; uninstall
- (uninstall-shortcut-aux! old-state handler-id)
- ;; update new states
- (install-shortcut-aux! new-state handler-id))
- :will-unmount
- (fn [state]
- (uninstall-shortcut-aux! state handler-id)
- (dissoc state :shortcut-key))})
- (defn unlisten-all []
- (doseq [{:keys [handler]} (vals @*installed)]
- (.removeAllListeners handler)))
- (defn listen-all []
- (doseq [{:keys [handler dispatch-fn]} (vals @*installed)]
- (events/listen handler EventType/SHORTCUT_TRIGGERED dispatch-fn)))
- (defn disable-all-shortcuts []
- {:did-mount
- (fn [state]
- (unlisten-all)
- state)
- :will-unmount
- (fn [state]
- (listen-all)
- state)})
|