mirror of
https://github.com/penpot/penpot.git
synced 2025-12-12 06:24:17 +01:00
🐛 Unapply layout item tokens when moving out of a layout
This commit is contained in:
@@ -40,6 +40,7 @@
|
||||
- Fix typo [Taiga #11970](https://tree.taiga.io/project/penpot/issue/11970)
|
||||
- Fix typos [Taiga #11971](https://tree.taiga.io/project/penpot/issue/11971)
|
||||
- Fix inconsistent naming for "Flatten" [Taiga #8371](https://tree.taiga.io/project/penpot/issue/8371)
|
||||
- Layout item tokens should be unapplied when moving out of a layout [Taiga #11012](https://tree.taiga.io/project/penpot/issue/11012)
|
||||
|
||||
## 2.9.0
|
||||
|
||||
|
||||
@@ -405,9 +405,10 @@
|
||||
(remove #(= % parent-id) all-parents))]
|
||||
|
||||
(-> changes
|
||||
;; Remove layout-item properties when moving a shape outside a layout
|
||||
;; Remove layout-item properties and tokens when moving a shape outside a layout
|
||||
(cond-> (not (ctl/any-layout? parent))
|
||||
(pcb/update-shapes ids ctl/remove-layout-item-data))
|
||||
(-> (pcb/update-shapes ids ctl/remove-layout-item-data)
|
||||
(pcb/update-shapes ids cto/unapply-layout-item-tokens)))
|
||||
|
||||
;; Remove the hide in viewer flag
|
||||
(cond-> (and (not= uuid/zero parent-id) (cfh/frame-shape? parent))
|
||||
|
||||
@@ -77,22 +77,25 @@
|
||||
[file shape-label token-name token-attrs shape-attrs resolved-value]
|
||||
(let [page (thf/current-page file)
|
||||
shape (ths/get-shape file shape-label)
|
||||
shape' (as-> shape $
|
||||
(cto/apply-token-to-shape {:shape $
|
||||
:token {:name token-name}
|
||||
:attributes token-attrs})
|
||||
(reduce (fn [shape attr]
|
||||
(case attr
|
||||
:stroke-width (set-stroke-width shape resolved-value)
|
||||
:stroke-color (set-stroke-color shape resolved-value)
|
||||
:fill (set-fill-color shape resolved-value)
|
||||
(ctn/set-shape-attr shape attr resolved-value {:ignore-touched true})))
|
||||
$
|
||||
shape-attrs))]
|
||||
shape' (when shape
|
||||
(as-> shape $
|
||||
(cto/apply-token-to-shape {:shape $
|
||||
:token {:name token-name}
|
||||
:attributes token-attrs})
|
||||
(reduce (fn [shape attr]
|
||||
(case attr
|
||||
:stroke-width (set-stroke-width shape resolved-value)
|
||||
:stroke-color (set-stroke-color shape resolved-value)
|
||||
:fill (set-fill-color shape resolved-value)
|
||||
(ctn/set-shape-attr shape attr resolved-value {:ignore-touched true})))
|
||||
$
|
||||
shape-attrs)))]
|
||||
|
||||
(ctf/update-file-data
|
||||
file
|
||||
(fn [file-data]
|
||||
(ctpl/update-page file-data
|
||||
(:id page)
|
||||
#(ctst/set-shape % shape'))))))
|
||||
(if shape'
|
||||
(ctf/update-file-data
|
||||
file
|
||||
(fn [file-data]
|
||||
(ctpl/update-page file-data
|
||||
(:id page)
|
||||
#(ctst/set-shape % shape'))))
|
||||
file)))
|
||||
|
||||
@@ -14,22 +14,22 @@
|
||||
[app.common.schema :as sm]
|
||||
[app.common.uuid :as uuid]))
|
||||
|
||||
;; :layout ;; :flex, :grid in the future
|
||||
;; :layout-flex-dir ;; :row, :row-reverse, :column, :column-reverse
|
||||
;; :layout-gap-type ;; :simple, :multiple
|
||||
;; :layout-gap ;; {:row-gap number , :column-gap number}
|
||||
;; :layout ;; :flex, :grid in the future
|
||||
;; :layout-flex-dir ;; :row, :row-reverse, :column, :column-reverse
|
||||
;; :layout-gap-type ;; :simple, :multiple
|
||||
;; :layout-gap ;; {:row-gap number , :column-gap number}
|
||||
|
||||
;; :layout-align-items ;; :start :end :center :stretch
|
||||
;; :layout-align-content ;; :start :center :end :space-between :space-around :space-evenly :stretch (by default)
|
||||
;; :layout-align-items ;; :start :end :center :stretch
|
||||
;; :layout-align-content ;; :start :center :end :space-between :space-around :space-evenly :stretch (by default)
|
||||
;; :layout-justify-items ;; :start :center :end :space-between :space-around :space-evenly
|
||||
;; :layout-justify-content ;; :start :center :end :space-between :space-around :space-evenly
|
||||
;; :layout-wrap-type ;; :wrap, :nowrap
|
||||
;; :layout-padding-type ;; :simple, :multiple
|
||||
;; :layout-padding ;; {:p1 num :p2 num :p3 num :p4 num} number could be negative
|
||||
;; :layout-justify-content ;; :start :center :end :space-between :space-around :space-evenly
|
||||
;; :layout-wrap-type ;; :wrap, :nowrap
|
||||
;; :layout-padding-type ;; :simple, :multiple
|
||||
;; :layout-padding ;; {:p1 num :p2 num :p3 num :p4 num} number could be negative
|
||||
|
||||
;; layout-grid-rows ;; vector of grid-track
|
||||
;; layout-grid-columns ;; vector of grid-track
|
||||
;; layout-grid-cells ;; map of id->grid-cell
|
||||
;; layout-grid-rows ;; vector of grid-track
|
||||
;; layout-grid-columns ;; vector of grid-track
|
||||
;; layout-grid-cells ;; map of id->grid-cell
|
||||
|
||||
;; ITEMS
|
||||
;; :layout-item-margin ;; {:m1 0 :m2 0 :m3 0 :m4 0}
|
||||
|
||||
@@ -83,15 +83,25 @@
|
||||
|
||||
(def stroke-width-keys (schema-keys schema:stroke-width))
|
||||
|
||||
(def ^:private schema:sizing
|
||||
[:map {:title "SizingTokenAttrs"}
|
||||
(def ^:private schema:sizing-base
|
||||
[:map {:title "SizingBaseTokenAttrs"}
|
||||
[:width {:optional true} token-name-ref]
|
||||
[:height {:optional true} token-name-ref]
|
||||
[:height {:optional true} token-name-ref]])
|
||||
|
||||
(def ^:private schema:sizing-layout-item
|
||||
[:map {:title "SizingLayoutItemTokenAttrs"}
|
||||
[:layout-item-min-w {:optional true} token-name-ref]
|
||||
[:layout-item-max-w {:optional true} token-name-ref]
|
||||
[:layout-item-min-h {:optional true} token-name-ref]
|
||||
[:layout-item-max-h {:optional true} token-name-ref]])
|
||||
|
||||
(def ^:private schema:sizing
|
||||
(-> (reduce mu/union [schema:sizing-base
|
||||
schema:sizing-layout-item])
|
||||
(mu/update-properties assoc :title "SizingTokenAttrs")))
|
||||
|
||||
(def sizing-layout-item-keys (schema-keys schema:sizing-layout-item))
|
||||
|
||||
(def sizing-keys (schema-keys schema:sizing))
|
||||
|
||||
(def ^:private schema:opacity
|
||||
@@ -378,6 +388,13 @@
|
||||
(defn unapply-token-id [shape attributes]
|
||||
(update shape :applied-tokens d/without-keys attributes))
|
||||
|
||||
(defn unapply-layout-item-tokens
|
||||
"Unapplies all layout item related tokens from shape."
|
||||
[shape]
|
||||
(let [layout-item-attrs (set/union sizing-layout-item-keys
|
||||
spacing-margin-keys)]
|
||||
(unapply-token-id shape layout-item-attrs)))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; TYPOGRAPHY
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
[app.common.test-helpers.files :as thf]
|
||||
[app.common.test-helpers.ids-map :as thi]
|
||||
[app.common.test-helpers.shapes :as ths]
|
||||
[app.common.test-helpers.tokens :as tht]
|
||||
[app.common.types.tokens-lib :as ctob]
|
||||
[app.common.uuid :as uuid]
|
||||
[clojure.test :as t]))
|
||||
|
||||
@@ -47,7 +49,6 @@
|
||||
(t/is (= (:parent-id frame-to-move) uuid/zero))
|
||||
(t/is (= (:parent-id frame-to-move') (:id frame-parent')))))
|
||||
|
||||
|
||||
(t/deftest test-relocate-shape-out-of-group
|
||||
(let [;; ==== Setup
|
||||
file (-> (thf/sample-file :file1)
|
||||
@@ -68,7 +69,6 @@
|
||||
0 ;; to-index
|
||||
#{(:id circle)}) ;; ids
|
||||
|
||||
|
||||
file' (thf/apply-changes file changes)
|
||||
|
||||
;; ==== Get
|
||||
@@ -81,4 +81,134 @@
|
||||
(t/is (= (:parent-id circle) (:id group)))
|
||||
(t/is (= (:parent-id circle') uuid/zero))
|
||||
(t/is group)
|
||||
(t/is (nil? group'))))
|
||||
(t/is (nil? group'))))
|
||||
|
||||
(t/deftest test-relocate-shape-out-of-layout-manual
|
||||
(let [;; ==== Setup
|
||||
file (-> (thf/sample-file :file1)
|
||||
(tho/add-frame :frame-1
|
||||
:layout :flex ;; TODO: those values come from main.data.workspace.shape_layout/default-layout-params
|
||||
:layout-flex-dir :row ;; it should be good to use it directly, but first it should be moved to common.logic
|
||||
:layout-gap-type :multiple
|
||||
:layout-gap {:row-gap 0 :column-gap 0}
|
||||
:layout-align-items :start
|
||||
:layout-justify-content :start
|
||||
:layout-align-content :stretch
|
||||
:layout-wrap-type :nowrap
|
||||
:layout-padding-type :simple
|
||||
:layout-padding {:p1 0 :p2 0 :p3 0 :p4 0})
|
||||
(ths/add-sample-shape :circle-1 :parent-label :frame-1
|
||||
:layout-item-margin {:m1 10 :m2 10 :m3 10 :m4 10}
|
||||
:layout-item-margin-type :multiple
|
||||
:layout-item-h-sizing :auto
|
||||
:layout-item-v-sizing :auto
|
||||
:layout-item-max-h 1000
|
||||
:layout-item-min-h 100
|
||||
:layout-item-max-w 2000
|
||||
:layout-item-min-w 200
|
||||
:layout-item-absolute false
|
||||
:layout-item-z-index 10))
|
||||
|
||||
page (thf/current-page file)
|
||||
circle (ths/get-shape file :circle-1)
|
||||
|
||||
;; ==== Action
|
||||
|
||||
changes (cls/generate-relocate (-> (pcb/empty-changes nil)
|
||||
(pcb/with-page-id (:id page))
|
||||
(pcb/with-objects (:objects page)))
|
||||
uuid/zero ;; parent-id
|
||||
0 ;; to-index
|
||||
#{(:id circle)}) ;; ids
|
||||
|
||||
;; ==== Get
|
||||
file' (thf/apply-changes file changes)
|
||||
circle' (ths/get-shape file' :circle-1)]
|
||||
|
||||
;; ==== Check
|
||||
|
||||
;; the layout item attributes are removed
|
||||
(t/is (nil? (:layout-item-margin circle')))
|
||||
(t/is (nil? (:layout-item-margin-type circle')))
|
||||
(t/is (nil? (:layout-item-h-sizing circle')))
|
||||
(t/is (nil? (:layout-item-v-sizing circle')))
|
||||
(t/is (nil? (:layout-item-max-h circle')))
|
||||
(t/is (nil? (:layout-item-min-h circle')))
|
||||
(t/is (nil? (:layout-item-max-w circle')))
|
||||
(t/is (nil? (:layout-item-min-w circle')))
|
||||
(t/is (nil? (:layout-item-absolute circle')))
|
||||
(t/is (nil? (:layout-item-z-index circle')))))
|
||||
|
||||
(t/deftest test-relocate-shape-out-of-layout-with-tokens
|
||||
(let [;; ==== Setup
|
||||
file (-> (thf/sample-file :file1)
|
||||
(tht/add-tokens-lib)
|
||||
(tht/update-tokens-lib #(-> %
|
||||
(ctob/add-set (ctob/make-token-set :name "test-token-set"))
|
||||
(ctob/add-theme (ctob/make-token-theme :name "test-theme"
|
||||
:sets #{"test-token-set"}))
|
||||
(ctob/set-active-themes #{"/test-theme"})
|
||||
(ctob/add-token-in-set "test-token-set"
|
||||
(ctob/make-token :id (thi/new-id! :token-sizing)
|
||||
:name "token-sizing"
|
||||
:type :sizing
|
||||
:value 10))
|
||||
(ctob/add-token-in-set "test-token-set"
|
||||
(ctob/make-token :id (thi/new-id! :token-spacing)
|
||||
:name "token-spacing"
|
||||
:type :spacing
|
||||
:value 30))))
|
||||
(tho/add-frame :frame-1
|
||||
:layout :flex ;; TODO: those values come from main.data.workspace.shape_layout/default-layout-params
|
||||
:layout-flex-dir :row ;; it should be good to use it directly, but first it should be moved to common.logic
|
||||
:layout-gap-type :multiple
|
||||
:layout-gap {:row-gap 0 :column-gap 0}
|
||||
:layout-align-items :start
|
||||
:layout-justify-content :start
|
||||
:layout-align-content :stretch
|
||||
:layout-wrap-type :nowrap
|
||||
:layout-padding-type :simple
|
||||
:layout-padding {:p1 0 :p2 0 :p3 0 :p4 0})
|
||||
(ths/add-sample-shape :circle-1 :parent-label :frame-1)
|
||||
(tht/apply-token-to-shape :circle-1
|
||||
"token-sizing"
|
||||
[:layout-item-max-h :layout-item-max-w :layout-item-min-h :layout-item-min-w]
|
||||
[:layout-item-max-h :layout-item-max-w :layout-item-min-h :layout-item-min-w]
|
||||
10)
|
||||
(tht/apply-token-to-shape :circle-1
|
||||
"token-spacing"
|
||||
[:m1 :m2 :m3 :m4]
|
||||
[:layout-item-margin]
|
||||
{:m1 30 :m2 30 :m3 30 :m4 30}))
|
||||
|
||||
page (thf/current-page file)
|
||||
circle (ths/get-shape file :circle-1)
|
||||
|
||||
;; ==== Action
|
||||
|
||||
changes (cls/generate-relocate (-> (pcb/empty-changes nil)
|
||||
(pcb/with-page-id (:id page))
|
||||
(pcb/with-objects (:objects page)))
|
||||
uuid/zero ;; parent-id
|
||||
0 ;; to-index
|
||||
#{(:id circle)}) ;; ids
|
||||
|
||||
|
||||
;; ==== Get
|
||||
file' (thf/apply-changes file changes)
|
||||
circle' (ths/get-shape file' :circle-1)]
|
||||
|
||||
;; ==== Check
|
||||
|
||||
;; the layout item attributes and tokens are removed
|
||||
(t/is (empty? (:applied-tokens circle')))
|
||||
(t/is (nil? (:layout-item-margin circle')))
|
||||
(t/is (nil? (:layout-item-margin-type circle')))
|
||||
(t/is (nil? (:layout-item-h-sizing circle')))
|
||||
(t/is (nil? (:layout-item-v-sizing circle')))
|
||||
(t/is (nil? (:layout-item-max-h circle')))
|
||||
(t/is (nil? (:layout-item-min-h circle')))
|
||||
(t/is (nil? (:layout-item-max-w circle')))
|
||||
(t/is (nil? (:layout-item-min-w circle')))
|
||||
(t/is (nil? (:layout-item-absolute circle')))
|
||||
(t/is (nil? (:layout-item-z-index circle')))))
|
||||
Reference in New Issue
Block a user