Динамические стили из middleware

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

Middleware вызываются на каждом кадре при обновлении позиции элемента и могут:

  • Смещать элемент относительно якоря (offset).
  • Изменять ориентацию и размещение для предотвращения выхода за границы окна (flip и shift).
  • Добавлять динамические эффекты вроде автоматического смещения по вертикали или горизонтали.

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

Middleware в Floating UI подключаются через массив middleware, передаваемый в функцию computePosition. Пример базовой структуры:

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

const referenceElement = document.querySelector('#button');
const floatingElement = document.querySelector('#tooltip');

computePosition(referenceElement, floatingElement, {
  placement: 'top',
  middleware: [
    offset(10),
    flip(),
    shift({ padding: 5 })
  ]
}).then(({ x, y }) => {
  Object.assign(floatingElement.style, {
    left: `${x}px`,
    top: `${y}px`
  });
});
  • offset(10) — добавляет отступ 10px от якоря.
  • flip() — проверяет доступное пространство и переворачивает позицию при необходимости.
  • shift({ padding: 5 }) — смещает элемент, чтобы он не выходил за границы окна, с отступом 5px.

Middleware можно комбинировать в любом порядке, что дает гибкость при динамическом позиционировании.

Динамическое управление стилями через middleware

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

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

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

computePosition(referenceElement, floatingElement, {
  placement: 'top',
  middleware: [
    offset(10),
    flip(),
    shift({ padding: 5 }),
    arrow({ element: arrowElement })
  ]
}).then(({ x, y, middlewareData }) => {
  const { x: arrowX, y: arrowY } = middlewareData.arrow;
  Object.assign(floatingElement.style, {
    left: `${x}px`,
    top: `${y}px`
  });
  Object.assign(arrowElement.style, {
    left: arrowX != null ? `${arrowX}px` : '',
    top: arrowY != null ? `${arrowY}px` : ''
  });
});

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

Пользовательские middleware

Floating UI позволяет создавать собственные middleware, возвращающие не только координаты, но и дополнительные данные для стилизации:

function dynamicBackground(options) {
  return {
    name: 'dynamicBackground',
    fn({ placement, rects }) {
      const background = rects.reference.width > 100 ? 'lightblue' : 'lightcoral';
      return { data: { background } };
    }
  };
}

computePosition(referenceElement, floatingElement, {
  middleware: [dynamicBackground()]
}).then(({ middlewareData }) => {
  floatingElement.style.backgroundColor = middlewareData.dynamicBackground.background;
});

В данном примере middleware dynamicBackground вычисляет цвет фона в зависимости от ширины элемента-источника. Это демонстрирует, как middleware может использоваться не только для позиционирования, но и для динамических стилей на основе состояния интерфейса.

Управление реактивностью

Для обеспечения плавного и динамического поведения рекомендуется использовать autoUpdate:

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

autoUpdate(referenceElement, floatingElement, () => {
  computePosition(referenceElement, floatingElement, {
    placement: 'top',
    middleware: [
      offset(10),
      flip(),
      shift({ padding: 5 }),
      arrow({ element: arrowElement }),
      dynamicBackground()
    ]
  }).then(({ x, y, middlewareData }) => {
    Object.assign(floatingElement.style, {
      left: `${x}px`,
      top: `${y}px`,
      backgroundColor: middlewareData.dynamicBackground.background
    });
    Object.assign(arrowElement.style, {
      left: middlewareData.arrow.x != null ? `${middlewareData.arrow.x}px` : '',
      top: middlewareData.arrow.y != null ? `${middlewareData.arrow.y}px` : ''
    });
  });
});

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

Советы по оптимизации

  • Минимизировать количество middleware при высокой частоте обновлений. Каждый middleware вызывается при каждом пересчете позиции.
  • Использовать middlewareData для передачи информации между middleware, чтобы избежать повторных вычислений.
  • Делать стили CSS как можно проще — для плавной отрисовки рекомендуется использовать transform: translate вместо прямой установки top и left.

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