mirror of
https://github.com/penpot/penpot.git
synced 2026-09-19 10:26:14 +00:00
♻️ Improve tokens implementation
This commit is contained in:
parent
3a146c1c09
commit
2b96a220bd
@ -277,6 +277,10 @@
|
||||
|
||||
(def stroke-width-keys (schema-keys schema:stroke-width))
|
||||
|
||||
(def per-side-stroke-width-keys
|
||||
"Per-side stroke width attribute keys."
|
||||
#{:stroke-width-top :stroke-width-right :stroke-width-bottom :stroke-width-left})
|
||||
|
||||
(def ^:private schema:dimensions
|
||||
(-> (reduce mu/union [schema:sizing
|
||||
schema:spacing
|
||||
@ -438,13 +442,13 @@
|
||||
#{:fill}
|
||||
|
||||
(and (= :strokes shape-attr) (nil? changed-sub-attr))
|
||||
(set/union stroke-width-keys #{:stroke-color})
|
||||
(set/union stroke-width-keys #{:stroke-color})
|
||||
|
||||
(= :strokes shape-attr)
|
||||
(let [sub-attrs (set changed-sub-attr)]
|
||||
(cond
|
||||
(sub-attrs :stroke-color) #{:stroke-color}
|
||||
(sub-attrs :stroke-width) stroke-width-keys
|
||||
(sub-attrs :stroke-width) stroke-width-keys
|
||||
:else
|
||||
(let [per-side (set/intersection sub-attrs stroke-width-keys)]
|
||||
(when (seq per-side) per-side))))
|
||||
|
||||
@ -655,9 +655,9 @@
|
||||
{ctt/border-radius-keys update-shape-radius-for-corners
|
||||
ctt/color-keys update-fill-stroke
|
||||
ctt/stroke-width-keys update-stroke-width-side
|
||||
ctt/sizing-keys apply-dimensions-token
|
||||
ctt/opacity-keys update-opacity
|
||||
ctt/rotation-keys update-rotation
|
||||
ctt/sizing-keys apply-dimensions-token
|
||||
ctt/opacity-keys update-opacity
|
||||
ctt/rotation-keys update-rotation
|
||||
|
||||
;; Typography
|
||||
ctt/font-family-keys update-font-family
|
||||
@ -1043,7 +1043,6 @@
|
||||
{:title "Stroke Width"
|
||||
:attributes ctt/stroke-width-keys
|
||||
:on-update-shape update-stroke-width
|
||||
:on-update-shape-side update-stroke-width-side
|
||||
:modal {:key :tokens/stroke-width
|
||||
:fields [{:label "Stroke Width"
|
||||
:key :stroke-width}]}}
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.types.color :as ctc]
|
||||
[app.common.types.token :as ctt]
|
||||
[app.main.data.workspace.colors :as dc]
|
||||
[app.main.data.workspace.tokens.application :as dwta]
|
||||
[app.main.features :as features]
|
||||
@ -198,8 +199,7 @@
|
||||
:stroke-width-left v}
|
||||
index)))
|
||||
ids
|
||||
#{:stroke-width-top :stroke-width-right
|
||||
:stroke-width-bottom :stroke-width-left})
|
||||
ctt/per-side-stroke-width-keys)
|
||||
(st/emit! (dc/change-stroke-attrs
|
||||
ids
|
||||
{:stroke-width value
|
||||
@ -211,9 +211,8 @@
|
||||
(soc/emit-value-or-token
|
||||
value
|
||||
#(on-stroke-width-change index %)
|
||||
ids
|
||||
#{:stroke-width-top :stroke-width-right
|
||||
:stroke-width-bottom :stroke-width-left}))))
|
||||
ids
|
||||
ctt/per-side-stroke-width-keys))))
|
||||
|
||||
;; The SVG renderer defaults dash and gap to `stroke-width + 10` when
|
||||
;; unset. Showing that value as placeholder makes the override obvious.
|
||||
@ -303,8 +302,7 @@
|
||||
(mf/use-fn
|
||||
(mf/deps on-detach-token)
|
||||
(fn [token]
|
||||
(on-detach-token token #{:stroke-width-top :stroke-width-right
|
||||
:stroke-width-bottom :stroke-width-left})))
|
||||
(on-detach-token token ctt/per-side-stroke-width-keys)))
|
||||
|
||||
on-detach-token-width-top
|
||||
(mf/use-fn
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
[app.common.test-helpers.ids-map :as cthi]
|
||||
[app.common.test-helpers.shapes :as cths]
|
||||
[app.common.types.text :as txt]
|
||||
[app.common.types.token :as ctt]
|
||||
[app.common.types.tokens-lib :as ctob]
|
||||
[app.main.data.workspace.tokens.application :as dwta]
|
||||
[app.main.data.workspace.tokens.library-edit :as dwtl]
|
||||
@ -508,7 +509,7 @@
|
||||
rect-with-stroke (cths/get-shape file :rect-1)
|
||||
rect-without-stroke (cths/get-shape file :rect-2)
|
||||
events [(dwta/apply-token {:shape-ids [(:id rect-with-stroke) (:id rect-without-stroke)]
|
||||
:attributes #{:stroke-width-top :stroke-width-right :stroke-width-bottom :stroke-width-left}
|
||||
:attributes ctt/per-side-stroke-width-keys
|
||||
:token (toht/get-token file "stroke-width.sm")
|
||||
:on-update-shape dwta/update-stroke-width-side})]]
|
||||
(tohs/run-store-async
|
||||
@ -531,6 +532,108 @@
|
||||
(t/is (= (:stroke-width-left (:applied-tokens rect-without-stroke')) (:name token-target')))
|
||||
(t/is (= (get-in rect-without-stroke' [:strokes 0 :stroke-width]) 10))))))))))
|
||||
|
||||
(t/deftest test-apply-stroke-width-overwrites-per-side-token
|
||||
(t/testing "applying token to all sides overwrites per-side token"
|
||||
(t/async
|
||||
done
|
||||
(let [token-sm {:name "stroke-width.sm"
|
||||
:value "6"
|
||||
:type :stroke-width}
|
||||
token-lg {:name "stroke-width.lg"
|
||||
:value "12"
|
||||
:type :stroke-width}
|
||||
file (-> (setup-file-with-tokens {:rect-1 {:strokes [{:stroke-alignment :inner
|
||||
:stroke-style :solid
|
||||
:stroke-color "#000000"
|
||||
:stroke-opacity 1
|
||||
:stroke-width 5}]}})
|
||||
(update-in [:data :tokens-lib]
|
||||
(fn [lib]
|
||||
(-> lib
|
||||
(ctob/add-token (cthi/id :set-a) (ctob/make-token token-sm))
|
||||
(ctob/add-token (cthi/id :set-a) (ctob/make-token token-lg))))))
|
||||
store (ths/setup-store file)
|
||||
rect-1 (cths/get-shape file :rect-1)
|
||||
step2 (fn [state1]
|
||||
(let [file1 (ths/get-file-from-state state1)
|
||||
rect1 (cths/get-shape file1 :rect-1)]
|
||||
;; Verify token-sm applied to top only
|
||||
(t/is (= (:stroke-width-top (:applied-tokens rect1)) "stroke-width.sm"))
|
||||
(t/is (nil? (:stroke-width-right (:applied-tokens rect1))))
|
||||
;; Second: apply token-lg to all 4 sides
|
||||
(let [events2 [(dwta/apply-token {:shape-ids [(:id rect1)]
|
||||
:attributes ctt/per-side-stroke-width-keys
|
||||
:token (toht/get-token file1 "stroke-width.lg")
|
||||
:on-update-shape dwta/update-stroke-width})]]
|
||||
(tohs/run-store-async
|
||||
(ths/setup-store file1) done events2
|
||||
(fn [state2]
|
||||
(let [file2 (ths/get-file-from-state state2)
|
||||
rect2 (cths/get-shape file2 :rect-1)]
|
||||
(t/testing "token-lg overwrites all sides"
|
||||
(t/is (= (:stroke-width-top (:applied-tokens rect2)) "stroke-width.lg"))
|
||||
(t/is (= (:stroke-width-right (:applied-tokens rect2)) "stroke-width.lg"))
|
||||
(t/is (= (:stroke-width-bottom (:applied-tokens rect2)) "stroke-width.lg"))
|
||||
(t/is (= (:stroke-width-left (:applied-tokens rect2)) "stroke-width.lg"))
|
||||
(t/is (= (get-in rect2 [:strokes 0 :stroke-width]) 12)))))))))]
|
||||
;; First: apply token-sm to top side only
|
||||
(tohs/run-store-async
|
||||
store (constantly nil)
|
||||
[(dwta/apply-token {:shape-ids [(:id rect-1)]
|
||||
:attributes #{:stroke-width-top}
|
||||
:token (toht/get-token file "stroke-width.sm")
|
||||
:on-update-shape dwta/update-stroke-width-side})]
|
||||
step2)))))
|
||||
|
||||
(t/deftest test-detach-token-from-single-side
|
||||
(t/testing "detaching token from one side leaves other sides intact"
|
||||
(t/async
|
||||
done
|
||||
(let [token {:name "stroke-width.sm"
|
||||
:value "10"
|
||||
:type :stroke-width}
|
||||
file (-> (setup-file-with-tokens {:rect-1 {:strokes [{:stroke-alignment :inner
|
||||
:stroke-style :solid
|
||||
:stroke-color "#000000"
|
||||
:stroke-opacity 1
|
||||
:stroke-width 5}]}})
|
||||
(update-in [:data :tokens-lib]
|
||||
#(ctob/add-token % (cthi/id :set-a)
|
||||
(ctob/make-token token))))
|
||||
store (ths/setup-store file)
|
||||
rect-1 (cths/get-shape file :rect-1)
|
||||
step2 (fn [state1]
|
||||
(let [file1 (ths/get-file-from-state state1)
|
||||
rect1 (cths/get-shape file1 :rect-1)]
|
||||
;; Verify all sides have the token
|
||||
(t/is (= (:stroke-width-top (:applied-tokens rect1)) "stroke-width.sm"))
|
||||
(t/is (= (:stroke-width-right (:applied-tokens rect1)) "stroke-width.sm"))
|
||||
(t/is (= (:stroke-width-bottom (:applied-tokens rect1)) "stroke-width.sm"))
|
||||
(t/is (= (:stroke-width-left (:applied-tokens rect1)) "stroke-width.sm"))
|
||||
;; Second: unapply token from top side only
|
||||
(let [events2 [(dwta/unapply-token {:token-name "stroke-width.sm"
|
||||
:attributes #{:stroke-width-top}
|
||||
:shape-ids [(:id rect1)]})]]
|
||||
(tohs/run-store-async
|
||||
(ths/setup-store file1) done events2
|
||||
(fn [state2]
|
||||
(let [file2 (ths/get-file-from-state state2)
|
||||
rect2 (cths/get-shape file2 :rect-1)]
|
||||
(t/testing "top side token detached"
|
||||
(t/is (nil? (:stroke-width-top (:applied-tokens rect2)))))
|
||||
(t/testing "other sides retain the token"
|
||||
(t/is (= (:stroke-width-right (:applied-tokens rect2)) "stroke-width.sm"))
|
||||
(t/is (= (:stroke-width-bottom (:applied-tokens rect2)) "stroke-width.sm"))
|
||||
(t/is (= (:stroke-width-left (:applied-tokens rect2)) "stroke-width.sm")))))))))]
|
||||
;; First: apply token to all 4 sides
|
||||
(tohs/run-store-async
|
||||
store (constantly nil)
|
||||
[(dwta/apply-token {:shape-ids [(:id rect-1)]
|
||||
:attributes ctt/per-side-stroke-width-keys
|
||||
:token (toht/get-token file "stroke-width.sm")
|
||||
:on-update-shape dwta/update-stroke-width})]
|
||||
step2)))))
|
||||
|
||||
(t/deftest test-apply-shadow
|
||||
(t/testing "applies shadow token and updates the shapes with shadow"
|
||||
(t/async
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user