Mapping конфигурации

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


Понятие Mapping

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

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

Mapping работает через функцию обратного вызова, которая получает исходные параметры и возвращает новые значения. Пример базового синтаксиса:

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

const mapping = (config) => ({
  ...config,
  middleware: [
    offset({mainAxis: config.offset || 10}),
    ...config.middleware
  ]
});

const elementConfig = {placement: 'bottom', offset: 20};

computePosition(referenceElement, floatingElement, mapping(elementConfig))
  .then(({x, y}) => {
    Object.assign(floatingElement.style, {
      left: `${x}px`,
      top: `${y}px`
    });
  });

Здесь функция mapping принимает объект конфигурации и добавляет динамическое смещение, сохраняя остальные параметры без изменений.


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

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

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

const dynamicMapping = (config) => {
  const middleware = [];

  if (window.innerWidth < 768) {
    middleware.push(offset({mainAxis: 5}));
  } else {
    middleware.push(offset({mainAxis: 15}));
  }

  if (config.enableFlip) {
    middleware.push(flip());
  }

  middleware.push(shift({padding: 8}));

  return {...config, middleware};
};

computePosition(referenceElement, floatingElement, dynamicMapping({placement: 'top', enableFlip: true}));

Особенности подхода:

  • Middleware добавляется программно, что позволяет адаптировать позиционирование под любые размеры и ориентации.
  • Mapping может возвращать как новый массив middleware, так и обновленный объект конфигурации, включая placement, strategy и другие параметры.

Mapping для комплексных сценариев

В реальных проектах часто требуется, чтобы позиционирование зависело не только от размера окна, но и от состояния элемента или контента. Например, тултип, который меняет свое положение при переполнении:

const adaptiveMapping = (config, element) => {
  const {clientHeight} = element;
  const mainAxisOffset = clientHeight > 100 ? 20 : 10;

  return {
    ...config,
    middleware: [
      offset({mainAxis: mainAxisOffset}),
      flip(),
      shift({padding: 12})
    ]
  };
};

computePosition(referenceElement, floatingElement, adaptiveMapping({placement: 'bottom'}, floatingElement));

Здесь offset подстраивается под высоту элемента, а остальные middleware обеспечивают корректное отображение при изменении контента.


Переиспользуемые Mapping-функции

Для упрощения поддержки кода рекомендуется создавать универсальные функции mapping, которые можно применять к любым элементам:

export const defaultMapping = (config) => ({
  ...config,
  middleware: [
    offset({mainAxis: 10}),
    flip({fallbackPlacements: ['top', 'right', 'left']}),
    shift({padding: 8})
  ]
});

// Применение
computePosition(referenceElement, floatingElement, defaultMapping({placement: 'bottom'}));

Преимущества:

  • Сокращение повторяющегося кода.
  • Централизованное управление параметрами позиционирования.
  • Возможность быстро изменять поведение всех всплывающих элементов проекта.

Динамическое наложение нескольких Mapping

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

const combineMappings = (...mappings) => (config) =>
  mappings.reduce((acc, fn) => fn(acc), config);

const combined = combineMappings(
  (cfg) => ({...cfg, offset: 10}),
  (cfg) => ({...cfg, placement: 'top'}),
  (cfg) => ({...cfg, middleware: [...(cfg.middleware || []), flip()]})
);

computePosition(referenceElement, floatingElement, combined({}));

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


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

  1. Минимизировать тяжелые вычисления внутри mapping — функция вызывается при каждом обновлении позиции.
  2. Использовать fallback-значения для параметров, которые могут отсутствовать в исходной конфигурации.
  3. Комбинировать mapping и middleware для динамического поведения при изменении размеров окна, скролла или состояния контента.
  4. Проверять совместимость с strategyabsolute и fixed могут требовать разных смещений.
  5. Документировать стандартные mapping в проекте, чтобы новые разработчики понимали логику позиционирования.

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