Platform

Use Floating UI's positioning logic on any platform that can execute Rust.

Floating UI's core is essentially a bunch of mathematical calculations performed on rectangles. These calculations are pure and agnostic, allowing Floating UI to work on any platform that can execute Rust.

To make it work with a given platform, methods are used to allow it to hook into measurement APIs, for instance, to measure the bounding box of a given element.

Possible platforms other than the DOM include Native, Canvas/WebGL, etc.

Custom Platform Struct

If you're building a platform from scratch, e.g. your own tiny custom DOM platform, you'll be using the floating-ui-core package - see Methods.

If you're extending or customizing the existing DOM methods, and are using floating-ui-dom, this is accessible via the Platform import - see Extending the DOM Platform.

Shadow DOM Fix

TODO

Concepts

The library works largely with a Rect:

pub struct Rect {
    pub x: f64,
    pub y: f64,
    pub width: f64,
    pub height: f64,
}

This data can come from anywhere, and the library will perform the right computations. x and y represent the coordinates of the element relative to another one.

Methods

A platform is a struct implementing the Platform trait, which consists of 3 required and 7 optional methods. These methods allow the platform to interface with Floating UI's logic.

The Platform<Element, Window> trait has two generic types that reflect the element and window of the platform. The DOM platform uses web_sys::Element and web_sys::Window.

Required Methods

get_element_rects

Takes in the elements and the positioning strategy and returns the element Rect struct instances.

pub fn get_element_rects(
    GetElementRectsArgs {
        reference,
        floating,
        strategy,
    }: GetElementRectsArgs<Element>
) -> ElementRects {
    ElementRects {
        reference: Rect {x: 0.0, y: 0.0, width: 0.0, height: 0.0},
        floating: Rect {x: 0.0, y: 0.0, width: 0.0, height: 0.0},
    }
}

reference

The x and y values of a reference Rect should be its coordinates relative to the floating element's offset_parent element if required rather than the viewport.

floating

Both x and y are not relevant initially, so you can set these both of these to 0.0.

get_dimensions

pub fn get_dimensions(element: &Element) -> Dimensions {
    Dimensions {
        width: 0.0,
        height: 0.0,
    }
}

get_clipping_rect

Returns the Rect (relative to the viewport) whose outside bounds will clip the given element. For instance, the viewport itself.

pub fn get_clipping_rect(
    GetClippingRectArgs {
        element,
        boundary,
        root_boundary,
        strategy,
    }: GetClippingRectArgs<Element>,
) -> Rect {
    Rect {
        x: 0.0,
        y: 0.0,
        width: 0.0,
        height: 0.0,
    }
}

Optional Methods

Depending on the platform you're working with, these may or may not be necessary.

convert_offset_parent_relative_rect_to_viewport_relative_rect

This function will take a Rect that is relative to a given offset_parent element and convert its x and y values such that it is instead relative to the viewport.

pub fn convert_offset_parent_relative_rect_to_viewport_relative_rect(
    ConvertOffsetParentRelativeRectToViewportRelativeRectArgs {
        elements,
        rect,
        offset_parent,
        strategy,
    }: ConvertOffsetParentRelativeRectToViewportRelativeRectArgs<Element, Window>,
) -> Rect {
    rect
}

get_offset_parent

Returns the offset_parent of a given element.

pub fn get_offset_parent(
    element: &Element,
    polyfill: Option<Polyfill>,
) -> OwnedElementOrWindow<Element, Window> {
    OwnedElementOrWindow::Window(window)
}

The polyfill parameter exists only for floating-ui-dom and is optional to fix the Shadow DOM Bug.

get_document_element

Returns the document element.

fn get_document_element(element: &Element) -> Element {
    document_element
}

get_client_rects

Returns a vector of ClientRects.

fn get_client_rects(element: ElementOrVirtual) -> Vec<ClientRectObject> {
    vec![]
}

is_rtl

Determines if an element is in RTL layout.

fn is_rtl(element: &Element) -> bool {
    false
}

get_scale

Determines the scale of an element.

fn get_scale(element: &Element) -> Coords {
    Coords {
        x: 1.0,
        y: 1.0
    }
}

get_client_length

Returns the client width or height of an element.

fn get_client_length(element: &Element, length: Length) -> f64 {
    match length {
        Length::Width => 0.0,
        Length::Height => 0.0,
    }
}

Usage

All these methods are passed in the implementation of the Platform trait.

use floating_ui_core::{compute_position, ComputePositionConfig, Platform};

#[derive(Debug)]
pub struct CustomPlatform {}

impl Platform<Element, Window> for CustomPlatform {
    // Required

    fn get_element_rects(&self, args: GetElementRectsArgs<Element>) -> ElementRects {
        get_element_rects(args)
    }

    fn get_dimensions(&self, element: &Element) -> Dimensions {
        get_dimensions(element)
    }

    fn get_clipping_rect(&self, args: GetClippingRectArgs<Element>) -> Rect {
        get_clipping_rect(args)
    }

    // Optional (pass `None` if not implemented)

    fn convert_offset_parent_relative_rect_to_viewport_relative_rect(
        &self,
        args: ConvertOffsetParentRelativeRectToViewportRelativeRectArgs<Element, Window>,
    ) -> Option<Rect> {
        Some(convert_offset_parent_relative_rect_to_viewport_relative_rect(args))
    }

    fn get_offset_parent(
        &self,
        element: &Element,
    ) -> Option<OwnedElementOrWindow<Element, Window>> {
        Some(get_offset_parent(element, None))
    }

    fn get_document_element(&self, element: &Element) -> Option<Element> {
        Some(get_document_element(element))
    }

    fn get_client_rects(&self, element: ElementOrVirtual) -> Option<Vec<ClientRectObject>> {
        Some(get_client_rects(element))
    }

    fn is_rtl(&self, element: &Element) -> Option<bool> {
        Some(is_rtl(element))
    }

    fn get_scale(&self, element: &Element) -> Option<Coords> {
        Some(get_scale(element))
    }

    fn get_client_length(
        &self,
        element: &Element,
        length: floating_ui_utils::Length,
    ) -> Option<f64> {
        Some(get_client_length(element, length))
    }
}

const PLATFORM: CustomPlatform = CustomPlatform {};

compute_position(
    reference_el,
    floating_el,
    ComputePositionConfig::new(&PLATFORM),
);

Extending the DOM Platform

use floating_ui_core::{compute_position, ComputePositionConfig, Platform};
use floating_ui_dom::Platform as DomPlatform;
use web_sys::{Element, Window};

#[derive(Debug)]
struct CustomPlatform {
    dom_platform: DomPlatform,
}

impl Platform<Element, Window> for CustomPlatform {
    // Use existing DOM methods.
    fn get_element_rects(&self, args: GetElementRectsArgs<Element>) -> ElementRects {
        self.dom_platform.get_element_rects(self, args)
    }

    // Overwrite methods with your own.
    fn get_dimensions(&self, element: &Element) -> Dimensions {
        Dimensions {
            width: 0.0,
            height: 0.0,
        }
    }

    // Etc.
}

const PLATFORM: CustomPlatform = CustomPlatform {
    dom_platform: DomPlatform {}
};

compute_position(
    reference_el,
    floating_el,
    ComputePositionConfig::new(&PLATFORM),
);

See Also