Типизация в Floating UI

Библиотека Floating UI предоставляет мощный механизм позиционирования всплывающих элементов (tooltip, dropdown, popover) относительно якорных элементов. В средах с использованием TypeScript типизация играет ключевую роль в обеспечении надежности, предсказуемости и удобства разработки.

Типы в Floating UI охватывают:

  • координаты и геометрию
  • конфигурацию позиционирования
  • middleware
  • платформенные адаптеры
  • взаимодействие с DOM и виртуальными элементами

Базовые типы координат и размещения

Основой позиционирования является структура координат:

interface Coords {
  x: number;
  y: number;
}

Эти значения представляют финальную позицию floating-элемента.

Ключевым типом является Placement, определяющий расположение относительно якоря:

type Placement =
  | 'top'
  | 'bottom'
  | 'left'
  | 'right'
  | 'top-start'
  | 'top-end'
  | 'bottom-start'
  | 'bottom-end'
  | 'left-start'
  | 'left-end'
  | 'right-start'
  | 'right-end';

Типизация placement:

  • ограничивает допустимые значения
  • предотвращает ошибки при передаче строк
  • обеспечивает автодополнение в IDE

Типы для computePosition

Главная функция библиотеки:

computePosition(reference, floating, options)

Типизация параметров:

function computePosition(
  reference: ReferenceElement,
  floating: FloatingElement,
  options?: ComputePositionConfig
): Promise<ComputePositionReturn>;

ReferenceElement

type ReferenceElement =
  | Element
  | VirtualElement;

Поддержка виртуальных элементов позволяет позиционировать относительно:

  • курсора мыши
  • произвольных координат
  • кастомных областей

FloatingElement

type FloatingElement = HTMLElement;

Виртуальные элементы

Тип виртуального элемента:

interface VirtualElement {
  getBoundingClientRect(): DOMRect;
  contextElement?: Element;
}

Особенности:

  • имитирует DOM-элемент
  • предоставляет геометрию через getBoundingClientRect
  • может использоваться без реального DOM-узла

Типизация гарантирует:

  • корректный контракт
  • совместимость с внутренними алгоритмами

Конфигурация computePosition

interface ComputePositionConfig {
  placement?: Placement;
  strategy?: 'absolute' | 'fixed';
  middleware?: Middleware[];
  platform?: Platform;
}

placement

Тип строго ограничен Placement, исключая некорректные значения.

strategy

type Strategy = 'absolute' | 'fixed';

Типизация:

  • исключает передачу невалидных CSS-значений
  • упрощает интеграцию с layout-логикой

middleware

type Middleware = {
  name: string;
  options?: any;
  fn: MiddlewareFn;
};

Типы middleware

Middleware — ключевая часть Floating UI. Они обрабатывают позиционирование по цепочке.

MiddlewareFn

type MiddlewareFn = (state: MiddlewareState) => MiddlewareReturn;

MiddlewareState

interface MiddlewareState {
  x: number;
  y: number;
  initialPlacement: Placement;
  placement: Placement;
  strategy: Strategy;
  middlewareData: Record<string, any>;
  elements: {
    reference: ReferenceElement;
    floating: FloatingElement;
  };
  rects: {
    reference: Rect;
    floating: Rect;
  };
  platform: Platform;
}

Типизация состояния:

  • предоставляет полный контекст вычислений
  • делает middleware предсказуемыми
  • облегчает отладку

MiddlewareReturn

interface MiddlewareReturn {
  x?: number;
  y?: number;
  data?: Record<string, any>;
  reset?: {
    placement?: Placement;
    rects?: boolean;
  };
}

Особенности:

  • частичное обновление координат
  • передача данных между middleware
  • возможность перезапуска вычислений

Типизация встроенных middleware

Floating UI предоставляет готовые middleware с типизированными опциями.

offset

function offset(
  value: number | OffsetOptions
): Middleware;
interface OffsetOptions {
  mainAxis?: number;
  crossAxis?: number;
  alignmentAxis?: number | null;
}

