Generic типы

Popper.js — это мощная библиотека для создания динамических всплывающих элементов, таких как тултипы, всплывающие меню и подсказки, которые позиционируются относительно других элементов на странице. В основе работы Popper.js лежит концепция поппера (popper — всплывающий элемент) и референс-элемента (reference element), относительно которого этот поппер размещается.

Создание поппера

Поппер создаётся через конструктор Popper или через функцию createPopper. Основная структура:

import { createPopper } from '@popperjs/core';

const referenceElement = document.querySelector('#button');
const popperElement = document.querySelector('#tooltip');

const popperInstance = createPopper(referenceElement, popperElement, {
  placement: 'top', // место размещения: top, bottom, left, right
  modifiers: [],
});
  • referenceElement — элемент, к которому будет привязан поппер.
  • popperElement — сам всплывающий элемент.
  • placement — позиция относительно референс-элемента.
  • modifiers — массив объектов, которые изменяют поведение поппера.

Основные модификаторы

Модификаторы позволяют контролировать динамическое поведение поппера:

  • offset — смещение поппера относительно референса:
modifiers: [
  {
    name: 'offset',
    options: {
      offset: [0, 8], // по оси X и Y
    },
  },
]
  • flip — автоматическая смена стороны при нехватке места:
modifiers: [
  {
    name: 'flip',
    options: {
      fallbackPlacements: ['bottom', 'right'],
    },
  },
]
  • preventOverflow — предотвращает выход поппера за пределы контейнера:
modifiers: [
  {
    name: 'preventOverflow',
    options: {
      boundary: 'viewport',
    },
  },
]

Generic типы в Popper.js

Popper.js использует TypeScript Generic типы для строгой типизации конфигураций, что обеспечивает безопасность и автокомплитацию. Основной generic тип — это <Modifiers>, который позволяет указать набор модификаторов, используемых для конкретного поппера.

import type { Modifier, Placement } from '@popperjs/core';

interface MyModifiers extends Array<Modifier<any, any>> {}

const popperInstance = createPopper<HTMLButtonElement, HTMLDivElement, MyModifiers>(
  referenceElement,
  popperElement,
  {
    placement: 'top',
    modifiers: [
      { name: 'offset', options: { offset: [0, 10] } },
    ],
  }
);
  • <HTMLButtonElement, HTMLDivElement, MyModifiers> — типы для референса, поппера и модификаторов.
  • Позволяет TypeScript точно проверять корректность использования каждого модификатора и опций.

Кастомные модификаторы с типами

Создание собственного модификатора требует указания типа:

const customModifier: Modifier<'myModifier', { factor: number }> = {
  name: 'myModifier',
  enabled: true,
  phase: 'write',
  fn({ state, options }) {
    const factor = options.factor;
    state.styles.popper.top = `${parseFloat(state.styles.popper.top) * factor}px`;
  },
  options: {
    factor: 1.5,
  },
};
  • Modifier<'myModifier', { factor: number }> — generic тип, где первый параметр имя модификатора, второй — структура опций.
  • Позволяет TypeScript валидировать структуру options и предотвратить ошибки.

Расширение типов модификаторов

Generic типы обеспечивают возможность расширять стандартные модификаторы:

type ExtendedModifiers = Modifier<'offset', { offset: [number, number] }> | Modifier<'flip', { fallbackPlacements: Placement[] }>;

const popperInstance = createPopper<HTMLDivElement, HTMLDivElement, ExtendedModifiers>(
  referenceElement,
  popperElement,
  {
    placement: 'right',
    modifiers: [
      { name: 'offset', options: { offset: [0, 15] } },
      { name: 'flip', options: { fallbackPlacements: ['bottom'] } },
    ],
  }
);
  • Позволяет точно описывать используемые модификаторы.
  • Упрощает поддержку больших проектов с множеством попперов.

Типизация функций обратного вызова

Generic типы применяются также к колбэкам модификаторов:

type ModifierFn<Options = any> = (args: {
  state: any;
  instance: any;
  options: Options;
}) => void;

const logModifier: ModifierFn<{ prefix: string }> = ({ state, options }) => {
  console.log(options.prefix, state);
};
  • Позволяет использовать строгую типизацию options.
  • Уменьшает риск ошибок при работе с динамическими данными.

Итоги применения generic типов

  • Строгая проверка типов — гарантирует правильное использование модификаторов.
  • Автокомплитация в IDE — ускоряет разработку.
  • Гибкость расширений — легко создавать кастомные модификаторы и описывать их опции.
  • Поддержка больших проектов — особенно критично при сложных всплывающих интерфейсах с множеством правил позиционирования.

Generic типы в Popper.js делают библиотеку безопасной, масштабируемой и удобной для интеграции в проекты на TypeScript. Они позволяют описывать сложное поведение попперов без потери контроля над типами и предотвращают распространённые ошибки на этапе компиляции.