From a79be052616e0c666a1e906278f4b6c8051ae6e5 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Thu, 4 Sep 2025 09:29:38 +0200 Subject: [PATCH] :bug: Fix selection and devtools problem (#7259) --- CHANGES.md | 2 + .../app/main/ui/workspace/sidebar/assets.cljs | 62 +++++++++++-------- 2 files changed, 38 insertions(+), 26 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 1eea345b41..819e2ea6b7 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,6 +12,8 @@ ### :bug: Bugs fixed +- Fix selection problems when devtools open [Taiga #11950](https://tree.taiga.io/project/penpot/issue/11950) + ## 2.10.0 (Unreleased) ### :rocket: Epics and highlights diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets.cljs index b04f5f4bf1..25df2103ea 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/assets.cljs @@ -137,21 +137,30 @@ on-menu-close (mf/use-fn #(swap! filters* assoc :open-menu false)) + ;; Memoize options to prevent infinite re-render loops when dev-tools are open. + ;; + ;; Problem: When dev-tools are open, they constantly monitor the application state, + ;; triggering frequent updates to okulary refs. This causes the parent component to + ;; re-render constantly, recreating the options array on every render. + ;; + ;; The context-menu* component has a mf/with-effect that depends on [options]. + ;; When options are recreated (even with identical content), the effect runs, + ;; updating the internal state, which triggers another re-render, creating + ;; an infinite loop: render -> new options -> effect -> state update -> render... options - [{:name (tr "workspace.assets.box-filter-all") - :id "all" - :handler on-section-filter-change} - {:name (tr "workspace.assets.components") - :id "components" - :handler on-section-filter-change} - - {:name (tr "workspace.assets.colors") - :id "colors" - :handler on-section-filter-change} - - {:name (tr "workspace.assets.typography") - :id "typographies" - :handler on-section-filter-change}]] + (mf/with-memo [on-section-filter-change] + [{:name (tr "workspace.assets.box-filter-all") + :id "all" + :handler on-section-filter-change} + {:name (tr "workspace.assets.components") + :id "components" + :handler on-section-filter-change} + {:name (tr "workspace.assets.colors") + :id "colors" + :handler on-section-filter-change} + {:name (tr "workspace.assets.typography") + :id "typographies" + :handler on-section-filter-change}])] [:article {:class (stl/css :assets-bar)} [:div {:class (stl/css :assets-header)} @@ -178,18 +187,19 @@ :class (stl/css-case :section-button true :opened menu-open?)} i/filter-icon]] - (when menu-open? - [:> context-menu* - {:on-close on-menu-close - :selectable true - :selected section - :show true - :fixed true - :min-width true - :width size - :top 158 - :left 18 - :options options}]) + + [:> context-menu* + {:on-close on-menu-close + :selectable true + :selected section + :show menu-open? + :fixed true + :min-width true + :width size + :top 158 + :left 18 + :options options}] + [:> icon-button* {:variant "ghost" :aria-label (tr "workspace.assets.sort") :on-click toggle-ordering