floating_ui_dioxus/
use_floating.rs1use std::{cell::RefCell, rc::Rc};
2
3use dioxus::{core::use_drop, prelude::*, web::WebEventExt};
4use floating_ui_dom::{
5 ComputePositionConfig, MiddlewareData, Placement, Strategy, compute_position,
6};
7
8use crate::{
9 FloatingStyles, UseFloatingOptions, UseFloatingReturn, WhileElementsMountedCleanupFn,
10 utils::{get_dpr::get_dpr, round_by_dpr::round_by_dpr},
11};
12
13pub fn use_floating(
15 reference: Signal<Option<Rc<MountedData>>>,
16 floating: Signal<Option<Rc<MountedData>>>,
17 options: UseFloatingOptions,
18) -> UseFloatingReturn {
19 let open_option = use_memo(move || options.open.unwrap_or(true));
20 let placement_option = use_memo(move || options.placement.unwrap_or(Placement::Bottom));
21 let strategy_option = use_memo(move || options.strategy.unwrap_or(Strategy::Absolute));
22 let middleware_option = use_memo(move || options.middleware.clone().unwrap_or_default());
23 let transform_option = use_memo(move || options.transform.unwrap_or(true));
24 let while_elements_mounted_option = options.while_elements_mounted;
25
26 let mut x = use_signal(|| 0.0);
27 let mut y = use_signal(|| 0.0);
28 #[expect(clippy::redundant_closure)]
29 let mut strategy = use_signal(|| strategy_option());
30 #[expect(clippy::redundant_closure)]
31 let mut placement = use_signal(|| placement_option());
32 let mut middleware_data = use_signal(MiddlewareData::default);
33 let mut is_positioned = use_signal(|| false);
34 let floating_styles = use_memo(move || {
35 let initial_styles = FloatingStyles {
36 position: strategy(),
37 top: "0".to_owned(),
38 left: "0".to_owned(),
39 transform: None,
40 will_change: None,
41 };
42
43 match floating().map(|floating| floating.as_web_event()) {
44 Some(floating_element) => {
45 let x_val = round_by_dpr(&floating_element, x());
46 let y_val = round_by_dpr(&floating_element, y());
47
48 if transform_option() {
49 FloatingStyles {
50 transform: Some(format!("translate({x_val}px, {y_val}px)")),
51 will_change: (get_dpr(&floating_element) >= 1.5)
52 .then_some("transform".to_owned()),
53 ..initial_styles
54 }
55 } else {
56 FloatingStyles {
57 left: format!("{x_val}px"),
58 top: format!("{y_val}px"),
59 ..initial_styles
60 }
61 }
62 }
63 _ => initial_styles,
64 }
65 });
66
67 let update = use_callback(move |_| {
68 if let Some(reference_element) = reference().map(|reference| reference.as_web_event())
69 && let Some(floating_element) = floating().map(|floating| floating.as_web_event())
70 {
71 let config = ComputePositionConfig {
72 placement: Some(placement_option()),
73 strategy: Some(strategy_option()),
74 middleware: Some(middleware_option()),
75 };
76
77 let open = open_option();
78
79 let position = compute_position((&reference_element).into(), &floating_element, config);
80 x.set(position.x);
81 y.set(position.y);
82 strategy.set(position.strategy);
83 placement.set(position.placement);
84 middleware_data.set(position.middleware_data);
85 is_positioned.set(open);
90 }
91 });
92
93 let while_elements_mounted_cleanup = use_hook::<
94 Rc<RefCell<Option<Rc<WhileElementsMountedCleanupFn>>>>,
95 >(|| Rc::new(RefCell::new(None)));
96
97 let cleanup = use_callback({
98 let while_elements_mounted_cleanup = while_elements_mounted_cleanup.clone();
99
100 move |_| {
101 if let Some(while_elements_mounted_cleanup) = while_elements_mounted_cleanup.take() {
102 while_elements_mounted_cleanup();
103 }
104 }
105 });
106
107 let attach = use_callback(move |_| {
108 cleanup.call(());
109
110 if let Some(while_elements_mounted) = &while_elements_mounted_option {
111 if let Some(reference_element) = reference().map(|reference| reference.as_web_event())
112 && let Some(floating_element) = floating().map(|floating| floating.as_web_event())
113 {
114 while_elements_mounted_cleanup.replace(Some(Rc::new((*while_elements_mounted)(
115 (&reference_element).into(),
116 &floating_element,
117 Rc::new(move || {
118 update.call(());
119 }),
120 ))));
121 }
122 } else {
123 update.call(());
124 }
125 });
126
127 let reset = use_callback(move |_| {
128 if open_option() {
129 is_positioned.set(false);
130 }
131 });
132
133 use_effect(move || {
134 _ = open_option();
135 _ = placement_option();
136 _ = strategy_option();
137 _ = middleware_option();
138
139 update.call(());
140 });
141
142 use_effect(move || {
143 _ = reference();
144 _ = floating();
145
146 attach(());
147 });
148
149 use_effect(move || {
150 _ = open_option();
151
152 reset.call(());
153 });
154
155 use_drop(move || {
156 cleanup.call(());
157 });
158
159 UseFloatingReturn {
160 x,
161 y,
162 placement,
163 strategy,
164 middleware_data,
165 is_positioned,
166 floating_styles,
167 update,
168 }
169}