Skip to content

Instantly share code, notes, and snippets.

@mccraigmccraig
Last active June 30, 2017 23:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mccraigmccraig/05637d98213988ff577e2d533966a292 to your computer and use it in GitHub Desktop.
Save mccraigmccraig/05637d98213988ff577e2d533966a292 to your computer and use it in GitHub Desktop.
(ns er-webui.components.swipable.views
(:require-macros
[schema.core :as sm])
(:require
[clojure.string :as str]
[taoensso.timbre :refer-macros [debug info warn]]
[reagent.core :as r]
[re-frame.core
:refer [reg-sub-raw subscribe reg-event-db dispatch]]
[schema.core :as s]
[er-model.util.set :as setu]
[er-webui.util.dom :as dom]
[er-webui.util.hiccup :as hu]
[er-webui.util.touch :as touch]
[er-webui.util.touch.swipe :as touch-swipe]))
(defn transform-style
[{x :x y :y z :z}]
(let [s (str "translate3d("
(or x 0) "px, "
(or y 0) "px, "
(or z 0) "px)")]
{:-webkit-transform s
:transform s}))
(defn reveal-style
[swipe-reveals reveal]
(case reveal
:left (transform-style {:x (- (touch-swipe/direction-value swipe-reveals reveal))})
:right (transform-style {:x (touch-swipe/direction-value swipe-reveals reveal)})
:up (transform-style {:y (- (touch-swipe/direction-value swipe-reveals reveal))})
:down (transform-style {:y (touch-swipe/direction-value swipe-reveals reveal)})))
(defn reveal->transform
[swipe-reveals reveal]
(case reveal
:left {:x (- (touch-swipe/direction-value swipe-reveals reveal))}
:right {:x (touch-swipe/direction-value swipe-reveals reveal)}
:up {:y (- (touch-swipe/direction-value swipe-reveals reveal))}
:down {:y (touch-swipe/direction-value swipe-reveals reveal)}))
(defn touch-move->transform
([touch-move]
(touch-move->transform nil touch-move))
([{start-x :x
start-y :y
:or {start-x 0 start-y 0}
:as start-transform}
touch-move]
(->> (for [dir touch-swipe/swipe-directions-set]
(when-let [d (get touch-move dir)]
(when (>= d 0) ;; only take the +ve of opposing dirs
(case dir
:left [:x (- start-x d)]
:right [:x (+ start-x d)]
:up [:y (- start-y d)]
:down [:y (+ start-y d)]))))
(filter identity)
(into {}))))
(defn limit-value
[lower upper n]
(->> n
(max (or lower n))
(min (or upper n))))
(defn limit-transform
[{l-left :left
l-right :right
l-up :up
l-down :down
:as limits}
transform]
(let [r (->> (for [[k v] transform]
[k
(case k
:x (limit-value (when l-left (- l-left)) l-right v)
:y (limit-value (when l-up (- l-up)) l-down v)
v)])
(into {}))]
;; (info "limit-transform" limits transform r)
r))
(def initial-transform
{:x 0 :y 0 :z 0})
(def initial-reveal-state
{;; set when a swipe has completed successfully
:reveal nil
;; set when a swipe is in progress
:transform initial-transform
;; set when a swipe has completed or cancelled
;; and final animation is taking place
:animate nil
;; the final touch-move at which a swipe was cancelled
:cancelled-at nil})
(sm/defn ^:always-validate swipe-to-reveal
([opts content revealed-content]
(swipe-to-reveal nil opts content revealed-content))
([el :- (s/maybe s/Keyword)
{swipe-directions :swipe-directions
swipe-thresholds :swipe-thresholds
swipe-reveals :swipe-reveals
swipe-limits :swipe-limits
:as opts} :- {:swipe-directions touch-swipe/SwipeDirections
:swipe-thresholds touch-swipe/SwipeDirectionValues
:swipe-reveals touch-swipe/SwipeDirectionValues
:swipe-limits touch-swipe/SwipeDirectionValues}
content
revealed-content]
(let [dn-a (atom nil)
;; need to re-render when swipe-handlers are set, so r/atom
swipe-handlers-a (r/atom nil)
reveal-state-a (r/atom initial-reveal-state)]
(r/create-class
{:display-name
"swipe-to-reveal"
:component-did-mount
(fn [ref]
(reset! dn-a (r/dom-node ref))
(reset! swipe-handlers-a
(touch/touch-handlers
(touch-swipe/create-touch-swipe-state
@dn-a
{:swipe-directions (touch-swipe/add-opposing-directions
swipe-directions)
:swipe-thresholds swipe-thresholds
:on-start-fn (fn [tm]
(if (:reveal @reveal-state-a)
(swap! reveal-state-a
assoc
:transform
(reveal->transform
swipe-reveals
(:reveal @reveal-state-a)))
(reset! reveal-state-a
initial-reveal-state))
(info "on-start-fn" @reveal-state-a))
:on-move-fn (fn [tm]
(cond
(and
(:reveal @reveal-state-a)
(:transform @reveal-state-a))
(swap! reveal-state-a
assoc
:transform
(limit-transform
swipe-limits
(touch-move->transform
(reveal->transform
swipe-reveals
(:reveal @reveal-state-a))
tm)))
(:transform @reveal-state-a)
(swap! reveal-state-a
assoc
:transform
(limit-transform
swipe-limits
(touch-move->transform
tm)))))
:on-swipe-fn (fn [swipe]
(info "on-swipe-fn" swipe)
(cond
(and
(:reveal @reveal-state-a)
(:transform @reveal-state-a)
(= (touch-swipe/opposing-direction
(:reveal @reveal-state-a))
(-> swipe first first)))
(let [sk (-> swipe first first)]
(reset! reveal-state-a
{:reveal nil
:transform nil
:animate :home
:cancelled-at swipe}))
;; swipe in a permitted direction
(and (:transform @reveal-state-a)
(contains?
(setu/coerce swipe-directions)
(-> swipe first first)))
(let [sk (-> swipe first first)]
(reset! reveal-state-a
{:reveal sk
:transform nil
:animate sk
:cancelled-at nil}))
;; not a permitted direction
(:transform @reveal-state-a)
(reset! reveal-state-a
{:reveal nil
:transform nil
:animate :home
:cancelled-at swipe})))
:on-cancel-fn (fn [tm]
(cond
(:reveal @reveal-state-a)
(reset! reveal-state-a
{:reveal (:reveal @reveal-state-a)
:transform nil
:animate (:reveal @reveal-state-a)
:cancelled-at tm})
:else
(reset! reveal-state-a
{:reveal nil
:transform nil
:animate :home
:cancelled-at tm})))}))))
:component-will-unmount
(fn [_]
(reset! dn-a nil)
(reset! swipe-handlers-a nil)
(reset! reveal-state-a initial-reveal-state))
:reagent-render
(fn swipe-to-reveal-r
([opts content revealed-content]
(swipe-to-reveal-r nil opts content revealed-content))
([el
{swipe-directions :swipe-directions
swipe-thresholds :swipe-thresholds
swipe-reveals :swipe-reveals
swipe-limits :swipe-limits
:as opts}
content
revealed-content]
(let [opts (dissoc opts
:swipe-directions
:swipe-thresholds
:swipe-reveals
:swipe-limits)
{rs-reveal :reveal
rs-transform :transform
rs-animate :animate
:as reveal-state} @reveal-state-a]
[(or el :div.swipe-to-reveal-container)
(merge opts @swipe-handlers-a)
[:div.swipe-to-reveal-content-wrapper
{
:on-click-capture (fn [e]
(info "on-click-capture" @reveal-state-a)
(cond
(and (:reveal @reveal-state-a)
(:cancelled-at @reveal-state-a))
(do
(.preventDefault e)
(.stopPropagation e)
;; click on the main content when revealed
;; closes the reveal
(when (< (->> @reveal-state-a
:cancelled-at
vals
(map #(js/Math.abs %))
(apply max))
10)
(reset! reveal-state-a
{:reveal nil
:transform nil
:animate :home
:cancelled-at nil})))
(:reveal @reveal-state-a)
(do
(.preventDefault e)
(.stopPropagation e))
;; ignore click and drag which didn't swipe
(and (:cancelled-at @reveal-state-a)
(> (->> @reveal-state-a
:cancelled-at
vals
(map #(js/Math.abs %))
(apply max))
10))
(do
(.preventDefault e)
(.stopPropagation e))))
:style (cond
rs-transform (transform-style rs-transform)
(touch-swipe/swipe-directions-set rs-reveal)
(reveal-style swipe-reveals rs-reveal)
:else (transform-style initial-transform))
;; :class (cond
;; (= :home rs-animate)
;; "animate animate-home"
;; (= :left rs-animate)
;; "animate animate-left"
;; :else nil)
}
content]
(hu/into-or-wrap
[:div.swipe-to-reveal-revealed-content-wrapper]
revealed-content)])))}))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment