0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-03-13 16:21:57 -05:00

♻️ Remove unnecesary RX and RY from shapes

This commit is contained in:
Eva Marco 2024-12-03 09:24:22 +01:00
parent b332f128b0
commit 73e48b3d81
36 changed files with 364 additions and 458 deletions

View file

@ -884,8 +884,10 @@
:shapes (or (:shapes shape) [])
:hide-in-viewer (if frame? (boolean (:hide-in-viewer shape)) true)
:show-content (if frame? (boolean (:show-content shape)) true)
:rx (or (:rx shape) 0)
:ry (or (:ry shape) 0)))
:r1 (or (:r1 shape) 0)
:r2 (or (:r2 shape) 0)
:r3 (or (:r3 shape) 0)
:r4 (or (:r4 shape) 0)))
shape))]
(-> file-data
(update :pages-index update-vals fix-container)

View file

@ -64,7 +64,7 @@
;; (def shapes [{:stroke-color "#ff0000"
;; :stroke-width 3
;; :fill-color "#0000ff"
;; :x 1000 :y 2000 :rx nil}
;; :x 1000 :y 2000}
;; {:stroke-width "#ff0000"
;; :stroke-width 5
;; :x 1500 :y 2000}])
@ -72,13 +72,17 @@
;; (get-attrs-multi shapes [:stroke-color
;; :stroke-width
;; :fill-color
;; :rx
;; :ry])
;; :r1
;; :r2
;; :r3
;; :r4])
;; >>> {:stroke-color "#ff0000"
;; :stroke-width :multiple
;; :fill-color "#0000ff"
;; :rx nil
;; :ry nil}
;; :r1 nil
;; :r2 nil
;; :r3 nil
;; :r4 nil}
;;
(defn get-attrs-multi
([objs attrs]

View file

@ -6,4 +6,4 @@
(ns app.common.files.defaults)
(def version 57)
(def version 58)

View file

@ -1130,6 +1130,45 @@
(update :pages-index dissoc nil)
(update :pages-index update-vals update-page))))
(defn migrate-up-58
[data]
(letfn [(update-object [object]
(if (and (:rx object) (not (:r1 object)))
(-> object
(assoc :r1 (:rx object))
(assoc :r2 (:rx object))
(assoc :r3 (:rx object))
(assoc :r4 (:rx object)))
object))
(update-container [container]
(d/update-when container :objects update-vals update-object))]
(-> data
(update :pages-index update-vals update-container)
(update :components update-vals update-container))))
(defn migrate-down-58
[data]
(letfn [(update-object [object]
(if (= (:r1 object) (:r2 object) (:r3 object) (:r4 object))
(-> object
(dissoc :r1 :r2 :r3 :r4)
(assoc :rx (:r1 object))
(assoc :ry (:r1 object)))
object))
(update-container [container]
(d/update-when container :objects update-vals update-object))]
(-> data
(update :pages-index update-vals update-container)
(update :components update-vals update-container))))
(def migrations
"A vector of all applicable migrations"
[{:id 2 :migrate-up migrate-up-2}
@ -1178,5 +1217,6 @@
{:id 54 :migrate-up migrate-up-54}
{:id 55 :migrate-up migrate-up-55}
{:id 56 :migrate-up migrate-up-56}
{:id 57 :migrate-up migrate-up-57}])
{:id 57 :migrate-up migrate-up-57}
{:id 58 :migrate-up migrate-up-58 :migrate-down migrate-down-58}])

View file

