♻️ Refactor sidebar focus timer handling into helpers

Extract the repeated dispose-before-schedule focus idiom into
schedule-focus-by-id! (sidebar.cljs) and focus-and-untabbable!
(app.util.dom). Replaces the six duplicated blocks and adds
mf/use-effect unmount cleanup to dispose any pending timer in the
three sidebar components, closing the remaining leak noted in the
original fix.

AI-assisted-by: opencode/hy3-free
This commit is contained in:
Andrey Antukh 2026-07-11 14:51:44 +00:00
parent efc3ace448
commit 1213dc128d
2 changed files with 28 additions and 58 deletions

View File

@ -94,6 +94,14 @@
(def ^:private ^:svg-id penpot-logo-icon "penpot-logo-icon")
(def ^:private ^:svg-id penpot-logo-icon-subtle "penpot-logo-subtle")
(defn schedule-focus-by-id!
[ref element-id]
(when-let [h @ref]
(ts/dispose! h))
(mf/set-ref-val! ref
(ts/schedule
#(dom/focus-and-untabbable! (dom/get-element element-id)))))
(mf/defc sidebar-project*
{::mf/private true}
[{:keys [item is-selected]}]
@ -114,6 +122,8 @@
focus-timer-ref (mf/use-ref nil)
_ (mf/use-effect (fn [] #(some-> @focus-timer-ref ts/dispose!)))
on-click
(mf/use-fn
(mf/deps project-id)
@ -126,16 +136,8 @@
(fn [event]
(when (kbd/enter? event)
(st/emit!
(dcm/go-to-dashboard-files :project-id project-id))
(when-let [h @focus-timer-ref]
(ts/dispose! h))
(mf/set-ref-val! focus-timer-ref
(ts/schedule
(fn []
(when-let [title (dom/get-element (str project-id))]
(dom/set-attribute! title "tabindex" "0")
(dom/focus! title)
(dom/set-attribute! title "tabindex" "-1"))))))))
(dcm/go-to-dashboard-files :project-id project-id)
(schedule-focus-by-id! focus-timer-ref (str project-id))))))
on-menu-click
(mf/use-fn
@ -235,6 +237,8 @@
focus-timer-ref (mf/use-ref nil)
_ (mf/use-effect (fn [] #(some-> @focus-timer-ref ts/dispose!)))
on-search-blur
(mf/use-fn
(fn [_]
@ -261,16 +265,7 @@
(mf/use-fn
(fn [e]
(when (kbd/enter? e)
(when-let [h @focus-timer-ref]
(ts/dispose! h))
(mf/set-ref-val! focus-timer-ref
(ts/schedule
(fn []
(let [search-title (dom/get-element (str "dashboard-search-title"))]
(when search-title
(dom/set-attribute! search-title "tabindex" "0")
(dom/focus! search-title)
(dom/set-attribute! search-title "tabindex" "-1"))))))
(schedule-focus-by-id! focus-timer-ref "dashboard-search-title")
(dom/prevent-default e)
(dom/stop-propagation e))))
@ -960,6 +955,8 @@
focus-timer-ref (mf/use-ref nil)
_ (mf/use-effect (fn [] #(some-> @focus-timer-ref ts/dispose!)))
go-projects
(mf/use-fn #(st/emit! (dcm/go-to-dashboard-recent)))
@ -969,15 +966,7 @@
(fn []
(st/emit!
(dcm/go-to-dashboard-recent :team-id team-id))
(when-let [h @focus-timer-ref]
(ts/dispose! h))
(mf/set-ref-val! focus-timer-ref
(ts/schedule
(fn []
(when-let [projects-title (dom/get-element "dashboard-projects-title")]
(dom/set-attribute! projects-title "tabindex" "0")
(dom/focus! projects-title)
(dom/set-attribute! projects-title "tabindex" "-1")))))))
(schedule-focus-by-id! focus-timer-ref "dashboard-projects-title")))
go-fonts
(mf/use-fn
@ -990,16 +979,7 @@
(fn []
(st/emit!
(dcm/go-to-dashboard-fonts :team-id team-id))
(when-let [h @focus-timer-ref]
(ts/dispose! h))
(mf/set-ref-val! focus-timer-ref
(ts/schedule
(fn []
(let [font-title (dom/get-element "dashboard-fonts-title")]
(when font-title
(dom/set-attribute! font-title "tabindex" "0")
(dom/focus! font-title)
(dom/set-attribute! font-title "tabindex" "-1"))))))))
(schedule-focus-by-id! focus-timer-ref "dashboard-fonts-title")))
go-drafts
(mf/use-fn
@ -1012,15 +992,7 @@
(mf/deps team-id default-project-id)
(fn []
(st/emit! (dcm/go-to-dashboard-files :team-id team-id :project-id default-project-id))
(when-let [h @focus-timer-ref]
(ts/dispose! h))
(mf/set-ref-val! focus-timer-ref
(ts/schedule
(fn []
(when-let [title (dom/get-element "dashboard-drafts-title")]
(dom/set-attribute! title "tabindex" "0")
(dom/focus! title)
(dom/set-attribute! title "tabindex" "-1")))))))
(schedule-focus-by-id! focus-timer-ref "dashboard-drafts-title")))
go-libs
(mf/use-fn
@ -1033,16 +1005,7 @@
(fn []
(st/emit!
(dcm/go-to-dashboard-libraries :team-id team-id))
(when-let [h @focus-timer-ref]
(ts/dispose! h))
(mf/set-ref-val! focus-timer-ref
(ts/schedule
(fn []
(let [libs-title (dom/get-element "dashboard-libraries-title")]
(when libs-title
(dom/set-attribute! libs-title "tabindex" "0")
(dom/focus! libs-title)
(dom/set-attribute! libs-title "tabindex" "-1"))))))))
(schedule-focus-by-id! focus-timer-ref "dashboard-libraries-title")))
pinned-projects
(mf/with-memo [projects]

View File

@ -699,6 +699,13 @@
(when (some? node)
(.setAttribute node attr value)))
(defn focus-and-untabbable!
[^js node]
(when (some? node)
(set-attribute! node "tabindex" "0")
(focus! node)
(set-attribute! node "tabindex" "-1")))
(defn set-style!
[^js node ^string style value]
(when (some? node)