diff --git a/resources/public/styles/partials/dashboard-bar.scss b/resources/public/styles/partials/dashboard-bar.scss index 19b89c59ae..a632e319d0 100644 --- a/resources/public/styles/partials/dashboard-bar.scss +++ b/resources/public/styles/partials/dashboard-bar.scss @@ -28,22 +28,44 @@ } .dashboard-search { - cursor: pointer; - margin-left: $big; + align-items: center; + display: flex; + margin-left: $small; - svg { - fill: $color-gray-dark; - height: 20px; - width: 20px; + .input-text { + background: rgba(255,255,255,.4); + border: 0; + color: $dark-ui-text; + padding: 4px 8px; + margin: 0; + max-width: 160px; + } + + .clear-search { + align-items: center; + background: rgba(255,255,255,.4); + cursor: pointer; + display: flex; + height: 28px; + padding: 0 5px; + + svg { + fill: $light-ui-icons; + height: 15px; + transform: rotate(45deg); + width: 15px; + + &:hover { + fill: $color-danger; + } - &:hover { - fill: $color-gray-darker; } } } + &.library-gap { padding: $small $medium $small 270px; } diff --git a/src/uxbox/data/dashboard.cljs b/src/uxbox/data/dashboard.cljs index f405e678a2..f67f365d21 100644 --- a/src/uxbox/data/dashboard.cljs +++ b/src/uxbox/data/dashboard.cljs @@ -62,6 +62,39 @@ (-pr-writer [mv writer _] (-write writer "#")))) +(defn set-project-ordering + [order] + (reify + rs/UpdateEvent + (-apply-update [_ state] + (assoc-in state [:dashboard :project-order] order)) + + IPrintWithWriter + (-pr-writer [mv writer _] + (-write writer "#")))) + +(defn set-project-filtering + [term] + (reify + rs/UpdateEvent + (-apply-update [_ state] + (assoc-in state [:dashboard :project-filter] term)) + + IPrintWithWriter + (-pr-writer [mv writer _] + (-write writer "#")))) + +(defn clear-project-filtering + [] + (reify + rs/UpdateEvent + (-apply-update [_ state] + (assoc-in state [:dashboard :project-filter] "")) + + IPrintWithWriter + (-pr-writer [mv writer _] + (-write writer "#")))) + (defn set-collection-type [type] {:pre [(contains? #{:builtin :own} type)]} diff --git a/src/uxbox/data/projects.cljs b/src/uxbox/data/projects.cljs index 5c8c4e3fa9..0744bb7648 100644 --- a/src/uxbox/data/projects.cljs +++ b/src/uxbox/data/projects.cljs @@ -1,5 +1,6 @@ (ns uxbox.data.projects (:require [bouncer.validators :as v] + [cuerdas.core :as str] [uxbox.rstore :as rs] [uxbox.router :as r] [uxbox.state :as st] @@ -57,6 +58,23 @@ (sort-by :created) (into []))) +(defn sort-projects-by + [ordering projs] + (case ordering + :name (sort-by :name projs) + :created (reverse (sort-by :created projs)) + projs)) + +(defn contains-term? + [phrase term] + (str/contains? (str/lower phrase) (str/trim (str/lower term)))) + +(defn filter-projects-by + [term projs] + (if (str/blank? term) + projs + (filter #(contains-term? (:name %) term) projs))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Events ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/src/uxbox/locales.cljs b/src/uxbox/locales.cljs index 4c3d05bb0d..f6bcc86075 100644 --- a/src/uxbox/locales.cljs +++ b/src/uxbox/locales.cljs @@ -34,7 +34,14 @@ (let [value (get-in +locales+ [+locale+ t] (name t)) plural (first (filter c? args)) args (mapv #(if (c? %) @% %) args) - value (if vector? + value (cond + (and (vector? value) + (= 3 (count value))) + (nth value (min 2 @plural)) + + (vector? value) (if (= @plural 1) (first value) (second value)) + + :else value)] (apply str/format value args)))) diff --git a/src/uxbox/locales/en.cljs b/src/uxbox/locales/en.cljs index 63fe70cbd4..aa4e3c6da2 100644 --- a/src/uxbox/locales/en.cljs +++ b/src/uxbox/locales/en.cljs @@ -2,6 +2,15 @@ (defonce +locales+ {"ds.projects" "PROJECTS" + "ds.num-projects" ["No projects" + "%s project" + "%s projects"] + "ds.project-ordering" "Sort by" + "ds.project-ordering.by-name" "name" + "ds.project-ordering.by-last-update" "last update" + "ds.project-ordering.by-creation-date" "creation date" + "ds.project-search.placeholder" "Search..." + "ds.elements" "ELEMENTS" "ds.icons" "ICONS" "ds.colors" "COLORS" diff --git a/src/uxbox/state.cljs b/src/uxbox/state.cljs index fcc916dca8..2d0b290fec 100644 --- a/src/uxbox/state.cljs +++ b/src/uxbox/state.cljs @@ -7,7 +7,8 @@ (defonce stream (rs/init {:user {:fullname "Cirilla Fiona" :avatar "http://lorempixel.com/50/50/"} - :dashboard {} + :dashboard {:project-order :name + :project-filter ""} :workspace {} :shapes-by-id {} :elements-by-id {} diff --git a/src/uxbox/ui/dashboard/projects.cljs b/src/uxbox/ui/dashboard/projects.cljs index 7d02a31f65..55770c7229 100644 --- a/src/uxbox/ui/dashboard/projects.cljs +++ b/src/uxbox/ui/dashboard/projects.cljs @@ -3,10 +3,12 @@ [rum.core :as rum] [cats.labs.lens :as l] [cuerdas.core :as str] + [uxbox.locales :as t :refer (tr)] [uxbox.router :as r] [uxbox.rstore :as rs] [uxbox.state :as s] [uxbox.time :as time] + [uxbox.data.dashboard :as dd] [uxbox.data.projects :as dp] [uxbox.data.workspace :as dw] [uxbox.ui.icons :as i] @@ -20,9 +22,8 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; FIXME rename -(def +ordering-options+ {:name "name" - :last-updated "date updated" - :created "date created"}) +(def +ordering-options+ {:name "ds.project-ordering.by-name" + :created "ds.project-ordering.by-creation-date"}) (def +layouts+ {:mobile {:name "Mobile" :id "mobile" @@ -149,32 +150,75 @@ ;; Menu ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; (def ^:static menu-l -;; (as-> (l/select-keys [:projects]) $ -;; (l/focus-atom $ s/state))) +(def ^:static menu-l + (as-> (l/select-keys [:projects-by-id]) $ + (l/focus-atom $ s/state))) -(rum/defc project-sort-selector < rum/reactive - [sort-order] - nil) - ;; (let [sort-name (get project-orderings (rum/react sort-order))] - ;; [:select.input-select - ;; {:on-change #(reset! sort-order (name->order (.-value (.-target %)))) - ;; :value sort-name} - ;; (for [order (keys project-orderings) - ;; :let [name (get project-orderings order)]] - ;; [:option {:key name} name])])) +(def ^:static project-ordering-l + (as-> (l/in [:dashboard :project-order]) $ + (l/focus-atom $ s/state))) + +(def ^:static project-filtering-l + (as-> (l/in [:dashboard :project-filter]) $ + (l/focus-atom $ s/state))) + +(defn project-sort-render + [] + (let [ordering (rum/react project-ordering-l) + change-order #(rs/emit! (dd/set-project-ordering (keyword (.-value (.-target %)))))] + (html + [:div + [:span (tr "ds.project-ordering")] + [:select.input-select + {:on-change change-order + :value (name ordering)} + (for [option (keys +ordering-options+) + :let [option-id (get +ordering-options+ option) + option-value (name option) + option-text (tr option-id)]] + [:option + {:key option-id + :value option-value} + option-text])]]))) + +(def project-sorting + (mx/component + {:render project-sort-render + :name "project-sort-order" + :mixins [rum/reactive]})) + +(defn project-search-render + [] + (let [change-term #(rs/emit! (dd/set-project-filtering (.-value (.-target %)))) + clear-term #(rs/emit! (dd/clear-project-filtering))] + (html + [:form.dashboard-search + [:input.input-text + {:type "text" + :on-change change-term + :auto-focus true + :placeholder (tr "ds.project-search.placeholder") + :value (rum/react project-filtering-l)}] + [:div.clear-search + {:on-click clear-term} + i/close]]))) + +(def project-search + (mx/component + {:render project-search-render + :name "project-search" + :mixins [rum/reactive]})) (defn menu-render [] - (let [state {:projects []} #_(rum/react menu-l) - pcount (count (:projects state))] + (let [projects (rum/react menu-l) + pcount (count (:projects-by-id projects))] ;; FIXME: redundant project-by-id key (html [:section#dashboard-bar.dashboard-bar [:div.dashboard-info - [:span.dashboard-projects pcount " projects"] - [:span "Sort by"]] - [:div.dashboard-search - i/search]]))) + [:span.dashboard-projects (tr "ds.num-projects" (t/c pcount))] + (project-sorting) + (project-search)]]))) (def menu (mx/component @@ -235,14 +279,17 @@ (letfn [(on-click [e] (dom/prevent-default e) (lightbox/open! :new-project))] - (let [state (rum/react grid-l)] + (let [state (rum/react grid-l) + ordering (rum/react project-ordering-l) + filtering (rum/react project-filtering-l) + projects (dp/filter-projects-by filtering (vals (:projects-by-id state)))] (html [:section.dashboard-grid [:h2 "Your projects"] [:div.dashboard-grid-content [:div.grid-item.add-project {:on-click on-click} [:span "+ New project"]] - (for [item (vals (:projects-by-id state))] + (for [item (dp/sort-projects-by ordering projects)] (rum/with-key (project-item item) (:id item)))]])))) (def grid @@ -250,4 +297,3 @@ {:render grid-render :name "grid" :mixins [rum/reactive]})) -