();
+
+ let UseFloatingReturn {
+ floating_styles, ..
+ } = use_floating(reference, floating, UseFloatingOptions::default().into());
+
+ view! {
+
Reference
+
+ Floating
+
+ }
+}
diff --git a/packages/leptos/example/src/main.rs b/packages/leptos/example/src/main.rs
new file mode 100644
index 0000000..5b8c173
--- /dev/null
+++ b/packages/leptos/example/src/main.rs
@@ -0,0 +1,13 @@
+// TODO: remove after leptos is fixed
+#![allow(clippy::empty_docs)]
+
+mod app;
+
+use crate::app::App;
+
+pub fn main() {
+ _ = console_log::init_with_level(log::Level::Debug);
+ console_error_panic_hook::set_once();
+
+ leptos::mount_to_body(App);
+}
diff --git a/packages/leptos/src/lib.rs b/packages/leptos/src/lib.rs
index b731a03..45e352b 100644
--- a/packages/leptos/src/lib.rs
+++ b/packages/leptos/src/lib.rs
@@ -1,3 +1,6 @@
+mod types;
mod use_floating;
+mod utils;
+pub use types::*;
pub use use_floating::*;
diff --git a/packages/leptos/src/types.rs b/packages/leptos/src/types.rs
new file mode 100644
index 0000000..e7b6d70
--- /dev/null
+++ b/packages/leptos/src/types.rs
@@ -0,0 +1,97 @@
+use floating_ui_dom::{Middleware, MiddlewareData, Placement, Strategy};
+use leptos::{Attribute, IntoAttribute, Signal};
+use web_sys::{Element, Window};
+
+/// Options for [`use_floating`].
+#[derive(Clone, Default)]
+pub struct UseFloatingOptions<'a> {
+ /// Represents the open/close state of the floating element.
+ ///
+ /// Defaults to `true`.
+ pub open: Option
,
+
+ /// Where to place the floating element relative to the reference element.
+ ///
+ /// Defaults to [`Placement::Bottom`].
+ pub placement: Option,
+
+ /// The strategy to use when positioning the floating element.
+ ///
+ /// Defaults to [`Strategy::Absolute`].
+ pub strategy: Option,
+
+ /// Array of middleware objects to modify the positioning or provide data for rendering.
+ ///
+ /// Defaults to an empty vector.
+ pub middleware: Option>>,
+
+ /// Whether to use `transform` for positioning instead of `top` and `left` in the `floatingStyles` object.
+ ///
+ /// Defaults to `true`.
+ pub transform: Option,
+
+ /// Callback to handle mounting/unmounting of the elements.
+ ///
+ ///Detauls to [`Option::None`].
+ pub while_elements_mounted: Option, // TODO: type
+}
+
+#[derive(Clone, Debug, PartialEq)]
+pub struct FloatingStyles {
+ pub position: Strategy,
+ pub top: String,
+ pub left: String,
+ pub transform: Option,
+ pub will_change: Option,
+}
+
+impl IntoAttribute for FloatingStyles {
+ fn into_attribute(self) -> Attribute {
+ Attribute::String(
+ format!(
+ "position: {:?}; top: {}; left: {};{}{}",
+ self.position,
+ self.top,
+ self.left,
+ self.transform
+ .map_or("".into(), |transform| format!(" transform: {};", transform),),
+ self.will_change.map_or("".into(), |will_change| format!(
+ " will-change: {};",
+ will_change
+ ))
+ )
+ .into(),
+ )
+ }
+
+ fn into_attribute_boxed(self: Box) -> Attribute {
+ self.into_attribute()
+ }
+}
+
+#[derive(Debug)]
+pub struct UseFloatingReturn {
+ /// The x-coord of the floating element.
+ pub x: Signal,
+
+ /// The y-coord of the floating element.
+ pub y: Signal,
+
+ /// The stateful placement, which can be different from the initial `placement` passed as options.
+ pub placement: Signal,
+
+ /// The strategy to use when positioning the floating element.
+ pub strategy: Signal,
+
+ /// Additional data from middleware.
+ pub middleware_data: Signal,
+
+ /// Indicates if the floating element has been positioned.
+ pub is_positioned: Signal,
+
+ /// CSS styles to apply to the floating element to position it.
+ pub floating_styles: Signal,
+
+ /// The function to update floating position manually.
+ pub update: bool, // TODO: type
+}
diff --git a/packages/leptos/src/use_floating.rs b/packages/leptos/src/use_floating.rs
index 53b5cf7..a75ef55 100644
--- a/packages/leptos/src/use_floating.rs
+++ b/packages/leptos/src/use_floating.rs
@@ -1,60 +1,137 @@
-use floating_ui_dom::{Middleware, MiddlewareData, Placement, Strategy};
-use leptos::{create_signal, MaybeSignal};
-use web_sys::{Element, Window};
-
-/// Options for [`use_floating`].
-#[derive(Clone, Default)]
-pub struct UseFloatingOptions<'a> {
- /// Where to place the floating element relative to the reference element.
- ///
- /// Defaults to [`Placement::Bottom`].
- pub placement: Option,
-
- /// The strategy to use when positioning the floating element.
- ///
- /// Defaults to [`Strategy::Absolute`].
- pub strategy: Option,
-
- /// Array of middleware objects to modify the positioning or provide data for rendering.
- ///
- /// Defaults to an empty vector.
- pub middleware: Option>>,
-}
+use std::ops::Deref;
-/// Data stored by [`use_floating`].
-pub struct UseFloatingData {
- pub x: f64,
- pub y: f64,
- pub strategy: Strategy,
- pub placement: Placement,
- pub middleware_data: MiddlewareData,
- pub is_positioned: bool,
-}
+use floating_ui_dom::{
+ compute_position, ComputePositionConfig, MiddlewareData, Placement, Strategy,
+};
+use leptos::{create_memo, create_signal, html::ElementDescriptor, MaybeSignal, NodeRef, Signal};
+use log::info;
+
+use crate::{
+ types::{FloatingStyles, UseFloatingOptions, UseFloatingReturn},
+ utils::{get_dpr::get_dpr, round_by_dpr::round_by_dpr},
+};
+
+pub fn use_floating(
+ reference: NodeRef,
+ floating: NodeRef,
+ options: MaybeSignal,
+) -> UseFloatingReturn
+where
+ Reference: ElementDescriptor + Deref + Clone + 'static,
+ ReferenceEl: Deref,
+ Floating: ElementDescriptor + Deref + Clone + 'static,
+ FloatingEl: Deref,
+{
+ let options = Signal::derive(options);
+
+ let open_option = move || options().open.unwrap_or(true);
+ let placement_option = move || options().placement.unwrap_or(Placement::Bottom);
+ let strategy_option = move || options().strategy.unwrap_or(Strategy::Absolute);
+ let middleware_option = move || options().middleware;
+ let transform_option = move || options().transform.unwrap_or(true);
+
+ let (x, set_x) = create_signal(0.0);
+ let (y, set_y) = create_signal(0.0);
+ let (strategy, set_strategy) = create_signal(strategy_option());
+ let (placement, set_placement) = create_signal(placement_option());
+ let (middleware_data, set_middleware_data) = create_signal(MiddlewareData::default());
+ let (is_positioned, set_is_positioned) = create_signal(false);
+ let floating_styles = create_memo(move |_| {
+ let initial_styles = FloatingStyles {
+ position: strategy(),
+ top: "0".into(),
+ left: "0".into(),
+ transform: None,
+ will_change: None,
+ };
+
+ info!("floating styles memo");
+
+ if let Some(floating_element) = floating.get() {
+ info!("floating element exists");
-pub fn use_floating(options: MaybeSignal) {
- // let placement = create_memo(move |_| {
- // options.with(|options| options.placement.unwrap_or(Placement::Bottom))
- // });
-
- // let strategy = create_memo(move |_| {
- // options.with(|options| options.strategy.unwrap_or(Strategy::Absolute))
- // });
-
- let (_data, _set_data) = create_signal(UseFloatingData {
- x: 0.0,
- y: 0.0,
- strategy: options().strategy.unwrap_or(Strategy::Absolute),
- placement: options().placement.unwrap_or(Placement::Bottom),
- middleware_data: MiddlewareData::default(),
- is_positioned: false,
+ let x_val = round_by_dpr(&floating_element, x());
+ let y_val = round_by_dpr(&floating_element, y());
+
+ if transform_option() {
+ FloatingStyles {
+ transform: Some(format!("translate({x_val}px, {y_val}px)")),
+ will_change: match get_dpr(&floating_element) >= 1.5 {
+ true => Some("transform".into()),
+ false => None,
+ },
+ ..initial_styles
+ }
+ } else {
+ FloatingStyles {
+ left: format!("{x_val}px"),
+ top: format!("{y_val}px"),
+ ..initial_styles
+ }
+ }
+ } else {
+ initial_styles
+ }
});
- // let (_latest_middleware, _set_latest_middleware) = create_signal(options.middleware);
+ let update = move || {
+ info!("update");
+ if let Some(reference_element) = reference.get() {
+ info!("ref");
+ if let Some(floating_element) = floating.get() {
+ info!("float");
+ let config = ComputePositionConfig {
+ placement: Some(placement_option()),
+ strategy: Some(strategy_option()),
+ middleware: middleware_option(),
+ };
+
+ let position =
+ compute_position(&reference_element, &floating_element, Some(config));
+ set_x(position.x);
+ set_y(position.x);
+ set_strategy(position.strategy);
+ set_placement(position.placement);
+ set_middleware_data(position.middleware_data);
+ set_is_positioned(true);
+ }
+ }
+ };
+
+ let cleanup = move || {
+ // TODO
+ };
+
+ let attach = move || {
+ cleanup();
+
+ // TODO: the rest of the function
+ update();
+ };
+
+ let reset = move || {
+ if !open_option() {
+ set_is_positioned(false);
+ }
+ };
+
+ // TODO: call attach/reset
- // // TODO: compare latest_middleware and options.middleware and update it
+ create_memo(move |_| {
+ info!("{} {}", reference.get().is_some(), floating.get().is_some());
- // let (_reference, _set_reference) = create_signal::