🎉 Separate the content of the text of the rest of properties on variants

This commit is contained in:
Pablo Alba 2025-06-11 11:22:43 +02:00 committed by GitHub
parent 9761cba337
commit 925b6c02d6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 150 additions and 90 deletions

View file

@ -1665,45 +1665,26 @@
:shapes all-parents})]))))
(defn- text-partial-change-value
[touched-shape untouched-shape touched]
(cond
(touched :text-content-structure-same-attrs)
;; Keep the touched-shape structure and texts, update its attrs to make them like the untouched-shape
(cttx/copy-attrs-keys touched-shape (cttx/get-first-paragraph-text-attrs untouched-shape))
(touched :text-content-text)
;; Keep the texts touched in touched-shape copy the texts from dest over the attrs of untouched-shape
(cttx/copy-text-keys touched-shape untouched-shape)
(touched :text-content-attribute)
;; Keep the attrs touched in touched-shape copy the texts from untouched-shape over the attrs of touched-shape
(cttx/copy-text-keys untouched-shape touched-shape)))
(defn- add-update-attr-operations
[attr dest-shape origin-shape roperations uoperations touched is-text-partial-change?]
(let [orig-value (get origin-shape attr)
dest-value (get dest-shape attr)
;; position-data is a special case because can be affected by :geometry-group and :content-group
;; so, if the position-data changes but the geometry is touched we need to reset the position-data
;; so it's calculated again
reset-pos-data?
(and (cfh/text-shape? origin-shape)
(= attr :position-data)
(not= orig-value dest-value)
(touched :geometry-group))
;; We want to split the changes on the text itself and on its properties
text-value
(when is-text-partial-change?
(cond
(touched :text-content-structure-same-attrs)
;; Keep the dest structure and texts, update its attrs to make them like the origin
(cttx/copy-attrs-keys dest-value (cttx/get-first-paragraph-text-attrs orig-value))
(touched :text-content-text)
;; Keep the texts touched in dest: copy the texts from dest over the attrs of origin
(cttx/copy-text-keys dest-value orig-value)
(touched :text-content-attribute)
;; Keep the attrs touched in dest: copy the texts from origin over the attrs of dest
(cttx/copy-text-keys orig-value dest-value)))
val (cond
;; If position data changes and the geometry group is touched
;; we need to put to nil so we can regenerate it
reset-pos-data? nil
is-text-partial-change? text-value
:else orig-value)
roperation {:type :set
[attr dest-shape roperations uoperations attr-val]
(let [roperation {:type :set
:attr attr
:val val
:val attr-val
:ignore-touched true}
uoperation {:type :set
:attr attr
@ -1714,13 +1695,13 @@
(defn- is-text-partial-change?
"Check if the attr update is a text partial change"
[origin-shape dest-shape attr touched]
(let [partial-text-keys [:text-content-attribute :text-content-text]
active-keys (filter touched partial-text-keys)
orig-content (get origin-shape attr)
orig-attrs (cttx/get-first-paragraph-text-attrs orig-content)
equal-orig-attrs? (cttx/equal-attrs? orig-content orig-attrs)]
[untouched-shape touched-shape]
(let [touched (get touched-shape :touched #{})
partial-text-keys [:text-content-attribute :text-content-text]
active-keys (filter touched partial-text-keys)
untouched-content (:content untouched-shape)
untouched-attrs (cttx/get-first-paragraph-text-attrs untouched-content)
eq-untouched-attrs? (cttx/equal-attrs? untouched-content untouched-attrs)]
(and
(or
;; One and only one of the keys is pressent
@ -1731,12 +1712,12 @@
(or
;; Both has the same structure
(cttx/equal-structure? (:content origin-shape) (:content dest-shape))
(cttx/equal-structure? untouched-content (:content touched-shape))
;; The origin and destiny have different structures, but each have the same attrs
;; for all the items on its content tree
(and
equal-orig-attrs?
eq-untouched-attrs?
(touched :text-content-structure-same-attrs))))))
(defn- update-attrs
@ -1782,73 +1763,152 @@
(generate-update-tokens container dest-shape origin-shape touched omit-touched?))
(let [attr-group (get ctk/sync-attrs attr)
;; position-data is a special case because can be affected by
;; :geometry-group and :content-group so, if the position-data
;; changes but the geometry is touched we need to reset the position-data
;; so it's calculated again
reset-pos-data? (and (cfh/text-shape? origin-shape)
(= attr :position-data)
(not= (:position-data origin-shape) (:position-data dest-shape))
(touched :geometry-group))
;; On texts, when we want to omit the touched attrs, both text (the actual letters)
;; and attrs (bold, font, etc) are in the same attr :content.
;; If only one of them is touched, we want to adress this case and
;; only update the untouched one
text-partial-change? (when (and
omit-touched?
(= :text (:type origin-shape))
(= :content attr)
(touched attr-group))
(is-text-partial-change? origin-shape dest-shape attr touched))
text-partial-change?
(when (and
omit-touched?
(cfh/text-shape? origin-shape)
(= :content attr)
(touched attr-group))
(is-text-partial-change? origin-shape dest-shape))
skip-operations? (or (= (get origin-shape attr) (get dest-shape attr))
(and (touched attr-group)
omit-touched?
;; When it is a text-partial-change, we should generate operations
;; even when omit-touched? is true, but updating only the text or
;; the attributes, omiting the other part
(not text-partial-change?)))
skip-operations?
(or (= (get origin-shape attr) (get dest-shape attr))
(and (touched attr-group)
omit-touched?
;; When it is a text-partial-change, we should generate operations
;; even when omit-touched? is true, but updating only the text or
;; the attributes, omiting the other part
(not text-partial-change?)))
attr-val (when-not skip-operations?
(cond
;; If position data changes and the geometry group is touched
;; we need to put to nil so we can regenerate it
reset-pos-data?
nil
text-partial-change?
(text-partial-change-value (:content dest-shape)
(:content origin-shape)
touched)
:else
(get origin-shape attr)))
[roperations' uoperations']
(if skip-operations?
[roperations uoperations]
(add-update-attr-operations attr dest-shape origin-shape roperations uoperations touched text-partial-change?))]
(add-update-attr-operations attr dest-shape roperations uoperations attr-val))]
(recur (next attrs)
roperations'
uoperations')))))))
(defn update-attrs-on-switch
"Copy attributes that have changed in the origin shape to the dest shape. Used on variants switch"
[changes dest-shape origin-shape dest-root origin-root origin-ref-shape container]
"Copy attributes that have changed in the shape previous to the switch
to the current shape (post switch). Used only on variants switch"
;; NOTE: This function have similitudes but is very different to
;; update-attrs:
;; In components (update-attrs), the source shape is "clean", and the destination
;; shape may have touched elements that shouldn't be overwritten.
;; In variants (update-attrs-on-switch), the destination shape is "clean",
;; and it's the source shape that may have touched elements, and we only want
;; to copy those touched elements.
[changes current-shape previous-shape current-root prev-root origin-ref-shape container]
(let [;; We need to sync only the position relative to the origin of the component.
;; (see update-attrs for a full explanation)
origin-shape (reposition-shape origin-shape origin-root dest-root)
touched (get dest-shape :touched #{})
touched-origin (get origin-shape :touched #{})]
previous-shape (reposition-shape previous-shape prev-root current-root)
touched (get previous-shape :touched #{})]
(loop [attrs updatable-attrs
roperations [{:type :set-touched :touched (:touched origin-shape)}]
uoperations (list {:type :set-touched :touched (:touched dest-shape)})]
roperations [{:type :set-touched :touched (:touched previous-shape)}]
uoperations (list {:type :set-touched :touched (:touched current-shape)})]
(if-let [attr (first attrs)]
(let [attr-group (get ctk/sync-attrs attr)
skip-operations?
(or
;; If the attribute is not valid for the destiny, don't copy it
(not (cts/is-allowed-attr? attr (:type current-shape)))
;; If the values are already equal, don't copy them
(= (get previous-shape attr) (get current-shape attr))
;; If the referenced shape on the original component doesn't
;; have the same value, don't copy it
;; Exceptions: :points :selrect and :content can be different
(and
(not (contains? #{:points :selrect :content} attr))
(not= (get origin-ref-shape attr) (get current-shape attr)))
;; The :content attr cant't be copied to elements of different type
(and (= attr :content) (not= (:type previous-shape) (:type current-shape)))
;; If the attr is not touched, don't copy it
(not (touched attr-group)))
;; On texts, both text (the actual letters)
;; and attrs (bold, font, etc) are in the same attr :content.
;; If only one of them is touched, we want to adress this case and
;; only update the untouched one
text-partial-change?
(when (and
(not skip-operations?)
(cfh/text-shape? current-shape)
(cfh/text-shape? previous-shape)
(= :content attr)
(touched attr-group))
(is-text-partial-change? current-shape previous-shape))
;; position-data is a special case because can be affected by :geometry-group and :content-group
;; so, if the position-data changes but the geometry is touched we need to reset the position-data
;; so it's calculated again
reset-pos-data? (and
(not skip-operations?)
(cfh/text-shape? previous-shape)
(= attr :position-data)
(not= (:position-data previous-shape) (:position-data current-shape))
(touched :geometry-group))
attr-val (when-not skip-operations?
(cond
;; If position data changes and the geometry group is touched
;; we need to put to nil so we can regenerate it
reset-pos-data?
nil
text-partial-change?
(text-partial-change-value (:content previous-shape)
(:content current-shape)
touched)
:else
(get previous-shape attr)))
[roperations' uoperations']
(if (or
;; If the attribute is not valid for the destiny, don't copy it
(not (cts/is-allowed-attr? attr (:type dest-shape)))
;; If the values are already equal, don't copy it
(= (get origin-shape attr) (get dest-shape attr))
;; If the referenced shape on the original component doesn't have the same value, don't copy it
;; Exceptions: :points :selrect and :content can be different
(and
(not (contains? #{:points :selrect :content} attr))
(not= (get origin-ref-shape attr) (get dest-shape attr)))
;; The :content attr cant't be copied to elements of different type
(and (= attr :content) (not= (:type origin-shape) (:type dest-shape)))
;; If the attr is not touched in the origin shape, don't copy it
(not (touched-origin attr-group)))
(if skip-operations?
[roperations uoperations]
(add-update-attr-operations attr dest-shape origin-shape roperations uoperations touched false))]
(add-update-attr-operations attr current-shape roperations uoperations attr-val))]
(recur (next attrs)
roperations'
uoperations'))
(cond-> changes
(> (count roperations) 1)
(add-update-attr-changes dest-shape container roperations uoperations)
(add-update-attr-changes current-shape container roperations uoperations)
:always
(generate-update-tokens container dest-shape origin-shape touched false))))))
(generate-update-tokens container current-shape previous-shape touched false))))))
(defn- propagate-attrs
"Helper that puts the origin attributes (attrs) into dest but only if

View file

@ -75,16 +75,16 @@
objects
(:id new-shape))
new-shapes-map (into {} (map (juxt :shape-path identity) new-shapes-w-path))
orig-touched (filter (comp seq :touched) orig-shapes-w-path)
orig-touched (filter (comp seq :touched) orig-shapes-w-path)
container (ctn/make-container page :page)]
(reduce
(fn [changes touched-shape]
(let [related-shape (get new-shapes-map (:shape-path touched-shape))
orig-ref-shape (ctf/find-ref-shape nil container libraries touched-shape)]
(if related-shape
(fn [changes previous-shape]
(let [current-shape (get new-shapes-map (:shape-path previous-shape))
orig-ref-shape (ctf/find-ref-shape nil container libraries previous-shape)]
(if current-shape
(cll/update-attrs-on-switch
changes related-shape touched-shape new-shape original-shape orig-ref-shape container)
changes current-shape previous-shape new-shape original-shape orig-ref-shape container)
changes)))
changes
orig-touched)))