Last active
August 14, 2019 03:28
-
-
Save DogLooksGood/9264fb093d744d31cc8bdadd49478b2a to your computer and use it in GitHub Desktop.
SimpleListView
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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