🎉 Add copy shortands button to panels (#7580)

* 🎉 Add copy shorthands button to panels
* 🎉 Add shorthand for strokes
* 🎉 Add shorthand for fonts
* 🎉 Add shorthand for borders
* 🎉 Add shorthand for padding
* 🎉 Add shorthand for grid
* 🎉 Add shorthand for layout element
* 🐛 Refactor to fix hook rendering
This commit is contained in:
Xaviju 2025-10-29 13:51:36 +01:00 committed by GitHub
parent 034463e63a
commit 6c824651df
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 483 additions and 231 deletions

View File

@ -77,7 +77,7 @@
(:content shape)) (:content shape))
(defn- has-shadow? [shape] (defn- has-shadow? [shape]
(:shadow shape)) (seq (:shadow shape)))
(defn- get-shape-type (defn- get-shape-type
[shapes first-shape first-component] [shapes first-shape first-component]
@ -111,7 +111,26 @@
(not (or (= shape-type :text) (= shape-type :group))) (not (or (= shape-type :text) (= shape-type :group)))
(or (:opacity shape) (or (:opacity shape)
(:blend-mode shape) (:blend-mode shape)
(:visibility shape))))))] (:visibility shape))))))
shorthands* (mf/use-state #(do {:fill nil
:stroke nil
:text nil
:shadow nil
:blur nil
:layout nil
:layout-element nil
:geometry nil
:svg nil
:visibility nil
:variant nil
:grid-element nil}))
shorthands (deref shorthands*)
set-shorthands
;; This fn must receive an object `shorthand` with :panel and :property (the shorthand string) keys
(mf/use-fn
(mf/deps shorthands*)
(fn [shorthand]
(swap! shorthands* assoc (:panel shorthand) (:property shorthand))))]
[:ol {:class (stl/css :styles-tab) :aria-label (tr "labels.styles")} [:ol {:class (stl/css :styles-tab) :aria-label (tr "labels.styles")}
;; TOKENS PANEL ;; TOKENS PANEL
(when (or active-themes active-sets) (when (or active-themes active-sets)
@ -130,18 +149,22 @@
:data data}]] :data data}]]
;; GEOMETRY PANEL ;; GEOMETRY PANEL
:geometry :geometry
[:> style-box* {:panel :geometry} [:> style-box* {:panel :geometry
:shorthand (:geometry shorthands)}
[:> geometry-panel* {:shapes shapes [:> geometry-panel* {:shapes shapes
:objects objects :objects objects
:resolved-tokens resolved-active-tokens}]] :resolved-tokens resolved-active-tokens
:on-geometry-shorthand set-shorthands}]]
;; LAYOUT PANEL ;; LAYOUT PANEL
:layout :layout
(let [layout-shapes (->> shapes (filter ctl/any-layout?))] (let [layout-shapes (->> shapes (filter ctl/any-layout?))]
(when (seq layout-shapes) (when (seq layout-shapes)
[:> style-box* {:panel :layout} [:> style-box* {:panel :layout
:shorthand (:layout shorthands)}
[:> layout-panel* {:shapes layout-shapes [:> layout-panel* {:shapes layout-shapes
:objects objects :objects objects
:resolved-tokens resolved-active-tokens}]])) :resolved-tokens resolved-active-tokens
:on-layout-shorthand set-shorthands}]]))
;; LAYOUT ELEMENT PANEL ;; LAYOUT ELEMENT PANEL
:layout-element :layout-element
(let [shapes (->> shapes (filter #(ctl/any-layout-immediate-child? objects %))) (let [shapes (->> shapes (filter #(ctl/any-layout-immediate-child? objects %)))
@ -157,29 +180,35 @@
(if only-grid? (if only-grid?
:grid-element :grid-element
:layout-element))] :layout-element))]
[:> style-box* {:panel panel} [:> style-box* {:panel panel
:shorthand (:layout-element shorthands)}
[:> layout-element-panel* {:shapes shapes [:> layout-element-panel* {:shapes shapes
:objects objects :objects objects
:resolved-tokens resolved-active-tokens :resolved-tokens resolved-active-tokens
:layout-element-properties layout-element-properties}]]))) :layout-element-properties layout-element-properties
:on-layout-element-shorthand set-shorthands}]])))
;; FILL PANEL ;; FILL PANEL
:fill :fill
(let [shapes (filter has-fill? shapes)] (let [shapes (filter has-fill? shapes)]
(when (seq shapes) (when (seq shapes)
[:> style-box* {:panel :fill} [:> style-box* {:panel :fill
:shorthand (:fill shorthands)}
[:> fill-panel* {:color-space color-space [:> fill-panel* {:color-space color-space
:shapes shapes :shapes shapes
:resolved-tokens resolved-active-tokens}]])) :resolved-tokens resolved-active-tokens
:on-fill-shorthand set-shorthands}]]))
;; STROKE PANEL ;; STROKE PANEL
:stroke :stroke
(let [shapes (filter has-stroke? shapes)] (let [shapes (filter has-stroke? shapes)]
(when (seq shapes) (when (seq shapes)
[:> style-box* {:panel :stroke} [:> style-box* {:panel :stroke
:shorthand (:stroke shorthands)}
[:> stroke-panel* {:color-space color-space [:> stroke-panel* {:color-space color-space
:shapes shapes :shapes shapes
:objects objects :objects objects
:resolved-tokens resolved-active-tokens}]])) :resolved-tokens resolved-active-tokens
:on-stroke-shorthand set-shorthands}]]))
;; VISIBILITY PANEL ;; VISIBILITY PANEL
:visibility :visibility
@ -207,18 +236,23 @@
:text :text
(let [shapes (filter has-text? shapes)] (let [shapes (filter has-text? shapes)]
(when (seq shapes) (when (seq shapes)
[:> style-box* {:panel :text} [:> style-box* {:panel :text
:shorthand (:text shorthands)}
[:> text-panel* {:shapes shapes [:> text-panel* {:shapes shapes
:color-space color-space :color-space color-space
:objects objects :resolved-tokens resolved-active-tokens
:resolved-tokens resolved-active-tokens}]])) :on-font-shorthand set-shorthands}]]))
;; SHADOW PANEL ;; SHADOW PANEL
:shadow :shadow
(let [shapes (filter has-shadow? shapes)] (let [shapes (filter has-shadow? shapes)]
(when (seq shapes) (when (seq shapes)
[:> style-box* {:panel :shadow} [:> style-box* {:panel :shadow
:shorthand (:shadow shorthands)}
[:> shadow-panel* {:shapes shapes [:> shadow-panel* {:shapes shapes
:color-space color-space}]])) :color-space color-space
:on-shadow-shorthand set-shorthands}]]))
;; DEFAULT WIP ;; DEFAULT WIP
[:> style-box* {:panel panel} [:> style-box* {:panel panel}
[:div color-space]])])])) [:div color-space]])])]))

