From 6379315e4b50561c7d23666a9ee244375f18b13c Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Sat, 5 Nov 2016 19:50:58 +0100 Subject: [PATCH] Refactor color collections page. --- src/uxbox/main/data/colors.cljs | 164 +++++---- src/uxbox/main/repo.cljs | 2 +- src/uxbox/main/ui/dashboard/colors.cljs | 337 ++++++++++-------- src/uxbox/main/ui/workspace/colorpalette.cljs | 2 +- 4 files changed, 288 insertions(+), 217 deletions(-) diff --git a/src/uxbox/main/data/colors.cljs b/src/uxbox/main/data/colors.cljs index 9235368e5..c5ef6d0f5 100644 --- a/src/uxbox/main/data/colors.cljs +++ b/src/uxbox/main/data/colors.cljs @@ -32,43 +32,28 @@ ;; --- Initialize (declare fetch-collections) +(declare persist-collections) (declare collections-fetched?) -(declare conditional-fetch) (defrecord Initialize [type id] rs/UpdateEvent (-apply-update [_ state] (let [type (or type :own) - data {:type type :id id :selected #{}}] + data {:type type + :id id + :selected #{}}] (-> state (assoc-in [:dashboard :colors] data) (assoc-in [:dashboard :section] :dashboard/colors)))) rs/WatchEvent (-apply-watch [_ state s] - (rx/of (conditional-fetch)))) + (rx/of (fetch-collections)))) (defn initialize [type id] (Initialize. type id)) -;; --- Conditional fetch of Color Collections - -(defrecord ConditionalFetch [] - rs/WatchEvent - (-apply-watch [_ state s] - (if (nil? (:color-colls-by-id state)) - (rx/merge - (rx/of (fetch-collections)) - (->> (rx/filter collections-fetched? s) - (rx/take 1) - (rx/ignore))) - (rx/empty)))) - -(defn conditional-fetch - [] - (ConditionalFetch.)) - ;; --- Select a Collection (defrecord SelectCollection [type id] @@ -86,85 +71,81 @@ ;; --- Collections Fetched -(defrecord CollectionFetched [items] +(defrecord CollectionsFetched [data] rs/UpdateEvent (-apply-update [_ state] - (reduce assoc-collection state items))) + (let [value (:value data)] + (-> state + (assoc-in [:kvstore :color-collections] data) + (update :color-colls-by-id merge value))))) (defn collections-fetched - [items] - (CollectionFetched. items)) + [data] + {:pre [(map? data)]} + (CollectionsFetched. data)) (defn collections-fetched? [v] - (instance? CollectionFetched v)) + (instance? CollectionsFetched v)) ;; --- Fetch Collections (defrecord FetchCollections [] rs/WatchEvent (-apply-watch [_ state s] - (->> (rp/req :fetch/color-collections) + (->> (rp/req :fetch/kvstore "color-collections") (rx/map :payload) + (rx/map (fn [payload] + (if (nil? payload) + {:key "color-collections" + :value nil} + payload))) (rx/map collections-fetched)))) (defn fetch-collections [] (FetchCollections.)) -;; --- Collection Created - -(defrecord CollectionCreated [item] - rs/UpdateEvent - (-apply-update [_ state] - (-> state - (assoc-collection item) - (assoc-in [:dashboard :colors :id] (:id item)) - (assoc-in [:dashboard :colors :type] :own)))) - -(defn collection-created - [item] - (CollectionCreated. item)) - ;; --- Create Collection (defrecord CreateCollection [] + rs/UpdateEvent + (-apply-update [_ state] + (let [id (uuid/random) + item {:name "Unnamed collection" + :type :own + :id id}] + (-> state + (assoc-in [:color-colls-by-id id] item) + (assoc-in [:dashboard :colors :id] id) + (assoc-in [:dashboard :colors :type] :own)))) + rs/WatchEvent - (-apply-watch [_ state s] - (let [coll {:name "Unnamed collection" - :id (uuid/random)}] - (->> (rp/req :create/color-collection coll) - (rx/map :payload) - (rx/map collection-created))))) + (-apply-watch [_ state stream] + (rx/of (persist-collections)))) (defn create-collection [] (CreateCollection.)) -;; --- Collection Updated +;; --- Persist Collections -(defrecord CollectionUpdated [item] - rs/UpdateEvent - (-apply-update [_ state] - (assoc-collection state item))) - -(defn collection-updated - [item] - (CollectionUpdated. item)) - -;; --- Update Collection - -(defrecord UpdateCollection [id] +(defrecord PersistCollections [] rs/WatchEvent - (-apply-watch [_ state s] - (let [item (get-in state [:color-colls-by-id id])] - (->> (rp/req :update/color-collection item) - (rx/map :payload) - (rx/map collection-updated))))) + (-apply-watch [_ state stream] + (let [builtin? #(= :builtin (:type %)) + xf (remove (comp builtin? second)) -(defn update-collection - [id] - (UpdateCollection. id)) + colls (get state :color-colls-by-id) + data (-> (get-in state [:kvstore :color-collections]) + (assoc :value (into {} xf colls)))] + (->> (rp/req :update/kvstore data) + (rx/map :payload) + (rx/map collections-fetched))))) + +(defn persist-collections + [] + (PersistCollections.)) ;; --- Rename Collection @@ -175,7 +156,7 @@ rs/WatchEvent (-apply-watch [_ state s] - (rx/of (update-collection id)))) + (rx/of (persist-collections)))) (defn rename-collection [item name] @@ -186,12 +167,11 @@ (defrecord DeleteCollection [id] rs/UpdateEvent (-apply-update [_ state] - (dissoc-collection state id)) + (update state :color-colls-by-id dissoc id)) rs/WatchEvent (-apply-watch [_ state s] - (->> (rp/req :delete/color-collection id) - (rx/ignore)))) + (rx/of (persist-collections)))) (defn delete-collection [id] @@ -207,11 +187,12 @@ rs/WatchEvent (-apply-watch [_ state s] - (rx/of (update-collection id)))) + (rx/of (persist-collections)))) (defn replace-color "Add or replace color in a collection." [{:keys [id from to] :as params}] + (println "replace-color" params) (ReplaceColor. id from to)) ;; --- Remove Color @@ -224,7 +205,7 @@ rs/WatchEvent (-apply-watch [_ state s] - (rx/of (update-collection id)))) + (rx/of (persist-collections)))) (defn remove-colors "Remove color in a collection." @@ -257,6 +238,43 @@ {:pre [(color/hex? color)]} (ToggleColorSelection. color)) +;; --- Copy Selected Icon + +(defrecord CopySelected [id] + rs/UpdateEvent + (-apply-update [_ state] + (let [selected (get-in state [:dashboard :colors :selected])] + (update-in state [:color-colls-by-id id :data] set/union selected))) + + rs/WatchEvent + (-apply-watch [_ state stream] + (rx/of (persist-collections)))) + +(defn copy-selected + [id] + {:pre [(or (uuid? id) (nil? id))]} + (CopySelected. id)) + +;; --- Move Selected Icon + +(defrecord MoveSelected [from to] + rs/UpdateEvent + (-apply-update [_ state] + (let [selected (get-in state [:dashboard :colors :selected])] + (-> state + (update-in [:color-colls-by-id from :data] set/difference selected) + (update-in [:color-colls-by-id to :data] set/union selected)))) + + rs/WatchEvent + (-apply-watch [_ state stream] + (rx/of (persist-collections)))) + +(defn move-selected + [from to] + {:pre [(or (uuid? from) (nil? from)) + (or (uuid? to) (nil? to))]} + (MoveSelected. from to)) + ;; --- Delete Selected Colors (defrecord DeleteSelectedColors [] diff --git a/src/uxbox/main/repo.cljs b/src/uxbox/main/repo.cljs index f82852448..062291859 100644 --- a/src/uxbox/main/repo.cljs +++ b/src/uxbox/main/repo.cljs @@ -13,7 +13,7 @@ [uxbox.main.repo.pages] [uxbox.main.repo.images] [uxbox.main.repo.icons] - [uxbox.main.repo.colors] + [uxbox.main.repo.kvstore] [uxbox.main.repo.impl :as impl])) (defn req diff --git a/src/uxbox/main/ui/dashboard/colors.cljs b/src/uxbox/main/ui/dashboard/colors.cljs index 428454ee2..b9fbb4c89 100644 --- a/src/uxbox/main/ui/dashboard/colors.cljs +++ b/src/uxbox/main/ui/dashboard/colors.cljs @@ -32,19 +32,10 @@ (-> (l/in [:dashboard :colors]) (l/derive st/state))) -(def collections-map-ref +(def collections-ref (-> (l/key :color-colls-by-id) (l/derive st/state))) -(def collections-ref - (-> (l/lens vals) - (l/derive collections-map-ref))) - -(defn- focus-collection - [id] - (-> (l/key id) - (l/derive collections-map-ref))) - ;; --- Page Title (mx/defcs page-title @@ -56,7 +47,7 @@ edit? (:edit @local)] (letfn [(save [] (let [dom (mx/ref-node own "input") - name (.-innerText dom)] + name (dom/get-inner-text dom)] (rs/emit! (dc/rename-collection id (str/trim name))) (swap! local assoc :edit false))) (cancel [] @@ -85,7 +76,7 @@ [:span.close {:on-click cancel} i/close]] [:span.dashboard-title-field {:on-double-click edit} - (:name coll)])] + (:name coll "Storage")])] (if (and (not own?) coll) [:div.edition (if edit? @@ -93,8 +84,178 @@ [:span {:on-click edit} i/pencil]) [:span {:on-click delete-collection} i/trash]])]))) +;; --- Nav + +(mx/defc nav-item + {:mixins [mx/static]} + [{:keys [id type name] :as coll} selected?] + (letfn [(on-click [event] + (let [type (or type :own)] + (rs/emit! (dc/select-collection type id))))] + (let [colors (count (:data coll))] + [:li {:on-click on-click + :class-name (when selected? "current")} + [:span.element-title + (if coll name "Storage")] + [:span.element-subtitle + (tr "ds.num-elements" (t/c colors))]]))) + +(def ^:private storage-num-colors-ref + (-> (comp (l/in [:color-colls-by-id nil :data]) + (l/lens count)) + (l/derive st/state))) + +(mx/defc nav-item-storage + {:mixins [mx/static mx/reactive]} + [selected?] + (let [num-colors (mx/react storage-num-colors-ref) + on-click #(rs/emit! (dc/select-collection :own nil))] + [:li {:on-click on-click :class (when selected? "current")} + [:span.element-title "Storage"] + [:span.element-subtitle + (tr "ds.num-elements" (t/c num-colors))]])) + +(mx/defc nav-section + {:mixins [mx/static]} + [type selected colls] + (let [own? (= type :own) + builtin? (= type :builtin) + colls (cond->> (vals colls) + own? (filter #(= :own (:type %))) + builtin? (filter #(= :builtin (:type %))) + own? (sort-by :id))] + [:ul.library-elements + (when own? + [:li + [:a.btn-primary + {:on-click #(rs/emit! (dc/create-collection))} + "+ New library"]]) + (when own? + (nav-item-storage (nil? selected))) + (for [coll colls + :let [selected? (= (:id coll) selected) + key (str (:id coll))]] + (-> (nav-item coll selected?) + (mx/with-key key)))])) + +(mx/defc nav + {:mixins [mx/static mx/reactive]} + [{:keys [id type] :as state} colls] + (let [own? (= type :own) + builtin? (= type :builtin)] + (letfn [(select-tab [type] + (if own? + (rs/emit! (dc/select-collection type)) + (let [coll (->> (map second colls) + (filter #(= type (:type %))) + (sort-by :name) + (first))] + (if coll + (rs/emit! (dc/select-collection type (:id coll))) + (rs/emit! (dc/select-collection type))))))] + [:div.library-bar + [:div.library-bar-inside + [:ul.library-tabs + [:li {:class-name (when own? "current") + :on-click (partial select-tab :own)} + "YOUR COLORS"] + [:li {:class-name (when builtin? "current") + :on-click (partial select-tab :builtin)} + "COLORS STORE"]] + (nav-section type id colls)]]))) + ;; --- Grid +(mx/defc grid-form + [coll-id] + [:div.grid-item.small-item.add-project + {:on-click #(udl/open! :color-form {:coll coll-id})} + [:span "+ New color"]]) + +(mx/defc grid-options-copy + {:mixins [mx/reactive mx/static]} + [current-coll] + {:pre [(uuid? current-coll)]} + (let [colls (mx/react collections-ref) + colls (->> (vals colls) + (filter #(= :own (:type %))) + (remove #(= current-coll (:id %))) + (sort-by :name colls)) + on-select (fn [event id] + (dom/prevent-default event) + (rs/emit! (dc/copy-selected id)))] + [:ul.move-list + [:li.title "Copy to library"] + [:li [:a {:href "#" :on-click #(on-select % nil)} "Storage"]] + (for [coll colls + :let [id (:id coll) + name (:name coll)]] + [:li {:key (str id)} + [:a {:on-click #(on-select % id)} name]])])) + +(mx/defc grid-options-move + {:mixins [mx/reactive mx/static]} + [current-coll] + {:pre [(uuid? current-coll)]} + (let [colls (mx/react collections-ref) + colls (->> (vals colls) + (filter #(= :own (:type %))) + (remove #(= current-coll (:id %))) + (sort-by :name colls)) + on-select (fn [event id] + (dom/prevent-default event) + (rs/emit! (dc/move-selected current-coll id)))] + [:ul.move-list + [:li.title "Move to library"] + [:li [:a {:href "#" :on-click #(on-select % nil)} "Storage"]] + (for [coll colls + :let [id (:id coll) + name (:name coll)]] + [:li {:key (str id)} + [:a {:on-click #(on-select % id)} name]])])) + +(mx/defcs grid-options + {:mixins [mx/static (mx/local)]} + [own {:keys [type id] :as coll}] + (let [editable? (or (= type :own) (nil? id)) + local (:rum/local own)] + (letfn [(delete [event] + (rs/emit! (dc/delete-selected-colors))) + (on-delete [event] + (udl/open! :confirm {:on-accept delete})) + (on-toggle-copy [event] + (swap! local assoc + :show-copy-tooltip not + :show-move-tooltip false)) + (on-toggle-move [event] + (swap! local assoc + :show-move-tooltip not + :show-copy-tooltip false))] + ;; MULTISELECT OPTIONS BAR + [:div.multiselect-bar + (if editable? + [:div.multiselect-nav + [:span.move-item.tooltip.tooltip-top + {:on-click on-toggle-copy} + (when (:show-copy-tooltip @local) + (grid-options-copy id)) + i/organize] + [:span.move-item.tooltip.tooltip-top + {:on-click on-toggle-move} + (when (:show-move-tooltip @local) + (grid-options-move id)) + i/organize] + [:span.delete.tooltip.tooltip-top + {:alt "Delete" + :on-click on-delete} + i/trash]] + [:div.multiselect-nav + [:span.move-item.tooltip.tooltip-top + {:on-click on-toggle-copy} + (when (:show-copy-tooltip @local) + (grid-options-copy id)) + i/organize]])]))) + (mx/defc grid-item [color selected?] (let [color-rgb (hex->rgb color)] @@ -115,139 +276,28 @@ [:span.color-data color] [:span.color-data (apply str "RGB " (interpose ", " color-rgb))]]))) -(mx/defc grid-options - [coll] - (let [own? (= (:type coll) :own)] - (letfn [(on-delete [event] - (rs/emit! (dc/delete-selected-colors)))] - ;; MULTISELECT OPTIONS BAR - [:div.multiselect-bar - (if own? - [:div.multiselect-nav - [:span.move-item.tooltip.tooltip-top - {:alt "Move to"} - i/organize] - [:span.delete.tooltip.tooltip-top - {:alt "Delete" - :on-click on-delete} - i/trash]] - [:div.multiselect-nav - [:span.move-item.tooltip.tooltip-top - {:alt "Copy to"} - [:ul.move-list - [:li.title "Copy to library"] - [:li - [:a {:href "#"} "Red palette"]] - [:li - [:a {:href "#"} "Protoype"]] - [:li - [:a {:href "#"} "Gray scale palette"]] - [:li - [:a {:href "#"} "Current project"]] - [:li - [:a {:href "#"} "Gray scale palette"]] - [:li - [:a {:href "#"} "Current project"]] - [:li - [:a {:href "#"} "Library 3"]]] - i/organize]])]))) - (mx/defc grid {:mixins [mx/static]} - [selected coll] - (let [own? (= (:type coll) :own) - colors (->> (:data coll) - (remove nil?) + [{:keys [id type data] :as coll} selected] + (let [editable? (or (= :own type) (nil? id)) + colors (->> (remove nil? data) (sort-by identity))] [:div.dashboard-grid-content [:div.dashboard-grid-row - (when own? - [:div.grid-item.small-item.add-project - {:on-click #(udl/open! :color-form {:coll coll})} - [:span "+ New color"]]) + (when editable? (grid-form id)) (for [color colors :let [selected? (contains? selected color)]] (-> (grid-item color selected?) (mx/with-key (str color))))]])) (mx/defc content - {:mixins [mx/static mx/reactive]} - [] - (let [dashboard (mx/react dashboard-ref) - selected (:selected dashboard) - coll-type (:type dashboard) - coll-id (:id dashboard) - coll (mx/react (focus-collection coll-id)) - own? (= coll-type :own)] - (when coll - [:section.dashboard-grid.library - (page-title coll) - (grid selected coll) - (when (and (seq selected)) - (grid-options coll))]))) - -;; --- Nav - -(mx/defc nav-item {:mixins [mx/static]} - [coll selected?] - (letfn [(on-click [event] - (let [type (:type coll) - id (:id coll)] - (rs/emit! (dc/select-collection type id))))] - (let [colors (count (:data coll))] - [:li {:on-click on-click - :class-name (when selected? "current")} - [:span.element-title (:name coll)] - [:span.element-subtitle - (tr "ds.num-elements" (t/c colors))]]))) - -(mx/defc nav-section - {:mixins [mx/static mx/reactive]} - [type selected] - (let [own? (= type :own) - builtin? (= type :builtin) - colls (cond->> (mx/react collections-ref) - own? (filter #(= :own (:type %))) - builtin? (filter #(= :builtin (:type %))) - own? (sort-by :id))] - [:ul.library-elements - (when own? - [:li - [:a.btn-primary - {:on-click #(rs/emit! (dc/create-collection))} - "+ New library"]]) - (for [coll colls - :let [selected? (= (:id coll) selected) - key (str (:id coll))]] - (-> (nav-item coll selected?) - (mx/with-key key)))])) - -(mx/defc nav - {:mixins [mx/static mx/reactive]} - [] - (let [dashboard (mx/react dashboard-ref) - colls (mx/react collections-ref) - selected (:id dashboard) - type (:type dashboard) - own? (= type :own) - builtin? (= type :builtin)] - (letfn [(select-tab [type] - (let [xf (filter #(= type (:type %))) - colls (sequence xf colls)] - (if-let [item (first colls)] - (rs/emit! (dc/select-collection type (:id item))) - (rs/emit! (dc/select-collection type)))))] - [:div.library-bar - [:div.library-bar-inside - [:ul.library-tabs - [:li {:class-name (when own? "current") - :on-click (partial select-tab :own)} - "YOUR COLORS"] - [:li {:class-name (when builtin? "current") - :on-click (partial select-tab :builtin)} - "COLORS STORE"]] - (nav-section type selected)]]))) + [{:keys [selected] :as state} coll] + [:section.dashboard-grid.library + (page-title coll) + (grid coll selected) + (when (and (seq selected)) + (grid-options coll))]) ;; --- Colors Page @@ -269,22 +319,25 @@ (mx/defc colors-page {:will-mount colors-page-will-mount :did-remount colors-page-did-remount - :mixins [mx/static]} - [type id] - [:main.dashboard-main - (header) - [:section.dashboard-content - (nav) - (content)]]) + :mixins [mx/static mx/reactive]} + [_ _] + (let [state (mx/react dashboard-ref) + colls (mx/react collections-ref) + coll (get colls (:id state))] + [:main.dashboard-main + (header) + [:section.dashboard-content + (nav state colls) + (content state coll)]])) ;; --- Colors Lightbox (Component) (mx/defcs color-lightbox {:mixins [(mx/local {}) mx/static]} - [own {:keys [coll color]}] + [own {:keys [coll color] :as params}] (let [local (:rum/local own)] (letfn [(on-submit [event] - (let [params {:id (:id coll) + (let [params {:id coll :from color :to (:hex @local)}] (rs/emit! (dc/replace-color params)) diff --git a/src/uxbox/main/ui/workspace/colorpalette.cljs b/src/uxbox/main/ui/workspace/colorpalette.cljs index bb106b7b7..721705fc1 100644 --- a/src/uxbox/main/ui/workspace/colorpalette.cljs +++ b/src/uxbox/main/ui/workspace/colorpalette.cljs @@ -76,7 +76,7 @@ (defn- colorpalette-will-mount [own] - (rs/emit! (dc/conditional-fetch)) + (rs/emit! (dc/fetch-collections)) own) (mx/defc colorpalette