Popover — это плавающий элемент интерфейса, который появляется относительно другого элемента, называемого reference element. Библиотека Floating UI предоставляет инструменты для точного позиционирования таких элементов с учётом ограничений видимой области (viewport), коллизий с другими элементами и автоматического обновления позиции при изменении размера или прокрутки.
В основе Popover лежит функция
computePosition, которая принимает два основных
аргумента: элемент-источник и плавающий элемент, а также объект с
опциями. Простейший пример использования:
import { computePosition, offset, flip, shift } from '@floating-ui/dom';
const reference = document.querySelector('#button');
const popover = document.querySelector('#popover');
computePosition(reference, popover, {
placement: 'bottom-start',
middleware: [offset(10), flip(), shift({ padding: 5 })],
}).then(({ x, y }) => {
Object.assign(popover.style, {
left: `${x}px`,
top: `${y}px`,
position: 'absolute',
});
});
Здесь важно понимать ключевые моменты:
top, bottom, left,
right и их вариации, например,
bottom-start).Middleware — это мощный инструмент для управления динамическим поведением Popover. Основные встроенные middleware:
Пример использования arrow middleware:
import { arrow } from '@floating-ui/dom';
const arrowElement = document.querySelector('#arrow');
computePosition(reference, popover, {
placement: 'top',
middleware: [offset(10), flip(), shift(), arrow({ element: arrowElement })],
}).then(({ x, y, middlewareData }) => {
Object.assign(popover.style, {
left: `${x}px`,
top: `${y}px`,
});
const { x: arrowX, y: arrowY } = middlewareData.arrow;
Object.assign(arrowElement.style, {
left: arrowX != null ? `${arrowX}px` : '',
top: arrowY != null ? `${arrowY}px` : '',
});
});
Floating UI позволяет автоматически обновлять позицию
Popover, если reference или viewport меняют размеры. Для этого
используется функция autoUpdate:
import { autoUpdate } from '@floating-ui/dom';
const cleanup = autoUpdate(reference, popover, () => {
computePosition(reference, popover, {
placement: 'bottom',
middleware: [offset(8), flip(), shift()],
}).then(({ x, y }) => {
Object.assign(popover.style, { left: `${x}px`, top: `${y}px` });
});
});
// Вызов cleanup() прекратит автоматическое обновление
Это особенно важно для динамически изменяющихся интерфейсов, например, при анимации, скролле или изменении размеров окна.
Floating UI можно использовать не только с чистым DOM, но и с React,
Vue и Svelte. В React есть отдельный пакет
@floating-ui/react-dom с хуками, которые упрощают работу с
состоянием:
import { useFloating, offset, flip, shift, arrow } from '@floating-ui/react-dom';
function Popover({ referenceRef, open }) {
const { x, y, reference, floating, strategy, middlewareData } = useFloating({
placement: 'bottom',
middleware: [offset(10), flip(), shift(), arrow({ element: arrowRef })],
});
return open ? (
<div ref={floating} style={{ position: strategy, left: x ?? 0, top: y ?? 0 }}>
Контент Popover
<div ref={arrowRef} className="arrow" />
</div>
) : null;
}
Здесь хуки автоматически обновляют позицию при изменении размеров и
скролле, избавляя от необходимости вручную вызывать
computePosition.
mousedown и
touchstart.autoUpdate только для видимых
Popover.Popover с Floating UI предоставляет гибкое и высокопроизводительное решение для любых всплывающих элементов, сочетая точное позиционирование, обработку коллизий, стрелки, динамическое обновление и интеграцию с современными фреймворками.