Что такое middleware и зачем они нужны

Middleware в Floating UI представляет собой функции, которые выполняются между вычислением позиции плавающего элемента и его фактическим рендерингом в DOM. Они позволяют модифицировать поведение позиционирования, корректировать координаты, управлять перекрытиями и обеспечивать адаптивность интерфейса. Middleware формирует цепочку обработки, через которую проходят все вычисленные значения позиции, прежде чем элемент будет отрисован на странице.

Основная идея заключается в том, что позиция не всегда может быть определена одним вызовом computePosition. Различные обстоятельства, такие как ограничение видимой области, наличие смещения, размеры триггера и всплывающего элемента, требуют промежуточной обработки. Именно здесь middleware проявляет себя как мощный инструмент для тонкой настройки поведения.


Структура middleware

Каждая функция middleware получает объект с текущей позицией и набором данных:

{
  x: number,      // текущая координата по горизонтали
  y: number,      // текущая координата по вертикали
  placement: string, // выбранное начальное положение ('top', 'bottom', 'left', 'right')
  strategy: string,  // стратегия позиционирования ('absolute' или 'fixed')
  elements: { reference: HTMLElement, floating: HTMLElement }, // DOM-элементы
  rects: { reference: DOMRect, floating: DOMRect } // размеры элементов
}

Middleware может:

  • Изменять координаты x и y
  • Изменять placement
  • Возвращать дополнительные данные через объект data, которые могут использовать другие middleware

Каждая функция middleware возвращает объект вида:

{
  x?: number,
  y?: number,
  data?: any,
  reset?: { placement?: string } | true
}
  • x, y – новые координаты элемента
  • data – объект для передачи промежуточной информации
  • reset – сигнал для повторного расчета позиции с новым placement

Основные типы middleware

1. offset Позволяет задать смещение плавающего элемента относительно reference-элемента.

import { offset } from '@floating-ui/dom';

computePosition(reference, floating, {
  middleware: [offset(10)]
});
  • Можно задать фиксированное значение, массив для X и Y, либо функцию для динамического расчета.
  • Используется для создания отступов, например, между кнопкой и всплывающей подсказкой.

2. flip Обеспечивает автоматическую смену стороны, если элемент выходит за пределы видимой области.

import { flip } from '@floating-ui/dom';

computePosition(reference, floating, {
  middleware: [flip()]
});
  • Проверяет пространство вокруг reference-элемента.
  • Переставляет элемент на противоположную сторону при необходимости.
  • Можно настроить fallback-placements для последовательного перебора сторон.

3. shift Корректирует положение элемента, чтобы он не выходил за границы контейнера или окна.

import { shift } from '@floating-ui/dom';

computePosition(reference, floating, {
  middleware: [shift({ padding: 5 })]
});
  • padding задаёт отступ от краёв.
  • Полезно для предотвращения обрезания всплывающих подсказок и меню.

4. arrow Используется для позиционирования стрелки на всплывающем элементе, направленной на reference.

import { arrow } from '@floating-ui/dom';

const arrowElement = document.querySelector('.arrow');

computePosition(reference, floating, {
  middleware: [arrow({ element: arrowElement })]
});
  • Рассчитывает координаты стрелки, учитывая размеры floating и reference.
  • Возвращает координаты через data для последующего позиционирования DOM-элемента.

Создание пользовательских middleware

Любая функция может стать middleware при соблюдении контракта:

function myMiddleware(options) {
  return ({ x, y, rects, elements }) => {
    const newX = x + 10; // смещаем элемент на 10px вправо
    return { x: newX };
  };
}

Особенности пользовательских middleware:

  • Возможность получать данные из предыдущих middleware через объект data
  • Возможность инициировать reset, если необходимо изменить placement или другие параметры
  • Возможность объединять несколько middleware в цепочку для сложной логики позиционирования

Последовательность выполнения middleware

Middleware выполняются строго по порядку, указанному в массиве middleware. Каждый последующий получает уже обработанные координаты и данные.

Пример цепочки:

middleware: [
  offset(5),
  flip(),
  shift({ padding: 10 }),
  arrow({ element: arrowEl })
]
  1. Смещение на 5px
  2. Проверка и смена стороны, если элемент выходит за экран
  3. Корректировка позиции по краям окна
  4. Позиционирование стрелки

Такой подход обеспечивает гибкость и предсказуемость.


Ключевые преимущества middleware

  • Модульность: каждая функция решает отдельную задачу
  • Расширяемость: легко добавлять кастомные решения
  • Комбинируемость: функции можно объединять для сложных сценариев
  • Адаптивность: учитывают размеры viewport, динамическое содержание и смещения

Middleware делает Floating UI универсальным инструментом для создания динамических и интерактивных интерфейсов с точным управлением положением элементов.