-
-
Save onetom/26b55b0f01e592658c1f0ff876af2880 to your computer and use it in GitHub Desktop.
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 ui.paging | |
(:require [hoplon.core :refer (def-values)])) | |
(defmacro defp | |
[name & args] | |
`(def ~name (paginate ~@args))) |
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 ui.paging | |
(:require [ui.util :as util] | |
[clojure.walk :as walk])) | |
(defn deref-vals [x] | |
(walk/postwalk #(try @% (catch js/Error e %)) x)) | |
(defn window | |
[n current-page pages] | |
(if (> n pages) ; the window is bigger than the total number of pages; return everything | |
(range 1 (inc pages)) | |
(let [start (- current-page (.ceil js/Math (/ n 2))) | |
end (+ start n) | |
r (range start end)] | |
(cond | |
; shift right until it starts at 1 | |
(<= start 0) (range 1 (inc n)) | |
; shift left until end = pages | |
(> end pages) (range (inc (- pages n)) (inc pages)) | |
:else r)))) | |
(defn refresh! | |
[{:keys [params]}] | |
(swap! params update :refresh inc)) | |
(defn clear! | |
[{:keys [data] :as this}] | |
(reset! data nil) | |
this) | |
(defn activate! | |
[{:keys [params] :as this}] | |
(swap! params assoc :active true)) | |
(defn deactivate! | |
[{:keys [params] :as this}] | |
(clear! this) | |
(swap! params assoc :active false)) | |
(defn params->page-opts | |
[{:keys [page-size current-page] :as params}] | |
(merge (select-keys params [:filters :sorting]) | |
{:paging {:size page-size | |
:from (* (dec current-page) page-size)}})) | |
(defn page-opts | |
[{:keys [params]}] | |
(params->page-opts @params)) | |
(defn fetch! | |
[remote data fetch-state params] | |
(reset! fetch-state :fetching) | |
(doto (remote (params->page-opts params)) | |
(.done #(do (reset! data %) | |
(reset! fetch-state :idle))))) | |
(defn set-page-size! | |
[{:keys [current-page page-size] :as this} new-size] | |
(reset! current-page 1) | |
(reset! page-size new-size) | |
this) | |
(defn next! | |
[{:keys [current-page] :as this}] | |
(swap! current-page inc) | |
this) | |
(defn prev! | |
[{:keys [current-page] :as this}] | |
(swap! current-page dec) | |
this) | |
(defn set-page! | |
[{:keys [current-page] :as this} n] | |
(reset! current-page n) | |
this) | |
(defn filter-equals! | |
[{:keys [filters] :as this} & kv-pairs] | |
(->> kv-pairs | |
(partition-all 2) | |
(reduce (fn [r [k v]] (assoc r k {:type := :val v})) | |
{}) | |
(swap! filters merge)) | |
this) | |
(defn filter-equals? | |
[{:keys [filters]} k v] | |
(= (k @filters) | |
{:type := :val v})) | |
(defn set-filters! | |
[{:keys [filters] :as this} filter-spec] | |
(reset! filters filter-spec) | |
this) | |
(defn set-sorting! | |
[{:keys [sorting] :as this} sorting-spec] | |
(reset! sorting sorting-spec) | |
this) | |
(defn sort! | |
[{:keys [sorting] :as this} column] | |
(let [{:keys [by order]} @sorting] | |
(reset! sorting | |
{:by column | |
:order (if (not= by column) | |
:asc | |
(get {:asc :desc :desc :asc} order))}) | |
this)) | |
(defn watch-map | |
"When cell `c` changes, update the filter of pager `this` according to `mapping`. | |
Example: (watch-map rpc/creatives {:flightid :id} flight) | |
When flight changes, rpc/creatives will look something like | |
{:filters {:flightid {:type := :val 123}}} | |
where `123` is (:id flight)" | |
[this mapping c] | |
(let [source-keys (vals mapping) | |
watch-map (cell= (and c (select-keys c source-keys)))] | |
(cell= (when watch-map | |
(clear! this) | |
(apply filter-equals! | |
this | |
(mapcat (fn [[filter-attr cell-attr]] | |
[filter-attr (get watch-map cell-attr)]) | |
mapping)) | |
(activate! this))))) | |
(defn watch-val | |
[this k c] | |
(util/on-change c (fn [v] (when (and v (not (filter-equals? this k v))) | |
(clear! this) | |
(filter-equals! this k v))))) | |
(defn paginate | |
[remote & {:keys [filters sorting page-size]}] | |
(let [fetch-state (cell :idle) | |
params (cell {:page-size (or page-size 25) | |
:current-page 1 | |
:filters (or filters {}) | |
:sorting (or sorting {:by :id :order :desc}) | |
:refresh 0 | |
:active false}) | |
current-page (util/path-cell params [:current-page]) | |
page-size (util/path-cell params [:page-size]) | |
things (cell nil) | |
rows (cell= (map :__row__ things)) | |
total-rows (cell= (:__rows__ (first things))) | |
pages (cell= (.ceil js/Math (/ total-rows page-size))) | |
page-window (cell= (window 10 current-page pages)) | |
at-beginning (cell= (or (zero? pages) (= current-page (first page-window)))) | |
at-end (cell= (or (zero? pages) (= current-page (last page-window))))] | |
(util/on-change params | |
(fn [{:keys [active] :as new-params}] | |
(when active (fetch! remote things fetch-state new-params)))) | |
{:data things | |
:remote remote | |
:fetching (cell= (= :fetching fetch-state)) | |
:params params | |
:filters (util/path-cell params [:filters]) | |
:sorting (util/path-cell params [:sorting]) | |
:current-page current-page | |
:page-size page-size | |
:rows rows | |
:total-rows total-rows | |
:pages pages | |
:page-window page-window | |
:at-beginning at-beginning | |
:at-end at-end})) | |
(defn paginate-with-localstorage | |
[remote & {:keys [storage params]}] | |
(let [fetch-state (cell :idle) | |
params (cell (merge params (select-keys @storage [:page-size]))) | |
current-page (util/path-cell params [:current-page]) | |
page-size (util/path-cell params [:page-size]) | |
things (cell nil) | |
rows (cell= (map :__row__ things)) | |
total-rows (cell= (:__rows__ (first things))) | |
pages (cell= (.ceil js/Math (/ total-rows page-size))) | |
page-window (cell= (window 10 current-page pages)) | |
at-beginning (cell= (or (zero? pages) (= current-page (first page-window)))) | |
at-end (cell= (or (zero? pages) (= current-page (last page-window))))] | |
(util/on-change params | |
(fn [{:keys [active] :as new-params}] | |
(when active (fetch! remote things fetch-state new-params)) | |
(reset! storage (select-keys new-params [:page-size])))) | |
{:data things | |
:remote remote | |
:fetching (cell= (= :fetching fetch-state)) | |
:params params | |
:filters (util/path-cell params [:filters]) | |
:sorting (util/path-cell params [:sorting]) | |
:current-page current-page | |
:page-size page-size | |
:rows rows | |
:total-rows total-rows | |
:pages pages | |
:page-window page-window | |
:at-beginning at-beginning | |
:at-end at-end})) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment