Skip to content

Commit

Permalink
Merge pull request #986 from CSCfi/refactor-table
Browse files Browse the repository at this point in the history
Refactor table
  • Loading branch information
Macroz committed Mar 5, 2019
2 parents 54c3f59 + 3abb403 commit 41c9e43
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 100 deletions.
52 changes: 15 additions & 37 deletions src/clj/rems/css/styles.clj
Expand Up @@ -68,31 +68,7 @@
{:height (u/px 150)}]))
(stylesheet/at-media {:min-width (u/px 992)}
(list
[(s/descendant :.rems-table :td:before)
{:display "none"}]
[:.rems-table-search-toggle
{:display "flex !important"
:margin-top (u/px 20)}]
[:.rems-table
[:th
:td
{:display "table-cell"
:vertical-align "top"}]]
[:.rems-table
[:.column-header
{:white-space "nowrap"}]]
[:.rems-table
[:.column-filter
{:position "relative"}
[:input
{:width "100%"}]
[:.reset-button
{:position "absolute"
:right "0px"
:top "50%"
:margin-top "-0.5em"}]]] ; center vertically
[:.language-switcher
{:padding ".5em .5em"}]))
[:.language-switcher {:padding ".5em .5em"}]))
(stylesheet/at-media {:min-width (u/px 480)}
[:.commands {:white-space "nowrap"}])))

Expand Down Expand Up @@ -149,29 +125,29 @@
{:color "#000"}]
[:tr
[(s/& (s/nth-child "2n")) {:background "#fff"}]]]
[:.rems-table-search-toggle ;; TODO: search fields are not visible in mobile mode
{:display "none !important"}]
[:#event-table
{:white-space "pre-wrap"}
[:.date {:min-width "160px"}]]
[:.table-border {:padding 0
:margin "1em 0"
:border (util/get-theme-attribute :table-border "1px solid #ccc")
:border-radius (u/rem 0.4)}]
:border-radius (u/rem 0.4)
:overflow :hidden}]
[:.rems-table {:min-width "100%"
:background-color (util/get-theme-attribute :table-bgcolor :color1)
:box-shadow (util/get-theme-attribute :table-shadow)
:color (util/get-theme-attribute :table-text-color)
:border-radius (u/rem 0.4)
:overflow "hidden"}
:color (util/get-theme-attribute :table-text-color)}
[:.column-header {:white-space "nowrap"}]
[:.column-filter {:position "relative"}
[:input
{:width "100%"}]
[:.reset-button
{:position "absolute"
:right "0px"
:top "50%"
:margin-top "-0.5em"}]]
[:th {:color (util/get-theme-attribute :table-heading-color "#fff")
:background-color (util/get-theme-attribute :table-heading-bgcolor :color3)}]
[:td {:display "block"}
[:&:before {:content "attr(data-th)\":\""
:font-weight "bold"
:margin-right (u/rem 0.5)
:display "inline-block"}]
[:&:last-child:before {:content "attr(data-th)\"\""}]]
[:th
:td
{:text-align "left"
Expand Down Expand Up @@ -304,8 +280,10 @@
:flex-direction :column
:flex-wrap :none
:min-height (u/px 300)
:max-width (u/px 1200)
:flex-grow 1}]
[(s/> :.spaced-sections "*:not(:first-child)") {:margin-top (u/rem 1)}]
[:.btn {:white-space :nowrap}]
[:.btn-primary
[:&:hover
:&:focus
Expand Down
11 changes: 8 additions & 3 deletions src/cljs/rems/application_list.cljs
Expand Up @@ -80,13 +80,18 @@
(component-info component)
(example "empty list"
[component {:visible-columns +default-columns+
:sorting {:sort-column :id, :sort-order :asc}
:sorting {:sort-column :id :sort-order :asc}
:items []}])
(example "applications, default order"
[component {:visible-columns +default-columns+
:sorting {:sort-column :id, :sort-order :asc}
:sorting {:sort-column :id :sort-order :asc}
:items +example-applications+}])
(example "applications, descending date, all columns"
[component {:visible-columns +all-columns+
:sorting{:sort-column :created, :sort-order :desc}
:sorting {:sort-column :created :sort-order :desc}
:items +example-applications+}])
(example "applications, initially sorted by id descending, then resource descending"
[component {:visible-columns +all-columns+
:sorting {:initial-sort [{:sort-column :id :sort-order :desc}
{:sort-column :resource :sort-order :desc}]}
:items +example-applications+}])])
3 changes: 2 additions & 1 deletion src/cljs/rems/catalogue.cljs
Expand Up @@ -90,7 +90,8 @@
:id-function :id
:items (filter :enabled items)
:class "catalogue"}
(select-keys [:sorting :filtering] params))])
(when sorting {:sorting sorting})
(when filtering {:filtering filtering}))])

