♻️ Extract floating position as hook

This commit is contained in:
Eva Marco
2026-02-18 18:29:11 +01:00
parent 32df7cc2f4
commit 1ca643514e
2 changed files with 73 additions and 56 deletions

View File

@@ -19,6 +19,8 @@
[app.main.ui.ds.controls.utilities.utils :as csu]
[app.main.ui.ds.foundations.assets.icon :as i]
[app.main.ui.forms :as fc]
[app.main.ui.workspace.tokens.management.forms.controls.floating :refer [use-floating-dropdown]]
[app.main.ui.workspace.tokens.management.forms.controls.navigation :refer [use-navigation]]
[app.util.dom :as dom]
[app.util.forms :as fm]
[app.util.i18n :refer [tr]]
@@ -173,36 +175,6 @@
raw-tokens-by-type (mf/use-ctx muc/active-tokens-by-type)
calculate-position
(mf/use-fn
(mf/deps dropdown-ref)
(fn [node]
(let [combobox-rect (dom/get-bounding-rect node)
dropdown-node (mf/ref-val dropdown-ref)
dropdown-height (if dropdown-node
(-> (dom/get-bounding-rect dropdown-node)
(:height))
0)
windows-height (-> (dom/get-window-size)
(:height))
space-below (- windows-height (:bottom combobox-rect))
space-above (:top combobox-rect)
place-top? (< space-below dropdown-height)
position (if place-top?
{:top (str (- space-above dropdown-height -14) "px")
:left (str (:left combobox-rect) "px")
:width (str (:width combobox-rect) "px")
:placement :top}
{:top (str (+ (:bottom combobox-rect) 4) "px")
:left (str (:left combobox-rect) "px")
:width (str (:width combobox-rect) "px")
:placement :bottom})]
(reset! dropdown-pos* position))))
filtered-tokens-by-type
(mf/with-memo [raw-tokens-by-type token-type]
(csu/filter-tokens-for-input raw-tokens-by-type token-type))
@@ -438,28 +410,6 @@
(dom/scroll-into-view-if-needed! node {:block "nearest"
:inline "nearest"})))))
(mf/with-effect [is-open dropdown-ref wrapper-ref]
(when is-open
(let [handler (fn [event]
(let [dropdown-node (mf/ref-val dropdown-ref)
target (dom/get-target event)]
(when (or (nil? dropdown-node)
(not (instance? js/Node target))
(not (.contains dropdown-node target)))
(js/requestAnimationFrame
(fn []
(let [wrapper-node (mf/ref-val wrapper-ref)]
(reset! dropdown-ready* true)
(calculate-position wrapper-node)))))))]
(handler nil)
(.addEventListener js/window "resize" handler)
(.addEventListener js/window "scroll" handler true)
(fn []
(.removeEventListener js/window "resize" handler)
(.removeEventListener js/window "scroll" handler true)))))
[:div {:ref wrapper-ref}
[:> ds/input* props]
(when ^boolean is-open
@@ -468,10 +418,10 @@
(mf/html
[:> options-dropdown* {:on-click on-option-click
:class (stl/css :dropdown)
:style {:visibility (if dropdown-ready "visible" "hidden")
:left (:left dropdown-pos)
:top (:top dropdown-pos)
:width (:width dropdown-pos)}
:style {:visibility (if (:ready? floating) "visible" "hidden")
:left (get-in floating [:style :left])
:top (get-in floating [:style :top])
:width (get-in floating [:style :width])}
:id listbox-id
:options options
:focused focused-id

View File

@@ -0,0 +1,67 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
(ns app.main.ui.workspace.tokens.management.forms.controls.floating
(:require
[app.util.dom :as dom]
[rumext.v2 :as mf]))
(defn use-floating-dropdown [is-open wrapper-ref dropdown-ref]
(let [pos* (mf/use-state nil)
ready* (mf/use-state false)
calculate-position
(fn [node]
(let [combobox-rect (dom/get-bounding-rect node)
dropdown-node (mf/ref-val dropdown-ref)
dropdown-height (if dropdown-node
(-> (dom/get-bounding-rect dropdown-node)
(:height))
0)
windows-height (-> (dom/get-window-size)
(:height))
space-below (- windows-height (:bottom combobox-rect))
space-above (:top combobox-rect)
place-top? (< space-below dropdown-height)
position (if place-top?
{:top (str (- space-above dropdown-height -14) "px")
:left (str (:left combobox-rect) "px")
:width (str (:width combobox-rect) "px")
:placement :top}
{:top (str (+ (:bottom combobox-rect) 4) "px")
:left (str (:left combobox-rect) "px")
:width (str (:width combobox-rect) "px")
:placement :bottom})]
(reset! ready* true)
(reset! pos* position)))]
(mf/with-effect [is-open dropdown-ref wrapper-ref]
(when is-open
(let [handler (fn [event]
(let [dropdown-node (mf/ref-val dropdown-ref)
target (dom/get-target event)]
(when (or (nil? dropdown-node)
(not (instance? js/Node target))
(not (.contains dropdown-node target)))
(js/requestAnimationFrame
(fn []
(let [wrapper-node (mf/ref-val wrapper-ref)]
(reset! ready* true)
(calculate-position wrapper-node)))))))]
(handler nil)
(.addEventListener js/window "resize" handler)
(.addEventListener js/window "scroll" handler true)
(fn []
(.removeEventListener js/window "resize" handler)
(.removeEventListener js/window "scroll" handler true)))))
{:style @pos*
:ready? @ready*}))