В библиотеке Floating UI middleware представляет
собой функции, которые модифицируют поведение позиционирования
всплывающих элементов. Каждая middleware-функция получает объект с
текущими координатами (x, y) и размерами
элементов (rects), а также дополнительные параметры
конфигурации. Она возвращает объект с изменёнными координатами или
дополнительными данными, которые могут быть использованы последующими
middleware или самим Floating UI для расчета финального положения.
Middleware позволяет:
Каждая middleware может быть синхронной или асинхронной, что важно при работе с вычислениями, требующими времени, например, при измерении размеров контента, загружаемого динамически.
Middleware — это функция, которая принимает объект с параметрами и
возвращает объект с функцией fn:
const myMiddleware = (options) => ({
name: 'myMiddleware',
options,
fn({ x, y, rects, placement, strategy }) {
// Логика изменения координат
return { x: x + 10, y };
}
});
Ключевые свойства и аргументы функции
fn:
x, y — текущие координаты
floating-элемента;rects — объект с размерами reference и
floating элементов;placement — текущее расположение элемента
(top, bottom, left,
right);strategy — стратегия позиционирования
(absolute или fixed);middlewareData — объект для передачи данных между
различными middleware.Возвращаемый объект может содержать:
x и y — новые координаты;data — любые дополнительные данные, которые могут быть
использованы другими middleware.При создании middleware важно придерживаться следующих принципов:
Именование Каждое middleware должно иметь
уникальное свойство name, чтобы Floating UI мог различать
их и использовать при необходимости повторно.
Чистота функции Middleware не должен напрямую
изменять внешние переменные. Все изменения координат должны возвращаться
через объект из функции fn.
Совместимость с другими middleware Любые
дополнительные данные лучше помещать в data внутри объекта,
чтобы последующие middleware могли их использовать:
const shiftRight = () => ({
name: 'shiftRight',
fn({ x, y }) {
return {
x: x + 20,
data: { shifted: true }
};
}
});
Middleware для предотвращения выхода floating-элемента за границы окна:
const keepInViewport = () => ({
name: 'keepInViewport',
fn({ x, y, rects }) {
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
let newX = x;
let newY = y;
if (x + rects.floating.width > viewportWidth) {
newX = viewportWidth - rects.floating.width;
}
if (y + rects.floating.height > viewportHeight) {
newY = viewportHeight - rects.floating.height;
}
return { x: newX, y: newY };
}
});
Middleware для смещения элемента при скролле родителя:
const scrollOffset = ({ offset = 0 }) => ({
name: 'scrollOffset',
options: { offset },
fn({ y }) {
return { y: y + offset };
}
});
Иногда требуется подождать, пока DOM полностью загрузится, чтобы правильно рассчитать координаты:
const asyncAdjust = () => ({
name: 'asyncAdjust',
async fn({ x, y }) {
await new Promise(resolve => setTimeout(resolve, 50));
return { x: x + 5, y: y + 5 };
}
});
При вызове функции computePosition middleware
подключаются через массив:
import { computePosition } from '@floating-ui/dom';
computePosition(referenceElement, floatingElement, {
middleware: [
keepInViewport(),
shiftRight(),
scrollOffset({ offset: 10 })
]
}).then(({ x, y }) => {
Object.assign(floatingElement.style, {
left: `${x}px`,
top: `${y}px`
});
});
Особенности работы с несколькими middleware:
middlewareData;Можно создавать композируемые middleware, которые сами вызывают другие middleware внутри себя. Это удобно для комплексных UI-компонентов с динамическими позициями:
const combinedMiddleware = () => ({
name: 'combinedMiddleware',
fn(args) {
const shiftResult = shiftRight().fn(args);
const viewportResult = keepInViewport().fn({ ...args, ...shiftResult });
return { ...shiftResult, ...viewportResult };
}
});
Такой подход позволяет создавать библиотеку собственных логик позиционирования, полностью интегрируемую с Floating UI.
await внутри fn только при реальной
необходимости;flip, shift, offset) с
собственными, чтобы достичь максимально гибкой системы
позиционирования.Создание собственного middleware позволяет полностью контролировать поведение всплывающих элементов, расширять стандартный функционал и адаптировать его под сложные сценарии пользовательского интерфейса.