mirror of
https://github.com/penpot/penpot.git
synced 2026-04-25 11:18:36 +00:00
♻️ Use shared singleton containers for React portals (#8957)
Refactor use-portal-container to allocate one persistent <div> per logical category (:modal, :popup, :tooltip, :default) instead of creating a new div for every component instance. This keeps the DOM clean with at most four fixed portal containers and eliminates the arbitrary growth of empty <div> elements on document.body while preserving the removeChild race condition fix.
This commit is contained in:
parent
b3645658fb
commit
c39609b991
@ -160,7 +160,7 @@
|
||||
|
||||
tooltip-ref (mf/use-ref nil)
|
||||
|
||||
container (hooks/use-portal-container)
|
||||
container (hooks/use-portal-container :tooltip)
|
||||
|
||||
id
|
||||
(d/nilv id internal-id)
|
||||
|
||||
@ -380,17 +380,35 @@
|
||||
|
||||
state))
|
||||
|
||||
(defn- get-or-create-portal-container
|
||||
"Returns the singleton container div for the given category, creating
|
||||
and appending it to document.body on first access."
|
||||
[category]
|
||||
(let [body (dom/get-body)
|
||||
id (str "portal-container-" category)]
|
||||
(or (dom/query body (str "#" id))
|
||||
(let [container (dom/create-element "div")]
|
||||
(dom/set-attribute! container "id" id)
|
||||
(dom/append-child! body container)
|
||||
container))))
|
||||
|
||||
(defn use-portal-container
|
||||
"Creates a dedicated div container for React portals. The container
|
||||
is appended to document.body on mount and removed on cleanup, preventing
|
||||
removeChild race conditions when multiple portals target the same body."
|
||||
[]
|
||||
(let [container (mf/use-memo #(dom/create-element "div"))]
|
||||
(mf/with-effect []
|
||||
(let [body (dom/get-body)]
|
||||
(dom/append-child! body container)
|
||||
#(dom/remove-child! body container)))
|
||||
container))
|
||||
"Returns a shared singleton container div for React portals, identified
|
||||
by a logical category. Available categories:
|
||||
|
||||
:modal — modal dialogs
|
||||
:popup — popups, dropdowns, context menus
|
||||
:tooltip — tooltips
|
||||
:default — general portal use (default)
|
||||
|
||||
All portals in the same category share one <div> on document.body,
|
||||
keeping the DOM clean and avoiding removeChild race conditions."
|
||||
([]
|
||||
(use-portal-container :default))
|
||||
([category]
|
||||
(let [category (name category)]
|
||||
(mf/with-memo [category]
|
||||
(get-or-create-portal-container category)))))
|
||||
|
||||
(defn use-dynamic-grid-item-width
|
||||
([] (use-dynamic-grid-item-width nil))
|
||||
|
||||
@ -84,7 +84,7 @@
|
||||
(mf/defc modal-container*
|
||||
{::mf/props :obj}
|
||||
[]
|
||||
(let [container (hooks/use-portal-container)]
|
||||
(let [container (hooks/use-portal-container :modal)]
|
||||
(when-let [modal (mf/deref ref:modal)]
|
||||
(mf/portal
|
||||
(mf/html [:> modal-wrapper* {:data modal :key (dm/str (:id modal))}])
|
||||
|
||||
@ -517,7 +517,7 @@
|
||||
dropdown-direction-change* (mf/use-ref 0)
|
||||
top (+ (get-in mdata [:position :y]) 5)
|
||||
left (+ (get-in mdata [:position :x]) 5)
|
||||
container (hooks/use-portal-container)]
|
||||
container (hooks/use-portal-container :popup)]
|
||||
|
||||
(mf/use-effect
|
||||
(mf/deps is-open?)
|
||||
|
||||
@ -36,7 +36,7 @@
|
||||
dropdown-direction-change* (mf/use-ref 0)
|
||||
top (+ (get-in mdata [:position :y]) 5)
|
||||
left (+ (get-in mdata [:position :x]) 5)
|
||||
container (hooks/use-portal-container)
|
||||
container (hooks/use-portal-container :popup)
|
||||
|
||||
delete-node (mf/use-fn
|
||||
(mf/deps mdata)
|
||||
|
||||
@ -114,7 +114,7 @@
|
||||
:is-open? true
|
||||
:rect rect))))))
|
||||
|
||||
container (hooks/use-portal-container)]
|
||||
container (hooks/use-portal-container :popup)]
|
||||
|
||||
[:div {:on-click on-open-dropdown
|
||||
:disabled (not can-edit?)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user