View File

@ -17,7 +17,7 @@
[{:keys [shapes objects]}] [{:keys [shapes objects]}]
[:div {:class (stl/css :blur-panel)} [:div {:class (stl/css :blur-panel)}
(for [shape shapes] (for [shape shapes]
[:div {:key (:id shape) :class "blur-shape"} [:div {:key (:id shape) :class (stl/css :blur-shape)}
(let [property :filter (let [property :filter
value (css/get-css-value objects shape property) value (css/get-css-value objects shape property)
property-name (cmm/get-css-rule-humanized property) property-name (cmm/get-css-rule-humanized property)

View File

@ -7,9 +7,12 @@
(ns app.main.ui.inspect.styles.panels.fill (ns app.main.ui.inspect.styles.panels.fill
(:require-macros [app.main.style :as stl]) (:require-macros [app.main.style :as stl])
(:require (:require
[app.common.data.macros :as dm]
[app.common.types.fills :as types.fills] [app.common.types.fills :as types.fills]
[app.config :as cfg]
[app.main.ui.inspect.attributes.common :as cmm] [app.main.ui.inspect.attributes.common :as cmm]
[app.main.ui.inspect.styles.rows.color-properties-row :refer [color-properties-row*]] [app.main.ui.inspect.styles.rows.color-properties-row :refer [color-properties-row*]]
[app.util.color :as uc]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
(defn- get-applied-tokens-in-shape (defn- get-applied-tokens-in-shape
@ -32,28 +35,56 @@
(and (= (:resolved-value resolved-token) (:color color-type)) (and (= (:resolved-value resolved-token) (:color color-type))
(= 0 idx))) (= 0 idx)))
(mf/defc fill-panel* (defn- generate-fill-shorthand
[{:keys [shapes resolved-tokens color-space]}] [shape]
[:div {:class (stl/css :fill-panel)} (reduce
(for [shape shapes] (fn [acc fill]
[:div {:key (:id shape) :class "fill-shape"} (let [color-type (types.fills/fill->color fill)
(for [[idx fill] (map-indexed vector (:fills shape))] color-value (:color color-type)
(let [property :background color-gradient (:gradient color-type)
color-type (types.fills/fill->color fill) ;; can be :color, :gradient or :image gradient-data {:type color-type
property-name (cmm/get-css-rule-humanized property) :stops (:stops color-gradient)}
resolved-token (get-resolved-token shape resolved-tokens) color-image (:image color-type)
has-token (has-token? resolved-token color-type idx)] prefix (if color-value "background-color: " "background-image: ")
value (cond
(:color color-type) (dm/str color-value)
color-gradient (uc/gradient->css gradient-data)
color-image (str "url(\"" (cfg/resolve-file-media color-image) "\")")
:else "")
full-value (str prefix value ";")]
(if (empty? acc)
full-value
(str acc " " full-value))))
""
(:fills shape)))
(if (:color color-type) (mf/defc fill-panel*
[:> color-properties-row* {:key idx [{:keys [shapes resolved-tokens color-space on-fill-shorthand]}]
:term property-name (let [shorthand* (mf/use-state #(generate-fill-shorthand (first shapes)))
:color color-type shorthand (deref shorthand*)]
:token (when has-token resolved-token) (mf/use-effect
:format color-space (mf/deps shorthand on-fill-shorthand shapes)
:copiable true}] (fn []
(if (or (:gradient color-type) (:image color-type)) (reset! shorthand* (generate-fill-shorthand (first shapes)))
(on-fill-shorthand {:panel :fill
:property shorthand})))
[:div {:class (stl/css :fill-panel)}
(for [shape shapes]
[:div {:key (:id shape) :class (stl/css :fill-shape)}
(for [[idx fill] (map-indexed vector (:fills shape))]
(let [property :background
color-type (types.fills/fill->color fill) ;; can be :color, :gradient or :image
property-name (cmm/get-css-rule-humanized property)
resolved-token (get-resolved-token shape resolved-tokens)
has-token (has-token? resolved-token color-type idx)]
(if (:color color-type)
[:> color-properties-row* {:key idx [:> color-properties-row* {:key idx
:term property-name :term property-name
:color color-type :color color-type
:token (when has-token resolved-token)
:format color-space
:copiable true}] :copiable true}]
[:span "background-image"]))))])]) [:> color-properties-row* {:key idx
:term property-name
:color color-type
:copiable true}])))])]))

View File

@ -30,6 +30,15 @@
:border-end-start-radius :r3 :border-end-start-radius :r3
:border-end-end-radius :r4}) :border-end-end-radius :r4})
(defn- has-border-radius?
"Returns true if the shape has any non-zero border radius values."
[shape]
(let [radius-keys [:r1 :r2 :r3 :r4]]
(some (fn [key]
(and (contains? shape key)
(not= 0 (get shape key))))
radius-keys)))
(defn- get-applied-tokens-in-shape (defn- get-applied-tokens-in-shape
[shape-tokens property] [shape-tokens property]
(let [border-prop (get shape-prop->border-radius-prop property)] (let [border-prop (get shape-prop->border-radius-prop property)]
@ -44,19 +53,33 @@
token (get resolved-tokens applied-tokens-in-shape)] token (get resolved-tokens applied-tokens-in-shape)]
token)) token))
(defn- generate-geometry-shorthand
[shapes objects]
(when (and (= (count shapes) 1) (has-border-radius? (first shapes)))
(css/get-css-property objects (first shapes) :border-radius)))
(mf/defc geometry-panel* (mf/defc geometry-panel*
[{:keys [shapes objects resolved-tokens]}] [{:keys [shapes objects resolved-tokens on-geometry-shorthand]}]
[:div {:class (stl/css :geometry-panel)} (let [shorthand* (mf/use-state #(generate-geometry-shorthand shapes objects))
(for [shape shapes] shorthand (deref shorthand*)]
[:div {:key (:id shape) :class "geometry-shape"} (mf/use-effect
(for [property properties] (mf/deps shorthand on-geometry-shorthand shapes objects)
(when-let [value (css/get-css-value objects shape property)] (fn []
(let [property-name (cmm/get-css-rule-humanized property) (reset! shorthand* (generate-geometry-shorthand shapes objects))
resolved-token (get-resolved-token property shape resolved-tokens) (on-geometry-shorthand {:panel :geometry
property-value (if (not resolved-token) (css/get-css-property objects shape property) "")] :property shorthand})))
[:> properties-row* {:key (dm/str "geometry-property-" property) [:div {:class (stl/css :geometry-panel)}
:term property-name (for [shape shapes]
:detail value [:div {:key (:id shape) :class (stl/css :geometry-shape)}
:token resolved-token
:property property-value (for [property properties]
:copiable true}])))])]) (when-let [value (css/get-css-value objects shape property)]
(let [property-name (cmm/get-css-rule-humanized property)
resolved-token (get-resolved-token property shape resolved-tokens)
property-value (if (not resolved-token) (css/get-css-property objects shape property) "")]
[:> properties-row* {:key (dm/str "geometry-property-" property)
:term property-name
:detail value
:token resolved-token
:property property-value
:copiable true}])))])]))

View File

@ -37,6 +37,15 @@
:padding-block-end :p3 :padding-block-end :p3
:padding-inline-start :p4}) :padding-inline-start :p4})
(defn- has-padding?
[shape]
(let [padding (:layout-padding shape)
padding-keys [:p1 :p2 :p3 :p4]]
(some (fn [key]
(and (contains? padding key)
(not= 0 (get padding key))))
padding-keys)))
(defn- get-applied-tokens-in-shape (defn- get-applied-tokens-in-shape
[shape-tokens property] [shape-tokens property]
(let [padding-prop (get shape-prop->padding-prop property)] (let [padding-prop (get shape-prop->padding-prop property)]
@ -51,19 +60,43 @@
token (get resolved-tokens applied-tokens-in-shape)] token (get resolved-tokens applied-tokens-in-shape)]
token)) token))
(defn- generate-layout-shorthand
[shapes objects]
(let [shape (first shapes)
shorthand-padding (when (and (= (count shapes) 1) (has-padding? shape))
(css/get-css-property objects shape :padding))
shorthand-grid (when (and (= (count shapes) 1)
(= :grid (:layout shape)))
(str "grid: "
(css/get-css-value objects shape :grid-template-rows)
" / "
(css/get-css-value objects shape :grid-template-columns)
";"))
shorthand (when (or shorthand-padding shorthand-grid)
(str shorthand-grid " " shorthand-padding))]
shorthand))
(mf/defc layout-panel* (mf/defc layout-panel*
[{:keys [shapes objects resolved-tokens]}] [{:keys [shapes objects resolved-tokens on-layout-shorthand]}]
[:div {:class (stl/css :variants-panel)} (let [shorthand* (mf/use-state #(generate-layout-shorthand shapes objects))
(for [shape shapes] shorthand (deref shorthand*)]
[:div {:key (:id shape) :class "layout-shape"} (mf/use-effect
(for [property properties] (mf/deps shorthand on-layout-shorthand shapes objects)
(when-let [value (css/get-css-value objects shape property)] (fn []
(let [property-name (cmm/get-css-rule-humanized property) (reset! shorthand* (generate-layout-shorthand shapes objects))
resolved-token (get-resolved-token property shape resolved-tokens) (on-layout-shorthand {:panel :layout
property-value (if (not resolved-token) (css/get-css-property objects shape property) "")] :property shorthand})))
[:> properties-row* {:key (dm/str "layout-property-" property) [:div {:class (stl/css :variants-panel)}
:term property-name (for [shape shapes]
:detail value [:div {:key (:id shape) :class (stl/css :layout-shape)}
:token resolved-token (for [property properties]
:property property-value (when-let [value (css/get-css-value objects shape property)]
:copiable true}])))])]) (let [property-name (cmm/get-css-rule-humanized property)
resolved-token (get-resolved-token property shape resolved-tokens)
property-value (if (not resolved-token) (css/get-css-property objects shape property) "")]
[:> properties-row* {:key (dm/str "layout-property-" property)
:term property-name
:detail value
:token resolved-token
:property property-value
:copiable true}])))])]))

View File

@ -25,6 +25,15 @@
:min-inline-size :layout-item-min-w ;; :min-width :min-inline-size :layout-item-min-w ;; :min-width
}) })
(defn- has-margin?
[shape]
(let [margin (:layout-item-margin shape)
margin-keys [:m1 :m2 :m3 :m4]]
(some (fn [key]
(and (contains? margin key)
(not= 0 (get margin key))))
margin-keys)))
(defn- get-applied-margins-in-shape (defn- get-applied-margins-in-shape
[shape-tokens property] [shape-tokens property]
(if-let [margin-prop (get shape-prop->margin-prop property)] (if-let [margin-prop (get shape-prop->margin-prop property)]
@ -38,12 +47,40 @@
token (get resolved-tokens applied-tokens-in-shape)] token (get resolved-tokens applied-tokens-in-shape)]
token))) token)))
(defn- generate-layout-element-shorthand
[shapes objects]
(let [shape (first shapes)
shorthand-margin (when (and (= (count shapes) 1) (has-margin? shape))
(css/get-css-property objects shape :margin))
shorthand-grow (when (and (= (count shapes) 1)
(ctl/flex-layout-immediate-child? objects shape))
(if-let [flex-value (css/get-css-value objects shape :flex)]
flex-value
0))
shorthand-basis 0 ;; Default-value. Currently not supported in the UI
shorthand-shrink (when (and (= (count shapes) 1)
(ctl/flex-layout-immediate-child? objects shape))
(if-let [flex-shrink-value (css/get-css-value objects shape :flex-shrink)]
flex-shrink-value
0))
shorthand-flex (dm/str "flex: " shorthand-grow " " shorthand-basis " " shorthand-shrink ";")
shorthand (dm/str shorthand-margin " " shorthand-flex)]
shorthand))
(mf/defc layout-element-panel* (mf/defc layout-element-panel*
[{:keys [shapes objects resolved-tokens layout-element-properties]}] [{:keys [shapes objects resolved-tokens layout-element-properties on-layout-element-shorthand]}]
(let [shapes (->> shapes (filter #(ctl/any-layout-immediate-child? objects %)))] (let [shapes (->> shapes (filter #(ctl/any-layout-immediate-child? objects %)))
shorthand* (mf/use-state #(generate-layout-element-shorthand shapes objects))
shorthand (deref shorthand*)]
(mf/use-effect
(mf/deps shorthand on-layout-element-shorthand shapes objects)
(fn []
(reset! shorthand* (generate-layout-element-shorthand shapes objects))
(on-layout-element-shorthand {:panel :layout-element
:property shorthand})))
[:div {:class (stl/css :layout-element-panel)} [:div {:class (stl/css :layout-element-panel)}
(for [shape shapes] (for [shape shapes]
[:div {:key (:id shape) :class "layout-element-shape"} [:div {:key (:id shape) :class (stl/css :layout-element-shape)}
(for [property layout-element-properties] (for [property layout-element-properties]
(when-let [value (css/get-css-value objects shape property)] (when-let [value (css/get-css-value objects shape property)]
(let [property-name (cmm/get-css-rule-humanized property) (let [property-name (cmm/get-css-rule-humanized property)

View File

@ -12,22 +12,45 @@
[app.main.ui.inspect.styles.rows.color-properties-row :refer [color-properties-row*]] [app.main.ui.inspect.styles.rows.color-properties-row :refer [color-properties-row*]]
[app.main.ui.inspect.styles.rows.properties-row :refer [properties-row*]] [app.main.ui.inspect.styles.rows.properties-row :refer [properties-row*]]
[app.util.code-gen.style-css :as css] [app.util.code-gen.style-css :as css]
[app.util.code-gen.style-css-formats :as scf]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
(defn- generate-shadow-shorthand
[shapes]
(when (= (count shapes) 1)
(let [shorthand-property (dm/str "box-shadow: ")
shorthand-value (reduce
(fn [acc shadow]
(let [value (scf/format-shadow->css shadow {})]
(if (empty? acc)
value
(dm/str acc ", " value))))
""
(:shadow (first shapes)))]
(dm/str shorthand-property shorthand-value ";"))))
(mf/defc shadow-panel* (mf/defc shadow-panel*
[{:keys [shapes color-space]}] [{:keys [shapes color-space on-shadow-shorthand]}]
[:div {:class (stl/css :shadow-panel)} (let [shorthand* (mf/use-state #(generate-shadow-shorthand shapes))
(for [shape shapes] shorthand (deref shorthand*)]
(for [shadow (:shadow shape)] (mf/use-effect
[:div {:key (dm/str (:id shape) (:type shadow)) :class "shadow-shape"} (mf/deps shorthand on-shadow-shorthand shapes)
[:> color-properties-row* {:term "Shadow Color" (fn []
:color (:color shadow) (reset! shorthand* (generate-shadow-shorthand shapes))
:format color-space (on-shadow-shorthand {:panel :shadow
:copiable true}] :property shorthand})))
(let [value (dm/str (:offset-x shadow) "px" " " (:offset-y shadow) "px" " " (:blur shadow) "px" " " (:spread shadow) "px") [:div {:class (stl/css :shadow-panel)}
property-name (cmm/get-css-rule-humanized (:style shadow)) (for [shape shapes]
property-value (css/shadow->css shadow)] (for [[idx shadow] (map-indexed vector (:shadow shape))]
[:> properties-row* {:term property-name [:div {:key (dm/str idx) :class (stl/css :shadow-shape)}
:detail (dm/str value) [:> color-properties-row* {:term "Shadow Color"
:property property-value :color (:color shadow)
:copiable true}])]))]) :format color-space
:copiable true}]
(let [value (dm/str (:offset-x shadow) "px" " " (:offset-y shadow) "px" " " (:blur shadow) "px" " " (:spread shadow) "px")
property-name (cmm/get-css-rule-humanized (:style shadow))
property-value (css/shadow->css shadow)]
[:> properties-row* {:term property-name
:detail (dm/str value)
:property property-value
:copiable true}])]))]))

View File

@ -9,22 +9,17 @@
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.types.color :as ctc]
[app.config :as cfg]
[app.main.ui.inspect.attributes.common :as cmm] [app.main.ui.inspect.attributes.common :as cmm]
[app.main.ui.inspect.styles.rows.color-properties-row :refer [color-properties-row*]] [app.main.ui.inspect.styles.rows.color-properties-row :refer [color-properties-row*]]
[app.main.ui.inspect.styles.rows.properties-row :refer [properties-row*]] [app.main.ui.inspect.styles.rows.properties-row :refer [properties-row*]]
[app.util.code-gen.style-css :as css] [app.util.code-gen.style-css :as css]
[app.util.color :as uc]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
(def ^:private properties [:border-color :border-style :border-width]) (def ^:private properties [:border-color :border-style :border-width])
(defn- stroke->color [shape]
{:color (:stroke-color shape)
:opacity (:stroke-opacity shape)
:gradient (:stroke-color-gradient shape)
:id (:stroke-color-ref-id shape)
:file-id (:stroke-color-ref-file shape)
:image (:stroke-image shape)})
(def ^:private shape-prop->stroke-prop (def ^:private shape-prop->stroke-prop
{:border-style :stroke-style {:border-style :stroke-style
:border-width :stroke-width :border-width :stroke-width
@ -57,29 +52,62 @@
(and (= (:resolved-value resolved-token) (:color stroke-type)) (and (= (:resolved-value resolved-token) (:color stroke-type))
(is-first-element? idx))) (is-first-element? idx)))
(defn- generate-stroke-shorthand
[shapes]
(when (= (count shapes) 1)
(let [shape (first shapes)]
(reduce
(fn [acc stroke]
(let [stroke-type (ctc/stroke->color stroke)
stroke-width (:stroke-width stroke)
stroke-style (:stroke-style stroke)
color-value (:color stroke-type)
color-gradient (:gradient stroke-type)
gradient-data {:type (get-in stroke-type [:gradient :type])
:stops (get-in stroke-type [:gradient :stops])}
color-image (:image stroke-type)
value (cond
color-value (dm/str "border: " stroke-width "px " (d/name stroke-style) " " color-value ";")
color-gradient (dm/str "border-image: " (uc/gradient->css gradient-data) " 100 / " stroke-width "px;")
color-image (dm/str "border-image: url(" (cfg/resolve-file-media color-image) ") 100 / " stroke-width "px;")
:else "")]
(if (empty? acc)
value
(str acc " " value))))
""
(:strokes shape)))))
(mf/defc stroke-panel* (mf/defc stroke-panel*
[{:keys [shapes objects resolved-tokens color-space]}] [{:keys [shapes objects resolved-tokens color-space on-stroke-shorthand]}]
[:div {:class (stl/css :stroke-panel)} (let [shorthand* (mf/use-state #(generate-stroke-shorthand shapes))
(for [shape shapes] shorthand (deref shorthand*)]
[:div {:key (:id shape) :class "stroke-shape"} (mf/use-effect
(for [[idx stroke] (map-indexed vector (:strokes shape))] (mf/deps shorthand on-stroke-shorthand shapes)
(for [property properties] (fn []
(let [value (css/get-css-value objects stroke property) (reset! shorthand* (generate-stroke-shorthand shapes))
stroke-type (stroke->color stroke) (on-stroke-shorthand {:panel :stroke
property-name (cmm/get-css-rule-humanized property) :property shorthand})))
property-value (css/get-css-property objects stroke property) [:div {:class (stl/css :stroke-panel)}
resolved-token (when (is-first-element? idx) (get-resolved-token property shape resolved-tokens)) (for [shape shapes]
has-color-token (has-color-token? resolved-token stroke-type idx)] [:div {:key (:id shape) :class (stl/css :stroke-shape)}
(if (= property :border-color) (for [[idx stroke] (map-indexed vector (:strokes shape))]
[:> color-properties-row* {:key (str idx property) (for [property properties]
:term property-name (let [value (css/get-css-value objects stroke property)
:color stroke-type stroke-type (ctc/stroke->color stroke)
:token (when has-color-token resolved-token) property-name (cmm/get-css-rule-humanized property)
:format color-space property-value (css/get-css-property objects stroke property)
:copiable true}] resolved-token (when (is-first-element? idx) (get-resolved-token property shape resolved-tokens))
[:> properties-row* {:key (str idx property) has-color-token (has-color-token? resolved-token stroke-type idx)]
:term (d/name property-name) (if (= property :border-color)
:detail (dm/str value) [:> color-properties-row* {:key (str idx property)
:token resolved-token :term property-name
:property property-value :color stroke-type
:copiable true}]))))])]) :token (when has-color-token resolved-token)
:format color-space
:copiable true}]
[:> properties-row* {:key (str idx property)
:term (d/name property-name)
:detail (dm/str value)
:token resolved-token
:property property-value
:copiable true}]))))])]))

View File

@ -7,6 +7,7 @@
(ns app.main.ui.inspect.styles.panels.text (ns app.main.ui.inspect.styles.panels.text
(:require-macros [app.main.style :as stl]) (:require-macros [app.main.style :as stl])
(:require (:require
[app.common.data.macros :as dm]
[app.common.types.fills :as types.fills] [app.common.types.fills :as types.fills]
[app.common.types.text :as txt] [app.common.types.text :as txt]
[app.main.fonts :as fonts] [app.main.fonts :as fonts]
@ -38,6 +39,23 @@
(remove (fn [[_ text]] (str/empty? (str/trim text)))) (remove (fn [[_ text]] (str/empty? (str/trim text))))
(mapv (fn [[style text]] (vector (merge (txt/get-default-text-attrs) style) text))))) (mapv (fn [[style text]] (vector (merge (txt/get-default-text-attrs) style) text)))))
(defn- generate-typography-shorthand
[shapes]
(when (= (count shapes) 1)
(let [shape (first shapes)
style-text-blocks (get-style-text shape)]
(reduce
(fn [acc [style _]]
(let [font-style (:font-style style)
font-family (dm/str (:font-family style))
font-size (:font-size style)
font-weight (:font-weight style)
line-height (:line-height style)
text-transform (:text-transform style)]
(dm/str acc "font:" font-style " " text-transform " " font-weight " " font-size "/" line-height " " \" font-family \" ";")))
""
style-text-blocks))))
(mf/defc typography-name-block* (mf/defc typography-name-block*
[{:keys [style]}] [{:keys [style]}]
(let [typography (ict/get-typography style) (let [typography (ict/get-typography style)
@ -59,126 +77,133 @@
:copiable true}])) :copiable true}]))
(mf/defc text-panel* (mf/defc text-panel*
[{:keys [shapes _ resolved-tokens color-space]}] [{:keys [shapes resolved-tokens color-space on-font-shorthand]}]
[:div {:class (stl/css :text-panel)} (let [shorthand* (mf/use-state #(generate-typography-shorthand shapes))
(for [shape shapes] shorthand (deref shorthand*)]
(let [style-text-blocks (get-style-text shape) (mf/use-effect
composite-typography-token (get-resolved-token :typography shape resolved-tokens)] (mf/deps shorthand on-font-shorthand shapes)
(fn []
(reset! shorthand* (generate-typography-shorthand shapes))
(on-font-shorthand {:panel :text
:property shorthand})))
[:div {:class (stl/css :text-panel)}
(for [shape shapes]
(let [style-text-blocks (get-style-text shape)
composite-typography-token (get-resolved-token :typography shape resolved-tokens)]
[:div {:key (:id shape) :class (stl/css :text-shape)}
(for [[style text] style-text-blocks]
[:div {:key (:id shape) :class "text-shape"} [:div {:key (:id shape) :class (stl/css :text-properties)}
(for [[style text] style-text-blocks]
[:div {:key (:id shape) :class "text-properties"} (when (:fills style)
(for [[idx fill] (map-indexed vector (:fills style))]
(when (:fills style) [:> typography-color-row* {:key idx
(for [[idx fill] (map-indexed vector (:fills style))] :fill fill
[:> typography-color-row* {:key idx :shape shape
:fill fill :resolved-tokens resolved-tokens
:shape shape :color-space color-space}]))
:resolved-tokens resolved-tokens
:color-space color-space}]))
;; Typography style ;; Typography style
(when (and (not composite-typography-token) (when (and (not composite-typography-token)
(:typography-ref-id style)) (:typography-ref-id style))
[:> typography-name-block* {:style style}]) [:> typography-name-block* {:style style}])
;; Composite Typography token ;; Composite Typography token
(when (and (not (:typography-ref-id style)) (when (and (not (:typography-ref-id style))
composite-typography-token) composite-typography-token)
[:> properties-row* {:term "Typography" [:> properties-row* {:term "Typography"
:detail (:name composite-typography-token) :detail (:name composite-typography-token)
:token composite-typography-token :token composite-typography-token
:property (:name composite-typography-token) :property (:name composite-typography-token)
:copiable true}]) :copiable true}])
(when (:font-id style) (when (:font-id style)
(let [name (get (fonts/get-font-data (:font-id style)) :name) (let [name (get (fonts/get-font-data (:font-id style)) :name)
resolved-token (get-resolved-token :font-family shape resolved-tokens)] resolved-token (get-resolved-token :font-family shape resolved-tokens)]
[:> properties-row* {:term "Font Family" [:> properties-row* {:term "Font Family"
:detail name :detail name
:token resolved-token :token resolved-token
:property (str "font-family: \"" name "\";") :property (str "font-family: \"" name "\";")
:copiable true}])) :copiable true}]))
(when (:font-style style) (when (:font-style style)
[:> properties-row* {:term "Font Style" [:> properties-row* {:term "Font Style"
:detail (:font-style style) :detail (:font-style style)
:property (str "font-style: " (:font-style style) ";") :property (str "font-style: " (:font-style style) ";")
:copiable true}]) :copiable true}])
(when (:font-size style) (when (:font-size style)
(let [font-size (fmt/format-pixels (:font-size style)) (let [font-size (fmt/format-pixels (:font-size style))
resolved-token (get-resolved-token :font-size shape resolved-tokens)] resolved-token (get-resolved-token :font-size shape resolved-tokens)]
[:> properties-row* {:term "Font Size" [:> properties-row* {:term "Font Size"
:detail font-size :detail font-size
:token resolved-token :token resolved-token
:property (str "font-size: " font-size ";") :property (str "font-size: " font-size ";")
:copiable true}])) :copiable true}]))
(when (:font-weight style) (when (:font-weight style)
(let [resolved-token (get-resolved-token :font-weight shape resolved-tokens)] (let [resolved-token (get-resolved-token :font-weight shape resolved-tokens)]
[:> properties-row* {:term "Font Weight" [:> properties-row* {:term "Font Weight"
:detail (:font-weight style) :detail (:font-weight style)
:token resolved-token :token resolved-token
:property (str "font-weight: " (:font-weight style) ";") :property (str "font-weight: " (:font-weight style) ";")
:copiable true}])) :copiable true}]))
(when (:line-height style) (when (:line-height style)
(let [line-height (:line-height style) (let [line-height (:line-height style)
resolved-token (get-resolved-token :line-height shape resolved-tokens)] resolved-token (get-resolved-token :line-height shape resolved-tokens)]
[:> properties-row* {:term "Line Height" [:> properties-row* {:term "Line Height"
:detail (str line-height) :detail (str line-height)
:token resolved-token :token resolved-token
:property (str "line-height: " line-height ";") :property (str "line-height: " line-height ";")
:copiable true}])) :copiable true}]))
(when (:letter-spacing style) (when (:letter-spacing style)
(let [letter-spacing (fmt/format-pixels (:letter-spacing style)) (let [letter-spacing (fmt/format-pixels (:letter-spacing style))
resolved-token (get-resolved-token :letter-spacing shape resolved-tokens)] resolved-token (get-resolved-token :letter-spacing shape resolved-tokens)]
[:> properties-row* {:term "Letter Spacing" [:> properties-row* {:term "Letter Spacing"
:detail letter-spacing :detail letter-spacing
:token resolved-token :token resolved-token
:property (str "letter-spacing: " letter-spacing ";") :property (str "letter-spacing: " letter-spacing ";")
:copiable true}])) :copiable true}]))
(when (:text-decoration style) (when (:text-decoration style)
(let [resolved-token (get-resolved-token :text-decoration shape resolved-tokens)] (let [resolved-token (get-resolved-token :text-decoration shape resolved-tokens)]
[:> properties-row* {:term "Text Decoration" [:> properties-row* {:term "Text Decoration"
:detail (:text-decoration style) :detail (:text-decoration style)
:token resolved-token :token resolved-token
:property (str "text-decoration: " (:text-decoration style) ";") :property (str "text-decoration: " (:text-decoration style) ";")
:copiable true}])) :copiable true}]))
(when (:text-transform style) (when (:text-transform style)
(let [resolved-token (get-resolved-token :text-case shape resolved-tokens)] (let [resolved-token (get-resolved-token :text-case shape resolved-tokens)]
[:> properties-row* {:term "Text Transform" [:> properties-row* {:term "Text Transform"
:detail (:text-transform style) :detail (:text-transform style)
:token resolved-token :token resolved-token
:property (str "text-transform: " (:text-transform style) ";") :property (str "text-transform: " (:text-transform style) ";")
:copiable true}])) :copiable true}]))
(when text (when text
(let [copied* (mf/use-state false) (let [copied* (mf/use-state false)
copied (deref copied*) copied (deref copied*)
text (str/trim text) text (str/trim text)
copy-text copy-text
(mf/use-fn (mf/use-fn
(mf/deps copied) (mf/deps copied)
(fn [] (fn []
(let [formatted-text (if (= (:text-transform style) "uppercase") (let [formatted-text (if (= (:text-transform style) "uppercase")
(.toUpperCase text) (.toUpperCase text)
text)] text)]
(reset! copied* true) (reset! copied* true)
(wapi/write-to-clipboard formatted-text) (wapi/write-to-clipboard formatted-text)
(tm/schedule 1000 #(reset! copied* false)))))] (tm/schedule 1000 #(reset! copied* false)))))]
[:div {:class (stl/css :text-content-wrapper)} [:div {:class (stl/css :text-content-wrapper)}
[:> property-detail-copiable* {:copied copied [:> property-detail-copiable* {:copied copied
:on-click copy-text} :on-click copy-text}
[:span {:class (stl/css :text-content) [:span {:class (stl/css :text-content)
:style {:font-family (:font-family style) :style {:font-family (:font-family style)
:font-weight (:font-weight style) :font-weight (:font-weight style)
:text-transform (:text-transform style) :text-transform (:text-transform style)
:letter-spacing (fmt/format-pixels (:letter-spacing style)) :letter-spacing (fmt/format-pixels (:letter-spacing style))
:font-style (:font-style style)}} :font-style (:font-style style)}}
text]]]))])]))]) text]]]))])]))]))

View File

@ -34,7 +34,6 @@
[{:keys [class term color format token]}] [{:keys [class term color format token]}]
(let [copied* (mf/use-state false) (let [copied* (mf/use-state false)
copied (deref copied*) copied (deref copied*)
color-value (:color color) color-value (:color color)
color-gradient (:gradient color) color-gradient (:gradient color)
color-image (:image color) color-image (:image color)

View File

@ -48,8 +48,9 @@
copy-shorthand copy-shorthand
(mf/use-fn (mf/use-fn
(mf/deps shorthand)
(fn [] (fn []
(wapi/write-to-clipboard (str "Style: " title))))] (wapi/write-to-clipboard (str shorthand))))]
[:article {:class (stl/css :style-box)} [:article {:class (stl/css :style-box)}
[:header {:class (stl/css :disclosure-header)} [:header {:class (stl/css :disclosure-header)}
[:button {:class (stl/css :disclosure-button) [:button {:class (stl/css :disclosure-button)

View File

@ -17,6 +17,7 @@
.disclosure-header { .disclosure-header {
display: flex; display: flex;
align-items: center;
gap: var(--title-gap); gap: var(--title-gap);
padding-block: var(--title-padding); padding-block: var(--title-padding);
} }

View File

@ -1904,7 +1904,7 @@ msgstr "Info"
#: src/app/main/ui/inspect/styles/style_box.cljs:66 #: src/app/main/ui/inspect/styles/style_box.cljs:66
#, fuzzy #, fuzzy
msgid "inspect.tabs.styles.panel.copy-style-shorthand" msgid "inspect.tabs.styles.panel.copy-style-shorthand"
msgstr "" msgstr "Copy CSS shorthand to clipboard"
#: src/app/main/ui/inspect/styles/property_detail_copiable.cljs:52 #: src/app/main/ui/inspect/styles/property_detail_copiable.cljs:52
msgid "inspect.tabs.styles.panel.copy-to-clipboard" msgid "inspect.tabs.styles.panel.copy-to-clipboard"

View File

@ -1896,6 +1896,23 @@ msgstr "Calculado"
msgid "inspect.tabs.info" msgid "inspect.tabs.info"
msgstr "Información" msgstr "Información"
#: src/app/main/ui/inspect/styles/style_box.cljs:66
#, fuzzy
msgid "inspect.tabs.styles.panel.copy-style-shorthand"
msgstr "Copiar CSS shorthand al portapapeles"
#: src/app/main/ui/inspect/styles/property_detail_copiable.cljs:52
msgid "inspect.tabs.styles.panel.copy-to-clipboard"
msgstr "Copiar al portapapeles"
#: src/app/main/ui/inspect/styles/panels/tokens_panel.cljs:26
msgid "inspect.tabs.styles.panel.tokens.active-sets"
msgstr "Sets activos"
#: src/app/main/ui/inspect/styles/panels/tokens_panel.cljs:21
msgid "inspect.tabs.styles.panel.tokens.active-themes"
msgstr "Temas activos"
#: src/app/main/ui/inspect/styles/style_box.cljs:22 #: src/app/main/ui/inspect/styles/style_box.cljs:22
msgid "inspect.tabs.styles.panel.geometry" msgid "inspect.tabs.styles.panel.geometry"
msgstr "Tamaño y posición" msgstr "Tamaño y posición"