From 833a53f131c05927c8e4b565d81759579e52134d Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Fri, 30 Oct 2020 14:02:35 +0100 Subject: [PATCH] :sparkles: Improvements in the handoff --- .../styles/main/layouts/handoff.scss | 42 +++++++++++- .../styles/main/partials/handoff.scss | 49 +++++++++---- .../styles/main/partials/sidebar.scss | 5 ++ .../app/main/ui/components/code_block.cljs | 23 +++++++ .../app/main/ui/components/copy_button.cljs | 35 ++++++++++ frontend/src/app/main/ui/viewer/handoff.cljs | 7 +- .../ui/viewer/handoff/attributes/blur.cljs | 23 ++++--- .../ui/viewer/handoff/attributes/common.cljs | 12 ++-- .../ui/viewer/handoff/attributes/fill.cljs | 33 ++++----- .../ui/viewer/handoff/attributes/image.cljs | 7 +- .../ui/viewer/handoff/attributes/layout.cljs | 68 ++++++++----------- .../ui/viewer/handoff/attributes/shadow.cljs | 40 ++++++----- .../ui/viewer/handoff/attributes/stroke.cljs | 49 +++++++------ .../ui/viewer/handoff/attributes/text.cljs | 67 +++++++++--------- .../src/app/main/ui/viewer/handoff/code.cljs | 47 +++++++------ .../main/ui/viewer/handoff/right_sidebar.cljs | 12 ++-- frontend/src/app/util/code_gen.cljs | 52 +++++++------- 17 files changed, 345 insertions(+), 226 deletions(-) create mode 100644 frontend/src/app/main/ui/components/code_block.cljs create mode 100644 frontend/src/app/main/ui/components/copy_button.cljs diff --git a/frontend/resources/styles/main/layouts/handoff.scss b/frontend/resources/styles/main/layouts/handoff.scss index 2eb5c2548..bd7e626a1 100644 --- a/frontend/resources/styles/main/layouts/handoff.scss +++ b/frontend/resources/styles/main/layouts/handoff.scss @@ -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; + } } diff --git a/frontend/resources/styles/main/partials/handoff.scss b/frontend/resources/styles/main/partials/handoff.scss index 70ba1be4e..ca741a3d2 100644 --- a/frontend/resources/styles/main/partials/handoff.scss +++ b/frontend/resources/styles/main/partials/handoff.scss @@ -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; diff --git a/frontend/resources/styles/main/partials/sidebar.scss b/frontend/resources/styles/main/partials/sidebar.scss index bc3e60c6d..e03353aec 100644 --- a/frontend/resources/styles/main/partials/sidebar.scss +++ b/frontend/resources/styles/main/partials/sidebar.scss @@ -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; diff --git a/frontend/src/app/main/ui/components/code_block.cljs b/frontend/src/app/main/ui/components/code_block.cljs new file mode 100644 index 000000000..f1455233c --- /dev/null +++ b/frontend/src/app/main/ui/components/code_block.cljs @@ -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])) + diff --git a/frontend/src/app/main/ui/components/copy_button.cljs b/frontend/src/app/main/ui/components/copy_button.cljs new file mode 100644 index 000000000..c23f57537 --- /dev/null +++ b/frontend/src/app/main/ui/components/copy_button.cljs @@ -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)])) diff --git a/frontend/src/app/main/ui/viewer/handoff.cljs b/frontend/src/app/main/ui/viewer/handoff.cljs index cd1aa34cd..1c22c07f1 100644 --- a/frontend/src/app/main/ui/viewer/handoff.cljs +++ b/frontend/src/app/main/ui/viewer/handoff.cljs @@ -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 diff --git a/frontend/src/app/main/ui/viewer/handoff/attributes/blur.cljs b/frontend/src/app/main/ui/viewer/handoff/attributes/blur.cljs index a84a91b9c..203f5658c 100644 --- a/frontend/src/app/main/ui/viewer/handoff/attributes/blur.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/attributes/blur.cljs @@ -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)}]])]))) 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 85c3a6875..fc50006b0 100644 --- a/frontend/src/app/main/ui/viewer/handoff/attributes/common.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/attributes/common.cljs @@ -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}])])) diff --git a/frontend/src/app/main/ui/viewer/handoff/attributes/fill.cljs b/frontend/src/app/main/ui/viewer/handoff/attributes/fill.cljs index c4c9d6e27..e433d557f 100644 --- a/frontend/src/app/main/ui/viewer/handoff/attributes/fill.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/attributes/fill.cljs @@ -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)) diff --git a/frontend/src/app/main/ui/viewer/handoff/attributes/image.cljs b/frontend/src/app/main/ui/viewer/handoff/attributes/image.cljs index 057c02369..c4fd0c639 100644 --- a/frontend/src/app/main/ui/viewer/handoff/attributes/image.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/attributes/image.cljs @@ -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" 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 4c28d0145..3aa984b7b 100644 --- a/frontend/src/app/main/ui/viewer/handoff/attributes/layout.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/attributes/layout.cljs @@ -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}])]) 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 d9b020635..44089cc46 100644 --- a/frontend/src/app/main/ui/viewer/handoff/attributes/shadow.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/attributes/shadow.cljs @@ -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] diff --git a/frontend/src/app/main/ui/viewer/handoff/attributes/stroke.cljs b/frontend/src/app/main/ui/viewer/handoff/attributes/stroke.cljs index 6dc73f410..de9f7da64 100644 --- a/frontend/src/app/main/ui/viewer/handoff/attributes/stroke.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/attributes/stroke.cljs @@ -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)) diff --git a/frontend/src/app/main/ui/viewer/handoff/attributes/text.cljs b/frontend/src/app/main/ui/viewer/handoff/attributes/text.cljs index b5847cc14..583c22280 100644 --- a/frontend/src/app/main/ui/viewer/handoff/attributes/text.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/attributes/text.cljs @@ -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]}] diff --git a/frontend/src/app/main/ui/viewer/handoff/code.cljs b/frontend/src/app/main/ui/viewer/handoff/code.cljs index 21df187b7..19ff75c57 100644 --- a/frontend/src/app/main/ui/viewer/handoff/code.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/code.cljs @@ -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 "" "") - (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 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..3a63df429 100644 --- a/frontend/src/app/main/ui/viewer/handoff/right_sidebar.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/right_sidebar.cljs @@ -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)}]]]]])]])) diff --git a/frontend/src/app/util/code_gen.cljs b/frontend/src/app/util/code_gen.cljs index f3c4f5766..f40a4f372 100644 --- a/frontend/src/app/util/code_gen.cljs +++ b/frontend/src/app/util/code_gen.cljs @@ -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))