diff --git a/frontend/src/app/main/ui/components/select.cljs b/frontend/src/app/main/ui/components/select.cljs index d96069516..e3ae25732 100644 --- a/frontend/src/app/main/ui/components/select.cljs +++ b/frontend/src/app/main/ui/components/select.cljs @@ -15,6 +15,7 @@ [app.util.dom :as dom] [rumext.v2 :as mf])) + (defn- as-key-value [item] (if (map? item) @@ -37,6 +38,10 @@ current-label (get label-index current-value) is-open? (:is-open? state) + dropdown-element* (mf/use-ref nil) + dropdown-direction* (mf/use-state "down") + dropdown-direction-change* (mf/use-ref 0) + open-dropdown (mf/use-fn (mf/deps disabled) @@ -81,6 +86,13 @@ (mf/with-effect [default-value] (swap! state* assoc :current-value default-value)) + (mf/with-effect [is-open? dropdown-element*] + (let [dropdown-element (mf/ref-val dropdown-element*)] + (when (and (= 0 (mf/ref-val dropdown-direction-change*)) dropdown-element) + (let [is-outside? (dom/is-element-outside? dropdown-element)] + (reset! dropdown-direction* (if is-outside? "up" "down")) + (mf/set-ref-val! dropdown-direction-change* (inc (mf/ref-val dropdown-direction-change*))))))) + (let [selected-option (first (filter #(= (:value %) default-value) options)) current-icon (:icon selected-option) current-icon-ref (i/key->icon current-icon)] @@ -93,7 +105,7 @@ [:span {:class (stl/css :current-label)} current-label] [:span {:class (stl/css :dropdown-button)} i/arrow-refactor] [:& dropdown {:show is-open? :on-close close-dropdown} - [:ul {:class (dm/str dropdown-class " " (stl/css :custom-select-dropdown))} + [:ul {:ref dropdown-element* :data-direction @dropdown-direction* :class (dm/str dropdown-class " " (stl/css :custom-select-dropdown))} (for [[index item] (d/enumerate options)] (if (= :separator item) [:li {:class (dom/classnames (stl/css :separator) true) diff --git a/frontend/src/app/main/ui/components/select.scss b/frontend/src/app/main/ui/components/select.scss index 0817253a6..734fb3a40 100644 --- a/frontend/src/app/main/ui/components/select.scss +++ b/frontend/src/app/main/ui/components/select.scss @@ -51,6 +51,12 @@ border-top: $s-1 solid var(--dropdown-separator-color); } } + + .custom-select-dropdown[data-direction="up"] { + bottom: $s-32; + top: auto; + } + .checked-element { @extend .dropdown-element-base; .icon { diff --git a/frontend/src/app/util/dom.cljs b/frontend/src/app/util/dom.cljs index efe510868..df29c9da7 100644 --- a/frontend/src/app/util/dom.cljs +++ b/frontend/src/app/util/dom.cljs @@ -24,6 +24,7 @@ cljs.core/IDeref (-deref [it] (.getBrowserEvent it))) +(declare get-window-size) (defn browser-event? [o] @@ -411,6 +412,19 @@ :width (.-width ^js rect) :height (.-height ^js rect)})) +(defn is-bounding-rect-outside? + [rect] + (let [{:keys [left top right bottom]} rect + {:keys [width height]} (get-window-size)] + (or (< left 0) + (< top 0) + (> right width) + (> bottom height)))) + +(defn is-element-outside? + [element] + (is-bounding-rect-outside? (get-bounding-rect element))) + (defn bounding-rect->rect [rect] (when (some? rect)