В основе библиотеки Floating UI лежит абстракция платформы — набора методов, отвечающих за получение геометрии, размеров и контекста размещаемых элементов. По умолчанию используется DOM-платформа, однако библиотека спроектирована так, чтобы позволить внедрение собственных реализаций.
Кастомная платформа становится необходимой в следующих случаях:
Платформа представляет собой объект с набором строго определённых функций, которые используются внутренним механизмом позиционирования.
Минимальная платформа должна реализовывать следующие методы:
interface Platform {
getElementRects: (args: {
reference: any;
floating: any;
strategy: 'absolute' | 'fixed';
}) => Promise<{
reference: Rect;
floating: Rect;
}>;
getClippingRect: (args: {
element: any;
boundary: any;
rootBoundary: any;
strategy: 'absolute' | 'fixed';
}) => Promise<Rect>;
getDimensions: (element: any) => Promise<Dimensions>;
getOffsetParent?: (element: any) => Promise<any>;
getDocumentElement?: (element: any) => any;
getClientRects?: (element: any) => DOMRect[];
isElement?: (value: any) => boolean;
isRTL?: (element: any) => boolean;
}
Типы Rect и Dimensions имеют следующий
вид:
type Rect = {
x: number;
y: number;
width: number;
height: number;
};
type Dimensions = {
width: number;
height: number;
};
При работе с TypeScript важно точно описывать типы элементов,
используемых платформой. Floating UI допускает произвольный тип
Element, поэтому рекомендуется вводить собственные
обобщения.
Пример:
type CustomElement = {
x: number;
y: number;
width: number;
height: number;
};
type CustomPlatform = Platform<CustomElement>;
Однако в текущей реализации Floating UI чаще применяется структурная типизация без явного дженерика, поэтому практическая реализация выглядит так:
const platform: Platform = {
async getElementRects({ reference, floating }) {
return {
reference: reference.getRect(),
floating: floating.getRect(),
};
},
async getDimensions(element) {
return {
width: element.width,
height: element.height,
};
},
async getClippingRect() {
return {
x: 0,
y: 0,
width: 1000,
height: 1000,
};
},
};
Метод getElementRects является ключевым. Он возвращает
прямоугольники reference и floating элементов в одной системе
координат.
Особенности:
absolute или
fixed);Пример для Canvas:
async function getElementRects({ reference, floating }) {
return {
reference: {
x: reference.x,
y: reference.y,
width: reference.width,
height: reference.height,
},
floating: {
x: floating.x,
y: floating.y,
width: floating.width,
height: floating.height,
},
};
}
Этот метод определяет область, в пределах которой элемент считается видимым.
В DOM это viewport или контейнер с overflow, но в кастомной среде логика может быть иной:
async function getClippingRect() {
return {
x: 0,
y: 0,
width: canvasWidth,
height: canvasHeight,
};
}
В сложных случаях можно учитывать:
Метод возвращает размеры элемента без учёта трансформаций:
async function getDimensions(element) {
return {
width: element.width,
height: element.height,
};
}
В виртуальных системах размеры могут храниться в модели данных, а не вычисляться.
Метод getOffsetParent используется для определения
контекста позиционирования.
В нестандартной платформе:
async function getOffsetParent(element) {
return element.parent || null;
}
Если концепция offsetParent отсутствует, можно возвращать
null, но это влияет на расчёт смещений.
Метод isElement позволяет определить, является ли объект
элементом платформы:
function isElement(value: any): boolean {
return value && typeof value === 'object' && 'x' in value;
}
Это важно при смешивании различных типов (например, виртуальные элементы + реальные).
Метод isRTL определяет направление текста:
function isRTL() {
return false;
}
В кастомных системах часто игнорируется, но при поддержке интернационализации может быть критичен.
Пример целостной реализации:
const customPlatform: Platform = {
async getElementRects({ reference, floating }) {
return {
reference: reference.rect,
floating: floating.rect,
};
},
async getDimensions(element) {
return {
width: element.rect.width,
height: element.rect.height,
};
},
async getClippingRect() {
return {
x: 0,
y: 0,
width: 1920,
height: 1080,
};
},
async getOffsetParent(element) {
return element.parent ?? null;
},
isElement(value) {
return value && value.rect !== undefined;
},
isRTL() {
return false;
},
};
Платформа передаётся в computePosition:
import { computePosition } from '@floating-ui/core';
computePosition(reference, floating, {
platform: customPlatform,
}).then(({ x, y }) => {
floating.x = x;
floating.y = y;
});
Floating UI поддерживает виртуальные reference-элементы. Это особенно полезно для:
Пример:
const virtualReference = {
getBoundingClientRect() {
return {
x: mouseX,
y: mouseY,
width: 0,
height: 0,
};
},
};
В кастомной платформе аналог реализуется через собственные структуры:
const virtualReference = {
rect: {
x: 100,
y: 200,
width: 0,
height: 0,
},
};
Дополнительные методы позволяют оптимизировать и адаптировать поведение:
Несогласованные координаты
Разные системы координат между reference и floating приводят к неправильному позиционированию.
Игнорирование стратегии
absolute и fixed могут требовать разных
вычислений.
Неверные размеры
Если getDimensions возвращает неточные значения,
ломаются middleware (например, flip,
shift).
Отсутствие clipping
Без корректного getClippingRect не работают ограничения
видимости.
Кастомная платформа полностью совместима со всеми middleware:
offsetflipshiftarrowsizeОднако корректность их работы зависит от точности платформенных методов.
Пример:
import { offset, flip, shift } from '@floating-ui/core';
computePosition(reference, floating, {
platform: customPlatform,
middleware: [
offset(10),
flip(),
shift(),
],
});
Canvas UI
Позиционирование tooltip внутри Canvas без DOM.
Игровые интерфейсы
Отображение всплывающих элементов в WebGL.
React Native
Использование Floating UI без браузерных API.
Графические редакторы
Привязка элементов к произвольным координатам сцены.
Кастомная платформа — это слой адаптации между движком позиционирования Floating UI и конкретной средой выполнения. Точная типизация, согласованность координат и корректная реализация базовых методов определяют стабильность и предсказуемость всей системы позиционирования.