Middleware в Floating UI представляет собой цепочку функций, которые обрабатывают позиционирование всплывающих элементов (popper, tooltip, dropdown) перед окончательным рендером. Каждая функция middleware получает текущее состояние позиции и возвращает модифицированное состояние. Middleware позволяет управлять смещением, предотвращать выход за границы окна, применять сдвиги и другие трансформации.
Структура middleware выглядит следующим образом:
import { offset, flip, shift } from '@floating-ui/dom';
const middleware = [
offset(10),
flip(),
shift({ padding: 5 })
];
Здесь каждая функция выполняет свою задачу в цепочке, а результат передаётся следующему middleware.
offset – добавляет смещение от целевого элемента. Параметры:
mainAxis – смещение по основной оси.crossAxis – смещение по перекрёстной оси.offset({ mainAxis: 8, crossAxis: 4 });
Это полезно для создания отступов между trigger-элементом и всплывающим окном.
flip – автоматически меняет позицию всплывающего элемента, если она выходит за пределы viewport. Параметры:
fallbackPlacements – массив альтернативных
позиций.padding – отступ от границ окна.flip({ fallbackPlacements: ['top', 'right'], padding: 10 });
Flip гарантирует, что элемент останется видимым вне зависимости от ограничений контейнера.
shift – корректирует позицию при перекрытии краёв viewport. Параметры:
padding – внутренний отступ от границ окна.shift({ padding: 5 });
Этот middleware особенно важен при динамически изменяемых размерах окна.
size – позволяет управлять размером всплывающего элемента относительно целевого. Параметры:
apply – функция для изменения стилей.padding – отступ для вычислений.size({
apply({ rects, availableHeight, elements }) {
elements.floating.style.maxHeight = `${availableHeight}px`;
},
});Custom middleware позволяет реализовать уникальные правила позиционирования:
function customShift({ x, y, elements }) {
return {
x: x + 20,
y: y,
};
}
const middleware = [customShift];
Функция middleware получает объект:
x, y – текущие координаты элемента.placement – выбранная позиция.rects – размеры target и floating.elements – DOM-элементы target и floating.Возвращаемое значение может включать:
x, y – новые координаты.data – объект с дополнительной информацией для
следующих middleware.reset – флаг перезапуска цепочки с новой позицией.Пример с цепочкой middleware:
const middlewareChain = [
offset(10),
flip({ fallbackPlacements: ['bottom', 'left'] }),
shift({ padding: 8 }),
size({
apply({ elements, availableHeight }) {
elements.floating.style.maxHeight = `${availableHeight}px`;
},
}),
];
Тестирование middleware требует проверки:
Использование Jest или Vitest позволяет тестировать функции изолированно:
import { offset } from '@floating-ui/dom';
test('offset middleware добавляет смещение', () => {
const result = offset({ mainAxis: 10 })({ x: 0, y: 0 });
expect(result.x).toBe(0);
expect(result.y).toBe(10);
});
Можно тестировать цепочку middleware через мок объектов
rects и elements:
import { flip } from '@floating-ui/dom';
const mockState = {
x: 0,
y: 0,
placement: 'top',
rects: {
reference: { width: 50, height: 50, top: 0, left: 0 },
floating: { width: 100, height: 100 },
},
elements: {
reference: document.createElement('div'),
floating: document.createElement('div'),
},
};
test('flip middleware корректно меняет позицию', () => {
const result = flip({ fallbackPlacements: ['bottom'] })(mockState);
expect(result.placement).toBe('bottom');
});
Для проверки middleware в реальных условиях:
const floating = document.createElement('div');
document.body.appendChild(floating);
computePosition(trigger, floating, { middleware: middlewareChain });
Интеграционные тесты позволяют выявить баги в цепочках middleware, которые не видны при юнит-тестах, например конфликты offset и shift или неправильная работа flip при нескольких fallbackPlacements.
rects и
elements, чтобы тесты были стабильными и независимыми от
реального DOM.Middleware в Floating UI — это гибкий инструмент управления позиционированием, позволяющий адаптировать поведение всплывающих элементов под любые требования интерфейса, при этом обеспечивая высокую тестируемость и предсказуемость работы.