(defn- format-catalogue-items [app]
(str/join ", " (map :title (:catalogue-items app))))
Expand Down
2 changes: 1 addition & 1 deletion src/cljs/rems/navbar.cljs
Expand Up @@ -50,7 +50,7 @@
[:button.navbar-toggler
{:type "button" :data-toggle "collapse" :data-target "#small-navbar"}
"\u2630"]
[navbar-items :div#big-navbar.collapse.navbar-collapse page-id identity]]
[navbar-items :div#big-navbar.collapse.navbar-collapse.mr-3 page-id identity]]
[:div.navbar [user-widget (:user identity)]]])

(defn navbar-small [page-id user]
Expand Down
3 changes: 2 additions & 1 deletion src/cljs/rems/spa.cljs
Expand Up @@ -236,7 +236,8 @@
[nav/navigation-widget page-id]
[status-modal/status-modal]
[logo]
[:div.container.main-content [content]]
[:div.container-fluid.main-content
[content]]
[footer]]))

(reg-event-fx
Expand Down
141 changes: 84 additions & 57 deletions src/cljs/rems/table.cljs
Expand Up @@ -44,19 +44,24 @@

(defn- flip [order]
(case order
:asc :desc
:desc :asc))
:desc :asc
:desc))

(defn- change-sort-order [old-column old-order new-column]
(if (= old-column new-column)
(flip old-order)
:asc))

