❄️ integration with new items

This commit is contained in:
alonso.torres 2020-03-17 12:22:21 +01:00
parent b2474fc3fb
commit 5b7182fac6
14 changed files with 1549 additions and 1332 deletions

View file

@ -1125,5 +1125,8 @@
"translations" : { "translations" : {
"en" : "Click to close the path" "en" : "Click to close the path"
} }
} },
"modal.create-color.new-color": "New Color",
"modal.create-color.edit-color": "Edit Color"
} }

View file

@ -18,7 +18,7 @@ body {
* { * {
box-sizing: border-box; box-sizing: border-box;
transition: all .4s ease; // transition: all .4s ease;
} }
.global-zeroclipboard-container { .global-zeroclipboard-container {

View file

@ -285,3 +285,53 @@
top: 1.5rem; top: 1.5rem;
} }
} }
.modal-create-color {
position: relative;
background-color: $color-white;
padding: 4rem;
display: flex;
flex-direction: column;
align-items: center;
& .sketch-picker {
box-shadow: none !important;
border: 1px solid $color-gray-lighter !important;
border-radius: 0 !important;
& input {
background-color: $color-white;
}
}
& .close {
position: absolute;
right: 1rem;
transform: rotate(45deg);
top: 1rem;
svg {
fill: $color-black;
height: 20px;
width: 20px;
&:hover {
fill: $color-danger;
}
}
}
& .btn-primary {
width: 10rem;
padding: 0.5rem;
margin-top: 1rem;
}
}
.modal-create-color-title {
color: $color-black;
font-size: 24px;
font-weight: normal;
}

View file

