0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-01-24 23:49:45 -05:00

Images dashboard

This commit is contained in:
Jesús Espino 2016-05-11 23:46:24 +02:00
parent 963d22d930
commit b3e9e7d1aa
12 changed files with 778 additions and 89 deletions

View file

@ -15,6 +15,7 @@
[uxbox.repo :as rp]
[uxbox.data.projects :as dp]
[uxbox.data.colors :as dc]
[uxbox.data.images :as di]
[uxbox.util.data :refer (deep-merge)]))
;; --- Events

299
src/uxbox/data/images.cljs Normal file
View file

@ -0,0 +1,299 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) 2015-2016 Andrey Antukh <niwi@niwi.nz>
;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
(ns uxbox.data.images
(:require [cuerdas.core :as str]
[beicon.core :as rx]
[uuid.core :as uuid]
[uxbox.rstore :as rs]
[uxbox.state :as st]
[uxbox.state.images :as sti]
[uxbox.repo :as rp]))
;; --- Initialize
(declare fetch-collections)
(declare collections-fetched?)
(defrecord Initialize []
rs/EffectEvent
(-apply-effect [_ state]
(when-not (seq (:images-by-id state))
(reset! st/loader true)))
rs/WatchEvent
(-apply-watch [_ state s]
(let [images (seq (:images-by-id state))]
(if images
(rx/empty)
(rx/merge
(rx/of (fetch-collections))
(->> (rx/filter collections-fetched? s)
(rx/take 1)
(rx/do #(reset! st/loader false))
(rx/ignore)))))))
(defn initialize
[]
(Initialize.))
;; --- Color Collections Fetched
(defrecord CollectionFetched [items]
rs/UpdateEvent
(-apply-update [_ state]
(reduce sti/assoc-collection state items)))
(defn collections-fetched
[items]
(CollectionFetched. items))
;; --- Fetch Color Collections
(defrecord FetchCollections []
rs/WatchEvent
(-apply-watch [_ state s]
(->> (rp/req :fetch/image-collections)
(rx/map :payload)
(rx/map collections-fetched))))
(defn fetch-collections
[]
(FetchCollections.))
;; --- Collection Created
(defrecord CollectionCreated [item]
rs/UpdateEvent
(-apply-update [_ state]
(-> state
(sti/assoc-collection item)
(assoc-in [:dashboard :collection-id] (:id item))
(assoc-in [:dashboard :collection-type] :own))))
(defn collection-created
[item]
(CollectionCreated. item))
;; --- Create Collection
(defrecord CreateCollection []
rs/WatchEvent
(-apply-watch [_ state s]
(let [coll {:name "Unnamed collection"
:id (uuid/random)}]
(->> (rp/req :create/image-collection coll)
(rx/map :payload)
(rx/map collection-created)))))
(defn create-collection
[]
(CreateCollection.))
(defn collections-fetched?
[v]
(instance? CollectionFetched v))
;; --- Collection Updated
(defrecord CollectionUpdated [item]
rs/UpdateEvent
(-apply-update [_ state]
(sti/assoc-collection state item)))
(defn collection-updated
[item]
(CollectionUpdated. item))
;; --- Update Collection
(defrecord UpdateCollection [id]
rs/WatchEvent
(-apply-watch [_ state s]
(let [item (get-in state [:images-by-id id])]
(->> (rp/req :update/image-collection item)
(rx/map :payload)
(rx/map collection-updated)))))
(defn update-collection
[id]
(UpdateCollection. id))
;; --- Rename Collection
(defrecord RenameCollection [id name]
rs/UpdateEvent
(-apply-update [_ state]
(assoc-in state [:images-by-id id :name] name))
rs/WatchEvent
(-apply-watch [_ state s]
(rx/of (update-collection id))))
(defn rename-collection
[item name]
(RenameCollection. item name))
;; --- Delete Collection
(defrecord DeleteCollection [id]
rs/UpdateEvent
(-apply-update [_ state]
(sti/dissoc-collection state id))
rs/WatchEvent
(-apply-watch [_ state s]
(->> (rp/req :delete/image-collection id)
(rx/ignore))))
(defn delete-collection
[id]
(DeleteCollection. id))
;; --- Image Created
(defrecord ImageCreated [item]
rs/UpdateEvent
(-apply-update [_ state]
(update-in state [:images-by-id (:collection item) :images]
#(conj % item))))
(defn image-created
[item]
(ImageCreated. item))
;; --- Create Image
(defrecord CreateImages [coll-id files]
rs/WatchEvent
(-apply-watch [_ state s]
(let [image-data {:coll coll-id
:id (uuid/random)
:files files}]
(->> (rp/req :create/image image-data)
(rx/map :payload)
(rx/map image-created)))))
(defn create-images
[coll-id files]
(CreateImages. coll-id files))
;; --- Images Loaded
(defrecord ImagesLoaded [coll-id items]
rs/UpdateEvent
(-apply-update [_ state]
(assoc-in state [:images-by-id coll-id :images] (set items))))
(defn images-loaded
[coll-id items]
(ImagesLoaded. coll-id items))
;; --- Load Images
(defrecord LoadImages [coll-id]
rs/WatchEvent
(-apply-watch [_ state s]
(let [params {:coll coll-id}]
(->> (rp/req :fetch/images params)
(rx/map :payload)
(rx/map #(images-loaded coll-id %))))))
(defn load-images
[coll-id]
(LoadImages. coll-id))
;; --- Delete Images
(defrecord DeleteImage [coll-id image]
rs/UpdateEvent
(-apply-update [_ state]
(sti/dissoc-image state coll-id image))
rs/WatchEvent
(-apply-watch [_ state s]
(->> (rp/req :delete/image (:id image))
(rx/ignore))))
(defn delete-image
[coll-id image]
(DeleteImage. coll-id image))
;; --- Set Collection
(defrecord SetCollection [id]
rs/UpdateEvent
(-apply-update [_ state]
(assoc-in state [:dashboard :collection-id] id))
rs/WatchEvent
(-apply-watch [_ state s]
(rx/of (load-images id))))
(defn set-collection
[id]
(SetCollection. id))
;; --- Set Collection Type
(defrecord SetCollectionType [type]
rs/WatchEvent
(-apply-watch [_ state s]
(if (= type :builtin)
(rx/of (set-collection 1))
(let [colls (sort-by :id (vals (:images-by-id state)))]
(rx/of (set-collection (:id (first colls)))))))
rs/UpdateEvent
(-apply-update [_ state]
(as-> state $
(assoc-in $ [:dashboard :collection-type] type))))
(defn set-collection-type
[type]
{:pre [(contains? #{:builtin :own} type)]}
(SetCollectionType. type))
;; --- Helpers
(defn sort-images-by
[ordering projs]
(case ordering
:name (sort-by :name projs)
:created (reverse (sort-by :created-at projs))
projs))
(defn contains-term?
[phrase term]
(str/contains? (str/lower phrase) (str/trim (str/lower term))))
(defn filter-images-by
[term projs]
(if (str/blank? term)
projs
(filter #(contains-term? (:name %) term) projs)))
(defn set-images-ordering
[order]
(reify
rs/UpdateEvent
(-apply-update [_ state]
(assoc-in state [:dashboard :images-order] order))))
(defn set-images-filtering
[term]
(reify
rs/UpdateEvent
(-apply-update [_ state]
(assoc-in state [:dashboard :images-filter] term))))
(defn clear-images-filtering
[]
(reify
rs/UpdateEvent
(-apply-update [_ state]
(assoc-in state [:dashboard :images-filter] ""))))

View file

@ -7,6 +7,7 @@
(ns uxbox.library
(:require [uxbox.library.colors :as colors]
[uxbox.library.images :as images]
[uxbox.library.icons :as icons]
[uxbox.library.fonts :as fonts]
[uxbox.util.data :refer (index-by-id)]))
@ -31,6 +32,16 @@
(def ^:const +icon-collections-by-id+
(index-by-id icons/+collections+))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Images
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def ^:const +image-collections+
images/+collections+)
(def ^:const +image-collections-by-id+
(index-by-id images/+collections+))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Fonts
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View file

@ -0,0 +1,65 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) 2015-2016 Andrey Antukh <niwi@niwi.nz>
;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
(ns uxbox.library.images)
(def ^:static +collections+
[{:name "Generic 1"
:id 1
:builtin true
:images [{:id 1
:name "image_name.jpg"
:url "https://images.unsplash.com/photo-1455098934982-64c622c5e066?crop=entropy&fit=crop&fm=jpg&h=1025&ixjsv=2.1.0&ixlib=rb-0.3.5&q=80&w=1400"
:thumbnail "https://images.unsplash.com/photo-1455098934982-64c622c5e066?crop=entropy&fit=crop&fm=jpg&h=1025&ixjsv=2.1.0&ixlib=rb-0.3.5&q=80&w=1400"}
{:id 2
:name "image_name_long_name.jpg"
:url "https://images.unsplash.com/photo-1422393462206-207b0fbd8d6b?crop=entropy&fit=crop&fm=jpg&h=1025&ixjsv=2.1.0&ixlib=rb-0.3.5&q=80&w=1925"
:thumbnail "https://images.unsplash.com/photo-1422393462206-207b0fbd8d6b?crop=entropy&fit=crop&fm=jpg&h=1025&ixjsv=2.1.0&ixlib=rb-0.3.5&q=80&w=1925"}
{:id 3
:name "image_name.jpg"
:url "https://images.unsplash.com/photo-1441986380878-c4248f5b8b5b?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&w=1080&fit=max&s=486f09671860a11e70bdd0a45e7c5014"
:thumbnail "https://images.unsplash.com/photo-1441986380878-c4248f5b8b5b?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&w=1080&fit=max&s=486f09671860a11e70bdd0a45e7c5014"}
{:id 4
:name "image_name_big_long_name.jpg"
:url "https://images.unsplash.com/photo-1423768164017-3f27c066407f?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&w=1080&fit=max&s=712b919f3a2f6fc34f29040e8082b6d9"
:thumbnail "https://images.unsplash.com/photo-1423768164017-3f27c066407f?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&w=1080&fit=max&s=712b919f3a2f6fc34f29040e8082b6d9"}
{:id 5
:name "image_name.jpg"
:url "https://images.unsplash.com/photo-1456428199391-a3b1cb5e93ab?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&w=1080&fit=max&s=765abd6dc931b7184e9795d8494966ed"
:thumbnail "https://images.unsplash.com/photo-1456428199391-a3b1cb5e93ab?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&w=1080&fit=max&s=765abd6dc931b7184e9795d8494966ed"}
{:id 6
:name "image_name.jpg"
:url "https://images.unsplash.com/photo-1447678523326-1360892abab8?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&w=1080&fit=max&s=91663afcd23da14f76cc8229c1780d47"
:thumbnail "https://images.unsplash.com/photo-1447678523326-1360892abab8?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&w=1080&fit=max&s=91663afcd23da14f76cc8229c1780d47"}]}
{:name "Generic 2"
:id 2
:builtin true
:images [{:id 7
:name "image_name.jpg"
:url "https://images.unsplash.com/photo-1455098934982-64c622c5e066?crop=entropy&fit=crop&fm=jpg&h=1025&ixjsv=2.1.0&ixlib=rb-0.3.5&q=80&w=1400"
:thumbnail "https://images.unsplash.com/photo-1455098934982-64c622c5e066?crop=entropy&fit=crop&fm=jpg&h=1025&ixjsv=2.1.0&ixlib=rb-0.3.5&q=80&w=1400"}
{:id 8
:name "image_name_long_name.jpg"
:url "https://images.unsplash.com/photo-1422393462206-207b0fbd8d6b?crop=entropy&fit=crop&fm=jpg&h=1025&ixjsv=2.1.0&ixlib=rb-0.3.5&q=80&w=1925"
:thumbnail "https://images.unsplash.com/photo-1422393462206-207b0fbd8d6b?crop=entropy&fit=crop&fm=jpg&h=1025&ixjsv=2.1.0&ixlib=rb-0.3.5&q=80&w=1925"}
{:id 9
:name "image_name.jpg"
:url "https://images.unsplash.com/photo-1441986380878-c4248f5b8b5b?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&w=1080&fit=max&s=486f09671860a11e70bdd0a45e7c5014"
:thumbnail "https://images.unsplash.com/photo-1441986380878-c4248f5b8b5b?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&w=1080&fit=max&s=486f09671860a11e70bdd0a45e7c5014"}
{:id 10
:name "image_name_big_long_name.jpg"
:url "https://images.unsplash.com/photo-1423768164017-3f27c066407f?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&w=1080&fit=max&s=712b919f3a2f6fc34f29040e8082b6d9"
:thumbnail "https://images.unsplash.com/photo-1423768164017-3f27c066407f?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&w=1080&fit=max&s=712b919f3a2f6fc34f29040e8082b6d9"}
{:id 11
:name "image_name.jpg"
:url "https://images.unsplash.com/photo-1456428199391-a3b1cb5e93ab?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&w=1080&fit=max&s=765abd6dc931b7184e9795d8494966ed"
:thumbnail "https://images.unsplash.com/photo-1456428199391-a3b1cb5e93ab?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&w=1080&fit=max&s=765abd6dc931b7184e9795d8494966ed"}
{:id 12
:name "image_name.jpg"
:url "https://images.unsplash.com/photo-1447678523326-1360892abab8?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&w=1080&fit=max&s=91663afcd23da14f76cc8229c1780d47"
:thumbnail "https://images.unsplash.com/photo-1447678523326-1360892abab8?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&w=1080&fit=max&s=91663afcd23da14f76cc8229c1780d47"}]}])

View file

@ -15,6 +15,9 @@
"ds.num-colors" ["No colors"
"%s color"
"%s colors"]
"ds.num-images" ["No images"
"%s image"
"%s images"]
"ds.project-ordering" "Sort by"
"ds.project-ordering.by-name" "name"
"ds.project-ordering.by-last-update" "last update"

View file

@ -13,6 +13,7 @@
[uxbox.repo.projects]
[uxbox.repo.pages]
[uxbox.repo.colors]
[uxbox.repo.images]
[httpurr.status :as status]
[beicon.core :as rx]))

View file

@ -47,14 +47,14 @@
(defn- send!
[{:keys [body headers auth method query url] :or {auth true} :as request}]
(let [headers (merge {}
(when body +headers+)
(when (map? body) +headers+)
headers
(when auth (auth-headers)))
request {:method method
:url url
:headers headers
:query-string (when query (encode-query query))
:body (when body (t/encode body))}]
:body (if (map? body) (t/encode body) body)}]
(->> (http/send! request)
(rx/from-promise)
(rx/map conditional-decode)

View file

@ -0,0 +1,80 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
(ns uxbox.repo.images
"A main interface for access to remote resources."
(:require [beicon.core :as rx]
[uxbox.repo.core :refer (request url send!)]
[uxbox.state :as ust]
[uxbox.util.transit :as t]))
(defn- decode-image-collection
[{:keys [data] :as coll}]
coll)
;; (merge coll
;; (when data {:data (t/decode data)})))
(defn- decode-payload
[{:keys [payload] :as rsp}]
rsp)
;; (if (sequential? payload)
;; (assoc rsp :payload (mapv decode-image-collection payload))
;; (assoc rsp :payload (decode-image-collection payload))))
(defmethod request :fetch/image-collections
[_]
(let [params {:url (str url "/library/image-collections")
:method :get}]
(->> (send! params)
(rx/map decode-payload))))
(defmethod request :delete/image-collection
[_ id]
(let [url (str url "/library/image-collections/" id)]
(send! {:url url :method :delete})))
(defmethod request :create/image-collection
[_ {:keys [data] :as body}]
(let [body (assoc body :data (t/encode data))
params {:url (str url "/library/image-collections")
:method :post
:body body}]
(->> (send! params)
(rx/map decode-payload))))
(defmethod request :update/image-collection
[_ {:keys [id data] :as body}]
(let [body (assoc body :data (t/encode data))
params {:url (str url "/library/image-collections/" id)
:method :put
:body body}]
(->> (send! params)
(rx/map decode-payload))))
(defmethod request :fetch/images
[_ {:keys [coll]}]
(let [params {:url (str url "/library/images/" coll)
:method :get}]
(->> (send! params)
(rx/map decode-payload))))
(defmethod request :create/image
[_ {:keys [coll id files] :as body}]
(let [build-body (fn []
(let [data (js/FormData.)]
(.append data "file" (aget files 0))
(.append data "id" id)
data))
params {:url (str url "/library/images/" coll)
:method :post
:body (build-body)}]
(->> (send! params)
(rx/map decode-payload))))
(defmethod request :delete/image
[_ id]
(let [url (str url "/library/images/" id)]
(send! {:url url :method :delete})))

View file

@ -25,7 +25,9 @@
(defn- get-initial-state
[]
{:dashboard {:project-order :name
:project-filter ""}
:project-filter ""
:images-order :name
:images-filter ""}
:route nil
:auth (:uxbox/auth storage nil)
:clipboard #queue []

View file

@ -0,0 +1,28 @@
(ns uxbox.state.images
"A collection of functions for manage dashboard data insinde the state.")
(defn assoc-collection
"A reduce function for assoc the image collection
to the state map."
[state coll]
(let [id (:id coll)]
(assoc-in state [:images-by-id id] coll)))
(defn dissoc-collection
"A reduce function for dissoc the image collection
to the state map."
[state id]
(update state :images-by-id dissoc id))
(defn select-first-collection
"A reduce function for select the first image collection
to the state map."
[state]
(let [colls (sort-by :id (vals (:images-by-id state)))]
(assoc-in state [:dashboard :collection-id] (:id (first colls)))))
(defn dissoc-image
"A reduce function for dissoc the image collection
to the state map."
[state coll-id image]
(update-in state [:images-by-id coll-id :images] disj image))

View file

@ -8,116 +8,300 @@
(ns uxbox.ui.dashboard.images
(:require [sablono.core :as html :refer-macros [html]]
[rum.core :as rum]
[lentes.core :as l]
[uxbox.locales :as t :refer (tr)]
[uxbox.state :as st]
[uxbox.rstore :as rs]
[uxbox.library :as library]
[uxbox.data.dashboard :as dd]
[uxbox.data.lightbox :as udl]
[uxbox.data.images :as di]
[uxbox.ui.icons :as i]
[uxbox.ui.mixins :as mx]
[uxbox.ui.lightbox :as lbx]
[uxbox.ui.keyboard :as k]
[uxbox.ui.library-bar :as ui.library-bar]
[uxbox.ui.dashboard.header :refer (header)]
[uxbox.util.lens :as ul]
[uxbox.util.dom :as dom]))
;; --- Helpers & Constants
(def +ordering-options+
{:name "ds.project-ordering.by-name"
:created "ds.project-ordering.by-creation-date"})
;; --- Lenses
(def ^:const ^:private dashboard-l
(-> (l/key :dashboard)
(l/focus-atom st/state)))
(def ^:const ^:private collections-by-id-l
(-> (comp (l/key :images-by-id)
(ul/merge library/+image-collections-by-id+))
(l/focus-atom st/state)))
(def images-ordering-l
(as-> (l/in [:dashboard :images-order]) $
(l/focus-atom $ st/state)))
(def images-filtering-l
(as-> (l/in [:dashboard :images-filter]) $
(l/focus-atom $ st/state)))
(defn- focus-collection
[collid]
(-> (l/key collid)
(l/focus-atom collections-by-id-l)))
;; --- Page Title
(defn page-title-render
[]
(html
[:div.dashboard-title
[:h2 "Element library name"]
[:div.edition
[:span i/pencil]
[:span i/trash]]]))
[own coll]
(let [local (:rum/local own)
dashboard (rum/react dashboard-l)
own? (:builtin coll false)]
(letfn [(on-title-save [e]
(rs/emit! (di/rename-collection (:id coll) (:coll-name @local)))
(swap! local assoc :edit false))
(on-title-edited [e]
(cond
(k/esc? e) (swap! local assoc :edit false)
(k/enter? e) (on-title-save e)
:else (let [content (dom/event->inner-text e)]
(swap! local assoc :coll-name content))))
(on-title-edit [e]
(swap! local assoc :edit true :coll-name (:name coll)))
(on-delete [e]
(rs/emit! (di/delete-collection (:id coll))))]
(html
[:div.dashboard-title {}
[:h2 {}
(if (:edit @local)
[:div.dashboard-title-field
[:span.edit
{:content-editable ""
:on-key-up on-title-edited}
(:name coll)]
[:span.close
{:on-click #(swap! local assoc :edit false)}
i/close]]
[:span.dashboard-title-field
(:name coll)])]
(if (and (not own?) coll)
[:div.edition
(if (:edit @local)
[:span {:on-click on-title-save} i/save]
[:span {:on-click on-title-edit} i/pencil])
[:span {:on-click on-delete} i/trash]])]))))
(def ^:const ^:private page-title
(def ^:private page-title
(mx/component
{:render page-title-render
:name "page-title"
:mixins [mx/static]}))
:mixins [(rum/local {}) mx/static rum/reactive]}))
;; --- Nav
(defn nav-render
[own]
(let [dashboard (rum/react dashboard-l)
collections-by-id (rum/react collections-by-id-l)
collid (:collection-id dashboard)
own? (= (:collection-type dashboard) :own)
builtin? (= (:collection-type dashboard) :builtin)
collections (as-> (vals collections-by-id) $
(if own?
(filter (comp not :builtin) $)
(filter :builtin $)))]
(html
[:div.library-bar
[:div.library-bar-inside
[:ul.library-tabs
[:li {:class-name (when builtin? "current")
:on-click #(rs/emit! (di/set-collection-type :builtin))}
"STANDARD"]
[:li {:class-name (when own? "current")
:on-click #(rs/emit! (di/set-collection-type :own))}
"YOUR LIBRARIES"]]
[:ul.library-elements
(when own?
[:li
[:a.btn-primary
{:on-click #(rs/emit! (di/create-collection))}
"+ New library"]])
(for [props collections
:let [num (count (:images props))]]
[:li {:key (str (:id props))
:on-click #(rs/emit! (di/set-collection (:id props)))
:class-name (when (= (:id props) collid) "current")}
[:span.element-title (:name props)]
[:span.element-subtitle
(tr "ds.num-elements" (t/c num))]])]]])))
(def ^:const ^:private nav
(mx/component
{:render nav-render
:name "nav"
:mixins [rum/reactive]}))
;; --- Grid
(defn grid-render
[own]
(html
[:div.dashboard-grid-content
[:div.dashboard-grid-row
[:div.grid-item.add-project
{on-click #(udl/open! :new-element)}
[:span "+ New image"]]
(let [local (:rum/local own)
dashboard (rum/react dashboard-l)
coll-type (:collection-type dashboard)
coll-id (:collection-id dashboard)
own? (= coll-type :own)
coll (rum/react (focus-collection coll-id))
images-filtering (rum/react images-filtering-l)
images-ordering (rum/react images-ordering-l)
coll-images (->> (:images coll)
(remove nil?)
(di/filter-images-by images-filtering)
(di/sort-images-by images-ordering))
toggle-image-check (fn [image]
(swap! local update :selected #(if (% image) (disj % image) (conj % image))))
delete-selected-images #(doseq [image (:selected @local)]
(rs/emit! (di/delete-image coll-id image)))]
(when coll
(html
[:section.dashboard-grid.library
(page-title coll)
[:div.dashboard-grid-content
[:div.dashboard-grid-row
(if own?
[:div.grid-item.add-project
{:on-click #(dom/click (dom/get-element-by-class "upload-image-input"))}
[:span "+ New image"]
[:input.upload-image-input {:style {:display "none"}
:type "file"
:on-change #(rs/emit! (di/create-images coll-id (dom/get-event-files %)))}]])
[:div.grid-item.images-th
[:div.grid-item-th
{:style
{:background-image "url('https://images.unsplash.com/photo-1455098934982-64c622c5e066?crop=entropy&fit=crop&fm=jpg&h=1025&ixjsv=2.1.0&ixlib=rb-0.3.5&q=80&w=1400')"}}
[:div.input-checkbox.check-primary
[:input {:type "checkbox" :id "item-1" :value "Yes"}]
[:label {:for "item-1"}]]]
[:span "image_name.jpg"]]
(for [image coll-images]
[:div.grid-item.images-th
{:key (:id image) :on-click #(when (k/shift? %) (toggle-image-check image))}
[:div.grid-item-th
{:style
{:background-image (str "url('" (:thumbnail image) "')")}}
[:div.input-checkbox.check-primary
[:input {:type "checkbox"
:id (:id image)
:on-click #(toggle-image-check image)
:checked ((:selected @local) image)}]
[:label {:for (:id image)}]]]
[:span (:name image)]])]
[:div.grid-item.images-th
[:div.grid-item-th
{:style
{:background-image "url('https://images.unsplash.com/photo-1422393462206-207b0fbd8d6b?crop=entropy&fit=crop&fm=jpg&h=1025&ixjsv=2.1.0&ixlib=rb-0.3.5&q=80&w=1925')"}}
[:div.input-checkbox.check-primary
[:input {:type "checkbox" :id "item-2" :value "Yes"}]
[:label {:for "item-2"}]]]
[:span "image_name_long_name.jpg"]]
[:div.grid-item.images-th
[:div.grid-item-th
{:style
{:background-image "url('https://images.unsplash.com/photo-1441986380878-c4248f5b8b5b?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&w=1080&fit=max&s=486f09671860a11e70bdd0a45e7c5014')"}}
[:div.input-checkbox.check-primary
[:input {:type "checkbox" :id "item-3" :value "Yes"}]
[:label {:for "item-3"}]]]
[:span "image_name.jpg"]]
[:div.grid-item.images-th
[:div.grid-item-th
{:style
{:background-image "url('https://images.unsplash.com/photo-1423768164017-3f27c066407f?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&w=1080&fit=max&s=712b919f3a2f6fc34f29040e8082b6d9')"}}
[:div.input-checkbox.check-primary
[:input {:type "checkbox" :id "item-4" :value "Yes"}]
[:label {:for "item-4"}]]]
[:span "image_name_big_long_name.jpg"]]
[:div.grid-item.images-th
[:div.grid-item-th
{:style
{:background-image "url('https://images.unsplash.com/photo-1456428199391-a3b1cb5e93ab?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&w=1080&fit=max&s=765abd6dc931b7184e9795d8494966ed')"}}
[:div.input-checkbox.check-primary
[:input {:type "checkbox" :id "item-5" :value "Yes"}]
[:label {:for "item-5"}]]]
[:span "image_name.jpg"]]
[:div.grid-item.images-th
[:div.grid-item-th
{:style
{:background-image "url('https://images.unsplash.com/photo-1447678523326-1360892abab8?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&w=1080&fit=max&s=91663afcd23da14f76cc8229c1780d47')"}}
[:div.input-checkbox.check-primary
[:input {:type "checkbox" :id "item-6" :value "Yes"}]
[:label {:for "item-6"}]]]
[:span "image_name.jpg"]]]
;; MULTISELECT OPTIONS BAR
[:div.multiselect-bar
[:div.multiselect-nav
[:span.move-item.tooltip.tooltip-top
{:alt "Move to"}
i/organize]
[:span.copy.tooltip.tooltip-top
{:alt "Duplicate"}
i/copy]
[:span.delete.tooltip.tooltip-top
{:alt "Delete"}
i/trash]]]]))
(when (not (empty? (:selected @local)))
;; MULTISELECT OPTIONS BAR
[:div.multiselect-bar
[:div.multiselect-nav
[:span.move-item.tooltip.tooltip-top
{:alt "Copy to"}
i/organize]
(if own?
[:span.copy.tooltip.tooltip-top
{:alt "Duplicate"}
i/copy])
(if own?
[:span.delete.tooltip.tooltip-top
{:alt "Delete"
:on-click #(delete-selected-images)}
i/trash])]])]]))))
(def ^:const ^:private grid
(mx/component
{:render grid-render
:name "grid"
:mixins [mx/static]}))
:mixins [(rum/local {:selected #{}})
mx/static
rum/reactive]}))
;; --- Sort Widget
(defn sort-widget-render
[]
(let [ordering (rum/react images-ordering-l)
on-change #(rs/emit! (di/set-images-ordering
(keyword (.-value (.-target %)))))]
(html
[:div
[:span (tr "ds.project-ordering")]
[:select.input-select
{:on-change on-change
:value (:name ordering)}
(for [option (keys +ordering-options+)
:let [option-id (get +ordering-options+ option)
option-value (:name option)
option-text (tr option-id)]]
[:option
{:key option-id
:value option-value}
option-text])]])))
(def ^:private sort-widget
(mx/component
{:render sort-widget-render
:name "sort-widget-render"
:mixins [rum/reactive mx/static]}))
;; --- Filtering Widget
(defn search-widget-render
[]
(letfn [(on-term-change [event]
(-> (dom/get-target event)
(dom/get-value)
(di/set-images-filtering)
(rs/emit!)))
(on-clear [event]
(rs/emit! (di/clear-images-filtering)))]
(html
[:form.dashboard-search
[:input.input-text
{:key :images-search-box
:type "text"
:on-change on-term-change
:auto-focus true
:placeholder (tr "ds.project-search.placeholder")
:value (rum/react images-filtering-l)}]
[:div.clear-search
{:on-click on-clear}
i/close]])))
(def ^:private search-widget
(mx/component
{:render search-widget-render
:name "search-widget"
:mixins [rum/reactive mx/static]}))
;; --- Menu
(defn menu-render
[]
(let [dashboard (rum/react dashboard-l)
coll-id (:collection-id dashboard)
coll (rum/react (focus-collection coll-id))
icount (count (:images coll)) ]
(html
[:section.dashboard-bar.library-gap
[:div.dashboard-info
[:span.dashboard-images (tr "ds.num-images" (t/c icount))]
(sort-widget)
(search-widget)]])))
(def menu
(mx/component
{:render menu-render
:name "menu"
:mixins [rum/reactive mx/static]}))
;; --- Images Page
@ -127,14 +311,14 @@
[:main.dashboard-main
(header)
[:section.dashboard-content
(ui.library-bar/library-bar)
[:section.dashboard-grid.library
(page-title)
(grid)]]]))
(nav)
(menu)
(grid)]]))
(defn images-page-will-mount
[own]
(rs/emit! (dd/initialize :dashboard/images))
(rs/emit! (dd/initialize :dashboard/images)
(di/initialize))
own)
(defn images-page-transfer-state

View file

@ -50,6 +50,16 @@
[node]
(.-value node))
(defn click
"Click a node"
[node]
(.click node))
(defn get-files
"Extract the files from dom node."
[node]
(.-files node))
(defn checked?
"Check if the node that reprsents a radio
or checkbox is checked or not."
@ -59,3 +69,8 @@
(defn ^boolean equals?
[node-a node-b]
(.isEqualNode node-a node-b))
(defn get-event-files
"Extract the files from event instance."
[event]
(get-files (get-target event)))