mirror of
https://github.com/penpot/penpot.git
synced 2026-09-13 07:30:24 +00:00
🐛 Fix token unapply for text shapes
This commit is contained in:
parent
43ba30c694
commit
cf78e68787
@ -57,6 +57,7 @@ on-premises instances** that want to keep up to date.
|
|||||||
- Fix button width [Taiga #11394](https://tree.taiga.io/project/penpot/issue/11394)
|
- Fix button width [Taiga #11394](https://tree.taiga.io/project/penpot/issue/11394)
|
||||||
- Fix mixed letter spacing and line height [Taiga #11178](https://tree.taiga.io/project/penpot/issue/11178)
|
- Fix mixed letter spacing and line height [Taiga #11178](https://tree.taiga.io/project/penpot/issue/11178)
|
||||||
- Fix snap nodes shortcut [Taiga #11054](https://tree.taiga.io/project/penpot/issue/11054)
|
- Fix snap nodes shortcut [Taiga #11054](https://tree.taiga.io/project/penpot/issue/11054)
|
||||||
|
- Fix changing a text property in a text layer does not unapply the previously applied token in the same property [Taiga #11337}(https://tree.taiga.io/project/penpot/issue/11337)
|
||||||
|
|
||||||
## 2.7.2
|
## 2.7.2
|
||||||
|
|
||||||
|
|||||||
@ -161,7 +161,6 @@
|
|||||||
(contains? (meta changes) ::file-data)
|
(contains? (meta changes) ::file-data)
|
||||||
"Call (with-file-data) before using this function"))
|
"Call (with-file-data) before using this function"))
|
||||||
|
|
||||||
|
|
||||||
(defn- lookup-objects
|
(defn- lookup-objects
|
||||||
[changes]
|
[changes]
|
||||||
(let [data (::file-data (meta changes))]
|
(let [data (::file-data (meta changes))]
|
||||||
|
|||||||
@ -16,19 +16,30 @@
|
|||||||
[app.common.types.pages-list :as ctpl]
|
[app.common.types.pages-list :as ctpl]
|
||||||
[app.common.types.shape.interactions :as ctsi]
|
[app.common.types.shape.interactions :as ctsi]
|
||||||
[app.common.types.shape.layout :as ctl]
|
[app.common.types.shape.layout :as ctl]
|
||||||
|
[app.common.types.text :as ctt]
|
||||||
[app.common.types.token :as cto]
|
[app.common.types.token :as cto]
|
||||||
[app.common.uuid :as uuid]))
|
[app.common.uuid :as uuid]
|
||||||
|
[clojure.set :as set]))
|
||||||
|
|
||||||
(defn- generate-unapply-tokens
|
(defn- generate-unapply-tokens
|
||||||
"When updating attributes that have a token applied, we must unapply it, because the value
|
"When updating attributes that have a token applied, we must unapply it, because the value
|
||||||
of the attribute now has been given directly, and does not come from the token."
|
of the attribute now has been given directly, and does not come from the token."
|
||||||
[changes objects changed-sub-attr]
|
[changes objects changed-sub-attr]
|
||||||
(let [mod-obj-changes (->> (:redo-changes changes)
|
(let [new-objects (pcb/get-objects changes)
|
||||||
|
mod-obj-changes (->> (:redo-changes changes)
|
||||||
(filter #(= (:type %) :mod-obj)))
|
(filter #(= (:type %) :mod-obj)))
|
||||||
|
|
||||||
|
text-changed-attrs
|
||||||
|
(fn [shape]
|
||||||
|
(let [new-shape (get new-objects (:id shape))
|
||||||
|
attrs (ctt/get-diff-attrs (:content shape) (:content new-shape))]
|
||||||
|
(apply set/union (map cto/shape-attr->token-attrs attrs))))
|
||||||
|
|
||||||
check-attr (fn [shape changes attr]
|
check-attr (fn [shape changes attr]
|
||||||
(let [tokens (get shape :applied-tokens {})
|
(let [tokens (get shape :applied-tokens {})
|
||||||
token-attrs (cto/shape-attr->token-attrs attr changed-sub-attr)]
|
token-attrs (if (or (not= (:type shape) :text) (not= attr :content))
|
||||||
|
(cto/shape-attr->token-attrs attr changed-sub-attr)
|
||||||
|
(text-changed-attrs shape))]
|
||||||
(if (some #(contains? tokens %) token-attrs)
|
(if (some #(contains? tokens %) token-attrs)
|
||||||
(pcb/update-shapes changes [(:id shape)] #(cto/unapply-token-id % token-attrs))
|
(pcb/update-shapes changes [(:id shape)] #(cto/unapply-token-id % token-attrs))
|
||||||
changes)))
|
changes)))
|
||||||
|
|||||||
@ -37,8 +37,6 @@
|
|||||||
(merge shape
|
(merge shape
|
||||||
text-params))))
|
text-params))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(defn add-frame
|
(defn add-frame
|
||||||
[file frame-label & {:keys [] :as params}]
|
[file frame-label & {:keys [] :as params}]
|
||||||
;; Generated shape tree:
|
;; Generated shape tree:
|
||||||
|
|||||||
@ -11,6 +11,7 @@
|
|||||||
[app.common.files.helpers :as cfh]
|
[app.common.files.helpers :as cfh]
|
||||||
[app.common.test-helpers.files :as thf]
|
[app.common.test-helpers.files :as thf]
|
||||||
[app.common.test-helpers.ids-map :as thi]
|
[app.common.test-helpers.ids-map :as thi]
|
||||||
|
[app.common.text :as txt]
|
||||||
[app.common.types.color :as ctc]
|
[app.common.types.color :as ctc]
|
||||||
[app.common.types.container :as ctn]
|
[app.common.types.container :as ctn]
|
||||||
[app.common.types.pages-list :as ctpl]
|
[app.common.types.pages-list :as ctpl]
|
||||||
@ -81,6 +82,21 @@
|
|||||||
(:id page)
|
(:id page)
|
||||||
#(ctst/set-shape % (ctn/set-shape-attr shape attr val)))))))
|
#(ctst/set-shape % (ctn/set-shape-attr shape attr val)))))))
|
||||||
|
|
||||||
|
(defn update-shape-text
|
||||||
|
[file shape-label attr val & {:keys [page-label]}]
|
||||||
|
(let [page (if page-label
|
||||||
|
(thf/get-page file page-label)
|
||||||
|
(thf/current-page file))
|
||||||
|
shape (ctst/get-shape page (thi/id shape-label))]
|
||||||
|
(update file :data
|
||||||
|
(fn [file-data]
|
||||||
|
(ctpl/update-page file-data
|
||||||
|
(:id page)
|
||||||
|
#(ctst/set-shape % (txt/update-text-content shape
|
||||||
|
txt/is-content-node?
|
||||||
|
d/txt-merge
|
||||||
|
{attr val})))))))
|
||||||
|
|
||||||
(defn sample-library-color
|
(defn sample-library-color
|
||||||
[label & {:keys [name path color opacity gradient image]}]
|
[label & {:keys [name path color opacity gradient image]}]
|
||||||
(-> {:id (thi/new-id! label)
|
(-> {:id (thi/new-id! label)
|
||||||
|
|||||||
@ -11,9 +11,12 @@
|
|||||||
|
|
||||||
(defn- compare-text-content
|
(defn- compare-text-content
|
||||||
"Given two content text structures, conformed by maps and vectors,
|
"Given two content text structures, conformed by maps and vectors,
|
||||||
compare them, and returns a set with the type of differences.
|
compare them, and returns a set with the differences info.
|
||||||
The possibilities are :text-content-text :text-content-attribute and :text-content-structure."
|
If the structures are equal, it returns an empty set. If the structure
|
||||||
[a b]
|
has changed, it returns :text-content-structure. There are two
|
||||||
|
callbacks to specify what to return when there is a text change with
|
||||||
|
the same structure, and when attributes change."
|
||||||
|
[a b {:keys [text-cb attribute-cb] :as callbacks}]
|
||||||
(cond
|
(cond
|
||||||
;; If a and b are equal, there is no diff
|
;; If a and b are equal, there is no diff
|
||||||
(= a b)
|
(= a b)
|
||||||
@ -38,18 +41,18 @@
|
|||||||
#{:text-content-structure}
|
#{:text-content-structure}
|
||||||
(into acc
|
(into acc
|
||||||
(apply set/union
|
(apply set/union
|
||||||
(map #(compare-text-content %1 %2) v1 v2))))
|
(map #(compare-text-content %1 %2 callbacks) v1 v2))))
|
||||||
|
|
||||||
;; If the key is :text, and they are different, it is a text differece
|
;; If the key is :text, and they are different, it is a text differece
|
||||||
(= k :text)
|
(= k :text)
|
||||||
(if (not= v1 v2)
|
(if (not= v1 v2)
|
||||||
(conj acc :text-content-text)
|
(text-cb acc)
|
||||||
acc)
|
acc)
|
||||||
|
|
||||||
:else
|
:else
|
||||||
;; If the key is not :text, and they are different, it is an attribute differece
|
;; If the key is not :text, and they are different, it is an attribute differece
|
||||||
(if (not= v1 v2)
|
(if (not= v1 v2)
|
||||||
(conj acc :text-content-attribute)
|
(attribute-cb acc k)
|
||||||
acc))))
|
acc))))
|
||||||
#{}
|
#{}
|
||||||
keys))
|
keys))
|
||||||
@ -57,7 +60,6 @@
|
|||||||
:else
|
:else
|
||||||
#{:text-content-structure}))
|
#{:text-content-structure}))
|
||||||
|
|
||||||
|
|
||||||
(defn equal-attrs?
|
(defn equal-attrs?
|
||||||
"Given a text structure, and a map of attrs, check that all the internal attrs in
|
"Given a text structure, and a map of attrs, check that all the internal attrs in
|
||||||
paragraphs and sentences have the same attrs"
|
paragraphs and sentences have the same attrs"
|
||||||
@ -79,10 +81,15 @@
|
|||||||
(defn get-diff-type
|
(defn get-diff-type
|
||||||
"Given two content text structures, conformed by maps and vectors,
|
"Given two content text structures, conformed by maps and vectors,
|
||||||
compare them, and returns a set with the type of differences.
|
compare them, and returns a set with the type of differences.
|
||||||
The possibilities are :text-content-text :text-content-attribute,
|
The possibilities are
|
||||||
:text-content-structure and :text-content-structure-same-attrs."
|
:text-content-text
|
||||||
|
:text-content-attribute,
|
||||||
|
:text-content-structure
|
||||||
|
:text-content-structure-same-attrs."
|
||||||
[a b]
|
[a b]
|
||||||
(let [diff-type (compare-text-content a b)]
|
(let [diff-type (compare-text-content a b
|
||||||
|
{:text-cb (fn [acc] (conj acc :text-content-text))
|
||||||
|
:attribute-cb (fn [acc _] (conj acc :text-content-attribute))})]
|
||||||
(if-not (contains? diff-type :text-content-structure)
|
(if-not (contains? diff-type :text-content-structure)
|
||||||
diff-type
|
diff-type
|
||||||
(let [;; get attrs of the first paragraph of the first paragraph-set
|
(let [;; get attrs of the first paragraph of the first paragraph-set
|
||||||
@ -92,6 +99,24 @@
|
|||||||
#{:text-content-structure :text-content-structure-same-attrs}
|
#{:text-content-structure :text-content-structure-same-attrs}
|
||||||
diff-type)))))
|
diff-type)))))
|
||||||
|
|
||||||
|
(defn get-diff-attrs
|
||||||
|
"Given two content text structures, conformed by maps and vectors,
|
||||||
|
compare them, and returns a set with the attributes that have changed.
|
||||||
|
This is independent of the text structure, so if the structure changes
|
||||||
|
but the attributes are the same, it will return an empty set."
|
||||||
|
[a b]
|
||||||
|
(let [diff-attrs (compare-text-content a b
|
||||||
|
{:text-cb identity
|
||||||
|
:attribute-cb (fn [acc attr] (conj acc attr))})]
|
||||||
|
(if-not (contains? diff-attrs :text-content-structure)
|
||||||
|
diff-attrs
|
||||||
|
(let [;; get attrs of the first paragraph of the first paragraph-set
|
||||||
|
attrs (get-first-paragraph-text-attrs a)]
|
||||||
|
(if (and (equal-attrs? a attrs)
|
||||||
|
(equal-attrs? b attrs))
|
||||||
|
#{}
|
||||||
|
(disj diff-attrs :text-content-structure))))))
|
||||||
|
|
||||||
;; TODO We know that there are cases that the blocks of texts are separated
|
;; TODO We know that there are cases that the blocks of texts are separated
|
||||||
;; differently: ["one" " " "two"], ["one " "two"], ["one" " two"]
|
;; differently: ["one" " " "two"], ["one " "two"], ["one" " two"]
|
||||||
;; so this won't work for 100% of the situations. But it's good enough for now,
|
;; so this won't work for 100% of the situations. But it's good enough for now,
|
||||||
@ -116,7 +141,6 @@
|
|||||||
:else
|
:else
|
||||||
true))
|
true))
|
||||||
|
|
||||||
|
|
||||||
(defn copy-text-keys
|
(defn copy-text-keys
|
||||||
"Given two equal content text structures, deep copy all the keys :text
|
"Given two equal content text structures, deep copy all the keys :text
|
||||||
from origin to destiny"
|
from origin to destiny"
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
(ns common-tests.logic.token-apply-test
|
(ns common-tests.logic.token-apply-test
|
||||||
(:require
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
[app.common.files.changes-builder :as pcb]
|
[app.common.files.changes-builder :as pcb]
|
||||||
[app.common.logic.shapes :as cls]
|
[app.common.logic.shapes :as cls]
|
||||||
[app.common.test-helpers.compositions :as tho]
|
[app.common.test-helpers.compositions :as tho]
|
||||||
@ -13,6 +14,7 @@
|
|||||||
[app.common.test-helpers.ids-map :as thi]
|
[app.common.test-helpers.ids-map :as thi]
|
||||||
[app.common.test-helpers.shapes :as ths]
|
[app.common.test-helpers.shapes :as ths]
|
||||||
[app.common.test-helpers.tokens :as tht]
|
[app.common.test-helpers.tokens :as tht]
|
||||||
|
[app.common.text :as txt]
|
||||||
[app.common.types.container :as ctn]
|
[app.common.types.container :as ctn]
|
||||||
[app.common.types.token :as cto]
|
[app.common.types.token :as cto]
|
||||||
[app.common.types.tokens-lib :as ctob]
|
[app.common.types.tokens-lib :as ctob]
|
||||||
@ -53,7 +55,8 @@
|
|||||||
(ctob/make-token :name "token-dimensions"
|
(ctob/make-token :name "token-dimensions"
|
||||||
:type :dimensions
|
:type :dimensions
|
||||||
:value 100))))
|
:value 100))))
|
||||||
(tho/add-frame :frame1)))
|
(tho/add-frame :frame1)
|
||||||
|
(tho/add-text :text1 "Hello World")))
|
||||||
|
|
||||||
(defn- apply-all-tokens
|
(defn- apply-all-tokens
|
||||||
[file]
|
[file]
|
||||||
@ -64,7 +67,8 @@
|
|||||||
(tht/apply-token-to-shape :frame1 "token-stroke-width" [:stroke-width] [:stroke-width] 2)
|
(tht/apply-token-to-shape :frame1 "token-stroke-width" [:stroke-width] [:stroke-width] 2)
|
||||||
(tht/apply-token-to-shape :frame1 "token-color" [:stroke-color] [:stroke-color] "#00ff00")
|
(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-color" [:fill] [:fill] "#00ff00")
|
||||||
(tht/apply-token-to-shape :frame1 "token-dimensions" [:width :height] [:width :height] 100)))
|
(tht/apply-token-to-shape :frame1 "token-dimensions" [:width :height] [:width :height] 100)
|
||||||
|
(tht/apply-token-to-shape :text1 "token-color" [:fill] [:fill] "#00ff00")))
|
||||||
|
|
||||||
(t/deftest apply-tokens-to-shape
|
(t/deftest apply-tokens-to-shape
|
||||||
(let [;; ==== Setup
|
(let [;; ==== Setup
|
||||||
@ -171,6 +175,7 @@
|
|||||||
(apply-all-tokens))
|
(apply-all-tokens))
|
||||||
page (thf/current-page file)
|
page (thf/current-page file)
|
||||||
frame1 (ths/get-shape file :frame1)
|
frame1 (ths/get-shape file :frame1)
|
||||||
|
text1 (ths/get-shape file :text1)
|
||||||
|
|
||||||
;; ==== Action
|
;; ==== Action
|
||||||
changes (-> (-> (pcb/empty-changes nil)
|
changes (-> (-> (pcb/empty-changes nil)
|
||||||
@ -190,13 +195,25 @@
|
|||||||
(ctn/set-shape-attr :width 0)
|
(ctn/set-shape-attr :width 0)
|
||||||
(ctn/set-shape-attr :height 0)))
|
(ctn/set-shape-attr :height 0)))
|
||||||
(:objects page)
|
(:objects page)
|
||||||
|
{})
|
||||||
|
(cls/generate-update-shapes [(:id text1)]
|
||||||
|
(fn [shape]
|
||||||
|
(txt/update-text-content
|
||||||
|
shape
|
||||||
|
txt/is-content-node?
|
||||||
|
d/txt-merge
|
||||||
|
{:fills (ths/sample-fills-color :fill-color "#fabada")}))
|
||||||
|
(:objects page)
|
||||||
{}))
|
{}))
|
||||||
|
|
||||||
file' (thf/apply-changes file changes)
|
file' (thf/apply-changes file changes)
|
||||||
|
|
||||||
;; ==== Get
|
;; ==== Get
|
||||||
frame1' (ths/get-shape file' :frame1)
|
frame1' (ths/get-shape file' :frame1)
|
||||||
applied-tokens' (:applied-tokens frame1')]
|
text1' (ths/get-shape file' :text1)
|
||||||
|
applied-tokens-frame' (:applied-tokens frame1')
|
||||||
|
applied-tokens-text' (:applied-tokens text1')]
|
||||||
|
|
||||||
;; ==== Check
|
;; ==== Check
|
||||||
(t/is (= (count applied-tokens') 0))))
|
(t/is (= (count applied-tokens-frame') 0))
|
||||||
|
(t/is (= (count applied-tokens-text') 0))))
|
||||||
@ -29,11 +29,12 @@
|
|||||||
#(conj % line)))
|
#(conj % line)))
|
||||||
|
|
||||||
(t/deftest test-get-diff-type
|
(t/deftest test-get-diff-type
|
||||||
(let [diff-text (cttx/get-diff-type content-base content-changed-text)
|
(let [diff-text (cttx/get-diff-type content-base content-changed-text)
|
||||||
diff-attr (cttx/get-diff-type content-base content-changed-attr)
|
diff-attr (cttx/get-diff-type content-base content-changed-attr)
|
||||||
diff-both (cttx/get-diff-type content-base content-changed-both)
|
diff-both (cttx/get-diff-type content-base content-changed-both)
|
||||||
diff-structure (cttx/get-diff-type content-base content-changed-structure)
|
diff-structure (cttx/get-diff-type content-base content-changed-structure)
|
||||||
diff-structure-same-attrs (cttx/get-diff-type content-base content-changed-structure-same-attrs)]
|
diff-structure-same-attrs (cttx/get-diff-type content-base content-changed-structure-same-attrs)]
|
||||||
|
|
||||||
(t/is (= #{:text-content-text} diff-text))
|
(t/is (= #{:text-content-text} diff-text))
|
||||||
(t/is (= #{:text-content-attribute} diff-attr))
|
(t/is (= #{:text-content-attribute} diff-attr))
|
||||||
(t/is (= #{:text-content-text :text-content-attribute} diff-both))
|
(t/is (= #{:text-content-text :text-content-attribute} diff-both))
|
||||||
@ -41,6 +42,20 @@
|
|||||||
(t/is (= #{:text-content-structure :text-content-structure-same-attrs} diff-structure-same-attrs))))
|
(t/is (= #{:text-content-structure :text-content-structure-same-attrs} diff-structure-same-attrs))))
|
||||||
|
|
||||||
|
|
||||||
|
(t/deftest test-get-diff-attrs
|
||||||
|
(let [attrs-text (cttx/get-diff-attrs content-base content-changed-text)
|
||||||
|
attrs-attr (cttx/get-diff-attrs content-base content-changed-attr)
|
||||||
|
attrs-both (cttx/get-diff-attrs content-base content-changed-both)
|
||||||
|
attrs-structure (cttx/get-diff-attrs content-base content-changed-structure)
|
||||||
|
attrs-structure-same-attrs (cttx/get-diff-attrs content-base content-changed-structure-same-attrs)]
|
||||||
|
|
||||||
|
(t/is (= #{} attrs-text))
|
||||||
|
(t/is (= #{:font-size} attrs-attr))
|
||||||
|
(t/is (= #{:font-size} attrs-both))
|
||||||
|
(t/is (= #{} attrs-structure))
|
||||||
|
(t/is (= #{} attrs-structure-same-attrs))))
|
||||||
|
|
||||||
|
|
||||||
(t/deftest test-equal-structure
|
(t/deftest test-equal-structure
|
||||||
(t/is (true? (cttx/equal-structure? content-base content-changed-text)))
|
(t/is (true? (cttx/equal-structure? content-base content-changed-text)))
|
||||||
(t/is (true? (cttx/equal-structure? content-base content-changed-attr)))
|
(t/is (true? (cttx/equal-structure? content-base content-changed-attr)))
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user