@ -19,230 +19,230 @@
[uxbox.util.time :as dt] [uxbox.util.time :as dt]
[uxbox.util.uuid :as uuid])) [uxbox.util.uuid :as uuid]))
;; TODO: need a good refactor ;; ;; TODO: need a good refactor
;;
;; --- Initialize ;; ;; --- Initialize
;;
(declare fetch-collections) ;; (declare fetch-collections)
(declare persist-collections) ;; (declare persist-collections)
(declare collections-fetched?) ;; (declare collections-fetched?)
;;
;; --- Collections Fetched ;; ;; --- Collections Fetched
;;
(defrecord CollectionsFetched [data] ;; (defrecord CollectionsFetched [data]
ptk/UpdateEvent ;; ptk/UpdateEvent
(update [_ state] ;; (update [_ state]
(let [{:keys [version value]} data] ;; (let [{:keys [version value]} data]
(-> state ;; (-> state
(update :colors-collections merge value) ;; (update :colors-collections merge value)
(assoc ::version version))))) ;; (assoc ::version version)))))
;;
(defn collections-fetched ;; (defn collections-fetched
[data] ;; [data]
{:pre [(map? data)]} ;; {:pre [(map? data)]}
(CollectionsFetched. data)) ;; (CollectionsFetched. data))
;;
(defn collections-fetched? ;; (defn collections-fetched?
[v] ;; [v]
(instance? CollectionsFetched v)) ;; (instance? CollectionsFetched v))
;;
;; --- Fetch Collections ;; ;; --- Fetch Collections
;;
(defrecord FetchCollections [] ;; (defrecord FetchCollections []
ptk/WatchEvent ;; ptk/WatchEvent
(watch [_ state s] ;; (watch [_ state s]
(->> (rp/query! :user-attr {:key "color-collections"}) ;; (->> (rp/query! :user-attr {:key "color-collections"})
(rx/map collections-fetched) ;; (rx/map collections-fetched)
(rx/catch (fn [{:keys [type] :as error}] ;; (rx/catch (fn [{:keys [type] :as error}]
(if (= type :not-found) ;; (if (= type :not-found)
(rx/empty) ;; (rx/empty)
(rx/throw error))))))) ;; (rx/throw error)))))))
;;
(defn fetch-collections ;; (defn fetch-collections
[] ;; []
(FetchCollections.)) ;; (FetchCollections.))
;;
;; --- Create Collection ;; ;; --- Create Collection
;;
(defrecord CreateCollection [id] ;; (defrecord CreateCollection [id]
ptk/UpdateEvent ;; ptk/UpdateEvent
(update [_ state] ;; (update [_ state]
(let [item {:name (tr "ds.default-library-title" (gensym "c")) ;; (let [item {:name (tr "ds.default-library-title" (gensym "c"))
:id id ;; :id id
:created-at (dt/now) ;; :created-at (dt/now)
:type :own ;; :type :own
:colors #{}}] ;; :colors #{}}]
(assoc-in state [:colors-collections id] item))) ;; (assoc-in state [:colors-collections id] item)))
;;
ptk/WatchEvent ;; ptk/WatchEvent
(watch [_ state stream] ;; (watch [_ state stream]
(rx/of (persist-collections) ;; (rx/of (persist-collections)
(rt/nav :dashboard/colors nil {:type :own :id id})))) ;; (rt/nav :dashboard/colors nil {:type :own :id id}))))
;;
(defn create-collection ;; (defn create-collection
[] ;; []
(let [id (uuid/next)] ;; (let [id (uuid/next)]
(CreateCollection. id))) ;; (CreateCollection. id)))
;;
;; --- Persist Collections ;; ;; --- Persist Collections
;;
(defrecord PersistCollections [] ;; (defrecord PersistCollections []
ptk/WatchEvent ;; ptk/WatchEvent
(watch [_ state stream] ;; (watch [_ state stream]
(let [builtin? #(= :builtin (:type %)) ;; (let [builtin? #(= :builtin (:type %))
xform (remove (comp builtin? second)) ;; xform (remove (comp builtin? second))
version (or (get state ::version) -1) ;; version (or (get state ::version) -1)
value (->> (get state :colors-collections) ;; value (->> (get state :colors-collections)
(into {} xform)) ;; (into {} xform))
data {:key "color-collections" ;; data {:key "color-collections"
:val value}] ;; :val value}]
(->> (rp/mutation! :upsert-user-attr data) ;; (->> (rp/mutation! :upsert-user-attr data)
(rx/map collections-fetched))))) ;; (rx/map collections-fetched)))))
;;
(defn persist-collections ;; (defn persist-collections
[] ;; []
(PersistCollections.)) ;; (PersistCollections.))
;;
;; --- Rename Collection ;; ;; --- Rename Collection
;;
(defrecord RenameCollection [id name] ;; (defrecord RenameCollection [id name]
ptk/UpdateEvent ;; ptk/UpdateEvent
(update [_ state] ;; (update [_ state]
(assoc-in state [:colors-collections id :name] name)) ;; (assoc-in state [:colors-collections id :name] name))
;;
ptk/WatchEvent ;; ptk/WatchEvent
(watch [_ state s] ;; (watch [_ state s]
(rx/of (persist-collections)))) ;; (rx/of (persist-collections))))
;;
(defn rename-collection ;; (defn rename-collection
[item name] ;; [item name]
(RenameCollection. item name)) ;; (RenameCollection. item name))
;;
;; --- Delete Collection ;; ;; --- Delete Collection
;;
(defrecord DeleteCollection [id] ;; (defrecord DeleteCollection [id]
ptk/UpdateEvent ;; ptk/UpdateEvent
(update [_ state] ;; (update [_ state]
(update state :colors-collections dissoc id)) ;; (update state :colors-collections dissoc id))
;;
ptk/WatchEvent ;; ptk/WatchEvent
(watch [_ state s] ;; (watch [_ state s]
(rx/of (persist-collections)))) ;; (rx/of (persist-collections))))
;;
(defn delete-collection ;; (defn delete-collection
[id] ;; [id]
(DeleteCollection. id)) ;; (DeleteCollection. id))
;;
;; --- Replace Color ;; ;; --- Replace Color
;;
(defrecord AddColor [coll-id color] ;; (defrecord AddColor [coll-id color]
ptk/UpdateEvent ;; ptk/UpdateEvent
(update [_ state] ;; (update [_ state]
(update-in state [:colors-collections coll-id :colors] set/union #{color})) ;; (update-in state [:colors-collections coll-id :colors] set/union #{color}))
;;
ptk/WatchEvent ;; ptk/WatchEvent
(watch [_ state s] ;; (watch [_ state s]
(rx/of (persist-collections)))) ;; (rx/of (persist-collections))))
;;
(defn add-color ;; (defn add-color
"Add or replace color in a collection." ;; "Add or replace color in a collection."
[coll-id color] ;; [coll-id color]
(AddColor. coll-id color)) ;; (AddColor. coll-id color))
;;
;; --- Remove Color ;; ;; --- Remove Color
;;
(defrecord RemoveColors [id colors] ;; (defrecord RemoveColors [id colors]
ptk/UpdateEvent ;; ptk/UpdateEvent
(update [_ state] ;; (update [_ state]
(update-in state [:colors-collections id :colors] ;; (update-in state [:colors-collections id :colors]
#(set/difference % colors))) ;; #(set/difference % colors)))
;;
ptk/WatchEvent ;; ptk/WatchEvent
(watch [_ state s] ;; (watch [_ state s]
(rx/of (persist-collections)))) ;; (rx/of (persist-collections))))
;;
(defn remove-colors ;; (defn remove-colors
"Remove color in a collection." ;; "Remove color in a collection."
[id colors] ;; [id colors]
(RemoveColors. id colors)) ;; (RemoveColors. id colors))
;;
;; --- Select color ;; ;; --- Select color
;;
(defrecord SelectColor [color] ;; (defrecord SelectColor [color]
ptk/UpdateEvent ;; ptk/UpdateEvent
(update [_ state] ;; (update [_ state]
(update-in state [:dashboard :colors :selected] conj color))) ;; (update-in state [:dashboard :colors :selected] conj color)))
;;
(defrecord DeselectColor [color] ;; (defrecord DeselectColor [color]
ptk/UpdateEvent ;; ptk/UpdateEvent
(update [_ state] ;; (update [_ state]
(update-in state [:dashboard :colors :selected] disj color))) ;; (update-in state [:dashboard :colors :selected] disj color)))
;;
(defrecord ToggleColorSelection [color] ;; (defrecord ToggleColorSelection [color]
ptk/WatchEvent ;; ptk/WatchEvent
(watch [_ state stream] ;; (watch [_ state stream]
(let [selected (get-in state [:dashboard :colors :selected])] ;; (let [selected (get-in state [:dashboard :colors :selected])]
(rx/of ;; (rx/of
(if (selected color) ;; (if (selected color)
(DeselectColor. color) ;; (DeselectColor. color)
(SelectColor. color)))))) ;; (SelectColor. color))))))
;;
(defn toggle-color-selection ;; (defn toggle-color-selection
[color] ;; [color]
{:pre [(color/hex? color)]} ;; {:pre [(color/hex? color)]}
(ToggleColorSelection. color)) ;; (ToggleColorSelection. color))
;;
;; --- Copy Selected Color ;; ;; --- Copy Selected Color
;;
(defrecord CopySelected [id] ;; (defrecord CopySelected [id]
ptk/UpdateEvent ;; ptk/UpdateEvent
(update [_ state] ;; (update [_ state]
(let [selected (get-in state [:dashboard :colors :selected])] ;; (let [selected (get-in state [:dashboard :colors :selected])]
(update-in state [:colors-collections id :colors] set/union selected))) ;; (update-in state [:colors-collections id :colors] set/union selected)))
;;
ptk/WatchEvent ;; ptk/WatchEvent
(watch [_ state stream] ;; (watch [_ state stream]
(rx/of (persist-collections)))) ;; (rx/of (persist-collections))))
;;
(defn copy-selected ;; (defn copy-selected
[id] ;; [id]
{:pre [(or (uuid? id) (nil? id))]} ;; {:pre [(or (uuid? id) (nil? id))]}
(CopySelected. id)) ;; (CopySelected. id))
;;
;; --- Move Selected Color ;; ;; --- Move Selected Color
;;
(defrecord MoveSelected [from to] ;; (defrecord MoveSelected [from to]
ptk/UpdateEvent ;; ptk/UpdateEvent
(update [_ state] ;; (update [_ state]
(let [selected (get-in state [:dashboard :colors :selected])] ;; (let [selected (get-in state [:dashboard :colors :selected])]
(-> state ;; (-> state
(update-in [:colors-collections from :colors] set/difference selected) ;; (update-in [:colors-collections from :colors] set/difference selected)
(update-in [:colors-collections to :colors] set/union selected)))) ;; (update-in [:colors-collections to :colors] set/union selected))))
;;
ptk/WatchEvent ;; ptk/WatchEvent
(watch [_ state stream] ;; (watch [_ state stream]
(rx/of (persist-collections)))) ;; (rx/of (persist-collections))))
;;
(defn move-selected ;; (defn move-selected
[from to] ;; [from to]
{:pre [(or (uuid? from) (nil? from)) ;; {:pre [(or (uuid? from) (nil? from))
(or (uuid? to) (nil? to))]} ;; (or (uuid? to) (nil? to))]}
(MoveSelected. from to)) ;; (MoveSelected. from to))
;;
;; --- Delete Colors ;; ;; --- Delete Colors
;;
(defrecord DeleteColors [coll-id colors] ;; (defrecord DeleteColors [coll-id colors]
ptk/UpdateEvent ;; ptk/UpdateEvent
(update [_ state] ;; (update [_ state]
(assoc-in state [:dashboard :colors :selected] #{})) ;; (assoc-in state [:dashboard :colors :selected] #{}))
;;
ptk/WatchEvent ;; ptk/WatchEvent
(watch [_ state stream] ;; (watch [_ state stream]
(rx/of (remove-colors coll-id colors)))) ;; (rx/of (remove-colors coll-id colors))))
;;
(defn delete-colors ;; (defn delete-colors
[coll-id colors] ;; [coll-id colors]
(DeleteColors. coll-id colors)) ;; (DeleteColors. coll-id colors))
;;;; NEW ;;;; NEW
@ -287,3 +287,43 @@
(update [_ state] (update [_ state]
(-> state (-> state
(assoc-in [:library :selected-items] data))))) (assoc-in [:library :selected-items] data)))))
(declare create-color-library-result)
(defn create-color-library
[team-id name]
(ptk/reify ::create-color-library
ptk/WatchEvent
(watch [_ state stream]
(->> (rp/mutation! :create-color-library {:team-id team-id
:name name})
(rx/map create-color-library-result)))))
(defn create-color-library-result [result]
(ptk/reify ::create-color-library-result
ptk/UpdateEvent
(update [_ state]
(-> state
(update-in [:library :color-libraries] #(into [result] %))))))
(declare create-color-result)
(defn create-color
[library-id color]
(s/assert (s/nilable uuid?) library-id)
(ptk/reify ::create-color
ptk/WatchEvent
(watch [_ state s]
(->> (rp/mutation! :create-color {:library-id library-id
:content color
:name color})
(rx/map create-color-result)))))
(defn create-color-result
[item]
(ptk/reify ::create-color-result
ptk/UpdateEvent
(update [_ state]
(-> state
(update-in [:library :selected-items] #(into [item] %) )))))

View file

@ -76,6 +76,26 @@
(-> state (-> state
(assoc-in [:library :selected-items] data))))) (assoc-in [:library :selected-items] data)))))
(declare create-icon-library-result)
(defn create-icon-library
[team-id name]
(ptk/reify ::create-icon-library
ptk/WatchEvent
(watch [_ state stream]
(->> (rp/mutation! :create-icon-library {:team-id team-id
:name name})
(rx/map create-icon-library-result)))))
(defn create-icon-library-result [result]
(ptk/reify ::create-icon-library-result
ptk/UpdateEvent
(update [_ state]
(-> state
(update-in [:library :icon-libraries] #(into [result] %))))))
;; (declare fetch-icons) ;; (declare fetch-icons)
;; ;;
;; (defn initialize ;; (defn initialize
@ -92,32 +112,32 @@
;; ;;
;; --- Fetch Collections ;; --- Fetch Collections
(declare collections-fetched) ;; (declare collections-fetched)
;;
(def fetch-collections ;; (def fetch-collections
(ptk/reify ::fetch-collections ;; (ptk/reify ::fetch-collections
ptk/WatchEvent ;; ptk/WatchEvent
(watch [_ state s] ;; (watch [_ state s]
(->> (rp/query! :icons-collections) ;; (->> (rp/query! :icons-collections)
(rx/map collections-fetched))))) ;; (rx/map collections-fetched)))))
;;
;; --- Collections Fetched ;; ;; --- Collections Fetched
;;
(defn collections-fetched ;; (defn collections-fetched
[items] ;; [items]
(s/assert (s/every ::collection) items) ;; (s/assert (s/every ::collection) items)
(ptk/reify ::collections-fetched ;; (ptk/reify ::collections-fetched
cljs.core/IDeref ;; cljs.core/IDeref
(-deref [_] items) ;; (-deref [_] items)
;;
ptk/UpdateEvent ;; ptk/UpdateEvent
(update [_ state] ;; (update [_ state]
(reduce (fn [state {:keys [id user] :as item}] ;; (reduce (fn [state {:keys [id user] :as item}]
(let [type (if (uuid/zero? (:user-id item)) :builtin :own) ;; (let [type (if (uuid/zero? (:user-id item)) :builtin :own)
item (assoc item :type type)] ;; item (assoc item :type type)]
(assoc-in state [:icons-collections id] item))) ;; (assoc-in state [:icons-collections id] item)))
state ;; state
items)))) ;; items))))
;; ;; --- Create Collection ;; ;; --- Create Collection
@ -175,77 +195,75 @@
;; (rx/tap on-success) ;; (rx/tap on-success)
;; (rx/ignore))))) ;; (rx/ignore)))))
;; ;;
;; ;; --- Icon Created ;; --- Icon Created
;;
;; (defrecord IconCreated [item] ;; --- Create Icon
;; ptk/UpdateEvent (defn- parse-svg
;; (update [_ state] [data]
;; (let [{:keys [id] :as item} (assoc item :type :icon)] (s/assert ::us/string data)
;; (update state :icons assoc id item)))) (let [valid-tags #{"defs" "path" "circle" "rect" "metadata" "g"
;; "radialGradient" "stop"}
;; (defn icon-created div (dom/create-element "div")
;; [item] gc (dom/create-element "div")
;; (IconCreated. item)) g (dom/create-element "http://www.w3.org/2000/svg" "g")
;; _ (dom/set-html! div data)
;; ;; --- Create Icon svg (dom/query div "svg")]
;; (loop [child (dom/get-first-child svg)]
;; (declare icon-created) (if child
;; (let [tagname (dom/get-tag-name child)]
;; (defn- parse-svg (if (contains? valid-tags tagname)
;; [data] (dom/append-child! g child)
;; (s/assert ::us/string data) (dom/append-child! gc child))
;; (let [valid-tags #{"defs" "path" "circle" "rect" "metadata" "g" (recur (dom/get-first-child svg)))
;; "radialGradient" "stop"} (let [width (.. svg -width -baseVal -value)
;; div (dom/create-element "div") height (.. svg -height -baseVal -value)
;; gc (dom/create-element "div") view-box [(.. svg -viewBox -baseVal -x)
;; g (dom/create-element "http://www.w3.org/2000/svg" "g") (.. svg -viewBox -baseVal -y)
;; _ (dom/set-html! div data) (.. svg -viewBox -baseVal -width)
;; svg (dom/query div "svg")] (.. svg -viewBox -baseVal -height)]
;; (loop [child (dom/get-first-child svg)] props {:width width
;; (if child :mimetype "image/svg+xml"
;; (let [tagname (dom/get-tag-name child)] :height height
;; (if (contains? valid-tags tagname) :view-box view-box}]
;; (dom/append-child! g child) [(dom/get-outer-html g) props])))))
;; (dom/append-child! gc child))
;; (recur (dom/get-first-child svg)))
;; (let [width (.. svg -width -baseVal -value) (declare create-icon-result)
;; height (.. svg -height -baseVal -value)
;; view-box [(.. svg -viewBox -baseVal -x) (defn create-icons
;; (.. svg -viewBox -baseVal -y) [library-id files]
;; (.. svg -viewBox -baseVal -width) (s/assert (s/nilable uuid?) library-id)
;; (.. svg -viewBox -baseVal -height)] (ptk/reify ::create-icons
;; props {:width width ptk/WatchEvent
;; :mimetype "image/svg+xml" (watch [_ state s]
;; :height height (letfn [(parse [file]
;; :view-box view-box}] (->> (wapi/read-file-as-text file)
;; [(dom/get-outer-html g) props]))))) (rx/map parse-svg)))
;; (allowed? [file]
;; (= (.-type file) "image/svg+xml"))
;; (defn create-icons (prepare [[content metadata]]
;; [id files] {:library-id library-id
;; (s/assert (s/nilable uuid?) id) :content content
;; (ptk/reify ::create-icons :id (uuid/next)
;; ptk/WatchEvent ;; TODO Keep the name of the original icon
;; (watch [_ state s] :name (str "Icon " (gensym "i"))
;; (letfn [(parse [file] :metadata metadata})]
;; (->> (wapi/read-file-as-text file) (->> (rx/from files)
;; (rx/map parse-svg))) (rx/filter allowed?)
;; (allowed? [file] (rx/merge-map parse)
;; (= (.-type file) "image/svg+xml")) (rx/map prepare)
;; (prepare [[content metadata]] (rx/flat-map #(rp/mutation! :create-icon %))
;; {:collection-id id (rx/map create-icon-result))))))
;; :content content
;; :id (uuid/next) (defn create-icon-result
;; ;; TODO Keep the name of the original icon [item]
;; :name (str "Icon " (gensym "i")) (ptk/reify ::create-icon-result
;; :metadata metadata})] ptk/UpdateEvent
;; (->> (rx/from files) (update [_ state]
;; (rx/filter allowed?) (let [{:keys [id] :as item} (assoc item :type :icon)]
;; (rx/merge-map parse) (-> state
;; (rx/map prepare) (update-in [:library :selected-items] #(into [item] %)))))))
;; (rx/flat-map #(rp/mutation! :create-icon %))
;; (rx/map icon-created))))))
;;
;; ;; --- Icon Persisted ;; ;; --- Icon Persisted
;; ;;
;; (defrecord IconPersisted [id data] ;; (defrecord IconPersisted [id data]
@ -272,27 +290,27 @@
;; ;;
;; --- Load Icons ;; --- Load Icons
(declare icons-fetched) ;; (declare icons-fetched)
;;
(defn fetch-icons ;; (defn fetch-icons
[id] ;; [id]
(ptk/reify ::fetch-icons ;; (ptk/reify ::fetch-icons
ptk/WatchEvent ;; ptk/WatchEvent
(watch [_ state s] ;; (watch [_ state s]
(let [params (cond-> {} id (assoc :collection-id id))] ;; (let [params (cond-> {} id (assoc :collection-id id))]
(->> (rp/query! :icons-by-collection params) ;; (->> (rp/query! :icons-by-collection params)
(rx/map icons-fetched)))))) ;; (rx/map icons-fetched))))))
;;
;; --- Icons Fetched ;; ;; --- Icons Fetched
;;
(defn icons-fetched ;; (defn icons-fetched
[items] ;; [items]
;; TODO: specs ;; ;; TODO: specs
(ptk/reify ::icons-fetched ;; (ptk/reify ::icons-fetched
ptk/UpdateEvent ;; ptk/UpdateEvent
(update [_ state] ;; (update [_ state]
(let [icons (d/index-by :id items)] ;; (let [icons (d/index-by :id items)]
(assoc state :icons icons))))) ;; (assoc state :icons icons)))))
;; ;; --- Rename Icon ;; ;; --- Rename Icon
;; ;;

View file

@ -55,305 +55,254 @@
::thumb-uri ::thumb-uri
::user-id])) ::user-id]))
;; --- Initialize Collection Page ;; ;; --- Initialize Collection Page
;;
(declare fetch-images) ;; (declare fetch-images)
;;
(defn initialize ;; (defn initialize
[collection-id] ;; [collection-id]
(us/verify ::us/uuid collection-id) ;; (us/verify ::us/uuid collection-id)
(ptk/reify ::initialize ;; (ptk/reify ::initialize
ptk/UpdateEvent ;; ptk/UpdateEvent
(update [_ state] ;; (update [_ state]
(assoc-in state [:dashboard-images :selected] #{})) ;; (assoc-in state [:dashboard-images :selected] #{}))
;;
ptk/WatchEvent ;; ptk/WatchEvent
(watch [_ state stream] ;; (watch [_ state stream]
(rx/of (fetch-images collection-id))))) ;; (rx/of (fetch-images collection-id)))))
;;
;; --- Fetch Collections ;; ;; --- Fetch Collections
;;
(declare collections-fetched) ;; (declare collections-fetched)
;;
(def fetch-collections ;; (def fetch-collections
(ptk/reify ::fetch-collections ;; (ptk/reify ::fetch-collections
ptk/WatchEvent ;; ptk/WatchEvent
(watch [_ state s] ;; (watch [_ state s]
(->> (rp/query! :image-collections) ;; (->> (rp/query! :image-collections)
(rx/map collections-fetched))))) ;; (rx/map collections-fetched)))))
;;
;;
;; --- Collections Fetched ;; ;; --- Collections Fetched
;;
(defn collections-fetched ;; (defn collections-fetched
[items] ;; [items]
(us/verify (s/every ::collection) items) ;; (us/verify (s/every ::collection) items)
(ptk/reify ::collections-fetched ;; (ptk/reify ::collections-fetched
ptk/UpdateEvent ;; ptk/UpdateEvent
(update [_ state] ;; (update [_ state]
(reduce (fn [state {:keys [id user] :as item}] ;; (reduce (fn [state {:keys [id user] :as item}]
(let [type (if (uuid/zero? (:user-id item)) :builtin :own) ;; (let [type (if (uuid/zero? (:user-id item)) :builtin :own)
item (assoc item :type type)] ;; item (assoc item :type type)]
(assoc-in state [:images-collections id] item))) ;; (assoc-in state [:images-collections id] item)))
state ;; state
items)))) ;; items))))
;;
;;
;; --- Create Collection ;; ;; --- Create Collection
;;
(declare collection-created) ;; (declare collection-created)
;;
(def create-collection ;; (def create-collection
(ptk/reify ::create-collection ;; (ptk/reify ::create-collection
ptk/WatchEvent ;; ptk/WatchEvent
(watch [_ state s] ;; (watch [_ state s]
(let [data {:name (tr "ds.default-library-title" (gensym "c"))}] ;; (let [data {:name (tr "ds.default-library-title" (gensym "c"))}]
(->> (rp/mutation! :create-image-collection data) ;; (->> (rp/mutation! :create-image-collection data)
(rx/map collection-created)))))) ;; (rx/map collection-created))))))
;;
;; --- Collection Created ;; ;; --- Collection Created
;;
(defn collection-created ;; (defn collection-created
[item] ;; [item]
(us/verify ::collection item) ;; (us/verify ::collection item)
(ptk/reify ::collection-created ;; (ptk/reify ::collection-created
ptk/UpdateEvent ;; ptk/UpdateEvent
(update [_ state] ;; (update [_ state]
(let [{:keys [id] :as item} (assoc item :type :own)] ;; (let [{:keys [id] :as item} (assoc item :type :own)]
(update state :images-collections assoc id item))))) ;; (update state :images-collections assoc id item)))))
;;
;; --- Rename Collection ;; ;; --- Rename Collection
;;
(defn rename-collection ;; (defn rename-collection
[id name] ;; [id name]
(ptk/reify ::rename-collection ;; (ptk/reify ::rename-collection
ptk/UpdateEvent ;; ptk/UpdateEvent
(update [_ state] ;; (update [_ state]
(assoc-in state [:images-collections id :name] name)) ;; (assoc-in state [:images-collections id :name] name))
;;
ptk/WatchEvent ;; ptk/WatchEvent
(watch [_ state s] ;; (watch [_ state s]
(let [params {:id id :name name}] ;; (let [params {:id id :name name}]
(->> (rp/mutation! :rename-image-collection params) ;; (->> (rp/mutation! :rename-image-collection params)
(rx/ignore)))))) ;; (rx/ignore))))))
;;
;; --- Delete Collection ;; ;; --- Delete Collection
;;
(defn delete-collection ;; (defn delete-collection
[id on-success] ;; [id on-success]
(ptk/reify ::delete-collection ;; (ptk/reify ::delete-collection
ptk/UpdateEvent ;; ptk/UpdateEvent
(update [_ state] ;; (update [_ state]
(update state :images-collections dissoc id)) ;; (update state :images-collections dissoc id))
;;
ptk/WatchEvent ;; ptk/WatchEvent
(watch [_ state s] ;; (watch [_ state s]
(->> (rp/mutation! :delete-image-collection {:id id}) ;; (->> (rp/mutation! :delete-image-collection {:id id})
(rx/tap on-success) ;; (rx/tap on-success)
(rx/ignore))))) ;; (rx/ignore)))))
;;
;; --- Create Image ;; ;; --- Update Image
;;
(declare image-created) ;; (defn persist-image
(def allowed-file-types #{"image/jpeg" "image/png"}) ;; [id]
;; (us/verify ::us/uuid id)
(defn create-images ;; (ptk/reify ::persist-image
([id files] (create-images id files identity)) ;; ptk/WatchEvent
([id files on-uploaded] ;; (watch [_ state stream]
(us/verify (s/nilable ::us/uuid) id) ;; (let [data (get-in state [:images id])]
(us/verify fn? on-uploaded) ;; (->> (rp/mutation! :update-image data)
(ptk/reify ::create-images ;; (rx/ignore))))))
ptk/UpdateEvent ;;
(update [_ state] ;; ;; --- Fetch Images
(assoc-in state [:dashboard-images :uploading] true)) ;;
;; (declare images-fetched)
ptk/WatchEvent ;;
(watch [_ state stream] ;; (defn fetch-images
(letfn [(allowed-file? [file] ;; "Fetch a list of images of the selected collection"
(contains? allowed-file-types (.-type file))) ;; [id]
(finalize-upload [state] ;; (us/verify ::us/uuid id)
(assoc-in state [:dashboard-images :uploading] false)) ;; (ptk/reify ::fetch-images
(on-success [_] ;; ptk/WatchEvent
(st/emit! finalize-upload) ;; (watch [_ state s]
(on-uploaded)) ;; (let [params {:collection-id id}]
(on-error [e] ;; (->> (rp/query! :images-by-collection params)
(st/emit! finalize-upload) ;; (rx/map (partial images-fetched id)))))))
(rx/throw e)) ;;
(prepare [file] ;; ;; --- Images Fetched
{:name (.-name file) ;;
:collection-id id ;; (s/def ::images (s/every ::image))
:content file})] ;;
(->> (rx/from files) ;; (defn images-fetched
(rx/filter allowed-file?) ;; [collection-id items]
(rx/map prepare) ;; (us/verify ::us/uuid collection-id)
(rx/mapcat #(rp/mutation! :upload-image %)) ;; (us/verify ::images items)
(rx/reduce conj []) ;; (ptk/reify ::images-fetched
(rx/do on-success) ;; ptk/UpdateEvent
(rx/mapcat identity) ;; (update [_ state]
(rx/map image-created) ;; (let [images (d/index-by :id items)]
(rx/catch on-error))))))) ;; (assoc state :images images)))))
;;
;; --- Image Created ;; ;; --- Fetch Image
;;
(defn image-created ;; (declare image-fetched)
[item] ;;
(us/verify ::image item) ;; (defrecord FetchImage [id]
(ptk/reify ::image-created ;; ptk/WatchEvent
ptk/UpdateEvent ;; (watch [_ state stream]
(update [_ state] ;; (let [existing (get-in state [:images id])]
(update state :images assoc (:id item) item)))) ;; (if existing
;; (rx/empty)
;; --- Update Image ;; (->> (rp/query! :image-by-id {:id id})
;; (rx/map image-fetched)
(defn persist-image ;; (rx/catch rp/client-error? #(rx/empty)))))))
[id] ;;
(us/verify ::us/uuid id) ;; (defn fetch-image
(ptk/reify ::persist-image ;; "Conditionally fetch image by its id. If image
ptk/WatchEvent ;; is already loaded, this event is noop."
(watch [_ state stream] ;; [id]
(let [data (get-in state [:images id])] ;; {:pre [(uuid? id)]}
(->> (rp/mutation! :update-image data) ;; (FetchImage. id))
(rx/ignore)))))) ;;
;; ;; --- Image Fetched
;; --- Fetch Images ;;
;; (defrecord ImageFetched [image]
(declare images-fetched) ;; ptk/UpdateEvent
;; (update [_ state]
(defn fetch-images ;; (let [id (:id image)]
"Fetch a list of images of the selected collection" ;; (update state :images assoc id image))))
[id] ;;
(us/verify ::us/uuid id) ;; (defn image-fetched
(ptk/reify ::fetch-images ;; [image]
ptk/WatchEvent ;; {:pre [(map? image)]}
(watch [_ state s] ;; (ImageFetched. image))
(let [params {:collection-id id}] ;;
(->> (rp/query! :images-by-collection params) ;; ;; --- Rename Image
(rx/map (partial images-fetched id))))))) ;;
;; (defn rename-image
;; --- Images Fetched ;; [id name]
;; (us/verify ::us/uuid id)
(s/def ::images (s/every ::image)) ;; (us/verify ::us/string name)
;; (ptk/reify ::rename-image
(defn images-fetched ;; ptk/UpdateEvent
[collection-id items] ;; (update [_ state]
(us/verify ::us/uuid collection-id) ;; (assoc-in state [:images id :name] name))
(us/verify ::images items) ;;
(ptk/reify ::images-fetched ;; ptk/WatchEvent
ptk/UpdateEvent ;; (watch [_ state stream]
(update [_ state] ;; (rx/of (persist-image id)))))
(let [images (d/index-by :id items)] ;;
(assoc state :images images))))) ;; ;; --- Image Selection
;;
;; --- Fetch Image ;; (defn select-image
;; [id]
(declare image-fetched) ;; (ptk/reify ::select-image
;; ptk/UpdateEvent
(defrecord FetchImage [id] ;; (update [_ state]
ptk/WatchEvent ;; (update-in state [:dashboard-images :selected] (fnil conj #{}) id))))
(watch [_ state stream] ;;
(let [existing (get-in state [:images id])] ;; (defn deselect-image
(if existing ;; [id]
(rx/empty) ;; (ptk/reify ::deselect-image
(->> (rp/query! :image-by-id {:id id}) ;; ptk/UpdateEvent
(rx/map image-fetched) ;; (update [_ state]
(rx/catch rp/client-error? #(rx/empty))))))) ;; (update-in state [:dashboard-images :selected] (fnil disj #{}) id))))
;;
(defn fetch-image ;; (def deselect-all-images
"Conditionally fetch image by its id. If image ;; (ptk/reify ::deselect-all-images
is already loaded, this event is noop." ;; ptk/UpdateEvent
[id] ;; (update [_ state]
{:pre [(uuid? id)]} ;; (assoc-in state [:dashboard-images :selected] #{}))))
(FetchImage. id)) ;;
;; ;; --- Delete Images
;; --- Image Fetched ;;
;; (defn delete-image
(defrecord ImageFetched [image] ;; [id]
ptk/UpdateEvent ;; (us/verify ::us/uuid id)
(update [_ state] ;; (ptk/reify ::delete-image
(let [id (:id image)] ;; ptk/UpdateEvent
(update state :images assoc id image)))) ;; (update [_ state]
;; (update state :images dissoc id))
(defn image-fetched ;;
[image] ;; ptk/WatchEvent
{:pre [(map? image)]} ;; (watch [_ state s]
(ImageFetched. image)) ;; (rx/merge
;; (rx/of deselect-all-images)
;; --- Rename Image ;; (->> (rp/mutation! :delete-image {:id id})
;; (rx/ignore))))))
(defn rename-image ;;
[id name] ;; ;; --- Delete Selected
(us/verify ::us/uuid id) ;;
(us/verify ::us/string name) ;; (def delete-selected
(ptk/reify ::rename-image ;; (ptk/reify ::delete-selected
ptk/UpdateEvent ;; ptk/WatchEvent
(update [_ state] ;; (watch [_ state stream]
(assoc-in state [:images id :name] name)) ;; (let [selected (get-in state [:dashboard-images :selected])]
;; (->> (rx/from selected)
ptk/WatchEvent ;; (rx/map delete-image))))))
(watch [_ state stream] ;;
(rx/of (persist-image id))))) ;; ;; --- Update Opts (Filtering & Ordering)
;;
;; --- Image Selection ;; (defn update-opts
;; [& {:keys [order filter edition]
(defn select-image ;; :or {edition false}}]
[id] ;; (ptk/reify ::update-opts
(ptk/reify ::select-image ;; ptk/UpdateEvent
ptk/UpdateEvent ;; (update [_ state]
(update [_ state] ;; (update state :dashboard-images merge
(update-in state [:dashboard-images :selected] (fnil conj #{}) id)))) ;; {:edition edition}
;; (when order {:order order})
(defn deselect-image ;; (when filter {:filter filter})))))
[id]
(ptk/reify ::deselect-image
ptk/UpdateEvent
(update [_ state]
(update-in state [:dashboard-images :selected] (fnil disj #{}) id))))
(def deselect-all-images
(ptk/reify ::deselect-all-images
ptk/UpdateEvent
(update [_ state]
(assoc-in state [:dashboard-images :selected] #{}))))
;; --- Delete Images
(defn delete-image
[id]
(us/verify ::us/uuid id)
(ptk/reify ::delete-image
ptk/UpdateEvent
(update [_ state]
(update state :images dissoc id))
ptk/WatchEvent
(watch [_ state s]
(rx/merge
(rx/of deselect-all-images)
(->> (rp/mutation! :delete-image {:id id})
(rx/ignore))))))
;; --- Delete Selected
(def delete-selected
(ptk/reify ::delete-selected
ptk/WatchEvent
(watch [_ state stream]
(let [selected (get-in state [:dashboard-images :selected])]
(->> (rx/from selected)
(rx/map delete-image))))))
;; --- Update Opts (Filtering & Ordering)
(defn update-opts
[& {:keys [order filter edition]
:or {edition false}}]
(ptk/reify ::update-opts
ptk/UpdateEvent
(update [_ state]
(update state :dashboard-images merge
{:edition edition}
(when order {:order order})
(when filter {:filter filter})))))
;; --- Copy Selected Image ;; --- Copy Selected Image
@ -431,7 +380,7 @@
ptk/WatchEvent ptk/WatchEvent
(watch [_ state stream] (watch [_ state stream]
(->> (rp/query! :image {:library-id library-id}) (->> (rp/query! :images {:library-id library-id})
(rx/map fetch-image-library-result))))) (rx/map fetch-image-library-result)))))
(defn fetch-image-library-result (defn fetch-image-library-result
@ -441,3 +390,71 @@
(update [_ state] (update [_ state]
(-> state (-> state
(assoc-in [:library :selected-items] data))))) (assoc-in [:library :selected-items] data)))))
(declare create-image-library-result)
(defn create-image-library
[team-id name]
(ptk/reify ::create-image-library
ptk/WatchEvent
(watch [_ state stream]
(->> (rp/mutation! :create-image-library {:team-id team-id
:name name})
(rx/map create-image-library-result)))))
(defn create-image-library-result [result]
(ptk/reify ::create-image-library-result
ptk/UpdateEvent
(update [_ state]
(-> state
(update-in [:library :image-libraries] #(into [result] %))))))
;; --- Create Image
(declare create-images-result)
(def allowed-file-types #{"image/jpeg" "image/png"})
(defn create-images
([library-id files] (create-images library-id files identity))
([library-id files on-uploaded]
(us/verify (s/nilable ::us/uuid) library-id)
(us/verify fn? on-uploaded)
(ptk/reify ::create-images
ptk/WatchEvent
(watch [_ state stream]
(letfn [(allowed-file? [file]
(contains? allowed-file-types (.-type file)))
#_(finalize-upload [state]
(assoc-in state [:dashboard-images :uploading] false))
(on-success [_]
#_(st/emit! finalize-upload)
(on-uploaded))
(on-error [e]
#_(st/emit! finalize-upload)
(rx/throw e))
(prepare [file]
{:name (.-name file)
:library-id library-id
:content file})]
(->> (rx/from files)
(rx/filter allowed-file?)
(rx/map prepare)
(rx/mapcat #(rp/mutation! :upload-image %))
(rx/reduce conj [])
(rx/do on-success)
(rx/mapcat identity)
(rx/map create-images-result)
(rx/catch on-error)))))))
;; --- Image Created
(defn create-images-result
[item]
#_(us/verify ::image item)
(ptk/reify ::create-images-result
ptk/UpdateEvent
(update [_ state]
(-> state
(update-in [:library :selected-items] #(into [item] %))))))

View file

@ -6,6 +6,8 @@
(ns uxbox.main.ui.colorpicker (ns uxbox.main.ui.colorpicker
(:require (:require
[lentes.core :as l]
[uxbox.main.store :as st]
[goog.object :as gobj] [goog.object :as gobj]
[rumext.alpha :as mf] [rumext.alpha :as mf]
[vendor.react-color])) [vendor.react-color]))
@ -16,5 +18,21 @@
[:> js/SketchPicker {:color value [:> js/SketchPicker {:color value
:disableAlpha true :disableAlpha true
:presetColors colors :presetColors colors
:onChangeComplete on-change-complete}])) :onChangeComplete on-change-complete
:style {:box-shadow "none"}}]))
(defn- lookup-colors
[state]
(as-> {} $
(reduce (fn [acc shape]
(-> acc
(update (:fill-color shape) (fnil inc 0))
(update (:stroke-color shape) (fnil inc 0))))
$ (vals (:shapes state)))
(reverse (sort-by second $))
(map first $)
(remove nil? $)))
(def most-used-colors
(-> (l/lens lookup-colors)
(l/derive st/state)))

View file

@ -24,272 +24,272 @@
[uxbox.util.i18n :as t :refer [tr]] [uxbox.util.i18n :as t :refer [tr]]
[uxbox.util.router :as rt])) [uxbox.util.router :as rt]))
;; --- Refs ;; ;; --- Refs
;;
(def collections-iref ;; (def collections-iref
(-> (l/key :colors-collections) ;; (-> (l/key :colors-collections)
(l/derive st/state))) ;; (l/derive st/state)))
;;
(def selected-colors-iref ;; (def selected-colors-iref
(-> (l/in [:dashboard :colors :selected]) ;; (-> (l/in [:dashboard :colors :selected])
(l/derive st/state))) ;; (l/derive st/state)))
;;
;; --- Colors Modal (Component) ;; ;; --- Colors Modal (Component)
;;
(mf/defc color-modal ;; (mf/defc color-modal
[{:keys [on-submit value] :as props}] ;; [{:keys [on-submit value] :as props}]
(let [local (mf/use-var value)] ;; (let [local (mf/use-var value)]
[:div.lightbox-body ;; [:div.lightbox-body
[:h3 (tr "ds.color-lightbox.title" )] ;; [:h3 (tr "ds.color-lightbox.title" )]
[:form ;; [:form
[:div.row-flex.center ;; [:div.row-flex.center
[:& colorpicker {:value (or @local "#00ccff") ;; [:& colorpicker {:value (or @local "#00ccff")
:on-change #(reset! local %)}]] ;; :on-change #(reset! local %)}]]
[:input#project-btn.btn-primary ;; [:input#project-btn.btn-primary
{:value (tr "ds.color-lightbox.add") ;; {:value (tr "ds.color-lightbox.add")
:on-click #(on-submit @local) ;; :on-click #(on-submit @local)
:type "button"}]]])) ;; :type "button"}]]]))
;;
;; --- Page Title ;; ;; --- Page Title
;;
;;
(mf/defc grid-header ;; (mf/defc grid-header
[{:keys [coll] :as props}] ;; [{:keys [coll] :as props}]
(letfn [(on-change [name] ;; (letfn [(on-change [name]
(st/emit! (dc/rename-collection (:id coll) name))) ;; (st/emit! (dc/rename-collection (:id coll) name)))
;;
(delete [] ;; (delete []
(st/emit! ;; (st/emit!
(dc/delete-collection (:id coll)) ;; (dc/delete-collection (:id coll))
(rt/nav :dashboard-colors nil {:type (:type coll)}))) ;; (rt/nav :dashboard-colors nil {:type (:type coll)})))
;;
(on-delete [] ;; (on-delete []
(modal/show! confirm-dialog {:on-accept delete}))] ;; (modal/show! confirm-dialog {:on-accept delete}))]
[:& common/grid-header {:value (:name coll) ;; [:& common/grid-header {:value (:name coll)
:on-change on-change ;; :on-change on-change
:on-delete on-delete}])) ;; :on-delete on-delete}]))
;;
;; --- Nav ;; ;; --- Nav
;;
(mf/defc nav-item ;; (mf/defc nav-item
[{:keys [coll selected?] :as props}] ;; [{:keys [coll selected?] :as props}]
(let [local (mf/use-state {}) ;; (let [local (mf/use-state {})
{:keys [id type name]} coll ;; {:keys [id type name]} coll
colors (count (:colors coll)) ;; colors (count (:colors coll))
editable? (= type :own)] ;; editable? (= type :own)]
(letfn [(on-click [event] ;; (letfn [(on-click [event]
(let [type (or type :own)] ;; (let [type (or type :own)]
(st/emit! (rt/nav :dashboard-colors nil {:type type :id id})))) ;; (st/emit! (rt/nav :dashboard-colors nil {:type type :id id}))))
(on-input-change [event] ;; (on-input-change [event]
(let [value (dom/get-target event) ;; (let [value (dom/get-target event)
value (dom/get-value value)] ;; value (dom/get-value value)]
(swap! local assoc :name value))) ;; (swap! local assoc :name value)))
(on-cancel [event] ;; (on-cancel [event]
(swap! local dissoc :name) ;; (swap! local dissoc :name)
(swap! local dissoc :edit)) ;; (swap! local dissoc :edit))
(on-double-click [event] ;; (on-double-click [event]
(when editable? ;; (when editable?
(swap! local assoc :edit true))) ;; (swap! local assoc :edit true)))
(on-input-keyup [event] ;; (on-input-keyup [event]
(when (k/enter? event) ;; (when (k/enter? event)
(let [value (dom/get-target event) ;; (let [value (dom/get-target event)
value (dom/get-value value)] ;; value (dom/get-value value)]
(st/emit! (dc/rename-collection id (str/trim (:name @local)))) ;; (st/emit! (dc/rename-collection id (str/trim (:name @local))))
(swap! local assoc :edit false))))] ;; (swap! local assoc :edit false))))]
[:li {:on-click on-click ;; [:li {:on-click on-click
:on-double-click on-double-click ;; :on-double-click on-double-click
:class-name (when selected? "current")} ;; :class-name (when selected? "current")}
(if (:edit @local) ;; (if (:edit @local)
[:div ;; [:div
[:input.element-title ;; [:input.element-title
{:value (if (:name @local) (:name @local) name) ;; {:value (if (:name @local) (:name @local) name)
:on-change on-input-change ;; :on-change on-input-change
:on-key-down on-input-keyup}] ;; :on-key-down on-input-keyup}]
[:span.close {:on-click on-cancel} i/close]] ;; [:span.close {:on-click on-cancel} i/close]]
[:span.element-title name]) ;; [:span.element-title name])
#_[:span.element-subtitle ;; #_[:span.element-subtitle
(tr "ds.num-elements" (t/c colors))]]))) ;; (tr "ds.num-elements" (t/c colors))]])))
;;
(mf/defc nav ;; (mf/defc nav
[{:keys [id type colls selected-coll] :as props}] ;; [{:keys [id type colls selected-coll] :as props}]
(let [own? (= type :own) ;; (let [own? (= type :own)
builtin? (= type :builtin) ;; builtin? (= type :builtin)
select-tab #(st/emit! (rt/nav :dashboard-colors nil {:type %}))] ;; select-tab #(st/emit! (rt/nav :dashboard-colors nil {:type %}))]
[:div.library-bar ;; [:div.library-bar
[:div.library-bar-inside ;; [:div.library-bar-inside
[:ul.library-tabs ;; [:ul.library-tabs
[:li {:class-name (when own? "current") ;; [:li {:class-name (when own? "current")
:on-click (partial select-tab :own)} ;; :on-click (partial select-tab :own)}
(tr "ds.your-colors-title")] ;; (tr "ds.your-colors-title")]
[:li {:class-name (when builtin? "current") ;; [:li {:class-name (when builtin? "current")
:on-click (partial select-tab :builtin)} ;; :on-click (partial select-tab :builtin)}
(tr "ds.store-colors-title")]] ;; (tr "ds.store-colors-title")]]
[:ul.library-elements ;; [:ul.library-elements
(when own? ;; (when own?
[:li ;; [:li
[:a.btn-primary {:on-click #(st/emit! (dc/create-collection))} ;; [:a.btn-primary {:on-click #(st/emit! (dc/create-collection))}
(tr "ds.colors-collection.new")]]) ;; (tr "ds.colors-collection.new")]])
(for [item colls] ;; (for [item colls]
(let [selected? (= (:id item) (:id selected-coll))] ;; (let [selected? (= (:id item) (:id selected-coll))]
[:& nav-item {:coll item :selected? selected? :key (:id item)}]))]]])) ;; [:& nav-item {:coll item :selected? selected? :key (:id item)}]))]]]))
;;
;; --- Grid ;; ;; --- Grid
;;
(mf/defc grid-form ;; (mf/defc grid-form
[{:keys [id] :as props}] ;; [{:keys [id] :as props}]
(letfn [(on-submit [val] ;; (letfn [(on-submit [val]
(st/emit! (dc/add-color id val)) ;; (st/emit! (dc/add-color id val))
(modal/hide!)) ;; (modal/hide!))
(on-click [event] ;; (on-click [event]
(modal/show! color-modal {:on-submit on-submit}))] ;; (modal/show! color-modal {:on-submit on-submit}))]
[:div.grid-item.small-item.add-project {:on-click on-click} ;; [:div.grid-item.small-item.add-project {:on-click on-click}
[:span (tr "ds.color-new")]])) ;; [:span (tr "ds.color-new")]]))
;;
(mf/defc grid-options-tooltip ;; (mf/defc grid-options-tooltip
[{:keys [selected on-select title] :as props}] ;; [{:keys [selected on-select title] :as props}]
{:pre [(uuid? selected) ;; {:pre [(uuid? selected)
(fn? on-select) ;; (fn? on-select)
(string? title)]} ;; (string? title)]}
(let [colls (mf/deref collections-iref) ;; (let [colls (mf/deref collections-iref)
colls (->> (vals colls) ;; colls (->> (vals colls)
(filter #(= :own (:type %))) ;; (filter #(= :own (:type %)))
(remove #(= selected (:id %))) ;; (remove #(= selected (:id %)))
(sort-by :name colls)) ;; (sort-by :name colls))
on-select (fn [event id] ;; on-select (fn [event id]
(dom/prevent-default event) ;; (dom/prevent-default event)
(dom/stop-propagation event) ;; (dom/stop-propagation event)
(on-select id))] ;; (on-select id))]
[:ul.move-list ;; [:ul.move-list
[:li.title title] ;; [:li.title title]
(for [{:keys [id name] :as coll} colls] ;; (for [{:keys [id name] :as coll} colls]
[:li {:key (str id)} ;; [:li {:key (str id)}
[:a {:on-click #(on-select % id)} name]])])) ;; [:a {:on-click #(on-select % id)} name]])]))
;;
(mf/defc grid-options ;; (mf/defc grid-options
[{:keys [id type coll selected] :as props}] ;; [{:keys [id type coll selected] :as props}]
(let [local (mf/use-state {})] ;; (let [local (mf/use-state {})]
(letfn [(delete [event] ;; (letfn [(delete [event]
(st/emit! (dc/delete-colors id selected))) ;; (st/emit! (dc/delete-colors id selected)))
(on-delete [event] ;; (on-delete [event]
(modal/show! confirm-dialog {:on-accept delete})) ;; (modal/show! confirm-dialog {:on-accept delete}))
(on-toggle-copy [event] ;; (on-toggle-copy [event]
(swap! local update :show-copy-tooltip not) ;; (swap! local update :show-copy-tooltip not)
(swap! local assoc :show-move-tooltip false)) ;; (swap! local assoc :show-move-tooltip false))
(on-toggle-move [event] ;; (on-toggle-move [event]
(swap! local update :show-move-tooltip not) ;; (swap! local update :show-move-tooltip not)
(swap! local assoc :show-copy-tooltip false)) ;; (swap! local assoc :show-copy-tooltip false))
(on-copy [selected] ;; (on-copy [selected]
(swap! local assoc ;; (swap! local assoc
:show-move-tooltip false ;; :show-move-tooltip false
:show-copy-tooltip false) ;; :show-copy-tooltip false)
(st/emit! (dc/copy-selected selected))) ;; (st/emit! (dc/copy-selected selected)))
(on-move [selected] ;; (on-move [selected]
(swap! local assoc ;; (swap! local assoc
:show-move-tooltip false ;; :show-move-tooltip false
:show-copy-tooltip false) ;; :show-copy-tooltip false)
(st/emit! (dc/move-selected id selected)))] ;; (st/emit! (dc/move-selected id selected)))]
;;
;; MULTISELECT OPTIONS BAR ;; ;; MULTISELECT OPTIONS BAR
[:div.multiselect-bar ;; [:div.multiselect-bar
(if (or (= type :own) (nil? id)) ;; (if (or (= type :own) (nil? id))
;; if editable ;; ;; if editable
[:div.multiselect-nav ;; [:div.multiselect-nav
[:span.move-item.tooltip.tooltip-top ;; [:span.move-item.tooltip.tooltip-top
{:alt (tr "ds.multiselect-bar.copy") ;; {:alt (tr "ds.multiselect-bar.copy")
:on-click on-toggle-copy} ;; :on-click on-toggle-copy}
(when (:show-copy-tooltip @local) ;; (when (:show-copy-tooltip @local)
[:& grid-options-tooltip {:selected id ;; [:& grid-options-tooltip {:selected id
:title (tr "ds.multiselect-bar.copy-to-library") ;; :title (tr "ds.multiselect-bar.copy-to-library")
:on-select on-copy}]) ;; :on-select on-copy}])
i/copy] ;; i/copy]
[:span.move-item.tooltip.tooltip-top ;; [:span.move-item.tooltip.tooltip-top
{:alt (tr "ds.multiselect-bar.move") ;; {:alt (tr "ds.multiselect-bar.move")
:on-click on-toggle-move} ;; :on-click on-toggle-move}
(when (:show-move-tooltip @local) ;; (when (:show-move-tooltip @local)
[:& grid-options-tooltip {:selected id ;; [:& grid-options-tooltip {:selected id
:title (tr "ds.multiselect-bar.move-to-library") ;; :title (tr "ds.multiselect-bar.move-to-library")
:on-select on-move}]) ;; :on-select on-move}])
i/move] ;; i/move]
[:span.delete.tooltip.tooltip-top ;; [:span.delete.tooltip.tooltip-top
{:alt (tr "ds.multiselect-bar.delete") ;; {:alt (tr "ds.multiselect-bar.delete")
:on-click on-delete} ;; :on-click on-delete}
i/trash]] ;; i/trash]]
;;
;; if not editable ;; ;; if not editable
[:div.multiselect-nav ;; [:div.multiselect-nav
[:span.move-item.tooltip.tooltip-top ;; [:span.move-item.tooltip.tooltip-top
{:alt (tr "ds.multiselect-bar.copy") ;; {:alt (tr "ds.multiselect-bar.copy")
:on-click on-toggle-copy} ;; :on-click on-toggle-copy}
(when (:show-copy-tooltip @local) ;; (when (:show-copy-tooltip @local)
[:& grid-options-tooltip {:selected id ;; [:& grid-options-tooltip {:selected id
:title (tr "ds.multiselect-bar.copy-to-library") ;; :title (tr "ds.multiselect-bar.copy-to-library")
:on-select on-copy}]) ;; :on-select on-copy}])
i/organize]])]))) ;; i/organize]])])))
;;
(mf/defc grid-item ;; (mf/defc grid-item
[{:keys [color selected?] :as props}] ;; [{:keys [color selected?] :as props}]
(letfn [(toggle-selection [event] ;; (letfn [(toggle-selection [event]
(st/emit! (dc/toggle-color-selection color)))] ;; (st/emit! (dc/toggle-color-selection color)))]
[:div.grid-item.small-item.project-th {:on-click toggle-selection} ;; [:div.grid-item.small-item.project-th {:on-click toggle-selection}
[:span.color-swatch {:style {:background-color color}}] ;; [:span.color-swatch {:style {:background-color color}}]
[:div.input-checkbox.check-primary ;; [:div.input-checkbox.check-primary
[:input {:type "checkbox" ;; [:input {:type "checkbox"
:id color ;; :id color
:on-change toggle-selection ;; :on-change toggle-selection
:checked selected?}] ;; :checked selected?}]
[:label {:for color}]] ;; [:label {:for color}]]
[:span.color-data color] ;; [:span.color-data color]
[:span.color-data (apply str "RGB " (interpose ", " (hex->rgb color)))]])) ;; [:span.color-data (apply str "RGB " (interpose ", " (hex->rgb color)))]]))
;;
(mf/defc grid ;; (mf/defc grid
[{:keys [id type coll selected] :as props}] ;; [{:keys [id type coll selected] :as props}]
(let [{:keys [colors]} coll ;; (let [{:keys [colors]} coll
editable? (= :own type) ;; editable? (= :own type)
colors (->> (remove nil? colors) ;; colors (->> (remove nil? colors)
(sort-by identity))] ;; (sort-by identity))]
[:div.dashboard-grid-content ;; [:div.dashboard-grid-content
[:div.dashboard-grid-row ;; [:div.dashboard-grid-row
(when (and editable? id) ;; (when (and editable? id)
[:& grid-form {:id id}]) ;; [:& grid-form {:id id}])
(for [color colors] ;; (for [color colors]
(let [selected? (contains? selected color)] ;; (let [selected? (contains? selected color)]
[:& grid-item {:color color :selected? selected? :key color}]))]])) ;; [:& grid-item {:color color :selected? selected? :key color}]))]]))
;;
(mf/defc content ;; (mf/defc content
[{:keys [id type coll] :as props}] ;; [{:keys [id type coll] :as props}]
(let [selected (mf/deref selected-colors-iref)] ;; (let [selected (mf/deref selected-colors-iref)]
[:section.dashboard-grid.library ;; [:section.dashboard-grid.library
(when coll ;; (when coll
[:& grid-header {:coll coll}]) ;; [:& grid-header {:coll coll}])
[:& grid {:coll coll :id id :type type :selected selected}] ;; [:& grid {:coll coll :id id :type type :selected selected}]
(when (seq selected) ;; (when (seq selected)
[:& grid-options {:id id :type type ;; [:& grid-options {:id id :type type
:selected selected ;; :selected selected
:coll coll}])])) ;; :coll coll}])]))
;;
;; --- Colors Page ;; ;; --- Colors Page
;;
(mf/defc colors-page ;; (mf/defc colors-page
[{:keys [id type] :as props}] ;; [{:keys [id type] :as props}]
(let [type (or type :own) ;; (let [type (or type :own)
;;
colls (mf/deref collections-iref) ;; colls (mf/deref collections-iref)
colls (cond->> (vals colls) ;; colls (cond->> (vals colls)
(= type :own) (filter #(= :own (:type %))) ;; (= type :own) (filter #(= :own (:type %)))
(= type :builtin) (filter #(= :builtin (:type %))) ;; (= type :builtin) (filter #(= :builtin (:type %)))
true (sort-by :created-at)) ;; true (sort-by :created-at))
selected-coll (if id ;; selected-coll (if id
(seek #(= id (:id %)) colls) ;; (seek #(= id (:id %)) colls)
(first colls)) ;; (first colls))
id (:id selected-coll)] ;; id (:id selected-coll)]
;;
(mf/use-effect #(st/emit! (dc/fetch-collections))) ;; (mf/use-effect #(st/emit! (dc/fetch-collections)))
;;
[:section.dashboard-content ;; [:section.dashboard-content
[:& nav {:type type ;; [:& nav {:type type
:id id ;; :id id
:colls colls}] ;; :colls colls}]
[:& content {:type type ;; [:& content {:type type
:id id ;; :id id
:coll selected-coll}]])) ;; :coll selected-coll}]]))
;;

View file

@ -1,15 +0,0 @@
(ns uxbox.main.ui.dashboard.components.context-menu
(:require
[rumext.alpha :as mf]
[uxbox.util.uuid :as uuid]))
(mf/defc context-menu
[{ :keys [ is-open options ]}]
[:div.context-menu
{ :class-name (when is-open "is-open")}
[:ul.context-menu-items
(for [[action-name action-handler] options]
[:li.context-menu-item
{ :key (uuid/next)}
[:a.context-menu-action {:on-click action-handler} action-name]])]])

View file

@ -28,360 +28,360 @@
[uxbox.util.router :as rt] [uxbox.util.router :as rt]
[uxbox.util.time :as dt])) [uxbox.util.time :as dt]))
;; --- Page Title ;; ;; --- Page Title
;;
(mf/defc grid-header ;; (mf/defc grid-header
[{:keys [collection] :as props}] ;; [{:keys [collection] :as props}]
(let [{:keys [id type]} collection ;; (let [{:keys [id type]} collection
on-change #(st/emit! (di/rename-collection id %)) ;; on-change #(st/emit! (di/rename-collection id %))
on-deleted #(st/emit! (rt/nav :dashboard-images nil {:type type})) ;; on-deleted #(st/emit! (rt/nav :dashboard-images nil {:type type}))
delete #(st/emit! (di/delete-collection id on-deleted)) ;; delete #(st/emit! (di/delete-collection id on-deleted))
on-delete #(modal/show! confirm-dialog {:on-accept delete})] ;; on-delete #(modal/show! confirm-dialog {:on-accept delete})]
[:& common/grid-header {:value (:name collection) ;; [:& common/grid-header {:value (:name collection)
:on-change on-change ;; :on-change on-change
:on-delete on-delete}])) ;; :on-delete on-delete}]))
;;
;; --- Nav ;; ;; --- Nav
;;
(mf/defc nav-item ;; (mf/defc nav-item
[{:keys [coll selected?] :as props}] ;; [{:keys [coll selected?] :as props}]
(let [local (mf/use-state {}) ;; (let [local (mf/use-state {})
{:keys [id type name num-images]} coll ;; {:keys [id type name num-images]} coll
editable? (= type :own) ;; editable? (= type :own)
;;
on-click ;; on-click
(fn [event] ;; (fn [event]
(let [type (or type :own)] ;; (let [type (or type :own)]
(st/emit! (rt/nav :dashboard-images {} {:type type :id id})))) ;; (st/emit! (rt/nav :dashboard-images {} {:type type :id id}))))
;;
on-cancel-edition #(swap! local dissoc :edit) ;; on-cancel-edition #(swap! local dissoc :edit)
on-double-click #(when editable? (swap! local assoc :edit true)) ;; on-double-click #(when editable? (swap! local assoc :edit true))
;;
on-input-keyup ;; on-input-keyup
(fn [event] ;; (fn [event]
(when (kbd/enter? event) ;; (when (kbd/enter? event)
(let [value (-> (dom/get-target event) ;; (let [value (-> (dom/get-target event)
(dom/get-value) ;; (dom/get-value)
(str/trim))] ;; (str/trim))]
(st/emit! (di/rename-collection id value)) ;; (st/emit! (di/rename-collection id value))
(swap! local assoc :edit false))))] ;; (swap! local assoc :edit false))))]
;;
[:li {:on-click on-click ;; [:li {:on-click on-click
:on-double-click on-double-click ;; :on-double-click on-double-click
:class-name (when selected? "current")} ;; :class-name (when selected? "current")}
(if (:edit @local) ;; (if (:edit @local)
[:div
[:input.element-title {:default-value name
:on-key-down on-input-keyup}]
[:span.close {:on-click on-cancel-edition} i/close]]
[:span.element-title (if id name "Storage")])]))
(mf/defc nav
[{:keys [id type collections] :as props}]
(let [locale (i18n/use-locale)
own? (= type :own)
builtin? (= type :builtin)
create-collection #(st/emit! di/create-collection)
select-own-tab #(st/emit! (rt/nav :dashboard-images nil {:type :own}))
select-buitin-tab #(st/emit! (rt/nav :dashboard-images nil {:type :builtin}))]
[:div.library-bar
[:div.library-bar-inside
;; Tabs
[:ul.library-tabs
[:li {:class (when own? "current")
:on-click select-own-tab}
(t locale "ds.your-images-title")]
[:li {:class (when builtin? "current")
:on-click select-buitin-tab}
(t locale "ds.store-images-title")]]
;; Collections List
[:ul.library-elements
(when own?
[:li
[:a.btn-primary {:on-click create-collection}
(t locale "ds.images-collection.new")]])
(for [item collections]
[:& nav-item {:coll item
:selected? (= (:id item) id)
:key (:id item)}])]]]))
;; --- Grid
;; (mf/defc grid-options-tooltip
;; [{:keys [selected on-select title] :as props}]
;; {:pre [(uuid? selected)
;; (fn? on-select)
;; (string? title)]}
;; (let [colls (mf/deref collections-iref)
;; colls (->> (vals colls)
;; (filter #(= :own (:type %)))
;; (remove #(= selected (:id %)))
;; #_(sort-by :name colls))
;; on-select (fn [event id]
;; (dom/prevent-default event)
;; (dom/stop-propagation event)
;; (on-select id))]
;; [:ul.move-list
;; [:li.title title]
;; [:li
;; (when (not (nil? selected))
;; [:a {:href "#" :on-click #(on-select % nil)} "Storage"])]
;; (for [{:keys [id name] :as coll} colls]
;; [:li {:key (pr-str id)}
;; [:a {:on-click #(on-select % id)} name]])]))
(mf/defc grid-options
[{:keys [id type selected] :as props}]
(let [local (mf/use-state {})
delete #(st/emit! di/delete-selected)
on-delete #(modal/show! confirm-dialog {:on-accept delete})
;; (on-toggle-copy [event]
;; (swap! local update :show-copy-tooltip not))
;; (on-toggle-move [event]
;; (swap! local update :show-move-tooltip not))
;; (on-copy [selected]
;; (swap! local assoc
;; :show-move-tooltip false
;; :show-copy-tooltip false)
;; (st/emit! (di/copy-selected selected)))
;; (on-move [selected]
;; (swap! local assoc
;; :show-move-tooltip false
;; :show-copy-tooltip false)
;; (st/emit! (di/move-selected selected)))
;; (on-rename [event]
;; (let [selected (first selected)]
;; (st/emit! (di/update-opts :edition selected))))
]
;; MULTISELECT OPTIONS BAR
[:div.multiselect-bar
(when (= type :own)
;; If editable
[:div.multiselect-nav
;; [:span.move-item.tooltip.tooltip-top
;; {:alt (tr "ds.multiselect-bar.copy")
;; :on-click on-toggle-copy}
;; (when (:show-copy-tooltip @local)
;; [:& grid-options-tooltip {:selected id
;; :title (tr "ds.multiselect-bar.copy-to-library")
;; :on-select on-copy}])
;; i/copy]
;; [:span.move-item.tooltip.tooltip-top
;; {:alt (tr "ds.multiselect-bar.move")
;; :on-click on-toggle-move}
;; (when (:show-move-tooltip @local)
;; [:& grid-options-tooltip {:selected id
;; :title (tr "ds.multiselect-bar.move-to-library")
;; :on-select on-move}])
;; i/move]
;; (when (= 1 (count selected))
;; [:span.move-item.tooltip.tooltip-top {:alt (tr "ds.multiselect-bar.rename")
;; :on-click on-rename}
;; i/pencil])
[:span.delete.tooltip.tooltip-top
{:alt (tr "ds.multiselect-bar.delete")
:on-click on-delete}
i/trash]]
;; If not editable
;; [:div.multiselect-nav
;; [:span.move-item.tooltip.tooltip-top {:alt (tr "ds.multiselect-bar.copy")
;; :on-click on-toggle-copy}
;; (when (:show-copy-tooltip @local)
;; [:& grid-options-tooltip {:selected id
;; :title (tr "ds.multiselect-bar.copy-to-library")
;; :on-select on-copy}])
;; i/organize]]
)]))
;; --- Grid Form
(mf/defc grid-form
[{:keys [id type uploading?] :as props}]
(let [input (mf/use-ref nil)
on-click #(dom/click (mf/ref-node input))
on-select #(st/emit! (->> (dom/get-target %)
(dom/get-files)
(array-seq)
(di/create-images id)))]
[:div.grid-item.add-project {:on-click on-click}
(if uploading?
[:div i/loader-pencil]
[:span (tr "ds.image-new")])
[:input.upload-image-input
{:style {:display "none"}
:multiple true
:ref input
:value ""
:accept "image/jpeg,image/png,image/webp"
:type "file"
:on-change on-select}]]))
;; --- Grid Item
(mf/defc grid-item
[{:keys [image selected? edition?] :as props}]
(let [toggle-selection #(st/emit! (if selected?
(di/deselect-image (:id image))
(di/select-image (:id image))))
on-blur
(fn [event]
(let [target (dom/get-target event)
name (dom/get-value target)]
(st/emit! (di/update-opts :edition false)
(di/rename-image (:id image) name))))
on-key-down
(fn [event]
(when (kbd/enter? event)
(on-blur event)))
on-edit
(fn [event]
(dom/stop-propagation event)
(dom/prevent-default event)
(st/emit! (di/update-opts :edition (:id image))))
background (str "url('" (:thumb-uri image) "')")]
[:div.grid-item.images-th
[:div.grid-item-th {:style {:background-image background}}
[:div.input-checkbox.check-primary
[:input {:type "checkbox"
:id (:id image)
:on-change toggle-selection
:checked selected?}]
[:label {:for (:id image)}]]]
[:div.item-info
(if edition?
[:input.element-name {:type "text"
:auto-focus true
:on-key-down on-key-down
:on-blur on-blur
:on-click on-edit
:default-value (:name image)}]
[:h3 {:on-double-click on-edit} (:name image)])
[:span.date (tr "ds.uploaded-at" (dt/format (:created-at image) "dd/MM/yyyy"))]]]))
;; --- Grid
;; (defn- make-images-iref
;; [collection-id]
;; (letfn [(selector [state]
;; (->> (vals (:images state))
;; (filterv #(= (:collection-id %) collection-id))))]
;; (-> (l/lens selector)
;; (l/derive st/state))))
(def images-iref
(-> (comp (l/key :images) (l/lens vals))
(l/derive st/state)))
(mf/defc grid
[{:keys [id type collection opts] :as props}]
(let [editable? (= type :own)
;; images-iref (mf/use-memo {:fn #(make-images-iref id)
;; :deps (mf/deps id)})
images (->> (mf/deref images-iref)
(sort-by :created-at))]
[:div.dashboard-grid-content
[:div.dashboard-grid-row
(when editable?
[:& grid-form {:id id :type type :uploading? (:uploading opts)}])
(for [item images]
[:& grid-item {:image item
:key (:id item)
:selected? (contains? (:selected opts) (:id item))
:edition? (= (:edition opts) (:id item))}])]]))
;; --- Menu
;; (mf/defc menu
;; [{:keys [opts coll] :as props}]
;; (let [ordering (:order opts :name)
;; filtering (:filter opts "")
;; icount (count (:images coll))]
;; (letfn [(on-term-change [event]
;; (let [term (-> (dom/get-target event)
;; (dom/get-value))]
;; (st/emit! (di/update-opts :filter term))))
;; (on-ordering-change [event]
;; (let [value (dom/event->value event)
;; value (read-string value)]
;; (st/emit! (di/update-opts :order value))))
;; (on-clear [event]
;; (st/emit! (di/update-opts :filter "")))]
;; [:section.dashboard-bar.library-gap
;; [:div.dashboard-info
;; ;; Counter
;; [:span.dashboard-images (tr "ds.num-images" (t/c icount))]
;; ;; Sorting
;; [:div ;; [:div
;; [:span (tr "ds.ordering")] ;; [:input.element-title {:default-value name
;; [:select.input-select {:on-change on-ordering-change ;; :on-key-down on-input-keyup}]
;; :value (pr-str ordering)} ;; [:span.close {:on-click on-cancel-edition} i/close]]
;; (for [[key value] (seq +ordering-options+)] ;; [:span.element-title (if id name "Storage")])]))
;; [:option {:key key :value (pr-str key)} (tr value)])]] ;;
;; (mf/defc nav
;; ;; Search ;; [{:keys [id type collections] :as props}]
;; [:form.dashboard-search ;; (let [locale (i18n/use-locale)
;; [:input.input-text {:key :images-search-box ;; own? (= type :own)
;; :type "text" ;; builtin? (= type :builtin)
;; :on-change on-term-change ;; create-collection #(st/emit! di/create-collection)
;; select-own-tab #(st/emit! (rt/nav :dashboard-images nil {:type :own}))
;; select-buitin-tab #(st/emit! (rt/nav :dashboard-images nil {:type :builtin}))]
;; [:div.library-bar
;; [:div.library-bar-inside
;;
;; ;; Tabs
;; [:ul.library-tabs
;; [:li {:class (when own? "current")
;; :on-click select-own-tab}
;; (t locale "ds.your-images-title")]
;;
;; [:li {:class (when builtin? "current")
;; :on-click select-buitin-tab}
;; (t locale "ds.store-images-title")]]
;;
;; ;; Collections List
;; [:ul.library-elements
;; (when own?
;; [:li
;; [:a.btn-primary {:on-click create-collection}
;; (t locale "ds.images-collection.new")]])
;;
;; (for [item collections]
;; [:& nav-item {:coll item
;; :selected? (= (:id item) id)
;; :key (:id item)}])]]]))
;;
;; ;; --- Grid
;;
;; ;; (mf/defc grid-options-tooltip
;; ;; [{:keys [selected on-select title] :as props}]
;; ;; {:pre [(uuid? selected)
;; ;; (fn? on-select)
;; ;; (string? title)]}
;; ;; (let [colls (mf/deref collections-iref)
;; ;; colls (->> (vals colls)
;; ;; (filter #(= :own (:type %)))
;; ;; (remove #(= selected (:id %)))
;; ;; #_(sort-by :name colls))
;; ;; on-select (fn [event id]
;; ;; (dom/prevent-default event)
;; ;; (dom/stop-propagation event)
;; ;; (on-select id))]
;; ;; [:ul.move-list
;; ;; [:li.title title]
;; ;; [:li
;; ;; (when (not (nil? selected))
;; ;; [:a {:href "#" :on-click #(on-select % nil)} "Storage"])]
;; ;; (for [{:keys [id name] :as coll} colls]
;; ;; [:li {:key (pr-str id)}
;; ;; [:a {:on-click #(on-select % id)} name]])]))
;;
;; (mf/defc grid-options
;; [{:keys [id type selected] :as props}]
;; (let [local (mf/use-state {})
;; delete #(st/emit! di/delete-selected)
;; on-delete #(modal/show! confirm-dialog {:on-accept delete})
;;
;; ;; (on-toggle-copy [event]
;; ;; (swap! local update :show-copy-tooltip not))
;; ;; (on-toggle-move [event]
;; ;; (swap! local update :show-move-tooltip not))
;; ;; (on-copy [selected]
;; ;; (swap! local assoc
;; ;; :show-move-tooltip false
;; ;; :show-copy-tooltip false)
;; ;; (st/emit! (di/copy-selected selected)))
;; ;; (on-move [selected]
;; ;; (swap! local assoc
;; ;; :show-move-tooltip false
;; ;; :show-copy-tooltip false)
;; ;; (st/emit! (di/move-selected selected)))
;; ;; (on-rename [event]
;; ;; (let [selected (first selected)]
;; ;; (st/emit! (di/update-opts :edition selected))))
;; ]
;; ;; MULTISELECT OPTIONS BAR
;; [:div.multiselect-bar
;; (when (= type :own)
;; ;; If editable
;; [:div.multiselect-nav
;; ;; [:span.move-item.tooltip.tooltip-top
;; ;; {:alt (tr "ds.multiselect-bar.copy")
;; ;; :on-click on-toggle-copy}
;; ;; (when (:show-copy-tooltip @local)
;; ;; [:& grid-options-tooltip {:selected id
;; ;; :title (tr "ds.multiselect-bar.copy-to-library")
;; ;; :on-select on-copy}])
;; ;; i/copy]
;; ;; [:span.move-item.tooltip.tooltip-top
;; ;; {:alt (tr "ds.multiselect-bar.move")
;; ;; :on-click on-toggle-move}
;; ;; (when (:show-move-tooltip @local)
;; ;; [:& grid-options-tooltip {:selected id
;; ;; :title (tr "ds.multiselect-bar.move-to-library")
;; ;; :on-select on-move}])
;; ;; i/move]
;; ;; (when (= 1 (count selected))
;; ;; [:span.move-item.tooltip.tooltip-top {:alt (tr "ds.multiselect-bar.rename")
;; ;; :on-click on-rename}
;; ;; i/pencil])
;; [:span.delete.tooltip.tooltip-top
;; {:alt (tr "ds.multiselect-bar.delete")
;; :on-click on-delete}
;; i/trash]]
;;
;; ;; If not editable
;; ;; [:div.multiselect-nav
;; ;; [:span.move-item.tooltip.tooltip-top {:alt (tr "ds.multiselect-bar.copy")
;; ;; :on-click on-toggle-copy}
;; ;; (when (:show-copy-tooltip @local)
;; ;; [:& grid-options-tooltip {:selected id
;; ;; :title (tr "ds.multiselect-bar.copy-to-library")
;; ;; :on-select on-copy}])
;; ;; i/organize]]
;; )]))
;;
;;
;; ;; --- Grid Form
;;
;; (mf/defc grid-form
;; [{:keys [id type uploading?] :as props}]
;; (let [input (mf/use-ref nil)
;; on-click #(dom/click (mf/ref-node input))
;; on-select #(st/emit! (->> (dom/get-target %)
;; (dom/get-files)
;; (array-seq)
;; (di/create-images id)))]
;; [:div.grid-item.add-project {:on-click on-click}
;; (if uploading?
;; [:div i/loader-pencil]
;; [:span (tr "ds.image-new")])
;; [:input.upload-image-input
;; {:style {:display "none"}
;; :multiple true
;; :ref input
;; :value ""
;; :accept "image/jpeg,image/png,image/webp"
;; :type "file"
;; :on-change on-select}]]))
;;
;; ;; --- Grid Item
;;
;; (mf/defc grid-item
;; [{:keys [image selected? edition?] :as props}]
;; (let [toggle-selection #(st/emit! (if selected?
;; (di/deselect-image (:id image))
;; (di/select-image (:id image))))
;; on-blur
;; (fn [event]
;; (let [target (dom/get-target event)
;; name (dom/get-value target)]
;; (st/emit! (di/update-opts :edition false)
;; (di/rename-image (:id image) name))))
;;
;; on-key-down
;; (fn [event]
;; (when (kbd/enter? event)
;; (on-blur event)))
;;
;; on-edit
;; (fn [event]
;; (dom/stop-propagation event)
;; (dom/prevent-default event)
;; (st/emit! (di/update-opts :edition (:id image))))
;;
;; background (str "url('" (:thumb-uri image) "')")]
;;
;; [:div.grid-item.images-th
;; [:div.grid-item-th {:style {:background-image background}}
;; [:div.input-checkbox.check-primary
;; [:input {:type "checkbox"
;; :id (:id image)
;; :on-change toggle-selection
;; :checked selected?}]
;; [:label {:for (:id image)}]]]
;;
;; [:div.item-info
;; (if edition?
;; [:input.element-name {:type "text"
;; :auto-focus true ;; :auto-focus true
;; :placeholder (tr "ds.search.placeholder") ;; :on-key-down on-key-down
;; :value filtering}] ;; :on-blur on-blur
;; [:div.clear-search {:on-click on-clear} i/close]]]]))) ;; :on-click on-edit
;; :default-value (:name image)}]
(def opts-iref ;; [:h3 {:on-double-click on-edit} (:name image)])
(-> (l/key :dashboard-images) ;; [:span.date (tr "ds.uploaded-at" (dt/format (:created-at image) "dd/MM/yyyy"))]]]))
(l/derive st/state))) ;;
;; ;; --- Grid
(mf/defc content ;;
[{:keys [id type collection] :as props}] ;; ;; (defn- make-images-iref
(let [{:keys [selected] :as opts} (mf/deref opts-iref)] ;; ;; [collection-id]
[:section.dashboard-grid.library ;; ;; (letfn [(selector [state]
(when collection ;; ;; (->> (vals (:images state))
[:& grid-header {:collection collection}]) ;; ;; (filterv #(= (:collection-id %) collection-id))))]
(if collection ;; ;; (-> (l/lens selector)
[:& grid {:id id :type type :collection collection :opts opts}] ;; ;; (l/derive st/state))))
[:span "EMPTY STATE TODO"]) ;;
(when-not (empty? selected) ;; (def images-iref
[:& grid-options {:id id :type type :selected selected}])])) ;; (-> (comp (l/key :images) (l/lens vals))
;; (l/derive st/state)))
;; --- Images Page ;;
;; (mf/defc grid
(def collections-iref ;; [{:keys [id type collection opts] :as props}]
(-> (l/key :images-collections) ;; (let [editable? (= type :own)
(l/derive st/state))) ;; ;; images-iref (mf/use-memo {:fn #(make-images-iref id)
;; ;; :deps (mf/deps id)})
(mf/defc images-page ;; images (->> (mf/deref images-iref)
[{:keys [id type] :as props}] ;; (sort-by :created-at))]
(let [collections (mf/deref collections-iref) ;; [:div.dashboard-grid-content
collections (cond->> (vals collections) ;; [:div.dashboard-grid-row
(= type :own) (filter #(= :own (:type %))) ;; (when editable?
(= type :builtin) (filter #(= :builtin (:type %))) ;; [:& grid-form {:id id :type type :uploading? (:uploading opts)}])
true (sort-by :created-at)) ;; (for [item images]
;; [:& grid-item {:image item
collection (cond ;; :key (:id item)
(uuid? id) (d/seek #(= id (:id %)) collections) ;; :selected? (contains? (:selected opts) (:id item))
:else (first collections)) ;; :edition? (= (:edition opts) (:id item))}])]]))
id (:id collection)] ;;
;; ;; --- Menu
(mf/use-effect #(st/emit! di/fetch-collections)) ;;
(mf/use-effect ;; ;; (mf/defc menu
{:fn #(when id (st/emit! (di/initialize id))) ;; ;; [{:keys [opts coll] :as props}]
:deps (mf/deps id)}) ;; ;; (let [ordering (:order opts :name)
;; ;; filtering (:filter opts "")
[:section.dashboard-content ;; ;; icount (count (:images coll))]
[:& nav {:type type :id id :collections collections}] ;; ;; (letfn [(on-term-change [event]
[:& content {:type type :id id :collection collection}]])) ;; ;; (let [term (-> (dom/get-target event)
;; ;; (dom/get-value))]
;; ;; (st/emit! (di/update-opts :filter term))))
;; ;; (on-ordering-change [event]
;; ;; (let [value (dom/event->value event)
;; ;; value (read-string value)]
;; ;; (st/emit! (di/update-opts :order value))))
;; ;; (on-clear [event]
;; ;; (st/emit! (di/update-opts :filter "")))]
;; ;; [:section.dashboard-bar.library-gap
;; ;; [:div.dashboard-info
;;
;; ;; ;; Counter
;; ;; [:span.dashboard-images (tr "ds.num-images" (t/c icount))]
;;
;; ;; ;; Sorting
;; ;; [:div
;; ;; [:span (tr "ds.ordering")]
;; ;; [:select.input-select {:on-change on-ordering-change
;; ;; :value (pr-str ordering)}
;; ;; (for [[key value] (seq +ordering-options+)]
;; ;; [:option {:key key :value (pr-str key)} (tr value)])]]
;;
;; ;; ;; Search
;; ;; [:form.dashboard-search
;; ;; [:input.input-text {:key :images-search-box
;; ;; :type "text"
;; ;; :on-change on-term-change
;; ;; :auto-focus true
;; ;; :placeholder (tr "ds.search.placeholder")
;; ;; :value filtering}]
;; ;; [:div.clear-search {:on-click on-clear} i/close]]]])))
;;
;; (def opts-iref
;; (-> (l/key :dashboard-images)
;; (l/derive st/state)))
;;
;; (mf/defc content
;; [{:keys [id type collection] :as props}]
;; (let [{:keys [selected] :as opts} (mf/deref opts-iref)]
;; [:section.dashboard-grid.library
;; (when collection
;; [:& grid-header {:collection collection}])
;; (if collection
;; [:& grid {:id id :type type :collection collection :opts opts}]
;; [:span "EMPTY STATE TODO"])
;; (when-not (empty? selected)
;; [:& grid-options {:id id :type type :selected selected}])]))
;;
;; ;; --- Images Page
;;
;; (def collections-iref
;; (-> (l/key :images-collections)
;; (l/derive st/state)))
;;
;; (mf/defc images-page
;; [{:keys [id type] :as props}]
;; (let [collections (mf/deref collections-iref)
;; collections (cond->> (vals collections)
;; (= type :own) (filter #(= :own (:type %)))
;; (= type :builtin) (filter #(= :builtin (:type %)))
;; true (sort-by :created-at))
;;
;; collection (cond
;; (uuid? id) (d/seek #(= id (:id %)) collections)
;; :else (first collections))
;; id (:id collection)]
;;
;; (mf/use-effect #(st/emit! di/fetch-collections))
;; (mf/use-effect
;; {:fn #(when id (st/emit! (di/initialize id)))
;; :deps (mf/deps id)})
;;
;; [:section.dashboard-content
;; [:& nav {:type type :id id :collections collections}]
;; [:& content {:type type :id id :collection collection}]]))

View file

@ -16,13 +16,77 @@
[uxbox.util.router :as rt] [uxbox.util.router :as rt]
[uxbox.util.i18n :as i18n :refer [t tr]] [uxbox.util.i18n :as i18n :refer [t tr]]
[uxbox.util.color :as uc] [uxbox.util.color :as uc]
[uxbox.util.dom :as dom]
[uxbox.main.data.icons :as dico] [uxbox.main.data.icons :as dico]
[uxbox.main.data.images :as dimg] [uxbox.main.data.images :as dimg]
[uxbox.main.data.colors :as dcol] [uxbox.main.data.colors :as dcol]
[uxbox.builtins.icons :as i] [uxbox.builtins.icons :as i]
[uxbox.main.store :as st] [uxbox.main.store :as st]
[uxbox.main.refs :as refs] [uxbox.main.refs :as refs]
[uxbox.main.ui.dashboard.components.context-menu :refer [context-menu]])) [uxbox.main.ui.components.context-menu :refer [context-menu]]
[uxbox.main.ui.modal :as modal]
[uxbox.main.ui.confirm :refer [confirm-dialog]]
[uxbox.main.ui.colorpicker :refer [colorpicker most-used-colors]]
))
(mf/defc modal-create-color
[{:keys [on-accept on-cancel] :as ctx}]
(let [state (mf/use-state { :current-color "#406280" })]
(letfn [(accept [event]
(dom/prevent-default event)
(modal/hide!)
(when on-accept (on-accept (:current-color @state))))
(cancel [event]
(dom/prevent-default event)
(modal/hide!)
(when on-cancel (on-cancel)))]
[:div.modal-create-color
[:h3.modal-create-color-title (tr "modal.create-color.new-color")]
[:& colorpicker {:value (:current-color @state)
:colors (into-array @most-used-colors)
:on-change #(swap! state assoc :current-color %)}]
[:input.btn-primary {:type "button"
:value (tr "ds.button.save")
:on-click accept}]
[:a.close {:href "#" :on-click cancel} i/close]])))
(defmulti create-library (fn [x _] x))
(defmethod create-library :icons [_ team-id]
(let [name (str "Icon Library "(gensym "l"))]
(st/emit! (dico/create-icon-library team-id name))))
(defmethod create-library :images [_ team-id]
(let [name (str "Image Library "(gensym "l"))]
(st/emit! (dimg/create-image-library team-id name))))
(defmethod create-library :palettes [_ team-id]
(let [name (str "Image Library "(gensym "l"))]
(st/emit! (dcol/create-color-library team-id name))))
(defmulti create-item (fn [x _ _] x))
(defmethod create-item :icons [_ library-id data]
(let [files (->> data
(dom/get-target)
(dom/get-files)
(array-seq))]
(st/emit! (dico/create-icons library-id files))))
(defmethod create-item :images [_ library-id data]
(let [files (->> data
(dom/get-target)
(dom/get-files)
(array-seq))]
(st/emit! (dimg/create-images library-id files))))
(defmethod create-item :palettes [_ library-id]
(letfn [(dispatch-color [color]
(st/emit! (dcol/create-color library-id color)))]
(modal/show! modal-create-color {:on-accept dispatch-color})))
(mf/defc library-header (mf/defc library-header
[{:keys [section team-id] :as props}] [{:keys [section team-id] :as props}]
@ -51,22 +115,23 @@
(let [locale (i18n/use-locale)] (let [locale (i18n/use-locale)]
[:aside.library-sidebar [:aside.library-sidebar
[:button.library-sidebar-add-item [:button.library-sidebar-add-item
{:type "button"} {:type "button"
:on-click #(create-library section team-id)}
(t locale (str "dashboard.library.add-library." (name section)))] (t locale (str "dashboard.library.add-library." (name section)))]
[:ul.library-sidebar-list [:ul.library-sidebar-list
(for [item items] (for [item items]
[:li.library-sidebar-list-element [:li.library-sidebar-list-element
{:key (:id item) {:key (:id item)
:class-name (when (= library-id (:id item)) "current") :class-name (when (= library-id (:id item)) "current")
:on-click (fn [] (let [path (keyword (str "dashboard-library-" (name section))) :on-click
route (rt/nav path {:team-id team-id (fn []
:library-id (:id item)})] (let [path (keyword (str "dashboard-library-" (name section)))]
(dico/fetch-icon-library (:id item)) (dico/fetch-icon-library (:id item))
(st/emit! route)))} (st/emit! (rt/nav path {:team-id team-id :library-id (:id item)}))))}
[:a (:name item)]])]])) [:a (:name item)]])]]))
(mf/defc library-top-menu (mf/defc library-top-menu
[{:keys [selected section]}] [{:keys [selected section library-id]}]
(let [state (mf/use-state {:is-open false}) (let [state (mf/use-state {:is-open false})
locale (i18n/use-locale)] locale (i18n/use-locale)]
[:header.library-top-menu [:header.library-top-menu
@ -75,13 +140,27 @@
[:a.library-top-menu-current-action [:a.library-top-menu-current-action
{ :on-click #(swap! state update :is-open not)} { :on-click #(swap! state update :is-open not)}
[:span i/arrow-down]] [:span i/arrow-down]]
[:& context-menu {:is-open (:is-open @state) [:& context-menu
{:show (:is-open @state)
:on-close #(swap! state update :is-open not)
:options [[(t locale "ds.button.rename") #(println "Rename")] :options [[(t locale "ds.button.rename") #(println "Rename")]
[(t locale "ds.button.delete") #(println "Delete")]]}]] [(t locale "ds.button.delete") #(println "Delete")]]}]]
[:div.library-top-menu-actions [:div.library-top-menu-actions
[:a i/trash] [:a i/trash]
[:a.btn-dashboard (t locale (str "dashboard.library.add-item." (name section)))]]]))
(if (= section :palettes)
[:button.btn-dashboard
{:on-click #(create-item section library-id)}
(t locale (str "dashboard.library.add-item." (name section)))]
[:*
[:label {:for "file-upload" :class-name "btn-dashboard"}
(t locale (str "dashboard.library.add-item." (name section)))]
[:input {:on-change #(create-item section library-id %)
:id "file-upload" :type "file" :style {:display "none"}}]]
)]]))
(mf/defc library-icon-card (mf/defc library-icon-card
[{:keys [id name url content metadata]}] [{:keys [id name url content metadata]}]
@ -107,11 +186,13 @@
[:div.library-card-footer-menu [:div.library-card-footer-menu
{ :on-click #(swap! state update :is-open not) } { :on-click #(swap! state update :is-open not) }
i/actions] i/actions]
[:& context-menu {:is-open (:is-open @state) [:& context-menu
{:show (:is-open @state)
:on-close #(swap! state update :is-open not)
:options [[(t locale "ds.button.delete") #(println "Delete")]]}]]])) :options [[(t locale "ds.button.delete") #(println "Delete")]]}]]]))
(mf/defc library-image-card (mf/defc library-image-card
[{:keys [id name url]}] [{:keys [id name thumb-uri]}]
(let [locale (i18n/use-locale) (let [locale (i18n/use-locale)
state (mf/use-state {:is-open false})] state (mf/use-state {:is-open false})]
[:div.library-card.library-image [:div.library-card.library-image
@ -122,18 +203,21 @@
#_(:checked false)}] #_(:checked false)}]
[:label {:for (str "image-" id)}]] [:label {:for (str "image-" id)}]]
[:div.library-card-image [:div.library-card-image
[:img {:src url}]] [:img {:src thumb-uri}]]
[:div.library-card-footer [:div.library-card-footer
[:div.library-card-footer-name name] [:div.library-card-footer-name name]
[:div.library-card-footer-timestamp "Less than 5 seconds ago"] [:div.library-card-footer-timestamp "Less than 5 seconds ago"]
[:div.library-card-footer-menu [:div.library-card-footer-menu
{ :on-click #(swap! state update :is-open not) } { :on-click #(swap! state update :is-open not) }
i/actions] i/actions]
[:& context-menu {:is-open (:is-open @state) [:& context-menu
{:show (:is-open @state)
:on-close #(swap! state update :is-open not)
:options [[(t locale "ds.button.delete") #(println "Delete")]]}]]])) :options [[(t locale "ds.button.delete") #(println "Delete")]]}]]]))
(mf/defc library-color-card (mf/defc library-color-card
[{ :keys [ id content ] }] [{ :keys [ id content ] }]
(when content
(let [locale (i18n/use-locale) (let [locale (i18n/use-locale)
state (mf/use-state {:is-open false})] state (mf/use-state {:is-open false})]
[:div.library-card.library-color [:div.library-card.library-color
@ -153,8 +237,10 @@
[:div.library-card-footer-menu [:div.library-card-footer-menu
{ :on-click #(swap! state update :is-open not) } { :on-click #(swap! state update :is-open not) }
i/actions] i/actions]
[:& context-menu {:is-open (:is-open @state) [:& context-menu
:options [[(t locale "ds.button.delete") #(println "Delete")]]}]]])) {:show (:is-open @state)
:on-close #(swap! state update :is-open not)
:options [[(t locale "ds.button.delete") #(println "Delete")]]}]]])))
(def icon-libraries-ref (def icon-libraries-ref
(-> (comp (l/key :library) (l/key :icon-libraries)) (-> (comp (l/key :library) (l/key :icon-libraries))
@ -197,11 +283,11 @@
(when library-id (when library-id
[:section.library-content [:section.library-content
[:& library-top-menu {:selected selected-library :section section}] [:& library-top-menu {:selected selected-library :section section :library-id library-id}]
[:div.library-page-cards-container [:div.library-page-cards-container
(for [item items] (for [item items]
(let [item (assoc item :key (:id item))] (let [item (assoc item :key (:id item))]
(case section (case section
:icons [:& library-icon-card item] :icons [:& library-icon-card item]
:images [:& library-image-card { :name "Nicolas Cage" :url "https://www.placecage.com/200/200" }] :images [:& library-image-card item]
:palettes [:& library-color-card item ])))]])])) :palettes [:& library-color-card item ])))]])]))

View file

@ -125,5 +125,5 @@
(mf/defc colorpalette (mf/defc colorpalette
[props] [props]
(let [colls (mf/deref collections-iref)] (let [colls (mf/deref collections-iref)]
(mf/use-effect #(st/emit! (udc/fetch-collections))) #_(mf/use-effect #(st/emit! (udc/fetch-collections)))
[:& palette {:colls (vals colls)}])) [:& palette {:colls (vals colls)}]))

View file

@ -160,8 +160,8 @@
(d/read-string) (d/read-string)
(swap! local assoc :collection-id))] (swap! local assoc :collection-id))]
(mf/use-effect #(st/emit! udi/fetch-collections)) #_(mf/use-effect #(st/emit! udi/fetch-collections))
(mf/use-effect #_(mf/use-effect
{:deps (mf/deps collection-id) {:deps (mf/deps collection-id)
:fn #(when collection-id :fn #(when collection-id
(st/emit! (udi/fetch-images collection-id)))}) (st/emit! (udi/fetch-images collection-id)))})

View file

@ -46,7 +46,7 @@
(fn [event data] (fn [event data]
(st/emit! (dw/select-for-drawing :icon data)))] (st/emit! (dw/select-for-drawing :icon data)))]
(mf/use-effect #_(mf/use-effect
{:fn #(st/emit! (di/fetch-icons collection-id)) {:fn #(st/emit! (di/fetch-icons collection-id))
:deps (mf/deps collection-id)}) :deps (mf/deps collection-id)})
@ -79,7 +79,7 @@
(st/emit! (dw/select-for-drawing nil)) (st/emit! (dw/select-for-drawing nil))
(reset! selected val))] (reset! selected val))]
(mf/use-effect #_(mf/use-effect
{:fn #(st/emit! di/fetch-collections)}) {:fn #(st/emit! di/fetch-collections)})
[:div#form-figures.tool-window [:div#form-figures.tool-window