mirror of
https://github.com/penpot/penpot.git
synced 2025-03-20 19:51:23 -05:00
✨ Performance improvements
This commit is contained in:
parent
ea03477e8e
commit
6a077c967a
6 changed files with 289 additions and 242 deletions
|
@ -9,71 +9,68 @@
|
|||
|
||||
(ns app.common.attrs)
|
||||
|
||||
;; Extract some attributes of a list of shapes.
|
||||
;; For each attribute, if the value is the same in all shapes,
|
||||
;; wll take this value. If there is any shape that is different,
|
||||
;; the value of the attribute will be the keyword :multiple.
|
||||
;;
|
||||
;; If some shape has the value nil in any attribute, it's
|
||||
;; considered a different value. If the shape does not contain
|
||||
;; the attribute, it's ignored in the final result.
|
||||
;;
|
||||
;; Example:
|
||||
;; (def shapes [{:stroke-color "#ff0000"
|
||||
;; :stroke-width 3
|
||||
;; :fill-color "#0000ff"
|
||||
;; :x 1000 :y 2000 :rx nil}
|
||||
;; {:stroke-width "#ff0000"
|
||||
;; :stroke-width 5
|
||||
;; :x 1500 :y 2000}])
|
||||
;;
|
||||
;; (get-attrs-multi shapes [:stroke-color
|
||||
;; :stroke-width
|
||||
;; :fill-color
|
||||
;; :rx
|
||||
;; :ry])
|
||||
;; >>> {:stroke-color "#ff0000"
|
||||
;; :stroke-width :multiple
|
||||
;; :fill-color "#0000ff"
|
||||
;; :rx nil
|
||||
;; :ry nil}
|
||||
;;
|
||||
|
||||
(defn get-attrs-multi
|
||||
([shapes attrs] (get-attrs-multi shapes attrs = identity))
|
||||
([shapes attrs eq-fn sel-fn]
|
||||
;; Extract some attributes of a list of shapes.
|
||||
;; For each attribute, if the value is the same in all shapes,
|
||||
;; wll take this value. If there is any shape that is different,
|
||||
;; the value of the attribute will be the keyword :multiple.
|
||||
;;
|
||||
;; If some shape has the value nil in any attribute, it's
|
||||
;; considered a different value. If the shape does not contain
|
||||
;; the attribute, it's ignored in the final result.
|
||||
;;
|
||||
;; Example:
|
||||
;; (def shapes [{:stroke-color "#ff0000"
|
||||
;; :stroke-width 3
|
||||
;; :fill-color "#0000ff"
|
||||
;; :x 1000 :y 2000 :rx nil}
|
||||
;; {:stroke-width "#ff0000"
|
||||
;; :stroke-width 5
|
||||
;; :x 1500 :y 2000}])
|
||||
;;
|
||||
;; (get-attrs-multi shapes [:stroke-color
|
||||
;; :stroke-width
|
||||
;; :fill-color
|
||||
;; :rx
|
||||
;; :ry])
|
||||
;; >>> {:stroke-color "#ff0000"
|
||||
;; :stroke-width :multiple
|
||||
;; :fill-color "#0000ff"
|
||||
;; :rx nil
|
||||
;; :ry nil}
|
||||
;;
|
||||
(let [defined-shapes (filter some? shapes)
|
||||
([objs attrs]
|
||||
(get-attrs-multi objs attrs = identity))
|
||||
|
||||
combine-value (fn [v1 v2]
|
||||
(cond
|
||||
(and (= v1 :undefined) (= v2 :undefined)) :undefined
|
||||
(= v1 :undefined) (if (= v2 :multiple) :multiple (sel-fn v2))
|
||||
(= v2 :undefined) (if (= v1 :multiple) :multiple (sel-fn v1))
|
||||
(or (= v1 :multiple) (= v2 :multiple)) :multiple
|
||||
(eq-fn v1 v2) (sel-fn v1)
|
||||
:else :multiple))
|
||||
([objs attrs eqfn sel]
|
||||
|
||||
combine-values (fn [attrs shape values]
|
||||
(map #(combine-value (get shape % :undefined)
|
||||
(get values % :undefined)) attrs))
|
||||
(loop [attr (first attrs)
|
||||
attrs (rest attrs)
|
||||
result (transient {})]
|
||||
|
||||
select-attrs (fn [shape attrs]
|
||||
(zipmap attrs (map #(get shape % :undefined) attrs)))
|
||||
(if attr
|
||||
(let [value
|
||||
(loop [curr (first objs)
|
||||
objs (rest objs)
|
||||
value ::undefined]
|
||||
|
||||
reducer (fn [result shape]
|
||||
(zipmap attrs (combine-values attrs shape result)))
|
||||
(if (and curr (not= value :multiple))
|
||||
;;
|
||||
(let [new-val (get curr attr ::undefined)
|
||||
value (cond
|
||||
(= new-val ::undefined) value
|
||||
(= value ::undefined) (sel new-val)
|
||||
(eqfn new-val value) value
|
||||
:else :multiple)]
|
||||
(recur (first objs) (rest objs) value))
|
||||
;;
|
||||
value))]
|
||||
(recur (first attrs)
|
||||
(rest attrs)
|
||||
(cond-> result
|
||||
(not= value ::undefined)
|
||||
(assoc! attr value))))
|
||||
|
||||
combined (reduce reducer
|
||||
(select-attrs (first defined-shapes) attrs)
|
||||
(rest defined-shapes))
|
||||
(persistent! result)))))
|
||||
|
||||
cleanup-value (fn [value]
|
||||
(if (= value :undefined) nil value))
|
||||
|
||||
cleanup (fn [result]
|
||||
(->> attrs
|
||||
(map #(get result %))
|
||||
(zipmap attrs)
|
||||
(filter #(not= (second %) :undefined))
|
||||
(into {})))]
|
||||
|
||||
(cleanup combined))))
|
||||
|
|
|
@ -124,9 +124,6 @@
|
|||
[shape {:keys [x y]}]
|
||||
(move shape (gpt/point (- x) (- y))))
|
||||
|
||||
(defn translate-from-frame
|
||||
[shape {:keys [x y]}]
|
||||
(move shape (gpt/point x y)))
|
||||
|
||||
;; --- Helpers
|
||||
|
||||
|
|
|
@ -18,64 +18,89 @@
|
|||
[app.util.object :as obj]
|
||||
[app.util.color :as uc]
|
||||
[app.main.ui.shapes.text.styles :as sts]
|
||||
[app.main.ui.shapes.text.embed :as ste]))
|
||||
[app.main.ui.shapes.text.embed :as ste]
|
||||
[app.util.perf :as perf]))
|
||||
|
||||
(mf/defc render-text
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [node (obj/get props "node")
|
||||
text (:text node)
|
||||
style (sts/generate-text-styles props)]
|
||||
[:span {:style style
|
||||
:className (when (:fill-color-gradient node) "gradient")}
|
||||
(if (= text "") "\u00A0" text)]))
|
||||
|
||||
(mf/defc render-root
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [node (obj/get props "node")
|
||||
embed-fonts? (obj/get props "embed-fonts?")
|
||||
children (obj/get props "children")
|
||||
style (sts/generate-root-styles props)]
|
||||
[:div.root.rich-text
|
||||
{:style style
|
||||
:xmlns "http://www.w3.org/1999/xhtml"}
|
||||
[:*
|
||||
[:style ".gradient { background: var(--text-color); -webkit-text-fill-color: transparent; -webkit-background-clip: text;"]
|
||||
(when embed-fonts?
|
||||
[ste/embed-fontfaces-style {:node node}])]
|
||||
children]))
|
||||
|
||||
(mf/defc render-paragraph-set
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [node (obj/get props "node")
|
||||
children (obj/get props "children")
|
||||
style (sts/generate-paragraph-set-styles props)]
|
||||
[:div.paragraph-set {:style style} children]))
|
||||
|
||||
(mf/defc render-paragraph
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [node (obj/get props "node")
|
||||
children (obj/get props "children")
|
||||
style (sts/generate-paragraph-styles props)]
|
||||
[:p.paragraph {:style style} children]))
|
||||
|
||||
;; -- Text nodes
|
||||
(mf/defc text-node
|
||||
[{:keys [node index shape] :as props}]
|
||||
(let [embed-resources? (mf/use-ctx muc/embed-ctx)
|
||||
{:keys [type text children]} node
|
||||
props #js {:shape shape}
|
||||
render-node
|
||||
(fn [index node]
|
||||
(mf/element text-node {:index index
|
||||
:node node
|
||||
:key index
|
||||
:shape shape}))]
|
||||
|
||||
(mf/defc render-node
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [node (obj/get props "node")
|
||||
index (obj/get props "index")
|
||||
{:keys [type text children]} node]
|
||||
(if (string? text)
|
||||
(let [style (sts/generate-text-styles (clj->js node) props)]
|
||||
[:span {:style style
|
||||
:className (when (:fill-color-gradient node) "gradient")}
|
||||
(if (= text "") "\u00A0" text)])
|
||||
[:> render-text props]
|
||||
|
||||
(let [children (map-indexed render-node children)]
|
||||
(case type
|
||||
"root"
|
||||
(let [style (sts/generate-root-styles (clj->js node) props)]
|
||||
[:div.root.rich-text
|
||||
{:key index
|
||||
:style style
|
||||
:xmlns "http://www.w3.org/1999/xhtml"}
|
||||
[:*
|
||||
[:style ".gradient { background: var(--text-color); -webkit-text-fill-color: transparent; -webkit-background-clip: text;"]
|
||||
(when embed-resources?
|
||||
[ste/embed-fontfaces-style {:node node}])]
|
||||
children])
|
||||
|
||||
"paragraph-set"
|
||||
(let [style (sts/generate-paragraph-set-styles (clj->js node) props)]
|
||||
[:div.paragraph-set {:key index :style style} children])
|
||||
|
||||
"paragraph"
|
||||
(let [style (sts/generate-paragraph-styles (clj->js node) props)]
|
||||
[:p.paragraph {:key index :style style} children])
|
||||
|
||||
nil)))))
|
||||
(let [component (case type
|
||||
"root" render-root
|
||||
"paragraph-set" render-paragraph-set
|
||||
"paragraph" render-paragraph
|
||||
nil)]
|
||||
(when component
|
||||
[:> component (obj/set! props "key" index)
|
||||
(for [[index child] (d/enumerate children)]
|
||||
(let [props (-> props
|
||||
(obj/set! "node" child)
|
||||
(obj/set! "index" index))]
|
||||
[:> render-node props]))])))))
|
||||
|
||||
(mf/defc text-content
|
||||
{::mf/wrap-props false
|
||||
::mf/wrap [mf/memo]}
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [root (obj/get props "content")
|
||||
shape (obj/get props "shape")]
|
||||
[:& text-node {:index 0
|
||||
:node root
|
||||
:shape shape}]))
|
||||
shape (obj/get props "shape")
|
||||
embed-fonts? (obj/get props "embed-fonts?")]
|
||||
[:& render-node {:index 0
|
||||
:node root
|
||||
:shape shape
|
||||
:embed-fonts? embed-fonts?}]))
|
||||
|
||||
(defn- retrieve-colors
|
||||
[shape]
|
||||
(let [colors (->> shape :content
|
||||
(let [colors (->> shape
|
||||
:content
|
||||
(tree-seq map? :children)
|
||||
(into #{} (comp (map :fill-color) (filter string?))))]
|
||||
(if (empty? colors)
|
||||
|
@ -87,8 +112,8 @@
|
|||
::mf/forward-ref true}
|
||||
[props ref]
|
||||
(let [shape (unchecked-get props "shape")
|
||||
selected? (unchecked-get props "selected?")
|
||||
grow-type (unchecked-get props "grow-type")
|
||||
embed-fonts? (mf/use-ctx muc/embed-ctx)
|
||||
{:keys [id x y width height content]} shape]
|
||||
[:foreignObject {:x x
|
||||
:y y
|
||||
|
@ -99,4 +124,5 @@
|
|||
:height (if (#{:auto-height :auto-width} grow-type) 10000 height)
|
||||
:ref ref}
|
||||
[:& text-content {:shape shape
|
||||
:content (:content shape)}]]))
|
||||
:content (:content shape)
|
||||
:embed-fonts? embed-fonts?}]]))
|
||||
|
|
|
@ -17,111 +17,115 @@
|
|||
[app.util.text :as ut]))
|
||||
|
||||
(defn generate-root-styles
|
||||
[data props]
|
||||
(let [valign (obj/get data "vertical-align" "top")
|
||||
talign (obj/get data "text-align" "flex-start")
|
||||
shape (obj/get props "shape")
|
||||
base #js {:height (or (:height shape) "100%")
|
||||
:width (or (:width shape) "100%")
|
||||
:display "flex"}]
|
||||
(cond-> base
|
||||
(= valign "top") (obj/set! "alignItems" "flex-start")
|
||||
(= valign "center") (obj/set! "alignItems" "center")
|
||||
(= valign "bottom") (obj/set! "alignItems" "flex-end")
|
||||
([props] (generate-root-styles (clj->js (obj/get props "node")) props))
|
||||
([data props]
|
||||
(let [valign (obj/get data "vertical-align" "top")
|
||||
talign (obj/get data "text-align" "flex-start")
|
||||
shape (obj/get props "shape")
|
||||
base #js {:height (or (:height shape) "100%")
|
||||
:width (or (:width shape) "100%")
|
||||
:display "flex"}]
|
||||
(cond-> base
|
||||
(= valign "top") (obj/set! "alignItems" "flex-start")
|
||||
(= valign "center") (obj/set! "alignItems" "center")
|
||||
(= valign "bottom") (obj/set! "alignItems" "flex-end")
|
||||
|
||||
(= talign "left") (obj/set! "justifyContent" "flex-start")
|
||||
(= talign "center") (obj/set! "justifyContent" "center")
|
||||
(= talign "right") (obj/set! "justifyContent" "flex-end")
|
||||
(= talign "justify") (obj/set! "justifyContent" "stretch"))))
|
||||
(= talign "left") (obj/set! "justifyContent" "flex-start")
|
||||
(= talign "center") (obj/set! "justifyContent" "center")
|
||||
(= talign "right") (obj/set! "justifyContent" "flex-end")
|
||||
(= talign "justify") (obj/set! "justifyContent" "stretch")))))
|
||||
|
||||
(defn generate-paragraph-set-styles
|
||||
[data props]
|
||||
;; The position absolute is used so the paragraph is "outside"
|
||||
;; the normal layout and can grow outside its parent
|
||||
;; We use this element to measure the size of the text
|
||||
(let [base #js {:display "inline-block"}]
|
||||
base))
|
||||
([props] (generate-paragraph-set-styles nil props))
|
||||
([data props]
|
||||
;; The position absolute is used so the paragraph is "outside"
|
||||
;; the normal layout and can grow outside its parent
|
||||
;; We use this element to measure the size of the text
|
||||
(let [base #js {:display "inline-block"}]
|
||||
base)))
|
||||
|
||||
(defn generate-paragraph-styles
|
||||
[data props]
|
||||
(let [shape (obj/get props "shape")
|
||||
grow-type (:grow-type shape)
|
||||
base #js {:fontSize "14px"
|
||||
:margin "inherit"
|
||||
:lineHeight "1.2"}
|
||||
lh (obj/get data "line-height")
|
||||
ta (obj/get data "text-align")]
|
||||
(cond-> base
|
||||
ta (obj/set! "textAlign" ta)
|
||||
lh (obj/set! "lineHeight" lh)
|
||||
(= grow-type :auto-width) (obj/set! "whiteSpace" "pre"))))
|
||||
([props] (generate-paragraph-styles (clj->js (obj/get props "node")) props))
|
||||
([data props]
|
||||
(let [shape (obj/get props "shape")
|
||||
grow-type (:grow-type shape)
|
||||
base #js {:fontSize "14px"
|
||||
:margin "inherit"
|
||||
:lineHeight "1.2"}
|
||||
lh (obj/get data "line-height")
|
||||
ta (obj/get data "text-align")]
|
||||
(cond-> base
|
||||
ta (obj/set! "textAlign" ta)
|
||||
lh (obj/set! "lineHeight" lh)
|
||||
(= grow-type :auto-width) (obj/set! "whiteSpace" "pre")))))
|
||||
|
||||
(defn generate-text-styles
|
||||
[data props]
|
||||
(let [letter-spacing (obj/get data "letter-spacing")
|
||||
text-decoration (obj/get data "text-decoration")
|
||||
text-transform (obj/get data "text-transform")
|
||||
line-height (obj/get data "line-height")
|
||||
([props] (generate-text-styles (clj->js (obj/get props "node")) props))
|
||||
([data props]
|
||||
(let [letter-spacing (obj/get data "letter-spacing")
|
||||
text-decoration (obj/get data "text-decoration")
|
||||
text-transform (obj/get data "text-transform")
|
||||
line-height (obj/get data "line-height")
|
||||
|
||||
font-id (obj/get data "font-id" (:font-id ut/default-text-attrs))
|
||||
font-variant-id (obj/get data "font-variant-id")
|
||||
font-id (obj/get data "font-id" (:font-id ut/default-text-attrs))
|
||||
font-variant-id (obj/get data "font-variant-id")
|
||||
|
||||
font-family (obj/get data "font-family")
|
||||
font-size (obj/get data "font-size")
|
||||
font-family (obj/get data "font-family")
|
||||
font-size (obj/get data "font-size")
|
||||
|
||||
;; Old properties for backwards compatibility
|
||||
fill (obj/get data "fill")
|
||||
opacity (obj/get data "opacity" 1)
|
||||
;; Old properties for backwards compatibility
|
||||
fill (obj/get data "fill")
|
||||
opacity (obj/get data "opacity" 1)
|
||||
|
||||
fill-color (obj/get data "fill-color" fill)
|
||||
fill-opacity (obj/get data "fill-opacity" opacity)
|
||||
fill-color-gradient (obj/get data "fill-color-gradient" nil)
|
||||
fill-color-gradient (when fill-color-gradient
|
||||
(-> (js->clj fill-color-gradient :keywordize-keys true)
|
||||
(update :type keyword)))
|
||||
fill-color (obj/get data "fill-color" fill)
|
||||
fill-opacity (obj/get data "fill-opacity" opacity)
|
||||
fill-color-gradient (obj/get data "fill-color-gradient" nil)
|
||||
fill-color-gradient (when fill-color-gradient
|
||||
(-> (js->clj fill-color-gradient :keywordize-keys true)
|
||||
(update :type keyword)))
|
||||
|
||||
;; Uncomment this to allow to remove text colors. This could break the texts that already exist
|
||||
;;[r g b a] (if (nil? fill-color)
|
||||
;; [0 0 0 0] ;; Transparent color
|
||||
;; (uc/hex->rgba fill-color fill-opacity))
|
||||
;; Uncomment this to allow to remove text colors. This could break the texts that already exist
|
||||
;;[r g b a] (if (nil? fill-color)
|
||||
;; [0 0 0 0] ;; Transparent color
|
||||
;; (uc/hex->rgba fill-color fill-opacity))
|
||||
|
||||
[r g b a] (uc/hex->rgba fill-color fill-opacity)
|
||||
[r g b a] (uc/hex->rgba fill-color fill-opacity)
|
||||
|
||||
text-color (if fill-color-gradient
|
||||
(uc/gradient->css (js->clj fill-color-gradient))
|
||||
(str/format "rgba(%s, %s, %s, %s)" r g b a))
|
||||
text-color (if fill-color-gradient
|
||||
(uc/gradient->css (js->clj fill-color-gradient))
|
||||
(str/format "rgba(%s, %s, %s, %s)" r g b a))
|
||||
|
||||
fontsdb (deref fonts/fontsdb)
|
||||
fontsdb (deref fonts/fontsdb)
|
||||
|
||||
base #js {:textDecoration text-decoration
|
||||
:textTransform text-transform
|
||||
:lineHeight (or line-height "inherit")
|
||||
:color text-color
|
||||
"--text-color" text-color}]
|
||||
base #js {:textDecoration text-decoration
|
||||
:textTransform text-transform
|
||||
:lineHeight (or line-height "inherit")
|
||||
:color text-color
|
||||
"--text-color" text-color}]
|
||||
|
||||
(when (and (string? letter-spacing)
|
||||
(pos? (alength letter-spacing)))
|
||||
(obj/set! base "letterSpacing" (str letter-spacing "px")))
|
||||
(when (and (string? letter-spacing)
|
||||
(pos? (alength letter-spacing)))
|
||||
(obj/set! base "letterSpacing" (str letter-spacing "px")))
|
||||
|
||||
(when (and (string? font-size)
|
||||
(pos? (alength font-size)))
|
||||
(obj/set! base "fontSize" (str font-size "px")))
|
||||
(when (and (string? font-size)
|
||||
(pos? (alength font-size)))
|
||||
(obj/set! base "fontSize" (str font-size "px")))
|
||||
|
||||
(when (and (string? font-id)
|
||||
(pos? (alength font-id)))
|
||||
(fonts/ensure-loaded! font-id)
|
||||
(let [font (get fontsdb font-id)]
|
||||
(let [font-family (or (:family font)
|
||||
(obj/get data "fontFamily"))
|
||||
font-variant (d/seek #(= font-variant-id (:id %))
|
||||
(:variants font))
|
||||
font-style (or (:style font-variant)
|
||||
(obj/get data "fontStyle"))
|
||||
font-weight (or (:weight font-variant)
|
||||
(obj/get data "fontWeight"))]
|
||||
(obj/set! base "fontFamily" font-family)
|
||||
(obj/set! base "fontStyle" font-style)
|
||||
(obj/set! base "fontWeight" font-weight))))
|
||||
(when (and (string? font-id)
|
||||
(pos? (alength font-id)))
|
||||
(fonts/ensure-loaded! font-id)
|
||||
(let [font (get fontsdb font-id)]
|
||||
(let [font-family (or (:family font)
|
||||
(obj/get data "fontFamily"))
|
||||
font-variant (d/seek #(= font-variant-id (:id %))
|
||||
(:variants font))
|
||||
font-style (or (:style font-variant)
|
||||
(obj/get data "fontStyle"))
|
||||
font-weight (or (:weight font-variant)
|
||||
(obj/get data "fontWeight"))]
|
||||
(obj/set! base "fontFamily" font-family)
|
||||
(obj/set! base "fontStyle" font-style)
|
||||
(obj/set! base "fontWeight" font-weight))))
|
||||
|
||||
|
||||
base))
|
||||
base)))
|
||||
|
|
|
@ -34,46 +34,33 @@
|
|||
|
||||
;; --- Events
|
||||
|
||||
(defn use-double-click [{:keys [id]} selected?]
|
||||
(defn use-double-click [{:keys [id]}]
|
||||
(mf/use-callback
|
||||
(mf/deps id selected?)
|
||||
(mf/deps id)
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(dom/prevent-default event)
|
||||
(when selected?
|
||||
(st/emit! (dw/start-edition-mode id))))))
|
||||
(st/emit! (dw/start-edition-mode id)))))
|
||||
|
||||
;; --- Text Wrapper for workspace
|
||||
|
||||
(mf/defc text-wrapper
|
||||
(mf/defc text-static-content
|
||||
[{:keys [shape]}]
|
||||
[:& text/text-shape {:shape shape
|
||||
:grow-type (:grow-type shape)}])
|
||||
|
||||
(mf/defc text-resize-content
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [{:keys [id name x y width height grow-type] :as shape} (unchecked-get props "shape")
|
||||
ghost? (mf/use-ctx muc/ghost-ctx)
|
||||
selected-iref (mf/use-memo (mf/deps (:id shape))
|
||||
#(refs/make-selected-ref (:id shape)))
|
||||
selected? (mf/deref selected-iref)
|
||||
edition (mf/deref refs/selected-edition)
|
||||
current-transform (mf/deref refs/current-transform)
|
||||
|
||||
render-editor (mf/use-state false)
|
||||
|
||||
edition? (= edition id)
|
||||
embed-resources? (mf/use-ctx muc/embed-ctx)
|
||||
|
||||
handle-mouse-down (we/use-mouse-down shape)
|
||||
handle-context-menu (we/use-context-menu shape)
|
||||
handle-pointer-enter (we/use-pointer-enter shape)
|
||||
handle-pointer-leave (we/use-pointer-leave shape)
|
||||
handle-double-click (use-double-click shape selected?)
|
||||
|
||||
(let [shape (obj/get props "shape")
|
||||
{:keys [id name x y grow-type]} shape
|
||||
paragraph-ref (mf/use-state nil)
|
||||
|
||||
handle-resize-text
|
||||
(mf/use-callback
|
||||
(mf/deps id)
|
||||
(fn [entries]
|
||||
(when (and (not ghost?) (seq entries))
|
||||
(when (seq entries)
|
||||
;; RequestAnimationFrame so the "loop limit error" error is not thrown
|
||||
;; https://stackoverflow.com/questions/49384120/resizeobserver-loop-limit-exceeded
|
||||
(timers/raf
|
||||
|
@ -97,24 +84,41 @@
|
|||
(mf/use-effect
|
||||
(mf/deps @paragraph-ref handle-resize-text grow-type)
|
||||
(fn []
|
||||
(when (not ghost?)
|
||||
(when-let [paragraph-node @paragraph-ref]
|
||||
(let [observer (js/ResizeObserver. handle-resize-text)]
|
||||
(log/debug :msg "Attach resize observer" :shape-id id :shape-name name)
|
||||
(.observe observer paragraph-node)
|
||||
#(.disconnect observer))))))
|
||||
(when-let [paragraph-node @paragraph-ref]
|
||||
(let [observer (js/ResizeObserver. handle-resize-text)]
|
||||
(log/debug :msg "Attach resize observer" :shape-id id :shape-name name)
|
||||
(.observe observer paragraph-node)
|
||||
#(.disconnect observer)))))
|
||||
|
||||
[:& text/text-shape {:ref text-ref-cb
|
||||
:shape shape
|
||||
:grow-type (:grow-type shape)}]))
|
||||
|
||||
(mf/defc text-wrapper
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [{:keys [id x y width height] :as shape} (unchecked-get props "shape")
|
||||
ghost? (mf/use-ctx muc/ghost-ctx)
|
||||
edition (mf/deref refs/selected-edition)
|
||||
edition? (= edition id)
|
||||
|
||||
handle-mouse-down (we/use-mouse-down shape)
|
||||
handle-context-menu (we/use-context-menu shape)
|
||||
handle-pointer-enter (we/use-pointer-enter shape)
|
||||
handle-pointer-leave (we/use-pointer-leave shape)
|
||||
handle-double-click (use-double-click shape)]
|
||||
|
||||
[:> shape-container {:shape shape}
|
||||
;; We keep hidden the shape when we're editing so it keeps track of the size
|
||||
;; and updates the selrect acordingly
|
||||
[:g.text-shape {:opacity (when edition? 0)
|
||||
:pointer-events "none"}
|
||||
[:& text/text-shape {:key (str "text-shape" (:id shape))
|
||||
:ref text-ref-cb
|
||||
:shape shape
|
||||
:selected? selected?
|
||||
:grow-type (:grow-type shape)}]]
|
||||
|
||||
(if ghost?
|
||||
[:& text-static-content {:shape shape}]
|
||||
[:& text-resize-content {:shape shape}])]
|
||||
|
||||
|
||||
(when (and (not ghost?) edition?)
|
||||
[:& editor/text-shape-edit {:key (str "editor" (:id shape))
|
||||
:shape shape}])
|
||||
|
|
|
@ -93,12 +93,31 @@
|
|||
(rec-fn {} node)))
|
||||
|
||||
|
||||
(defn content->nodes [node]
|
||||
(loop [result (transient [])
|
||||
curr node
|
||||
pending (transient [])]
|
||||
|
||||
(let [result (conj! result curr)]
|
||||
;; Adds children to the pending list
|
||||
(let [children (:children curr)
|
||||
pending (loop [child (first children)
|
||||
children (rest children)
|
||||
pending pending]
|
||||
(if child
|
||||
(recur (first children)
|
||||
(rest children)
|
||||
(conj! pending child))
|
||||
pending))]
|
||||
|
||||
(if (= 0 (count pending))
|
||||
(persistent! result)
|
||||
;; Iterates with the next value in pending
|
||||
(let [next (get pending (dec (count pending)))]
|
||||
(recur result next (pop! pending))))))))
|
||||
|
||||
(defn get-text-attrs-multi
|
||||
[node attrs]
|
||||
(let [rec-fn
|
||||
(fn rec-fn [current node]
|
||||
(let [current (reduce rec-fn current (:children node []))]
|
||||
(get-attrs-multi [current node] attrs)))]
|
||||
(merge (select-keys default-text-attrs attrs)
|
||||
(rec-fn {} node))))
|
||||
(let [nodes (content->nodes node)]
|
||||
(get-attrs-multi nodes attrs)))
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue