Skip to content

Instantly share code, notes, and snippets.

@smitch88
Last active February 26, 2017 18:43
Show Gist options
  • Save smitch88/b6b0c19620c3e8d7e770 to your computer and use it in GitHub Desktop.
Save smitch88/b6b0c19620c3e8d7e770 to your computer and use it in GitHub Desktop.
Generic Table Component
;; Removing the table component relying on a data fn
(def ext-data
[{:a "1" :b "2" :c "3" :d "4"}
{:a "5" :b "6" :c "7" :d "8"}
{:a "9" :b "10" :c "11" :d "12"}])
(def table-options
{:id "some-table-id"
:fixed-headers true
:filterable false
:sortable true
:pagination true
:checkable true
:caption false
:footer false
:row-handlers {}
:column-defs [{:field :a
;; indicates header or this can be in a header-defs if we are so inclined
:display-name "Field A"
:cell-transform (fn [v]
[:div.custom-cell-markup
[:strong v]])
:style {:min-width 100
:background-color "#eee"
:font-weight 500}}
{:field :b
:display-name "Field B"}
{:field :c
:display-name "Field C"}
{:field :d
:display-name "Field D"}]})
;; table render fn in some widget
(defn render-fn
[]
(let [table-comp (Table table-opts)]
(fn []
(let [data ext-data]
[:div.table-container
[table-comp data]]))))
;; table component internals
;; default table styles
(defonce table-styles
{:head {:container {}
:row {}
:cell {}}
:body {:container {}
:row {}
:cell {}}
:footer {:container {}
:row {}
:cell {}}})
;; data driven fns
(defn filter-data)
(defn sort-data)
(defn paginate-data)
;; markup fns
(defn make-filterable
[v]
[:div.filterable
v
;; add filter stuff below the value
[:div.filter-container
[:input {:type "text"}]]])
(defn make-sortable
[v]
[:div.sortable-link
;; add sort stuff
[:a {:on-click do-sort!} v]])
(defn wrap-header-cell
[v sortable filterable]
(if-not (or sortable filterable)
v
(do
(let [v (if sortable
(make-sortable v)]
(when filterable
[make-filterable v]))))))
(defn table-header
[{:keys [column-defs fixed-headers sortable filterable checkable] :as props}]
(let [headers-values (map-indexed :display-name column-defs)]
[:div.table-header-container {:class (if fixed-headers
"fixed-header" "non-fixed-header")
:style (get-in table-styles [:header :container])}
[:div.table-header-row {:style (get-in table-styles [:header :row])}
(for [[value idx] headers-values
;; this is what could potentially allow for dynamic widths
:let [cell-style (merge (get-in table-styles [:header :cell])
(get-in column-defs [idx :style]))]
wrapped-value (wrap-header-cell sortable filterable value)]
[:div.table-header-cell {:style cell-style}
wrapped-value])]]))
(defn table-footer)
(defn table-body)
(defn table-row)
(defn table-cell)
(defn generate-table-state
"Dynamically creates an initial local state based on the props provided"
[{:keys [column-defs sort-init sortable filterable checkable pagination] :as props}]
(reagent/atom (cond-> {}
sortable (assoc :sort-obj {:dir "asc"
:field (or sort-init (:field (first column-defs)))})
filterable (assoc :filter-obj {:value nil})
checkable (assoc :checkable-obj {:toggle-all false
:toggled #{}})
pagination (assoc :pagination-obj {:current-page 1}))))
(defn Table
[table-opts]
;; note that the sort/filter/page
(let [table-state (generate-table-state table-opts)]
(fn [data]
;; code up dynamic nature based on table-opts and the data passed in from the caller
(let [{:keys [sort-obj filter-obj checkable-obj pagination-obj]} @table-state
data (if sort-obj
(sort-by (:field sort-obj) (case (:dir sort-obj)
"asc" >
"desc" <))
data)
data (if filter-obj
(filter (fn [v]
;;do filter stuff
) data)
data)]
[:div.table {:id (:id table-opts)}
[table-header table-opts]
[table-body
(for [row data]
;; table row is just a wrapping div that ends up displaying the cells
^{:key row}
[table-row
(for [cell row]
^{:key cell}
[table-cell cell])]]
(when (:pagination table-opts)
[table-pagination pagination-obj data])
(when (:footer table-opts)
[table-footer data])]))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment