diff --git a/frontend/resources/locales.json b/frontend/resources/locales.json index 1fd5dbfc0..08268699c 100644 --- a/frontend/resources/locales.json +++ b/frontend/resources/locales.json @@ -3366,6 +3366,16 @@ "handoff.tabs.info": "Info", "handoff.tabs.code": "Code", + "handoff.tabs.code.selected.frame": "Artboard", + "handoff.tabs.code.selected.group": "Group", + "handoff.tabs.code.selected.rect": "Rectangle", + "handoff.tabs.code.selected.circle": "Circle", + "handoff.tabs.code.selected.path": "Path", + "handoff.tabs.code.selected.curve": "Curve", + "handoff.tabs.code.selected.image": "Image", + "handoff.tabs.code.selected.text": "Text", + "handoff.tabs.code.selected.multiple": "%s Selected", + "handoff.attributes.color.hex": "HEX", "handoff.attributes.color.rgba": "RGBA", "handoff.attributes.color.hsla": "HSLA", diff --git a/frontend/resources/styles/main/partials/handoff.scss b/frontend/resources/styles/main/partials/handoff.scss index 5a1856093..eb5796ea1 100644 --- a/frontend/resources/styles/main/partials/handoff.scss +++ b/frontend/resources/styles/main/partials/handoff.scss @@ -22,6 +22,14 @@ padding-bottom: 0.5rem; font-size: $fs12; + .attributes-text-block { + border-bottom: 1px solid $color-gray-60; + } + + & :last-child{ + border-bottom: none; + } + .attributes-copy-button { visibility: hidden; opacity: 0; @@ -139,13 +147,18 @@ .attributes-content { overflow-y: auto; - max-height: 10rem; + max-height: 5rem; background: $color-gray-60; border-radius: 4px; padding: 1rem 0.5rem; color: $color-gray-10; white-space: pre-wrap; } + + .attributes-copy-button { + padding: 0.5rem; + margin-top: 0.25rem; + } } .attributes-image-row { @@ -194,6 +207,16 @@ justify-content: space-between; } + .attributes-typography-row { + position: relative; + margin: 0.5rem; + padding-right: 2rem; + + .typography-sample { + font-size: $fs16; + } + } + .download-button { display: block; text-align: center; @@ -216,7 +239,9 @@ .attributes-unit-row, .attributes-color-row, .attributes-shadow-row, - .attributes-stroke-row { + .attributes-stroke-row, + .attributes-typography-row, + .attributes-content-row { &:hover .attributes-copy-button { visibility: visible; opacity: 1; diff --git a/frontend/src/app/main/store.cljs b/frontend/src/app/main/store.cljs index 393d470a5..5814c4a5d 100644 --- a/frontend/src/app/main/store.cljs +++ b/frontend/src/app/main/store.cljs @@ -77,6 +77,13 @@ (let [page-id (get @state :current-page-id)] (logjs "state" (get-in @state [:workspace-data :pages-index page-id :objects])))) +(defn ^:export dump-object [name] + (let [page-id (get @state :current-page-id)] + (let [objects (get-in @state [:workspace-data :pages-index page-id :objects]) + target (d/seek (fn [[id shape]] (= name (:name shape))) objects)] + (->> target + (logjs "state"))))) + (defn ^:export dump-tree ([] (dump-tree false)) ([show-touched] diff --git a/frontend/src/app/main/ui/viewer/handoff/attrib_panel.cljs b/frontend/src/app/main/ui/viewer/handoff/attrib_panel.cljs index 20aa9df7c..b8888e0d4 100644 --- a/frontend/src/app/main/ui/viewer/handoff/attrib_panel.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/attrib_panel.cljs @@ -12,150 +12,271 @@ [rumext.alpha :as mf] [cuerdas.core :as str] [app.config :as cfg] + [app.util.data :as d] + [app.util.dom :as dom] [app.util.i18n :refer [locale t]] - [app.common.geom.shapes :as gsh] - [app.common.math :as mth] - [app.main.ui.icons :as i] [app.util.color :as uc] [app.util.text :as ut] + [app.common.math :as mth] + [app.common.geom.shapes :as gsh] [app.main.fonts :as fonts] + [app.main.ui.icons :as i] + [app.util.webapi :as wapi] [app.main.ui.components.color-bullet :refer [color-bullet color-name]])) -(mf/defc color-row [{:keys [color]}] +(defn copy-cb [values properties & {:keys [to-prop format] :or {to-prop {}}}] + (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)] + + (wapi/write-to-clipboard result)))) + + +(mf/defc color-row [{:keys [color format on-copy on-change-format]}] (let [locale (mf/deref locale)] [:div.attributes-color-row [:& color-bullet {:color color}] - [:* - [:& color-name {:color color}] - (when-not (:gradient color) [:div (str (* 100 (:opacity color)) "%")])] - - [:select - [:option (t locale "handoff.attributes.color.hex")] - [:option (t locale "handoff.attributes.color.rgba")] - [:option (t locale "handoff.attributes.color.hsla")]] + (if (:gradient color) + [:& color-name {:color color}] + (case format + :rgba (let [[r g b a] (->> (uc/hex->rgba (:color color) (:opacity color)) (map #(mth/precision % 2)))] + [:div (str/fmt "%s, %s, %s, %s" r g b a)]) + :hsla (let [[h s l a] (->> (uc/hex->hsla (:color color) (:opacity color)) (map #(mth/precision % 2)))] + [:div (str/fmt "%s, %s, %s, %s" h s l a)]) + [:* + [:& color-name {:color color}] + (when-not (:gradient color) [:div (str (* 100 (:opacity color)) "%")])])) - [:button.attributes-copy-button i/copy]])) + (when-not (and on-change-format (:gradient color)) + [:select {:on-change #(-> (dom/get-target-val %) keyword on-change-format)} + [:option {:value "hex"} + (t locale "handoff.attributes.color.hex")] + + [:option {:value "rgba"} + (t locale "handoff.attributes.color.rgba")] + + [:option {:value "hsla"} + (t locale "handoff.attributes.color.hsla")]]) + + (when on-copy + [:button.attributes-copy-button {:on-click on-copy} i/copy])])) (mf/defc layout-panel [{:keys [shape locale]}] [:div.attributes-block [:div.attributes-block-title [:div.attributes-block-title-text (t locale "handoff.attributes.layout")] - [:button.attributes-copy-button i/copy]] + [:button.attributes-copy-button + {:on-click (copy-cb shape + [:width :height :x :y :rotation] + :to-prop {:x "left" :y "top" :rotation "transform"} + :format {:rotation #(str/fmt "rotate(%sdeg)" %)})} + i/copy]] [: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 i/copy]] + [:button.attributes-copy-button + {:on-click (copy-cb shape :width)} + i/copy]] [: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 i/copy]] + [:button.attributes-copy-button + {:on-click (copy-cb shape :height)} + i/copy]] (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 i/copy]]) + [:button.attributes-copy-button + {:on-click (copy-cb shape :x :to-prop "left")} + i/copy]]) (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 i/copy]]) + [:button.attributes-copy-button + {:on-click (copy-cb shape :y :to-prop "top")} + i/copy]]) - (when (not= (:rotation shape) 0) + (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 i/copy]])]) + [:button.attributes-copy-button + {:on-click (copy-cb shape + :rotation + :to-prop "transform" + :format #(str/fmt "rotate(%sdeg)" %))} + i/copy]])]) (mf/defc fill-panel [{:keys [shape locale]}] - (let [{:keys [fill-color fill-opacity fill-color-gradient fill-ref-id fill-ref-file-id]} shape] - (when (or fill-color fill-color-gradient) + (let [color-format (mf/use-state :hex) + 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)} + + handle-copy (copy-cb shape + [:fill-color :fill-color-gradient] + :to-prop "background" + :format #(uc/color->background color))] + + (when (or (:color color) (:gradient color)) [:div.attributes-block [:div.attributes-block-title [:div.attributes-block-title-text (t locale "handoff.attributes.fill")] - [:button.attributes-copy-button i/copy]] + [:button.attributes-copy-button + {:on-click handle-copy} + i/copy]] - (let [color {:color fill-color - :opacity fill-opacity - :gradient fill-color-gradient - :id fill-ref-id - :file-id fill-ref-file-id}] - [:& color-row {:color color}])]))) + [:& color-row {:color color + :format @color-format + :on-change-format #(reset! color-format %) + :on-copy handle-copy}]]))) (mf/defc stroke-panel [{:keys [shape locale]}] - (when (and (:stroke-style shape) (not= (:stroke-style shape) :none)) - (let [{:keys [stroke-style stroke-alignment stroke-width - stroke-color stroke-opacity stroke-color-gradient - stroke-color-ref-id stroke-color-file-id]} shape - color {:color stroke-color - :opacity stroke-opacity - :gradient stroke-color-gradient - :id stroke-color-ref-id - :file-id stroke-color-file-id}] + (let [color-format (mf/use-state :hex) + color {:color (:stroke-color shape) + :opacity (:stroke-opacity shape) + :gradient (:stroke-color-gradient shape) + :id (:stroke-color-ref-id shape) + :file-id (:stroke-color-file-id shape)} + + handle-copy-stroke (copy-cb shape + :stroke-style + :to-prop "border" + :format #(let [width (:stroke-width %2) + style (name (:stroke-style %2)) + color (uc/color->background color)] + (str/format "%spx %s %s" width style color)))] + + (when (and (:stroke-style shape) (not= (:stroke-style shape) :none)) [:div.attributes-block [:div.attributes-block-title [:div.attributes-block-title-text (t locale "handoff.attributes.stroke")] - [:button.attributes-copy-button i/copy]] + [:button.attributes-copy-button + {:on-click handle-copy-stroke} i/copy]] - [:& color-row {:color color}] + [:& color-row {:color color + :format @color-format + :on-change-format #(reset! color-format %) + :on-copy (copy-cb shape + :stroke-color + :to-prop "border-color" + :format #(uc/color->background color))}] [:div.attributes-stroke-row [:div.attributes-label (t locale "handoff.attributes.stroke.width")] - [:div.attributes-value (str stroke-width) "px"] - [:div.attributes-value (->> stroke-style name (str "handoff.attributes.stroke.style.") (t locale))] - [:div.attributes-label (->> stroke-alignment name (str "handoff.attributes.stroke.alignment.") (t locale))] - [:button.attributes-copy-button i/copy]]]))) + [: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]]]))) + +(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 + [:div.attributes-shadow-row + [:div.attributes-label (->> shadow :style name (str "handoff.attributes.shadow.style.") (t locale))] + [:div.attributes-shadow + [:div.attributes-label (t locale "handoff.attributes.shadow.shorthand.offset-x")] + [:div.attributes-value (str (:offset-x shadow))]] + + [:div.attributes-shadow + [:div.attributes-label (t locale "handoff.attributes.shadow.shorthand.offset-y")] + [:div.attributes-value (str (:offset-y shadow))]] + + [:div.attributes-shadow + [:div.attributes-label (t locale "handoff.attributes.shadow.shorthand.blur")] + [:div.attributes-value (str (:blur shadow))]] + + [:div.attributes-shadow + [: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 #(shadow->css shadow))} + i/copy]] + [:& color-row {:color (:color shadow) + :format @color-format + :on-change-format #(reset! color-format %)}]])) (mf/defc shadow-panel [{:keys [shape locale]}] (when (seq (:shadow shape)) [:div.attributes-block [:div.attributes-block-title [:div.attributes-block-title-text (t locale "handoff.attributes.shadow")] - [:button.attributes-copy-button i/copy]] + [:button.attributes-copy-button + {:on-click (copy-cb shape + :shadow + :to-prop "box-shadow" + :format #(str/join ", " (map shadow->css (:shadow shape))))} + i/copy]] (for [shadow (:shadow shape)] - (do - (prn "???" (:spread shadow)) - [:* - [:div.attributes-shadow-row - [:div.attributes-label (->> shadow :style name (str "handoff.attributes.shadow.style.") (t locale))] - [:div.attributes-shadow - [:div.attributes-label (t locale "handoff.attributes.shadow.shorthand.offset-x")] - [:div.attributes-value (str (:offset-x shadow))]] - - [:div.attributes-shadow - [:div.attributes-label (t locale "handoff.attributes.shadow.shorthand.offset-y")] - [:div.attributes-value (str (:offset-y shadow))]] - - [:div.attributes-shadow - [:div.attributes-label (t locale "handoff.attributes.shadow.shorthand.blur")] - [:div.attributes-value (str (:blur shadow))]] - - [:div.attributes-shadow - [:div.attributes-label (t locale "handoff.attributes.shadow.shorthand.spread")] - [:div.attributes-value (str (:spread shadow))]] - - [:button.attributes-copy-button i/copy]] - [:& color-row {:color (:color shadow)}]]))])) + [:& shadow-block {:shape shape + :locale locale + :shadow shadow}])])) (mf/defc blur-panel [{:keys [shape locale]}] - (when (:blur shape) - [:div.attributes-block - [:div.attributes-block-title - [:div.attributes-block-title-text (t locale "handoff.attributes.blur")] - [:button.attributes-copy-button i/copy]] + (let [handle-copy + (copy-cb shape + :blur + :to-prop "filter" + :format #(str/fmt "blur(%spx)" %))] + (when (:blur shape) + [:div.attributes-block + [:div.attributes-block-title + [:div.attributes-block-title-text (t locale "handoff.attributes.blur")] + [:button.attributes-copy-button {:on-click handle-copy} i/copy]] - [:div.attributes-unit-row - [:div.attributes-label (t locale "handoff.attributes.blur.value")] - [:div.attributes-value (-> shape :blur :value) "px"] - [:button.attributes-copy-button i/copy]]])) + [: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 handle-copy} i/copy]]]))) (mf/defc image-panel [{:keys [shape locale]}] [:div.attributes-block @@ -163,70 +284,143 @@ [:div.attributes-image [:img {:src (cfg/resolve-media-path (-> shape :metadata :path))}]]] [:div.attributes-unit-row - [:div.attributes-label (t locale "handoff.attributes.image.width")] - [:div.attributes-value (-> shape :metadata :width) "px"]] + [: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]] [:div.attributes-unit-row - [:div.attributes-label (t locale "handoff.attributes.image.height")] - [:div.attributes-value (-> shape :metadata :height) "px"]] + [: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]] (let [filename (last (str/split (-> shape :metadata :path) "/"))] [:a.download-button {:target "_blank" :download filename :href (cfg/resolve-media-path (-> shape :metadata :path))} (t locale "handoff.attributes.image.download")])]) + +(mf/defc text-block [{:keys [shape locale text style full-style]}] + (let [color-format (mf/use-state :hex) + color {:color (:fill-color style) + :opacity (:fill-opacity style) + :gradient (:fill-color-gradient style) + :id (:fill-color-ref-id style) + :file-id (:fill-color-ref-file-id style)} + properties [:fill-color + :fill-color-gradient + :font-family + :font-style + :font-size + :line-height + :letter-spacing + :text-decoration + :text-transform] + format {:font-family identity + :font-style identity + :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)} + to-prop {:fill-color "color" + :fill-color-gradient "color"}] + [:div.attributes-text-block + [:div.attributes-typography-row + [:div.typography-sample + {:style {:font-family (:font-family full-style) + :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)} i/copy]] + + [: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]] + + (when (or (:fill-color style) (:fill-color-gradient style)) + (let [color {:color (:fill-color style) + :opacity (:fill-opacity style) + :gradient (:fill-color-gradient style) + :id (:fill-ref-id style) + :file-id (:fill-ref-file-id style)}] + [:& color-row {:format @color-format + :on-change-format #(reset! color-format %) + :color color + :on-copy (copy-cb style [:fill-color :fill-color-gradient] :to-prop to-prop :format 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]]) + + (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]]) + + (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]]) + + (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]]) + + (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]]) + + (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]]) + + (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]])])) + (mf/defc typography-panel [{:keys [shape locale]}] (let [font (ut/search-text-attrs (:content shape) (keys ut/default-text-attrs)) + + style-text-blocks (->> (keys ut/default-text-attrs) + (ut/parse-style-text-blocks (:content shape)) + (remove (fn [[style text]] (str/empty? (str/trim text)))) + (mapv (fn [[style text]] (vector (merge ut/default-text-attrs style) text)))) + font (merge ut/default-text-attrs font)] [:div.attributes-block [:div.attributes-block-title - [:div.attributes-block-title-text (t locale "handoff.attributes.typography")] - [:button.attributes-copy-button i/copy]] + [:div.attributes-block-title-text (t locale "handoff.attributes.typography")]] - [:div.attributes-unit-row - [:div.attributes-label (t locale "handoff.attributes.typography.font-family")] - [:div.attributes-value (-> font :font-id fonts/get-font-data :name)] - [:button.attributes-copy-button i/copy]] + (for [[idx [full-style text]] (map-indexed vector style-text-blocks)] + (let [previus-style (first (nth style-text-blocks (dec idx) nil)) + style (d/remove-equal-values full-style previus-style) - [:div.attributes-unit-row - [:div.attributes-label (t locale "handoff.attributes.typography.font-style")] - [:div.attributes-value (str (:font-style font))] - [:button.attributes-copy-button i/copy]] - - [:div.attributes-unit-row - [:div.attributes-label (t locale "handoff.attributes.typography.font-size")] - [:div.attributes-value (str (:font-size font)) "px"] - [:button.attributes-copy-button i/copy]] - - [:div.attributes-unit-row - [:div.attributes-label (t locale "handoff.attributes.typography.line-height")] - [:div.attributes-value (str (:line-height font)) "px"] - [:button.attributes-copy-button i/copy]] - - [:div.attributes-unit-row - [:div.attributes-label (t locale "handoff.attributes.typography.letter-spacing")] - [:div.attributes-value (str (:letter-spacing font)) "px"] - [:button.attributes-copy-button i/copy]] - - [:div.attributes-unit-row - [:div.attributes-label (t locale "handoff.attributes.typography.text-decoration")] - [:div.attributes-value (->> font :text-decoration (str "handoff.attributes.typography.text-decoration.") (t locale))] - [:button.attributes-copy-button i/copy]] - - [:div.attributes-unit-row - [:div.attributes-label (t locale "handoff.attributes.typography.text-transform")] - [:div.attributes-value (->> font :text-transform (str "handoff.attributes.typography.text-transform.") (t locale))] - [:button.attributes-copy-button i/copy]]])) - -(mf/defc content-panel [{:keys [shape locale]}] - [:div.attributes-block - [:div.attributes-block-title - [:div.attributes-block-title-text (t locale "handoff.attributes.content")] - [:button.attributes-copy-button i/copy]] - - [:div.attributes-content-row - [:pre.attributes-content (ut/content->text (:content shape))] - [:button.attributes-copy-button i/copy]]]) + ;; If the color is set we need to add opacity otherwise the display will not work + style (cond-> style + (:fill-color style) + (assoc :fill-opacity (:fill-opacity full-style)))] + [:& text-block {:shape shape + :locale locale + :full-style full-style + :style style + :text text}]))])) (mf/defc attrib-panel [{:keys [shape frame options]}] (let [locale (mf/deref locale)] @@ -240,9 +434,7 @@ :shadow shadow-panel :blur blur-panel :image image-panel - :typography typography-panel - :content content-panel - ) + :typography typography-panel) {:shape (gsh/translate-to-frame shape frame) :frame frame :locale locale}])])) 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 2098f602b..3f20d212b 100644 --- a/frontend/src/app/main/ui/viewer/handoff/attributes/layout.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/attributes/layout.cljs @@ -15,7 +15,6 @@ (mf/defc layout-panel [{:keys [shapes]}] - (prn "???" shapes) [:* [:div.attributes-block [:div.attributes-block-title diff --git a/frontend/src/app/main/ui/viewer/handoff/attributes_sidebar.cljs b/frontend/src/app/main/ui/viewer/handoff/attributes_sidebar.cljs index 11ecb2797..195628073 100644 --- a/frontend/src/app/main/ui/viewer/handoff/attributes_sidebar.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/attributes_sidebar.cljs @@ -11,10 +11,12 @@ (:require [rumext.alpha :as mf] [okulary.core :as l] + [app.util.i18n :refer [t] :as i18n] [app.main.store :as st] [app.main.ui.icons :as i] [app.main.ui.components.tab-container :refer [tab-container tab-element]] - [app.main.ui.viewer.handoff.attrib-panel :refer [attrib-panel]])) + [app.main.ui.viewer.handoff.attrib-panel :refer [attrib-panel]] + [app.main.ui.workspace.sidebar.layers :refer [element-icon]])) (defn make-selected-shapes-iref [] @@ -26,44 +28,54 @@ (mapv resolve-shape selected)))] #(l/derived selected->shapes st/state))) -(mf/defc info-panel [{:keys [frame]}] - (let [selected-ref (mf/use-memo (make-selected-shapes-iref)) - shapes (mf/deref selected-ref)] - (if (> (count shapes) 1) - ;; Multiple selection - nil - ;; Single shape - (when-let [shape (first shapes)] - (let [options - (case (:type shape) - :frame [:layout :fill] - :group [:layout] - :rect [:layout :fill :stroke :shadow :blur] - :circle [:layout :fill :stroke :shadow :blur] - :path [:layout :fill :stroke :shadow :blur] - :curve [:layout :fill :stroke :shadow :blur] - :image [:image :layout :shadow :blur] - :text [:layout :fill :typography :content :shadow :blur])] - [:& attrib-panel {:frame frame - :shape shape - :options options}]))))) +(mf/defc info-panel [{:keys [frame shapes]}] + (if (> (count shapes) 1) + ;; TODO:Multiple selection + nil + ;; Single shape + (when-let [shape (first shapes)] + (let [options + (case (:type shape) + :frame [:layout :fill] + :group [:layout] + :rect [:layout :fill :stroke :shadow :blur] + :circle [:layout :fill :stroke :shadow :blur] + :path [:layout :fill :stroke :shadow :blur] + :curve [:layout :fill :stroke :shadow :blur] + :image [:image :layout :shadow :blur] + :text [:layout :typography :shadow :blur])] + [:& attrib-panel {:frame frame + :shape shape + :options options}])))) (mf/defc code-panel [] [:div.element-options]) (mf/defc attributes-sidebar [{:keys [frame]}] - (let [section (mf/use-state :info #_:code)] + (let [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 [:div.settings-bar-inside - [:div.tool-window - [:div.tool-window-bar.big - [:span.tool-window-bar-icon i/text] - [:span.tool-window-bar-title "Text"]] - [:div.tool-window-content - [:& tab-container {:on-change-tab #(reset! section %) - :selected @section} - [:& tab-element {:id :info :title "Info"} - [:& info-panel {:frame frame}]] + (when (seq shapes) + [:div.tool-window + [:div.tool-window-bar.big + (if (> (count shapes) 1) + [:* + [:span.tool-window-bar-icon i/layers] + [:span.tool-window-bar-title (t locale "handoff.tabs.code.selected.multiple" (count shapes))]] + [:* + [:span.tool-window-bar-icon + [:& element-icon {:shape (-> shapes first)}]] + [: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 %) + :selected @section} + [:& tab-element {:id :info :title (t locale "handoff.tabs.info")} + [:& info-panel {:frame frame + :shapes shapes}]] - [:& tab-element {:id :code :title "Code"} - [:& code-panel]]]]]]])) + [:& tab-element {:id :code :title (t locale "handoff.tabs.code")} + [:& code-panel]]]]])]])) diff --git a/frontend/src/app/util/color.cljs b/frontend/src/app/util/color.cljs index 2ed47a672..f17c9c755 100644 --- a/frontend/src/app/util/color.cljs +++ b/frontend/src/app/util/color.cljs @@ -53,6 +53,11 @@ (into [] (gcolor/hexToHsl hex)) (catch :default e [0 0 0]))) +(defn hex->hsla + [^string data ^number opacity] + (-> (hex->hsl data) + (conj opacity))) + (defn hsl->rgb [[h s l]] (gcolor/hslToRgb h s l)) diff --git a/frontend/src/app/util/data.cljs b/frontend/src/app/util/data.cljs index 19a55f236..0a6c2889c 100644 --- a/frontend/src/app/util/data.cljs +++ b/frontend/src/app/util/data.cljs @@ -111,6 +111,14 @@ not-found)) not-found coll))) +(defn remove-equal-values [m1 m2] + (if (and (map? m1) (map? m2) (not (nil? m1)) (not (nil? m2))) + (->> m1 + (remove (fn [[k v]] (= (k m2) v))) + (into {})) + m1)) + + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Numbers Parsing ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/frontend/src/app/util/text.cljs b/frontend/src/app/util/text.cljs index 4ba1d4e96..e5226b04d 100644 --- a/frontend/src/app/util/text.cljs +++ b/frontend/src/app/util/text.cljs @@ -50,6 +50,36 @@ (str/join (if (= "paragraph-set" (:type node)) "\n" "") (map content->text (:children node))) (:text node "")))) +(defn parse-style-text-blocks + [node attrs] + (letfn + [(rec-style-text-map [acc node style] + (let [node-style (merge style (select-keys node attrs)) + head (or (-> acc first) [{} ""]) + [head-style head-text] head + + new-acc + (cond + (:children node) + (reduce #(rec-style-text-map %1 %2 node-style) acc (:children node)) + + (not= head-style node-style) + (cons [node-style (:text node "")] acc) + + :else + (cons [node-style (str head-text "" (:text node))] (rest acc))) + + ;; We add an end-of-line when finish a paragraph + new-acc + (if (= (:type node) "paragraph") + (let [[hs ht] (first new-acc)] + (cons [hs (str ht "\n")] (rest new-acc))) + new-acc)] + new-acc))] + + (-> (rec-style-text-map [] node {}) + reverse))) + (defn search-text-attrs [node attrs]