core.cljs 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. (ns frontend.modules.shortcut.core
  2. (:require [clojure.string :as str]
  3. [frontend.handler.notification :as notification]
  4. [frontend.modules.shortcut.data-helper :as dh]
  5. [frontend.util :as util]
  6. [goog.events :as events]
  7. [goog.ui.KeyboardShortcutHandler.EventType :as EventType]
  8. [lambdaisland.glogi :as log]
  9. [medley.core :as medley])
  10. (:import [goog.events KeyCodes]
  11. [goog.ui KeyboardShortcutHandler]))
  12. (def *installed (atom {}))
  13. (def global-keys #js
  14. [KeyCodes/TAB
  15. KeyCodes/ENTER
  16. KeyCodes/BACKSPACE KeyCodes/DELETE
  17. KeyCodes/UP KeyCodes/LEFT KeyCodes/DOWN KeyCodes/RIGHT])
  18. (defn install-shortcut!
  19. [handler-id {:keys [set-global-keys?
  20. prevent-default?
  21. skip-installed?
  22. state]
  23. :or {set-global-keys? true
  24. prevent-default? false
  25. skip-installed? false}}]
  26. (let [shortcut-map (dh/shortcut-map handler-id state)
  27. handler (new KeyboardShortcutHandler js/window)]
  28. ;; set arrows enter, tab to global
  29. (when set-global-keys?
  30. (.setGlobalKeys handler global-keys))
  31. (.setAlwaysPreventDefault handler prevent-default?)
  32. ;; register shortcuts
  33. (doseq [[id _] shortcut-map]
  34. ;; (log/info :shortcut/install-shortcut {:id id :shortcut (dh/shortcut-binding id)})
  35. (when-not (false? (dh/shortcut-binding id))
  36. (doseq [k (dh/shortcut-binding id)]
  37. (try
  38. (log/debug :shortcut/register-shortcut {:id id :binding k})
  39. (.registerShortcut handler (util/keyname id) k)
  40. (catch js/Object e
  41. (log/error :shortcut/register-shortcut {:id id
  42. :binding k
  43. :error e})
  44. (notification/show! (str/join " " [id k (.-message e)]) :error false))))))
  45. (let [f (fn [e]
  46. (let [dispatch-fn (get shortcut-map (keyword (.-identifier e)))]
  47. ;; trigger fn
  48. (dispatch-fn e)))
  49. install-id (medley/random-uuid)
  50. data {install-id
  51. {:group handler-id
  52. :dispatch-fn f
  53. :handler handler}}]
  54. (events/listen handler EventType/SHORTCUT_TRIGGERED f)
  55. (when-not skip-installed?
  56. (swap! *installed merge data))
  57. install-id)))
  58. (defn- install-shortcuts!
  59. []
  60. (->> [:shortcut.handler/misc
  61. :shortcut.handler/editor-global
  62. :shortcut.handler/global-non-editing-only
  63. :shortcut.handler/global-prevent-default]
  64. (map #(install-shortcut! % {}))
  65. doall))
  66. (defn- uninstall-shortcut! [install-id]
  67. (when-let
  68. [handler (-> (get @*installed install-id)
  69. :handler)]
  70. (.dispose ^js handler)
  71. (swap! *installed dissoc install-id)))
  72. (defn- uninstall-shortcut-aux!
  73. [state handler-id]
  74. (some-> (get state :shortcut-key)
  75. uninstall-shortcut!))
  76. (defn- install-shortcut-aux!
  77. [state handler-id]
  78. (let [install-id (-> handler-id
  79. (install-shortcut! {:state state}))]
  80. (assoc state :shortcut-key install-id)))
  81. (defn mixin [handler-id]
  82. {:did-mount
  83. (fn [state]
  84. (install-shortcut-aux! state handler-id))
  85. :did-remount (fn [old-state new-state]
  86. ;; uninstall
  87. (uninstall-shortcut-aux! old-state handler-id)
  88. ;; update new states
  89. (install-shortcut-aux! new-state handler-id))
  90. :will-unmount
  91. (fn [state]
  92. (uninstall-shortcut-aux! state handler-id)
  93. (dissoc state :shortcut-key))})
  94. (defn unlisten-all []
  95. (doseq [{:keys [handler group]} (vals @*installed)
  96. :when (not= group :shortcut.handler/misc)]
  97. (.removeAllListeners handler)))
  98. (defn listen-all []
  99. (doseq [{:keys [handler group dispatch-fn]} (vals @*installed)
  100. :when (not= group :shortcut.handler/misc)]
  101. (events/listen handler EventType/SHORTCUT_TRIGGERED dispatch-fn)))
  102. (defn disable-all-shortcuts []
  103. {:did-mount
  104. (fn [state]
  105. (unlisten-all)
  106. state)
  107. :will-unmount
  108. (fn [state]
  109. (listen-all)
  110. state)})
  111. (defn refresh!
  112. "Always use this function to refresh shortcuts"
  113. []
  114. (log/info :shortcut/refresh @*installed)
  115. (doseq [id (keys @*installed)]
  116. (uninstall-shortcut! id))
  117. (install-shortcuts!))