or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

advanced-middleware.mdbasic-middleware.mdcore-positioning.mdindex.mdoverflow-detection.md
tile.json

advanced-middleware.mddocs/

Advanced Middleware

Specialized middleware for complex positioning scenarios including auto-placement, arrow positioning, size constraints, visibility detection, and inline element handling.

Capabilities

Auto Placement

Optimizes visibility by automatically choosing the placement that has the most space available. Alternative to flip when you want to automatically find the best placement.

/**
 * Optimizes the visibility of the floating element by choosing the placement
 * that has the most space available automatically, without needing to specify
 * a preferred placement.
 * 
 * @param options - Auto placement configuration
 * @returns Middleware object
 */
function autoPlacement(options?: AutoPlacementOptions): Middleware;

interface AutoPlacementOptions extends DetectOverflowOptions {
  /** Whether to check cross axis for most space */
  crossAxis?: boolean;
  /** Prefer placements with particular alignment */
  alignment?: Alignment | null;
  /** Whether to choose opposite alignments if preferred doesn't fit */
  autoAlignment?: boolean;
  /** Which placements are allowed to be chosen */
  allowedPlacements?: Array<Placement>;
}

Usage Examples:

import { computePosition, autoPlacement } from "@floating-ui/core";

// Automatic placement selection
await computePosition(reference, floating, {
  middleware: [autoPlacement()], // Chooses best available placement
  platform: domPlatform
});

// Prefer specific alignment
await computePosition(reference, floating, {
  middleware: [
    autoPlacement({
      alignment: "start", // Prefer start-aligned placements
      autoAlignment: true // Allow end-aligned if start doesn't fit
    })
  ],
  platform: domPlatform
});

// Limit allowed placements
await computePosition(reference, floating, {
  middleware: [
    autoPlacement({
      allowedPlacements: ["top", "bottom", "top-start", "bottom-start"]
    })
  ],
  platform: domPlatform
});

Arrow

Provides data to position an inner arrow element so it appears centered relative to the reference element.

/**
 * Provides data to position an inner element of the floating element so that it
 * appears centered to the reference element.
 * 
 * @param options - Arrow configuration
 * @returns Middleware object
 */
function arrow(options: ArrowOptions): Middleware;

interface ArrowOptions {
  /** The arrow element to be positioned */
  element: any;
  /** Padding between arrow and floating element edges */
  padding?: Padding;
}

Usage Examples:

import { computePosition, arrow, offset, flip } from "@floating-ui/core";

// Basic arrow positioning
const arrowElement = document.querySelector('.arrow');

const { x, y, placement, middlewareData } = await computePosition(
  reference, 
  floating, 
  {
    placement: "top",
    middleware: [
      offset(8),
      flip(),
      arrow({ element: arrowElement })
    ],
    platform: domPlatform
  }
);

// Position the floating element
Object.assign(floating.style, {
  left: `${x}px`,
  top: `${y}px`,
});

// Position the arrow using middleware data
const arrowData = middlewareData.arrow;
if (arrowData) {
  const side = placement.split('-')[0];
  const arrowSide = {
    top: 'bottom',
    right: 'left', 
    bottom: 'top',
    left: 'right'
  }[side];
  
  Object.assign(arrowElement.style, {
    left: arrowData.x != null ? `${arrowData.x}px` : '',
    top: arrowData.y != null ? `${arrowData.y}px` : '',
    [arrowSide]: `-4px` // Half of arrow size
  });
}

Size

Provides data to change the size of the floating element, for instance to prevent it from overflowing clipping boundaries or to match the reference element width.

/**
 * Provides data that allows you to change the size of the floating element —
 * for instance, prevent it from overflowing the clipping boundary or match the
 * width of the reference element.
 * 
 * @param options - Size configuration
 * @returns Middleware object
 */
function size(options?: SizeOptions): Middleware;

interface SizeOptions extends DetectOverflowOptions {
  /** Function to apply size changes to the floating element */
  apply?(args: MiddlewareState & {
    availableWidth: number;
    availableHeight: number;
  }): void | Promise<void>;
}

Usage Examples:

import { computePosition, size, flip } from "@floating-ui/core";

// Prevent overflow by constraining size
await computePosition(reference, floating, {
  middleware: [
    size({
      apply({ availableWidth, availableHeight, elements }) {
        // Constrain to available space
        Object.assign(elements.floating.style, {
          maxWidth: `${availableWidth}px`,
          maxHeight: `${availableHeight}px`,
        });
      },
    }),
    flip()
  ],
  platform: domPlatform
});

// Match reference width
await computePosition(reference, floating, {
  middleware: [
    size({
      apply({ rects, elements }) {
        // Match reference element width
        Object.assign(elements.floating.style, {
          width: `${rects.reference.width}px`,
        });
      },
    })
  ],
  platform: domPlatform
});

Hide

Provides data to hide the floating element when it's not in the same clipping context as the reference element or has escaped the clipping boundary.

/**
 * Provides data to hide the floating element in applicable situations, such as
 * when it is not in the same clipping context as the reference element.
 * 
 * @param options - Hide configuration
 * @returns Middleware object
 */
