Floating элементы — это панели, всплывающие окна, тултипы и дропдауны, которые позиционируются относительно другого элемента на странице. Важнейший аспект работы с ними — управление их видимостью, то есть открытием и закрытием. В библиотеке Floating UI это реализуется через комбинацию позиционирования, состояний и обработчиков событий.
Для управления отображением floating элемента используется состояние,
чаще всего реализуемое через React useState или любой
другой механизм управления состоянием:
import { useState } from "react";
const [isOpen, setIsOpen] = useState(false);
isOpen — булев флаг, определяющий, виден ли floating
элемент.setIsOpen — функция для изменения состояния.Ключевая идея: состояние isOpen
связывается с компонентом, который рендерит floating элемент, что
позволяет контролировать его появление и исчезновение динамически.
Floating элемент всегда позиционируется относительно активатора — кнопки, ссылки, иконки и т.п. Для этого используется реф активатора:
import { useRef } from "react";
const referenceRef = useRef(null);
referenceRef привязывается к DOM-элементу
активатора.Открытие floating элемента обычно реализуется через событие
click или hover:
isOpen переключается.Для событий наведения (hover) используется комбинация
onMouseEnter и onMouseLeave:
Floating UI предоставляет утилиты для закрытия floating элемента при
клике вне его области. Обычно это реализуется с помощью хука
useFloating:
import { useFloating, useClick, useDismiss } from "@floating-ui/react";
const { x, y, reference, floating, strategy } = useFloating({
open: isOpen,
onOpenChange: setIsOpen
});
useClick(reference, { enabled: true });
useDismiss(floating, { outsidePress: true });
useClick управляет открытием по клику.useDismiss отвечает за закрытие при клике вне floating
элемента или при нажатии клавиши Escape.Особенность: хук useDismiss можно
настраивать, например, отключить закрытие при клике на сам элемент.
Для плавного появления floating элемента можно использовать CSS-переходы или анимации:
.floating {
opacity: 0;
transform: translateY(-10px);
transition: opacity 0.2s ease, transform 0.2s ease;
}
.floating.open {
opacity: 1;
transform: translateY(0);
}
И в компоненте:
Меню
isOpen для управления
классом open.Закрытие floating элемента через клавиши повышает доступность:
useEffect(() => {
const handleKeyDown = (event) => {
if (event.key === "Escape") setIsOpen(false);
};
document.addEventListener("keydown", handleKeyDown);
return () => document.removeEventListener("keydown", handleKeyDown);
}, []);
Escape является стандартом для
модальных и всплывающих элементов.Когда на странице несколько floating элементов, важно управлять их приоритетом и состоянием:
isOpen для каждого
floating элемента.setIsOpen(false) на остальных элементах при
открытии нового.useDismiss с учётом конкретного
активатора.Пример:
const handleOpenMenu = () => {
setIsOpenMenu(true);
setIsOpenTooltip(false);
};
Иногда нужно, чтобы клики внутри floating элемента не закрывали его.
Для этого useDismiss поддерживает
insidePress:
useDismiss(floating, {
outsidePress: true,
insidePress: false
});
outsidePress: true — закрывает элемент при клике
вне.insidePress: false — игнорирует клики внутри floating
элемента.Полная интеграция открытия и закрытия выглядит так:
import { useState, useRef } from "react";
import { useFloating, useClick, useDismiss } from "@floating-ui/react";
function FloatingMenu() {
const [isOpen, setIsOpen] = useState(false);
const { x, y, reference, floating, strategy } = useFloating({
open: isOpen,
onOpenChange: setIsOpen
});
useClick(reference);
useDismiss(floating, { outsidePress: true });
return (
<>
{isOpen && (
- Пункт 1
- Пункт 2
- Пункт 3
)}
>
);
}
useClick и useDismiss
покрывает все стандартные сценарии взаимодействия.