canonrs-core 0.1.0

CanonRS core types, traits and primitives
#[cfg(feature = "hydrate")]
use leptos::web_sys::{Element, HtmlElement, window, DomRect};
#[cfg(feature = "hydrate")]
use leptos::wasm_bindgen::JsCast;
#[cfg(feature = "hydrate")]
use leptos::prelude::ReadSignal;
use super::types::*;

#[cfg(feature = "hydrate")]
pub fn use_floating_position(
    anchor_id: &str,
    floating_id: &str,
    config: FloatingConfig,
    active: ReadSignal<bool>,
) {
    use leptos::prelude::{Effect, Get};

    let anchor_id = anchor_id.to_string();
    let floating_id = floating_id.to_string();

    Effect::new(move |_| {
        if !active.get() {
            return;
        }

        let document = window()
            .expect("window not available")
            .document()
            .expect("document not available");

        let anchor: Element = match document.get_element_by_id(&anchor_id) {
            Some(el) => el,
            None => return,
        };

        let floating: Element = match document.get_element_by_id(&floating_id) {
            Some(el) => el,
            None => return,
        };

        let anchor_rect = anchor.get_bounding_client_rect();
        let floating_rect = floating.get_bounding_client_rect();

        let viewport_width = window().unwrap().inner_width().unwrap().as_f64().unwrap();
        let viewport_height = window().unwrap().inner_height().unwrap().as_f64().unwrap();

        let position = calculate_position(
            &anchor_rect,
            &floating_rect,
            viewport_width,
            viewport_height,
            config,
        );

        if let Some(html_el) = floating.dyn_ref::<HtmlElement>() {
            let style = html_el.style();
            let _ = style.set_property("--floating-x", &format!("{}px", position.x));
            let _ = style.set_property("--floating-y", &format!("{}px", position.y));
            let _ = style.set_property("--floating-placement", position.placement.as_str());
        }
    });
}

#[cfg(feature = "hydrate")]
fn calculate_position(
    anchor_rect: &DomRect,
    floating_rect: &DomRect,
    viewport_width: f64,
    viewport_height: f64,
    config: FloatingConfig,
) -> FloatingPosition {
    #[allow(unused_assignments)]
    let mut x = 0.0;
    #[allow(unused_assignments)]
    let mut y = 0.0;
    let mut placement = config.placement;

    match config.placement {
        Placement::Bottom | Placement::BottomStart | Placement::BottomEnd => {
            x = anchor_rect.left() + (anchor_rect.width() / 2.0) - (floating_rect.width() / 2.0);
            y = anchor_rect.bottom() + config.offset;
            if config.flip && y + floating_rect.height() > viewport_height {
                y = anchor_rect.top() - floating_rect.height() - config.offset;
                placement = Placement::Top;
            }
        }
        Placement::Top | Placement::TopStart | Placement::TopEnd => {
            x = anchor_rect.left() + (anchor_rect.width() / 2.0) - (floating_rect.width() / 2.0);
            y = anchor_rect.top() - floating_rect.height() - config.offset;
            if config.flip && y < 0.0 {
                y = anchor_rect.bottom() + config.offset;
                placement = Placement::Bottom;
            }
        }
        Placement::Left | Placement::LeftStart | Placement::LeftEnd => {
            x = anchor_rect.left() - floating_rect.width() - config.offset;
            y = anchor_rect.top() + (anchor_rect.height() / 2.0) - (floating_rect.height() / 2.0);
            if config.flip && x < 0.0 {
                x = anchor_rect.right() + config.offset;
                placement = Placement::Right;
            }
        }
        Placement::Right | Placement::RightStart | Placement::RightEnd => {
            x = anchor_rect.right() + config.offset;
            y = anchor_rect.top() + (anchor_rect.height() / 2.0) - (floating_rect.height() / 2.0);
            if config.flip && x + floating_rect.width() > viewport_width {
                x = anchor_rect.left() - floating_rect.width() - config.offset;
                placement = Placement::Left;
            }
        }
    }

    x = x.max(0.0).min(viewport_width - floating_rect.width());
    y = y.max(0.0).min(viewport_height - floating_rect.height());

    FloatingPosition { x, y, placement }
}