В предыдущих версиях Floating UI в системе позиционирования элементов использовались modifiers — объекты, определяющие смещение, ограничение позиции и другие поведенческие свойства всплывающих элементов. В последних версиях библиотека перешла на middleware, что обеспечивает более модульную, гибкую и предсказуемую архитектуру.
Middleware — это функции, которые обрабатывают положение floating-элемента по цепочке и возвращают изменения координат, смещения или стратегий отображения. Каждый middleware может быть включен или исключен в любой комбинации, что позволяет создавать точное поведение элемента без необходимости глубокой кастомизации одного большого объекта-модификатора.
Modifiers представляли собой массив объектов с
обязательными свойствами name и options:
const modifiers = [
{ name: 'offset', options: { offset: [0, 10] } },
{ name: 'flip', options: { fallbackPlacements: ['top', 'bottom'] } },
];
Middleware использует функцию
middleware, переданную в computePosition:
import { offset, flip } from '@floating-ui/dom';
computePosition(reference, floating, {
middleware: [
offset(10),
flip({ fallbackPlacements: ['top', 'bottom'] })
],
});
Ключевые различия:
name и options.offset Управляет смещением floating-элемента относительно reference.
offset(10) // смещает на 10px по основной оси
offset({ mainAxis: 10, crossAxis: 5 }) // настройка по обеим осям
Отличие от старого offset в modifiers: теперь можно
точно задавать смещение по каждой оси и интегрировать его в цепочку
middleware.
flip Реализует автоматический выбор альтернативной позиции при выходе элемента за пределы видимой области.
flip({ fallbackPlacements: ['top', 'left'] })
Middleware flip возвращает объект с координатами и новой позицией, что упрощает обработку нестандартных сценариев.
shift Подвинет floating-элемент, чтобы он не выходил за границы контейнера или viewport:
shift({ limiter: limitShift() })
Можно комбинировать с flip и offset для
более точного контроля.
arrow Позиционирует стрелку (tooltip arrow) относительно reference.
arrow({ element: arrowElement })
Middleware arrow интегрируется в цепочку после offset и
flip, чтобы корректно учитывать смещения и изменение
позиции.
Любая функция middleware должна возвращать объект с ключами:
x и y — координаты смещения, если нужно
изменить позицию.data — дополнительная информация для последующих
middleware.reset — объект с новыми параметрами
placement для повторного вычисления позиции, если произошла
коллизия.Пример кастомного middleware:
function customShift() {
return ({ x, y, placement }) => {
const adjustedX = Math.max(0, x);
const adjustedY = Math.max(0, y);
return { x: adjustedX, y: adjustedY };
};
}
Такой подход исключает необходимость создавать сложные модификаторы с множеством опций, как это было ранее.
Анализ существующих modifiers Определить, какие
modifiers использовались (offset, flip,
preventOverflow и др.).
Замена на соответствующие middleware Каждому modifier соответствует один или несколько middleware:
offset → offset()flip → flip()preventOverflow → shift()Композиция цепочки Middleware нужно располагать
в логическом порядке: сначала смещения, затем корректировка границ
(shift), затем flip, затем arrow.
Тестирование edge-cases Проверить поведение при ограниченном пространстве, динамическом изменении размеров и скролле, чтобы убедиться в корректной работе цепочки middleware.
import { computePosition, offset, flip, shift, arrow } from '@floating-ui/dom';
const floatingElement = document.querySelector('#tooltip');
const arrowElement = document.querySelector('#arrow');
computePosition(referenceElement, floatingElement, {
placement: 'bottom',
middleware: [
offset({ mainAxis: 8, crossAxis: 4 }),
flip({ fallbackPlacements: ['top', 'right'] }),
shift({ limiter: limitShift() }),
arrow({ element: arrowElement })
],
}).then(({ x, y, placement, middlewareData }) => {
Object.assign(floatingElement.style, {
left: `${x}px`,
top: `${y}px`,
});
const arrowX = middlewareData.arrow?.x ?? 0;
const arrowY = middlewareData.arrow?.y ?? 0;
Object.assign(arrowElement.style, {
left: `${arrowX}px`,
top: `${arrowY}px`,
});
});
Этот пример демонстрирует полный переход от старого механизма modifiers к новой цепочке middleware, обеспечивая точное позиционирование, обработку коллизий и корректное расположение стрелки tooltip.