flip

interface FlipOptions {
  fallbackPlacements?: Placement[];
  padding?: Padding;
  boundary?: Boundary;
}

shift

interface ShiftOptions {
  mainAxis?: boolean;
  crossAxis?: boolean;
  limiter?: {
    fn: (state: MiddlewareState) => Coords;
  };
}

Типизация:

  • строго описывает допустимые параметры
  • предотвращает логические ошибки
  • облегчает композицию middleware

Тип Platform

Абстракция платформы позволяет использовать Floating UI вне браузера.

interface Platform {
  getElementRects(...): Promise<ElementRects>;
  getClippingRect(...): Promise<Rect>;
  getDimensions(...): Promise<Dimensions>;
  convertOffsetParentRelativeRectToViewportRelativeRect(...): Rect;
  getOffsetParent(...): Promise<Element | null>;
  isElement(value: unknown): value is Element;
  getDocumentElement(...): Element;
}

Типизация:

  • задает контракт для кастомных платформ
  • позволяет адаптировать библиотеку под Canvas, WebGL, React Native

Возвращаемое значение computePosition

interface ComputePositionReturn {
  x: number;
  y: number;
  placement: Placement;
  strategy: Strategy;
  middlewareData: Record<string, any>;
}

Типизация результата:

  • гарантирует наличие координат
  • сохраняет финальное размещение
  • предоставляет доступ к данным middleware

Обобщенные типы и расширяемость

Floating UI активно использует дженерики для расширяемости.

Пример пользовательского middleware:

type CustomData = {
  myValue: number;
};

const myMiddleware: Middleware = {
  name: 'myMiddleware',
  fn(state) {
    return {
      data: {
        myValue: 42
      }
    };
  }
};

При строгой типизации можно расширить middlewareData:

interface ExtendedMiddlewareData {
  myMiddleware?: CustomData;
}

Это позволяет:

  • получать типизированный доступ к данным
  • избегать any
  • улучшать поддержку IDE

Типизация padding и boundary

type Padding =
  | number
  | {
      top?: number;
      right?: number;
      bottom?: number;
      left?: number;
    };
type Boundary =
  | Element
  | Element[]
  | 'clippingAncestors';

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

  • гибкость конфигурации
  • строгий контроль допустимых значений

Проверки типов (Type Guards)

Floating UI использует type guards:

function isElement(value: unknown): value is Element;

Это позволяет:

  • безопасно работать с DOM
  • избегать runtime-ошибок
  • улучшать контроль типов внутри middleware

Интеграция с React и типизация

При использовании с React:

const referenceRef = useRef<HTMLElement | null>(null);
const floatingRef = useRef<HTMLDivElement | null>(null);

Типизация:

  • гарантирует корректную работу с DOM
  • предотвращает null-ошибки
  • упрощает интеграцию с хуками

Стратегии строгой типизации

Эффективное использование типизации в Floating UI включает:

1. Избегание any

  • использование встроенных типов библиотеки
  • расширение через интерфейсы

2. Явное указание Placement

const placement: Placement = 'bottom-start';

3. Типизация middlewareData

const data = middlewareData.myMiddleware?.myValue;

4. Использование дженериков

  • для кастомных решений
  • для расширения платформы

Потенциальные ошибки и их предотвращение

Ошибка: неверный placement

placement: 'bottom-left' // ошибка

Типизация предотвращает это на этапе компиляции.

Ошибка: неправильная структура middleware

fn: () => ({ wrongField: true }) // ошибка типов

Ошибка: работа с null-элементами

referenceRef.current.getBoundingClientRect() // возможна ошибка

Решение:

if (referenceRef.current) {
  // безопасно
}

Влияние типизации на архитектуру

Типизация в Floating UI:

  • формализует API
  • делает middleware композиционными
  • упрощает переносимость между платформами
  • обеспечивает масштабируемость

Строгие типы превращают библиотеку из утилиты позиционирования в устойчивый фундамент для построения сложных UI-систем.