From f974e6456f5e036cd60ab2f1f970a988daf9abb5 Mon Sep 17 00:00:00 2001 From: Kimo Knowles Date: Wed, 17 Jan 2024 14:49:32 +0100 Subject: [PATCH] Use reagent idiom for react refs https://cljdoc.org/d/reagent/reagent/1.2.0/doc/frequently-asked-questions/how-do-i-use-react-s-refs- --- CHANGELOG.md | 12 +++++ src/re_com/dropdown.cljs | 101 ++++++++++++++++----------------------- src/re_com/popover.cljs | 18 +++---- 3 files changed, 62 insertions(+), 69 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2854821c..b9be3e1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,18 @@ > Committed but unreleased changes are put here, at the top. Older releases are detailed chronologically below. +## 2.16.2 (2024-01-16) + +#### Fixed + +- Replace deprecated `r/dom-node` with ref callbacks. (#329, #334) Thanks [RolT](https://github.com/RolT) for PR. + +## 2.16.1 (2024-01-16) + +#### Fixed + +- Cleanup & fixes for the new tree-select component. + ## 2.16.0 (2024-01-16) #### Added diff --git a/src/re_com/dropdown.cljs b/src/re_com/dropdown.cljs index d1764d7d..9c8c6a94 100644 --- a/src/re_com/dropdown.cljs +++ b/src/re_com/dropdown.cljs @@ -123,38 +123,27 @@ "Render a choice item and set up appropriate mouse events" [id label on-click internal-model] (let [mouse-over? (reagent/atom false) - ref (react/createRef)] + !ref (atom nil) + ref! (partial reset! !ref) + show! #(when (= @internal-model id) (show-selected-item @!ref))] (reagent/create-class - {:component-did-mount - (fn [this] - (let [node (.-current ref) - selected (= @internal-model id)] - (when selected (show-selected-item node)))) - - :component-did-update - (fn [this] - (let [node (.-current ref) - selected (= @internal-model id)] - (when selected (show-selected-item node)))) - - :display-name "choice-item" - - :reagent-render - (fn - [id label on-click internal-model] - (let [selected (= @internal-model id) - class (if selected - "highlighted" - (when @mouse-over? "mouseover"))] - [:li - {:class (str "active-result group-option " class) - :ref ref - :on-mouse-over (handler-fn (reset! mouse-over? true)) - :on-mouse-out (handler-fn (reset! mouse-over? false)) - :on-mouse-down (handler-fn - (on-click id) - (.preventDefault event))} ;; Prevent free-text input as well as the normal dropdown from loosing focus - label]))}))) + {:component-did-mount show! + :component-did-update show! + :display-name "choice-item" + :reagent-render (fn [id label on-click internal-model] + (let [selected (= @internal-model id) + class (if selected + "highlighted" + (when @mouse-over? "mouseover"))] + [:li + {:class (str "active-result group-option " class) + :ref ref! + :on-mouse-over (handler-fn (reset! mouse-over? true)) + :on-mouse-out (handler-fn (reset! mouse-over? false)) + :on-mouse-down (handler-fn + (on-click id) + (.preventDefault event))} ;; Prevent free-text input as well as the normal dropdown from losing focus + label]))}))) (defn make-choice-item [id-fn render-fn callback internal-model opt] @@ -162,39 +151,31 @@ markup (render-fn opt)] ^{:key (str id)} [choice-item id markup callback internal-model])) -(defn- filter-text-box-base - "Base function (before lifecycle metadata) to render a filter text box" - [ref] - (fn [filter-box? filter-text key-handler drop-showing? set-filter-text filter-placeholder] - [:div.chosen-search {:ref ref} - [:input - {:type "text" - :auto-complete "off" - :style (when-not filter-box? {:position "absolute" ;; When no filter box required, use it but hide it off screen - :width "0px" ;; The rest of these styles make the textbox invisible - :padding "0px" - :border "none"}) - :value @filter-text - :placeholder filter-placeholder - :on-change (handler-fn (set-filter-text (-> event .-target .-value))) - :on-key-down (handler-fn (when-not (key-handler event) - (.stopPropagation event) - (.preventDefault event))) ;; When key-handler returns false, preventDefault - :on-blur (handler-fn (reset! drop-showing? false))}]])) - (defn- filter-text-box "Render a filter text box" [filter-box? filter-text key-handler drop-showing? set-filter-text filter-placeholder] - (let [ref (react/createRef) - render-fn (filter-text-box-base ref)] + (let [!ref (atom nil) + ref! (partial reset! !ref) + focus! #(.focus (.-firstChild @!ref))] (reagent/create-class - {:component-did-mount (fn [this] - (let [node (.. ref -current -firstChild)] - (.focus node))) - :component-did-update (fn [this] - (let [node (.. ref -current -firstChild)] - (.focus node))) - :reagent-render render-fn}))) + {:component-did-mount focus! + :component-did-update focus! + :reagent-render (fn [filter-box? filter-text key-handler drop-showing? set-filter-text filter-placeholder] + [:div.chosen-search {:ref ref!} + [:input + {:type "text" + :auto-complete "off" + :style (when-not filter-box? {:position "absolute" ;; When no filter box required, use it but hide it off screen + :width "0px" ;; The rest of these styles make the textbox invisible + :padding "0px" + :border "none"}) + :value @filter-text + :placeholder filter-placeholder + :on-change (handler-fn (set-filter-text (-> event .-target .-value))) + :on-key-down (handler-fn (when-not (key-handler event) + (.stopPropagation event) + (.preventDefault event))) ;; When key-handler returns false, preventDefault + :on-blur (handler-fn (reset! drop-showing? false))}]])}))) (defn- dropdown-top "Render the top part of the dropdown, with the clickable area and the up/down arrow" diff --git a/src/re_com/popover.cljs b/src/re_com/popover.cljs index 50e8c665..88d5d40e 100644 --- a/src/re_com/popover.cljs +++ b/src/re_com/popover.cljs @@ -302,7 +302,8 @@ p-height (reagent/atom 0) pop-offset (reagent/atom 0) found-optimal (reagent/atom false) - pop-border-ref (react/createRef) + !pop-border (atom nil) + ref! (partial reset! !pop-border) calc-metrics (fn [pos] (let [popover-elem (get-element-by-id pop-id) [orientation arrow-pos] (split-keyword pos "-") @@ -320,9 +321,8 @@ :component-did-update (fn [this] - (let [pop-border-node (.-current pop-border-ref) - clipped? (popover-clipping pop-border-node) - anchor-node (-> pop-border-node .-parentNode .-parentNode .-parentNode)] ;; Get reference to rc-point-wrapper node + (let [clipped? (popover-clipping @!pop-border) + anchor-node (-> @!pop-border .-parentNode .-parentNode .-parentNode)] ;; Get reference to rc-point-wrapper node (when (and clipped? (not @found-optimal)) (reset! position (calculate-optimal-position (calc-element-midpoint anchor-node))) (reset! found-optimal true)) @@ -375,7 +375,7 @@ style)} (->attr args) attr - {:ref pop-border-ref}) + {:ref ref!}) [popover-arrow orientation @pop-offset arrow-length arrow-width grey-arrow? tooltip-style? popover-color popover-border-color parts] (when title title) (into [:div @@ -434,12 +434,12 @@ (validate-args-macro popover-content-wrapper-args-desc args) (let [left-offset (reagent/atom 0) top-offset (reagent/atom 0) - ref (react/createRef) + !ref (atom nil) + ref! (partial reset! !ref) position-no-clip-popover (fn position-no-clip-popover [this] (when no-clip? - (let [node (.-current ref) - popover-point-node (.-parentNode node) ;; Get reference to rc-popover-point node + (let [popover-point-node (.-parentNode @!ref) ;; Get reference to rc-popover-point node bounding-rect (.getBoundingClientRect popover-point-node)] ;; The modern magical way of getting offsetLeft and offsetTop. Returns this: https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIDOMClientRect (reset! left-offset (.-left bounding-rect)) (reset! top-offset (.-top bounding-rect)))))] @@ -474,7 +474,7 @@ style)} (->attr args) attr - {:ref ref}) + {:ref ref!}) (when (and (deref-or-value showing-injected?) on-cancel) [backdrop :src (at)