| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148 |
- (ns frontend.mixins
- "Rum mixins for use in components"
- (:require [frontend.state :as state]
- [frontend.util :refer [profile] :as util]
- [goog.dom :as dom]
- [rum.core :as rum])
- (:import [goog.events EventHandler]))
- (defn detach
- "Detach all event listeners."
- [state]
- (some-> state ::event-handler .removeAll))
- (defn listen
- "Register an event `handler` for events of `type` on `target`."
- [state target type handler & [opts]]
- (when-let [^EventHandler event-handler (::event-handler state)]
- (.listen event-handler target (name type) handler (clj->js opts))))
- (def event-handler-mixin
- "The event handler mixin."
- {:will-mount
- (fn [state]
- (assoc state ::event-handler (EventHandler.)))
- :will-unmount
- (fn [state]
- (detach state)
- (dissoc state ::event-handler))})
- (defn hide-when-esc-or-outside
- [state & {:keys [on-hide node visibilitychange? outside?]}]
- (let [opts (last (:rum/args state))
- outside? (cond-> opts (nil? outside?) (:outside?))]
- (try
- (let [dom-node (rum/dom-node state)]
- (when-let [dom-node (or node dom-node)]
- (let [click-fn (fn [e]
- (let [target (.. e -target)]
- ;; If the click target is outside of current node
- (when (and
- (not (dom/contains dom-node target))
- (not (.contains (.-classList target) "ignore-outside-event")))
- (on-hide state e :click))))]
- (when-not (false? outside?)
- (listen state js/window "mousedown" click-fn)))
- (listen state js/window "keydown"
- (fn [e]
- (case (.-keyCode e)
- ;; Esc
- 27 (on-hide state e :esc)
- nil)))
- (when visibilitychange?
- (listen state js/window "visibilitychange"
- (fn [e]
- (on-hide state e :visibilitychange))))))
- (catch :default _e
- ;; TODO: Unable to find node on an unmounted component.
- nil))))
- (defn on-enter
- [state & {on-enter-fn :on-enter :keys [node]}]
- (let [node (or node (rum/dom-node state))]
- (listen state node "keyup"
- (fn [e]
- (case (.-keyCode e)
- ;; Enter
- 13 (on-enter-fn e)
- nil)))))
- (defn on-key-up
- "Caution: This mixin uses a different args than on-key-down"
- [state keycode-map all-handler]
- (listen state js/window "keyup"
- (fn [e]
- (let [key-code (.-keyCode e)]
- (when-let [f (get keycode-map key-code)]
- (f state e))
- (when all-handler (all-handler e key-code))))))
- (defn on-key-down
- ([state keycode-map]
- (on-key-down state keycode-map {}))
- ([state keycode-map {:keys [not-matched-handler all-handler target keycode?]
- :or {keycode? true}}]
- (listen state (or target js/window) "keydown"
- (fn [e]
- (let [key (if keycode? (.-keyCode e) (.-key e))]
- (if-let [f (get keycode-map key)]
- (f state e)
- (when (and not-matched-handler (fn? not-matched-handler))
- (not-matched-handler e key)))
- (when (and all-handler (fn? all-handler))
- (all-handler e key)))))))
- (defn event-mixin
- ([attach-listeners]
- (event-mixin attach-listeners identity))
- ([attach-listeners init-callback]
- (merge
- event-handler-mixin
- {:init (fn [state _props]
- (init-callback state))
- :did-mount (fn [state]
- (attach-listeners state)
- state)})))
- (defn modal
- [k]
- (event-mixin
- (fn [state]
- (let [open? (get state k)]
- (hide-when-esc-or-outside
- state
- :on-hide (fn []
- (when (and open? @open?)
- (reset! open? false))))))
- (fn [state]
- (let [open? (atom false)
- component (:rum/react-component state)]
- (add-watch open? ::open
- (fn [_ _ _ _]
- (rum/request-render component)))
- (assoc state
- :open? open?
- :close-fn (fn []
- (reset! open? false))
- :open-fn (fn []
- (reset! open? true))
- :toggle-fn (fn []
- (swap! open? not)))))))
- (def container-id
- "Notice: the first parameter needs to be a `config` with `id`, optional `sidebar?`, `whiteboard?`"
- {:init (fn [state]
- (let [config (first (:rum/args state))
- key (select-keys config [:id :sidebar? :whiteboard? :embed? :custom-query? :query :current-block :table?])
- container-id (or (:container-id config) (state/get-container-id key))]
- (assoc state :container-id container-id)))})
- (defn perf-measure-mixin
- "Does performance measurements in development."
- [desc]
- {:wrap-render
- (fn wrap-render [render-fn]
- (fn [state]
- (profile
- (str "Render " desc)
- (render-fn state))))})
|