floating_ui_leptos/
arrow.rs

1use floating_ui_dom::{
2    ARROW_NAME, Arrow as CoreArrow, ArrowOptions as CoreArrowOptions, Middleware, MiddlewareReturn,
3    MiddlewareState, Padding,
4};
5use leptos::prelude::*;
6use leptos_node_ref::AnyNodeRef;
7use web_sys::wasm_bindgen::JsCast;
8
9/// Options for [`Arrow`].
10#[derive(Clone)]
11pub struct ArrowOptions {
12    /// The arrow element to be positioned.
13    pub element: AnyNodeRef,
14
15    /// The padding between the arrow element and the floating element edges.
16    /// Useful when the floating element has rounded corners.
17    ///
18    /// Defaults to `0` on all sides.
19    pub padding: Option<Padding>,
20}
21
22impl ArrowOptions {
23    pub fn new(element: AnyNodeRef) -> Self {
24        ArrowOptions {
25            element,
26            padding: None,
27        }
28    }
29
30    /// Set `element` option.
31    pub fn element(mut self, value: AnyNodeRef) -> Self {
32        self.element = value;
33        self
34    }
35
36    /// Set `padding` option.
37    pub fn padding(mut self, value: Padding) -> Self {
38        self.padding = Some(value);
39        self
40    }
41}
42
43impl PartialEq for ArrowOptions {
44    fn eq(&self, other: &Self) -> bool {
45        self.element.get_untracked() == other.element.get_untracked()
46            && self.padding == other.padding
47    }
48}
49
50/// Arrow middleware.
51///
52/// Provides data to position an inner element of the floating element so that it appears centered to the reference element.
53///
54/// See [the Rust Floating UI book](https://floating-ui.rustforweb.org/middleware/arrow.html) for more documentation.
55#[derive(Clone, PartialEq)]
56pub struct Arrow {
57    options: ArrowOptions,
58}
59
60impl Arrow {
61    pub fn new(options: ArrowOptions) -> Self {
62        Arrow { options }
63    }
64}
65
66impl Middleware<web_sys::Element, web_sys::Window> for Arrow {
67    fn name(&self) -> &'static str {
68        ARROW_NAME
69    }
70
71    fn compute(
72        &self,
73        state: MiddlewareState<web_sys::Element, web_sys::Window>,
74    ) -> MiddlewareReturn {
75        let element = self
76            .options
77            .element
78            .get_untracked()
79            .and_then(|element| element.dyn_into::<web_sys::Element>().ok());
80
81        if let Some(element) = element {
82            CoreArrow::new(CoreArrowOptions {
83                element,
84                padding: self.options.padding.clone(),
85            })
86            .compute(state)
87        } else {
88            MiddlewareReturn {
89                x: None,
90                y: None,
91                data: None,
92                reset: None,
93            }
94        }
95    }
96}