mirror of
https://github.com/penpot/penpot.git
synced 2026-08-22 12:49:26 +00:00
⚡ Improve shape attrs parsing performance (#11259)
* ⚡ Memoize shape-attr->token-attrs and hoist per-type attrs in get-attrs* * ⚡ Skip redundant token merges for token-less shapes in get-attrs* * ⚡ Freeze group descendant attrs in design panel during transforms
This commit is contained in:
parent
8da13b5fa1
commit
54aaebee1e
@ -423,11 +423,8 @@
|
||||
:stroke-width :strokes
|
||||
token-attr))
|
||||
|
||||
(defn shape-attr->token-attrs
|
||||
"Returns the token-attr affected when a given attribute in a shape is changed.
|
||||
The sub-attr is for attributes that may have multiple values, like strokes
|
||||
(may be width or color) and layout padding & margin (may have 4 edges)."
|
||||
([shape-attr] (shape-attr->token-attrs shape-attr nil))
|
||||
(defn- shape-attr->token-attrs*
|
||||
([shape-attr] (shape-attr->token-attrs* shape-attr nil))
|
||||
([shape-attr changed-sub-attr]
|
||||
(cond
|
||||
(= :fills shape-attr)
|
||||
@ -468,6 +465,20 @@
|
||||
(number-keys shape-attr) #{shape-attr}
|
||||
(axis-keys shape-attr) #{shape-attr})))
|
||||
|
||||
(def ^:private shape-attr->token-attrs-1
|
||||
(memoize shape-attr->token-attrs*))
|
||||
|
||||
(defn shape-attr->token-attrs
|
||||
"Returns the token-attr affected when a given attribute in a shape is changed.
|
||||
The sub-attr is for attributes that may have multiple values, like strokes
|
||||
(may be width or color) and layout padding & margin (may have 4 edges)."
|
||||
([shape-attr]
|
||||
(shape-attr->token-attrs-1 shape-attr))
|
||||
([shape-attr changed-sub-attr]
|
||||
(if (nil? changed-sub-attr)
|
||||
(shape-attr->token-attrs-1 shape-attr)
|
||||
(shape-attr->token-attrs* shape-attr changed-sub-attr))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; HELPERS for token attributes by shape type
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
@ -93,20 +93,38 @@
|
||||
[constraint-ids constraint-values]
|
||||
(get-attrs shapes objects :constraint)
|
||||
|
||||
[fill-ids fill-values fill-tokens]
|
||||
(get-attrs shapes objects :fill)
|
||||
|
||||
[shadow-ids]
|
||||
(get-attrs shapes objects :shadow)
|
||||
|
||||
[blur-ids blur-values]
|
||||
(get-attrs shapes objects :blur)
|
||||
|
||||
transform
|
||||
(mf/deref refs/current-transform)
|
||||
|
||||
;; A transform cannot change the descendants read here.
|
||||
descendant-attrs-ref
|
||||
(mf/use-ref nil)
|
||||
|
||||
descendant-attrs
|
||||
(let [cached (mf/ref-val descendant-attrs-ref)]
|
||||
(if (and (some? transform) (some? cached))
|
||||
cached
|
||||
(let [attrs {:fill (get-attrs shapes objects :fill)
|
||||
:stroke (get-attrs shapes objects :stroke)
|
||||
:text (get-attrs shapes objects :text)
|
||||
:colors (vals objects)}]
|
||||
(mf/set-ref-val! descendant-attrs-ref attrs)
|
||||
attrs)))
|
||||
|
||||
[fill-ids fill-values fill-tokens]
|
||||
(get descendant-attrs :fill)
|
||||
|
||||
[stroke-ids stroke-values stroke-tokens]
|
||||
(get-attrs shapes objects :stroke)
|
||||
(get descendant-attrs :stroke)
|
||||
|
||||
[text-ids text-values text-tokens]
|
||||
(get-attrs shapes objects :text)
|
||||
(get descendant-attrs :text)
|
||||
|
||||
[layout-item-ids layout-item-values]
|
||||
(get-attrs shapes objects :layout-item)]
|
||||
@ -164,7 +182,7 @@
|
||||
|
||||
[:> color-selection-menu*
|
||||
{:type type
|
||||
:shapes (vals objects)
|
||||
:shapes (get descendant-attrs :colors)
|
||||
:file-id file-id
|
||||
:libraries libraries}]
|
||||
|
||||
|
||||
@ -268,6 +268,21 @@
|
||||
applies (some of them ignore some attributes)"
|
||||
[shapes objects attr-group]
|
||||
(let [attrs (group->attrs attr-group)
|
||||
|
||||
type->editable-attrs
|
||||
(memoize (fn [type]
|
||||
(if-let [editable? (get editable-attrs type)]
|
||||
(filterv editable? attrs)
|
||||
[])))
|
||||
|
||||
type->nil-values
|
||||
(memoize (fn [type] (into {} (map (fn [attr] [attr nil])) (type->editable-attrs type))))
|
||||
|
||||
type->token-attrs
|
||||
(memoize (fn [type]
|
||||
(into [] (comp (mapcat tt/shape-attr->token-attrs) (distinct))
|
||||
(type->editable-attrs type))))
|
||||
|
||||
merge-attrs
|
||||
(fn [v1 v2]
|
||||
(cond
|
||||
@ -289,24 +304,31 @@
|
||||
(= existing new-val) acc
|
||||
:else (assoc acc t-attr :multiple))))
|
||||
|
||||
merge-shape-attr
|
||||
(fn [acc applied-tokens shape-attr]
|
||||
"Merges all token attributes derived from a single shape attribute
|
||||
into the accumulator map using `merge-attr`."
|
||||
(let [token-attrs (tt/shape-attr->token-attrs shape-attr)]
|
||||
(reduce #(merge-attr %1 applied-tokens %2) acc token-attrs)))
|
||||
;; Merging an empty `applied-tokens` into an accumulator that a previous
|
||||
;; empty merge already produced is a fixed point, so long runs of
|
||||
;; token-less shapes of the same type only pay for the first one.
|
||||
stable-token-acc (volatile! nil)
|
||||
|
||||
merge-token-values
|
||||
(fn [acc shape-attrs applied-tokens]
|
||||
"Merges token values across all shape attributes.
|
||||
For each shape attribute, its corresponding token attributes are merged
|
||||
into the accumulator."
|
||||
(reduce #(merge-shape-attr %1 applied-tokens %2) acc shape-attrs))
|
||||
(fn [acc token-attrs applied-tokens]
|
||||
"Merges token values across all token attributes derived from the shape's
|
||||
editable attributes."
|
||||
(let [no-tokens? (empty? applied-tokens)
|
||||
stable (deref stable-token-acc)]
|
||||
(if (and no-tokens?
|
||||
(some? stable)
|
||||
(identical? (nth stable 0) token-attrs)
|
||||
(identical? (nth stable 1) acc))
|
||||
acc
|
||||
(let [result (reduce #(merge-attr %1 applied-tokens %2) acc token-attrs)]
|
||||
(when no-tokens?
|
||||
(vreset! stable-token-acc [token-attrs result]))
|
||||
result))))
|
||||
|
||||
extract-attrs
|
||||
(fn [[ids values token-acc] {:keys [id type applied-tokens] :as shape}]
|
||||
(let [read-mode (get-in type->read-mode [type attr-group])
|
||||
editable-attrs (filter (get editable-attrs (:type shape)) attrs)]
|
||||
editable-attrs (type->editable-attrs type)]
|
||||
(case read-mode
|
||||
:ignore
|
||||
[ids values]
|
||||
@ -315,14 +337,14 @@
|
||||
(let [;; Get the editable attrs from the shape, ensuring that all attributes
|
||||
;; are present, with value nil if they are not present in the shape.
|
||||
shape-values (merge
|
||||
(into {} (map #(vector % nil)) editable-attrs)
|
||||
(type->nil-values type)
|
||||
(cond
|
||||
(= attr-group :measure) (select-measure-keys shape)
|
||||
:else (select-keys shape editable-attrs)))
|
||||
shape-values (cond-> shape-values
|
||||
(= attr-group :layer)
|
||||
(update :hidden #(if (nil? %) false %)))
|
||||
new-token-acc (merge-token-values token-acc editable-attrs applied-tokens)]
|
||||
new-token-acc (merge-token-values token-acc (type->token-attrs type) applied-tokens)]
|
||||
[(conj ids id)
|
||||
(merge-attrs values shape-values)
|
||||
new-token-acc])
|
||||
@ -338,7 +360,7 @@
|
||||
(merge-attrs shape-attrs)
|
||||
(merge-attrs content-attrs))
|
||||
|
||||
new-token-acc (merge-token-values token-acc editable-attrs applied-tokens)]
|
||||
new-token-acc (merge-token-values token-acc (type->token-attrs type) applied-tokens)]
|
||||
[(conj ids id)
|
||||
new-values
|
||||
new-token-acc])
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user