mirror of
https://github.com/penpot/penpot.git
synced 2026-09-30 07:46:16 +00:00
⚡ Add xf:add-index and memoize interactions menu rendering (#11915)
Introduce a shared xf:add-index transducer in app.common.data that attaches the position to each item, and cover it with unit tests. Use it in the workspace interactions menu: the indexed interactions list is now derived in a memoized step keyed on the interactions prop, so it is not rebuilt when the section is collapsed or expanded. The previous code called d/enumerate on every render. Update the frontend UI conventions memory with the pattern and the constraint that the transducer only works on associative items. AI-assisted-by: deepseek-v4.1-flash
This commit is contained in:
parent
cdb0b3950d
commit
de14311ce7
@ -33,6 +33,7 @@
|
||||
## Performance
|
||||
|
||||
- Keep expensive derived data in refs, memoized selectors, or pure helpers. In hot render paths, prefer existing `app.common.data.macros` helpers where local code already uses them.
|
||||
- Derive index-aware or sorted/filtered sequences once: wrap the transformation in `mf/with-memo` keyed on the source collection instead of calling `d/enumerate` in the render body. Use the shared `d/xf:add-index`, which attaches `:app.common.data/index` to each item; the items must be associative (maps/records), so it does not work on keywords or plain ids.
|
||||
- Avoid creating new callback functions/objects inside hot renders when a named function, memoized callback, data attribute, or precomputed JS props object works.
|
||||
- Destructure props/state values used repeatedly. Avoid repeated deref/property access in render loops.
|
||||
|
||||
|
||||
@ -41,6 +41,7 @@
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(def xf:map-id (map :id))
|
||||
(def xf:add-index (map-indexed (fn [index item] (assoc item ::index index))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Data Structures
|
||||
|
||||
@ -1031,3 +1031,31 @@
|
||||
;; Clamp out-of-range positions
|
||||
(t/is (= [:b :c :d :a] (d/reorder [:a :b :c :d] 0 100)))
|
||||
(t/is (= [:a :b :c :d] (d/reorder [:a :b :c :d] -5 0))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Commonly used transducers
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(t/deftest xf-add-index-adds-index-to-each-item
|
||||
(t/is (= [{:id 1 ::d/index 0}
|
||||
{:id 2 ::d/index 1}
|
||||
{:id 3 ::d/index 2}]
|
||||
(into [] d/xf:add-index [{:id 1} {:id 2} {:id 3}]))))
|
||||
|
||||
(t/deftest xf-add-index-preserves-input-order
|
||||
(t/is (= [0 1 2]
|
||||
(mapv ::d/index (into [] d/xf:add-index [{:id :c} {:id :a} {:id :b}])))))
|
||||
|
||||
(t/deftest xf-add-index-handles-empty-and-nil-input
|
||||
(t/is (= [] (into [] d/xf:add-index [])))
|
||||
(t/is (= [] (into [] d/xf:add-index nil))))
|
||||
|
||||
(t/deftest xf-add-index-composes-with-other-transducers
|
||||
;; The index is assigned after previous stages run, so it always
|
||||
;; reflects the position in the output of the chain.
|
||||
(t/is (= [{:id 1 :keep? true ::d/index 0}
|
||||
{:id 3 :keep? true ::d/index 1}]
|
||||
(into [] (comp (filter :keep?) d/xf:add-index)
|
||||
[{:id 1 :keep? true}
|
||||
{:id 2 :keep? false}
|
||||
{:id 3 :keep? true}]))))
|
||||
|
||||
@ -710,6 +710,10 @@
|
||||
(let [show-content* (mf/use-state true)
|
||||
show-content? (deref show-content*)
|
||||
|
||||
interactions
|
||||
(mf/with-memo [interactions]
|
||||
(into [] d/xf:add-index interactions))
|
||||
|
||||
toggle-content
|
||||
(mf/use-fn
|
||||
#(swap! show-content* not))
|
||||
@ -745,7 +749,7 @@
|
||||
|
||||
(when show-content?
|
||||
[:div {:class (stl/css :content :content-interactions)}
|
||||
(for [[index interaction] (d/enumerate interactions)]
|
||||
(for [{:keys [::d/index] :as interaction} interactions]
|
||||
[:> interaction-item* {:key (str (:id shape) "-" index)
|
||||
:index index
|
||||
:shape shape
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user