Комбинирование с другими middleware

Floating UI предоставляет мощную архитектуру для управления позиционированием всплывающих элементов, таких как тултипы, поповеры и dropdown-меню. Ключевым аспектом является использование middleware — функций, которые изменяют или корректируют поведение позиции floating-элемента. Возможность комбинирования нескольких middleware позволяет создавать гибкие и предсказуемые интерфейсы.


Основы middleware

Каждое middleware в Floating UI получает объект state с текущими координатами (x, y), размерами элементов (rects), а также контекстом позиционирования. Middleware могут:

  • Смещать элемент относительно целевого (offset),
  • Перенаправлять элемент при ограничении пространства (flip),
  • Обеспечивать автоматическое изменение позиции при изменении размеров окна (autoUpdate),
  • Ограничивать видимую область элемента (shift).

Каждое middleware возвращает объект с изменёнными координатами и, при необходимости, дополнительными данными.


Принцип комбинирования

Middleware выполняются последовательно в порядке объявления. Выходные данные одного middleware становятся входными для следующего. Это позволяет строить цепочки, например:

import { computePosition, offset, flip, shift } from '@floating-ui/dom';

computePosition(reference, floating, {
  middleware: [
    offset(10),
    flip(),
    shift({ padding: 5 })
  ],
});

В этом примере происходит:

  1. Смещение floating-элемента на 10 пикселей от reference.
  2. Переключение стороны, если выбранная позиция не помещается на экране.
  3. Сдвиг, чтобы элемент не выходил за границы видимой области, с отступом 5 пикселей.

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


Использование кастомных middleware

Floating UI поддерживает создание собственных middleware. Структура middleware обычно выглядит так:

function customMiddleware(options) {
  return {
    name: 'customMiddleware',
    fn(state) {
      const { x, y } = state;
      // Пример: корректировка координат
      return { x: x + (options.dx || 0), y: y + (options.dy || 0) };
    }
  };
}

computePosition(reference, floating, {
  middleware: [
    offset(5),
    customMiddleware({ dx: 10, dy: -5 }),
    flip()
  ],
});

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


Взаимодействие нескольких middleware

  1. offset + shift offset изменяет позицию элемента относительно reference, а shift корректирует координаты, если элемент выходит за пределы видимой области. Это позволяет реализовать динамические смещения с учётом ограничений экрана.

  2. flip + shift flip выбирает оптимальную сторону для отображения floating-элемента. Если выбранная сторона всё ещё выходит за пределы viewport, shift корректирует позицию, чтобы элемент оставался видимым.

  3. arrow + offset Middleware arrow управляет положением стрелки относительно reference. В комбинации с offset можно точно позиционировать стрелку независимо от смещения всего floating-элемента.

  4. autoUpdate с middleware цепочкой Функция autoUpdate следит за изменением размеров и позиции reference или floating-элемента, вызывая пересчёт цепочки middleware автоматически. Это обеспечивает постоянное корректное позиционирование при изменении окна или контента.


Передача данных между middleware

Некоторые middleware могут добавлять данные в state.middlewareData, которые могут использоваться последующими middleware. Например:

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

computePosition(reference, floating, {
  middleware: [
    shift({ padding: 5 }),
    arrow({ element: arrowElement, padding: 10 })
  ]
});

Здесь arrow использует информацию о сдвиге, созданную shift, чтобы корректно разместить стрелку относительно floating-элемента.


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

  • Всегда тестировать порядок middleware, особенно при использовании flip и shift.
  • Для сложных интерфейсов создавать цепочки middleware с учётом зависимостей данных, передаваемых между ними.
  • Использовать кастомные middleware для уникальных требований позиционирования, чтобы расширять стандартный набор функционала.
  • Минимизировать количество middleware в цепочке, если производительность критична, так как каждый middleware выполняет расчёт координат.

Пример сложной цепочки

computePosition(reference, floating, {
  middleware: [
    offset({ mainAxis: 10, crossAxis: 5 }),
    flip({ fallbackPlacements: ['top', 'bottom', 'right'] }),
    shift({ padding: 8 }),
    arrow({ element: arrowEl, padding: 5 }),
    customMiddleware({ dx: 2, dy: -2 })
  ],
});

Такой подход обеспечивает:

  • Смещение floating-элемента на заданное расстояние,
  • Автоматический выбор стороны при ограниченном пространстве,
  • Корректировку выхода за границы экрана,
  • Точное позиционирование стрелки,
  • Дополнительную тонкую настройку координат через кастомный middleware.

Комбинирование middleware позволяет гибко управлять поведением floating-элементов, учитывая ограничения экрана, пользовательские смещения и точное позиционирование стрелок. Такой подход обеспечивает предсказуемое поведение UI-компонентов при любых сценариях взаимодействия.