0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-01-24 07:29:08 -05:00

Major images page refactor.

This commit is contained in:
Andrey Antukh 2016-10-19 17:42:24 +02:00
parent 5b995f78aa
commit 2143bb6e04
6 changed files with 326 additions and 105 deletions

View file

@ -2,8 +2,7 @@
;; 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>
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
(ns uxbox.main.data.images
(:require [cuerdas.core :as str]
@ -26,10 +25,8 @@
(defrecord Initialize [type id]
rs/UpdateEvent
(-apply-update [_ state]
(let [type (or type :builtin)
id (or id (if (= type :builtin) 1 nil))
(let [type (or type :own)
data {:type type :id id :selected #{}}]
(println "initialize:" data)
(-> state
(assoc-in [:dashboard :images] data)
(assoc-in [:dashboard :section] :dashboard/images))))
@ -159,8 +156,8 @@
(rx/of (update-collection id))))
(defn rename-collection
[item name]
(RenameCollection. item name))
[id name]
(RenameCollection. id name))
;; --- Delete Collection
@ -193,7 +190,7 @@
(def allowed-file-types #{"image/jpeg" "image/png"})
(defrecord CreateImages [coll-id files]
(defrecord CreateImages [id files]
rs/WatchEvent
(-apply-watch [_ state s]
(letfn [(image-size [file]
@ -202,7 +199,7 @@
(allowed-file? [file]
(contains? allowed-file-types (.-type file)))
(prepare [[file [width height]]]
{:coll coll-id
{:collection id
:mimetype (.-type file)
:id (uuid/random)
:file file
@ -217,12 +214,13 @@
(rx/map image-created)))))
(defn create-images
[coll-id files]
(CreateImages. coll-id files))
[id files]
{:pre [(or (uuid? id) (nil? id))]}
(CreateImages. id files))
;; --- Images Fetched
(defrecord ImagesFetched [coll-id items]
(defrecord ImagesFetched [items]
rs/UpdateEvent
(-apply-update [_ state]
(reduce (fn [state {:keys [id] :as image}]
@ -231,58 +229,77 @@
items)))
(defn images-fetched
[coll-id items]
(ImagesFetched. coll-id items))
[items]
(ImagesFetched. items))
;; --- Fetch Images
(defrecord FetchImages [coll-id]
(defrecord FetchImages [id]
rs/WatchEvent
(-apply-watch [_ state s]
(let [params {:coll coll-id}]
(let [params {:coll id}]
(->> (rp/req :fetch/images params)
(rx/map :payload)
(rx/map #(images-fetched coll-id %))))))
(rx/map images-fetched)))))
(defn fetch-images
[coll-id]
(FetchImages. coll-id))
"Fetch a list of images of the selected collection"
[id]
{:pre [(or (uuid? id) (nil? id))]}
(FetchImages. id))
;; --- Fetch Image
(declare image-fetched)
(defrecord FetchImage [id]
rs/WatchEvent
(-apply-watch [_ state stream]
(let [existing (get-in state [:images-by-id id])]
(if existing
(rx/empty)
(->> (rp/req :fetch/image {:id id})
(rx/map :payload)
(rx/map image-fetched))))))
(defn fetch-image
"Conditionally fetch image by its id. If image
is already loaded, this event is noop."
[id]
{:pre [(uuid? id)]}
(FetchImage. id))
;; --- Image Fetched
(defrecord ImageFetched [image]
rs/UpdateEvent
(-apply-update [_ state]
(let [id (:id image)]
(update state :images-by-id assoc id image))))
(defn image-fetched
[image]
{:pre [(map? image)]}
(ImageFetched. image))
;; --- Delete Images
(defrecord DeleteImage [coll-id image-id]
(defrecord DeleteImage [id]
rs/UpdateEvent
(-apply-update [_ state]
(update state [:images-by-id] dissoc image-id))
(-> state
(update :images-by-id dissoc id)
(update-in [:dashboard :images :selected] disj id)))
rs/WatchEvent
(-apply-watch [_ state s]
(->> (rp/req :delete/image image-id)
(->> (rp/req :delete/image id)
(rx/ignore))))
(defn delete-image
[coll-id image-id]
{:pre [(uuid? coll-id)
(uuid? image-id)]}
(DeleteImage. coll-id image-id))
;; --- Remove Image
(defrecord RemoveImages [id images]
rs/UpdateEvent
(-apply-update [_ state]
#_(update-in state [:image-colls-by-id id :data]
#(set/difference % images)))
rs/WatchEvent
(-apply-watch [_ state s]
(rx/of (update-collection id))))
(defn remove-images
"Remove image in a collection."
[id images]
(RemoveImages. id images))
[id]
{:pre [(uuid? id)]}
(DeleteImage. id))
;; --- Select image
@ -314,9 +331,9 @@
(defrecord DeleteSelected []
rs/WatchEvent
(-apply-watch [_ state stream]
(let [{:keys [id selected]} (get-in state [:dashboard :images])]
(rx/of (remove-images id selected)
#(assoc-in % [:dashboard :images :selected] #{})))))
(let [selected (get-in state [:dashboard :images :selected])]
(->> (rx/from-coll selected)
(rx/map delete-image)))))
(defn delete-selected
[]

View file

@ -40,20 +40,28 @@
(defmethod request :fetch/images
[_ {:keys [coll]}]
(let [params {:url (str url "/library/image-collections/" coll "/images")
(let [url (if coll
(str url "/library/image-collections/" coll "/images")
(str url "/library/image-collections/images"))
params {:url url :method :get}]
(send! params)))
(defmethod request :fetch/image
[_ {:keys [id]}]
(let [params {:url (str url "/library/images/" id)
:method :get}]
(send! params)))
(defmethod request :create/image
[_ {:keys [coll id file width height mimetype] :as body}]
[_ {:keys [collection id file width height mimetype] :as body}]
(let [body (doto (js/FormData.)
(.append "mimetype" mimetype)
;; (.append "collection" (str coll))
(.append "collection" (str collection))
(.append "file" file)
(.append "width" width)
(.append "height" height)
(.append "id" id))
params {:url (str url "/library/image-collections/" coll "/images")
params {:url (str url "/library/images/")
:method :post
:body body}]
(send! params)))

View file

@ -166,19 +166,148 @@
(defn- new-element-lightbox-render
[own]
(html
[:div.lightbox-body
[:h3 "New element"]
[:div.row-flex
[:div.lightbox-big-btn
[:span.big-svg i/shapes]
[:span.text "Go to workspace"]]
[:div.lightbox-big-btn
[:span.big-svg.upload i/exit]
[:span.text "Upload file"]]]
;;------Element lightbox
;;[:div.lightbox-body
;;[:h3 "New element"]
;;[:div.row-flex
;;[:div.lightbox-big-btn
;;[:span.big-svg i/shapes]
;;[:span.text "Go to workspace"]]
;;[:div.lightbox-big-btn
;;[:span.big-svg.upload i/exit]
;;[:span.text "Upload file"]]]
;;[:a.close {:href "#"
;;:on-click #(do (dom/prevent-default %)
;;(udl/close!))}
;;i/close]]
;;------Upload image lightbox
;;[:div.lightbox-body
;;[:h3 "Import image"]
;;[:div.row-flex
;;[:div.lightbox-big-btn
;;[:span.big-svg i/image]
;;[:span.text "Select from library"]]
;;[:div.lightbox-big-btn
;;[:span.big-svg.upload i/exit]
;;[:span.text "Upload file"]]]
;;[:a.close {:href "#"
;;:on-click #(do (dom/prevent-default %)
;;(udl/close!))}
;;i/close]]
;;------Upload image library lightbox
[:div.lightbox-body.big-lightbox
[:h3 "Import image from library"]
[:div.import-img-library
[:div.library-actions
[:ul.toggle-library
[:li.standard.current "STANDARD"]
[:li.your-images "YOUR IMAGES"]]
[:select.input-select
[:option "Library 1"]
[:option "Library 2"]
[:option "Library 3"]
[:option "Library 4"]
[:option "Library 5"]
[:option "Library 6"]]]
[:div.library-content
[:div.library-item
[:div.library-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')"}}]
[:span "image_name.jpg"]]
[:div.library-item
[:div.library-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')"}}]
[:span "image_name.jpg"]]
[:div.library-item
[:div.library-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')"}}]
[:span "image_name.jpg"]]
[:div.library-item
[:div.library-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')"}}]
[:span "image_name.jpg"]]
[:div.library-item
[:div.library-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')"}}]
[:span "image_name.jpg"]]
[:div.library-item
[:div.library-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')"}}]
[:span "image_name.jpg"]]
[:div.library-item
[:div.library-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')"}}]
[:span "image_name.jpg"]]
[:div.library-item
[:div.library-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')"}}]
[:span "image_name.jpg"]]
[:div.library-item
[:div.library-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')"}}]
[:span "image_name.jpg"]]
[:div.library-item
[:div.library-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')"}}]
[:span "image_name.jpg"]]
[:div.library-item
[:div.library-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')"}}]
[:span "image_name.jpg"]]
[:div.library-item
[:div.library-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')"}}]
[:span "image_name.jpg"]]
[:div.library-item
[:div.library-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')"}}]
[:span "image_name.jpg"]]
[:div.library-item
[:div.library-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')"}}]
[:span "image_name.jpg"]]
]]
[:a.close {:href "#"
:on-click #(do (dom/prevent-default %)
(udl/close!))}
i/close]]))
i/close]]
))
(def ^:private new-element-lightbox
(mx/component

View file

@ -6,25 +6,19 @@
;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
(ns uxbox.main.ui.dashboard.images
(:require [sablono.core :as html :refer-macros [html]]
[rum.core :as rum]
[cuerdas.core :as str]
(:require [cuerdas.core :as str]
[lentes.core :as l]
[uxbox.util.i18n :as t :refer (tr)]
[uxbox.main.state :as st]
[uxbox.util.rstore :as rs]
[uxbox.main.library :as library]
[uxbox.main.data.dashboard :as dd]
[uxbox.main.data.lightbox :as udl]
[uxbox.main.data.images :as di]
[uxbox.main.ui.icons :as i]
[uxbox.util.mixins :as mx :include-macros true]
[uxbox.main.ui.lightbox :as lbx]
[uxbox.main.ui.keyboard :as kbd]
[uxbox.main.ui.library-bar :as ui.library-bar]
[uxbox.main.ui.dashboard.header :refer (header)]
[uxbox.util.data :as data :refer (read-string)]
[uxbox.util.lens :as ul]
[uxbox.util.dom :as dom]))
;; --- Helpers & Constants
@ -65,15 +59,6 @@
(-> (l/key :images-by-id)
(l/derive st/state)))
;; (def ^:private collections-ref
;; (-> (l/lens vals)
;; (l/derive collections-map-ref)))
;; (defn- focus-collection
;; [id]
;; (-> (l/key id)
;; (l/derive collections-map-ref)))
;; --- Page Title
(mx/defcs page-title
@ -117,7 +102,7 @@
[:span.close {:on-click on-cancel} i/close]]
[:span.dashboard-title-field
{:on-double-click on-edit}
(:name coll)])]
(:name coll "Storage")])]
(if (and (not own?) coll)
[:div.edition
(if edit?
@ -127,19 +112,26 @@
;; --- Nav
(defn react-count-images
[id]
(->> (mx/react images-ref)
(vals)
(filter #(= id (:collection %)))
(count)))
(mx/defc nav-item
{:mixins [mx/static]}
[collection selected?]
{:mixins [mx/static mx/reactive]}
[{:keys [id type name] :as coll} selected?]
(letfn [(on-click [event]
(let [type (:type collection)
id (:id collection)]
(let [type (or type :own)]
(rs/emit! (di/select-collection type id))))]
(let [images (count (:images collection []))]
(let [num-images (react-count-images id)]
[:li {:on-click on-click
:class-name (when selected? "current")}
[:span.element-title (:name collection)]
[:span.element-title
(if coll name "Storage")]
[:span.element-subtitle
(tr "ds.num-elements" (t/c images))]])))
(tr "ds.num-elements" (t/c num-images))]])))
(mx/defc nav-section
{:mixins [mx/static mx/reactive]}
@ -156,6 +148,8 @@
[:a.btn-primary
{:on-click #(rs/emit! (di/create-collection))}
"+ New library"]])
(when own?
(nav-item nil (nil? selected)))
(for [coll collections
:let [selected? (= (:id coll) selected)
key (str (:id coll))]]
@ -179,12 +173,13 @@
[:div.library-bar
[:div.library-bar-inside
[:ul.library-tabs
[:li {:class-name (when builtin? "current")
:on-click (partial select-tab :builtin)}
"STANDARD"]
[:li {:class-name (when own? "current")
:on-click (partial select-tab :own)}
"YOUR LIBRARIES"]]
"YOUR IMAGES"]
[:li {:class-name (when builtin? "current")
:on-click (partial select-tab :builtin)}
"IMAGES STORE"]]
(nav-section type id colls)]])))
;; --- Grid
@ -208,15 +203,15 @@
:on-change on-file-selected}]]))
(mx/defc grid-options
[coll]
(let [own? (= (:type coll) :own)]
[{:keys [type] :as coll}]
(let [editable? (or (= type :own) (nil? coll))]
(letfn [(delete []
(rs/emit! (di/delete-selected)))
(on-delete [event]
(udl/open! :confirm {:on-accept delete}))]
;; MULTISELECT OPTIONS BAR
[:div.multiselect-bar
(if own?
(if editable?
[:div.multiselect-nav
#_[:span.move-item.tooltip.tooltip-top
{:alt "Move to"}
@ -251,19 +246,18 @@
(mx/defc grid
{:mixins [mx/static mx/reactive]}
[{:keys [id type selected] :as state}]
(let [filtering (:filter state)
[{:keys [id type selected] :as state}]
(let [editable? (or (= type :own) (nil? id))
filtering (:filter state)
ordering (:order state)
own? (= type :own)
images (rum/react images-ref)
images (mx/react images-ref)
images (->> (vals images)
(filter #(= id (:collection %)))
(filter-images-by filtering)
(sort-images-by ordering))]
[:div.dashboard-grid-content
[:div.dashboard-grid-row
(when own?
(grid-form id))
(when editable? (grid-form id))
(for [image images
:let [id (:id image)
selected? (contains? selected id)]]
@ -273,13 +267,11 @@
(mx/defc content
{:mixins [mx/static]}
[{:keys [type id selected] :as state} coll]
(let [own? (= type :own)]
(when coll
[:section.dashboard-grid.library
(page-title coll)
(grid state)
(when (seq selected)
(grid-options coll))])))
[:section.dashboard-grid.library
(page-title coll)
(grid state)
(when (seq selected)
(grid-options coll))])
;; --- Menu

View file

@ -15,6 +15,7 @@
[uxbox.main.ui.shapes.circle :as circle]
[uxbox.main.ui.shapes.text :as text]
[uxbox.main.ui.shapes.path :as path]
[uxbox.main.ui.shapes.image :as image]
[uxbox.main.geom :as geom]))
;; --- Helpers
@ -34,6 +35,7 @@
:icon (icon/icon-component shape)
:rect (rect/rect-component shape)
:path (path/path-component shape)
:image (image/image-component shape)
:circle (circle/circle-component shape)))
(mx/defc component-container

View file

@ -0,0 +1,73 @@
;; 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.main.ui.shapes.image
(:require [beicon.core :as rx]
[lentes.core :as l]
[uxbox.util.mixins :as mx :include-macros true]
[uxbox.util.http :as http]
[uxbox.util.rstore :as rs]
[uxbox.main.state :as st]
[uxbox.main.ui.shapes.common :as common]
[uxbox.main.ui.shapes.attrs :as attrs]
[uxbox.main.data.images :as udi]
[uxbox.main.geom :as geom]))
;; --- Refs
(defn image-ref
[id]
(-> (l/in [:images-by-id id])
(l/derive st/state)))
;; --- Image Component
(declare image-shape)
(defn- will-mount
[own]
(let [{:keys [image-id]} (first (:rum/args own))]
(rs/emit! (udi/fetch-image image-id))
own))
(mx/defcs image-component
{:mixins [mx/static mx/reactive]
:will-mount will-mount}
[own {:keys [id image-id] :as shape}]
(let [selected (mx/react common/selected-ref)
image (mx/react (image-ref image-id))
selected? (contains? selected id)
local (:rum/local own)
on-mouse-down #(common/on-mouse-down % shape selected)]
(when image
[:g.shape {:class (when selected? "selected")
:on-mouse-down on-mouse-down}
(image-shape (assoc shape :image image))])))
;; --- Image Shape
(mx/defc image-shape
{:mixins [mx/static]}
[{:keys [id x1 y1 image] :as shape}]
(let [key (str "shape-" id)
;; rfm (geom/transformation-matrix shape)
size (geom/size shape)
props {:x x1 :y y1 :id key :key key
:preserve-aspect-ratio "none"
:xlink-href (:url image)}
attrs (-> (attrs/extract-style-attrs shape)
(merge props size))]
[:image attrs]))
;; --- Image SVG
(mx/defc image-svg
{:mixins [mx/static]}
[{:keys [data id view-box] :as shape}]
(let [key (str "image-svg-" id)
view-box (apply str (interpose " " view-box))
props {:view-box view-box :id key :key key}]
[:svg props data]))