mirror of
https://github.com/penpot/penpot.git
synced 2025-03-28 07:31:25 -05:00
✨ Improvements in the handoff
This commit is contained in:
parent
7a80297d31
commit
833a53f131
17 changed files with 345 additions and 226 deletions
|
@ -1,3 +1,5 @@
|
|||
$width-settings-bar: 16rem;
|
||||
|
||||
.handoff-layout {
|
||||
display: grid;
|
||||
grid-template-rows: 40px auto;
|
||||
|
@ -28,6 +30,42 @@
|
|||
}
|
||||
}
|
||||
|
||||
.handoff-layout .settings-bar.settings-bar-left {
|
||||
left: 0;
|
||||
.handoff-layout {
|
||||
.viewer-preview {
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
.settings-bar {
|
||||
transition: width 0.2s;
|
||||
&.expanded {
|
||||
width: $width-settings-bar * 3;
|
||||
}
|
||||
|
||||
&.settings-bar-right,
|
||||
&.settings-bar-left {
|
||||
position: relative;
|
||||
left: unset;
|
||||
right: unset;
|
||||
|
||||
.settings-bar-inside {
|
||||
padding-top: 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.handoff-svg-wrapper {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.handoff-svg-container {
|
||||
display: grid;
|
||||
width: 100%;
|
||||
height: calc(100% - 35px);
|
||||
overflow: auto;
|
||||
align-items: center;
|
||||
justify-content: safe center;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,13 +15,12 @@
|
|||
justify-content: center;
|
||||
}
|
||||
|
||||
.attributes-copy-button {
|
||||
.expand-button,
|
||||
.copy-button {
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
|
@ -39,6 +38,19 @@
|
|||
}
|
||||
}
|
||||
|
||||
.expand-button {
|
||||
right: 24px;
|
||||
top: -1px;
|
||||
|
||||
svg {
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
}
|
||||
.copy-button {
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.attributes-block {
|
||||
user-select: text;
|
||||
|
||||
|
@ -68,7 +80,7 @@
|
|||
padding: 0.5rem;
|
||||
font-size: $fs14;
|
||||
|
||||
.attributes-copy-button {
|
||||
.copy-button {
|
||||
padding: 0.5rem;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
@ -84,7 +96,7 @@
|
|||
.attributes-value {
|
||||
width: 50%;
|
||||
}
|
||||
.attributes-copy-button {
|
||||
.copy-button {
|
||||
padding: 1rem 0.5rem;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
@ -111,7 +123,7 @@
|
|||
border-radius: $br-small;
|
||||
border: 1px solid $color-gray-60;
|
||||
}
|
||||
.attributes-copy-button {
|
||||
.copy-button {
|
||||
padding: 1rem 0.5rem;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
@ -162,7 +174,7 @@
|
|||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.attributes-copy-button {
|
||||
.copy-button {
|
||||
padding: 0.5rem;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
@ -249,9 +261,12 @@
|
|||
.attributes-stroke-row,
|
||||
.attributes-typography-row,
|
||||
.attributes-content-row {
|
||||
&:hover .attributes-copy-button {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
&:hover {
|
||||
.expand-button,
|
||||
.copy-button {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -274,9 +289,12 @@
|
|||
flex-direction: row;
|
||||
margin: 0.5rem;
|
||||
|
||||
&:hover .attributes-copy-button {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
&:hover {
|
||||
.expand-button,
|
||||
.copy-button {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.code-selection {
|
||||
|
@ -294,14 +312,15 @@
|
|||
background-position: 90% 48%;
|
||||
background-size: 8px;
|
||||
}
|
||||
.attributes-copy-button {
|
||||
.expand-button,
|
||||
.copy-button {
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.code-row-display {
|
||||
margin: 0.5rem;
|
||||
font-size: $fs12;
|
||||
font-size: $fs14;
|
||||
|
||||
.code-display {
|
||||
border-radius: 4px;
|
||||
|
|
|
@ -16,6 +16,11 @@ $width-settings-bar: 16rem;
|
|||
position: fixed;
|
||||
right: 0;
|
||||
width: $width-settings-bar;
|
||||
|
||||
&.expanded {
|
||||
width: $width-settings-bar * 3;
|
||||
}
|
||||
|
||||
z-index: 10;
|
||||
overflow-y: auto;
|
||||
|
||||
|
|
23
frontend/src/app/main/ui/components/code_block.cljs
Normal file
23
frontend/src/app/main/ui/components/code_block.cljs
Normal file
|
@ -0,0 +1,23 @@
|
|||
;; 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.main.ui.components.code-block
|
||||
(:require
|
||||
["highlight.js" :as hljs]
|
||||
[rumext.alpha :as mf]))
|
||||
|
||||
(mf/defc code-block [{:keys [code type]}]
|
||||
(let [block-ref (mf/use-ref)]
|
||||
(mf/use-effect
|
||||
(mf/deps code type block-ref)
|
||||
(fn []
|
||||
(hljs/highlightBlock (mf/ref-val block-ref))))
|
||||
[:pre.code-display {:class type
|
||||
:ref block-ref} code]))
|
||||
|
35
frontend/src/app/main/ui/components/copy_button.cljs
Normal file
35
frontend/src/app/main/ui/components/copy_button.cljs
Normal file
|
@ -0,0 +1,35 @@
|
|||
;; 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.main.ui.components.copy-button
|
||||
(:require
|
||||
[beicon.core :as rx]
|
||||
[rumext.alpha :as mf]
|
||||
[app.util.webapi :as wapi]
|
||||
[app.util.timers :as timers]
|
||||
[app.main.ui.icons :as i]))
|
||||
|
||||
(mf/defc copy-button [{:keys [data]}]
|
||||
(let [just-copied (mf/use-state false)]
|
||||
(mf/use-effect
|
||||
(mf/deps @just-copied)
|
||||
(fn []
|
||||
(when @just-copied
|
||||
(let [sub (timers/schedule 1000 #(reset! just-copied false))]
|
||||
;; On umounto we dispose the timer
|
||||
#(rx/-dispose sub)))))
|
||||
|
||||
[:button.copy-button
|
||||
{:on-click #(when-not @just-copied
|
||||
(do
|
||||
(reset! just-copied true)
|
||||
(wapi/write-to-clipboard data)))}
|
||||
(if @just-copied
|
||||
i/tick
|
||||
i/copy)]))
|
|
@ -63,9 +63,10 @@
|
|||
[:*
|
||||
[:& left-sidebar {:frame frame}]
|
||||
[:div.handoff-svg-wrapper {:on-click (handle-select-frame frame)}
|
||||
[:& render-frame-svg {:frame-id (:id frame)
|
||||
:zoom (:zoom local)
|
||||
:objects objects}]]
|
||||
[:div.handoff-svg-container
|
||||
[:& render-frame-svg {:frame-id (:id frame)
|
||||
:zoom (:zoom local)
|
||||
:objects objects}]]]
|
||||
[:& right-sidebar {:frame frame}]])]))
|
||||
|
||||
(mf/defc handoff-content
|
||||
|
|
|
@ -13,29 +13,30 @@
|
|||
[cuerdas.core :as str]
|
||||
[app.util.i18n :refer [t]]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.main.ui.viewer.handoff.attributes.common :refer [copy-cb]]))
|
||||
[app.util.code-gen :as cg]
|
||||
[app.main.ui.components.copy-button :refer [copy-button]]))
|
||||
|
||||
(defn has-blur? [shape]
|
||||
(:blur shape))
|
||||
|
||||
(defn copy-blur [shape]
|
||||
(copy-cb shape
|
||||
:blur
|
||||
:to-prop "filter"
|
||||
:format #(str/fmt "blur(%spx)" (:value %))))
|
||||
(defn copy-data [shape]
|
||||
(cg/generate-css-props
|
||||
shape
|
||||
:blur
|
||||
{:to-prop "filter"
|
||||
:format #(str/fmt "blur(%spx)" (:value %))}))
|
||||
|
||||
(mf/defc blur-panel [{:keys [shapes locale]}]
|
||||
(let [shapes (->> shapes (filter has-blur?))
|
||||
handle-copy (when (= (count shapes) 1) (copy-blur (first shapes)))]
|
||||
(let [shapes (->> shapes (filter has-blur?))]
|
||||
(when (seq shapes)
|
||||
[:div.attributes-block
|
||||
[:div.attributes-block-title
|
||||
[:div.attributes-block-title-text (t locale "handoff.attributes.blur")]
|
||||
(when handle-copy
|
||||
[:button.attributes-copy-button {:on-click handle-copy} i/copy])]
|
||||
(when (= (count shapes) 1)
|
||||
[:& copy-button {:data (copy-data (first shapes))}])]
|
||||
|
||||
(for [shape shapes]
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.blur.value")]
|
||||
[:div.attributes-value (-> shape :blur :value) "px"]
|
||||
[:button.attributes-copy-button {:on-click (copy-blur shape)} i/copy]])])))
|
||||
[:& copy-button {:data (copy-data shape)}]])])))
|
||||
|
|
|
@ -18,14 +18,10 @@
|
|||
[app.main.ui.icons :as i]
|
||||
[app.util.code-gen :as cg]
|
||||
[app.util.webapi :as wapi]
|
||||
[app.main.ui.components.copy-button :refer [copy-button]]
|
||||
[app.main.ui.components.color-bullet :refer [color-bullet color-name]]))
|
||||
|
||||
(defn copy-cb [values properties & {:keys [to-prop format] :as params}]
|
||||
(fn [event]
|
||||
(let [result (cg/generate-css-props values properties params)]
|
||||
(wapi/write-to-clipboard result))))
|
||||
|
||||
(mf/defc color-row [{:keys [color format on-copy on-change-format]}]
|
||||
(mf/defc color-row [{:keys [color format copy-data on-change-format]}]
|
||||
(let [locale (mf/deref i18n/locale)]
|
||||
[:div.attributes-color-row
|
||||
[:& color-bullet {:color color}]
|
||||
|
@ -52,6 +48,6 @@
|
|||
[:option {:value "hsla"}
|
||||
(t locale "handoff.attributes.color.hsla")]])
|
||||
|
||||
(when on-copy
|
||||
[:button.attributes-copy-button {:on-click on-copy} i/copy])]))
|
||||
(when copy-data
|
||||
[:& copy-button {:data copy-data}])]))
|
||||
|
||||
|
|
|
@ -13,7 +13,9 @@
|
|||
[app.util.i18n :refer [t]]
|
||||
[app.util.color :as uc]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.main.ui.viewer.handoff.attributes.common :refer [copy-cb color-row]]))
|
||||
[app.util.code-gen :as cg]
|
||||
[app.main.ui.components.copy-button :refer [copy-button]]
|
||||
[app.main.ui.viewer.handoff.attributes.common :refer [color-row]]))
|
||||
|
||||
(def fill-attributes [:fill-color :fill-color-gradient])
|
||||
|
||||
|
@ -30,36 +32,31 @@
|
|||
(or (:fill-color shape)
|
||||
(:fill-color-gradient shape))))
|
||||
|
||||
(defn copy-data [shape]
|
||||
(cg/generate-css-props
|
||||
shape
|
||||
fill-attributes
|
||||
{:to-prop "background"
|
||||
:format #(uc/color->background (shape->color shape))}))
|
||||
|
||||
(mf/defc fill-block [{:keys [shape locale]}]
|
||||
(let [color-format (mf/use-state :hex)
|
||||
color (shape->color shape)
|
||||
handle-copy (copy-cb shape
|
||||
fill-attributes
|
||||
:to-prop "background"
|
||||
:format #(uc/color->background color))]
|
||||
color (shape->color shape)]
|
||||
|
||||
[:& color-row {:color color
|
||||
:format @color-format
|
||||
:on-change-format #(reset! color-format %)
|
||||
:on-copy handle-copy}]))
|
||||
:copy-data (copy-data shape)}]))
|
||||
|
||||
(mf/defc fill-panel
|
||||
[{:keys [shapes locale]}]
|
||||
(let [shapes (->> shapes (filter has-color?))
|
||||
handle-copy (when (= (count shapes) 1)
|
||||
(copy-cb (first shapes)
|
||||
fill-attributes
|
||||
:to-prop "background"
|
||||
:format #(-> shapes first shape->color uc/color->background)))]
|
||||
|
||||
(let [shapes (->> shapes (filter has-color?))]
|
||||
(when (seq shapes)
|
||||
[:div.attributes-block
|
||||
[:div.attributes-block-title
|
||||
[:div.attributes-block-title-text (t locale "handoff.attributes.fill")]
|
||||
(when handle-copy
|
||||
[:button.attributes-copy-button
|
||||
{:on-click handle-copy}
|
||||
i/copy])]
|
||||
(when (= (count shapes) 1)
|
||||
[:& copy-button {:data (copy-data (first shapes))}])]
|
||||
|
||||
(for [shape shapes]
|
||||
[:& fill-block {:key (str "fill-block-" (:id shape))
|
||||
|
|
|
@ -14,7 +14,8 @@
|
|||
[app.config :as cfg]
|
||||
[app.util.i18n :refer [t]]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.main.ui.viewer.handoff.attributes.common :refer [copy-cb]]))
|
||||
[app.util.code-gen :as cg]
|
||||
[app.main.ui.components.copy-button :refer [copy-button]]))
|
||||
|
||||
(defn has-image? [shape]
|
||||
(and (= (:type shape) :image)))
|
||||
|
@ -30,12 +31,12 @@
|
|||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.image.width")]
|
||||
[:div.attributes-value (-> shape :metadata :width) "px"]
|
||||
[:button.attributes-copy-button {:on-click (copy-cb shape :width)} i/copy]]
|
||||
[:& copy-button {:data (cg/generate-css-props shape :width)}]]
|
||||
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.image.height")]
|
||||
[:div.attributes-value (-> shape :metadata :height) "px"]
|
||||
[:button.attributes-copy-button {:on-click (copy-cb shape :height)} i/copy]]
|
||||
[:& copy-button {:data (cg/generate-css-props shape :height)}]]
|
||||
|
||||
(let [filename (last (str/split (-> shape :metadata :path) "/"))]
|
||||
[:a.download-button {:target "_blank"
|
||||
|
|
|
@ -14,13 +14,22 @@
|
|||
[app.util.i18n :refer [t]]
|
||||
[app.common.math :as mth]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.main.ui.viewer.handoff.attributes.common :refer [copy-cb]]))
|
||||
[app.util.code-gen :as cg]
|
||||
[app.main.ui.components.copy-button :refer [copy-button]]))
|
||||
|
||||
(defn copy-layout [shape]
|
||||
(copy-cb shape
|
||||
[:width :height :x :y :radius :rx]
|
||||
:to-prop {:x "left" :y "top" :rotation "transform" :rx "border-radius"}
|
||||
:format {:rotation #(str/fmt "rotate(%sdeg)" %)}))
|
||||
(def properties [:width :height :x :y :radius :rx])
|
||||
(def params
|
||||
{:to-prop {:x "left"
|
||||
:y "top"
|
||||
:rotation "transform"
|
||||
:rx "border-radius"}
|
||||
:format {:rotation #(str/fmt "rotate(%sdeg)" %)}})
|
||||
|
||||
(defn copy-data
|
||||
([shape]
|
||||
(apply copy-data shape properties))
|
||||
([shape & properties]
|
||||
(cg/generate-css-props shape properties params)))
|
||||
|
||||
(mf/defc layout-block
|
||||
[{:keys [shape locale]}]
|
||||
|
@ -28,65 +37,46 @@
|
|||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.layout.width")]
|
||||
[:div.attributes-value (mth/precision (:width shape) 2) "px"]
|
||||
[:button.attributes-copy-button
|
||||
{:on-click (copy-cb shape :width)}
|
||||
i/copy]]
|
||||
[:& copy-button {:data (copy-data shape :width)}]]
|
||||
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.layout.height")]
|
||||
[:div.attributes-value (mth/precision (:height shape) 2) "px"]
|
||||
[:button.attributes-copy-button
|
||||
{:on-click (copy-cb shape :height)}
|
||||
i/copy]]
|
||||
[:& copy-button {:data (copy-data shape :height)}]]
|
||||
|
||||
(when (not= (:x shape) 0)
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.layout.left")]
|
||||
[:div.attributes-value (mth/precision (:x shape) 2) "px"]
|
||||
[:button.attributes-copy-button
|
||||
{:on-click (copy-cb shape :x :to-prop "left")}
|
||||
i/copy]])
|
||||
[:& copy-button {:data (copy-data shape :x)}]])
|
||||
|
||||
(when (not= (:y shape) 0)
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.layout.top")]
|
||||
[:div.attributes-value (mth/precision (:y shape) 2) "px"]
|
||||
[:button.attributes-copy-button
|
||||
{:on-click (copy-cb shape :y :to-prop "top")}
|
||||
i/copy]])
|
||||
[:& copy-button {:data (copy-data shape :y)}]])
|
||||
|
||||
(when (and (:rx shape) (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]])
|
||||
[:& copy-button {:data (copy-data shape :rx)}]])
|
||||
|
||||
(when (not= (:rotation shape 0) 0)
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.layout.rotation")]
|
||||
[:div.attributes-value (mth/precision (:rotation shape) 2) "deg"]
|
||||
[:button.attributes-copy-button
|
||||
{:on-click (copy-cb shape
|
||||
:rotation
|
||||
:to-prop "transform"
|
||||
:format #(str/fmt "rotate(%sdeg)" %))}
|
||||
i/copy]])])
|
||||
[:& copy-button {:data (copy-data shape :rotation)}]])])
|
||||
|
||||
|
||||
(mf/defc layout-panel
|
||||
[{:keys [shapes locale]}]
|
||||
(let [handle-copy (when (= (count shapes) 1)
|
||||
(copy-layout (first shapes)))]
|
||||
[:div.attributes-block
|
||||
[:div.attributes-block-title
|
||||
[:div.attributes-block-title-text (t locale "handoff.attributes.layout")]
|
||||
(when handle-copy
|
||||
[:button.attributes-copy-button
|
||||
{:on-click handle-copy}
|
||||
i/copy])]
|
||||
[:div.attributes-block
|
||||
[:div.attributes-block-title
|
||||
[:div.attributes-block-title-text (t locale "handoff.attributes.layout")]
|
||||
(when (= (count shapes) 1)
|
||||
[:& copy-button {:data (copy-data (first shapes))}])]
|
||||
|
||||
(for [shape shapes]
|
||||
[:& layout-block {:shape shape
|
||||
:locale locale}])]))
|
||||
(for [shape shapes]
|
||||
[:& layout-block {:shape shape
|
||||
:locale locale}])])
|
||||
|
|
|
@ -14,13 +14,30 @@
|
|||
[app.util.i18n :refer [t]]
|
||||
[app.util.code-gen :as cg]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.main.ui.viewer.handoff.attributes.common :refer [copy-cb color-row]]))
|
||||
[app.util.code-gen :as cg]
|
||||
[app.main.ui.components.copy-button :refer [copy-button]]
|
||||
[app.main.ui.viewer.handoff.attributes.common :refer [color-row]]))
|
||||
|
||||
(defn has-shadow? [shape]
|
||||
(:shadow shape))
|
||||
|
||||
(defn shape-copy-data [shape]
|
||||
(cg/generate-css-props
|
||||
shape
|
||||
:shadow
|
||||
{:to-prop "box-shadow"
|
||||
:format #(str/join ", " (map cg/shadow->css (:shadow shape)))}))
|
||||
|
||||
(defn shadow-copy-data [shadow]
|
||||
(cg/generate-css-props
|
||||
shadow
|
||||
:style
|
||||
{:to-prop "box-shadow"
|
||||
:format #(cg/shadow->css shadow)}))
|
||||
|
||||
(mf/defc shadow-block [{:keys [shape locale shadow]}]
|
||||
(let [color-format (mf/use-state :hex)]
|
||||
(let [color-format (mf/use-state :hex)
|
||||
copy-data (shadow-copy-data shadow)]
|
||||
[:div.attributes-shadow-block
|
||||
[:div.attributes-shadow-row
|
||||
[:div.attributes-label (->> shadow :style name (str "handoff.attributes.shadow.style.") (t locale))]
|
||||
|
@ -40,29 +57,20 @@
|
|||
[:div.attributes-label (t locale "handoff.attributes.shadow.shorthand.spread")]
|
||||
[:div.attributes-value (str (:spread shadow))]]
|
||||
|
||||
[:button.attributes-copy-button
|
||||
{:on-click (copy-cb shadow
|
||||
:style
|
||||
:to-prop "box-shadow"
|
||||
:format #(cg/shadow->css shadow))}
|
||||
i/copy]]
|
||||
[:& copy-button {:data (shadow-copy-data shadow)}]]
|
||||
|
||||
[:& color-row {:color (:color shadow)
|
||||
:format @color-format
|
||||
:on-change-format #(reset! color-format %)}]]))
|
||||
|
||||
(mf/defc shadow-panel [{:keys [shapes locale]}]
|
||||
(let [shapes (->> shapes (filter has-shadow?))
|
||||
handle-copy-shadow (when (= (count shapes) 1)
|
||||
(copy-cb (first shapes)
|
||||
:shadow
|
||||
:to-prop "box-shadow"
|
||||
:format #(str/join ", " (map cg/shadow->css (:shadow (first shapes))))))]
|
||||
(let [shapes (->> shapes (filter has-shadow?))]
|
||||
(when (seq shapes)
|
||||
[:div.attributes-block
|
||||
[:div.attributes-block-title
|
||||
[:div.attributes-block-title-text (t locale "handoff.attributes.shadow")]
|
||||
(when handle-copy-shadow
|
||||
[:button.attributes-copy-button {:on-click handle-copy-shadow} i/copy])]
|
||||
(when (= (count shapes) 1)
|
||||
[:& copy-button {:data (shape-copy-data (first shapes))}])]
|
||||
|
||||
[:div.attributes-shadow-blocks
|
||||
(for [shape shapes]
|
||||
|
|
|
@ -14,7 +14,9 @@
|
|||
[app.util.i18n :refer [t]]
|
||||
[app.util.color :as uc]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.main.ui.viewer.handoff.attributes.common :refer [copy-cb color-row]]))
|
||||
[app.util.code-gen :as cg]
|
||||
[app.main.ui.components.copy-button :refer [copy-button]]
|
||||
[app.main.ui.viewer.handoff.attributes.common :refer [color-row]]))
|
||||
|
||||
(defn shape->color [shape]
|
||||
{:color (:stroke-color shape)
|
||||
|
@ -33,49 +35,46 @@
|
|||
(and (:stroke-style shape)
|
||||
(not= (:stroke-style shape) :none)))
|
||||
|
||||
(defn copy-stroke-data [shape]
|
||||
(cg/generate-css-props
|
||||
shape
|
||||
:stroke-style
|
||||
{:to-prop "border"
|
||||
:format #(format-stroke shape)}))
|
||||
|
||||
(defn copy-color-data [shape]
|
||||
(cg/generate-css-props
|
||||
shape
|
||||
:stroke-color
|
||||
{:to-prop "border-color"
|
||||
:format #(uc/color->background (shape->color shape))}))
|
||||
|
||||
(mf/defc stroke-block
|
||||
[{:keys [shape locale]}]
|
||||
(let [color-format (mf/use-state :hex)
|
||||
color (shape->color shape)
|
||||
handle-copy-stroke (copy-cb shape
|
||||
:stroke-style
|
||||
:to-prop "border"
|
||||
:format #(format-stroke shape))
|
||||
|
||||
handle-copy-color (copy-cb shape
|
||||
:stroke-color
|
||||
:to-prop "border-color"
|
||||
:format #(uc/color->background color))]
|
||||
|
||||
color (shape->color shape)]
|
||||
[:*
|
||||
[:& color-row {:color color
|
||||
:format @color-format
|
||||
:on-change-format #(reset! color-format %)
|
||||
:on-copy handle-copy-color}]
|
||||
:copy-data (copy-color-data shape)
|
||||
:on-change-format #(reset! color-format %)}]
|
||||
|
||||
[:div.attributes-stroke-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.stroke.width")]
|
||||
[:div.attributes-value (:stroke-width shape) "px"]
|
||||
[:div.attributes-value (->> shape :stroke-style name (str "handoff.attributes.stroke.style.") (t locale))]
|
||||
[:div.attributes-label (->> shape :stroke-alignment name (str "handoff.attributes.stroke.alignment.") (t locale))]
|
||||
[:button.attributes-copy-button {:on-click handle-copy-stroke} i/copy]]]))
|
||||
[:& copy-button {:data (copy-stroke-data shape)}]]]))
|
||||
|
||||
(mf/defc stroke-panel
|
||||
[{:keys [shapes locale]}]
|
||||
(let [shapes (->> shapes (filter has-stroke?))
|
||||
handle-copy (when (= (count shapes) 1)
|
||||
(copy-cb (first shapes)
|
||||
:stroke-style
|
||||
:to-prop "border"
|
||||
:format #(format-stroke (first shapes))))]
|
||||
|
||||
(let [shapes (->> shapes (filter has-stroke?))]
|
||||
(when (seq shapes)
|
||||
[:div.attributes-block
|
||||
[:div.attributes-block-title
|
||||
[:div.attributes-block-title-text (t locale "handoff.attributes.stroke")]
|
||||
(when handle-copy
|
||||
[:button.attributes-copy-button
|
||||
{:on-click handle-copy} i/copy])]
|
||||
(when (= (count shapes) 1)
|
||||
[:& copy-button {:data (copy-stroke-data (first shapes))}])]
|
||||
|
||||
(for [shape shapes]
|
||||
[:& stroke-block {:key (str "stroke-color-" (:id shape))
|
||||
|
|
|
@ -18,7 +18,9 @@
|
|||
[app.main.fonts :as fonts]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.util.webapi :as wapi]
|
||||
[app.main.ui.viewer.handoff.attributes.common :refer [copy-cb color-row]]))
|
||||
[app.main.ui.viewer.handoff.attributes.common :refer [color-row]]
|
||||
[app.util.code-gen :as cg]
|
||||
[app.main.ui.components.copy-button :refer [copy-button]]))
|
||||
|
||||
(defn has-text? [shape]
|
||||
(:content shape))
|
||||
|
@ -40,22 +42,28 @@
|
|||
:id (:fill-ref-id shape)
|
||||
:file-id (:fill-ref-file-id shape)})
|
||||
|
||||
(defn format-style [color]
|
||||
{: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 #(uc/color->background color)
|
||||
:fill-color-gradient #(uc/color->background color)})
|
||||
(def params
|
||||
{:to-prop {:fill-color "color"
|
||||
:fill-color-gradient "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 #(-> %2 shape->color uc/color->background)
|
||||
:fill-color-gradient #(-> %2 shape->color uc/color->background)}})
|
||||
|
||||
(defn copy-style-data
|
||||
([style]
|
||||
(cg/generate-css-props style properties params))
|
||||
([style & properties]
|
||||
(cg/generate-css-props style properties params)))
|
||||
|
||||
(mf/defc typography-block [{:keys [shape locale text style full-style]}]
|
||||
(let [color-format (mf/use-state :hex)
|
||||
color (shape->color style)
|
||||
to-prop {:fill-color "color"
|
||||
:fill-color-gradient "color"}]
|
||||
color (shape->color style)]
|
||||
[:div.attributes-text-block
|
||||
[:div.attributes-typography-row
|
||||
[:div.typography-sample
|
||||
|
@ -63,68 +71,59 @@
|
|||
:font-weight (:font-weight full-style)
|
||||
:font-style (:font-style full-style)}}
|
||||
(t locale "workspace.assets.typography.sample")]
|
||||
[:button.attributes-copy-button
|
||||
{:on-click (copy-cb style properties
|
||||
:to-prop to-prop
|
||||
:format (format-style color))}
|
||||
i/copy]]
|
||||
[:& copy-button {:data (copy-style-data style)}]]
|
||||
|
||||
[:div.attributes-content-row
|
||||
[:pre.attributes-content (str/trim text)]
|
||||
[:button.attributes-copy-button
|
||||
{:on-click #(wapi/write-to-clipboard (str/trim text))}
|
||||
i/copy]]
|
||||
[:& copy-button {:data (str/trim text)}]]
|
||||
|
||||
(when (or (:fill-color style) (:fill-color-gradient style))
|
||||
[:& color-row {:format @color-format
|
||||
:on-change-format #(reset! color-format %)
|
||||
:color (shape->color style)
|
||||
:on-copy (copy-cb style
|
||||
[:fill-color :fill-color-gradient]
|
||||
:to-prop to-prop
|
||||
:format (format-style color))}])
|
||||
:copy-data (copy-style-data style :fill-color :fill-color-gradient)
|
||||
:on-change-format #(reset! color-format %)}])
|
||||
|
||||
(when (:font-id style)
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.typography.font-family")]
|
||||
[:div.attributes-value (-> style :font-id fonts/get-font-data :name)]
|
||||
[:button.attributes-copy-button {:on-click (copy-cb style :font-family :format identity)} i/copy]])
|
||||
[:& copy-button {:data (copy-style-data style :font-family)}]])
|
||||
|
||||
(when (:font-style style)
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.typography.font-style")]
|
||||
[:div.attributes-value (str (:font-style style))]
|
||||
[:button.attributes-copy-button {:on-click (copy-cb style :font-style :format identity)} i/copy]])
|
||||
[:& copy-button {:data (copy-style-data style :font-style)}]])
|
||||
|
||||
(when (:font-size style)
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.typography.font-size")]
|
||||
[:div.attributes-value (str (:font-size style)) "px"]
|
||||
[:button.attributes-copy-button {:on-click (copy-cb style :font-size :format #(str % "px"))} i/copy]])
|
||||
[:& copy-button {:data (copy-style-data style :font-size)}]])
|
||||
|
||||
(when (:line-height style)
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.typography.line-height")]
|
||||
[:div.attributes-value (str (:line-height style)) "px"]
|
||||
[:button.attributes-copy-button {:on-click (copy-cb style :line-height :format #(str % "px"))} i/copy]])
|
||||
[:& copy-button {:data (copy-style-data style :line-height)}]])
|
||||
|
||||
(when (:letter-spacing style)
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.typography.letter-spacing")]
|
||||
[:div.attributes-value (str (:letter-spacing style)) "px"]
|
||||
[:button.attributes-copy-button {:on-click (copy-cb style :letter-spacing :format #(str % "px"))} i/copy]])
|
||||
[:& copy-button {:data (copy-style-data style :letter-spacing)}]])
|
||||
|
||||
(when (:text-decoration style)
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.typography.text-decoration")]
|
||||
[:div.attributes-value (->> style :text-decoration (str "handoff.attributes.typography.text-decoration.") (t locale))]
|
||||
[:button.attributes-copy-button {:on-click (copy-cb style :text-decoration :format name)} i/copy]])
|
||||
[:& copy-button {:data (copy-style-data style :text-decoration)}]])
|
||||
|
||||
(when (:text-transform style)
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.typography.text-transform")]
|
||||
[:div.attributes-value (->> style :text-transform (str "handoff.attributes.typography.text-transform.") (t locale))]
|
||||
[:button.attributes-copy-button {:on-click (copy-cb style :text-transform :format name)} i/copy]])]))
|
||||
[:& copy-button {:data (copy-style-data style :text-transform)}]])]))
|
||||
|
||||
|
||||
(mf/defc text-block [{:keys [shape locale]}]
|
||||
|
|
|
@ -9,17 +9,16 @@
|
|||
|
||||
(ns app.main.ui.viewer.handoff.code
|
||||
(:require
|
||||
["highlight.js" :as hljs]
|
||||
["js-beautify" :as beautify]
|
||||
[cuerdas.core :as str]
|
||||
[rumext.alpha :as mf]
|
||||
[app.util.i18n :as i18n]
|
||||
[app.util.color :as uc]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.webapi :as wapi]
|
||||
[app.util.code-gen :as cg]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.common.geom.shapes :as gsh]))
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.main.ui.components.copy-button :refer [copy-button]]
|
||||
[app.main.ui.components.code-block :refer [code-block]]))
|
||||
|
||||
(defn generate-markup-code [type shapes]
|
||||
(let [frame (dom/query js/document "#svg-frame")
|
||||
|
@ -35,22 +34,15 @@
|
|||
(remove nil?)
|
||||
(str/join "\n\n"))))
|
||||
|
||||
(mf/defc code-block [{:keys [code type]}]
|
||||
(defn format-code [code type]
|
||||
(let [code (-> code
|
||||
(str/replace "<defs></defs>" "")
|
||||
(str/replace "><" ">\n<"))
|
||||
code (cond-> code
|
||||
(= type "svg") (beautify/html #js {"indent_size" 2}))
|
||||
block-ref (mf/use-ref)]
|
||||
(mf/use-effect
|
||||
(mf/deps code type block-ref)
|
||||
(fn []
|
||||
(hljs/highlightBlock (mf/ref-val block-ref))))
|
||||
[:pre.code-display {:class type
|
||||
:ref block-ref} code]))
|
||||
(str/replace "><" ">\n<"))]
|
||||
(cond-> code
|
||||
(= type "svg") (beautify/html #js {"indent_size" 2}))))
|
||||
|
||||
(mf/defc code
|
||||
[{:keys [shapes frame]}]
|
||||
[{:keys [shapes frame on-expand]}]
|
||||
(let [style-type (mf/use-state "css")
|
||||
markup-type (mf/use-state "svg")
|
||||
|
||||
|
@ -58,8 +50,11 @@
|
|||
shapes (->> shapes
|
||||
(map #(gsh/translate-to-frame % frame)))
|
||||
|
||||
style-code (cg/generate-style-code @style-type shapes)
|
||||
markup-code (mf/use-memo (mf/deps shapes) #(generate-markup-code @markup-type shapes))]
|
||||
style-code (-> (cg/generate-style-code @style-type shapes)
|
||||
(format-code "css"))
|
||||
|
||||
markup-code (-> (mf/use-memo (mf/deps shapes) #(generate-markup-code @markup-type shapes))
|
||||
(format-code "svg"))]
|
||||
[:div.element-options
|
||||
[:div.code-block
|
||||
[:div.code-row-lang
|
||||
|
@ -69,9 +64,11 @@
|
|||
#_[:option {:value "less"} "Less"]
|
||||
#_[:option {:value "stylus"} "Stylus"]]
|
||||
|
||||
[:button.attributes-copy-button
|
||||
{:on-click #(wapi/write-to-clipboard style-code)}
|
||||
i/copy]]
|
||||
[:button.expand-button
|
||||
{:on-click on-expand }
|
||||
i/full-screen]
|
||||
|
||||
[:& copy-button { :data style-code }]]
|
||||
|
||||
[:div.code-row-display
|
||||
[:& code-block {:type @style-type
|
||||
|
@ -83,9 +80,11 @@
|
|||
[:option "SVG"]
|
||||
[:option "HTML"]]
|
||||
|
||||
[:button.attributes-copy-button
|
||||
{:on-click #(wapi/write-to-clipboard markup-code)}
|
||||
i/copy]]
|
||||
[:button.expand-button
|
||||
{:on-click on-expand}
|
||||
i/full-screen]
|
||||
|
||||
[:& copy-button { :data markup-code }]]
|
||||
|
||||
[:div.code-row-display
|
||||
[:& code-block {:type @markup-type
|
||||
|
|
|
@ -32,11 +32,12 @@
|
|||
|
||||
(mf/defc right-sidebar
|
||||
[{:keys [frame]}]
|
||||
(let [locale (mf/deref i18n/locale)
|
||||
(let [expanded (mf/use-state false)
|
||||
locale (mf/deref i18n/locale)
|
||||
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
|
||||
[:aside.settings-bar.settings-bar-right {:class (when @expanded "expanded")}
|
||||
[:div.settings-bar-inside
|
||||
(when (seq shapes)
|
||||
[:div.tool-window
|
||||
|
@ -51,7 +52,9 @@
|
|||
[:span.tool-window-bar-title (->> shapes first :type name (str "handoff.tabs.code.selected.") (t locale))]])
|
||||
]
|
||||
[:div.tool-window-content
|
||||
[:& tab-container {:on-change-tab #(reset! section %)
|
||||
[:& tab-container {:on-change-tab #(do
|
||||
(reset! expanded false)
|
||||
(reset! section %))
|
||||
:selected @section}
|
||||
[:& tab-element {:id :info :title (t locale "handoff.tabs.info")}
|
||||
[:& attributes {:frame frame
|
||||
|
@ -59,4 +62,5 @@
|
|||
|
||||
[:& tab-element {:id :code :title (t locale "handoff.tabs.code")}
|
||||
[:& code {:frame frame
|
||||
:shapes shapes}]]]]])]]))
|
||||
:shapes shapes
|
||||
:on-expand #(swap! expanded not)}]]]]])]]))
|
||||
|
|
|
@ -77,34 +77,38 @@
|
|||
:fill-color format-fill-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)
|
||||
(defn generate-css-props
|
||||
([values properties]
|
||||
(generate-css-props values properties nil))
|
||||
|
||||
format (if (not (map? format))
|
||||
(into {} (map #(vector % format) properties))
|
||||
format)
|
||||
([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)
|
||||
|
||||
to-prop (if (not (map? to-prop))
|
||||
(into {} (map #(vector % to-prop) properties))
|
||||
to-prop)
|
||||
format (if (not (map? format))
|
||||
(into {} (map #(vector % format) properties))
|
||||
format)
|
||||
|
||||
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)))))]
|
||||
to-prop (if (not (map? to-prop))
|
||||
(into {} (map #(vector % to-prop) properties))
|
||||
to-prop)
|
||||
|
||||
(->> properties
|
||||
(remove #(let [value (get values %)]
|
||||
(or (nil? value) (= value 0))))
|
||||
(map format-property)
|
||||
(str/join "\n"))))
|
||||
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))
|
||||
|
|
Loading…
Add table
Reference in a new issue