Shift

Shifts the floating element to keep it in view.

This prevents the floating element from overflowing along its axis of alignment, thereby preserving the side it's placed on.

Type: Visibility Optimizer

Usage

use floating_ui_core::{compute_position, ComputePositionConfig, Shift, ShiftOptions};

compute_position(
    reference_el,
    floating_el,
    ComputePositionConfig::new(platform)
        .middleware(vec![
            Box::new(Shift::new(ShiftOptions::default())),
        ]),
);

Options

These are the options you can pass to Shift.

pub struct ShiftOptions<Element, Window> {
    pub detect_overflow: Option<DetectOverflowOptions<Element>>,
    pub main_axis: Option<bool>,
    pub cross_axis: Option<bool>,
    pub limiter: Option<Box<dyn Limiter<Element, Window>>>,
}

pub trait Limiter<Element, Window>: Clone + PartialEq {
    fn compute(&self, state: MiddlewareState<Element, Window>) -> Coords;
}

main_axis

Default: true

This is the main axis in which shifting is applied.

  • x-axis for Top and Bottom placements
  • y-axis for Left and Right placements
Shift::new(ShiftOptions::default().main_axis(false))

cross_axis

Default: false

This is the cross axis in which shifting is applied, the opposite axis of main_axis.

Enabling this can lead to the floating element overlapping the reference element, which may not be desired and is often replaced by the Flip middleware.

Shift::new(ShiftOptions::default().cross_axis(true))

limiter

Default: no-op

This accepts a struct instance that limits the shifting done, in order to prevent detachment or “overly-eager” behavior. The behavior is to stop shifting once the opposite edges of the elements are aligned.

Shift::new(ShiftOptions::default().limiter(
    Box::new(LimitShift::new(
        LimitShiftOptions::default()
    )),
))

This struct itself takes options.

pub struct LimitShiftOptions<'a, Element, Window> {
    pub offset: Option<Derivable<'a, Element, Window, LimitShiftOffset>>,
    pub main_axis: Option<bool>,
    pub cross_axis: Option<bool>,
}

pub enum LimitShiftOffset {
    Value(f64),
    Values(LimitShiftOffsetValues),
}

pub struct LimitShiftOffsetValues {
    pub main_axis: Option<f64>,
    pub cross_axis: Option<f64>,
}

main_axis

Default: true

Whether to apply limiting on the main axis.

Shift::new(ShiftOptions::default().limiter(
    Box::new(LimitShift::new(
        LimitShiftOptions::default().main_axis(false)
    )),
))

cross_axis

Default: true

Whether to apply limiting on the cross axis.

Shift::new(ShiftOptions::default().limiter(
    Box::new(LimitShift::new(
        LimitShiftOptions::default().cross_axis(false)
    )),
))

offset

Default: 0.0

This will offset when the limiting starts. A positive number will start limiting earlier, while negative later.

Shift::new(ShiftOptions::default().limiter(
    Box::new(LimitShift::new(
        // Start limiting 5px earlier
        LimitShiftOptions::default().offset(LimitShiftOffset::Value(5.0)),
    )),
))

You may also pass a struct instance to configure both axes:

Shift::new(ShiftOptions::default().limiter(
    Box::new(LimitShift::new(
        LimitShiftOptions::default().offset(LimitShiftOffset::Values(
            LimitShitOffsetValues::default()
                .main_axis(10.0)
                .cross_axis(5.0),
        )),
    )),
))

detect_overflow

All of detect_overflow's options can be passed. For instance:

Shift::new(ShiftOptions::default().detect_overflow(
    DetectOvverflowOptions::default().padding(Padding::All(5.0)),
))

Data

The following data is available in middleware_data under the SHIFT_NAME key:

middleware_data.get_as::<ShiftData>(SHIFT_NAME)
pub struct ShiftData {
    pub x: f64,
    pub y: f64,
}

x and y represent how much the floating element has been shifted along that axis. The values are offsets, and therefore can be negative.

See Also