mirror of
https://github.com/penpot/penpot.git
synced 2026-08-01 19:06:18 +00:00
Merge pull request #471 from penpot/fixes/performance
Performance improvements
This commit is contained in:
commit
d7a5cddcb3
@ -177,15 +177,15 @@
|
|||||||
(join [1 2 3] [:a :b]) => ([1 :a] [1 :b] [2 :a] [2 :b] [3 :a] [3 :b])
|
(join [1 2 3] [:a :b]) => ([1 :a] [1 :b] [2 :a] [2 :b] [3 :a] [3 :b])
|
||||||
You can pass a function to merge the items. By default is `vector`:
|
You can pass a function to merge the items. By default is `vector`:
|
||||||
(join [1 2 3] [1 10 100] *) => (1 10 100 2 20 200 3 30 300)"
|
(join [1 2 3] [1 10 100] *) => (1 10 100 2 20 200 3 30 300)"
|
||||||
([col1 col2] (join col1 col2 vector '()))
|
([col1 col2] (join col1 col2 vector []))
|
||||||
([col1 col2 join-fn] (join col1 col2 join-fn '()))
|
([col1 col2 join-fn] (join col1 col2 join-fn []))
|
||||||
([col1 col2 join-fn acc]
|
([col1 col2 join-fn acc]
|
||||||
(cond
|
(cond
|
||||||
(empty? col1) acc
|
(empty? col1) acc
|
||||||
(empty? col2) acc
|
(empty? col2) acc
|
||||||
:else (recur (rest col1) col2 join-fn
|
:else (recur (rest col1) col2 join-fn
|
||||||
(core/concat acc (map (partial join-fn (first col1)) col2))))))
|
(let [other (mapv (partial join-fn (first col1)) col2)]
|
||||||
|
(concat acc other))))))
|
||||||
|
|
||||||
(def sentinel
|
(def sentinel
|
||||||
#?(:clj (Object.)
|
#?(:clj (Object.)
|
||||||
|
|||||||
@ -26,6 +26,10 @@
|
|||||||
#?(:cljs (and (not (nil? v)) (js/isFinite v))
|
#?(:cljs (and (not (nil? v)) (js/isFinite v))
|
||||||
:clj (and (not (nil? v)) (Double/isFinite v))))
|
:clj (and (not (nil? v)) (Double/isFinite v))))
|
||||||
|
|
||||||
|
(defn finite
|
||||||
|
[v default]
|
||||||
|
(if (finite? v) v default))
|
||||||
|
|
||||||
(defn abs
|
(defn abs
|
||||||
[v]
|
[v]
|
||||||
#?(:cljs (js/Math.abs v)
|
#?(:cljs (js/Math.abs v)
|
||||||
|
|||||||
@ -88,14 +88,35 @@
|
|||||||
[component]
|
[component]
|
||||||
(get-in component [:objects (:id component)]))
|
(get-in component [:objects (:id component)]))
|
||||||
|
|
||||||
|
;; Implemented with transient for performance
|
||||||
(defn get-children
|
(defn get-children
|
||||||
"Retrieve all children ids recursively for a given object"
|
"Retrieve all children ids recursively for a given object"
|
||||||
[id objects]
|
[id objects]
|
||||||
;; TODO: find why does this sometimes come as a list instead of vector
|
|
||||||
(let [shapes (vec (get-in objects [id :shapes]))]
|
(loop [result (transient [])
|
||||||
(if shapes
|
pending (transient [])
|
||||||
(d/concat shapes (mapcat #(get-children % objects) shapes))
|
next id]
|
||||||
[])))
|
(let [children (get-in objects [next :shapes] [])
|
||||||
|
[result pending]
|
||||||
|
;; Iterate through children and add them to the result
|
||||||
|
;; also add them in pending to check for their children
|
||||||
|
(loop [result result
|
||||||
|
pending pending
|
||||||
|
current (first children)
|
||||||
|
children (rest children)]
|
||||||
|
(if current
|
||||||
|
(recur (conj! result current)
|
||||||
|
(conj! pending current)
|
||||||
|
(first children)
|
||||||
|
(rest children))
|
||||||
|
[result pending]))]
|
||||||
|
|
||||||
|
;; If we have still pending, advance the iterator
|
||||||
|
(let [length (count pending)]
|
||||||
|
(if (pos? length)
|
||||||
|
(let [next (get pending (dec length))]
|
||||||
|
(recur result (pop! pending) next))
|
||||||
|
(persistent! result))))))
|
||||||
|
|
||||||
(defn get-children-objects
|
(defn get-children-objects
|
||||||
"Retrieve all children objects recursively for a given object"
|
"Retrieve all children objects recursively for a given object"
|
||||||
|
|||||||
@ -11,6 +11,7 @@
|
|||||||
(:require
|
(:require
|
||||||
["slate" :as slate :refer [Editor Node Transforms Text]]
|
["slate" :as slate :refer [Editor Node Transforms Text]]
|
||||||
["slate-react" :as rslate]
|
["slate-react" :as rslate]
|
||||||
|
[app.common.math :as mth]
|
||||||
[app.common.attrs :as attrs]
|
[app.common.attrs :as attrs]
|
||||||
[app.common.geom.shapes :as gsh]
|
[app.common.geom.shapes :as gsh]
|
||||||
[app.common.pages :as cp]
|
[app.common.pages :as cp]
|
||||||
@ -217,6 +218,9 @@
|
|||||||
(= (-> selected first :type) :text))
|
(= (-> selected first :type) :text))
|
||||||
(assoc-in [:workspace-local :edition] (-> selected first :id)))))))
|
(assoc-in [:workspace-local :edition] (-> selected first :id)))))))
|
||||||
|
|
||||||
|
(defn not-changed? [old-dim new-dim]
|
||||||
|
(> (mth/abs (- old-dim new-dim)) 0.1))
|
||||||
|
|
||||||
(defn resize-text [id new-width new-height]
|
(defn resize-text [id new-width new-height]
|
||||||
(ptk/reify ::resize-text
|
(ptk/reify ::resize-text
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
@ -238,12 +242,13 @@
|
|||||||
(and (= :fixed grow-type) overflow-text (<= new-height shape-height))
|
(and (= :fixed grow-type) overflow-text (<= new-height shape-height))
|
||||||
(conj (update-overflow-text id false))
|
(conj (update-overflow-text id false))
|
||||||
|
|
||||||
(and (or (not= shape-width new-width) (not= shape-height new-height))
|
(and (or (not-changed? shape-width new-width) (not-changed? shape-height new-height))
|
||||||
(= grow-type :auto-width))
|
(= grow-type :auto-width))
|
||||||
(conj (dwt/update-dimensions [id] :width new-width)
|
(conj (dwt/update-dimensions [id] :width new-width)
|
||||||
(dwt/update-dimensions [id] :height new-height))
|
(dwt/update-dimensions [id] :height new-height))
|
||||||
|
|
||||||
(and (not= shape-height new-height) (= grow-type :auto-height))
|
(and (not-changed? shape-height new-height)
|
||||||
|
(= grow-type :auto-height))
|
||||||
(conj (dwt/update-dimensions [id] :height new-height)))]
|
(conj (dwt/update-dimensions [id] :height new-height)))]
|
||||||
|
|
||||||
(if (not (empty? events))
|
(if (not (empty? events))
|
||||||
|
|||||||
@ -20,8 +20,8 @@
|
|||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.util.geom.snap-points :as sp]))
|
[app.util.geom.snap-points :as sp]))
|
||||||
|
|
||||||
(def ^:private snap-accuracy 5)
|
(defonce ^:private snap-accuracy 5)
|
||||||
(def ^:private snap-distance-accuracy 10)
|
(defonce ^:private snap-distance-accuracy 10)
|
||||||
|
|
||||||
(defn- remove-from-snap-points
|
(defn- remove-from-snap-points
|
||||||
[remove-id?]
|
[remove-id?]
|
||||||
@ -96,57 +96,69 @@
|
|||||||
;; snap-x is the second parameter because is the "source" to combine
|
;; snap-x is the second parameter because is the "source" to combine
|
||||||
(rx/combine-latest snap->vector snap-y snap-x)))
|
(rx/combine-latest snap->vector snap-y snap-x)))
|
||||||
|
|
||||||
(defn search-snap-distance [selrect coord shapes-lt shapes-gt]
|
(defn calculate-snap [coord selrect shapes-lt shapes-gt]
|
||||||
(let [dist (fn [[sh1 sh2]] (-> sh1 (gsh/distance-shapes sh2) coord))
|
(let [dist (fn [[sh1 sh2]] (-> sh1 (gsh/distance-shapes sh2) coord))
|
||||||
dist-lt (fn [other] (-> (:selrect other) (gsh/distance-selrect selrect) coord))
|
dist-lt (fn [other] (-> (:selrect other) (gsh/distance-selrect selrect) coord))
|
||||||
dist-gt (fn [other] (-> selrect (gsh/distance-selrect (:selrect other)) coord))
|
dist-gt (fn [other] (-> selrect (gsh/distance-selrect (:selrect other)) coord))
|
||||||
|
|
||||||
;; Calculates the distance between all the shapes given as argument
|
|
||||||
inner-distance (fn [shapes]
|
|
||||||
(->> shapes
|
|
||||||
(sort-by coord)
|
|
||||||
(d/map-perm vector)
|
|
||||||
(filter (fn [[sh1 sh2]] (gsh/overlap-coord? coord sh1 sh2)))
|
|
||||||
(map dist)
|
|
||||||
(filter #(> % 0))))
|
|
||||||
|
|
||||||
;; Calculates the snap distance when in the middle of two shapes
|
;; Calculates the snap distance when in the middle of two shapes
|
||||||
between-snap (fn [[sh-lt sh-gt]]
|
between-snap
|
||||||
;; To calculate the middle snap.
|
(fn [[sh-lt sh-gt]]
|
||||||
;; Given x, the distance to a left shape and y to a right shape
|
;; To calculate the middle snap.
|
||||||
;; x - v = y + v => v = (x - y)/2
|
;; Given x, the distance to a left shape and y to a right shape
|
||||||
;; v will be the vector that we need to move the shape so it "snaps"
|
;; x - v = y + v => v = (x - y)/2
|
||||||
;; in the middle
|
;; v will be the vector that we need to move the shape so it "snaps"
|
||||||
(/ (- (dist-gt sh-gt)
|
;; in the middle
|
||||||
(dist-lt sh-lt)) 2))
|
(/ (- (dist-gt sh-gt)
|
||||||
]
|
(dist-lt sh-lt)) 2))
|
||||||
(->> shapes-lt
|
|
||||||
(rx/combine-latest vector shapes-gt)
|
|
||||||
(rx/map (fn [[shapes-lt shapes-gt]]
|
|
||||||
(let [;; Distance between the elements in an area, these are the snap
|
|
||||||
;; candidates to either side
|
|
||||||
lt-cand (inner-distance shapes-lt)
|
|
||||||
gt-cand (inner-distance shapes-gt)
|
|
||||||
|
|
||||||
;; Distance between the elements to either side and the current shape
|
;; Calculates the distance between all the shapes given as argument
|
||||||
;; this is the distance that will "snap"
|
inner-distance
|
||||||
lt-dist (map dist-lt shapes-lt)
|
(fn [shapes]
|
||||||
gt-dist (map dist-gt shapes-gt)
|
(->> shapes
|
||||||
|
(sort-by coord)
|
||||||
|
(d/map-perm vector)
|
||||||
|
(filter (fn [[sh1 sh2]] (gsh/overlap-coord? coord sh1 sh2)))
|
||||||
|
(map dist)
|
||||||
|
(filterv #(> % 0))))
|
||||||
|
|
||||||
;; Calculate the snaps, we need to reverse depending on area
|
best-snap
|
||||||
lt-snap (d/join lt-cand lt-dist -)
|
(fn [acc val]
|
||||||
gt-snap (d/join gt-dist gt-cand -)
|
;; Using a number is faster than accesing the variable.
|
||||||
|
;; Keep up to date with `snap-distance-accuracy`
|
||||||
|
(if (and (<= val 10) (>= val (- 10)))
|
||||||
|
(min acc val)
|
||||||
|
acc))
|
||||||
|
|
||||||
;; Calculate snap-between
|
;; Distance between the elements in an area, these are the snap
|
||||||
between-snap (->> (d/join shapes-lt shapes-gt)
|
;; candidates to either side
|
||||||
(map between-snap))
|
lt-cand (inner-distance shapes-lt)
|
||||||
|
gt-cand (inner-distance shapes-gt)
|
||||||
|
|
||||||
;; Search the minimum snap
|
;; Distance between the elements to either side and the current shape
|
||||||
min-snap (->> (concat lt-snap gt-snap between-snap)
|
;; this is the distance that will "snap"
|
||||||
(filter #(<= (mth/abs %) snap-distance-accuracy))
|
lt-dist (mapv dist-lt shapes-lt)
|
||||||
(reduce min ##Inf))]
|
gt-dist (mapv dist-gt shapes-gt)
|
||||||
|
|
||||||
(if (mth/finite? min-snap) [0 min-snap] nil)))))))
|
;; Calculate the snaps, we need to reverse depending on area
|
||||||
|
lt-snap (d/join lt-cand lt-dist -)
|
||||||
|
gt-snap (d/join gt-dist gt-cand -)
|
||||||
|
|
||||||
|
;; Calculate snap-between
|
||||||
|
between-snap (->> (d/join shapes-lt shapes-gt)
|
||||||
|
(map between-snap))
|
||||||
|
|
||||||
|
;; Search the minimum snap
|
||||||
|
snap-list (-> [] (d/concat lt-snap) (d/concat gt-snap) (d/concat between-snap))
|
||||||
|
min-snap (reduce best-snap ##Inf snap-list)]
|
||||||
|
|
||||||
|
(if (mth/finite? min-snap) [0 min-snap] nil)))
|
||||||
|
|
||||||
|
(defn search-snap-distance [selrect coord shapes-lt shapes-gt]
|
||||||
|
(->> shapes-lt
|
||||||
|
(rx/combine-latest vector shapes-gt)
|
||||||
|
(rx/map (fn [[shapes-lt shapes-gt]]
|
||||||
|
(calculate-snap coord selrect shapes-lt shapes-gt)))))
|
||||||
|
|
||||||
(defn select-shapes-area
|
(defn select-shapes-area
|
||||||
[page-id shapes objects area-selrect]
|
[page-id shapes objects area-selrect]
|
||||||
|
|||||||
@ -13,6 +13,7 @@
|
|||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.pages :as cp]
|
[app.common.pages :as cp]
|
||||||
|
[app.common.pages.helpers :as helpers]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.util.storage :refer [storage]]
|
[app.util.storage :refer [storage]]
|
||||||
[app.util.debug :refer [debug? debug-exclude-events logjs]]))
|
[app.util.debug :refer [debug? debug-exclude-events logjs]]))
|
||||||
|
|||||||
@ -14,6 +14,7 @@
|
|||||||
(def embed-ctx (mf/create-context false))
|
(def embed-ctx (mf/create-context false))
|
||||||
(def render-ctx (mf/create-context nil))
|
(def render-ctx (mf/create-context nil))
|
||||||
(def def-ctx (mf/create-context false))
|
(def def-ctx (mf/create-context false))
|
||||||
|
(def ghost-ctx (mf/create-context false))
|
||||||
|
|
||||||
(def current-route (mf/create-context nil))
|
(def current-route (mf/create-context nil))
|
||||||
(def current-team-id (mf/create-context nil))
|
(def current-team-id (mf/create-context nil))
|
||||||
|
|||||||
@ -21,6 +21,7 @@
|
|||||||
[app.main.streams :as ms]
|
[app.main.streams :as ms]
|
||||||
[app.main.ui.cursors :as cur]
|
[app.main.ui.cursors :as cur]
|
||||||
[app.main.ui.hooks :as hooks]
|
[app.main.ui.hooks :as hooks]
|
||||||
|
[app.main.ui.context :as muc]
|
||||||
[app.main.ui.shapes.circle :as circle]
|
[app.main.ui.shapes.circle :as circle]
|
||||||
[app.main.ui.shapes.image :as image]
|
[app.main.ui.shapes.image :as image]
|
||||||
[app.main.ui.shapes.rect :as rect]
|
[app.main.ui.shapes.rect :as rect]
|
||||||
@ -52,12 +53,9 @@
|
|||||||
false
|
false
|
||||||
(let [o-shape (obj/get op "shape")
|
(let [o-shape (obj/get op "shape")
|
||||||
n-frame (obj/get np "frame")
|
n-frame (obj/get np "frame")
|
||||||
o-frame (obj/get op "frame")
|
o-frame (obj/get op "frame")]
|
||||||
n-ghost (obj/get np "ghost?")
|
|
||||||
o-ghost (obj/get op "ghost?")]
|
|
||||||
(and (identical? n-shape o-shape)
|
(and (identical? n-shape o-shape)
|
||||||
(identical? n-frame o-frame)
|
(identical? n-frame o-frame))))))
|
||||||
(identical? n-ghost o-ghost))))))
|
|
||||||
|
|
||||||
(defn make-is-moving-ref
|
(defn make-is-moving-ref
|
||||||
[id]
|
[id]
|
||||||
@ -73,7 +71,7 @@
|
|||||||
[props]
|
[props]
|
||||||
(let [shape (obj/get props "shape")
|
(let [shape (obj/get props "shape")
|
||||||
frame (obj/get props "frame")
|
frame (obj/get props "frame")
|
||||||
ghost? (obj/get props "ghost?")
|
ghost? (mf/use-ctx muc/ghost-ctx)
|
||||||
shape (-> (geom/transform-shape shape)
|
shape (-> (geom/transform-shape shape)
|
||||||
(geom/translate-to-frame frame))
|
(geom/translate-to-frame frame))
|
||||||
opts #js {:shape shape
|
opts #js {:shape shape
|
||||||
@ -84,14 +82,14 @@
|
|||||||
moving-iref (mf/use-memo (mf/deps (:id shape)) (make-is-moving-ref (:id shape)))
|
moving-iref (mf/use-memo (mf/deps (:id shape)) (make-is-moving-ref (:id shape)))
|
||||||
moving? (mf/deref moving-iref)
|
moving? (mf/deref moving-iref)
|
||||||
svg-element? (and (= (:type shape) :svg-raw)
|
svg-element? (and (= (:type shape) :svg-raw)
|
||||||
(not= :svg (get-in shape [:content :tag])))]
|
(not= :svg (get-in shape [:content :tag])))
|
||||||
|
hide-moving? (and (not ghost?) moving?)]
|
||||||
|
|
||||||
(when (and shape
|
(when (and shape (not (:hidden shape)))
|
||||||
(or ghost? (not moving?))
|
|
||||||
(not (:hidden shape)))
|
|
||||||
[:*
|
[:*
|
||||||
(if-not svg-element?
|
(if-not svg-element?
|
||||||
[:g.shape-wrapper {:style {:cursor (if alt? cur/duplicate nil)}}
|
[:g.shape-wrapper {:style {:display (when hide-moving? "none")
|
||||||
|
:cursor (if alt? cur/duplicate nil)}}
|
||||||
(case (:type shape)
|
(case (:type shape)
|
||||||
:path [:> path/path-wrapper opts]
|
:path [:> path/path-wrapper opts]
|
||||||
:text [:> text/text-wrapper opts]
|
:text [:> text/text-wrapper opts]
|
||||||
|
|||||||
@ -22,7 +22,8 @@
|
|||||||
[app.util.timers :as ts]
|
[app.util.timers :as ts]
|
||||||
[beicon.core :as rx]
|
[beicon.core :as rx]
|
||||||
[okulary.core :as l]
|
[okulary.core :as l]
|
||||||
[rumext.alpha :as mf]))
|
[rumext.alpha :as mf]
|
||||||
|
[app.main.ui.context :as muc]))
|
||||||
|
|
||||||
(defn- frame-wrapper-factory-equals?
|
(defn- frame-wrapper-factory-equals?
|
||||||
[np op]
|
[np op]
|
||||||
@ -100,14 +101,15 @@
|
|||||||
(mf/fnc deferred
|
(mf/fnc deferred
|
||||||
{::mf/wrap-props false}
|
{::mf/wrap-props false}
|
||||||
[props]
|
[props]
|
||||||
(let [tmp (mf/useState false)
|
(let [ghost? (mf/use-ctx muc/ghost-ctx)
|
||||||
|
tmp (mf/useState false)
|
||||||
^boolean render? (aget tmp 0)
|
^boolean render? (aget tmp 0)
|
||||||
^js set-render (aget tmp 1)]
|
^js set-render (aget tmp 1)]
|
||||||
(mf/use-layout-effect
|
(mf/use-layout-effect
|
||||||
(fn []
|
(fn []
|
||||||
(let [sem (ts/schedule-on-idle #(set-render true))]
|
(let [sem (ts/schedule-on-idle #(set-render true))]
|
||||||
#(rx/dispose! sem))))
|
#(rx/dispose! sem))))
|
||||||
(if (unchecked-get props "ghost?")
|
(if ghost?
|
||||||
(mf/create-element component props)
|
(mf/create-element component props)
|
||||||
(when render? (mf/create-element component props))))))
|
(when render? (mf/create-element component props))))))
|
||||||
|
|
||||||
@ -120,7 +122,7 @@
|
|||||||
[props]
|
[props]
|
||||||
(let [shape (unchecked-get props "shape")
|
(let [shape (unchecked-get props "shape")
|
||||||
objects (unchecked-get props "objects")
|
objects (unchecked-get props "objects")
|
||||||
ghost? (unchecked-get props "ghost?")
|
ghost? (mf/use-ctx muc/ghost-ctx)
|
||||||
|
|
||||||
moving-iref (mf/use-memo (mf/deps (:id shape))
|
moving-iref (mf/use-memo (mf/deps (:id shape))
|
||||||
#(make-is-moving-ref (:id shape)))
|
#(make-is-moving-ref (:id shape)))
|
||||||
@ -136,15 +138,16 @@
|
|||||||
|
|
||||||
handle-context-menu (we/use-context-menu shape)
|
handle-context-menu (we/use-context-menu shape)
|
||||||
handle-double-click (use-select-shape shape)
|
handle-double-click (use-select-shape shape)
|
||||||
handle-mouse-down (we/use-mouse-down shape)]
|
handle-mouse-down (we/use-mouse-down shape)
|
||||||
|
|
||||||
(when (and shape
|
hide-moving? (and (not ghost?) moving?)]
|
||||||
(or ghost? (not moving?))
|
|
||||||
(not (:hidden shape)))
|
(when (and shape (not (:hidden shape)))
|
||||||
[:g {:class (when selected? "selected")
|
[:g.frame-wrapper {:class (when selected? "selected")
|
||||||
:on-context-menu handle-context-menu
|
:style {:display (when hide-moving? "none")}
|
||||||
:on-double-click handle-double-click
|
:on-context-menu handle-context-menu
|
||||||
:on-mouse-down handle-mouse-down}
|
:on-double-click handle-double-click
|
||||||
|
:on-mouse-down handle-mouse-down}
|
||||||
|
|
||||||
[:& frame-title {:frame shape}]
|
[:& frame-title {:frame shape}]
|
||||||
|
|
||||||
|
|||||||
@ -49,6 +49,7 @@
|
|||||||
{::mf/wrap-props false}
|
{::mf/wrap-props false}
|
||||||
[props]
|
[props]
|
||||||
(let [{:keys [id name x y width height grow-type] :as shape} (unchecked-get props "shape")
|
(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))
|
selected-iref (mf/use-memo (mf/deps (:id shape))
|
||||||
#(refs/make-selected-ref (:id shape)))
|
#(refs/make-selected-ref (:id shape)))
|
||||||
selected? (mf/deref selected-iref)
|
selected? (mf/deref selected-iref)
|
||||||
@ -73,14 +74,15 @@
|
|||||||
(mf/use-callback
|
(mf/use-callback
|
||||||
(mf/deps id)
|
(mf/deps id)
|
||||||
(fn [entries]
|
(fn [entries]
|
||||||
(when (seq entries)
|
(when (and (not ghost?) (seq entries))
|
||||||
;; RequestAnimationFrame so the "loop limit error" error is not thrown
|
;; RequestAnimationFrame so the "loop limit error" error is not thrown
|
||||||
;; https://stackoverflow.com/questions/49384120/resizeobserver-loop-limit-exceeded
|
;; https://stackoverflow.com/questions/49384120/resizeobserver-loop-limit-exceeded
|
||||||
(timers/raf
|
(timers/raf
|
||||||
#(let [width (obj/get-in entries [0 "contentRect" "width"])
|
#(let [width (obj/get-in entries [0 "contentRect" "width"])
|
||||||
height (obj/get-in entries [0 "contentRect" "height"])]
|
height (obj/get-in entries [0 "contentRect" "height"])]
|
||||||
(log/debug :msg "Resize detected" :shape-id id :width width :height height)
|
(when (and (not (mth/almost-zero? width)) (not (mth/almost-zero? height)))
|
||||||
(st/emit! (dwt/resize-text id (mth/ceil width) (mth/ceil height))))))))
|
(do (log/debug :msg "Resize detected" :shape-id id :width width :height height)
|
||||||
|
(st/emit! (dwt/resize-text id (mth/ceil width) (mth/ceil height))))))))))
|
||||||
|
|
||||||
text-ref-cb
|
text-ref-cb
|
||||||
(mf/use-callback
|
(mf/use-callback
|
||||||
@ -96,11 +98,12 @@
|
|||||||
(mf/use-effect
|
(mf/use-effect
|
||||||
(mf/deps @paragraph-ref handle-resize-text grow-type)
|
(mf/deps @paragraph-ref handle-resize-text grow-type)
|
||||||
(fn []
|
(fn []
|
||||||
(when-let [paragraph-node @paragraph-ref]
|
(when (not ghost?)
|
||||||
(let [observer (js/ResizeObserver. handle-resize-text)]
|
(when-let [paragraph-node @paragraph-ref]
|
||||||
(log/debug :msg "Attach resize observer" :shape-id id :shape-name name)
|
(let [observer (js/ResizeObserver. handle-resize-text)]
|
||||||
(.observe observer paragraph-node)
|
(log/debug :msg "Attach resize observer" :shape-id id :shape-name name)
|
||||||
#(.disconnect observer)))))
|
(.observe observer paragraph-node)
|
||||||
|
#(.disconnect observer))))))
|
||||||
|
|
||||||
|
|
||||||
[:> shape-container {:shape shape}
|
[:> shape-container {:shape shape}
|
||||||
|
|||||||
@ -112,52 +112,11 @@
|
|||||||
:x2 x2 :y2 y2
|
:x2 x2 :y2 y2
|
||||||
:style {:stroke line-color :stroke-width (str (/ 1 zoom))}}])]))
|
:style {:stroke line-color :stroke-width (str (/ 1 zoom))}}])]))
|
||||||
|
|
||||||
(mf/defc shape-distance
|
(defn calculate-segments [coord selrect lt-shapes gt-shapes]
|
||||||
{::mf/wrap-props false}
|
(let [pair->distance+pair
|
||||||
[props]
|
|
||||||
(let [frame (unchecked-get props "frame")
|
|
||||||
selrect (unchecked-get props "selrect")
|
|
||||||
page-id (unchecked-get props "page-id")
|
|
||||||
zoom (unchecked-get props "zoom")
|
|
||||||
coord (unchecked-get props "coord")
|
|
||||||
selected (unchecked-get props "selected")
|
|
||||||
|
|
||||||
subject (mf/use-memo #(rx/subject))
|
|
||||||
to-measure (mf/use-state [])
|
|
||||||
|
|
||||||
pair->distance+pair
|
|
||||||
(fn [[sh1 sh2]]
|
(fn [[sh1 sh2]]
|
||||||
[(-> (gsh/distance-shapes sh1 sh2) coord (mth/precision 0)) [sh1 sh2]])
|
[(-> (gsh/distance-shapes sh1 sh2) coord (mth/precision 0)) [sh1 sh2]])
|
||||||
|
|
||||||
contains-selected?
|
|
||||||
(fn [selected pairs]
|
|
||||||
(let [has-selected?
|
|
||||||
(fn [[_ [sh1 sh2]]]
|
|
||||||
(or (selected (:id sh1))
|
|
||||||
(selected (:id sh2))))]
|
|
||||||
(some has-selected? pairs)))
|
|
||||||
|
|
||||||
query-worker
|
|
||||||
(fn [[selrect selected frame]]
|
|
||||||
(let [lt-side (if (= coord :x) :left :top)
|
|
||||||
gt-side (if (= coord :x) :right :bottom)
|
|
||||||
container-selrec (or (:selrect frame)
|
|
||||||
(gsh/rect->selrect @refs/vbox))
|
|
||||||
areas (gsh/selrect->areas container-selrec selrect)
|
|
||||||
query-side (fn [side]
|
|
||||||
(let [rect (gsh/pad-selrec (areas side))]
|
|
||||||
(if (and (> (:width rect) 0) (> (:height rect) 0))
|
|
||||||
(->> (uw/ask! {:cmd :selection/query
|
|
||||||
:page-id page-id
|
|
||||||
:frame-id (:id frame)
|
|
||||||
:rect rect})
|
|
||||||
(rx/map #(set/difference % selected))
|
|
||||||
(rx/map #(->> % (map (partial get @refs/workspace-page-objects)))))
|
|
||||||
(rx/of nil))))]
|
|
||||||
|
|
||||||
(->> (query-side lt-side)
|
|
||||||
(rx/combine-latest vector (query-side gt-side)))))
|
|
||||||
|
|
||||||
distance-to-selrect
|
distance-to-selrect
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
(let [sr (:selrect shape)]
|
(let [sr (:selrect shape)]
|
||||||
@ -183,7 +142,6 @@
|
|||||||
(some #(<= (mth/abs (- value %)) 1))))
|
(some #(<= (mth/abs (- value %)) 1))))
|
||||||
|
|
||||||
;; Left/Top shapes and right/bottom shapes (depends on `coord` parameter
|
;; Left/Top shapes and right/bottom shapes (depends on `coord` parameter
|
||||||
[lt-shapes gt-shapes] @to-measure
|
|
||||||
|
|
||||||
;; Gets the distance to the current selection
|
;; Gets the distance to the current selection
|
||||||
lt-distances (->> lt-shapes (map distance-to-selrect) (filter pos?) (into #{}))
|
lt-distances (->> lt-shapes (map distance-to-selrect) (filter pos?) (into #{}))
|
||||||
@ -225,6 +183,46 @@
|
|||||||
(map #(vector selrect (:selrect %))))
|
(map #(vector selrect (:selrect %))))
|
||||||
|
|
||||||
segments-to-display (d/concat #{} other-shapes-segments selection-segments)]
|
segments-to-display (d/concat #{} other-shapes-segments selection-segments)]
|
||||||
|
segments-to-display))
|
||||||
|
|
||||||
|
(mf/defc shape-distance
|
||||||
|
{::mf/wrap-props false}
|
||||||
|
[props]
|
||||||
|
(let [frame (unchecked-get props "frame")
|
||||||
|
selrect (unchecked-get props "selrect")
|
||||||
|
page-id (unchecked-get props "page-id")
|
||||||
|
zoom (unchecked-get props "zoom")
|
||||||
|
coord (unchecked-get props "coord")
|
||||||
|
selected (unchecked-get props "selected")
|
||||||
|
|
||||||
|
subject (mf/use-memo #(rx/subject))
|
||||||
|
to-measure (mf/use-state [])
|
||||||
|
|
||||||
|
query-worker
|
||||||
|
(fn [[selrect selected frame]]
|
||||||
|
(let [lt-side (if (= coord :x) :left :top)
|
||||||
|
gt-side (if (= coord :x) :right :bottom)
|
||||||
|
container-selrec (or (:selrect frame)
|
||||||
|
(gsh/rect->selrect @refs/vbox))
|
||||||
|
areas (gsh/selrect->areas container-selrec selrect)
|
||||||
|
query-side (fn [side]
|
||||||
|
(let [rect (gsh/pad-selrec (areas side))]
|
||||||
|
(if (and (> (:width rect) 0) (> (:height rect) 0))
|
||||||
|
(->> (uw/ask! {:cmd :selection/query
|
||||||
|
:page-id page-id
|
||||||
|
:frame-id (:id frame)
|
||||||
|
:rect rect})
|
||||||
|
(rx/map #(set/difference % selected))
|
||||||
|
(rx/map #(->> % (map (partial get @refs/workspace-page-objects)))))
|
||||||
|
(rx/of nil))))]
|
||||||
|
|
||||||
|
(->> (query-side lt-side)
|
||||||
|
(rx/combine-latest vector (query-side gt-side)))))
|
||||||
|
|
||||||
|
|
||||||
|
[lt-shapes gt-shapes] @to-measure
|
||||||
|
|
||||||
|
segments-to-display (calculate-segments coord selrect lt-shapes gt-shapes)]
|
||||||
|
|
||||||
(mf/use-effect
|
(mf/use-effect
|
||||||
(fn []
|
(fn []
|
||||||
|
|||||||
@ -162,7 +162,6 @@
|
|||||||
(let [hover (unchecked-get props "hover")
|
(let [hover (unchecked-get props "hover")
|
||||||
selected (unchecked-get props "selected")
|
selected (unchecked-get props "selected")
|
||||||
ids (unchecked-get props "ids")
|
ids (unchecked-get props "ids")
|
||||||
ghost? (unchecked-get props "ghost?")
|
|
||||||
edition (unchecked-get props "edition")
|
edition (unchecked-get props "edition")
|
||||||
data (mf/deref refs/workspace-page)
|
data (mf/deref refs/workspace-page)
|
||||||
objects (:objects data)
|
objects (:objects data)
|
||||||
@ -180,17 +179,14 @@
|
|||||||
(if (= (:type item) :frame)
|
(if (= (:type item) :frame)
|
||||||
[:& frame-wrapper {:shape item
|
[:& frame-wrapper {:shape item
|
||||||
:key (:id item)
|
:key (:id item)
|
||||||
:objects objects
|
:objects objects}]
|
||||||
:ghost? ghost?}]
|
|
||||||
[:& shape-wrapper {:shape item
|
[:& shape-wrapper {:shape item
|
||||||
:key (:id item)
|
:key (:id item)}]))]
|
||||||
:ghost? ghost?}]))]
|
|
||||||
|
|
||||||
(when (not ghost?)
|
[:& shape-outlines {:objects objects
|
||||||
[:& shape-outlines {:objects objects
|
:selected selected
|
||||||
:selected selected
|
:hover hover
|
||||||
:hover hover
|
:edition edition}]]))
|
||||||
:edition edition}])]))
|
|
||||||
|
|
||||||
(mf/defc ghost-frames
|
(mf/defc ghost-frames
|
||||||
{::mf/wrap-props false}
|
{::mf/wrap-props false}
|
||||||
@ -206,18 +202,22 @@
|
|||||||
(map gsh/transform-shape))
|
(map gsh/transform-shape))
|
||||||
|
|
||||||
selrect (->> (into [] xf sobjects)
|
selrect (->> (into [] xf sobjects)
|
||||||
(gsh/selection-rect))]
|
(gsh/selection-rect))
|
||||||
[:svg.ghost
|
|
||||||
{:x (:x selrect)
|
|
||||||
:y (:y selrect)
|
|
||||||
:width (:width selrect)
|
|
||||||
:height (:height selrect)
|
|
||||||
:style {:pointer-events "none"}}
|
|
||||||
|
|
||||||
[:g {:transform (str/fmt "translate(%s,%s)" (- (:x selrect-orig)) (- (:y selrect-orig)))}
|
transform (when (and (mth/finite? (:x selrect-orig))
|
||||||
[:& frames
|
(mth/finite? (:y selrect-orig)))
|
||||||
{:ids selected
|
(str/fmt "translate(%s,%s)" (- (:x selrect-orig)) (- (:y selrect-orig))))]
|
||||||
:ghost? true}]]]))
|
[:& (mf/provider ctx/ghost-ctx) {:value true}
|
||||||
|
[:svg.ghost
|
||||||
|
{:x (mth/finite (:x selrect) 0)
|
||||||
|
:y (mth/finite (:y selrect) 0)
|
||||||
|
:width (mth/finite (:width selrect) 100)
|
||||||
|
:height (mth/finite (:height selrect) 100)
|
||||||
|
:style {:pointer-events "none"}}
|
||||||
|
|
||||||
|
[:g {:transform transform}
|
||||||
|
[:& frames
|
||||||
|
{:ids selected}]]]]))
|
||||||
|
|
||||||
(defn format-viewbox [vbox]
|
(defn format-viewbox [vbox]
|
||||||
(str/join " " [(+ (:x vbox 0) (:left-offset vbox 0))
|
(str/join " " [(+ (:x vbox 0) (:left-offset vbox 0))
|
||||||
@ -655,9 +655,9 @@
|
|||||||
:selected selected
|
:selected selected
|
||||||
:edition edition}]
|
:edition edition}]
|
||||||
|
|
||||||
(when (= :move (:transform local))
|
[:g {:style {:display (when (not= :move (:transform local)) "none")}}
|
||||||
[:& ghost-frames {:modifiers (:modifiers local)
|
[:& ghost-frames {:modifiers (:modifiers local)
|
||||||
:selected selected}])
|
:selected selected}]]
|
||||||
|
|
||||||
(when (seq selected)
|
(when (seq selected)
|
||||||
[:& selection-handlers {:selected selected
|
[:& selection-handlers {:selected selected
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user