Типы для модификаторов

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


1. Основная структура модификатора

Модификатор в Popper.js представляет собой объект с обязательными и опциональными полями:

const exampleModifier = {
  name: 'example',
  enabled: true,
  phase: 'main',
  fn: ({ state, name, options }) => {
    // логика модификатора
  },
  effect: ({ state, name, options }) => {
    // побочный эффект модификатора
    return () => {
      // функция очистки
    };
  },
  options: {
    key1: 'value1',
    key2: 42
  },
};

Ключевые свойства:

  • name — уникальное имя модификатора.
  • enabled — флаг включения/отключения.
  • phase — этап жизненного цикла (beforeRead, read, afterRead, beforeMain, main, afterMain, beforeWrite, write, afterWrite).
  • fn — функция, выполняющая основное действие модификатора.
  • effect — функция побочного эффекта, которая вызывается один раз при инициализации.
  • options — объект с пользовательскими настройками.

2. Типы данных для options

Popper.js позволяет создавать модификаторы с различными типами опций. Стандартно выделяют несколько категорий:

  1. Булевы значения (boolean)

Используются для включения или отключения специфического поведения модификатора.

const hideModifier = {
  name: 'hide',
  enabled: true,
  options: {
    checkOverflow: true
  }
};
  1. Числа (number)

Применяются для указания расстояний, задержек, смещений и коэффициентов масштабирования.

const offsetModifier = {
  name: 'offset',
  enabled: true,
  options: {
    offset: [0, 10] // [skidding, distance]
  }
};
  1. Строки (string)

Обычно указывают направление (start, end, top, bottom, left, right) или стратегию позиционирования (absolute, fixed).

const flipModifier = {
  name: 'flip',
  enabled: true,
  options: {
    fallbackPlacements: ['top', 'right']
  }
};
  1. Массивы (array)

Используются для перечисления допустимых значений, например fallback-позиций или списка элементов для наблюдения.

const preventOverflowModifier = {
  name: 'preventOverflow',
  enabled: true,
  options: {
    altAxis: true,
    padding: [5, 10, 5, 10] // [top, right, bottom, left]
  }
};
  1. Объекты (object)

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

const arrowModifier = {
  name: 'arrow',
  enabled: true,
  options: {
    element: document.querySelector('.arrow'),
    padding: { top: 5, bottom: 5, left: 8, right: 8 }
  }
};
  1. Функции (function)

Используются для динамического расчёта значения модификатора во время его выполнения.

const computeStylesModifier = {
  name: 'computeStyles',
  enabled: true,
  options: {
    adaptive: (state) => state.rects.reference.width > 100
  }
};

3. Типизация через TypeScript

Popper.js поставляется с типами, которые помогают строго контролировать структуры модификаторов. Основной тип модификатора в TypeScript выглядит так:

interface Modifier<Name extends string = string, Options = any> {
  name: Name;
  enabled?: boolean;
  phase?: ModifierPhases;
  requires?: string[];
  requiresIfExists?: string[];
  fn: ModifierFn<Options>;
  effect?: ModifierEffect<Options>;
  options?: Partial<Options>;
}

Ключевые моменты типизации:

  • Name — строковый литерал, уникальный для каждого модификатора.
  • Options — интерфейс, описывающий типы данных внутри options.
  • fn и effect строго типизированы через обобщённые параметры Options.
  • requires и requiresIfExists задают зависимости модификаторов.

4. Проверка типов пользовательских модификаторов

Popper.js допускает создание пользовательских модификаторов. Чтобы избежать ошибок при передаче некорректных опций:

interface MyCustomOptions {
  offsetValue: number;
  isEnabled: boolean;
}

const myCustomModifier: Modifier<'myModifier', MyCustomOptions> = {
  name: 'myModifier',
  enabled: true,
  phase: 'main',
  fn: ({ state, options }) => {
    if (options.isEnabled) {
      state.styles.popper.transform += ` translateY(${options.offsetValue}px)`;
    }
  },
  options: {
    offsetValue: 10,
    isEnabled: true
  }
};

Использование строгих типов позволяет IDE и TypeScript сразу предупреждать о несоответствиях, что снижает количество ошибок в рантайме.


5. Совместимость и вложенные типы

Модификаторы Popper.js могут быть вложенными. Например, объект padding внутри preventOverflow может иметь разные типы:

  • number — одинаковое значение для всех сторон.
  • object — отдельные значения для каждой стороны.
  • function — динамическое вычисление значения на основе состояния.
options: {
  padding: ({ placement, rects }) => {
    return placement.startsWith('top') ? 10 : 5;
  }
}

Использование правильного типа обеспечивает корректное позиционирование и работу модификатора с разными вариантами конфигурации.


6. Вывод

Типы модификаторов — это не просто формальность, а мощный инструмент контроля поведения Popper.js. Они позволяют:

  • Чётко структурировать настройки модификаторов.
  • Обеспечивать совместимость с TypeScript.
  • Минимизировать ошибки при динамическом изменении опций.
  • Создавать сложные пользовательские модификаторы с предсказуемым поведением.

Правильная типизация и строгий контроль типов делают работу с Popper.js безопасной и удобной даже в крупных проектах.