Комбинирование взаимодействий

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


Механизм useInteractions

Базовым инструментом для комбинирования взаимодействий является хук useInteractions. Он принимает массив взаимодействий (interactions) и возвращает объект с методами для привязки к элементам: getReferenceProps, getFloatingProps и другие. Эти методы объединяют поведение всех переданных взаимодействий, обеспечивая синхронную работу клика, наведения и фокуса.

Пример:

import { useFloating, useClick, useHover, useInteractions } from '@floating-ui/react';

const MyComponent = () => {
  const { x, y, reference, floating, strategy } = useFloating();
  const click = useClick({ enabled: true });
  const hover = useHover({ delay: 100 });
  const interactions = useInteractions([click, hover]);

  return (
    <>
      <button ref={interactions.getReferenceProps(reference)}>Открыть меню</button>
      <div ref={interactions.getFloatingProps(floating)} style={{ position: strategy, top: y ?? 0, left: x ?? 0 }}>
        Меню
      </div>
    </>
  );
};

В этом примере кнопка одновременно реагирует на клик и наведение, а useInteractions гарантирует корректное объединение событий.


Настройка приоритетов и задержек

При комбинировании взаимодействий часто требуется контролировать приоритет событий. Например, нужно, чтобы меню открывалось по клику мгновенно, а по наведению — с задержкой. Для этого в настройках useHover используется параметр delay, а useClick можно включать или отключать динамически через enabled.

const hover = useHover({ delay: { open: 200, close: 100 } });
const click = useClick({ enabled: true });

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


Комбинирование фокуса и наведения

В интерактивных формах или меню важно, чтобы элемент оставался открытым при переходе фокуса с клавиатуры. Floating UI предоставляет useFocus для управления этим поведением. Его удобно комбинировать с useHover:

const hover = useHover({ delay: 100, handleClose: safePolygon });
const focus = useFocus();
const interactions = useInteractions([hover, focus]);

Функция safePolygon предотвращает закрытие меню при движении курсора по траектории между элементами, создавая естественное поведение, аналогичное нативным dropdown.


Использование useRole для семантики

Для повышения доступности элементов можно подключить useRole. Например, если всплывающее меню является списком или тултипом, useRole назначает соответствующий ARIA-атрибут:

const role = useRole({ role: 'menu' });
const interactions = useInteractions([click, hover, role]);

В результате элемент становится доступным для экранных читалок, а поведение взаимодействий остаётся консистентным.


Комбинированные стратегии закрытия

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

const dismiss = useDismiss({ outsidePress: true, escapeKey: true });
const interactions = useInteractions([click, hover, dismiss]);

Такое объединение обеспечивает, что элемент корректно закроется при любом типе взаимодействия, не конфликтуя с открытием по другим событиям.


Сложные сценарии: меню с вложенными уровнями

Для вложенных меню комбинируются несколько уровней useFloating и наборов взаимодействий:

  • Родительский элемент использует useClick и useHover для открытия первого уровня.
  • Вложенный уровень подключает аналогичный набор, при этом handleClose на hover предотвращает преждевременное закрытие родителя.
  • Все взаимодействия объединяются через useInteractions, обеспечивая плавное переключение между уровнями.

Пример структуры:

const parentHover = useHover({ delay: 150 });
const parentClick = useClick();
const parentInteractions = useInteractions([parentHover, parentClick]);

const childHover = useHover({ delay: 100 });
const childClick = useClick();
const childInteractions = useInteractions([childHover, childClick]);

Такой подход позволяет создать полноценное многоуровневое меню с естественным поведением для мыши и клавиатуры.


Рекомендации по производительности

  • Объединять только необходимые взаимодействия, чтобы не перегружать обработчики событий.
  • Использовать delay и handleClose для предотвращения лишних перерендеров при быстром движении курсора.
  • Минимизировать количество вложенных уровней useFloating, комбинируя поведение через useInteractions.

Эти принципы помогают поддерживать отзывчивость интерфейса даже при сложных комбинированных сценариях.