Implement font-size token type (#6708)

*  Implement font-size token type

*  Hide typography tokens behind FF

* 💄 Update icon

* 🔧 Add font-size to unapply check

* ♻️ Generalize status-icon logic and remove icon for font-size
This commit is contained in:
Florian Schrödl
2025-06-23 12:12:40 +02:00
committed by GitHub
parent 9ea0875e65
commit 580bb46a05
10 changed files with 141 additions and 32 deletions

View File

@@ -117,6 +117,7 @@
;; Only for developtment.
:tiered-file-data-storage
:token-units
:token-typography-types
:transit-readable-response
:user-feedback
;; TODO: remove this flag.

View File

@@ -33,6 +33,7 @@
:border-radius "borderRadius"
:color "color"
:dimensions "dimension"
:font-size "fontSize"
:number "number"
:opacity "opacity"
:other "other"
@@ -122,6 +123,12 @@
(def rotation-keys (schema-keys schema:rotation))
(def ^:private schema:font-size
[:map
[:font-size {:optional true} token-name-ref]])
(def font-size-keys (schema-keys schema:font-size))
(def ^:private schema:number
[:map
[:rotation {:optional true} token-name-ref]
@@ -137,6 +144,7 @@
spacing-keys
dimensions-keys
rotation-keys
font-size-keys
number-keys))
(def ^:private schema:tokens
@@ -150,6 +158,7 @@
schema:spacing
schema:rotation
schema:number
schema:font-size
schema:dimensions])
(defn shape-attr->token-attrs
@@ -177,6 +186,7 @@
changed-sub-attr
#{:m1 :m2 :m3 :m4})
(font-size-keys shape-attr) #{shape-attr}
(border-radius-keys shape-attr) #{shape-attr}
(sizing-keys shape-attr) #{shape-attr}
(opacity-keys shape-attr) #{shape-attr}

View File

@@ -54,9 +54,13 @@
(ctob/add-token-in-set "test-token-set"
(ctob/make-token :name "token-dimensions"
:type :dimensions
:value 100))))
:value 100))
(ctob/add-token-in-set "test-token-set"
(ctob/make-token :name "token-font-size"
:type :font-size
:value 24))))
(tho/add-frame :frame1)
(tho/add-text :text1 "Hello World")))
(tho/add-text :text1 "Hello World!")))
(defn- apply-all-tokens
[file]
@@ -68,19 +72,21 @@
(tht/apply-token-to-shape :frame1 "token-color" [:stroke-color] [:stroke-color] "#00ff00")
(tht/apply-token-to-shape :frame1 "token-color" [:fill] [:fill] "#00ff00")
(tht/apply-token-to-shape :frame1 "token-dimensions" [:width :height] [:width :height] 100)
(tht/apply-token-to-shape :text1 "token-color" [:fill] [:fill] "#00ff00")))
(tht/apply-token-to-shape :text1 "token-font-size" [:font-size] [:font-size] 24)))
(t/deftest apply-tokens-to-shape
(let [;; ==== Setup
file (setup-file)
page (thf/current-page file)
frame1 (ths/get-shape file :frame1)
text1 (ths/get-shape file :text1)
token-radius (tht/get-token file "test-token-set" "token-radius")
token-rotation (tht/get-token file "test-token-set" "token-rotation")
token-opacity (tht/get-token file "test-token-set" "token-opacity")
token-stroke-width (tht/get-token file "test-token-set" "token-stroke-width")
token-color (tht/get-token file "test-token-set" "token-color")
token-dimensions (tht/get-token file "test-token-set" "token-dimensions")
token-font-size (tht/get-token file "test-token-set" "token-font-size")
;; ==== Action
changes (-> (-> (pcb/empty-changes nil)
@@ -114,13 +120,23 @@
:shape $
:attributes [:width :height]})))
(:objects page)
{})
(cls/generate-update-shapes [(:id text1)]
(fn [shape]
(as-> shape $
(cto/maybe-apply-token-to-shape {:token token-font-size
:shape $
:attributes [:font-size]})))
(:objects page)
{}))
file' (thf/apply-changes file changes)
;; ==== Get
frame1' (ths/get-shape file' :frame1)
applied-tokens' (:applied-tokens frame1')]
applied-tokens' (:applied-tokens frame1')
text1' (ths/get-shape file' :text1)
text1-applied-tokens (:applied-tokens text1')]
;; ==== Check
(t/is (= (count applied-tokens') 11))
@@ -134,7 +150,9 @@
(t/is (= (:stroke-color applied-tokens') "token-color"))
(t/is (= (:fill applied-tokens') "token-color"))
(t/is (= (:width applied-tokens') "token-dimensions"))
(t/is (= (:height applied-tokens') "token-dimensions"))))
(t/is (= (:height applied-tokens') "token-dimensions"))
(t/is (= (count text1-applied-tokens) 1))
(t/is (= (:font-size text1-applied-tokens) "token-font-size"))))
(t/deftest unapply-tokens-from-shape
(let [;; ==== Setup
@@ -142,6 +160,7 @@
(apply-all-tokens))
page (thf/current-page file)
frame1 (ths/get-shape file :frame1)
text1 (ths/get-shape file :text1)
;; ==== Action
changes (-> (-> (pcb/empty-changes nil)
@@ -158,16 +177,25 @@
(cto/unapply-token-id [:fill])
(cto/unapply-token-id [:width :height])))
(:objects page)
{})
(cls/generate-update-shapes [(:id text1)]
(fn [shape]
(-> shape
(cto/unapply-token-id [:font-size])))
(:objects page)
{}))
file' (thf/apply-changes file changes)
;; ==== Get
frame1' (ths/get-shape file' :frame1)
applied-tokens' (:applied-tokens frame1')]
applied-tokens' (:applied-tokens frame1')
text1' (ths/get-shape file' :text1)
text1-applied-tokens (:applied-tokens text1')]
;; ==== Check
(t/is (= (count applied-tokens') 0))))
(t/is (= (count applied-tokens') 0))
(t/is (= (count text1-applied-tokens) 0))))
(t/deftest unapply-tokens-automatic
(let [;; ==== Setup
@@ -202,7 +230,8 @@
shape
txt/is-content-node?
d/txt-merge
{:fills (ths/sample-fills-color :fill-color "#fabada")}))
{:fills (ths/sample-fills-color :fill-color "#fabada")
:font-size "1"}))
(:objects page)
{}))

View File

@@ -22,7 +22,7 @@
[app.main.data.workspace.colors :as wdc]
[app.main.data.workspace.shape-layout :as dwsl]
[app.main.data.workspace.shapes :as dwsh]
[app.main.data.workspace.transforms :as dwt]
[app.main.data.workspace.transforms :as dwtr]
[app.main.data.workspace.undo :as dwu]
[app.main.store :as st]
[beicon.v2.core :as rx]
@@ -238,8 +238,8 @@
(watch [_ _ _]
(when (number? value)
(rx/of
(when (:width attributes) (dwt/update-dimensions shape-ids :width value {:ignore-touched true :page-id page-id}))
(when (:height attributes) (dwt/update-dimensions shape-ids :height value {:ignore-touched true :page-id page-id}))))))))
(when (:width attributes) (dwtr/update-dimensions shape-ids :width value {:ignore-touched true :page-id page-id}))
(when (:height attributes) (dwtr/update-dimensions shape-ids :height value {:ignore-touched true :page-id page-id}))))))))
(defn- attributes->layout-gap [attributes value]
(let [layout-gap (-> (set/intersection attributes #{:column-gap :row-gap})
@@ -311,7 +311,7 @@
(when (number? value)
(let [page-id (or page-id (get state :current-page-id))]
(->> (rx/from shape-ids)
(rx/map #(dwt/update-position % (zipmap attributes (repeat value))
(rx/map #(dwtr/update-position % (zipmap attributes (repeat value))
{:ignore-touched true
:page-id page-id})))))))))
@@ -346,6 +346,18 @@
{:ignore-touched true
:page-id page-id})))))
(defn update-font-size
([value shape-ids attributes] (update-font-size value shape-ids attributes nil))
([value shape-ids _attributes page-id]
(let [update-node? (fn [node]
(or (txt/is-text-node? node)
(txt/is-paragraph-node? node)))]
(when (number? value)
(dwsh/update-shapes shape-ids
#(txt/update-text-content % update-node? d/txt-merge {:font-size (str value)})
{:ignore-touched true
:page-id page-id})))))
;; Map token types to different properties used along the cokde ---------------------------------------------
;; FIXME: the values should be lazy evaluated, probably a function,
@@ -371,6 +383,14 @@
:modal {:key :tokens/color
:fields [{:label "Color" :key :color}]}}
:font-size
{:title "Font Size"
:attributes ctt/font-size-keys
:on-update-shape update-font-size
:modal {:key :tokens/font-size
:fields [{:label "Font Size"
:key :font-size}]}}
:stroke-width
{:title "Stroke Width"
:attributes ctt/stroke-width-keys

View File

@@ -33,6 +33,7 @@
ctt/sizing-keys dwta/update-shape-dimensions
ctt/opacity-keys dwta/update-opacity
#{:line-height} dwta/update-line-height
#{:font-size} dwta/update-font-size
#{:x :y} dwta/update-shape-position
#{:p1 :p2 :p3 :p4} dwta/update-layout-padding
#{:m1 :m2 :m3 :m4} dwta/update-layout-item-margin

View File

@@ -233,7 +233,8 @@
(dwta/update-shape-radius-for-corners value shape-ids attributes)))
(def shape-attribute-actions-map
(let [stroke-width (partial generic-attribute-actions #{:stroke-width} "Stroke Width")]
(let [stroke-width (partial generic-attribute-actions #{:stroke-width} "Stroke Width")
font-size (partial generic-attribute-actions #{:font-size} "Font Size")]
{:border-radius (partial all-or-separate-actions {:attribute-labels {:r1 "Top Left"
:r2 "Top Right"
:r4 "Bottom Left"
@@ -252,6 +253,7 @@
[(generic-attribute-actions #{:rotation} "Rotation" (assoc context-data :on-update-shape dwta/update-rotation))
(generic-attribute-actions #{:line-height} "Line Height" (assoc context-data :on-update-shape dwta/update-line-height))])
:stroke-width stroke-width
:font-size font-size
:dimensions (fn [context-data]
(concat
[{:title "Sizing" :submenu :sizing}

View File

@@ -179,3 +179,9 @@
::mf/register-as :tokens/typography}
[properties]
[:& token-update-create-modal properties])
(mf/defc font-size-modal
{::mf/register modal/components
::mf/register-as :tokens/font-size}
[properties]
[:& token-update-create-modal properties])

View File

@@ -50,6 +50,7 @@
:border-radius "corner-radius"
:color "drop"
:boolean "boolean-difference"
:font-size "text-font-size"
:opacity "percentage"
:number "number"
:rotation "rotation"
@@ -145,14 +146,15 @@
tokens exist for that type. Sort each group alphabetically (by their type).
If `:token-units` is not in cf/flags, number tokens are excluded."
[tokens-by-type]
(let [all-types (-> dwta/token-properties keys seq)
token-units? (contains? cf/flags :token-units)
filtered-types (if token-units?
all-types
(remove #(= % :number) all-types))]
(let [token-units? (contains? cf/flags :token-units)
token-typography-types? (contains? cf/flags :token-typography-types)
all-types (cond-> dwta/token-properties
(not token-units?) (dissoc :number)
(not token-typography-types?) (dissoc :font-size))
all-types (-> all-types keys seq)]
(loop [empty #js []
filled #js []
types filtered-types]
types all-types]
(if-let [type (first types)]
(if (not-empty (get tokens-by-type type))
(recur empty

View File

@@ -162,6 +162,9 @@
shape-ids (into #{} xf:map-id selected-shapes)]
(cft/shapes-applied-all? ids-by-attributes shape-ids attributes)))
(def token-types-with-status-icon
#{:color :border-radius :rotation :sizing :dimensions :opacity :spacing :stroke-width})
(mf/defc token-pill*
{::mf/wrap [mf/memo]}
[{:keys [on-click token on-context-menu selected-shapes active-theme-tokens]}]
@@ -208,7 +211,7 @@
(or (dwtc/resolved-token-bullet-color theme-token)
(dwtc/resolved-token-bullet-color token))))
number-token (= type :number)
status-icon? (contains? token-types-with-status-icon type)
on-click
(mf/use-fn
@@ -255,7 +258,7 @@
[:button {:class (stl/css-case
:token-pill true
:token-pill-no-icon (and number-token (not errors?))
:token-pill-no-icon (and (not status-icon?) (not errors?))
:token-pill-default can-edit?
:token-pill-applied (and can-edit? has-selected? (or half-applied? full-applied?))
:token-pill-invalid (and can-edit? errors?)
@@ -280,12 +283,13 @@
{:icon-id "broken-link"
:class (stl/css :token-pill-icon)}]
(not number-token)
(if color
color
[:& color-bullet {:color color :mini true}]
status-icon?
[:> token-status-icon*
{:icon-id token-status-id
:class (stl/css :token-pill-icon)}]))
:class (stl/css :token-pill-icon)}])
(if contains-path?
(let [[first-part last-part] (cfh/split-by-last-period name)]

View File

@@ -446,6 +446,40 @@
(t/is (= (:stroke-width (:applied-tokens rect-without-stroke')) (:name token-target')))
(t/is (empty? (:strokes rect-without-stroke')))))))))))
(t/deftest test-apply-font-size
(t/testing "applies font-size token and updates the text font-size"
(t/async
done
(let [font-size-token {:name "heading-size"
:value "24"
:type :font-size}
file (-> (setup-file-with-tokens)
(update-in [:data :tokens-lib]
#(ctob/add-token-in-set % "Set A" (ctob/make-token font-size-token))))
store (ths/setup-store file)
text-1 (cths/get-shape file :text-1)
events [(dwta/apply-token {:shape-ids [(:id text-1)]
:attributes #{:font-size}
:token (toht/get-token file "heading-size")
:on-update-shape dwta/update-font-size})]]
(tohs/run-store-async
store done events
(fn [new-state]
(let [file' (ths/get-file-from-state new-state)
token-target' (toht/get-token file' "heading-size")
text-1' (cths/get-shape file' :text-1)
style-text-blocks (->> (:content text-1')
(txt/content->text+styles)
(remove (fn [[_ text]] (str/empty? (str/trim text))))
(mapv (fn [[style text]]
{:styles (merge txt/default-text-attrs style)
:text-content text}))
(first)
(:styles))]
(t/is (some? (:applied-tokens text-1')))
(t/is (= (:font-size (:applied-tokens text-1')) (:name token-target')))
(t/is (= (:font-size style-text-blocks) "24")))))))))
(t/deftest test-apply-line-height
(t/testing "applies line-height token and updates the text line-height"
(t/async