Skip to content

Instantly share code, notes, and snippets.

@DogLooksGood
Last active August 14, 2019 03:28
Show Gist options
  • Save DogLooksGood/9264fb093d744d31cc8bdadd49478b2a to your computer and use it in GitHub Desktop.
Save DogLooksGood/9264fb093d744d31cc8bdadd49478b2a to your computer and use it in GitHub Desktop.
SimpleListView
(ns demo.list-view
"A ListView provides:
1. Refresh control
2. Pagination"
(:require [reagent.core :as reagent]))
(def ^:private default-bounce-height 100)
;;; Event Handlers
(defn- ->touch-y [e]
(some-> e .-touches (aget 0) .-screenY))
(defn- on-touch-move [state* {:keys [bounce-height]} e]
(let [st @state*
dom (:ref st)
refresh-state (:refresh-state st)
scroll-top (.-scrollTop dom)
y (->touch-y e)
touch-start-y (:touch-start-y st)
overflow (- y touch-start-y 30)]
(when (and touch-start-y ; Ensure touching
(zero? scroll-top) ; Ensure at top
(pos? overflow))
(swap! state* assoc
:refresh-state (if (and (= refresh-state :idle)
(>= overflow bounce-height))
:holding
refresh-state)
:scroll-top-overflow overflow))))
(defn- on-touch-start [state* e]
(let [y (->touch-y e)]
(swap! state* assoc
:touch-start-y y
:refresh-state :idle)))
(defn- on-touch-end [state* {:keys [on-refresh]} _e]
(swap! state* assoc
:touch-start-y nil)
(when (and on-refresh (= :holding (:refresh-state @state*)))
(swap! state* assoc
:last-max-scroll-top 0
:refresh-state :release)
(on-refresh)))
(defn- on-scroll [state* {:keys [on-scroll-end]} _e]
(let [st @state*
dom (:ref st)
last-max-scroll-top (:last-max-scroll-top st)
scroll-top (.-scrollTop dom)
offset-height (.-offsetHeight dom)
scroll-height (.-scrollHeight dom)]
(when (and
;; Callback function is present
on-scroll-end
;; We reach the end of the scroll
(>= (+ offset-height scroll-top) scroll-height)
;; Haven't load once at this position
(> scroll-top last-max-scroll-top))
(swap! state* assoc :last-max-scroll-top scroll-top)
(on-scroll-end))))
(defn on-timer [state*]
(let [{:keys [scroll-top-overflow touch-start-y]} @state*]
(when (and (not touch-start-y) ; Ensure not touching
(pos? scroll-top-overflow))
(swap! state* assoc
:scroll-top-overflow (max (- scroll-top-overflow 10) 0)))))
(defn default-refresh-controll
[{:keys [height refresh-state]}]
[:div {:style {:height (str height "px")}}
(case refresh-state
:idle "下拉重新加载"
:holding "放开加载"
:release "正在加载")])
(defn list-view
"List View
class: The class apply on the container.
on-refresh: (callback []) on pull to refresh.
on-scroll-end: (callback []) load next page load at bottom.
render-item: (render [{:keys [idx item]}])
key-fn: (key-fn [item]) returns the react-key for each item.
bounce-height: PX value, the maximum pull length.
"
[{:keys [class datasource on-refresh on-scroll-end render-item key-fn
refresh-controll bounce-height]
:or {refresh-controll default-refresh-controll
bounce-height default-bounce-height}}]
{:pre [(or (keyword? key-fn) (fn? key-fn))
(or (fn? on-refresh) (nil? on-refresh))
(or (fn? on-scroll-end) (nil? on-scroll-end))
(any? datasource)]}
(fn [{:keys [datasource]}]
(reagent/with-let [props {:on-refresh on-refresh
:on-scroll-end on-scroll-end
:bounce-height bounce-height}
state* (reagent/atom {:scroll-top-overflow 0
:refresh-state :idle
:last-max-scroll-top 0
:touch-start-y nil})
timer (js/setInterval (partial on-timer state*)
16)
on-touch-start-fn (partial on-touch-start state*)
on-touch-move-fn (partial on-touch-move state* props)
on-touch-end-fn (partial on-touch-end state* props)
on-scroll-fn (partial on-scroll state* props)]
(let [overflow (:scroll-top-overflow @state*)
refresh-state (:refresh-state @state*)]
[:div {:class class
:on-touch-start on-touch-start-fn
:on-touch-move on-touch-move-fn
:on-touch-end on-touch-end-fn
:on-scroll on-scroll-fn
:ref (fn [c] (when c (swap! state* assoc :ref c)))}
(when-not (and on-refresh (zero? overflow))
[refresh-controll {:height (min overflow bounce-height)
:refresh-state refresh-state}])
(for [[idx item] (map-indexed vector datasource)]
^{:key (key-fn item)}
[render-item idx item])])
(finally (js/clearInterval timer)))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment