Files
integreat/src/cljs/auto_ap/views/components/grid.cljs
2020-08-17 19:43:31 -07:00

258 lines
11 KiB
Clojure

(ns auto-ap.views.components.grid
(:require [reagent.core :as r]
[auto-ap.views.utils :refer [appearing]]
[react :as react]
[re-frame.core :as re-frame]
[auto-ap.views.pages.data-page :as data-page]))
(defonce grid-context (react/createContext "default"))
(def Provider (.-Provider grid-context))
(def Consumer (.-Consumer grid-context))
(defn toggle-sort-by [params sort-key sort-name]
(let [[found? sort] (reduce
(fn [[found? sort] sort-item]
(if (= sort-key (:sort-key sort-item))
[true (conj sort
(update sort-item :asc not))]
[found? (conj sort sort-item)]))
[false []]
(:sort params))
sort (if found?
sort
(conj sort {:sort-key sort-key
:sort-name sort-name
:asc true}))]
(-> params
(assoc :sort sort))))
(defn sort-icon [which sort]
(let [sort-item (first (filter #(= which (:sort-key %)) sort))]
(cond
(and sort-item (:asc sort-item))
[:span.icon
[:i.fa.fa-sort-up]]
(and sort-item (not (:asc sort-item)))
[:span.icon
[:i.fa.fa-sort-down]]
:else
[:span.icon
[:i.fa.fa-sort]])))
(defn bound [x y z]
(cond
(< z x)
x
(< y x)
x
(> y z)
z
:else
y))
(defn paginator [{:keys [start end count total on-change]}]
(let [per-page 100
max-buttons 5
buttons-before (Math/floor (/ max-buttons 2))
total-pages (Math/ceil (/ total per-page))
current-page (Math/floor (/ start per-page))
first-page-button (bound 0 (- current-page buttons-before) (- total-pages max-buttons))
all-buttons (into [] (for [x (range total-pages)]
^{:key x}
[:li
[:a.pagination-link {:class (when (= current-page x)
"is-current")
:on-click (fn [e] (on-change {:start (* x per-page)}))}
(inc x)]]))
last-page-button (Math/min total-pages (+ max-buttons first-page-button))
extended-last-page-button (when (not= last-page-button total-pages)
(list
^ {:key -1} [:li [:span.pagination-ellipsis "…"]]
^ {:key -2} (last all-buttons)))
extended-first-page-button (when (not= first-page-button 0)
(list
^{:key -1} (first all-buttons)
^{:key -2} [:li [:span.pagination-ellipsis "…"]]))]
[:nav.pagination {:role "pagination"}
[:ul.pagination-list
extended-first-page-button
(apply list (subvec all-buttons first-page-button last-page-button))
extended-last-page-button
"Showing " (Math/min (inc start) total) "-" end "/" total]]))
(defn sort-by-list [{:keys [sort on-change]}]
[:div.field.is-grouped.is-grouped-multiline
(for [{:keys [sort-key sort-name asc]} sort]
^{:key sort-key}
[:div.control
[:div.tags.has-addons
[:div.tag.is-medium [:span.icon (if asc
[:i.fa.fa-sort-up]
[:i.fa.fa-sort-down])]
[:span sort-name] ]
[:a.tag.is-medium.is-delete {:on-click (fn []
(on-change {:sort (filter #(not= sort-key (:sort-key %)) sort)}))}]]])])
(defn controls [{:keys [start end count total] :as para}]
(let [children (r/children (r/current-component))]
[:> Consumer {}
(fn [consume]
(let [{:strs [on-params-change params] :as consume} (js->clj consume)]
(println "PARAMS" params)
(r/as-element (into
[:div {:style {:margin-bottom "1rem"}}
[:div.level
(into
[:div.level-left
[:div.level-item
[paginator {:start start :end end :count count :total total
:on-change on-params-change}]]
[:div.level-item
[sort-by-list {:sort (:sort params)
:on-change on-params-change}]]]
(mapv (fn [c]
[:div.level-item c]) children))]]))))]))
(defn table [{:keys [fullwidth]}]
(into
[:table.table.compact.grid {:class (if fullwidth
["is-fullwidth"])}]
(r/children (r/current-component))))
(defn header []
(into
[:thead]
(r/children (r/current-component))))
(defn header-cell [{:keys [style class]}]
(into [:th {:style style
:class class}]
(r/children (r/current-component))))
(defn row [{:keys [class id checkable?]}]
(let [children (r/children (r/current-component))]
[:> Consumer {}
(fn [consume]
(let [{:strs [on-params-change params check-boxes? on-check-changed checked] :as consume} (js->clj consume)]
(apply r/create-element "tr" #js {:className class}
(when check-boxes?
(r/as-element [:th {:style {:width "35px"}}
[:input.checkbox (cond-> {:type "checkbox"
:checked (if (get checked id)
"checked"
"")
:on-change (fn [x e]
(let [checked (or checked #{})]
(if (get checked id)
(on-check-changed (disj checked id))
(on-check-changed (conj checked id)))))}
(boolean? checkable?) (assoc :disabled (not checkable?))) ]]))
(map r/as-element children))))]))
(defn button-cell [params]
(apply r/create-element "td" #js {"style" #js {"overflow" "visible"}}
(map r/as-element (r/children (r/current-component)))))
(defn cell [params]
(apply r/create-element "td" #js {}
(map r/as-element (r/children (r/current-component))))
)
(defn body []
(let [children (r/children (r/current-component))]
[:> Consumer {}
(fn [consume]
(let [{:strs [column-count status check-boxes?]} (js->clj consume)
column-count (cond-> column-count
check-boxes? inc)]
(r/as-element
(cond (= :loading (:state status))
^{:key "loading-body"}
[:tbody.test
(for [i (range 20)]
^{:key i}
[:tr
(for [x (range column-count)]
^{:key x}
[:td #_{:col-span column-count}
[:div
[:div.ph-item
[:div.ph-row
[:div.ph-col-12.big]]]]])])]
(= :error (:state status))
^{:key "error-body"}
[:tbody [:tr [:td.has-text-centered {:col-span column-count}
"An unexpected error has occured. "
(-> status :error first :message)
" Please try refreshing the page."]]]
:else
(into ^{:key "main-body"}
[:tbody]
children)))))]))
(defn sortable-header-cell [{:keys [style class sort-key sort-name asc]}]
(let [children (r/children (r/current-component))]
[:> Consumer {}
(fn [consume]
(let [{:strs [on-params-change params] :as consume} (js->clj consume)]
(r/as-element (conj (into
[:th {:on-click (fn [e]
(on-params-change
(toggle-sort-by {:sort (:sort params)} sort-key sort-name)))
:style (assoc style
:cursor "pointer")
:class class}]
children)
(sort-icon sort-key (:sort params))))))]))
(defn grid [{:keys [on-params-change on-check-changed checked params status column-count check-boxes? data-page]}]
(if data-page
(let [page @(re-frame/subscribe [::data-page/page data-page])]
(r/create-element Provider
#js {:value #js {:on-params-change (fn [p]
(re-frame/dispatch [::data-page/table-params-changed data-page p]))
:on-check-changed (fn [new]
(re-frame/dispatch [::data-page/toggle-check data-page new]))
:check-boxes? check-boxes?
:checked (:checked page)
:params (:params page)
:status (:status page)
:column-count column-count}}
(r/as-element
(into
[:<> ]
(r/children (r/current-component))))))
(r/create-element Provider
#js {:value #js {:on-params-change on-params-change
:on-check-changed on-check-changed
:check-boxes? check-boxes?
:checked checked
:params params
:status status
:column-count column-count}}
(r/as-element
(into
[:<> ]
(r/children (r/current-component)))))))
(defn virtual-paginate [start xs ]
(take 100 (drop (or start 0) xs)))
(defn virtual-paginate-controls [start xs]
{:start (or start 0) :end (min (+ (or start 0) 100)
(count xs))
:total (count xs)})