mirror of
https://github.com/penpot/penpot.git
synced 2026-06-01 05:00:17 +00:00
♻️ Extract use-portal-container hook to reduce duplication (#8798)
The dedicated-container portal pattern was repeated across 6 components. Extract it into a reusable use-portal-container hook under app.main.ui.hooks.
This commit is contained in:
parent
85cfb8161a
commit
5fca9457cf
@ -6,16 +6,12 @@
|
|||||||
|
|
||||||
(ns app.main.ui.components.portal
|
(ns app.main.ui.components.portal
|
||||||
(:require
|
(:require
|
||||||
[app.util.dom :as dom]
|
[app.main.ui.hooks :as hooks]
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
(mf/defc portal-on-document*
|
(mf/defc portal-on-document*
|
||||||
[{:keys [children]}]
|
[{:keys [children]}]
|
||||||
(let [container (mf/use-memo #(dom/create-element "div"))]
|
(let [container (hooks/use-portal-container)]
|
||||||
(mf/with-effect []
|
|
||||||
(let [body (dom/get-body)]
|
|
||||||
(dom/append-child! body container)
|
|
||||||
#(dom/remove-child! body container)))
|
|
||||||
(mf/portal
|
(mf/portal
|
||||||
(mf/html [:* children])
|
(mf/html [:* children])
|
||||||
container)))
|
container)))
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
[app.main.style :as stl])
|
[app.main.style :as stl])
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
|
[app.main.ui.hooks :as hooks]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.keyboard :as kbd]
|
[app.util.keyboard :as kbd]
|
||||||
[app.util.timers :as ts]
|
[app.util.timers :as ts]
|
||||||
@ -159,6 +160,8 @@
|
|||||||
|
|
||||||
tooltip-ref (mf/use-ref nil)
|
tooltip-ref (mf/use-ref nil)
|
||||||
|
|
||||||
|
container (hooks/use-portal-container)
|
||||||
|
|
||||||
id
|
id
|
||||||
(d/nilv id internal-id)
|
(d/nilv id internal-id)
|
||||||
|
|
||||||
@ -283,4 +286,4 @@
|
|||||||
[:div {:class (stl/css :tooltip-content)} content]
|
[:div {:class (stl/css :tooltip-content)} content]
|
||||||
[:div {:class (stl/css :tooltip-arrow)
|
[:div {:class (stl/css :tooltip-arrow)
|
||||||
:id "tooltip-arrow"}]]])
|
:id "tooltip-arrow"}]]])
|
||||||
(.-body js/document)))]))
|
container))]))
|
||||||
|
|||||||
@ -380,6 +380,18 @@
|
|||||||
|
|
||||||
state))
|
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
|
(defn use-dynamic-grid-item-width
|
||||||
([] (use-dynamic-grid-item-width nil))
|
([] (use-dynamic-grid-item-width nil))
|
||||||
([itemsize]
|
([itemsize]
|
||||||
|
|||||||
@ -10,6 +10,7 @@
|
|||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.main.data.modal :as modal]
|
[app.main.data.modal :as modal]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
|
[app.main.ui.hooks :as hooks]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.keyboard :as k]
|
[app.util.keyboard :as k]
|
||||||
[goog.events :as events]
|
[goog.events :as events]
|
||||||
@ -83,7 +84,8 @@
|
|||||||
(mf/defc modal-container*
|
(mf/defc modal-container*
|
||||||
{::mf/props :obj}
|
{::mf/props :obj}
|
||||||
[]
|
[]
|
||||||
(when-let [modal (mf/deref ref:modal)]
|
(let [container (hooks/use-portal-container)]
|
||||||
(mf/portal
|
(when-let [modal (mf/deref ref:modal)]
|
||||||
(mf/html [:> modal-wrapper* {:data modal :key (dm/str (:id modal))}])
|
(mf/portal
|
||||||
(dom/get-body))))
|
(mf/html [:> modal-wrapper* {:data modal :key (dm/str (:id modal))}])
|
||||||
|
container))))
|
||||||
|
|||||||
@ -20,6 +20,7 @@
|
|||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.ui.components.dropdown :refer [dropdown]]
|
[app.main.ui.components.dropdown :refer [dropdown]]
|
||||||
[app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i]
|
[app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i]
|
||||||
|
[app.main.ui.hooks :as hooks]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.i18n :refer [tr]]
|
[app.util.i18n :refer [tr]]
|
||||||
[app.util.timers :as timers]
|
[app.util.timers :as timers]
|
||||||
@ -515,7 +516,8 @@
|
|||||||
dropdown-direction (deref dropdown-direction*)
|
dropdown-direction (deref dropdown-direction*)
|
||||||
dropdown-direction-change* (mf/use-ref 0)
|
dropdown-direction-change* (mf/use-ref 0)
|
||||||
top (+ (get-in mdata [:position :y]) 5)
|
top (+ (get-in mdata [:position :y]) 5)
|
||||||
left (+ (get-in mdata [:position :x]) 5)]
|
left (+ (get-in mdata [:position :x]) 5)
|
||||||
|
container (hooks/use-portal-container)]
|
||||||
|
|
||||||
(mf/use-effect
|
(mf/use-effect
|
||||||
(mf/deps is-open?)
|
(mf/deps is-open?)
|
||||||
@ -554,4 +556,4 @@
|
|||||||
:on-context-menu prevent-default}
|
:on-context-menu prevent-default}
|
||||||
(when mdata
|
(when mdata
|
||||||
[:& token-context-menu-tree (assoc mdata :width @width :on-delete-token on-delete-token)])]])
|
[:& token-context-menu-tree (assoc mdata :width @width :on-delete-token on-delete-token)])]])
|
||||||
(dom/get-body)))))
|
container))))
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.ui.components.dropdown :refer [dropdown]]
|
[app.main.ui.components.dropdown :refer [dropdown]]
|
||||||
|
[app.main.ui.hooks :as hooks]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.i18n :refer [tr]]
|
[app.util.i18n :refer [tr]]
|
||||||
[okulary.core :as l]
|
[okulary.core :as l]
|
||||||
@ -35,6 +36,7 @@
|
|||||||
dropdown-direction-change* (mf/use-ref 0)
|
dropdown-direction-change* (mf/use-ref 0)
|
||||||
top (+ (get-in mdata [:position :y]) 5)
|
top (+ (get-in mdata [:position :y]) 5)
|
||||||
left (+ (get-in mdata [:position :x]) 5)
|
left (+ (get-in mdata [:position :x]) 5)
|
||||||
|
container (hooks/use-portal-container)
|
||||||
|
|
||||||
delete-node (mf/use-fn
|
delete-node (mf/use-fn
|
||||||
(mf/deps mdata)
|
(mf/deps mdata)
|
||||||
@ -80,4 +82,4 @@
|
|||||||
:type "button"
|
:type "button"
|
||||||
:on-click delete-node}
|
:on-click delete-node}
|
||||||
(tr "labels.delete")]]])]])
|
(tr "labels.delete")]]])]])
|
||||||
(dom/get-body)))))
|
container))))
|
||||||
|
|||||||
@ -17,6 +17,7 @@
|
|||||||
[app.main.ui.components.dropdown :refer [dropdown]]
|
[app.main.ui.components.dropdown :refer [dropdown]]
|
||||||
[app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i]
|
[app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i]
|
||||||
[app.main.ui.ds.foundations.typography.text :refer [text*]]
|
[app.main.ui.ds.foundations.typography.text :refer [text*]]
|
||||||
|
[app.main.ui.hooks :as hooks]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.i18n :refer [tr]]
|
[app.util.i18n :refer [tr]]
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
@ -111,7 +112,9 @@
|
|||||||
(let [rect (dom/get-bounding-rect node)]
|
(let [rect (dom/get-bounding-rect node)]
|
||||||
(swap! state* assoc
|
(swap! state* assoc
|
||||||
:is-open? true
|
:is-open? true
|
||||||
:rect rect))))))]
|
:rect rect))))))
|
||||||
|
|
||||||
|
container (hooks/use-portal-container)]
|
||||||
|
|
||||||
[:div {:on-click on-open-dropdown
|
[:div {:on-click on-open-dropdown
|
||||||
:disabled (not can-edit?)
|
:disabled (not can-edit?)
|
||||||
@ -140,4 +143,4 @@
|
|||||||
[:& theme-options {:active-theme-paths active-theme-paths
|
[:& theme-options {:active-theme-paths active-theme-paths
|
||||||
:themes themes
|
:themes themes
|
||||||
:on-close on-close-dropdown}]]])
|
:on-close on-close-dropdown}]]])
|
||||||
(dom/get-body)))]))
|
container))]))
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user