Порядок выполнения middleware

Middleware в Floating UI — это функции, которые позволяют управлять положением плавающего элемента относительно якоря (reference element). Они выполняются в строгой последовательности, и понимание порядка их вызова критически важно для корректного позиционирования. Каждый middleware может модифицировать координаты x и y, а также добавлять эффекты, такие как смещение, ограничение границ или автоматическое изменение направления.

Порядок выполнения middleware

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 })
  ]
});

В этом примере:

  1. offset(10) выполняется первым. Он смещает плавающий элемент на 10px относительно якоря.
  2. flip() выполняется вторым. Он проверяет, помещается ли элемент в текущем направлении, и при необходимости меняет сторону.
  3. shift({ padding: 5 }) выполняется последним. Он корректирует координаты, чтобы элемент не выходил за границы контейнера с отступом в 5px.

Влияние порядка на результат

Порядок middleware напрямую влияет на конечное положение плавающего элемента:

  • Если flip() выполнить до offset(), смещение будет применено уже после возможного изменения направления, что может привести к нежелательным смещениям.
  • Если shift() вызвать до flip(), элемент может быть сначала подвинут, а потом «перевернут», что создаст визуально нестабильное поведение.

Поэтому рекомендуется сначала применять простые трансформации, такие как offset, затем изменение направления (flip), и только после этого — ограничение области (shift, size).

Асинхронные middleware

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 через порядок выполнения и передачу состояния.

Рекурсивное выполнение при flip

Некоторые middleware, например flip, могут вызывать повторный проход цепочки при изменении направления. Это значит, что после изменения позиции middleware будут вызваны заново с новыми координатами, сохраняя исходный порядок.

Это поведение позволяет адаптироваться к ограничениям окна и контейнера, но требует понимания, что первый middleware может быть вызван несколько раз в рамках одного вычисления позиции.

Практические рекомендации

  • Всегда располагать middleware в логической последовательности: offset → flip → shift → size → custom.
  • Использовать data для передачи промежуточного состояния между middleware.
  • Помнить, что асинхронные middleware блокируют выполнение последующих до завершения.
  • Тестировать поведение при изменении размеров окна, так как middleware типа flip и shift могут изменить порядок или количество вызовов.

Правильная организация порядка middleware обеспечивает точное, предсказуемое и стабильное позиционирование Floating UI, даже при сложных динамических интерфейсах.