(defn- apply-sorting [column-definitions sort-column sort-order items]
(let [sorted (sort-by #(column-sort-value column-definitions sort-column %) items)]
(case sort-order
:asc sorted
:desc (reverse sorted))))
(sort-by #(column-sort-value column-definitions sort-column %)
(case sort-order
:desc #(compare %2 %1)
#(compare %1 %2))
items))

(defn- apply-initial-sorting [column-definitions initial-sort items]
(reduce (fn [items {:keys [sort-column sort-order]}]
(apply-sorting column-definitions sort-column sort-order items)) items initial-sort))

(defn matches-filter [column-definitions col filter-value item]
(let [actual-value (str (column-filter-value column-definitions col item))]
Expand Down Expand Up @@ -84,7 +89,9 @@
(s/optional-key :filter-value) Applicable
(s/optional-key :class) (s/cond-pre s/Str Applicable)}}
:visible-columns [s/Keyword]
(s/optional-key :sorting) {(s/optional-key :sort-column) s/Keyword
(s/optional-key :sorting) {(s/optional-key :initial-sort) [{:sort-column s/Keyword
(s/optional-key :sort-order) (s/enum :asc :desc)}]
(s/optional-key :sort-column) s/Keyword
(s/optional-key :sort-order) (s/enum :asc :desc)
(s/optional-key :set-sorting) Applicable}
(s/optional-key :filtering) {(s/optional-key :filters) {s/Keyword s/Str}
Expand All @@ -94,6 +101,69 @@
:items [s/Any]
(s/optional-key :class) s/Str})

(defn- filter-toggle [{:keys [show-filters set-filtering] :as filtering}]
(when filtering
[:div.rems-table-search-toggle.d-flex.flex-row-reverse
[:button.btn
{:class (if show-filters "btn-secondary" "btn-primary")
:on-click #(set-filtering (update filtering :show-filters not))}
(search-symbol)]]))

(defn- column-header-view [column column-definitions sorting]
(let [{:keys [sort-column sort-order set-sorting]} sorting
sortable? (get-in column-definitions [column :sortable?] true)]
[:th
[:div.column-header
(when (and sortable? set-sorting)
{:on-click (fn []
(set-sorting (-> sorting
(assoc :sort-column column)
(assoc :sort-order (change-sort-order sort-column sort-order column)))))})
(column-header column-definitions column)
" "
(when (= column sort-column)
(sort-symbol sort-order))]]))

(defn- column-filter-view [column column-definitions filtering]
(let [{:keys [show-filters filters set-filtering]} filtering]
[:th
(when (get-in column-definitions [column :filterable?] true)
[:div.column-filter
[:input
{:type "text"
:name (str (name column) "-search")
:value (str (column filters))
:placeholder ""
:on-input (fn [event]
(set-filtering
(assoc-in filtering [:filters column] (-> event .-target .-value))))}]
(when (not= "" (get filters column ""))
[:div.reset-button.icon-link.fa.fa-backspace
{:on-click (fn []
(set-filtering
(assoc-in filtering [:filters column] "")))
:aria-hidden true}])])]))

(defn- head [{:keys [column-definitions visible-columns sorting filtering id-function items class] :as params}]
(let [{:keys [show-filters]} filtering]
[:thead
(into [:tr]
(for [column visible-columns]
[column-header-view column column-definitions sorting]))
(when show-filters
(into [:tr]
(for [column visible-columns]
[column-filter-view column column-definitions filtering])))]))

(defn- body [{:keys [column-definitions visible-columns sorting filtering id-function items class] :as params}]
(let [{:keys [initial-sort sort-column sort-order set-sorting]} sorting
{:keys [show-filters filters set-filtering]} filtering]
[:tbody
(map (fn [item] ^{:key (id-function item)} [row column-definitions visible-columns item])
(cond->> items
(and initial-sort (not sort-column)) (apply-initial-sorting column-definitions initial-sort)
(and filtering filters) (apply-filtering column-definitions filters)
(and sorting sort-column) (apply-sorting column-definitions sort-column sort-order)))]))

(defn component
"Table component.
Expand All @@ -112,6 +182,7 @@
`:visible-columns` - a sequence of keys that occur in column-definitions
`:sorting` - sorting options map with keys
`:initial-sort` - seq of {`:sort-column` :xxx} and optionally `:sort-order` to initially sort by
`:sort-column` - the column to sort by
`:sort-order` - direction of sort (`:asc` or `:desc`)
`:set-sorting` - callback that is called when sorting changes
Expand All @@ -128,53 +199,9 @@
See also `TableParams`."
[{:keys [column-definitions visible-columns sorting filtering id-function items class] :as params}]
{:pre [(s/validate TableParams params)]}
(let [{:keys [sort-column sort-order set-sorting]} sorting
{:keys [show-filters filters set-filtering]} filtering]
[:div
(when filtering
[:div.rems-table-search-toggle.d-flex.flex-row-reverse
[:button.btn
{:class (if show-filters "btn-secondary" "btn-primary")
:on-click #(set-filtering (update filtering :show-filters not))}
(search-symbol)]])
[:div.table-border
[:table.rems-table (when class {:class class})
[:thead
(into [:tr]
(for [column visible-columns
:let [sortable? (get-in column-definitions [column :sortable?] true)]]
[:th
[:div.column-header
(when (and sortable? set-sorting)
{:on-click (fn []
(set-sorting (-> sorting
(assoc :sort-column column)
(assoc :sort-order (change-sort-order sort-column sort-order column)))))})
(column-header column-definitions column)
" "
(when (= column sort-column)
(sort-symbol sort-order))]]))
(when show-filters
(into [:tr]
(for [column visible-columns]
[:th
(when (get-in column-definitions [column :filterable?] true)
[:div.column-filter
[:input
{:type "text"
:name (str (name column) "-search")
:value (str (column filters))
:placeholder ""
:on-input (fn [event]
(set-filtering
(assoc-in filtering [:filters column] (-> event .-target .-value))))}]
(when (not= "" (get filters column ""))
[:div.reset-button.icon-link.fa.fa-backspace
{:on-click (fn [] (set-filtering
(assoc-in filtering [:filters column] "")))
:aria-hidden true}])])])))]
(into [:tbody]
(map (fn [item] ^{:key (id-function item)} [row column-definitions visible-columns item])
(cond->> items
(and filtering filters) (apply-filtering column-definitions filters)
(and sorting sort-column) (apply-sorting column-definitions sort-column sort-order))))]]]))
[:div
[filter-toggle filtering]
[:div.table-border
[:table.rems-table (when class {:class class})
[head params]
[body params]]]])

0 comments on commit 41c9e43

Please sign in to comment.