Remove usage of use-var on layer-item

Focus on use more basic primitves on performance
sensitive components
This commit is contained in:
Andrey Antukh 2026-02-05 10:28:21 +01:00
parent 05165ce014
commit 7be03e2ea6
2 changed files with 50 additions and 35 deletions

View File

@ -58,4 +58,3 @@
(when (nil? (:data file)) (when (nil? (:data file))
(migrate-file conn file))) (migrate-file conn file)))
(db/exec-one! conn ["drop table page cascade;"]))) (db/exec-one! conn ["drop table page cascade;"])))

View File

@ -37,6 +37,8 @@
(defonce ^:private sidebar-hover-queue (atom {:enter #{} :leave #{}})) (defonce ^:private sidebar-hover-queue (atom {:enter #{} :leave #{}}))
(defonce ^:private sidebar-hover-pending? (atom false)) (defonce ^:private sidebar-hover-pending? (atom false))
(def ^:const default-chunk-size 50)
(defn- schedule-sidebar-hover-flush [] (defn- schedule-sidebar-hover-flush []
(when (compare-and-set! sidebar-hover-pending? false true) (when (compare-and-set! sidebar-hover-pending? false true)
(ts/raf (ts/raf
@ -198,7 +200,7 @@
drag-disabled* (mf/use-state false) drag-disabled* (mf/use-state false)
drag-disabled? (deref drag-disabled*) drag-disabled? (deref drag-disabled*)
scroll-to-middle? (mf/use-var true) scroll-middle-ref (mf/use-ref true)
expanded-iref (mf/with-memo [id] expanded-iref (mf/with-memo [id]
(-> (l/in [:expanded id]) (-> (l/in [:expanded id])
(l/derived refs/workspace-local))) (l/derived refs/workspace-local)))
@ -214,6 +216,24 @@
parent-board? (and (cfh/frame-shape? item) parent-board? (and (cfh/frame-shape? item)
(= uuid/zero (:parent-id item))) (= uuid/zero (:parent-id item)))
name-node-ref (mf/use-ref)
depth (+ depth 1)
component-tree? (or ^boolean component-child?
^boolean (ctk/instance-root? item)
^boolean (ctk/instance-head? item))
enable-drag (mf/use-fn #(reset! drag-disabled* false))
disable-drag (mf/use-fn #(reset! drag-disabled* true))
;; Lazy loading of child elements via IntersectionObserver
children-count* (mf/use-state 0)
children-count (deref children-count*)
lazy-ref (mf/use-ref nil)
observer-ref (mf/use-ref nil)
toggle-collapse toggle-collapse
(mf/use-fn (mf/use-fn
(mf/deps expanded?) (mf/deps expanded?)
@ -247,7 +267,7 @@
(mf/deps id filtered? objects) (mf/deps id filtered? objects)
(fn [event] (fn [event]
(dom/prevent-default event) (dom/prevent-default event)
(reset! scroll-to-middle? false) (mf/set-ref-val! scroll-middle-ref false)
(cond (cond
(kbd/shift? event) (kbd/shift? event)
(if filtered? (if filtered?
@ -361,41 +381,31 @@
:data {:id (:id item) :data {:id (:id item)
:index index :index index
:name (:name item)} :name (:name item)}
;; We don't want to change the structure of component copies
:draggable? (and :draggable? (and
sortable? sortable?
(not read-only?) (not read-only?)
(not (ctn/has-any-copy-parent? objects item)))) ;; We don't want to change the structure of component copies (not (ctn/has-any-copy-parent? objects item))))]
ref (mf/use-ref)
depth (+ depth 1)
component-tree? (or component-child? (ctk/instance-root? item) (ctk/instance-head? item))
enable-drag (mf/use-fn #(reset! drag-disabled* false))
disable-drag (mf/use-fn #(reset! drag-disabled* true))
;; Lazy loading of child elements via IntersectionObserver
children-count* (mf/use-state 0)
children-count (deref children-count*)
lazy-ref (mf/use-ref nil)
observer-var (mf/use-var nil)
chunk-size 50]
(mf/with-effect [selected? selected] (mf/with-effect [selected? selected]
(let [single? (= (count selected) 1) (let [single? (= (count selected) 1)
node (mf/ref-val ref) node (mf/ref-val name-node-ref)
scroll-node (dom/get-parent-with-data node "scroll-container") scroll-node (dom/get-parent-with-data node "scroll-container")
parent-node (dom/get-parent-at node 2) parent-node (dom/get-parent-at node 2)
first-child-node (dom/get-first-child parent-node) first-child-node (dom/get-first-child parent-node)
scroll-to-middle? (mf/ref-val scroll-middle-ref)
subid subid
(when (and single? selected? @scroll-to-middle?) (when (and ^boolean single?
^boolean selected?
^boolean scroll-to-middle?)
(ts/schedule (ts/schedule
100 100
#(when (and node scroll-node) #(when (and node scroll-node)
(let [scroll-distance-ratio (dom/get-scroll-distance-ratio node scroll-node) (let [scroll-distance-ratio (dom/get-scroll-distance-ratio node scroll-node)
scroll-behavior (if (> scroll-distance-ratio 1) "instant" "smooth")] scroll-behavior (if (> scroll-distance-ratio 1) "instant" "smooth")]
(dom/scroll-into-view-if-needed! first-child-node #js {:block "center" :behavior scroll-behavior :inline "start"}) (dom/scroll-into-view-if-needed! first-child-node #js {:block "center" :behavior scroll-behavior :inline "start"})
(reset! scroll-to-middle? true)))))] (mf/set-ref-val! scroll-middle-ref true)))))]
#(when (some? subid) #(when (some? subid)
(rx/dispose! subid)))) (rx/dispose! subid))))
@ -409,7 +419,7 @@
(let [;; Children are rendered in reverse order, so index 0 in render = last in shapes-vec (let [;; Children are rendered in reverse order, so index 0 in render = last in shapes-vec
;; Find if any selected id is a direct child and get its render index ;; Find if any selected id is a direct child and get its render index
selected-child-render-idx selected-child-render-idx
(when (and (> total chunk-size) (seq selected)) (when (and (> total default-chunk-size) (seq selected))
(let [shapes-reversed (vec (reverse shapes-vec))] (let [shapes-reversed (vec (reverse shapes-vec))]
(some (fn [sel-id] (some (fn [sel-id]
(let [idx (.indexOf shapes-reversed sel-id)] (let [idx (.indexOf shapes-reversed sel-id)]
@ -418,24 +428,30 @@
;; Load at least enough to include the selected child plus extra ;; Load at least enough to include the selected child plus extra
;; for context (so it can be centered in the scroll view) ;; for context (so it can be centered in the scroll view)
min-count (if selected-child-render-idx min-count (if selected-child-render-idx
(+ selected-child-render-idx chunk-size) (+ selected-child-render-idx default-chunk-size)
chunk-size) default-chunk-size)
current @children-count* current @children-count*
new-count (min total (max current chunk-size min-count))] new-count (min total (max current default-chunk-size min-count))]
(reset! children-count* new-count)) (reset! children-count* new-count))
(reset! children-count* 0)))) (reset! children-count* 0)))
(fn []
(when-let [obs (mf/ref-val observer-ref)]
(.disconnect obs)
(mf/set-ref-val! obs nil))))
;; Re-observe sentinel whenever children-count changes (sentinel moves) ;; Re-observe sentinel whenever children-count changes (sentinel moves)
;; and (shapes item) to reconnect observer after shape changes ;; and (shapes item) to reconnect observer after shape changes
(mf/with-effect [children-count expanded? (:shapes item)] (mf/with-effect [children-count expanded? (:shapes item)]
(let [total (count (:shapes item)) (let [total (count (:shapes item))
node (mf/ref-val ref) node (mf/ref-val name-node-ref)
scroll-node (dom/get-parent-with-data node "scroll-container") scroll-node (dom/get-parent-with-data node "scroll-container")
lazy-node (mf/ref-val lazy-ref)] lazy-node (mf/ref-val lazy-ref)]
;; Disconnect previous observer ;; Disconnect previous observer
(when-let [obs ^js @observer-var] (when-let [obs (mf/ref-val observer-ref)]
(.disconnect obs) (.disconnect obs)
(reset! observer-var nil)) (mf/set-ref-val! observer-ref nil))
;; Setup new observer if there are more children to load ;; Setup new observer if there are more children to load
(when (and expanded? (when (and expanded?
(< children-count total) (< children-count total)
@ -446,18 +462,18 @@
(.-isIntersecting (first entries))) (.-isIntersecting (first entries)))
;; Load next chunk when sentinel intersects ;; Load next chunk when sentinel intersects
(let [current @children-count* (let [current @children-count*
next-count (min total (+ current chunk-size))] next-count (min total (+ current default-chunk-size))]
(reset! children-count* next-count)))) (reset! children-count* next-count))))
observer (js/IntersectionObserver. cb #js {:root scroll-node})] observer (js/IntersectionObserver. cb #js {:root scroll-node})]
(.observe observer lazy-node) (.observe observer lazy-node)
(reset! observer-var observer))))) (mf/set-ref-val! observer-ref observer)))))
[:& layer-item-inner [:& layer-item-inner
{:ref dref {:ref dref
:item item :item item
:depth depth :depth depth
:parent-size parent-size :parent-size parent-size
:name-ref ref :name-ref name-node-ref
:read-only? read-only? :read-only? read-only?
:highlighted? highlighted? :highlighted? highlighted?
:selected? selected? :selected? selected?