@ -434,8 +434,10 @@
(assoc shape :type :frame
:fills []
:hide-in-viewer true
:rx 0
:ry 0))]
:r1 0
:r2 0
:r3 0
:r4 0))]
(log/dbg :hint "repairing shape :instance-head-not-frame" :id (:id shape) :name (:name shape) :page-id page-id)
(-> (pcb/empty-changes nil page-id)

View file

@ -43,9 +43,9 @@
(defn shape-corners-1
"Retrieve the effective value for the corner given a single value for corner."
[{:keys [width height rx] :as shape}]
(if (and (some? rx) (not (mth/almost-zero? rx)))
(fix-radius width height rx)
[{:keys [width height r1] :as shape}]
(if (and (some? r1) (not (mth/almost-zero? r1)))
(fix-radius width height r1)
0))
(defn shape-corners-4
@ -55,26 +55,11 @@
(fix-radius width height r1 r2 r3 r4)
[r1 r2 r3 r4]))
(defn update-corners-scale-1
"Scales round corners (using a single value)"
[shape scale]
(update shape :rx * scale))
(defn update-corners-scale-4
"Scales round corners (using four values)"
(defn update-corners-scale
"Scales round corners"
[shape scale]
(-> shape
(update :r1 * scale)
(update :r2 * scale)
(update :r3 * scale)
(update :r4 * scale)))
(defn update-corners-scale
"Scales round corners"
[shape scale]
(cond-> shape
(and (some? (:rx shape)) (> (:rx shape) 0))
(update-corners-scale-1 scale)
(and (some? (:r1 shape)) (> (:r1 shape) 0))
(update-corners-scale-4 scale)))

View file

@ -65,8 +65,6 @@
:fill-color :fill-group
:fill-opacity :fill-group
:rx :radius-group
:ry :radius-group
:r1 :radius-group
:r2 :radius-group
:r3 :radius-group

View file

@ -192,8 +192,6 @@
[:constraints-v {:optional true}
[::sm/one-of vertical-constraint-types]]
[:fixed-scroll {:optional true} :boolean]
[:rx {:optional true} ::sm/safe-number]
[:ry {:optional true} ::sm/safe-number]
[:r1 {:optional true} ::sm/safe-number]
[:r2 {:optional true} ::sm/safe-number]
[:r3 {:optional true} ::sm/safe-number]
@ -400,13 +398,17 @@
:fills [{:fill-color default-color
:fill-opacity 1}]
:strokes []
:rx 0
:ry 0})
:r1 0
:r2 0
:r3 0
:r4 0})
(def ^:private minimal-image-attrs
{:type :image
:rx 0
:ry 0
:r1 0
:r2 0
:r3 0
:r4 0
:fills []
:strokes []})
@ -417,6 +419,10 @@
:strokes []
:name "Board"
:shapes []
:r1 0
:r2 0
:r3 0
:r4 0
:hide-fill-on-export false})
(def ^:private minimal-circle-attrs

View file

@ -15,7 +15,6 @@
{:frame #{:proportion-lock
:width :height
:x :y
:rx :ry
:r1 :r2 :r3 :r4
:rotation
:selrect
@ -126,7 +125,6 @@
:width :height
:x :y
:rotation
:rx :ry
:r1 :r2 :r3 :r4
:selrect
:points
@ -372,7 +370,6 @@
:width :height
:x :y
:rotation
:rx :ry
:r1 :r2 :r3 :r4
:selrect
:points
@ -410,7 +407,6 @@
:width :height
:x :y
:rotation
:rx :ry
:r1 :r2 :r3 :r4
:selrect
:points
@ -467,7 +463,6 @@
:width :height
:x :y
:rotation
:rx :ry
:r1 :r2 :r3 :r4
:selrect
:points

View file

@ -9,69 +9,42 @@
[app.common.types.shape.attrs :refer [editable-attrs]]))
;; There are some shapes that admit border radius, as rectangles
;; frames and images. Those shapes may define the radius of the corners in two modes:
;; - radius-1 all corners have the same radius (although we store two
;; values :rx and :ry because svg uses it this way).
;; - radius-4 each corner (top-left, top-right, bottom-right, bottom-left)
;; has an independent value. SVG does not allow this directly, so we
;; emulate it with paths.
;; A shape never will have both :rx and :r1 simultaneously
;; frames components and images.
;; Those shapes may define the radius of the corners with four values:
;; One for each corner (top-left, top-right, bottom-right, bottom-left)
;; has an independent value. SVG does not allow this directly, so we
;; emulate it with paths.
;; All operations take into account that the shape may not be a one of those
;; shapes that has border radius, and so it hasn't :rx nor :r1.
;; shapes that has border radius, and so it hasn't :r1.
;; In this case operations must leave shape untouched.
(defn can-get-border-radius?
[shape]
(contains? #{:rect :frame} (:type shape)))
(defn has-radius?
[shape]
(contains? (get editable-attrs (:type shape)) :rx))
(defn radius-mode
[shape]
(if (:r1 shape)
:radius-4
:radius-1))
(defn radius-1?
[shape]
(and (:rx shape) (not= (:rx shape) 0)))
(defn radius-4?
[shape]
(and (:r1 shape)
(or (not= (:r1 shape) 0)
(not= (:r2 shape) 0)
(not= (:r3 shape) 0)
(not= (:r4 shape) 0))))
(contains? (get editable-attrs (:type shape)) :r1))
(defn all-equal?
[shape]
(= (:r1 shape) (:r2 shape) (:r3 shape) (:r4 shape)))
(defn switch-to-radius-1
(defn radius-mode
[shape]
(let [r (if (all-equal? shape) (:r1 shape) 0)]
(-> shape
(assoc :rx r :ry r)
(dissoc :r1 :r2 :r3 :r4))))
(if (all-equal? shape)
:radius-1
:radius-4))
(defn switch-to-radius-4
[shape]
(let [rx (:rx shape 0)]
(-> (assoc shape :r1 rx :r2 rx :r3 rx :r4 rx)
(dissoc :rx :ry))))
(defn set-radius-1
(defn set-radius-to-all-corners
[shape value]
;; Only Apply changes to shapes that support Border Radius
(cond-> shape
(:r1 shape)
(-> (dissoc :r1 :r2 :r3 :r4)
(assoc :rx 0 :ry 0))
(can-get-border-radius? shape)
(assoc :r1 value :r2 value :r3 value :r4 value)))
:always
(assoc :rx value :ry value)))
(defn set-radius-4
(defn set-radius-to-single-corner
[shape attr value]
(let [attr (cond->> attr
(:flip-x shape)
@ -79,11 +52,7 @@
(:flip-y shape)
(get {:r1 :r4 :r2 :r3 :r3 :r2 :r4 :r1}))]
;; Only Apply changes to shapes that support border Radius
(cond-> shape
(:rx shape)
(-> (dissoc :rx :rx)
(assoc :r1 0 :r2 0 :r3 0 :r4 0))
:always
(can-get-border-radius? shape)
(assoc attr value))))

View file

@ -84,8 +84,6 @@
(sm/register!
^{::sm/type ::border-radius}
[:map
[:rx {:optional true} token-name-ref]
[:ry {:optional true} token-name-ref]
[:r1 {:optional true} token-name-ref]
[:r2 {:optional true} token-name-ref]
[:r3 {:optional true} token-name-ref]

View file

@ -97,7 +97,10 @@
"~ue117f7f6-433c-807e-8004-862a18bba46f": {
"~#shape": {
"~:y": 220,
"~:rx": 0,
"~:r1": 0,
"~:r2": 0,
"~:r3": 0,
"~:r4": 0,
"~:transform": {
"~#matrix": {
"~:a": 1.0,
@ -449,7 +452,10 @@
"~ue117f7f6-433c-807e-8004-862a8c166257": {
"~#shape": {
"~:y": 97,
"~:rx": 0,
"~:r1": 0,
"~:r2": 0,
"~:r3": 0,
"~:r4": 0,
"~:transform": {
"~#matrix": {
"~:a": 1.0,

View file

@ -111,7 +111,10 @@
"~ua30724ae-f8d8-8003-8004-69eca9b27c8c": {
"~#shape": {
"~:y": 168,
"~:rx": 0,
"~:r1": 0,
"~:r2": 0,
"~:r3": 0,
"~:r4": 0,
"~:transform": {
"~#matrix": {
"~:a": 1.0,

View file

@ -185,7 +185,10 @@
"~ub574c052-1a31-80bb-8004-75636a9b8205": {
"~#shape": {
"~:y": 136,
"~:rx": 0,
"~:r1": 0,
"~:r2": 0,
"~:r3": 0,
"~:r4": 0,
"~:transform": {
"~#matrix": {
"~:a": 1.0,

View file

@ -127,7 +127,10 @@
"~u2ace9ce8-8e01-8086-8004-7ba745d4305a":{
"~#shape":{
"~:y":221,
"~:rx":0,
"~:r1": 0,
"~:r2": 0,
"~:r3": 0,
"~:r4": 0,
"~:transform":{
"~#matrix":{
"~:a":1.0,

View file

@ -205,7 +205,10 @@
"~u86087f92-9a17-8067-8004-7cdec98dfa7f": {
"~#shape": {
"~:y": 375,
"~:rx": 0,
"~:r1": 0,
"~:r2": 0,
"~:r3": 0,
"~:r4": 0,
"~:transform": {
"~#matrix": {
"~:a": 1.0,

View file

@ -183,7 +183,10 @@
"~u2e0995e6-d90f-80ed-8005-2fd17ece880a": {
"~#shape": {
"~:y": 221,
"~:rx": 0,
"~:r1": 0,
"~:r2": 0,
"~:r3": 0,
"~:r4": 0,
"~:transform": {
"~#matrix": {
"~:a": 1.0,

View file

@ -97,7 +97,10 @@
"~u2e0995e6-d90f-80ed-8005-2fd0bd35e183": {
"~#shape": {
"~:y": 214,
"~:rx": 0,
"~:r1": 0,
"~:r2": 0,
"~:r3": 0,
"~:r4": 0,
"~:transform": {
"~#matrix": {
"~:a": 1.0,

View file

@ -1674,7 +1674,10 @@
"~u6ad3e6b9-c5a0-80cf-8005-283bbe378bd3": {
"~#shape": {
"~:y": 589.9999999999999,
"~:rx": 0,
"~:r1": 0,
"~:r2": 0,
"~:r3": 0,
"~:r4": 0,
"~:transform": {
"~#matrix": {
"~:a": 1.0,

View file

@ -127,7 +127,10 @@
"~uc70224ec-c410-807b-8004-743400e00be8":{
"~#shape":{
"~:y":255,
"~:rx":0,
"~:r1": 0,
"~:r2": 0,
"~:r3": 0,
"~:r4": 0,
"~:transform":{
"~#matrix":{
"~:a":1.0,

View file

@ -126,7 +126,10 @@
"~u7c75e310-c3a2-80fd-8004-7cc641479aef":{
"~#shape":{
"~:y":436,
"~:rx":0,
"~:r1": 0,
"~:r2": 0,
"~:r3": 0,
"~:r4": 0,
"~:transform":{
"~#matrix":{
"~:a":1.0,

View file

@ -113,7 +113,10 @@
"~u2e0995e6-d90f-80ed-8005-2fd0bd35e183": {
"~#shape": {
"~:y": 214,
"~:rx": 0,
"~:r1": 0,
"~:r2": 0,
"~:r3": 0,
"~:r4": 0,
"~:transform": {
"~#matrix": {
"~:a": 1.0,

View file

@ -1 +1 @@
{"~:id":"~u406b7b01-d3e2-80e4-8005-3138b7cc5f0b","~:file-id":"~u406b7b01-d3e2-80e4-8005-3138ac5d449c","~:created-at":"~m1730197748513","~:data":{"~:options":{},"~:objects":{"~u00000000-0000-0000-0000-000000000000":{"~#shape":{"~:y":0,"~:hide-fill-on-export":false,"~:transform":{"~#matrix":{"~:a":1.0,"~:b":0.0,"~:c":0.0,"~:d":1.0,"~:e":0.0,"~:f":0.0}},"~:rotation":0,"~:name":"Root Frame","~:width":0.01,"~:type":"~:frame","~:points":[{"~#point":{"~:x":0.0,"~:y":0.0}},{"~#point":{"~:x":0.01,"~:y":0.0}},{"~#point":{"~:x":0.01,"~:y":0.01}},{"~#point":{"~:x":0.0,"~:y":0.01}}],"~:proportion-lock":false,"~:transform-inverse":{"~#matrix":{"~:a":1.0,"~:b":0.0,"~:c":0.0,"~:d":1.0,"~:e":0.0,"~:f":0.0}},"~:id":"~u00000000-0000-0000-0000-000000000000","~:parent-id":"~u00000000-0000-0000-0000-000000000000","~:frame-id":"~u00000000-0000-0000-0000-000000000000","~:strokes":[],"~:x":0,"~:proportion":1.0,"~:selrect":{"~#rect":{"~:x":0,"~:y":0,"~:width":0.01,"~:height":0.01,"~:x1":0,"~:y1":0,"~:x2":0.01,"~:y2":0.01}},"~:fills":[{"~:fill-color":"#FFFFFF","~:fill-opacity":1}],"~:flip-x":null,"~:height":0.01,"~:flip-y":null,"~:shapes":["~ua88f39e6-60a5-80c2-8005-3138aeee944b"]}},"~ua88f39e6-60a5-80c2-8005-3138aeee944b":{"~#shape":{"~:y":427,"~:hide-fill-on-export":false,"~:transform":{"~#matrix":{"~:a":1.0,"~:b":0.0,"~:c":0.0,"~:d":1.0,"~:e":0.0,"~:f":0.0}},"~:rotation":0,"~:grow-type":"~:fixed","~:hide-in-viewer":false,"~:name":"Board","~:width":551,"~:type":"~:frame","~:points":[{"~#point":{"~:x":637,"~:y":427}},{"~#point":{"~:x":1188,"~:y":427}},{"~#point":{"~:x":1188,"~:y":761}},{"~#point":{"~:x":637,"~:y":761}}],"~:proportion-lock":false,"~:transform-inverse":{"~#matrix":{"~:a":1.0,"~:b":0.0,"~:c":0.0,"~:d":1.0,"~:e":0.0,"~:f":0.0}},"~:id":"~ua88f39e6-60a5-80c2-8005-3138aeee944b","~:parent-id":"~u00000000-0000-0000-0000-000000000000","~:frame-id":"~u00000000-0000-0000-0000-000000000000","~:strokes":[],"~:x":637,"~:proportion":1,"~:selrect":{"~#rect":{"~:x":637,"~:y":427,"~:width":551,"~:height":334,"~:x1":637,"~:y1":427,"~:x2":1188,"~:y2":761}},"~:fills":[{"~:fill-color":"#FFFFFF","~:fill-opacity":1}],"~:flip-x":null,"~:height":334,"~:flip-y":null,"~:shapes":["~ua88f39e6-60a5-80c2-8005-3138b4d36f07"]}},"~ua88f39e6-60a5-80c2-8005-3138b196dd95":{"~#shape":{"~:y":489,"~:rx":0,"~:transform":{"~#matrix":{"~:a":1.0,"~:b":0.0,"~:c":0.0,"~:d":1.0,"~:e":0.0,"~:f":0.0}},"~:rotation":0,"~:grow-type":"~:fixed","~:hide-in-viewer":false,"~:name":"Rectangle","~:width":149,"~:type":"~:rect","~:points":[{"~#point":{"~:x":677,"~:y":489}},{"~#point":{"~:x":826,"~:y":489}},{"~#point":{"~:x":826,"~:y":629}},{"~#point":{"~:x":677,"~:y":629}}],"~:proportion-lock":false,"~:transform-inverse":{"~#matrix":{"~:a":1.0,"~:b":0.0,"~:c":0.0,"~:d":1.0,"~:e":0.0,"~:f":0.0}},"~:id":"~ua88f39e6-60a5-80c2-8005-3138b196dd95","~:parent-id":"~ua88f39e6-60a5-80c2-8005-3138b4d36f07","~:frame-id":"~ua88f39e6-60a5-80c2-8005-3138aeee944b","~:strokes":[],"~:x":677,"~:proportion":1,"~:selrect":{"~#rect":{"~:x":677,"~:y":489,"~:width":149,"~:height":140,"~:x1":677,"~:y1":489,"~:x2":826,"~:y2":629}},"~:fills":[{"~:fill-color":"#B1B2B5","~:fill-opacity":1}],"~:flip-x":null,"~:ry":0,"~:height":140,"~:flip-y":null}},"~ua88f39e6-60a5-80c2-8005-3138b4d36f07":{"~#shape":{"~:y":489,"~:transform":{"~#matrix":{"~:a":1.0,"~:b":0.0,"~:c":0.0,"~:d":1.0,"~:e":0.0,"~:f":0.0}},"~:rotation":0,"~:index":1,"~:name":"Group","~:width":149,"~:type":"~:group","~:points":[{"~#point":{"~:x":677,"~:y":489}},{"~#point":{"~:x":826,"~:y":489}},{"~#point":{"~:x":826,"~:y":629}},{"~#point":{"~:x":677,"~:y":629}}],"~:proportion-lock":false,"~:transform-inverse":{"~#matrix":{"~:a":1.0,"~:b":0.0,"~:c":0.0,"~:d":1.0,"~:e":0.0,"~:f":0.0}},"~:id":"~ua88f39e6-60a5-80c2-8005-3138b4d36f07","~:parent-id":"~ua88f39e6-60a5-80c2-8005-3138aeee944b","~:frame-id":"~ua88f39e6-60a5-80c2-8005-3138aeee944b","~:strokes":[],"~:x":677,"~:proportion":1,"~:selrect":{"~#rect":{"~:x":677,"~:y":489,"~:width":149,"~:height":140,"~:x1":677,"~:y1":489,"~:x2":826,"~:y2":629}},"~:fills":[],"~:flip-x":null,"~:height":140,"~:flip-y":null,"~:shapes":["~ua88f39e6-60a5-80c2-8005-3138b196dd95"]}}},"~:id":"~u406b7b01-d3e2-80e4-8005-3138ac5d449d","~:name":"Page 1"}}
{"~:id":"~u406b7b01-d3e2-80e4-8005-3138b7cc5f0b","~:file-id":"~u406b7b01-d3e2-80e4-8005-3138ac5d449c","~:created-at":"~m1730197748513","~:data":{"~:options":{},"~:objects":{"~u00000000-0000-0000-0000-000000000000":{"~#shape":{"~:y":0,"~:hide-fill-on-export":false,"~:transform":{"~#matrix":{"~:a":1.0,"~:b":0.0,"~:c":0.0,"~:d":1.0,"~:e":0.0,"~:f":0.0}},"~:rotation":0,"~:name":"Root Frame","~:width":0.01,"~:type":"~:frame","~:points":[{"~#point":{"~:x":0.0,"~:y":0.0}},{"~#point":{"~:x":0.01,"~:y":0.0}},{"~#point":{"~:x":0.01,"~:y":0.01}},{"~#point":{"~:x":0.0,"~:y":0.01}}],"~:proportion-lock":false,"~:transform-inverse":{"~#matrix":{"~:a":1.0,"~:b":0.0,"~:c":0.0,"~:d":1.0,"~:e":0.0,"~:f":0.0}},"~:id":"~u00000000-0000-0000-0000-000000000000","~:parent-id":"~u00000000-0000-0000-0000-000000000000","~:frame-id":"~u00000000-0000-0000-0000-000000000000","~:strokes":[],"~:x":0,"~:proportion":1.0,"~:selrect":{"~#rect":{"~:x":0,"~:y":0,"~:width":0.01,"~:height":0.01,"~:x1":0,"~:y1":0,"~:x2":0.01,"~:y2":0.01}},"~:fills":[{"~:fill-color":"#FFFFFF","~:fill-opacity":1}],"~:flip-x":null,"~:height":0.01,"~:flip-y":null,"~:shapes":["~ua88f39e6-60a5-80c2-8005-3138aeee944b"]}},"~ua88f39e6-60a5-80c2-8005-3138aeee944b":{"~#shape":{"~:y":427,"~:hide-fill-on-export":false,"~:transform":{"~#matrix":{"~:a":1.0,"~:b":0.0,"~:c":0.0,"~:d":1.0,"~:e":0.0,"~:f":0.0}},"~:rotation":0,"~:grow-type":"~:fixed","~:hide-in-viewer":false,"~:name":"Board","~:width":551,"~:type":"~:frame","~:points":[{"~#point":{"~:x":637,"~:y":427}},{"~#point":{"~:x":1188,"~:y":427}},{"~#point":{"~:x":1188,"~:y":761}},{"~#point":{"~:x":637,"~:y":761}}],"~:proportion-lock":false,"~:transform-inverse":{"~#matrix":{"~:a":1.0,"~:b":0.0,"~:c":0.0,"~:d":1.0,"~:e":0.0,"~:f":0.0}},"~:id":"~ua88f39e6-60a5-80c2-8005-3138aeee944b","~:parent-id":"~u00000000-0000-0000-0000-000000000000","~:frame-id":"~u00000000-0000-0000-0000-000000000000","~:strokes":[],"~:x":637,"~:proportion":1,"~:selrect":{"~#rect":{"~:x":637,"~:y":427,"~:width":551,"~:height":334,"~:x1":637,"~:y1":427,"~:x2":1188,"~:y2":761}},"~:fills":[{"~:fill-color":"#FFFFFF","~:fill-opacity":1}],"~:flip-x":null,"~:height":334,"~:flip-y":null,"~:shapes":["~ua88f39e6-60a5-80c2-8005-3138b4d36f07"]}},"~ua88f39e6-60a5-80c2-8005-3138b196dd95":{"~#shape":{"~:y":489,"~:r1":0, "~:r2":0, "~:r3":0, "~:r4":0,"~:transform":{"~#matrix":{"~:a":1.0,"~:b":0.0,"~:c":0.0,"~:d":1.0,"~:e":0.0,"~:f":0.0}},"~:rotation":0,"~:grow-type":"~:fixed","~:hide-in-viewer":false,"~:name":"Rectangle","~:width":149,"~:type":"~:rect","~:points":[{"~#point":{"~:x":677,"~:y":489}},{"~#point":{"~:x":826,"~:y":489}},{"~#point":{"~:x":826,"~:y":629}},{"~#point":{"~:x":677,"~:y":629}}],"~:proportion-lock":false,"~:transform-inverse":{"~#matrix":{"~:a":1.0,"~:b":0.0,"~:c":0.0,"~:d":1.0,"~:e":0.0,"~:f":0.0}},"~:id":"~ua88f39e6-60a5-80c2-8005-3138b196dd95","~:parent-id":"~ua88f39e6-60a5-80c2-8005-3138b4d36f07","~:frame-id":"~ua88f39e6-60a5-80c2-8005-3138aeee944b","~:strokes":[],"~:x":677,"~:proportion":1,"~:selrect":{"~#rect":{"~:x":677,"~:y":489,"~:width":149,"~:height":140,"~:x1":677,"~:y1":489,"~:x2":826,"~:y2":629}},"~:fills":[{"~:fill-color":"#B1B2B5","~:fill-opacity":1}],"~:flip-x":null,"~:ry":0,"~:height":140,"~:flip-y":null}},"~ua88f39e6-60a5-80c2-8005-3138b4d36f07":{"~#shape":{"~:y":489,"~:transform":{"~#matrix":{"~:a":1.0,"~:b":0.0,"~:c":0.0,"~:d":1.0,"~:e":0.0,"~:f":0.0}},"~:rotation":0,"~:index":1,"~:name":"Group","~:width":149,"~:type":"~:group","~:points":[{"~#point":{"~:x":677,"~:y":489}},{"~#point":{"~:x":826,"~:y":489}},{"~#point":{"~:x":826,"~:y":629}},{"~#point":{"~:x":677,"~:y":629}}],"~:proportion-lock":false,"~:transform-inverse":{"~#matrix":{"~:a":1.0,"~:b":0.0,"~:c":0.0,"~:d":1.0,"~:e":0.0,"~:f":0.0}},"~:id":"~ua88f39e6-60a5-80c2-8005-3138b4d36f07","~:parent-id":"~ua88f39e6-60a5-80c2-8005-3138aeee944b","~:frame-id":"~ua88f39e6-60a5-80c2-8005-3138aeee944b","~:strokes":[],"~:x":677,"~:proportion":1,"~:selrect":{"~#rect":{"~:x":677,"~:y":489,"~:width":149,"~:height":140,"~:x1":677,"~:y1":489,"~:x2":826,"~:y2":629}},"~:fills":[],"~:flip-x":null,"~:height":140,"~:flip-y":null,"~:shapes":["~ua88f39e6-60a5-80c2-8005-3138b196dd95"]}}},"~:id":"~u406b7b01-d3e2-80e4-8005-3138ac5d449d","~:name":"Page 1"}}

View file

@ -522,8 +522,6 @@
:points
:x
:y
:rx
:ry
:r1
:r2
:r3

View file

@ -110,10 +110,6 @@
(add! :r3)
(add! :r4)))
(cond-> (and image? (some? (:rx shape)))
(-> (add! :rx)
(add! :ry)))
(cond-> path?
(-> (add! :stroke-cap-start)
(add! :stroke-cap-end)))

View file

@ -0,0 +1,123 @@
(ns app.main.ui.workspace.sidebar.options.menus.border-radius
(:require-macros [app.main.style :as stl])
(:require
[app.common.types.shape.radius :as ctsr]
[app.main.data.workspace.shapes :as dwsh]
[app.main.store :as st]
[app.main.ui.components.numeric-input :refer [numeric-input*]]
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
[app.main.ui.ds.foundations.assets.icon :refer [icon*]]
[app.util.i18n :as i18n :refer [tr]]
[rumext.v2 :as mf]))
(defn all-equal?
[shape]
(= (:r1 shape) (:r2 shape) (:r3 shape) (:r4 shape)))
(mf/defc border-radius-menu
{::mf/wrap-props false
::mf/wrap [mf/memo]}
[{:keys [ids ids-with-children values]}]
(let [all-equal? (all-equal? values)
radius-expanded* (mf/use-state false)
radius-expanded (deref radius-expanded*)
change-radius
(mf/use-fn
(mf/deps ids-with-children)
(fn [update-fn]
(dwsh/update-shapes ids-with-children
(fn [shape]
(if (ctsr/has-radius? shape)
(update-fn shape)
shape))
{:reg-objects? true
:attrs [:r1 :r2 :r3 :r4]})))
toggle-radius-mode
(mf/use-fn
(mf/deps radius-expanded)
(fn []
(swap! radius-expanded* not)))
on-single-radius-change
(mf/use-fn
(mf/deps ids change-radius)
(fn [value]
(let []
(st/emit!
(change-radius (fn [shape]
(ctsr/set-radius-to-all-corners shape value)))))))
on-radius-4-change
(mf/use-fn
(mf/deps ids change-radius)
(fn [value attr]
(st/emit! (change-radius #(ctsr/set-radius-to-single-corner % attr value)))))
on-radius-r1-change #(on-radius-4-change % :r1)
on-radius-r2-change #(on-radius-4-change % :r2)
on-radius-r3-change #(on-radius-4-change % :r3)
on-radius-r4-change #(on-radius-4-change % :r4)]
[:div {:class (stl/css :radius)}
(if (not radius-expanded)
[:div {:class (stl/css :radius-1)
:title (tr "workspace.options.radius")}
[:> icon* {:id "corner-radius"
:size "s"
:class (stl/css :icon)}]
[:> numeric-input*
{:placeholder (cond
(not all-equal?)
"Mixed"
(= :multiple (:r1 values))
(tr "settings.multiple")
:else
"--")
:min 0
:nillable true
:on-change on-single-radius-change
:value (if all-equal? (:r1 values) nil)}]]
[:div {:class (stl/css :radius-4)}
[:div {:class (stl/css :small-input)}
[:> numeric-input*
{:placeholder "--"
:title (tr "workspace.options.radius-top-left")
:min 0
:on-change on-radius-r1-change
:value (:r1 values)}]]
[:div {:class (stl/css :small-input)}
[:> numeric-input*
{:placeholder "--"
:title (tr "workspace.options.radius-top-right")
:min 0
:on-change on-radius-r2-change
:value (:r2 values)}]]
[:div {:class (stl/css :small-input)}
[:> numeric-input*
{:placeholder "--"
:title (tr "workspace.options.radius-bottom-left")
:min 0
:on-change on-radius-r4-change
:value (:r4 values)}]]
[:div {:class (stl/css :small-input)}
[:> numeric-input*
{:placeholder "--"
:title (tr "workspace.options.radius-bottom-right")
:min 0
:on-change on-radius-r3-change
:value (:r3 values)}]]])
[:> icon-button* {:class (stl/css-case :selected radius-expanded)
:variant "ghost"
:on-click toggle-radius-mode
:aria-label (tr "workspace.options.radius")
:title (if radius-expanded
(tr "workspace.options.radius.all-corners")
(tr "workspace.options.radius.single-corners"))
:icon "corner-radius"}]]))

View file

@ -0,0 +1,44 @@
// 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/.
//
// Copyright (c) KALEIDOS INC
@import "refactor/common-refactor.scss";
.radius {
display: flex;
align-items: flex-start;
justify-content: flex-start;
gap: $s-4;
}
.radius-1 {
@extend .input-element;
@include bodySmallTypography;
width: $s-108;
position: relative;
}
.radius-4 {
display: grid;
grid-template-columns: 1fr 1fr;
gap: $s-4;
}
.small-input {
@extend .input-element;
@include bodySmallTypography;
width: $s-52;
margin-block-end: 0;
}
.selected {
border-color: var(--button-icon-border-color-selected);
background-color: var(--button-icon-background-color-selected);
color: var(--button-icon-foreground-color-selected);
}
.icon {
margin-inline: $s-4;
}

View file

@ -12,10 +12,8 @@
[app.common.logic.shapes :as cls]
[app.common.types.shape :as cts]
[app.common.types.shape.layout :as ctl]
[app.common.types.shape.radius :as ctsr]
[app.common.types.tokens-lib :as ctob]
[app.main.constants :refer [size-presets]]
[app.main.data.tokens :as dt]
[app.main.data.workspace :as udw]
[app.main.data.workspace.interactions :as dwi]
[app.main.data.workspace.shapes :as dwsh]
@ -28,6 +26,7 @@
[app.main.ui.context :as muc]
[app.main.ui.hooks :as hooks]
[app.main.ui.icons :as i]
[app.main.ui.workspace.sidebar.options.menus.border-radius :refer [border-radius-menu]]
[app.main.ui.workspace.tokens.core :as wtc]
[app.main.ui.workspace.tokens.editable-select :refer [editable-select]]
[app.main.ui.workspace.tokens.style-dictionary :as sd]
@ -43,7 +42,6 @@
:x :y
:ox :oy
:rotation
:rx :ry
:r1 :r2 :r3 :r4
:selrect
:points
@ -111,13 +109,6 @@
(mf/deps tokens)
#(ctob/group-by-type tokens))
border-radius-tokens (:border-radius tokens-by-type)
border-radius-options (mf/use-memo
(mf/deps shape border-radius-tokens)
#(wtc/tokens->select-options
{:shape shape
:tokens border-radius-tokens
:attributes (wtty/token-attributes :border-radius)}))
sizing-tokens (:sizing tokens-by-type)
width-options (mf/use-memo
(mf/deps shape sizing-tokens)
@ -188,13 +179,6 @@
proportion-lock (:proportion-lock values)
radius-mode (ctsr/radius-mode values)
all-equal? (ctsr/all-equal? values)
radius-multi? (mf/use-state nil)
radius-input-ref (mf/use-ref nil)
clip-content-ref (mf/use-ref nil)
show-in-viewer-ref (mf/use-ref nil)
@ -298,89 +282,11 @@
(st/emit! (udw/trigger-bounding-box-cloaking ids)
(udw/increase-rotation ids value)))))
;; RADIUS
change-radius
(mf/use-fn
(mf/deps ids-with-children)
(fn [update-fn]
(dwsh/update-shapes ids-with-children
(fn [shape]
(if (ctsr/has-radius? shape)
(update-fn shape)
shape))
{:reg-objects? true
:attrs [:rx :ry :r1 :r2 :r3 :r4 :applied-tokens]})))
on-switch-to-radius-1
(mf/use-fn
(mf/deps ids change-radius)
(fn [_value]
(if all-equal?
(st/emit! (change-radius ctsr/switch-to-radius-1))
(reset! radius-multi? true))))
on-switch-to-radius-4
(mf/use-fn
(mf/deps ids change-radius)
(fn [_value]
(st/emit! (change-radius ctsr/switch-to-radius-4))
(reset! radius-multi? false)))
toggle-radius-mode
(mf/use-fn
(mf/deps radius-mode)
(fn []
(if (= :radius-1 radius-mode)
(on-switch-to-radius-4)
(on-switch-to-radius-1))))
on-border-radius-token-unapply
(mf/use-fn
(mf/deps ids change-radius)
(fn [token]
(let [token-value (wtc/maybe-resolve-token-value token)]
(st/emit!
(change-radius (fn [shape]
(-> (dt/unapply-token-id shape (wtty/token-attributes :border-radius))
(ctsr/set-radius-1 token-value))))))))
on-radius-1-change
(mf/use-fn
(mf/deps ids change-radius)
(fn [value]
(let [token-value (wtc/maybe-resolve-token-value value)]
(st/emit!
(change-radius (fn [shape]
(-> (dt/maybe-apply-token-to-shape {:token (when token-value value)
:shape shape
:attributes (wtty/token-attributes :border-radius)})
(ctsr/set-radius-1 (or token-value value)))))))))
on-radius-multi-change
(mf/use-fn
(mf/deps ids change-radius)
(fn [event]
(let [value (-> event dom/get-target dom/get-value d/parse-integer)]
(when (some? value)
(st/emit! (change-radius ctsr/switch-to-radius-1)
(change-radius #(ctsr/set-radius-1 % value)))
(reset! radius-multi? false)))))
on-radius-4-change
(mf/use-fn
(mf/deps ids change-radius)
(fn [value attr]
(st/emit! (change-radius #(ctsr/set-radius-4 % attr value)))))
on-width-change #(on-size-change % :width)
on-height-change #(on-size-change % :height)
on-pos-x-change #(on-position-change % :x)
on-pos-y-change #(on-position-change % :y)
on-radius-r1-change #(on-radius-4-change % :r1)
on-radius-r2-change #(on-radius-4-change % :r2)
on-radius-r3-change #(on-radius-4-change % :r3)
on-radius-r4-change #(on-radius-4-change % :r4)
;; CLIP CONTENT AND SHOW IN VIEWER
on-change-clip-content
@ -406,15 +312,6 @@
(st/emit! (dwu/commit-undo-transaction undo-id)))))]
(mf/use-layout-effect
(mf/deps radius-mode @radius-multi?)
(fn []
(when (and (= radius-mode :radius-1)
(= @radius-multi? false))
;; when going back from radius-multi to normal radius-1,
;; restore focus to the newly created numeric-input
(let [radius-input (mf/ref-val radius-input-ref)]
(dom/focus! radius-input)))))
[:div {:class (stl/css :element-set)}
(when (and (options :presets)
(or (nil? all-types) (= (count all-types) 1)))
@ -473,7 +370,7 @@
:class (stl/css :numeric-input)
:value (:width values)}]
[:& editable-select
{:placeholder (if (= :multiple (:rx values)) (tr "settings.multiple") "--")
{:placeholder (if (= :multiple (:r1 values)) (tr "settings.multiple") "--")
:class (stl/css :token-select)
:disabled disabled-width-sizing?
:on-change on-width-change
@ -497,7 +394,7 @@
:class (stl/css :numeric-input)
:value (:height values)}]
[:& editable-select
{:placeholder (if (= :multiple (:rx values)) (tr "settings.multiple") "--")
{:placeholder (if (= :multiple (:r1 values)) (tr "settings.multiple") "--")
:class (stl/css :token-select)
:disabled disabled-height-sizing?
:on-change on-height-change
@ -541,7 +438,6 @@
:value (:y values)}]]])
(when (or (options :rotation) (options :radius))
[:div {:class (stl/css :rotation-radius)}
(when (options :rotation)
[:div {:class (stl/css :rotation)
:title (tr "workspace.options.rotation")}
@ -555,91 +451,8 @@
:on-change on-rotation-change
:class (stl/css :numeric-input)
:value (:rotation values)}]])
(when (options :radius)
[:div {:class (stl/css :radius)}
[:div {:class (stl/css :radius-inputs)}
(cond
(= radius-mode :radius-1)
[:div {:class (stl/css :radius-1)
:title (tr "workspace.options.radius")}
[:span {:class (stl/css :icon)} i/corner-radius]
(if-not design-tokens?
[:> numeric-input*
{:placeholder (if (= :multiple (:rx values)) (tr "settings.multiple") "--")
:ref radius-input-ref
:min 0
:on-change on-radius-1-change
:class (stl/css :numeric-input)
:value (:rx values)}]
[:& editable-select
{:placeholder (if (= :multiple (:rx values)) (tr "settings.multiple") "--")
:class (stl/css :token-select)
:on-change on-radius-1-change
:on-token-remove on-border-radius-token-unapply
:options border-radius-options
:position :right
:value (:rx values)
:input-props {:type "number"
:min 0}}])]
@radius-multi?
[:div {:class (stl/css :radius-1)
:title (tr "workspace.options.radius")}
[:span {:class (stl/css :icon)} i/corner-radius]
[:input
{:type "number"
:placeholder "Mixed"
:min 0
:on-change on-radius-multi-change
:class (stl/css :numeric-input)
:value (if all-equal? (:rx values) nil)}]]
(= radius-mode :radius-4)
[:div {:class (stl/css :radius-4)}
[:div {:class (stl/css :small-input)
:title (tr "workspace.options.radius-top-left")}
[:> numeric-input*
{:placeholder "--"
:min 0
:on-change on-radius-r1-change
:class (stl/css :numeric-input)
:value (:r1 values)}]]
[:div {:class (stl/css :small-input)
:title (tr "workspace.options.radius-top-right")}
[:> numeric-input*
{:placeholder "--"
:min 0
:on-change on-radius-r2-change
:class (stl/css :numeric-input)
:value (:r2 values)}]]
[:div {:class (stl/css :small-input)
:title (tr "workspace.options.radius-bottom-left")}
[:> numeric-input*
{:placeholder "--"
:min 0
:on-change on-radius-r4-change
:class (stl/css :numeric-input)
:value (:r4 values)}]]
[:div {:class (stl/css :small-input)
:title (tr "workspace.options.radius-bottom-right")}
[:> numeric-input*
{:placeholder "--"
:min 0
:on-change on-radius-r3-change
:class (stl/css :numeric-input)
:value (:r3 values)}]]])]
[:button {:class (stl/css-case :radius-mode true
:selected (= radius-mode :radius-4))
:title (if (= radius-mode :radius-4)
(tr "workspace.options.radius.all-corners")
(tr "workspace.options.radius.single-corners"))
:on-click toggle-radius-mode}
i/corner-radius]])])
[:& border-radius-menu {:ids ids :ids-with-children ids-with-children :values values :shape shape}])])
(when (or (options :clip-content) (options :show-in-viewer))
[:div {:class (stl/css :clip-show)}
(when (options :clip-content)

View file

@ -172,48 +172,6 @@
padding-top: $s-1;
}
}
.radius {
display: flex;
align-items: flex-start;
justify-content: flex-start;
gap: $s-4;
}
.radius-inputs {
display: flex;
}
.radius-1 {
@extend .input-element;
@include bodySmallTypography;
width: $s-108;
position: relative;
}
.radius-4 {
display: grid;
grid-template-columns: 1fr 1fr;
gap: $s-4;
.small-input {
@extend .input-element;
@include bodySmallTypography;
width: $s-52;
}
}
.radius-mode {
@extend .button-tertiary;
height: $s-32;
width: $s-28;
border-radius: $br-8;
svg {
@extend .button-icon;
stroke: var(--icon-foreground);
}
&.selected {
@extend .button-icon-selected;
}
}
.clip-show {
display: flex;

View file

@ -92,8 +92,8 @@
(defn update-shape-radius-all [value shape-ids]
(dwsh/update-shapes shape-ids
(fn [shape]
(when (ctsr/has-radius? shape)
(ctsr/set-radius-1 shape value)))
(when (ctsr/can-get-border-radius? shape)
(ctsr/set-radius-to-all-corners shape value)))
{:reg-objects? true
:attrs ctt/border-radius-keys}))
@ -112,12 +112,10 @@
(defn update-shape-radius-single-corner [value shape-ids attributes]
(dwsh/update-shapes shape-ids
(fn [shape]
(when (ctsr/has-radius? shape)
(cond-> shape
(:rx shape) (ctsr/switch-to-radius-4)
:always (ctsr/set-radius-4 (first attributes) value))))
(when (ctsr/can-get-border-radius? shape)
(ctsr/set-radius-to-single-corner shape (first attributes) value)))
{:reg-objects? true
:attrs [:rx :ry :r1 :r2 :r3 :r4]}))
:attrs ctt/border-radius-keys}))
(defn update-stroke-width
[value shape-ids]

View file

@ -17,8 +17,8 @@
(def filter-existing-values? false)
(def attributes->shape-update
{#{:rx :ry} (fn [v ids _] (wtch/update-shape-radius-all v ids))
#{:r1 :r2 :r3 :r4} wtch/update-shape-radius-single-corner
{#{:r1 :r2 :r3 :r4} wtch/update-shape-radius-single-corner
#_(fn [v ids _] (wtch/update-shape-radius-all v ids))
#{:fill} wtch/update-fill
#{:stroke-color} wtch/update-stroke-color
ctt/stroke-width-keys wtch/update-stroke-width
@ -70,11 +70,6 @@
(reduce
(fn [acc [attrs v]]
(cond
(some attrs #{:rx :ry}) (let [[_ a b] (data/diff #{:rx :ry} attrs)]
(cond-> (assoc acc b v)
;; Exact match in attrs
a (assoc a v)))
(some attrs #{:widht :height}) (let [[_ a b] (data/diff #{:width :height} attrs)]
(cond-> (assoc acc b v)
;; Exact match in attrs

View file

@ -313,11 +313,10 @@
:borderRadius
{:this true
:get #(-> % u/proxy->shape :rx)
:get #(-> % u/proxy->shape :r1)
:set
(fn [self value]
(let [id (obj/get self "$id")
shape (u/proxy->shape self)]
(let [id (obj/get self "$id")]
(cond
(or (not (us/safe-int? value)) (< value 0))
(u/display-not-valid :borderRadius value)
@ -325,21 +324,15 @@
(not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :borderRadius "Plugin doesn't have 'content:write' permission")
(or (not (ctsr/has-radius? shape)) (ctsr/radius-4? shape))
(st/emit! (dwsh/update-shapes [id] #(-> %
ctsr/switch-to-radius-1
(ctsr/set-radius-1 value))))
:else
(st/emit! (dwsh/update-shapes [id] #(ctsr/set-radius-1 % value))))))}
(st/emit! (dwsh/update-shapes [id] #(ctsr/set-radius-to-all-corners % value))))))}
:borderRadiusTopLeft
{:this true
:get #(-> % u/proxy->shape :r1)
:set
(fn [self value]
(let [id (obj/get self "$id")
shape (u/proxy->shape self)]
(let [id (obj/get self "$id")]
(cond
(not (us/safe-int? value))
(u/display-not-valid :borderRadiusTopLeft value)
@ -347,21 +340,15 @@
(not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :borderRadiusTopLeft "Plugin doesn't have 'content:write' permission")
(or (not (ctsr/has-radius? shape)) (not (ctsr/radius-4? shape)))
(st/emit! (dwsh/update-shapes [id] #(-> %
(ctsr/switch-to-radius-4)
(ctsr/set-radius-4 :r1 value))))
:else
(st/emit! (dwsh/update-shapes [id] #(ctsr/set-radius-4 % :r1 value))))))}
(st/emit! (dwsh/update-shapes [id] #(ctsr/set-radius-to-single-corner % :r1 value))))))}
:borderRadiusTopRight
{:this true
:get #(-> % u/proxy->shape :r2)
:set
(fn [self value]
(let [id (obj/get self "$id")
shape (u/proxy->shape self)]
(let [id (obj/get self "$id")]
(cond
(not (us/safe-int? value))
(u/display-not-valid :borderRadiusTopRight value)
@ -369,21 +356,15 @@
(not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :borderRadiusTopRight "Plugin doesn't have 'content:write' permission")
(or (not (ctsr/has-radius? shape)) (not (ctsr/radius-4? shape)))
(st/emit! (dwsh/update-shapes [id] #(-> %
(ctsr/switch-to-radius-4)
(ctsr/set-radius-4 :r2 value))))
:else
(st/emit! (dwsh/update-shapes [id] #(ctsr/set-radius-4 % :r2 value))))))}
(st/emit! (dwsh/update-shapes [id] #(ctsr/set-radius-to-single-corner % :r2 value))))))}
:borderRadiusBottomRight
{:this true
:get #(-> % u/proxy->shape :r3)
:set
(fn [self value]
(let [id (obj/get self "$id")
shape (u/proxy->shape self)]
(let [id (obj/get self "$id")]
(cond
(not (us/safe-int? value))
(u/display-not-valid :borderRadiusBottomRight value)
@ -391,21 +372,15 @@
(not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :borderRadiusBottomRight "Plugin doesn't have 'content:write' permission")
(or (not (ctsr/has-radius? shape)) (not (ctsr/radius-4? shape)))
(st/emit! (dwsh/update-shapes [id] #(-> %
(ctsr/switch-to-radius-4)
(ctsr/set-radius-4 :r3 value))))
:else
(st/emit! (dwsh/update-shapes [id] #(ctsr/set-radius-4 % :r3 value))))))}
(st/emit! (dwsh/update-shapes [id] #(ctsr/set-radius-to-single-corner % :r3 value))))))}
:borderRadiusBottomLeft
{:this true
:get #(-> % u/proxy->shape :r4)
:set
(fn [self value]
(let [id (obj/get self "$id")
shape (u/proxy->shape self)]
(let [id (obj/get self "$id")]
(cond
(not (us/safe-int? value))
(u/display-not-valid :borderRadiusBottomLeft value)
@ -413,13 +388,8 @@
(not (r/check-permission plugin-id "content:write"))
(u/display-not-valid :borderRadiusBottomLeft "Plugin doesn't have 'content:write' permission")
(or (not (ctsr/has-radius? shape)) (not (ctsr/radius-4? shape)))
(st/emit! (dwsh/update-shapes [id] #(-> %
(ctsr/switch-to-radius-4)
(ctsr/set-radius-4 :r4 value))))
:else
(st/emit! (dwsh/update-shapes [id] #(ctsr/set-radius-4 % :r4 value))))))}
(st/emit! (dwsh/update-shapes [id] #(ctsr/set-radius-to-single-corner % :r4 value))))))}
:opacity
{:this true

View file

@ -541,16 +541,13 @@
r3 (get-meta node :r3 d/parse-double)
r4 (get-meta node :r4 d/parse-double)
rx (-> (get svg-data :rx 0) d/parse-double)
ry (-> (get svg-data :ry 0) d/parse-double)]
rx (-> (get svg-data :rx 0) d/parse-double)]
(cond-> props
(some? r1)
(assoc :r1 r1 :r2 r2 :r3 r3 :r4 r4
:rx nil :ry nil)
(assoc :r1 r1 :r2 r2 :r3 r3 :r4 r4)
(and (nil? r1) (some? rx))
(assoc :rx rx :ry ry))))
(assoc :r1 rx :r2 rx :r3 rx :r4 rx))))
(defn add-image-data
[props type node]

View file

@ -113,11 +113,10 @@
(t/testing " - borderRadius"
(set! (.-borderRadius shape) 10)
(t/is (= (.-borderRadius shape) 10))
(t/is (= (get-in @store (get-shape-path :rx)) 10))
(t/is (= (get-in @store (get-shape-path :r1)) 10))
(set! (.-borderRadiusTopLeft shape) 20)
(t/is (= (.-borderRadiusTopLeft shape) 20))
(t/is (= (get-in @store (get-shape-path :rx)) nil))
(t/is (= (get-in @store (get-shape-path :r1)) 20))
(t/is (= (get-in @store (get-shape-path :r2)) 10))
(t/is (= (get-in @store (get-shape-path :r3)) 10))
@ -130,7 +129,6 @@
(t/is (= (.-borderRadiusBottomRight shape) 40))
(t/is (= (.-borderRadiusBottomLeft shape) 50))
(t/is (= (get-in @store (get-shape-path :rx)) nil))
(t/is (= (get-in @store (get-shape-path :r1)) 20))
(t/is (= (get-in @store (get-shape-path :r2)) 30))
(t/is (= (get-in @store (get-shape-path :r3)) 40))

View file

@ -49,7 +49,7 @@
store (ths/setup-store file)
rect-1 (cths/get-shape file :rect-1)
events [(wtch/apply-token {:shape-ids [(:id rect-1)]
:attributes #{:rx :ry}
:attributes #{:r1 :r2 :r3 :r4}
:token (toht/get-token file "borderRadius.md")
:on-update-shape wtch/update-shape-radius-all})]]
(tohs/run-store-async
@ -60,11 +60,9 @@
rect-1' (cths/get-shape file' :rect-1)]
(t/testing "shape `:applied-tokens` got updated"
(t/is (some? (:applied-tokens rect-1')))
(t/is (= (:rx (:applied-tokens rect-1')) (:name token)))
(t/is (= (:ry (:applied-tokens rect-1')) (:name token))))
(t/is (= (:r1 (:applied-tokens rect-1')) (:name token))))
(t/testing "shape radius got update to the resolved token value."
(t/is (= (:rx rect-1') 24))
(t/is (= (:ry rect-1') 24))))))))))
(t/is (= (:r1 rect-1') 24))))))))))
(t/deftest test-apply-multiple-tokens
(t/testing "applying a token twice with the same attributes will override the previously applied tokens values"
@ -74,11 +72,11 @@
store (ths/setup-store file)
rect-1 (cths/get-shape file :rect-1)
events [(wtch/apply-token {:shape-ids [(:id rect-1)]
:attributes #{:rx :ry}
:attributes #{:r1 :r2 :r3 :r4}
:token (toht/get-token file "borderRadius.sm")
:on-update-shape wtch/update-shape-radius-all})
(wtch/apply-token {:shape-ids [(:id rect-1)]
:attributes #{:rx :ry}
:attributes #{:r1 :r2 :r3 :r4}
:token (toht/get-token file "borderRadius.md")
:on-update-shape wtch/update-shape-radius-all})]]
(tohs/run-store-async
@ -89,11 +87,9 @@
rect-1' (cths/get-shape file' :rect-1)]
(t/testing "shape `:applied-tokens` got updated"
(t/is (some? (:applied-tokens rect-1')))
(t/is (= (:rx (:applied-tokens rect-1')) (:name token)))
(t/is (= (:ry (:applied-tokens rect-1')) (:name token))))
(t/is (= (:r1 (:applied-tokens rect-1')) (:name token))))
(t/testing "shape radius got update to the resolved token value."
(t/is (= (:rx rect-1') 24))
(t/is (= (:ry rect-1') 24))))))))))
(t/is (= (:r1 rect-1') 24))))))))))
(t/deftest test-apply-token-overwrite
(t/testing "removes old token attributes and applies only single attribute"
@ -103,15 +99,14 @@
store (ths/setup-store file)
rect-1 (cths/get-shape file :rect-1)
events [;; Apply "borderRadius.sm" to all border radius attributes
(wtch/apply-token {:attributes #{:rx :ry :r1 :r2 :r3 :r4}
(wtch/apply-token {:attributes #{:r1 :r2 :r3 :r4}
:token (toht/get-token file "borderRadius.sm")
:shape-ids [(:id rect-1)]
:on-update-shape wtch/update-shape-radius-all})
;; Apply single `:r1` attribute to same shape
;; while removing other attributes from the border-radius set
;; but keep `:r4` for testing purposes
(wtch/apply-token {:attributes #{:r1}
:attributes-to-remove #{:rx :ry :r1 :r2 :r3}
(wtch/apply-token {:attributes #{:r1 :r2 :r3}
:token (toht/get-token file "borderRadius.md")
:shape-ids [(:id rect-1)]
:on-update-shape wtch/update-shape-radius-all})]]
@ -122,8 +117,6 @@
token-sm (toht/get-token file' "borderRadius.sm")
token-md (toht/get-token file' "borderRadius.md")
rect-1' (cths/get-shape file' :rect-1)]
(t/testing "other border-radius attributes got removed"
(t/is (nil? (:rx (:applied-tokens rect-1')))))
(t/testing "r1 got applied with borderRadius.md"
(t/is (= (:r1 (:applied-tokens rect-1')) (:name token-md))))
(t/testing "while :r4 was kept with borderRadius.sm"
@ -314,7 +307,7 @@
rect-1 (cths/get-shape file :rect-1)
rect-2 (cths/get-shape file :rect-2)
events [(wtch/toggle-token {:shapes [rect-1 rect-2]
:token-type-props {:attributes #{:rx :ry}
:token-type-props {:attributes #{:r1 :r2 :r3 :r4}
:on-update-shape wtch/update-shape-radius-all}
:token (toht/get-token file "borderRadius.md")})]]
(tohs/run-store-async
@ -326,20 +319,18 @@
rect-2' (cths/get-shape file' :rect-2)]
(t/is (some? (:applied-tokens rect-1')))
(t/is (some? (:applied-tokens rect-2')))
(t/is (= (:rx (:applied-tokens rect-1')) (:name token-2')))
(t/is (= (:rx (:applied-tokens rect-2')) (:name token-2')))
(t/is (= (:ry (:applied-tokens rect-1')) (:name token-2')))
(t/is (= (:ry (:applied-tokens rect-2')) (:name token-2')))
(t/is (= (:rx rect-1') 24))
(t/is (= (:rx rect-2') 24)))))))))
(t/is (= (:r1 (:applied-tokens rect-1')) (:name token-2')))
(t/is (= (:r1 (:applied-tokens rect-2')) (:name token-2')))
(t/is (= (:r1 rect-1') 24))
(t/is (= (:r1 rect-2') 24)))))))))
(t/deftest test-toggle-token-mixed
(t/testing "should unapply given token if one of the selected items has the token applied while keeping other tokens with some attributes"
(t/async
done
(let [file (-> (setup-file-with-tokens)
(toht/apply-token-to-shape :rect-1 "borderRadius.sm" #{:rx :ry})
(toht/apply-token-to-shape :rect-3 "borderRadius.md" #{:rx :ry}))
(toht/apply-token-to-shape :rect-1 "borderRadius.sm" #{:r1 :r2 :r3 :r4})
(toht/apply-token-to-shape :rect-3 "borderRadius.md" #{:r1 :r2 :r3 :r4}))
store (ths/setup-store file)
rect-with-token (cths/get-shape file :rect-1)
@ -348,7 +339,7 @@
events [(wtch/toggle-token {:shapes [rect-with-token rect-without-token rect-with-other-token]
:token (toht/get-token file "borderRadius.sm")
:token-type-props {:attributes #{:rx :ry}}})]]
:token-type-props {:attributes #{:r1 :r2 :r3 :r4}}})]]
(tohs/run-store-async
store done events
(fn [new-state]
@ -358,8 +349,7 @@
rect-with-other-token' (cths/get-shape file' :rect-3)]
(t/testing "rect-with-token got the token removed"
(t/is (nil? (:rx (:applied-tokens rect-with-token'))))
(t/is (nil? (:ry (:applied-tokens rect-with-token')))))
(t/is (nil? (:r1 (:applied-tokens rect-with-token')))))
(t/testing "rect-without-token didn't get updated"
(t/is (= (:applied-tokens rect-without-token') (:applied-tokens rect-without-token))))
@ -372,8 +362,8 @@
(t/async
done
(let [file (-> (setup-file-with-tokens)
(toht/apply-token-to-shape :rect-1 "borderRadius.md" #{:rx :ry})
(toht/apply-token-to-shape :rect-3 "borderRadius.md" #{:rx :ry}))
(toht/apply-token-to-shape :rect-1 "borderRadius.md" #{:r1 :r2 :r3 :r4})
(toht/apply-token-to-shape :rect-3 "borderRadius.md" #{:r1 :r2 :r3 :r4}))
store (ths/setup-store file)
rect-with-other-token-1 (cths/get-shape file :rect-1)
@ -382,7 +372,7 @@
events [(wtch/toggle-token {:shapes [rect-with-other-token-1 rect-without-token rect-with-other-token-2]
:token (toht/get-token file "borderRadius.sm")
:token-type-props {:attributes #{:rx :ry}}})]]
:token-type-props {:attributes #{:r1 :r2 :r3 :r4}}})]]
(tohs/run-store-async
store done events
(fn [new-state]
@ -393,10 +383,6 @@
rect-with-other-token-2' (cths/get-shape file' :rect-3)]
(t/testing "token got applied to all shapes"
(t/is (= (:rx (:applied-tokens rect-with-other-token-1')) (:name target-token)))
(t/is (= (:rx (:applied-tokens rect-without-token')) (:name target-token)))
(t/is (= (:rx (:applied-tokens rect-with-other-token-2')) (:name target-token)))
(t/is (= (:ry (:applied-tokens rect-with-other-token-1')) (:name target-token)))
(t/is (= (:ry (:applied-tokens rect-without-token')) (:name target-token)))
(t/is (= (:ry (:applied-tokens rect-with-other-token-2')) (:name target-token)))))))))))
(t/is (= (:r1 (:applied-tokens rect-with-other-token-1')) (:name target-token)))
(t/is (= (:r1 (:applied-tokens rect-without-token')) (:name target-token)))
(t/is (= (:r1 (:applied-tokens rect-with-other-token-2')) (:name target-token)))))))))))

View file

@ -25,11 +25,6 @@
(t/testing "doesnt accept invalid double"
(t/is (nil? (wtt/parse-token-value ".3")))))
(t/deftest remove-attributes-for-token-id
(t/testing "removes attributes matching the `token`, keeps other attributes"
(t/is (= {:ry "b"}
(wtt/remove-attributes-for-token #{:rx :ry} {:name "a"} {:rx "a" :ry "b"})))))
(t/deftest token-applied-test
(t/testing "matches passed token with `:token-attributes`"
(t/is (true? (wtt/token-applied? {:name "a"} {:applied-tokens {:x "a"}} #{:x}))))