🎉 Add stacked exports.

This commit is contained in:
Andrey Antukh 2020-07-02 14:48:17 +02:00 committed by Hirunatan
parent a8d5cdc29f
commit 2fb4e72240
14 changed files with 549 additions and 208 deletions

View file

@ -416,7 +416,7 @@
}
},
"dashboard.sidebar.drafts" : {
"used-in" : [ "src/uxbox/main/ui/dashboard/sidebar.cljs:129" ],
"used-in" : [ "src/uxbox/main/ui/dashboard/sidebar.cljs:128" ],
"translations" : {
"en" : "Drafts",
"fr" : "Brouillons",
@ -424,7 +424,7 @@
}
},
"dashboard.sidebar.libraries" : {
"used-in" : [ "src/uxbox/main/ui/dashboard/sidebar.cljs:135" ],
"used-in" : [ "src/uxbox/main/ui/dashboard/sidebar.cljs:134" ],
"translations" : {
"en" : "Libraries",
"fr" : "Librairies",
@ -432,7 +432,7 @@
}
},
"dashboard.sidebar.recent" : {
"used-in" : [ "src/uxbox/main/ui/dashboard/sidebar.cljs:122" ],
"used-in" : [ "src/uxbox/main/ui/dashboard/sidebar.cljs:121" ],
"translations" : {
"en" : "Recent",
"fr" : "Récent",
@ -528,7 +528,7 @@
}
},
"ds.search.placeholder" : {
"used-in" : [ "src/uxbox/main/ui/dashboard/sidebar.cljs:188" ],
"used-in" : [ "src/uxbox/main/ui/dashboard/sidebar.cljs:187" ],
"translations" : {
"en" : "Search...",
"fr" : "Rechercher...",
@ -584,7 +584,7 @@
}
},
"errors.image-format-unsupported" : {
"used-in" : [ "src/uxbox/main/data/images.cljs:376", "src/uxbox/main/data/workspace/persistence.cljs:365", "src/uxbox/main/data/users.cljs:177" ],
"used-in" : [ "src/uxbox/main/data/users.cljs:177", "src/uxbox/main/data/workspace/persistence.cljs:365", "src/uxbox/main/data/images.cljs:376" ],
"translations" : {
"en" : "The image format is not supported (must be svg, jpg or png).",
"fr" : "Le format d'image n'est pas supporté (doit être svg, jpg ou png).",
@ -592,7 +592,7 @@
}
},
"errors.image-too-large" : {
"used-in" : [ "src/uxbox/main/data/images.cljs:374", "src/uxbox/main/data/workspace/persistence.cljs:363", "src/uxbox/main/data/users.cljs:175" ],
"used-in" : [ "src/uxbox/main/data/users.cljs:175", "src/uxbox/main/data/workspace/persistence.cljs:363", "src/uxbox/main/data/images.cljs:374" ],
"translations" : {
"en" : "The image is too large to be inserted (must be under 5mb).",
"fr" : "L'image est trop grande (doit être inférieure à 5 Mo).",
@ -632,7 +632,7 @@
}
},
"errors.unexpected-error" : {
"used-in" : [ "src/uxbox/main/data/images.cljs:385", "src/uxbox/main/data/workspace/persistence.cljs:334", "src/uxbox/main/data/workspace/persistence.cljs:374", "src/uxbox/main/data/users.cljs:185", "src/uxbox/main/ui/auth/register.cljs:54", "src/uxbox/main/ui/settings/change_email.cljs:51" ],
"used-in" : [ "src/uxbox/main/data/users.cljs:185", "src/uxbox/main/data/workspace/persistence.cljs:334", "src/uxbox/main/data/workspace/persistence.cljs:374", "src/uxbox/main/data/images.cljs:385", "src/uxbox/main/ui/settings/change_email.cljs:51", "src/uxbox/main/ui/workspace/sidebar/options/exports.cljs:65", "src/uxbox/main/ui/auth/register.cljs:54" ],
"translations" : {
"en" : "An unexpected error occurred.",
"fr" : "Une erreur inattendue c'est produite",
@ -672,7 +672,7 @@
}
},
"image.loading" : {
"used-in" : [ "src/uxbox/main/data/images.cljs:393", "src/uxbox/main/data/workspace/persistence.cljs:341", "src/uxbox/main/data/workspace/persistence.cljs:382", "src/uxbox/main/data/users.cljs:191" ],
"used-in" : [ "src/uxbox/main/data/users.cljs:191", "src/uxbox/main/data/workspace/persistence.cljs:341", "src/uxbox/main/data/workspace/persistence.cljs:382", "src/uxbox/main/data/images.cljs:393" ],
"translations" : {
"en" : "Loading image...",
"fr" : "Chargement de l'image...",
@ -840,11 +840,12 @@
}
},
"settings.multiple" : {
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:132", "src/uxbox/main/ui/workspace/sidebar/options/rows/color_row.cljs:117", "src/uxbox/main/ui/workspace/sidebar/options/rows/color_row.cljs:126" ],
"translations" : {
"en" : "Multiple",
"es" : "Múltiple"
}
"en" : null,
"fr" : null,
"es" : null
},
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/rows/color_row.cljs:117", "src/uxbox/main/ui/workspace/sidebar/options/rows/color_row.cljs:126", "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:132" ]
},
"settings.new-email-label" : {
"used-in" : [ "src/uxbox/main/ui/settings/change_email.cljs:75" ],
@ -1367,27 +1368,33 @@
}
},
"workspace.options.design" : {
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options.cljs:70" ],
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options.cljs:65" ],
"translations" : {
"en" : "Design",
"fr" : "Conception",
"es" : "Diseño"
}
},
"workspace.options.export-object" : {
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options.cljs:78" ],
"workspace.options.export" : {
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/exports.cljs:115", "src/uxbox/main/ui/workspace/sidebar/options/exports.cljs:148" ],
"translations" : {
"en" : "Export"
}
},
"workspace.options.export-object" : {
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/exports.cljs:144" ],
"translations" : {
"en" : "Export shape"
}
},
"workspace.options.exporting-object" : {
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options.cljs:77" ],
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/exports.cljs:143" ],
"translations" : {
"en" : "Exporting..."
}
},
"workspace.options.fill" : {
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/fill.cljs:40", "src/uxbox/main/ui/workspace/sidebar/options/text.cljs:382" ],
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/text.cljs:382", "src/uxbox/main/ui/workspace/sidebar/options/fill.cljs:40" ],
"translations" : {
"en" : "Fill",
"fr" : "Remplissage",
@ -1395,7 +1402,7 @@
}
},
"workspace.options.font-options" : {
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/text.cljs:388" ],
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/text.cljs:387" ],
"translations" : {
"en" : "Text",
"fr" : "Texte",
@ -1715,18 +1722,20 @@
}
},
"workspace.options.group-fill" : {
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/fill.cljs:39" ],
"translations" : {
"en" : "Group fill",
"es" : "Relleno de grupo"
}
"en" : null,
"fr" : null,
"es" : null
},
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/fill.cljs:39" ]
},
"workspace.options.group-stroke" : {
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:65" ],
"translations" : {
"en" : "Group stroke",
"es" : "Borde de grupo"
}
"en" : null,
"fr" : null,
"es" : null
},
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:65" ]
},
"workspace.options.navigate-to" : {
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/interactions.cljs:59" ],
@ -1745,7 +1754,7 @@
}
},
"workspace.options.position" : {
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/measures.cljs:112", "src/uxbox/main/ui/workspace/sidebar/options/frame.cljs:125" ],
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/measures.cljs:144", "src/uxbox/main/ui/workspace/sidebar/options/frame.cljs:125" ],
"translations" : {
"en" : "Position",
"fr" : "Position",
@ -1753,7 +1762,7 @@
}
},
"workspace.options.prototype" : {
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options.cljs:80" ],
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options.cljs:74" ],
"translations" : {
"en" : "Prototype",
"fr" : "Prototype",
@ -1761,7 +1770,7 @@
}
},
"workspace.options.radius" : {
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/measures.cljs:158" ],
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/measures.cljs:186" ],
"translations" : {
"en" : "Radius",
"fr" : "Rayon",
@ -1769,7 +1778,7 @@
}
},
"workspace.options.rotation" : {
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/measures.cljs:131" ],
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/measures.cljs:163" ],
"translations" : {
"en" : "Rotation",
"fr" : "Rotation",
@ -1793,21 +1802,23 @@
}
},
"workspace.options.selection-fill" : {
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/fill.cljs:38" ],
"translations" : {
"en" : "Selection fill",
"es" : "Relleno de selección"
}
"en" : null,
"fr" : null,
"es" : null
},
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/fill.cljs:38" ]
},
"workspace.options.selection-stroke" : {
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:64" ],
"translations" : {
"en" : "Selection stroke",
"es" : "Borde de selección"
}
"en" : null,
"fr" : null,
"es" : null
},
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:64" ]
},
"workspace.options.size" : {
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/measures.cljs:82", "src/uxbox/main/ui/workspace/sidebar/options/frame.cljs:98" ],
"used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/measures.cljs:114", "src/uxbox/main/ui/workspace/sidebar/options/frame.cljs:98" ],
"translations" : {
"en" : "Size",
"fr" : "Taille",

View file

@ -760,3 +760,42 @@
cursor: auto;
}
}
.element-set-options-group {
display: flex;
padding: 3px;
border: 1px solid $color-black;
border-radius: 4px;
&:hover {
background: #1F1F1F;
}
}
.exports-options {
.element-set-options-group {
justify-content: space-between;
.delete-icon {
display: flex;
min-width: 40px;
min-height: 40px;
justify-content: center;
align-items: center;
cursor: pointer;
svg {
width: 20px;
height: 20px;
fill: $color-gray-20;
}
}
&:not(:first-child) {
margin-top: 7px;
}
}
.download-button {
margin-top: 10px;
}
}

