Композаблы

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

Принцип работы

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

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

const composedMiddleware = compose([
  offset(10),
  flip(),
  shift({ padding: 5 })
]);

В этом примере создается последовательность обработчиков:

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

Каждый модуль принимает контекст, содержащий текущее положение, размеры целевого элемента, размеры контейнера и дополнительные параметры. Возвращаемое значение модуля обычно модифицирует координаты x и y.

Создание собственных композаблов

Floating UI поддерживает кастомные композаблы, что позволяет расширять функциональность стандартных модулей. Для этого создается функция, которая принимает объект context и возвращает результат работы:

function customArrow({ x, y, placement, middlewareData }) {
  // Смещение стрелки на половину ширины
  const arrowOffset = 8;
  return { x: x + arrowOffset, y };
}

const myMiddleware = compose([
  offset(10),
  flip(),
  customArrow
]);

Особенности создания кастомного композабла:

  • Объект context содержит все координаты и данные предыдущих модулей.
  • Возвращаемый объект может содержать новые координаты или дополнительные свойства.
  • Кастомные модули могут взаимодействовать с другими стандартными модификаторами, сохраняя порядок их применения.

Использование в React

В React Floating UI предоставляет хуки, которые полностью интегрируются с концепцией композаблов. Основной хук — useFloating, где композаблы передаются через массив middleware:

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

const { x, y, reference, floating, strategy } = useFloating({
  middleware: [offset(10), flip(), shift({ padding: 5 })]
});

Хук возвращает:

  • x, y — координаты для позиционирования.
  • strategy — стратегия позиционирования (absolute или fixed).
  • reference — функция, привязывающая целевой элемент.
  • floating — функция, привязывающая всплывающий элемент.

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

Порядок применения композаблов

Порядок, в котором композаблы указаны в compose или в массиве middleware, критически важен:

  1. Модули применяются последовательно.
  2. Результаты предыдущих композаблов становятся входными данными для следующих.
  3. Неправильный порядок может привести к неверному позиционированию или конфликтам между смещениями и переворотами.

Встроенные композаблы

Floating UI предоставляет множество готовых модулей:

  • offset() — смещение от якоря.
  • flip() — автоматическое изменение позиции.
  • shift() — предотвращение выхода за границы окна.
  • size() — динамическое изменение размеров всплывающего элемента.
  • arrow() — поддержка стрелки, указывающей на элемент.

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

Примеры сложных комбинаций

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

const complexMiddleware = compose([
  offset(8),
  flip({ fallbackPlacements: ['top', 'left'] }),
  shift({ padding: 10 }),
  size({
    apply({ availableWidth, availableHeight, elements }) {
      Object.assign(elements.floating.style, {
        maxWidth: `${availableWidth}px`,
        maxHeight: `${availableHeight}px`
      });
    }
  }),
  arrow({ element: document.querySelector('.arrow') })
]);

В этом примере одновременно решаются несколько задач:

  • Позиционирование с отступом.
  • Автоматический выбор оптимальной стороны при нехватке места.
  • Контроль размеров элемента под доступное пространство.
  • Добавление стрелки с динамическим позиционированием.

Рекомендации по работе с композаблами

  • Всегда группировать модули по логическому принципу: сначала смещения, затем ограничения по экрану, затем визуальные модификаторы (стрелки, размеры).
  • Использовать кастомные композаблы для специфичных требований интерфейса.
  • Тестировать взаимодействие композаблов на разных разрешениях и в разных браузерах, чтобы избежать неожиданных конфликтов.

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