function hide(options?: HideOptions): Middleware;

interface HideOptions extends DetectOverflowOptions {
  /** Strategy for determining when to hide */
  strategy?: "referenceHidden" | "escaped";
}

Usage Examples:

import { computePosition, hide, offset, flip } from "@floating-ui/core";

// Hide when reference is not visible
const { middlewareData } = await computePosition(reference, floating, {
  middleware: [
    offset(10),
    flip(),
    hide({ strategy: "referenceHidden" })
  ],
  platform: domPlatform
});

// Apply visibility based on hide data
const hideData = middlewareData.hide;
if (hideData?.referenceHidden) {
  Object.assign(floating.style, {
    visibility: "hidden",
  });
}

// Hide when floating element escapes boundaries
const { middlewareData } = await computePosition(reference, floating, {
  middleware: [
    hide({ strategy: "escaped" })
  ],
  platform: domPlatform
});

const hideData = middlewareData.hide;
if (hideData?.escaped) {
  Object.assign(floating.style, {
    visibility: "hidden",
  });
}

Inline

Provides improved positioning for inline reference elements that span multiple lines, such as hyperlinks or range selections.

/**
 * Provides improved positioning for inline reference elements that can span
 * over multiple lines, such as hyperlinks or range selections.
 * 
 * @param options - Inline configuration
 * @returns Middleware object
 */
function inline(options?: InlineOptions): Middleware;

interface InlineOptions {
  /** Viewport-relative x coordinate to choose a ClientRect */
  x?: number;
  /** Viewport-relative y coordinate to choose a ClientRect */
  y?: number;
  /** Padding around disjoined rect when choosing it */
  padding?: Padding;
}

Usage Examples:

import { computePosition, inline, flip, shift } from "@floating-ui/core";

// Basic inline positioning
await computePosition(textSelection, tooltip, {
  middleware: [
    inline(),
    flip(),
    shift()
  ],
  platform: domPlatform
});

// Inline with cursor position
const handleMouseEnter = async (event) => {
  const { x, y } = await computePosition(linkElement, tooltip, {
    middleware: [
      inline({
        x: event.clientX,
        y: event.clientY,
        padding: 4
      }),
      flip(),
      shift()
    ],
    platform: domPlatform
  });
  
  Object.assign(tooltip.style, {
    left: `${x}px`,
    top: `${y}px`,
  });
};

Complex Examples

Advanced Tooltip with Arrow

import { 
  computePosition, 
  offset, 
  flip, 
  shift, 
  arrow, 
  hide,
  autoPlacement 
} from "@floating-ui/core";

const createAdvancedTooltip = async (reference, floating, arrowElement) => {
  const { x, y, placement, middlewareData } = await computePosition(
    reference,
    floating,
    {
      placement: "top",
      middleware: [
        offset(8),
        flip({
          fallbackAxisSideDirection: "start",
          fallbackStrategy: "bestFit"
        }),
        shift({ padding: 8 }),
        arrow({ 
          element: arrowElement,
          padding: 4 
        }),
        hide({ strategy: "referenceHidden" })
      ],
      platform: domPlatform
    }
  );

  // Position floating element
  Object.assign(floating.style, {
    left: `${x}px`,
    top: `${y}px`,
  });

  // Position arrow
  const arrowData = middlewareData.arrow;
  if (arrowData) {
    const side = placement.split('-')[0];
    const staticSide = {
      top: 'bottom',
      right: 'left',
      bottom: 'top', 
      left: 'right'
    }[side];

    Object.assign(arrowElement.style, {
      left: arrowData.x != null ? `${arrowData.x}px` : '',
      top: arrowData.y != null ? `${arrowData.y}px` : '',
      right: '',
      bottom: '',
      [staticSide]: '-4px',
    });
  }

  // Handle visibility
  const hideData = middlewareData.hide;
  if (hideData?.referenceHidden) {
    Object.assign(floating.style, {
      visibility: 'hidden',
    });
  }
};

Responsive Dropdown

import { computePosition, size, autoPlacement, shift } from "@floating-ui/core";

const createResponsiveDropdown = async (trigger, dropdown) => {
  const { x, y, placement } = await computePosition(trigger, dropdown, {
    middleware: [
      autoPlacement({
        allowedPlacements: ["bottom-start", "bottom-end", "top-start", "top-end"],
        crossAxis: true
      }),
      size({
        apply({ availableWidth, availableHeight, elements, rects }) {
          // Responsive sizing logic
          const maxWidth = Math.min(availableWidth, 400);
          const maxHeight = Math.min(availableHeight, 300);
          
          Object.assign(elements.floating.style, {
            maxWidth: `${maxWidth}px`,
            maxHeight: `${maxHeight}px`,
            width: rects.reference.width > 200 
              ? `${rects.reference.width}px` 
              : 'auto'
          });
        },
      }),
      shift({ padding: 16 })
    ],
    platform: domPlatform
  });

  Object.assign(dropdown.style, {
    left: `${x}px`,
    top: `${y}px`,
  });
};