Позиционирование списков — важный аспект интерфейсной архитектуры. В контексте библиотеки Radix UI списки часто используются внутри таких компонентов, как:
Ключевая задача позиционирования — корректно разместить список относительно элемента-триггера и обеспечить его адаптивное поведение при изменении размеров окна, прокрутке страницы и ограничениях контейнера.
Radix UI решает эту задачу с помощью интеграции с библиотекой Floating UI, которая отвечает за вычисление координат элементов и предотвращение выхода контента за границы видимой области.
Позиционирование списка строится вокруг нескольких базовых элементов:
| Элемент | Назначение |
|---|---|
| Trigger | элемент, вызывающий появление списка |
| Content | контейнер со списком |
| Portal | перенос списка в корень DOM |
| Viewport | область отображения элементов списка |
| Item | отдельный элемент списка |
Простейшая структура выпадающего списка:
import * as Sel ect from "@radix-ui/react-select";
<Select.Root>
<Select.Trigger>
<Select.Value />
</Select.Trigger>
<Select.Portal>
<Select.Content>
<Select.Viewport>
<Select.Item value="one">
<Select.ItemText>One</Select.ItemText>
</Select.Item>
</Select.Viewport>
</Select.Content>
</Select.Portal>
</Select.Root>
Контейнер Select.Content является ключевым элементом,
отвечающим за позиционирование.
Radix UI поддерживает две основные стратегии позиционирования:
Позиционирование относительно выбранного элемента списка.
Используется по умолчанию в компоненте Select. Список
выравнивается таким образом, чтобы выбранный элемент находился на уровне
триггера.
Пример:
<Select.Content position="item-aligned">
Особенности:
Позиционирование с использованием механизма Floating UI.
<Select.Content position="popper">
Особенности:
Этот режим обеспечивает более гибкий контроль над расположением элементов.
Параметр side определяет сторону, относительно которой
появляется список.
Возможные значения:
topbottomleftrightПример:
<Select.Content side="bottom">
Поведение:
Параметр align управляет горизонтальным или вертикальным
выравниванием.
Возможные значения:
startcenterendПример:
<Select.Content
side="bottom"
align="start"
>
Результат:
Для тонкой настройки расположения используются параметры смещения.
Расстояние между триггером и списком.
<Select.Content
side="bottom"
sideOffset={8}
>
Эффект:
Смещение вдоль оси выравнивания.
<Select.Content
align="start"
alignOffset={10}
>
Применяется для точной подстройки позиции.
Floating UI автоматически предотвращает выход списка за пределы видимой области.
В Radix UI это поведение управляется параметром:
avoidCollisions
Пример:
<Select.Content
avoidCollisions
>
Механизм работает следующим образом:
Параметр collisionPadding задаёт безопасную зону от края
окна.
<Select.Content
collisionPadding={10}
>
Список никогда не будет расположен ближе чем на 10px к границе viewport.
Иногда необходимо ограничить область позиционирования конкретным контейнером.
Для этого используется параметр:
collisionBoundary
Пример:
<Select.Content
collisionBoundary={document.body}
>
Допустимо передавать:
Списки часто находятся внутри контейнеров с
overflow: hidden или z-index. Это может ломать
позиционирование.
Radix UI решает проблему с помощью компонента
Portal.
<Select.Portal>
<Select.Content>
Портал перемещает список:
bodyЭто предотвращает:
Radix UI предоставляет CSS-переменные для адаптации размеров.
Пример:
.SelectContent {
width: var(--radix-select-trigger-width);
}
Доступные переменные:
| Переменная | Назначение |
|---|---|
--radix-select-trigger-width |
ширина триггера |
--radix-select-content-available-height |
доступная высота |
--radix-select-content-transform-origin |
точка трансформации |
Пример ограничения высоты:
.SelectContent {
max-height: var(--radix-select-content-available-height);
}
Компонент Viewport содержит сами элементы списка.
<Select.Viewport>
Он:
Пример:
<Select.Content>
<Select.Viewport className="SelectViewport">
{items}
</Select.Viewport>
</Select.Content>
CSS:
.SelectViewport {
padding: 5px;
}
Для длинных списков используются специальные элементы прокрутки:
<Select.ScrollUpButton />
<Select.ScrollDownButton />
Структура:
<Select.Content>
<Select.ScrollUpButton>
▲
</Select.ScrollUpButton>
<Select.Viewport>
...
</Select.Viewport>
<Select.ScrollDownButton>
▼
</Select.ScrollDownButton>
</Select.Content>
Эти элементы:
Radix UI передаёт состояние позиционирования через атрибуты:
data-state="open"
data-side="top"
data-side="bottom"
Пример CSS-анимации:
.SelectContent[data-state="open"] {
animation: fadeIn 120ms ease-out;
}
.SelectContent[data-side="top"] {
animation: slideDown 120ms ease-out;
}
.SelectContent[data-side="bottom"] {
animation: slideUp 120ms ease-out;
}
Пример keyframes:
@keyframes slideUp {
fr om {
opacity: 0;
transform: translateY(6px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
Анимации автоматически адаптируются к стороне появления.
Floating UI автоматически пересчитывает координаты при:
Это обеспечивает корректное поведение:
Radix UI передаёт точку трансформации через CSS-переменную:
--radix-select-content-transform-origin
Пример использования:
.SelectContent {
transform-origin: var(--radix-select-content-transform-origin);
}
Это позволяет создавать естественные анимации масштабирования.
Реальная структура выпадающего списка обычно выглядит так:
<Select.Root>
<Select.Trigger>
<Select.Value />
</Select.Trigger>
<Select.Portal>
<Select.Content
side="bottom"
align="start"
sideOffset={6}
collisionPadding={10}
>
<Select.ScrollUpButton />
<Select.Viewport>
<Select.Item value="a">
<Select.ItemText>A</Select.ItemText>
</Select.Item>
<Select.Item value="b">
<Select.ItemText>B</Select.ItemText>
</Select.Item>
</Select.Viewport>
<Select.ScrollDownButton />
</Select.Content>
</Select.Portal>
</Select.Root>
Такой подход обеспечивает: