diff --git a/frontend/resources/locales.json b/frontend/resources/locales.json
index 8a849f409..43cd97eee 100644
--- a/frontend/resources/locales.json
+++ b/frontend/resources/locales.json
@@ -978,6 +978,12 @@
"en" : "Left"
}
},
+ "handoff.attributes.layout.radius" : {
+ "used-in" : [ "src/app/main/ui/viewer/handoff/attributes/layout.cljs:60" ],
+ "translations" : {
+ "en" : "Radius"
+ }
+ },
"handoff.attributes.layout.rotation" : {
"used-in" : [ "src/app/main/ui/viewer/handoff/attributes/layout.cljs:60" ],
"translations" : {
diff --git a/frontend/resources/styles/main/partials/handoff.scss b/frontend/resources/styles/main/partials/handoff.scss
index 899dd818b..70ba1be4e 100644
--- a/frontend/resources/styles/main/partials/handoff.scss
+++ b/frontend/resources/styles/main/partials/handoff.scss
@@ -264,8 +264,10 @@
}
}
-
.code-block {
+ margin-top: 0.5rem;
+ border-top: 1px solid $color-gray-60;
+
.code-row-lang {
position: relative;
display: flex;
@@ -307,6 +309,7 @@
overflow: hidden;
white-space: pre-wrap;
background: $color-gray-60;
+ user-select: text;
.hljs-attr {
color: #a6e22e;
@@ -319,5 +322,8 @@
}
}
}
-
+}
+
+.element-options :first-child {
+ border-top: none;
}
diff --git a/frontend/src/app/main/ui/shapes/filters.cljs b/frontend/src/app/main/ui/shapes/filters.cljs
index 89a7374f3..140b5fb57 100644
--- a/frontend/src/app/main/ui/shapes/filters.cljs
+++ b/frontend/src/app/main/ui/shapes/filters.cljs
@@ -97,7 +97,7 @@
(mf/defc layer-blur-filter
[{:keys [filter-id params]}]
- [:feGaussianBlur {:stdDeviation (/ (:value params) 2)
+ [:feGaussianBlur {:stdDeviation (:value params)
:result filter-id}])
(mf/defc image-fix-filter [{:keys [filter-id]}]
diff --git a/frontend/src/app/main/ui/shapes/shape.cljs b/frontend/src/app/main/ui/shapes/shape.cljs
index 086f37bcb..fd046afb1 100644
--- a/frontend/src/app/main/ui/shapes/shape.cljs
+++ b/frontend/src/app/main/ui/shapes/shape.cljs
@@ -18,27 +18,6 @@
[app.main.ui.shapes.gradients :as grad]
[app.main.ui.context :as muc]))
-(mf/defc background-blur [{:keys [shape]}]
- (when-let [background-blur-filters (->> shape :blur (remove #(= (:type %) :layer-blur)) (remove :hidden))]
- (for [filter background-blur-filters]
- [:*
-
-
- [:foreignObject {:key (str "blur_" (:id filter))
- :pointerEvents "none"
- :x (:x shape)
- :y (:y shape)
- :width (:width shape)
- :height (:height shape)
- :transform (geom/transform-matrix shape)}
- [:style ""]
- [:div.backround-blur
- {:style {:width "100%"
- :height "100%"
- ;; :backdrop-filter (str/format "blur(%spx)" (:value filter))
- :filter (str/format "blur(4px")
- }}]]])))
-
(mf/defc shape-container
{::mf/wrap-props false}
[props]
@@ -51,23 +30,12 @@
(obj/clone)
(obj/without ["shape" "children"])
(obj/set! "className" "shape")
- (obj/set! "data-type" (:type shape))
- (obj/set! "filter" (filters/filter-str filter-id shape)))
-
- ;;group-props (if (seq (:blur shape))
- ;; (obj/set! group-props "clip-path" (str/fmt "url(#%s)" (str "blur_" render-id)))
- ;; group-props)
- ]
+ (obj/set! "filter" (filters/filter-str filter-id shape)))]
[:& (mf/provider muc/render-ctx) {:value render-id}
[:> :g group-props
[:defs
[:& filters/filters {:shape shape :filter-id filter-id}]
[:& grad/gradient {:shape shape :attr :fill-color-gradient}]
- [:& grad/gradient {:shape shape :attr :stroke-color-gradient}]
-
- #_(when (:blur shape)
- [:clipPath {:id (str "blur_" render-id)}
- children])]
-
- [:& background-blur {:shape shape}]
+ [:& grad/gradient {:shape shape :attr :stroke-color-gradient}]]
+
children]]))
diff --git a/frontend/src/app/main/ui/viewer/handoff/attributes/common.cljs b/frontend/src/app/main/ui/viewer/handoff/attributes/common.cljs
index b7ccd21fb..43972d237 100644
--- a/frontend/src/app/main/ui/viewer/handoff/attributes/common.cljs
+++ b/frontend/src/app/main/ui/viewer/handoff/attributes/common.cljs
@@ -16,38 +16,13 @@
[app.util.color :as uc]
[app.common.math :as mth]
[app.main.ui.icons :as i]
+ [app.util.code-gen :as code]
[app.util.webapi :as wapi]
[app.main.ui.components.color-bullet :refer [color-bullet color-name]]))
-(defn copy-cb [values properties & {:keys [to-prop format] :or {to-prop {}}}]
+(defn copy-cb [values properties & {:keys [to-prop format] :as params}]
(fn [event]
- (let [
- ;; We allow the :format and :to-prop to be a map for different properties
- ;; or just a value for a single property. This code transform a single
- ;; property to a uniform one
- properties (if-not (coll? properties) [properties] properties)
-
- format (if (not (map? format))
- (into {} (map #(vector % format) properties))
- format)
-
- to-prop (if (not (map? to-prop))
- (into {} (map #(vector % to-prop) properties))
- to-prop)
-
- default-format (fn [value] (str (mth/precision value 2) "px"))
- format-property (fn [prop]
- (let [css-prop (or (prop to-prop) (name prop))]
- (str/fmt " %s: %s;" css-prop ((or (prop format) default-format) (prop values) values))))
-
- text-props (->> properties
- (remove #(let [value (get values %)]
- (or (nil? value) (= value 0))))
- (map format-property)
- (str/join "\n"))
-
- result (str/fmt "{\n%s\n}" text-props)]
-
+ (let [result (code/generate-css-props values properties params)]
(wapi/write-to-clipboard result))))
(mf/defc color-row [{:keys [color format on-copy on-change-format]}]
@@ -79,3 +54,4 @@
(when on-copy
[:button.attributes-copy-button {:on-click on-copy} i/copy])]))
+
diff --git a/frontend/src/app/main/ui/viewer/handoff/attributes/layout.cljs b/frontend/src/app/main/ui/viewer/handoff/attributes/layout.cljs
index 748905449..3a2dcf01f 100644
--- a/frontend/src/app/main/ui/viewer/handoff/attributes/layout.cljs
+++ b/frontend/src/app/main/ui/viewer/handoff/attributes/layout.cljs
@@ -18,8 +18,8 @@
(defn copy-layout [shape]
(copy-cb shape
- [:width :height :x :y :rotation]
- :to-prop {:x "left" :y "top" :rotation "transform"}
+ [:width :height :x :y :radius :rx]
+ :to-prop {:x "left" :y "top" :rotation "transform" :rx "border-radius"}
:format {:rotation #(str/fmt "rotate(%sdeg)" %)}))
(mf/defc layout-block
@@ -55,6 +55,14 @@
{:on-click (copy-cb shape :y :to-prop "top")}
i/copy]])
+ (when (not= (:rx shape) 0)
+ [:div.attributes-unit-row
+ [:div.attributes-label (t locale "handoff.attributes.layout.radius")]
+ [:div.attributes-value (mth/precision (:rx shape) 2) "px"]
+ [:button.attributes-copy-button
+ {:on-click (copy-cb shape :rx :to-prop "border-radius")}
+ i/copy]])
+
(when (not= (:rotation shape 0) 0)
[:div.attributes-unit-row
[:div.attributes-label (t locale "handoff.attributes.layout.rotation")]
diff --git a/frontend/src/app/main/ui/viewer/handoff/attributes/shadow.cljs b/frontend/src/app/main/ui/viewer/handoff/attributes/shadow.cljs
index d5537f153..d9b020635 100644
--- a/frontend/src/app/main/ui/viewer/handoff/attributes/shadow.cljs
+++ b/frontend/src/app/main/ui/viewer/handoff/attributes/shadow.cljs
@@ -12,21 +12,13 @@
[rumext.alpha :as mf]
[cuerdas.core :as str]
[app.util.i18n :refer [t]]
- [app.util.color :as uc]
+ [app.util.code-gen :as cg]
[app.main.ui.icons :as i]
[app.main.ui.viewer.handoff.attributes.common :refer [copy-cb color-row]]))
(defn has-shadow? [shape]
(:shadow shape))
-(defn shadow->css [shadow]
- (let [{:keys [style offset-x offset-y blur spread]} shadow
- css-color (uc/color->background (:color shadow))]
- (str
- (if (= style :inner-shadow) "inset " "")
- (str/fmt "%spx %spx %spx %spx %s" offset-x offset-y blur spread css-color))))
-
-
(mf/defc shadow-block [{:keys [shape locale shadow]}]
(let [color-format (mf/use-state :hex)]
[:div.attributes-shadow-block
@@ -52,7 +44,7 @@
{:on-click (copy-cb shadow
:style
:to-prop "box-shadow"
- :format #(shadow->css shadow))}
+ :format #(cg/shadow->css shadow))}
i/copy]]
[:& color-row {:color (:color shadow)
:format @color-format
@@ -64,7 +56,7 @@
(copy-cb (first shapes)
:shadow
:to-prop "box-shadow"
- :format #(str/join ", " (map shadow->css (:shadow (first shapes))))))]
+ :format #(str/join ", " (map cg/shadow->css (:shadow (first shapes))))))]
(when (seq shapes)
[:div.attributes-block
[:div.attributes-block-title
diff --git a/frontend/src/app/main/ui/viewer/handoff/code.cljs b/frontend/src/app/main/ui/viewer/handoff/code.cljs
index 22a2d5577..ed9daf0a4 100644
--- a/frontend/src/app/main/ui/viewer/handoff/code.cljs
+++ b/frontend/src/app/main/ui/viewer/handoff/code.cljs
@@ -10,31 +10,33 @@
(ns app.main.ui.viewer.handoff.code
(:require
["highlight.js" :as hljs]
+ [cuerdas.core :as str]
[rumext.alpha :as mf]
[app.util.i18n :as i18n]
+ [app.util.color :as uc]
+ [app.util.webapi :as wapi]
+ [app.util.code-gen :as cg]
[app.main.ui.icons :as i]
[app.common.geom.shapes :as gsh]))
-(def css-example
- "/* text layer name */
-.shape {
- width: 142px;
- height: 40px;
- border-radius: 20px;
- background-color: var(--tiffany-blue);
-}")
-
(def svg-example
- "
-
-
-")
+ "
+")
+
+
+(defn generate-markup-code [type shapes]
+ svg-example)
(mf/defc code-block [{:keys [code type]}]
(let [block-ref (mf/use-ref)]
(mf/use-effect
- (mf/deps block-ref)
+ (mf/deps code type block-ref)
(fn []
(hljs/highlightBlock (mf/ref-val block-ref))))
[:pre.code-display {:class type
@@ -42,39 +44,46 @@
(mf/defc code
[{:keys [shapes frame]}]
- (let [locale (mf/deref i18n/locale)
+ (let [style-type (mf/use-state "css")
+ markup-type (mf/use-state "svg")
+
+ locale (mf/deref i18n/locale)
shapes (->> shapes
- (map #(gsh/translate-to-frame % frame)))]
+ (map #(gsh/translate-to-frame % frame)))
+
+ style-code (cg/generate-style-code @style-type shapes)
+ markup-code (generate-markup-code @markup-type shapes)]
[:div.element-options
[:div.code-block
[:div.code-row-lang
[:select.code-selection
- [:option "CSS"]
- [:option "SASS"]
- [:option "Less"]
- [:option "Stylus"]]
+ [:option {:value "css"} "CSS"]
+ #_[:option {:value "sass"} "SASS"]
+ #_[:option {:value "less"} "Less"]
+ #_[:option {:value "stylus"} "Stylus"]]
[:button.attributes-copy-button
- {:on-click #(prn "??")}
+ {:on-click #(wapi/write-to-clipboard style-code)}
i/copy]]
[:div.code-row-display
- [:& code-block {:type "css"
- :code css-example}]]]
+ [:& code-block {:type @style-type
+ :code style-code}]]]
[:div.code-block
[:div.code-row-lang
[:select.code-selection
[:option "SVG"]
- [:option "HTML"]]
+ #_[:option "HTML"]]
[:button.attributes-copy-button
- {:on-click #(prn "??")}
+ {:on-click #(wapi/write-to-clipboard markup-code)}
i/copy]]
[:div.code-row-display
- [:& code-block {:type "svg"
- :code svg-example}]]]
+ [:& code-block {:type @markup-type
+ :code markup-code}]]]
]))
+
diff --git a/frontend/src/app/main/ui/viewer/handoff/right_sidebar.cljs b/frontend/src/app/main/ui/viewer/handoff/right_sidebar.cljs
index 1fbb6e1cb..9fecb1734 100644
--- a/frontend/src/app/main/ui/viewer/handoff/right_sidebar.cljs
+++ b/frontend/src/app/main/ui/viewer/handoff/right_sidebar.cljs
@@ -33,7 +33,7 @@
(mf/defc right-sidebar
[{:keys [frame]}]
(let [locale (mf/deref i18n/locale)
- section (mf/use-state :info #_:code)
+ section (mf/use-state #_:info :code)
selected-ref (mf/use-memo (make-selected-shapes-iref))
shapes (mf/deref selected-ref)]
[:aside.settings-bar.settings-bar-right
diff --git a/frontend/src/app/util/code_gen.cljs b/frontend/src/app/util/code_gen.cljs
new file mode 100644
index 000000000..1d2ac42b1
--- /dev/null
+++ b/frontend/src/app/util/code_gen.cljs
@@ -0,0 +1,164 @@
+;; 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 app.util.code-gen
+ (:require
+ [cuerdas.core :as str]
+ [app.common.math :as mth]
+ [app.util.text :as ut]
+ [app.util.color :as uc]))
+
+(declare format-fill-color)
+(declare format-stroke)
+(declare shadow->css)
+
+(def styles-data
+ {:layout {:props [:width :height :x :y :radius :rx]
+ :to-prop {:x "left" :y "top" :rotation "transform" :rx "border-radius"}
+ :format {:rotation #(str/fmt "rotate(%sdeg)" %)}}
+ :fill {:props [:fill-color :fill-color-gradient]
+ :to-prop {:fill-color "background" :fill-color-gradient "background"}
+ :format {:fill-color format-fill-color :fill-color-gradient format-fill-color}}
+ :stroke {:props [:stroke-color]
+ :to-prop {:stroke-color "border"}
+ :format {:stroke-color format-stroke}}
+ :shadow {:props [:shadow]
+ :to-prop {:shadow :box-shadow}
+ :format {:shadow #(str/join ", " (map shadow->css %1))}}
+ :blur {:props [:blur]
+ :to-prop {:blur "filter"}
+ :format {:blur #(str/fmt "blur(%spx)" (:value %))}}})
+
+(def style-text
+ {:props [:fill-color
+ :font-family
+ :font-style
+ :font-size
+ :line-height
+ :letter-spacing
+ :text-decoration
+ :text-transform]
+ :to-prop {:fill-color "color" }
+ :format {:font-family #(str "'" % "'")
+ :font-style #(str "'" % "'")
+ :font-size #(str % "px")
+ :line-height #(str % "px")
+ :letter-spacing #(str % "px")
+ :text-decoration name
+ :text-transform name
+ :fill-color format-fill-color}})
+
+(defn shadow->css [shadow]
+ (let [{:keys [style offset-x offset-y blur spread]} shadow
+ css-color (uc/color->background (:color shadow))]
+ (str
+ (if (= style :inner-shadow) "inset " "")
+ (str/fmt "%spx %spx %spx %spx %s" offset-x offset-y blur spread css-color))))
+
+
+(defn format-fill-color [_ shape]
+ (let [color {:color (:fill-color shape)
+ :opacity (:fill-opacity shape)
+ :gradient (:fill-color-gradient shape)
+ :id (:fill-ref-id shape)
+ :file-id (:fill-ref-file-id shape)}]
+ (uc/color->background color)))
+
+(defn format-stroke [_ shape]
+ (let [width (:stroke-width shape)
+ style (name (:stroke-style shape))
+ color {:color (:stroke-color shape)
+ :opacity (:stroke-opacity shape)
+ :gradient (:stroke-color-gradient shape)
+ :id (:stroke-ref-id shape)
+ :file-id (:stroke-ref-file-id shape)}]
+ (str/format "%spx %s %s" width style (uc/color->background color))))
+
+
+(defn generate-css-props [values properties params]
+ (let [{:keys [to-prop format tab-size] :or {to-prop {} tab-size 0}} params
+ ;; We allow the :format and :to-prop to be a map for different properties
+ ;; or just a value for a single property. This code transform a single
+ ;; property to a uniform one
+ properties (if-not (coll? properties) [properties] properties)
+
+ format (if (not (map? format))
+ (into {} (map #(vector % format) properties))
+ format)
+
+ to-prop (if (not (map? to-prop))
+ (into {} (map #(vector % to-prop) properties))
+ to-prop)
+
+ default-format (fn [value] (str (mth/precision value 2) "px"))
+ format-property (fn [prop]
+ (let [css-prop (or (prop to-prop) (name prop))
+ format-fn (or (prop format) default-format)]
+ (str
+ (str/repeat " " tab-size)
+ (str/fmt "%s: %s;" css-prop (format-fn (prop values) values)))))]
+
+ (->> properties
+ (remove #(let [value (get values %)]
+ (or (nil? value) (= value 0))))
+ (map format-property)
+ (str/join "\n"))))
+
+(defn shape->properties [shape]
+ (let [props (->> styles-data vals (mapcat :props))
+ to-prop (->> styles-data vals (map :to-prop) (reduce merge))
+ format (->> styles-data vals (map :format) (reduce merge))]
+ (generate-css-props shape props {:to-prop to-prop
+ :format format
+ :tab-size 2})))
+(defn text->properties [shape]
+ (let [text-shape-style (select-keys styles-data [:layout :shadow :blur])
+
+ shape-props (->> text-shape-style vals (mapcat :props))
+ shape-to-prop (->> text-shape-style vals (map :to-prop) (reduce merge))
+ shape-format (->> text-shape-style vals (map :format) (reduce merge))
+
+
+ text-values (->> (ut/search-text-attrs (:content shape) (:props style-text))
+ (merge ut/default-text-attrs))]
+
+ (str/join
+ "\n"
+ [(generate-css-props shape
+ shape-props
+ {:to-prop shape-to-prop
+ :format shape-format
+ :tab-size 2})
+ (generate-css-props text-values
+ (:props style-text)
+ {:to-prop (:to-prop style-text)
+ :format (:format style-text)
+ :tab-size 2})]))
+
+ )
+
+(defn generate-css [shape]
+ (let [name (:name shape)
+ properties (if (= :text (:type shape))
+ (text->properties shape)
+ (shape->properties shape))
+
+ selector (str/css-selector name)
+ selector (if (str/starts-with? selector "-") (subs selector 1) selector)]
+ (str/join "\n" [(str/fmt "/* %s */" name)
+ (str/fmt ".%s {" selector)
+ properties
+ "}"])))
+
+(defn generate-style-code [type shapes]
+ (let [generate-style-fn (case type
+ "css" generate-css)]
+ (->> shapes
+ (map generate-style-fn)
+ (str/join "\n\n"))))