View file

@ -10,11 +10,8 @@
(ns uxbox.main.ui.workspace.sidebar.options
(:require
[beicon.core :as rx]
[cljs.spec.alpha :as s]
[cuerdas.core :as str]
[rumext.alpha :as mf]
[uxbox.common.spec :as us]
[uxbox.main.data.messages :as dm]
[uxbox.main.data.workspace :as udw]
[uxbox.main.refs :as refs]
[uxbox.main.store :as st]
@ -22,6 +19,7 @@
[uxbox.main.ui.icons :as i]
[uxbox.main.ui.workspace.sidebar.align :refer [align-options]]
[uxbox.main.ui.workspace.sidebar.options.circle :as circle]
[uxbox.main.ui.workspace.sidebar.options.exports :refer [exports-menu]]
[uxbox.main.ui.workspace.sidebar.options.frame :as frame]
[uxbox.main.ui.workspace.sidebar.options.group :as group]
[uxbox.main.ui.workspace.sidebar.options.icon :as icon]
@ -32,61 +30,11 @@
[uxbox.main.ui.workspace.sidebar.options.interactions :refer [interactions-menu]]
[uxbox.main.ui.workspace.sidebar.options.path :as path]
[uxbox.main.ui.workspace.sidebar.options.rect :as rect]
[uxbox.util.dom :as dom]
[uxbox.util.http :as http]
[uxbox.util.i18n :as i18n :refer [tr t]]
[uxbox.util.object :as obj]))
[uxbox.main.ui.workspace.sidebar.options.text :as text]
[uxbox.util.i18n :as i18n :refer [tr t]]))
;; --- Options
(defn- request-screenshot
[page-id shape-id]
(http/send! {:method :get
:uri "/export/bitmap"
:query {:page-id page-id
:object-id shape-id}}
{:credentials? true
:response-type :blob}))
(defn- trigger-download
[name blob]
(let [link (dom/create-element "a")
uri (dom/create-uri blob)]
(obj/set! link "href" uri)
(obj/set! link "download" (str/slug name))
(obj/set! (.-style ^js link) "display" "none")
(.appendChild (.-body ^js js/document) link)
(.click link)
(.remove link)))
(mf/defc shape-export
{::mf/wrap [mf/memo]}
[{:keys [shape page] :as props}]
(let [loading? (mf/use-state false)
locale (mf/deref i18n/locale)
on-click (fn [event]
(dom/prevent-default event)
(swap! loading? not)
(->> (request-screenshot (:id page) (:id shape))
(rx/subs
(fn [{:keys [status body] :as response}]
(if (= status 200)
(trigger-download (:name shape) body)
(st/emit! (dm/error (tr "errors.unexpected-error")))))
(constantly nil)
(fn []
(swap! loading? not)))))]
[:div.element-set
[:div.btn-large.btn-icon-dark
{:on-click (when-not @loading? on-click)
:class (dom/classnames
:btn-disabled @loading?)
:disabled @loading?}
(if @loading?
(t locale "workspace.options.exporting-object")
(t locale "workspace.options.export-object"))]]))
(mf/defc shape-options
{::mf/wrap [#(mf/throttle % 60)]}
[{:keys [shape page] :as props}]
@ -102,7 +50,7 @@
:curve [:& path/options {:shape shape}]
:image [:& image/options {:shape shape}]
nil)
[:& shape-export {:shape shape :page page}]])
[:& exports-menu {:shape shape :page page}]])
(mf/defc options-content

View file

@ -0,0 +1,151 @@
;; 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/.
;;
;; This Source Code Form is "Incompatible With Secondary Licenses", as
;; defined by the Mozilla Public License, v. 2.0.
;;
;; Copyright (c) 2020 UXBOX Labs SL
(ns uxbox.main.ui.workspace.sidebar.options.exports
(:require
[cuerdas.core :as str]
[beicon.core :as rx]
[rumext.alpha :as mf]
[uxbox.common.data :as d]
[uxbox.main.ui.icons :as i]
[uxbox.main.data.messages :as dm]
[uxbox.main.data.workspace :as udw]
[uxbox.main.store :as st]
[uxbox.util.object :as obj]
[uxbox.util.dom :as dom]
[uxbox.util.http-api :as http]
[uxbox.util.i18n :as i18n :refer [tr t]]))
(defn- request-export
[shape exports]
(http/send! {:method :post
:uri "/export/bitmap"
:response-type :blob
:auth true
:body {:page-id (:page-id shape)
:object-id (:id shape)
:name (:name shape)
:exports exports}}))
(defn- trigger-download
[name blob]
(let [link (dom/create-element "a")
uri (dom/create-uri blob)]
(obj/set! link "href" uri)
(obj/set! link "download" (str/slug name))
(obj/set! (.-style ^js link) "display" "none")
(.appendChild (.-body ^js js/document) link)
(.click link)
(.remove link)))
(mf/defc exports-menu
[{:keys [shape page] :as props}]
(let [locale (mf/deref i18n/locale)
exports (:exports shape [])
loading? (mf/use-state false)
on-download
(mf/use-callback
(mf/deps shape)
(fn [event]
(dom/prevent-default event)
(swap! loading? not)
(->> (request-export (assoc shape :page-id (:id page)) exports)
(rx/subs
(fn [{:keys [status body] :as response}]
(js/console.log status body)
(if (= status 200)
(trigger-download (:name shape) body)
(st/emit! (dm/error (tr "errors.unexpected-error")))))
(constantly nil)
(fn []
(swap! loading? not))))))
_ (prn "exports-menu" exports)
add-export
(mf/use-callback
(mf/deps shape)
(fn []
(let [xspec {:type :png
:suffix ""
:scale 1}]
(st/emit! (udw/update-shape (:id shape)
{:exports (conj exports xspec)})))))
delete-export
(mf/use-callback
(mf/deps shape)
(fn [index]
(let [[before after] (split-at index exports)
exports (d/concat [] before (rest after))]
(st/emit! (udw/update-shape (:id shape)
{:exports exports})))))
on-scale-change
(mf/use-callback
(mf/deps shape)
(fn [index event]
(let [target (dom/get-target event)
value (dom/get-value target)
value (d/parse-double value)
exports (assoc-in exports [index :scale] value)]
(st/emit! (udw/update-shape (:id shape)
{:exports exports})))))
on-suffix-change
(mf/use-callback
(mf/deps shape)
(fn [index event]
(let [target (dom/get-target event)
value (dom/get-value target)
exports (assoc-in exports [index :suffix] value)]
(st/emit! (udw/update-shape (:id shape)
{:exports exports})))))]
(if (seq exports)
[:div.element-set.exports-options
[:div.element-set-title
[:span (t locale "workspace.options.export")]
[:div.add-page {:on-click add-export} i/close]]
[:div.element-set-content
(for [[index export] (d/enumerate exports)]
[:div.element-set-options-group
{:key index}
[:select.input-select {:on-change (partial on-scale-change index)
:value (:scale export)}
[:option {:value "0.5"} "0.5x"]
[:option {:value "0.75"} "0.75x"]
[:option {:value "1"} "1x"]
[:option {:value "1.5"} "1.5x"]
[:option {:value "2"} "2x"]
[:option {:value "4"} "4x"]
[:option {:value "6"} "6x"]]
[:input.input-text {:value (:suffix export)
:on-change (partial on-suffix-change index)}]
[:select.input-select
[:option {:value "png"} "PNG"]]
[:div.delete-icon {:on-click (partial delete-export index)}
i/trash]])
[:div.btn-large.btn-icon-dark.download-button
{:on-click (when-not @loading? on-download)
:class (dom/classnames
:btn-disabled @loading?)
:disabled @loading?}
(if @loading?
(t locale "workspace.options.exporting-object")
(t locale "workspace.options.export-object"))]]]
[:div.element-set
[:div.element-set-title
[:span (t locale "workspace.options.export")]
[:div.add-page {:on-click add-export} i/close]]])))