Improve svg shapes attrs handling

And collaterally it improves performance since now the attrs
processing is done in the import and not in the render.
This commit is contained in:
Andrey Antukh
2023-09-14 16:24:24 +02:00
committed by Alonso Torres
parent 807f475a2d
commit 4f23852bca
8 changed files with 292 additions and 225 deletions

View File

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

View File

@@ -18,6 +18,7 @@
[app.common.math :as mth]
[app.common.pages.changes :as cpc]
[app.common.pages.helpers :as cph]
[app.common.svg :as csvg]
[app.common.text :as txt]
[app.common.types.shape :as cts]
[app.common.uuid :as uuid]
@@ -587,7 +588,23 @@
(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))))
(defmethod migrate 32
[data]
(letfn [(update-object [object]
(as-> object object
(if (contains? object :svg-attrs)
(update object :svg-attrs csvg/attrs->props)
object)
(if (contains? object :svg-viewbox)
(update object :svg-viewbox grc/make-rect)
object)))
(update-container [container]
(update container :objects update-vals update-object))]
(-> data
(update :pages-index update-vals update-container)
(update :components update-vals update-container))))

View File

@@ -6,10 +6,9 @@
(ns app.common.svg
(:require
#?(:cljs ["./svg_optimizer.js" :as svgo])
#?(:cljs ["./svg_optimizer.js" :as svgo])
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.geom.matrix :as gmt]
[app.common.geom.point :as gpt]
[app.common.geom.shapes :as gsh]
@@ -548,34 +547,74 @@
[num-str]
(cond
(str/starts-with? num-str ".")
(str "0" num-str)
(dm/str "0" num-str)
(str/starts-with? num-str "-.")
(str "-0" (subs num-str 1))
(dm/str "-0" (subs num-str 1))
:else
num-str))
(defn format-styles
"Transforms attributes to their react equivalent"
[attrs]
(letfn [(format-styles [style-str]
(if (string? style-str)
(->> (str/split style-str ";")
(map str/trim)
(map #(str/split % ":"))
(group-by first)
(map (fn [[key val]]
(vector (keyword key) (second (first val)))))
(into {}))
style-str))]
(defn- camelize
[s]
(when (string? s)
#?(:cljs (js* "~{}.replace(\":\", \"-\").replace(/-./g, x=>x[1].toUpperCase())", s)
:clj (str/camel s))))
(cond-> attrs
(contains? attrs :style)
(update :style format-styles))))
(defn parse-style
[style]
(reduce (fn [res item]
(let [[k v] (-> (str/trim item) (str/split ":" 2))
k (keyword k)]
(if (contains? res k)
res
(assoc res (keyword k) v))))
{}
(str/split style ";")))
;; FIXME: rename to `format-style` or directly use parse-style on code...
(defn format-styles
"Transform string based styles found on attrs map to key-value map."
[attrs]
(if (contains? attrs :style)
(update attrs :style
(fn [style]
(if (string? style)
(parse-style style)
style)))
attrs))
(defn attrs->props
"Transforms and cleans svg attributes to react compatible props"
([attrs]
(attrs->props attrs true))
([attrs whitelist?]
(reduce-kv (fn [res k v]
(if (or (not whitelist?)
(contains? svg-attr-list k)
(contains? svg-present-list k))
(cond
(= k :class)
(assoc res :className val)
(= k :style)
(let [v (if (string? v) (parse-style v) v)]
(assoc res k (attrs->props v false)))
:else
(let [k (if (contains? non-react-props k)
k
(-> k d/name camelize keyword))]
(assoc res k v)))
res))
{}
attrs)))
(defn clean-attrs
"Transforms attributes to their react equivalent"
"Transforms attributes to their react equivalent
DEPRECATED: replaced by attrs->props"
([attrs]
(clean-attrs attrs true))
@@ -590,8 +629,7 @@
#?(:cljs (js* "~{}.replace(\":\", \"-\").replace(/-./g, x=>x[1].toUpperCase())", s)
:clj (str/camel s))))
(transform-att [key]
(transform-key [key]
(if (contains? non-react-props key)
key
(-> (d/name key)
@@ -604,17 +642,26 @@
(map #(str/split % ":"))
(group-by first)
(map (fn [[key val]]
[(transform-att key)
[(transform-key key)
(second (first val))]))
(into {})))
(clean-att [[att val]]
(let [att (keyword att)]
(clean-key [[key val]]
(let [key (keyword key)]
(cond
(= att :class) [:className val]
(and (= att :style) (string? val)) [att (format-styles val)]
(and (= att :style) (map? val)) [att (clean-attrs val false)]
:else [(transform-att att) val])))]
(= key :class)
[:className val]
(and (= key :style)
(string? val))
[key (format-styles val)]
(and (= key :style)
(map? val))
[key (clean-attrs val false)]
:else
[(transform-key key) val])))]
;; Removed this warning because slows a lot rendering with big svgs
#_(let [filtered-props (->> attrs (remove known-property?) (map first))]
@@ -623,7 +670,7 @@
(into {}
(comp (filter known-property?)
(map clean-att))
(map clean-key))
attrs))))
(defn update-attr-ids
@@ -649,16 +696,17 @@
(defn replace-attrs-ids
"Replaces the ids inside a property"
[attrs ids-mapping]
(if (and ids-mapping (seq ids-mapping))
(update-attr-ids attrs (fn [id] (get ids-mapping id id)))
;; Ids-mapping is null
attrs))
(if (empty? ids-mapping)
attrs
(update-attr-ids attrs (fn [id] (get ids-mapping id id)))))
(defn generate-id-mapping [content]
(defn generate-id-mapping
[content]
(letfn [(visit-node [result node]
(let [element-id (get-in node [:attrs :id])
result (cond-> result
element-id (assoc element-id (str (uuid/next))))]
(let [element-id (dm/get-in node [:attrs :id])
result (if (some? element-id)
(assoc result element-id (dm/str (uuid/next)))
result)]
(reduce visit-node result (:content node))))]
(visit-node {} content)))