Template integration

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


Основные концепции

Reference и Floating элементы В Floating UI ключевыми являются два элемента: reference (якорь) и floating (всплывающий элемент). Reference — это элемент, относительно которого позиционируется всплывающий компонент, а floating — сам компонент, который необходимо разместить.

Middleware Middleware позволяют управлять поведением позиционирования. Они могут автоматически смещать элемент (offset), избегать выхода за границы экрана (flip, shift), добавлять стрелки (arrow) и многое другое. Middleware применяется в цепочке и возвращает объект с координатами, которые затем используются для рендеринга.

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

const reference = document.querySelector('#button');
const floating = document.querySelector('#tooltip');
const arrowElement = floating.querySelector('.arrow');

computePosition(reference, floating, {
  placement: 'top',
  middleware: [
    offset(8),
    flip(),
    shift({ padding: 5 }),
    arrow({ element: arrowElement })
  ]
}).then(({ x, y, middlewareData }) => {
  Object.assign(floating.style, {
    left: `${x}px`,
    top: `${y}px`
  });
  Object.assign(arrowElement.style, {
    left: `${middlewareData.arrow?.x ?? 0}px`,
    top: `${middlewareData.arrow?.y ?? 0}px`
  });
});

Использование с HTML-шаблонами

Floating UI легко интегрируется с системами шаблонов, такими как Handlebars, EJS или Vue/React-шаблонами. Основной принцип — создание DOM-элементов из шаблона, после чего Floating UI позиционирует их относительно якоря.

Пример с шаблоном Handlebars:

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

const template = Handlebars.compile(`
  <div class="tooltip">
    {{text}}
    <div class="arrow"></div>
  </div>
`);

const tooltipHTML = template({ text: 'Подсказка' });
document.body.insertAdjacentHTML('beforeend', tooltipHTML);

const reference = document.querySelector('#button');
const floating = document.querySelector('.tooltip');
const arrowElement = floating.querySelector('.arrow');

computePosition(reference, floating, {
  placement: 'bottom',
  middleware: [offset(10), flip()]
}).then(({ x, y }) => {
  floating.style.left = `${x}px`;
  floating.style.top = `${y}px`;
});

Здесь Floating UI обеспечивает точное позиционирование, а шаблон отвечает за визуальную структуру.


Реактивные шаблоны и динамическое обновление

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

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

const cleanup = autoUpdate(reference, floating, () => {
  computePosition(reference, floating, { placement: 'top' }).then(({ x, y }) => {
    floating.style.left = `${x}px`;
    floating.style.top = `${y}px`;
  });
});

// Для остановки наблюдения
// cleanup();

Использование autoUpdate особенно полезно при работе с шаблонами, где контент может динамически изменяться (например, при подгрузке данных или открытии модального окна с переменным содержимым).


Поддержка стрелок в шаблонах

Arrow middleware позволяет позиционировать стрелку относительно всплывающего элемента. В шаблоне можно заранее определить элемент стрелки и подключить его к Floating UI.

<div class="tooltip">
  <span>Текст подсказки</span>
  <div class="arrow"></div>
</div>

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


Параметры интеграции и оптимизация

  • placement — указывает предпочтительное направление (top, bottom, left, right) или комбинацию (top-start, bottom-end).
  • strategyabsolute или fixed; влияет на поведение при прокрутке и позиционировании относительно viewport.
  • middleware — цепочка функций для смещения, переворота, ограничения и работы с стрелкой.
  • autoUpdate — для реактивного пересчета при изменениях DOM и размеров окна.

Оптимизация заключается в минимизации количества пересчетов и правильном использовании middleware. Например, для статичных тултипов достаточно одного вызова computePosition, тогда как для динамических меню с анимацией лучше использовать autoUpdate.


Интеграция с фреймворками

React В React Floating UI используется через хуки, например useFloating. Шаблон реализуется в JSX:

import { useFloating } from '@floating-ui/react';

function Tooltip({ children, label }) {
  const { x, y, reference, floating } = useFloating({ placement: 'top' });

  return (
    <>
      <button ref={reference}>{children}</button>
      <div ref={floating} style={{ position: 'absolute', left: x ?? 0, top: y ?? 0 }}>
        {label}
        <div className="arrow"></div>
      </div>
    </>
  );
}

Vue В Vue Floating UI интегрируется через ref и реактивные вычисления позиции:

<template>
  <button ref="reference">Кнопка</button>
  <div ref="floating" :style="{ left: `${x}px`, top: `${y}px` }">
    Подсказка
    <div class="arrow"></div>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue';
import { computePosition, offset, flip } from '@floating-ui/dom';

const reference = ref(null);
const floating = ref(null);
const x = ref(0);
const y = ref(0);

onMounted(() => {
  computePosition(reference.value, floating.value, { placement: 'bottom', middleware: [offset(8), flip()] })
    .then(({ x: px, y: py }) => {
      x.value = px;
      y.value = py;
    });
});
</script>

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

  • Всегда создавать DOM элемента до вызова computePosition или autoUpdate.
  • Использовать middleware для обработки крайних случаев выхода за границы экрана.
  • Для динамических интерфейсов включать autoUpdate, чтобы позиции всплывающих элементов корректировались автоматически.
  • Разделять визуальную структуру (шаблон) и позиционирование (Floating UI), чтобы обеспечить гибкость и переносимость компонентов.

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