mirror of
https://github.com/penpot/penpot.git
synced 2026-04-25 11:18:36 +00:00
♻️ Extract use-portal-container hook to reduce duplication
The dedicated-container portal pattern was repeated across 7 components. Extract it into a reusable use-portal-container hook under app.main.ui.hooks. Signed-off-by: Andrey Antukh <niwi@niwi.nz>
This commit is contained in:
parent
ff60503ce6
commit
2905905a9f
@ -6,16 +6,12 @@
|
||||
|
||||
(ns app.main.ui.components.portal
|
||||
(:require
|
||||
[app.util.dom :as dom]
|
||||
[app.main.ui.hooks :as hooks]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(mf/defc portal-on-document*
|
||||
[{:keys [children]}]
|
||||
(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)))
|
||||
(let [container (hooks/use-portal-container)]
|
||||
(mf/portal
|
||||
(mf/html [:* children])
|
||||
container)))
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
[app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.main.ui.hooks :as hooks]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.keyboard :as kbd]
|
||||
[app.util.timers :as ts]
|
||||
@ -159,7 +160,7 @@
|
||||
|
||||
tooltip-ref (mf/use-ref nil)
|
||||
|
||||
container (mf/use-memo #(dom/create-element "div"))
|
||||
container (hooks/use-portal-container)
|
||||
|
||||
id
|
||||
(d/nilv id internal-id)
|
||||
@ -246,11 +247,6 @@
|
||||
content
|
||||
aria-label)})]
|
||||
|
||||
(mf/with-effect []
|
||||
(let [body (dom/get-body)]
|
||||
(dom/append-child! body container)
|
||||
#(dom/remove-child! body container)))
|
||||
|
||||
(mf/use-effect
|
||||
(mf/deps tooltip-id)
|
||||
(fn []
|
||||
|
||||
@ -380,6 +380,18 @@
|
||||
|
||||
state))
|
||||
|
||||
(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))
|
||||
|
||||
(defn use-dynamic-grid-item-width
|
||||
([] (use-dynamic-grid-item-width nil))
|
||||
([itemsize]
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
[app.common.data.macros :as dm]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.hooks :as hooks]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.keyboard :as k]
|
||||
[goog.events :as events]
|
||||
@ -83,11 +84,7 @@
|
||||
(mf/defc modal-container*
|
||||
{::mf/props :obj}
|
||||
[]
|
||||
(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)))
|
||||
(let [container (hooks/use-portal-container)]
|
||||
(when-let [modal (mf/deref ref:modal)]
|
||||
(mf/portal
|
||||
(mf/html [:> modal-wrapper* {:data modal :key (dm/str (:id modal))}])
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.dropdown :refer [dropdown]]
|
||||
[app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i]
|
||||
[app.main.ui.hooks :as hooks]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[app.util.timers :as timers]
|
||||
@ -516,12 +517,7 @@
|
||||
dropdown-direction-change* (mf/use-ref 0)
|
||||
top (+ (get-in mdata [:position :y]) 5)
|
||||
left (+ (get-in mdata [:position :x]) 5)
|
||||
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 (hooks/use-portal-container)]
|
||||
|
||||
(mf/use-effect
|
||||
(mf/deps is-open?)
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
[app.main.ui.ds.controls.shared.options-dropdown :refer [options-dropdown*]]
|
||||
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||
[app.main.ui.forms :as fc]
|
||||
[app.main.ui.hooks :as hooks]
|
||||
[app.main.ui.workspace.tokens.management.forms.controls.combobox-navigation :refer [use-navigation]]
|
||||
[app.main.ui.workspace.tokens.management.forms.controls.floating-dropdown :refer [use-floating-dropdown]]
|
||||
[app.main.ui.workspace.tokens.management.forms.controls.token-parsing :as tp]
|
||||
@ -92,7 +93,7 @@
|
||||
icon-button-ref (mf/use-ref nil)
|
||||
ref (or ref internal-ref)
|
||||
|
||||
container (mf/use-memo #(dom/create-element "div"))
|
||||
container (hooks/use-portal-container)
|
||||
|
||||
raw-tokens-by-type (mf/use-ctx muc/active-tokens-by-type)
|
||||
|
||||
@ -269,11 +270,6 @@
|
||||
(mf/with-effect [dropdown-options]
|
||||
(mf/set-ref-val! options-ref dropdown-options))
|
||||
|
||||
(mf/with-effect []
|
||||
(let [body (dom/get-body)]
|
||||
(dom/append-child! body container)
|
||||
#(dom/remove-child! body container)))
|
||||
|
||||
(mf/with-effect [is-open* ref wrapper-ref]
|
||||
(when is-open
|
||||
(let [handler (fn [event]
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.dropdown :refer [dropdown]]
|
||||
[app.main.ui.hooks :as hooks]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[okulary.core :as l]
|
||||
@ -35,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 (mf/use-memo #(dom/create-element "div"))
|
||||
container (hooks/use-portal-container)
|
||||
|
||||
delete-node (mf/use-fn
|
||||
(mf/deps mdata)
|
||||
@ -45,11 +46,6 @@
|
||||
(when node
|
||||
(on-delete-node node type)))))]
|
||||
|
||||
(mf/with-effect []
|
||||
(let [body (dom/get-body)]
|
||||
(dom/append-child! body container)
|
||||
#(dom/remove-child! body container)))
|
||||
|
||||
(mf/with-effect [is-open?]
|
||||
(when (and (not= 0 (mf/ref-val dropdown-direction-change*)) (= false is-open?))
|
||||
(reset! dropdown-direction* "down")
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
[app.main.ui.components.dropdown :refer [dropdown]]
|
||||
[app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i]
|
||||
[app.main.ui.ds.foundations.typography.text :refer [text*]]
|
||||
[app.main.ui.hooks :as hooks]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[cuerdas.core :as str]
|
||||
@ -113,12 +114,7 @@
|
||||
:is-open? true
|
||||
:rect rect))))))
|
||||
|
||||
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 (hooks/use-portal-container)]
|
||||
|
||||
[:div {:on-click on-open-dropdown
|
||||
:disabled (not can-edit?)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user