Middleware в Floating UI — это функции, которые позволяют управлять
положением плавающего элемента относительно якоря (reference element).
Они выполняются в строгой последовательности, и понимание порядка их
вызова критически важно для корректного позиционирования. Каждый
middleware может модифицировать координаты x и
y, а также добавлять эффекты, такие как смещение,
ограничение границ или автоматическое изменение направления.
Floating UI использует линейную цепочку вызова middleware. Каждый
элемент цепочки получает результат предыдущего
middleware и может его изменить. Порядок выполнения
определяется порядком передачи функций в массиве middleware
при вызове
computePosition(reference, floating, { middleware }).
Пример:
import { computePosition, offset, flip, shift } from '@floating-ui/dom';
computePosition(referenceElement, floatingElement, {
middleware: [
offset(10),
flip(),
shift({ padding: 5 })
]
});
В этом примере:
offset(10) выполняется первым. Он
смещает плавающий элемент на 10px относительно якоря.flip() выполняется вторым. Он
проверяет, помещается ли элемент в текущем направлении, и при
необходимости меняет сторону.shift({ padding: 5 }) выполняется
последним. Он корректирует координаты, чтобы элемент не выходил за
границы контейнера с отступом в 5px.Порядок middleware напрямую влияет на конечное положение плавающего элемента:
flip() выполнить до offset(),
смещение будет применено уже после возможного изменения направления, что
может привести к нежелательным смещениям.shift() вызвать до flip(), элемент
может быть сначала подвинут, а потом «перевернут», что создаст визуально
нестабильное поведение.Поэтому рекомендуется сначала применять простые
трансформации, такие как offset, затем
изменение направления (flip), и только
после этого — ограничение области (shift,
size).
Floating UI поддерживает middleware, возвращающие промисы. Асинхронные функции выполняются последовательно, аналогично синхронным, с сохранением порядка в массиве:
async function asyncMiddleware({ x, y }) {
const data = await fetchSomeData();
return { x: x + data.dx, y: y + data.dy };
}
Если в цепочке есть асинхронный middleware, все последующие middleware ждут его завершения. Это важно учитывать при комбинировании с синхронными функциями, чтобы избежать мерцания или неправильного позиционирования.
Каждый middleware может возвращать объект с полями:
x и y — новые координаты;data — произвольные данные, которые могут использовать
последующие middleware.Пример передачи данных между middleware:
const first = ({ x, y }) => ({ x: x + 10, y, data: { offsetApplied: true } });
const second = ({ x, y, data }) => {
if (data.offsetApplied) {
return { x, y: y + 5 };
}
return { x, y };
};
Здесь second использует data, установленное
first, чтобы скорректировать положение. Это демонстрирует
сильную связанность middleware через порядок выполнения
и передачу состояния.
Некоторые middleware, например flip, могут вызывать
повторный проход цепочки при изменении направления. Это
значит, что после изменения позиции middleware будут вызваны заново с
новыми координатами, сохраняя исходный порядок.
Это поведение позволяет адаптироваться к ограничениям окна и контейнера, но требует понимания, что первый middleware может быть вызван несколько раз в рамках одного вычисления позиции.
offset → flip → shift → size → custom.data для передачи промежуточного состояния
между middleware.flip и shift могут изменить
порядок или количество вызовов.Правильная организация порядка middleware обеспечивает точное, предсказуемое и стабильное позиционирование Floating UI, даже при сложных динамических интерфейсах.