# @cloud-ru/ds-list > Списочный UI — компонент List для плоских/вложенных списков с выбором, группами и поиском и Droplist — тот же список в поповере. Docs: /snack-v2/components/list/ ## Установка ```sh pnpm add @cloud-ru/ds-list ``` ## Когда использовать | Задача | Как решить | |--------|------------| | Навигация/меню/settings sidebar | `List` с `items` | | Выбор из коллекции (радио-группа/чекбоксы) | `List` + `selection={{ mode: 'single' \| 'multiple', ... }}` | | Выпадашка-селектор у кнопки/поля | `Droplist` с `children`-триггером | | Группы с раскрытием (inbox/starred/folders) | `items` типа `{ type: 'collapse', items: [...] }` + `collapse` | | Длинный список (1k+) | `virtualized` на `List` | | Закреплённые действия сверху/снизу | `pinTop` / `pinBottom` | Когда **не** нужен `List`: - Простой набор из 2–4 кнопок — используйте [`Button`](/components/button/button) + layout. - Табличные данные с сортировкой/фильтрацией — используйте `Table`. - Многошаговая форма — используйте `Stepper`. ## API ### AccordionItem | Prop | Type | Default | Required | Description | |------|------|---------|----------|-------------| | `afterContent` | `ReactElement` | — | no | Слот после основного контента | | `allChildIds` | `ItemId[]` | — | yes | | | `beforeContent` | `ReactElement` | — | no | Слот до основного контента | | `checked` | `boolean` | — | no | Управляемое состояние выбранности айтема | | `className` | `string` | — | no | CSS-класс | | `content` | `ReactNode \| ItemContentProps` | — | no | Основной контент айтема | | `data-test-id` | `string` | — | no | | | `disabled` | `boolean` | — | no | Флаг неактивности элемента | | `hidden` | `boolean` | — | no | Скрыть элемент из списка (не рендерится и выпадает из навигации) | | `id` | `ItemId` | — | no | Уникальный идентификатор | | `itemRef` | `RefObject` | — | no | Ссылка на DOM-элемент айтема | | `itemWrapRender` | `((item: ReactNode) => ReactNode)` | — | no | Рендер-обёртка вокруг айтема (например, для проксирования в `Tooltip`/`Link`) | | `items` | `ItemId[]` | — | yes | | | `onBlur` | `((e: FocusEvent) => void)` | — | no | Колбек обработки блюра | | `onClick` | `((e: MouseEvent) => void)` | — | no | Колбек обработки клика | | `onFocus` | `((e: FocusEvent) => void)` | — | no | Колбек обработки фокуса | | `onKeyDown` | `((e: KeyboardEvent) => void)` | — | no | Колбек обработки нажатия клавиши | | `onMouseDown` | `((e: MouseEvent) => void)` | — | no | Колбек обработки нажатия кнопки мыши | | `showSwitchIcon` | `boolean` | — | no | Флаг отображения иконки у чекбоксов | ### BaseItem | Prop | Type | Default | Required | Description | |------|------|---------|----------|-------------| | `afterContent` | `ReactElement` | — | no | Слот после основного контента | | `beforeContent` | `ReactElement` | — | no | Слот до основного контента | | `checked` | `boolean` | — | no | Управляемое состояние выбранности айтема | | `className` | `string` | — | no | CSS-класс | | `content` | `ReactNode \| ItemContentProps` | — | no | Основной контент айтема | | `data-test-id` | `string` | — | no | | | `disabled` | `boolean` | — | no | Флаг неактивности элемента | | `dragHandle` | `ReactNode` | — | no | Слот ручки drag&drop (Figma `centeredWrapper`) — рендерится первым в строке, перед маркером/чекбоксом. Интерактивность (обработчики `@dnd-kit`) и содержимое (иконка) приходят снаружи — `BaseItem` только позиционирует слот, ничего не знает про DnD. | | `expandIcon` | `ReactNode` | — | no | | | `hidden` | `boolean` | — | no | Скрыть элемент из списка (не рендерится и выпадает из навигации) | | `id` | `ItemId` | — | no | Уникальный идентификатор | | `inactive` | `boolean` | — | no | Флаг отображения отключения реакции на любое css состояние (hover/focus и тд)
Так же элемент пропадает из навигации с клавиатуры, и не может быть выбран (selection) | | `indeterminate` | `boolean` | — | no | | | `isParentNode` | `boolean` | — | no | | | `itemRef` | `RefObject` | — | no | Ссылка на DOM-элемент айтема | | `itemWrapRender` | `((item: ReactNode) => ReactNode)` | — | no | Рендер-обёртка вокруг айтема (например, для проксирования в `Tooltip`/`Link`) | | `onBlur` | `((e: FocusEvent) => void)` | — | no | Колбек обработки блюра | | `onClick` | `((e: MouseEvent) => void)` | — | no | Колбек обработки клика | | `onExpandIconClick` | `((e: MouseEvent) => void)` | — | no | Клик по явной кнопке-триггеру `groupIndicator` (шеврон справа), открывающей/закрывающей вложенный список. Если передан — шеврон рендерится как отдельная интерактивная кнопка со своим state-layer'ом, а клик по телу строки больше не переключает раскрытие. | | `onFocus` | `((e: FocusEvent) => void)` | — | no | Колбек обработки фокуса | | `onKeyDown` | `((e: KeyboardEvent) => void)` | — | no | Колбек обработки нажатия клавиши | | `onMouseDown` | `((e: MouseEvent) => void)` | — | no | Колбек обработки нажатия кнопки мыши | | `onOpenNestedList` | `((e?: KeyboardEvent) => void)` | — | no | | | `onSelect` | `(() => void)` | — | no | | | `open` | `boolean` | — | no | | | `showSwitchIcon` | `boolean` | — | no | Флаг отображения иконки у чекбоксов | | `switch` | `boolean` | — | no | Флаг отображения состояния выбранного элемента через switch | ### CollapseBlockPrivate | Prop | Type | Default | Required | Description | |------|------|---------|----------|-------------| | `children` | `string \| number \| boolean \| ReactElement> \| Iterable \| ReactPortal \| null \| undefined` | — | yes | | | `className` | `string` | — | no | | | `data-test-id` | `string` | — | no | | | `expanded` | `boolean` | — | yes | | | `header` | `ReactNode` | — | yes | | ### DesktopDroplist | Prop | Type | Default | Required | Description | |------|------|---------|----------|-------------| | `barHideStrategy` | `leave \| move \| never \| scroll` | — | no | Управление скрытием скролл баров:
- `Never` - показывать всегда
- `Leave` - скрывать когда курсор покидает компонент
- `Scroll` - показывать только когда происходит скроллинг
- `Move` - показывать при движении курсора над компонентом | | `children` | `ReactNode \| ({onKeyDown}) => ReactNode * Рендер функция принимает аргументы `onKeyDown` - хендлер ввода, для поддержки управления с клавиатуры` | — | yes | Триггер для дроплиста | | `className` | `string` | — | no | CSS-класс | | `closeDroplistOnItemClick` | `boolean` | `false` | no | Закрывать выпадающий список после клика на базовый айтем. Работает в режимах selection: 'none' \| 'single' | | `closeOnPopstate` | `boolean` | — | no | Закрывать ли поповер при переходе по истории браузера | | `collapse` | `CollapseState` | `{}` | no | Настройки раскрытия элементов | | `container` | `RefObject` | — | no | Контейнер портала (ref). Переопределяет `PortalContext` для этого дроплиста (по аналогии с `container` у Modal/Drawer). По умолчанию — из `PortalContextProvider`. | | `contentRender` | `((props: ContentRenderProps) => ReactNode)` | — | no | Рендер функция основного контента айтема | | `data-test-id` | `string` | — | no | | | `dataError` | `boolean` | — | no | Загрузка данных завершилась ошибкой: показывается `errorDataState` | | `dataFiltered` | `boolean` | — | no | Текущий пустой список — результат поиска/фильтра: показывается `noResultsState` вместо `noDataState` | | `errorDataState` | `EmptyStateProps` | — | no | Экран при ошибке запроса | | `footer` | `ReactNode ;` | — | no | Кастомизируемый элемент в конце списка | | `footerActiveElementsRefs` | `RefObject[]` | — | no | Список ссылок на кастомные элементы, помещенные в специальную секцию внизу списка | | `footerDivider` | `boolean` | — | no | Показывать divider между body и footer (Figma `dropdownContainer.dividerWrapper` снизу) | | `header` | `ReactNode ;` | — | no | Кастомизируемый элемент в начале списка — Figma `dropdownContainer.topBar`. Подходит для заголовка / справочного блока над поиском. | | `headerDivider` | `boolean` | — | no | Показывать divider между header и body (Figma `dropdownContainer.dividerWrapper` сверху) | | `items` | `Item[] \| ReorderItem[]` | — | yes | | | `limitedScrollHeight` | `boolean` | — | no | Ограничить максимальную высоту скролл-контейнера в зависимости от `size` | | `listRef` | `RefObject` | — | no | Ссылка на элемент выпадающего списка | | `loading` | `boolean` | — | no | Флаг, отвечающий за состояние загрузки списка | | `marker` | `boolean` | `true` | no | Отображать ли маркер у выбранного элемента списка | | `noDataState` | `EmptyStateProps` | — | no | Экран при отсутствии данных | | `noResultsState` | `EmptyStateProps` | — | no | Экран при отсутствии результатов поиска или фильтров | | `onItemsReorder` | `((items: ReorderItem[]) => void)` | — | no | | | `onOpenChange` | `((isOpen: boolean) => void)` | — | no | Колбек отображения компонента. Срабатывает при изменении состояния open. | | `onScroll` | `((event?: Event) => void)` | — | no | Колбек на скролл прокручиваемого списка | | `open` | `boolean` | — | no | Управляет состоянием показан/не показан. | | `pinBottom` | `Item[]` | `[]` | no | Элементы списка, закрепленные снизу | | `pinTop` | `Item[]` | `[]` | no | Элементы списка, закрепленные сверху | | `placement` | `bottom \| bottom-end \| bottom-start \| left \| left-end \| left-start \| right \| right-end \| right-start \| top \| top-end \| top-start` | `top` | no | Положение поповера относительно своего триггера (children). | | `scroll` | `boolean` | — | no | Включить ли скролл для основной части списка | | `scrollContainerClassName` | `string` | — | no | CSS-класс для scroll обертки основного списка айтемов | | `scrollContainerRef` | `Ref` | — | no | Ссылка на контейнер, который скроллится | | `scrollRef` | `Ref` | — | no | Ссылка на элемент, обозначающий самый конец прокручиваемого списка | | `scrollToSelectedItem` | `boolean` | — | no | Флаг, отвечающий за прокручивание до выбранного элемента | | `search` | `SearchState` | — | no | Настройки поисковой строки | | `selection` | `SelectionMultipleState \| SelectionSingleState` | — | no | Настройки выбора элементов. `mode: 'single'` — один выбранный элемент (`value: ItemId`), `mode: 'multiple'` — множественный выбор (`value: ItemId[]`). Без `selection` выбора нет — клик вызывает только `onClick` элемента. | | `size` | `l \| m \| s` | `m` | no | Размер списка | | `trigger` | `click \| clickAndFocusVisible \| focus \| focusVisible \| hover \| hoverAndFocus \| hoverAndFocusVisible` | — | no | Условие отображения поповера:
- `click` - открывать по клику
- `hover` - открывать по ховеру
- `focusVisible` - открывать по focus-visible
- `focus` - открывать по фокусу
- `hoverAndFocusVisible` - открывать по ховеру и focus-visible
- `hoverAndFocus` - открывать по ховеру и фокусу
- `clickAndFocusVisible` - открывать по клику и focus-visible | | `triggerClassName` | `string` | — | no | CSS-класс триггера | | `triggerElemRef` | `RefObject` | — | no | Ссылка на элемент-триггер для дроплиста | | `untouchableScrollbars` | `boolean` | `false` | no | Отключает возможность взаимодействовать со скролбарами мышью. | | `virtualized` | `boolean` | `false` | no | | | `widthStrategy` | `auto \| eq \| gte` | `auto` | no | Стратегия управления шириной контейнера поповера
- `auto` - соответствует ширине контента,
- `gte` - Great Than or Equal, равен ширине таргета или больше ее, если контент в поповере шире,
- `eq` - Equal, строго равен ширине таргета. | ### DragHandle _Нет публичных пропсов._ ### Droplist | Prop | Type | Default | Required | Description | |------|------|---------|----------|-------------| | `actionButton` | `ReactNode` | — | no | Только mobile (`BottomSheet`): action-кнопка справа в шапке. | | `barHideStrategy` | `leave \| move \| never \| scroll` | — | no | Управление скрытием скролл баров:
- `Never` - показывать всегда
- `Leave` - скрывать когда курсор покидает компонент
- `Scroll` - показывать только когда происходит скроллинг
- `Move` - показывать при движении курсора над компонентом | | `children` | `ReactNode \| ({onKeyDown}) => ReactNode * Рендер функция принимает аргументы `onKeyDown` - хендлер ввода, для поддержки управления с клавиатуры` | — | yes | Триггер для дроплиста | | `className` | `string` | — | no | CSS-класс | | `closeDroplistOnItemClick` | `boolean` | `false` | no | Закрывать выпадающий список после клика на базовый айтем. Работает в режимах selection: 'none' \| 'single' | | `closeOnPopstate` | `boolean` | — | no | Закрывать ли поповер при переходе по истории браузера | | `collapse` | `CollapseState` | — | no | Настройки раскрытия элементов | | `container` | `RefObject` | — | no | Контейнер портала (ref). Переопределяет `PortalContext` для этого дроплиста (по аналогии с `container` у Modal/Drawer). По умолчанию — из `PortalContextProvider`. | | `contentRender` | `((props: ContentRenderProps) => ReactNode)` | — | no | Рендер функция основного контента айтема | | `data-test-id` | `string` | — | no | | | `dataError` | `boolean` | — | no | Загрузка данных завершилась ошибкой: показывается `errorDataState` | | `dataFiltered` | `boolean` | — | no | Текущий пустой список — результат поиска/фильтра: показывается `noResultsState` вместо `noDataState` | | `errorDataState` | `EmptyStateProps` | — | no | Экран при ошибке запроса | | `footer` | `ReactNode ;` | — | no | Кастомизируемый элемент в конце списка | | `footerActiveElementsRefs` | `RefObject[]` | — | no | Список ссылок на кастомные элементы, помещенные в специальную секцию внизу списка | | `footerDivider` | `boolean` | — | no | Показывать divider между body и footer (Figma `dropdownContainer.dividerWrapper` снизу) | | `header` | `ReactNode ;` | — | no | Кастомизируемый элемент в начале списка — Figma `dropdownContainer.topBar`. Подходит для заголовка / справочного блока над поиском. | | `headerDivider` | `boolean` | — | no | Показывать divider между header и body (Figma `dropdownContainer.dividerWrapper` сверху) | | `items` | `Item[]` | — | yes | Основные элементы списка | | `label` | `string` | — | no | Только mobile (`BottomSheet`): заголовок шапки. | | `limitedScrollHeight` | `boolean` | — | no | Ограничить максимальную высоту скролл-контейнера в зависимости от `size` | | `listRef` | `RefObject` | — | no | Ссылка на элемент выпадающего списка | | `loading` | `boolean` | — | no | Флаг, отвечающий за состояние загрузки списка | | `marker` | `boolean` | — | no | Отображать ли маркер у выбранного элемента списка | | `noDataState` | `EmptyStateProps` | — | no | Экран при отсутствии данных | | `noResultsState` | `EmptyStateProps` | — | no | Экран при отсутствии результатов поиска или фильтров | | `onBackButtonClick` | `(() => void)` | — | no | Только mobile (`BottomSheet`): callback back-кнопки. | | `onOpenChange` | `((isOpen: boolean) => void)` | — | no | Колбек отображения компонента. Срабатывает при изменении состояния open. | | `onScroll` | `((event?: Event) => void)` | — | no | Колбек на скролл прокручиваемого списка | | `open` | `boolean` | — | no | Управляет состоянием показан/не показан. | | `pinBottom` | `Item[]` | — | no | Элементы списка, закрепленные снизу | | `pinTop` | `Item[]` | — | no | Элементы списка, закрепленные сверху | | `placement` | `bottom \| bottom-end \| bottom-start \| left \| left-end \| left-start \| right \| right-end \| right-start \| top \| top-end \| top-start` | `top` | no | Положение поповера относительно своего триггера (children). | | `scroll` | `boolean` | — | no | Включить ли скролл для основной части списка | | `scrollContainerClassName` | `string` | — | no | CSS-класс для scroll обертки основного списка айтемов | | `scrollContainerRef` | `Ref` | — | no | Ссылка на контейнер, который скроллится | | `scrollRef` | `Ref` | — | no | Ссылка на элемент, обозначающий самый конец прокручиваемого списка | | `scrollToSelectedItem` | `boolean` | — | no | Флаг, отвечающий за прокручивание до выбранного элемента | | `search` | `SearchState` | — | no | Настройки поисковой строки | | `selection` | `SelectionMultipleState \| SelectionSingleState` | — | no | Настройки выбора элементов. `mode: 'single'` — один выбранный элемент (`value: ItemId`), `mode: 'multiple'` — множественный выбор (`value: ItemId[]`). Без `selection` выбора нет — клик вызывает только `onClick` элемента. | | `size` | `l \| m \| s` | — | no | Размер списка | | `slotAfterTitle` | `ReactNode` | — | no | Только mobile (`BottomSheet`): slot справа от заголовка. | | `trigger` | `click \| clickAndFocusVisible \| focus \| focusVisible \| hover \| hoverAndFocus \| hoverAndFocusVisible` | — | no | Условие отображения поповера:
- `click` - открывать по клику
- `hover` - открывать по ховеру
- `focusVisible` - открывать по focus-visible
- `focus` - открывать по фокусу
- `hoverAndFocusVisible` - открывать по ховеру и focus-visible
- `hoverAndFocus` - открывать по ховеру и фокусу
- `clickAndFocusVisible` - открывать по клику и focus-visible | | `triggerClassName` | `string` | — | no | CSS-класс триггера | | `triggerElemRef` | `RefObject` | — | no | Ссылка на элемент-триггер для дроплиста | | `untouchableScrollbars` | `boolean` | — | no | Отключает возможность взаимодействовать со скролбарами мышью. | | `virtualized` | `boolean` | — | no | Включить виртуализацию элементов списка. Рекомендуется при количестве элементов от 1000. | | `widthStrategy` | `auto \| eq \| gte` | `auto` | no | Стратегия управления шириной контейнера поповера
- `auto` - соответствует ширине контента,
- `gte` - Great Than or Equal, равен ширине таргета или больше ее, если контент в поповере шире,
- `eq` - Equal, строго равен ширине таргета. | #### Related types - `BaseItemWithoutNonGroup` (interface) - `CollapseState` (interface) - `CommonGroupItem` (interface) - `EmptyStateProps` (interface) - `IconPredefinedProps` (interface) - `Item` (alias) - `ItemContent` (interface) - `ItemId` (alias) - `OnChangeHandler` (alias) - `Placement` = `bottom | bottom-end | bottom-start | left | left-end | left-start | right | right-end | right-start | top | top-end | top-start` - `PopoverWidthStrategy` = `auto | eq | gte` - `ScrollProps` (interface) - `SearchState` (interface) - `SelectionMultipleState` (interface) - `SelectionSingleState` (interface) - `Size` = `l | m | s` - `Trigger` = `click | clickAndFocusVisible | focus | focusVisible | hover | hoverAndFocus | hoverAndFocusVisible` - `TruncateStringProps` (alias) ### GroupSelectItem | Prop | Type | Default | Required | Description | |------|------|---------|----------|-------------| | `allChildIds` | `ItemId[]` | — | yes | | | `beforeContent` | `ReactNode` | — | no | Слот иконки слева от label. | | `divider` | `boolean` | — | no | Показать разделитель над группой | | `groupVariant` | `subtitle \| subtitleTertiary` | — | no | Визуальный стиль заголовка группы | | `hidden` | `boolean` | — | no | Скрыть группу из списка | | `id` | `ItemId` | — | no | | | `itemRef` | `RefObject` | — | no | | | `items` | `ItemId[]` | — | yes | | | `label` | `string` | — | no | Заголовок группы | | `selectButtonLabel` | `string` | — | no | | | `truncate` | `{ variant?: "end" \| "middle"; }` | — | no | Настройки усечения длинного заголовка группы | ### HiddenTabButton | Prop | Type | Default | Required | Description | |------|------|---------|----------|-------------| | `listRef` | `RefObject` | — | yes | | | `tabIndex` | `number` | — | no | | ### ItemContent | Prop | Type | Default | Required | Description | |------|------|---------|----------|-------------| | `caption` | `string` | — | no | | | `className` | `string` | — | no | | | `data-test-id` | `string` | — | no | | | `description` | `string` | — | no | | | `disabled` | `boolean` | — | no | | | `label` | `string \| number` | — | yes | | | `truncate` | `TruncateProps` | — | no | | #### Related types - `TruncateProps` (interface) - `TruncateStringProps` (alias) ### List | Prop | Type | Default | Required | Description | |------|------|---------|----------|-------------| | `barHideStrategy` | `leave \| move \| never \| scroll` | — | no | Управление скрытием скролл баров:
- `Never` - показывать всегда
- `Leave` - скрывать когда курсор покидает компонент
- `Scroll` - показывать только когда происходит скроллинг
- `Move` - показывать при движении курсора над компонентом | | `className` | `string` | — | no | CSS-класс | | `collapse` | `CollapseState` | `{}` | no | Настройки раскрытия элементов | | `contentRender` | `((props: ContentRenderProps) => ReactNode)` | — | no | Рендер функция основного контента айтема | | `data-test-id` | `string` | — | no | | | `dataError` | `boolean` | — | no | Загрузка данных завершилась ошибкой: показывается `errorDataState` | | `dataFiltered` | `boolean` | — | no | Текущий пустой список — результат поиска/фильтра: показывается `noResultsState` вместо `noDataState` | | `errorDataState` | `EmptyStateProps` | — | no | Экран при ошибке запроса | | `footer` | `ReactNode ;` | — | no | Кастомизируемый элемент в конце списка | | `footerActiveElementsRefs` | `RefObject[]` | — | no | Список ссылок на кастомные элементы, помещенные в специальную секцию внизу списка | | `footerDivider` | `boolean` | — | no | Показывать divider между body и footer (Figma `dropdownContainer.dividerWrapper` снизу) | | `hasListInFocusChain` | `boolean` | `true` | no | Флаг, отвечающий за включение самого родительского контейнера листа в цепочку фокусирующихся элементов | | `header` | `ReactNode ;` | — | no | Кастомизируемый элемент в начале списка — Figma `dropdownContainer.topBar`. Подходит для заголовка / справочного блока над поиском. | | `headerDivider` | `boolean` | — | no | Показывать divider между header и body (Figma `dropdownContainer.dividerWrapper` сверху) | | `items` | `Item[]` | `[]` | no | Основные элементы списка | | `keyboardNavigationRef` | `RefObject<{ focusItem(id: ItemId): void; }>` | — | no | Ссылка на управление навигацией листа с клавиатуры | | `limitedScrollHeight` | `boolean` | — | no | Ограничить максимальную высоту скролл-контейнера в зависимости от `size` | | `loading` | `boolean` | — | no | Флаг, отвечающий за состояние загрузки списка | | `marker` | `boolean` | `true` | no | Отображать ли маркер у выбранного элемента списка | | `noDataState` | `EmptyStateProps` | — | no | Экран при отсутствии данных | | `noResultsState` | `EmptyStateProps` | — | no | Экран при отсутствии результатов поиска или фильтров | | `onKeyDown` | `((e: KeyboardEvent) => void)` | — | no | Обработчик события по нажатию клавиш | | `onScroll` | `((event?: Event) => void)` | — | no | Колбек на скролл прокручиваемого списка | | `pinBottom` | `Item[]` | `[]` | no | Элементы списка, закрепленные снизу | | `pinTop` | `Item[]` | `[]` | no | Элементы списка, закрепленные сверху | | `scroll` | `boolean` | — | no | Включить ли скролл для основной части списка | | `scrollContainerClassName` | `string` | — | no | CSS-класс для scroll обертки основного списка айтемов | | `scrollContainerRef` | `Ref` | — | no | Ссылка на контейнер, который скроллится | | `scrollRef` | `Ref` | — | no | Ссылка на элемент, обозначающий самый конец прокручиваемого списка | | `scrollToSelectedItem` | `boolean` | — | no | Флаг, отвечающий за прокручивание до выбранного элемента | | `search` | `SearchState` | — | no | Настройки поисковой строки | | `selection` | `SelectionMultipleState \| SelectionSingleState` | — | no | Настройки выбора элементов. `mode: 'single'` — один выбранный элемент (`value: ItemId`), `mode: 'multiple'` — множественный выбор (`value: ItemId[]`). Без `selection` выбора нет — клик вызывает только `onClick` элемента. | | `size` | `l \| m \| s` | `m` | no | Размер списка | | `tabIndex` | `number` | `0` | no | `tabIndex` корневого элемента списка (для управления порядком фокуса) | | `untouchableScrollbars` | `boolean` | — | no | Отключает возможность взаимодействовать со скролбарами мышью. | | `virtualized` | `boolean` | — | no | Включить виртуализацию элементов списка. Рекомендуется при количестве элементов от 1000. | ### ListEmptyState | Prop | Type | Default | Required | Description | |------|------|---------|----------|-------------| | `dataError` | `boolean` | — | no | | | `dataFiltered` | `boolean` | — | no | | | `emptyStates` | `{ noDataState: EmptyStateProps; noResultsState: EmptyStateProps; errorDataState: EmptyStateProps; }` | — | yes | | | `hasNoItems` | `boolean` | — | yes | | | `loading` | `boolean` | — | no | | | `size` | `l \| m \| s \| undefined` | — | yes | | ### ListPrivate | Prop | Type | Default | Required | Description | |------|------|---------|----------|-------------| | `active` | `boolean` | — | no | | | `barHideStrategy` | `leave \| move \| never \| scroll` | `never` | no | Управление скрытием скролл баров:
- `Never` - показывать всегда
- `Leave` - скрывать когда курсор покидает компонент
- `Scroll` - показывать только когда происходит скроллинг
- `Move` - показывать при движении курсора над компонентом | | `className` | `string` | — | no | CSS-класс | | `collapse` | `CollapseState` | — | no | Настройки раскрытия элементов | | `contentRender` | `((props: ContentRenderProps) => ReactNode)` | — | no | Рендер функция основного контента айтема | | `data-test-id` | `string` | — | no | | | `dataError` | `boolean` | — | no | Загрузка данных завершилась ошибкой: показывается `errorDataState` | | `dataFiltered` | `boolean` | — | no | Текущий пустой список — результат поиска/фильтра: показывается `noResultsState` вместо `noDataState` | | `errorDataState` | `EmptyStateProps` | — | no | Экран при ошибке запроса | | `footer` | `ReactNode ;` | — | no | Кастомизируемый элемент в конце списка | | `footerActiveElementsRefs` | `RefObject[]` | — | no | Список ссылок на кастомные элементы, помещенные в специальную секцию внизу списка | | `footerDivider` | `boolean` | — | no | Показывать divider между body и footer (Figma `dropdownContainer.dividerWrapper` снизу) | | `header` | `ReactNode ;` | — | no | Кастомизируемый элемент в начале списка — Figma `dropdownContainer.topBar`. Подходит для заголовка / справочного блока над поиском. | | `headerDivider` | `boolean` | — | no | Показывать divider между header и body (Figma `dropdownContainer.dividerWrapper` сверху) | | `items` | `ItemId[]` | — | yes | | | `keyboardNavigationRef` | `RefObject<{ focusItem(id: ItemId): void; }>` | — | no | Ссылка на управление навигацией листа с клавиатуры | | `limitedScrollHeight` | `boolean` | — | no | Ограничить максимальную высоту скролл-контейнера в зависимости от `size` | | `loading` | `boolean` | — | no | Флаг, отвечающий за состояние загрузки списка | | `marker` | `boolean` | — | no | Отображать ли маркер у выбранного элемента списка | | `nested` | `boolean` | — | no | | | `noDataState` | `EmptyStateProps` | — | no | Экран при отсутствии данных | | `noResultsState` | `EmptyStateProps` | — | no | Экран при отсутствии результатов поиска или фильтров | | `onBlur` | `((e: FocusEvent) => void)` | — | no | | | `onDragEnd` | `((event: DragEndEvent) => void)` | — | no | @internal Обработчик завершения drag&drop-переупорядочивания (см. публичный `onItemsReorder` у `List`/`Droplist`) — вычисляется в `useListItemsModel` и оперирует исходным (не плоским) деревом `items`. Наличие принудительно включает невиртуализированный рендер и оборачивает айтемы в `DndContext`/`SortableContext`. | | `onFocus` | `((e: FocusEvent) => void)` | — | no | | | `onKeyDown` | `(((e: KeyboardEvent) => void) & ((e: KeyboardEvent) => void))` | — | no | Обработчик события по нажатию клавиш | | `onScroll` | `((event?: Event) => void)` | — | no | Колбек на скролл прокручиваемого списка | | `pinBottom` | `ItemId[]` | — | no | | | `pinTop` | `ItemId[]` | — | no | | | `scroll` | `boolean` | — | no | Включить ли скролл для основной части списка | | `scrollContainerClassName` | `string` | — | no | CSS-класс для scroll обертки основного списка айтемов | | `scrollContainerRef` | `Ref` | — | no | Ссылка на контейнер, который скроллится | | `scrollRef` | `Ref` | — | no | Ссылка на элемент, обозначающий самый конец прокручиваемого списка | | `scrollToSelectedItem` | `boolean` | `false` | no | Флаг, отвечающий за прокручивание до выбранного элемента | | `search` | `SearchState` | — | no | Настройки поисковой строки | | `searchItem` | `FlattenBaseItem` | — | no | | | `selection` | `SelectionMultipleState \| SelectionSingleState` | — | no | Настройки выбора элементов. `mode: 'single'` — один выбранный элемент (`value: ItemId`), `mode: 'multiple'` — множественный выбор (`value: ItemId[]`). Без `selection` выбора нет — клик вызывает только `onClick` элемента. | | `size` | `l \| m \| s` | — | no | Размер списка | | `sortableIds` | `ItemId[]` | — | no | @internal Плоский список id всех сортируемых элементов (все уровни) для `SortableContext` | | `tabIndex` | `number` | — | no | `tabIndex` корневого элемента списка (для управления порядком фокуса) | | `untouchableScrollbars` | `boolean` | — | no | Отключает возможность взаимодействовать со скролбарами мышью. | | `virtualized` | `boolean` | `false` | no | | ### MobileDroplist | Prop | Type | Default | Required | Description | |------|------|---------|----------|-------------| | `actionButton` | `ReactNode` | — | no | Только mobile (`BottomSheet`): action-кнопка справа в шапке. | | `barHideStrategy` | `leave \| move \| never \| scroll` | — | no | Управление скрытием скролл баров:
- `Never` - показывать всегда
- `Leave` - скрывать когда курсор покидает компонент
- `Scroll` - показывать только когда происходит скроллинг
- `Move` - показывать при движении курсора над компонентом | | `children` | `ReactNode \| ({onKeyDown}) => ReactNode * Рендер функция принимает аргументы `onKeyDown` - хендлер ввода, для поддержки управления с клавиатуры` | — | yes | Триггер для дроплиста | | `className` | `string` | — | no | CSS-класс | | `closeDroplistOnItemClick` | `boolean` | `false` | no | Закрывать выпадающий список после клика на базовый айтем. Работает в режимах selection: 'none' \| 'single' | | `closeOnPopstate` | `boolean` | — | no | Закрывать ли поповер при переходе по истории браузера | | `collapse` | `CollapseState` | — | no | Настройки раскрытия элементов | | `container` | `RefObject` | — | no | Контейнер портала (ref). Переопределяет `PortalContext` для этого дроплиста (по аналогии с `container` у Modal/Drawer). По умолчанию — из `PortalContextProvider`. | | `contentRender` | `((props: ContentRenderProps) => ReactNode)` | — | no | Рендер функция основного контента айтема | | `data-test-id` | `string` | — | no | | | `dataError` | `boolean` | — | no | Загрузка данных завершилась ошибкой: показывается `errorDataState` | | `dataFiltered` | `boolean` | — | no | Текущий пустой список — результат поиска/фильтра: показывается `noResultsState` вместо `noDataState` | | `errorDataState` | `EmptyStateProps` | — | no | Экран при ошибке запроса | | `footer` | `ReactNode ;` | — | no | Кастомизируемый элемент в конце списка | | `footerActiveElementsRefs` | `RefObject[]` | — | no | Список ссылок на кастомные элементы, помещенные в специальную секцию внизу списка | | `footerDivider` | `boolean` | — | no | Показывать divider между body и footer (Figma `dropdownContainer.dividerWrapper` снизу) | | `header` | `ReactNode ;` | — | no | Кастомизируемый элемент в начале списка — Figma `dropdownContainer.topBar`. Подходит для заголовка / справочного блока над поиском. | | `headerDivider` | `boolean` | — | no | Показывать divider между header и body (Figma `dropdownContainer.dividerWrapper` сверху) | | `items` | `Item[] \| ReorderItem[]` | — | yes | | | `label` | `string` | — | no | Только mobile (`BottomSheet`): заголовок шапки. | | `limitedScrollHeight` | `boolean` | — | no | Ограничить максимальную высоту скролл-контейнера в зависимости от `size` | | `loading` | `boolean` | — | no | Флаг, отвечающий за состояние загрузки списка | | `marker` | `boolean` | — | no | Отображать ли маркер у выбранного элемента списка | | `noDataState` | `EmptyStateProps` | — | no | Экран при отсутствии данных | | `noResultsState` | `EmptyStateProps` | — | no | Экран при отсутствии результатов поиска или фильтров | | `onBackButtonClick` | `(() => void)` | — | no | Только mobile (`BottomSheet`): callback back-кнопки. | | `onItemsReorder` | `((items: ReorderItem[]) => void)` | — | no | | | `onOpenChange` | `((isOpen: boolean) => void)` | — | no | Колбек отображения компонента. Срабатывает при изменении состояния open. | | `onScroll` | `((event?: Event) => void)` | — | no | Колбек на скролл прокручиваемого списка | | `open` | `boolean` | — | no | Управляет состоянием показан/не показан. | | `pinBottom` | `Item[]` | — | no | Элементы списка, закрепленные снизу | | `pinTop` | `Item[]` | — | no | Элементы списка, закрепленные сверху | | `scroll` | `boolean` | — | no | Включить ли скролл для основной части списка | | `scrollContainerClassName` | `string` | — | no | CSS-класс для scroll обертки основного списка айтемов | | `scrollContainerRef` | `Ref` | — | no | Ссылка на контейнер, который скроллится | | `scrollRef` | `Ref` | — | no | Ссылка на элемент, обозначающий самый конец прокручиваемого списка | | `scrollToSelectedItem` | `boolean` | — | no | Флаг, отвечающий за прокручивание до выбранного элемента | | `search` | `SearchState` | — | no | Настройки поисковой строки | | `selection` | `SelectionMultipleState \| SelectionSingleState` | — | no | Настройки выбора элементов. `mode: 'single'` — один выбранный элемент (`value: ItemId`), `mode: 'multiple'` — множественный выбор (`value: ItemId[]`). Без `selection` выбора нет — клик вызывает только `onClick` элемента. | | `size` | `l \| m \| s` | — | no | Размер списка | | `slotAfterTitle` | `ReactNode` | — | no | Только mobile (`BottomSheet`): slot справа от заголовка. | | `untouchableScrollbars` | `boolean` | — | no | Отключает возможность взаимодействовать со скролбарами мышью. | | `virtualized` | `boolean` | — | no | | ### NewListContextProvider | Prop | Type | Default | Required | Description | |------|------|---------|----------|-------------| | `contentRender` | `((props: ContentRenderProps) => ReactNode)` | — | no | Рендер функция основного контента айтема | | `firstItemId` | `ItemId` | — | no | | | `flattenItems` | `Record` | — | yes | | | `focusFlattenItems` | `Record` | — | yes | | | `marker` | `boolean` | — | no | Отображать ли маркер у выбранного элемента списка | | `size` | `l \| m \| s` | — | no | Размер списка | | `virtualized` | `boolean` | — | no | | ### NextListItem | Prop | Type | Default | Required | Description | |------|------|---------|----------|-------------| | `afterContent` | `ReactElement` | — | no | Слот после основного контента | | `allChildIds` | `ItemId[]` | — | yes | | | `barHideStrategy` | `leave \| move \| never \| scroll` | — | no | Управление скрытием скролл баров:
- `Never` - показывать всегда
- `Leave` - скрывать когда курсор покидает компонент
- `Scroll` - показывать только когда происходит скроллинг
- `Move` - показывать при движении курсора над компонентом | | `beforeContent` | `ReactElement` | — | no | Слот до основного контента | | `checked` | `boolean` | — | no | Управляемое состояние выбранности айтема | | `className` | `string` | — | no | CSS-класс | | `content` | `ReactNode \| ItemContentProps` | — | no | Основной контент айтема | | `data-test-id` | `string` | — | no | | | `dataError` | `boolean` | `false` | no | Загрузка вложенного списка завершилась ошибкой — показывается `errorDataState` | | `dataFiltered` | `boolean` | `false` | no | Вложенный список отфильтрован — пустой результат показывает `noResultsState`, а не `noDataState` | | `disabled` | `boolean` | — | no | Флаг неактивности элемента | | `focusId` | `ItemId` | — | no | | | `hidden` | `boolean` | — | no | Скрыть элемент из списка (не рендерится и выпадает из навигации) | | `id` | `ItemId` | — | no | Уникальный идентификатор | | `itemRef` | `RefObject` | — | no | Ссылка на DOM-элемент айтема | | `itemWrapRender` | `((item: ReactNode) => ReactNode)` | — | no | Рендер-обёртка вокруг айтема (например, для проксирования в `Tooltip`/`Link`) | | `items` | `ItemId[]` | — | yes | | | `loading` | `boolean` | `false` | no | Состояние загрузки вложенного списка — показывается спиннер вместо элементов | | `onBlur` | `((e: FocusEvent) => void)` | — | no | Колбек обработки блюра | | `onClick` | `((e: MouseEvent) => void)` | — | no | Колбек обработки клика | | `onFocus` | `((e: FocusEvent) => void)` | — | no | Колбек обработки фокуса | | `onKeyDown` | `((e: KeyboardEvent) => void)` | — | no | Колбек обработки нажатия клавиши | | `onMouseDown` | `((e: MouseEvent) => void)` | — | no | Колбек обработки нажатия кнопки мыши | | `onScroll` | `((event?: Event) => void)` | — | no | Колбек на скролл прокручиваемого списка | | `onSublistOpenChanged` | `((open: boolean, id?: ItemId) => void)` | — | no | Колбек смены состояния вложенного списка (открыт/закрыт) | | `placement` | `left \| left-end \| left-start \| right \| right-end \| right-start` | `right-start` | no | Сторона, с которой раскрывается вложенный список относительно элемента | | `scroll` | `boolean` | — | no | Включить ли скролл для основной части списка | | `scrollContainerRef` | `Ref` | — | no | Ссылка на контейнер, который скроллится | | `scrollRef` | `Ref` | — | no | Ссылка на элемент, обозначающий самый конец прокручиваемого списка | | `showSwitchIcon` | `boolean` | — | no | Флаг отображения иконки у чекбоксов | | `untouchableScrollbars` | `boolean` | `false` | no | Отключает возможность взаимодействовать со скролбарами мышью. | ### OverlayDragHandle _Нет публичных пропсов._ ### PinBottomGroupItem _Нет публичных пропсов._ ### PinTopGroupItem _Нет публичных пропсов._ ### ReorderableDroplist | Prop | Type | Default | Required | Description | |------|------|---------|----------|-------------| | `actionButton` | `ReactNode` | — | no | Только mobile (`BottomSheet`): action-кнопка справа в шапке. | | `barHideStrategy` | `leave \| move \| never \| scroll` | — | no | Управление скрытием скролл баров:
- `Never` - показывать всегда
- `Leave` - скрывать когда курсор покидает компонент
- `Scroll` - показывать только когда происходит скроллинг
- `Move` - показывать при движении курсора над компонентом | | `children` | `ReactNode \| ({onKeyDown}) => ReactNode * Рендер функция принимает аргументы `onKeyDown` - хендлер ввода, для поддержки управления с клавиатуры` | — | yes | Триггер для дроплиста | | `className` | `string` | — | no | CSS-класс | | `closeDroplistOnItemClick` | `boolean` | `false` | no | Закрывать выпадающий список после клика на базовый айтем. Работает в режимах selection: 'none' \| 'single' | | `closeOnPopstate` | `boolean` | — | no | Закрывать ли поповер при переходе по истории браузера | | `collapse` | `CollapseState` | — | no | Настройки раскрытия элементов | | `container` | `RefObject` | — | no | Контейнер портала (ref). Переопределяет `PortalContext` для этого дроплиста (по аналогии с `container` у Modal/Drawer). По умолчанию — из `PortalContextProvider`. | | `contentRender` | `((props: ContentRenderProps) => ReactNode)` | — | no | Рендер функция основного контента айтема | | `data-test-id` | `string` | — | no | | | `dataError` | `boolean` | — | no | Загрузка данных завершилась ошибкой: показывается `errorDataState` | | `dataFiltered` | `boolean` | — | no | Текущий пустой список — результат поиска/фильтра: показывается `noResultsState` вместо `noDataState` | | `errorDataState` | `EmptyStateProps` | — | no | Экран при ошибке запроса | | `footer` | `ReactNode ;` | — | no | Кастомизируемый элемент в конце списка | | `footerActiveElementsRefs` | `RefObject[]` | — | no | Список ссылок на кастомные элементы, помещенные в специальную секцию внизу списка | | `footerDivider` | `boolean` | — | no | Показывать divider между body и footer (Figma `dropdownContainer.dividerWrapper` снизу) | | `header` | `ReactNode ;` | — | no | Кастомизируемый элемент в начале списка — Figma `dropdownContainer.topBar`. Подходит для заголовка / справочного блока над поиском. | | `headerDivider` | `boolean` | — | no | Показывать divider между header и body (Figma `dropdownContainer.dividerWrapper` сверху) | | `items` | `ReorderItem[]` | — | yes | Основные элементы списка: строки `SimpleItem` и/или группы с заголовком `SimpleGroupItem` (`type: 'group'` + `label` + сортируемые `items`). | | `label` | `string` | — | no | Только mobile (`BottomSheet`): заголовок шапки. | | `limitedScrollHeight` | `boolean` | — | no | Ограничить максимальную высоту скролл-контейнера в зависимости от `size` | | `listRef` | `RefObject` | — | no | Ссылка на элемент выпадающего списка | | `loading` | `boolean` | — | no | Флаг, отвечающий за состояние загрузки списка | | `marker` | `boolean` | — | no | Отображать ли маркер у выбранного элемента списка | | `noDataState` | `EmptyStateProps` | — | no | Экран при отсутствии данных | | `noResultsState` | `EmptyStateProps` | — | no | Экран при отсутствии результатов поиска или фильтров | | `onBackButtonClick` | `(() => void)` | — | no | Только mobile (`BottomSheet`): callback back-кнопки. | | `onItemsReorder` | `(items: ReorderItem[]) => void` | — | yes | Колбек по завершению drag&drop-переупорядочивания элементов списка. Список остаётся управляемым: сам не хранит порядок, а отдаёт наружу целиком обновлённое дерево `items` — потребитель обновляет свой стейт этим значением. Переупорядочивание работает только среди «братьев» одного уровня (строки без группы либо строки внутри одной группы; перенос между группами не поддерживается). | | `onOpenChange` | `((isOpen: boolean) => void)` | — | no | Колбек отображения компонента. Срабатывает при изменении состояния open. | | `onScroll` | `((event?: Event) => void)` | — | no | Колбек на скролл прокручиваемого списка | | `open` | `boolean` | — | no | Управляет состоянием показан/не показан. | | `pinBottom` | `Item[]` | — | no | Элементы списка, закрепленные снизу | | `pinTop` | `Item[]` | — | no | Элементы списка, закрепленные сверху | | `placement` | `bottom \| bottom-end \| bottom-start \| left \| left-end \| left-start \| right \| right-end \| right-start \| top \| top-end \| top-start` | `top` | no | Положение поповера относительно своего триггера (children). | | `scroll` | `boolean` | — | no | Включить ли скролл для основной части списка | | `scrollContainerClassName` | `string` | — | no | CSS-класс для scroll обертки основного списка айтемов | | `scrollContainerRef` | `Ref` | — | no | Ссылка на контейнер, который скроллится | | `scrollRef` | `Ref` | — | no | Ссылка на элемент, обозначающий самый конец прокручиваемого списка | | `scrollToSelectedItem` | `boolean` | — | no | Флаг, отвечающий за прокручивание до выбранного элемента | | `search` | `SearchState` | — | no | Настройки поисковой строки | | `selection` | `SelectionMultipleState \| SelectionSingleState` | — | no | Настройки выбора элементов. `mode: 'single'` — один выбранный элемент (`value: ItemId`), `mode: 'multiple'` — множественный выбор (`value: ItemId[]`). Без `selection` выбора нет — клик вызывает только `onClick` элемента. | | `size` | `l \| m \| s` | — | no | Размер списка | | `slotAfterTitle` | `ReactNode` | — | no | Только mobile (`BottomSheet`): slot справа от заголовка. | | `trigger` | `click \| clickAndFocusVisible \| focus \| focusVisible \| hover \| hoverAndFocus \| hoverAndFocusVisible` | — | no | Условие отображения поповера:
- `click` - открывать по клику
- `hover` - открывать по ховеру
- `focusVisible` - открывать по focus-visible
- `focus` - открывать по фокусу
- `hoverAndFocusVisible` - открывать по ховеру и focus-visible
- `hoverAndFocus` - открывать по ховеру и фокусу
- `clickAndFocusVisible` - открывать по клику и focus-visible | | `triggerClassName` | `string` | — | no | CSS-класс триггера | | `triggerElemRef` | `RefObject` | — | no | Ссылка на элемент-триггер для дроплиста | | `untouchableScrollbars` | `boolean` | — | no | Отключает возможность взаимодействовать со скролбарами мышью. | | `widthStrategy` | `auto \| eq \| gte` | `auto` | no | Стратегия управления шириной контейнера поповера
- `auto` - соответствует ширине контента,
- `gte` - Great Than or Equal, равен ширине таргета или больше ее, если контент в поповере шире,
- `eq` - Equal, строго равен ширине таргета. | #### Related types - `BaseItemWithoutNonGroup` (interface) - `CollapseState` (interface) - `CommonGroupItem` (interface) - `EmptyStateProps` (interface) - `IconPredefinedProps` (interface) - `Item` (alias) - `ItemContent` (interface) - `ItemId` (alias) - `OnChangeHandler` (alias) - `Placement` = `bottom | bottom-end | bottom-start | left | left-end | left-start | right | right-end | right-start | top | top-end | top-start` - `PopoverWidthStrategy` = `auto | eq | gte` - `ReorderItem` (alias) - `ScrollProps` (interface) - `SearchState` (interface) - `SelectionMultipleState` (interface) - `SelectionSingleState` (interface) - `Size` = `l | m | s` - `Trigger` = `click | clickAndFocusVisible | focus | focusVisible | hover | hoverAndFocus | hoverAndFocusVisible` - `TruncateStringProps` (alias) ### ReorderableList | Prop | Type | Default | Required | Description | |------|------|---------|----------|-------------| | `barHideStrategy` | `leave \| move \| never \| scroll` | — | no | Управление скрытием скролл баров:
- `Never` - показывать всегда
- `Leave` - скрывать когда курсор покидает компонент
- `Scroll` - показывать только когда происходит скроллинг
- `Move` - показывать при движении курсора над компонентом | | `className` | `string` | — | no | CSS-класс | | `collapse` | `CollapseState` | `{}` | no | Настройки раскрытия элементов | | `contentRender` | `((props: ContentRenderProps) => ReactNode)` | — | no | Рендер функция основного контента айтема | | `data-test-id` | `string` | — | no | | | `dataError` | `boolean` | — | no | Загрузка данных завершилась ошибкой: показывается `errorDataState` | | `dataFiltered` | `boolean` | — | no | Текущий пустой список — результат поиска/фильтра: показывается `noResultsState` вместо `noDataState` | | `errorDataState` | `EmptyStateProps` | — | no | Экран при ошибке запроса | | `footer` | `ReactNode ;` | — | no | Кастомизируемый элемент в конце списка | | `footerActiveElementsRefs` | `RefObject[]` | — | no | Список ссылок на кастомные элементы, помещенные в специальную секцию внизу списка | | `footerDivider` | `boolean` | — | no | Показывать divider между body и footer (Figma `dropdownContainer.dividerWrapper` снизу) | | `hasListInFocusChain` | `boolean` | `true` | no | Флаг, отвечающий за включение самого родительского контейнера листа в цепочку фокусирующихся элементов | | `header` | `ReactNode ;` | — | no | Кастомизируемый элемент в начале списка — Figma `dropdownContainer.topBar`. Подходит для заголовка / справочного блока над поиском. | | `headerDivider` | `boolean` | — | no | Показывать divider между header и body (Figma `dropdownContainer.dividerWrapper` сверху) | | `items` | `ReorderItem[]` | `[]` | no | Основные элементы списка: строки `SimpleItem` и/или группы с заголовком `SimpleGroupItem` (`type: 'group'` + `label` + сортируемые `items`). | | `keyboardNavigationRef` | `RefObject<{ focusItem(id: ItemId): void; }>` | — | no | Ссылка на управление навигацией листа с клавиатуры | | `limitedScrollHeight` | `boolean` | — | no | Ограничить максимальную высоту скролл-контейнера в зависимости от `size` | | `loading` | `boolean` | — | no | Флаг, отвечающий за состояние загрузки списка | | `marker` | `boolean` | `true` | no | Отображать ли маркер у выбранного элемента списка | | `noDataState` | `EmptyStateProps` | — | no | Экран при отсутствии данных | | `noResultsState` | `EmptyStateProps` | — | no | Экран при отсутствии результатов поиска или фильтров | | `onItemsReorder` | `(items: ReorderItem[]) => void` | — | yes | Колбек по завершению drag&drop-переупорядочивания элементов списка. Список остаётся управляемым: сам не хранит порядок, а отдаёт наружу целиком обновлённое дерево `items` — потребитель обновляет свой стейт этим значением. Переупорядочивание работает только среди «братьев» одного уровня (строки без группы либо строки внутри одной группы; перенос между группами не поддерживается). | | `onKeyDown` | `((e: KeyboardEvent) => void)` | — | no | Обработчик события по нажатию клавиш | | `onScroll` | `((event?: Event) => void)` | — | no | Колбек на скролл прокручиваемого списка | | `pinBottom` | `Item[]` | `[]` | no | Элементы списка, закрепленные снизу | | `pinTop` | `Item[]` | `[]` | no | Элементы списка, закрепленные сверху | | `scroll` | `boolean` | — | no | Включить ли скролл для основной части списка | | `scrollContainerClassName` | `string` | — | no | CSS-класс для scroll обертки основного списка айтемов | | `scrollContainerRef` | `Ref` | — | no | Ссылка на контейнер, который скроллится | | `scrollRef` | `Ref` | — | no | Ссылка на элемент, обозначающий самый конец прокручиваемого списка | | `scrollToSelectedItem` | `boolean` | — | no | Флаг, отвечающий за прокручивание до выбранного элемента | | `search` | `SearchState` | — | no | Настройки поисковой строки | | `selection` | `SelectionMultipleState \| SelectionSingleState` | — | no | Настройки выбора элементов. `mode: 'single'` — один выбранный элемент (`value: ItemId`), `mode: 'multiple'` — множественный выбор (`value: ItemId[]`). Без `selection` выбора нет — клик вызывает только `onClick` элемента. | | `size` | `l \| m \| s` | `m` | no | Размер списка | | `tabIndex` | `number` | `0` | no | `tabIndex` корневого элемента списка (для управления порядком фокуса) | | `untouchableScrollbars` | `boolean` | — | no | Отключает возможность взаимодействовать со скролбарами мышью. | ### SearchItem | Prop | Type | Default | Required | Description | |------|------|---------|----------|-------------| | `inTopBar` | `boolean` | — | no | Рендер для шапки dropdown — без `.listItem`-обёртки. | | `itemRef` | `RefObject` | — | no | | | `onFocus` | `((e: FocusEvent) => void)` | — | no | onFocus поля (синхронизация активного item, когда search вне `
    `). | | `onNavKeyDown` | `((e: KeyboardEvent) => void)` | — | no | Доп. обработчик клавиш навигации по списку. Нужен, когда поле поиска вынесено из `
      ` в шапку dropdown (topBar) и события клавиатуры больше не всплывают на `
        `-onKeyDown — DropList передаёт сюда `handleListKeyDown` (factory). | | `search` | `SearchState` | — | no | | #### Related types - `SearchState` (interface) ### SelectionProvider | Prop | Type | Default | Required | Description | |------|------|---------|----------|-------------| | `defaultValue` | `ItemId \| ItemId[]` | — | no | Начальное состояние | | `mode` | `multiple \| none \| single` | — | no | Режим выбора | | `onChange` | `OnChangeHandler \| OnChangeHandler` | — | no | Controlled обработчик изменения состояния — получает массив выбранных `ItemId[]` Controlled обработчик изменения состояния — получает выбранный `ItemId` | | `value` | `ItemId \| ItemId[]` | — | no | Controlled состояние | ### Separator | Prop | Type | Default | Required | Description | |------|------|---------|----------|-------------| | `beforeContent` | `ReactNode` | — | no | Слот иконки слева от label. | | `divider` | `boolean` | — | no | | | `groupVariant` | `subtitle \| subtitleTertiary` | `subtitleTertiary` | no | | | `label` | `string` | — | no | | | `selectButton` | `{ onClick?(e: MouseEvent): void; indeterminate?: boolean; checked?: boolean; itemRef?: ForwardedRef<...> \| undefined; label?: string \| undefined; } \| undefined` | — | no | | | `truncate` | `{ variant?: "end" \| "middle"; }` | — | no | | #### Related types - `TruncateStringProps` (alias) ### SimpleGroupBlock | Prop | Type | Default | Required | Description | |------|------|---------|----------|-------------| | `beforeContent` | `ReactNode` | — | no | Слот иконки слева от label. | | `children` | `ReactNode` | — | yes | Отрисованные строки группы (`SimpleItem`). | | `divider` | `boolean` | — | no | | | `groupVariant` | `subtitle \| subtitleTertiary` | — | no | | | `id` | `ItemId` | — | yes | Идентификатор группы — sortable identity для перестановки групп между собой. | | `label` | `string` | — | no | | | `rowIds` | `ItemId[]` | — | yes | Id сортируемых строк группы для вложенного `SortableContext` (переупорядочивание строк внутри группы). | | `truncate` | `{ variant?: "end" \| "middle"; }` | — | no | | ### SimpleGroupBlockOverlay | Prop | Type | Default | Required | Description | |------|------|---------|----------|-------------| | `beforeContent` | `ReactNode` | — | no | Слот иконки слева от label. | | `divider` | `boolean` | — | no | | | `groupVariant` | `subtitle \| subtitleTertiary` | — | no | | | `label` | `string` | — | no | | | `rows` | `FlattenSimpleItem[]` | — | yes | Строки группы: копия блока рендерится целиком, вместе с ними. | | `size` | `l \| m \| s` | — | no | Размер списка — заголовок повторяет левую раскладку строки (`padding-left` + ручка + `gap`). | | `truncate` | `{ variant?: "end" \| "middle"; }` | — | no | | ### SimpleItem | Prop | Type | Default | Required | Description | |------|------|---------|----------|-------------| | `afterContent` | `ReactElement` | — | no | Слот после основного контента | | `allChildIds` | `ItemId[]` | — | yes | | | `beforeContent` | `ReactElement` | — | no | Слот до основного контента | | `checked` | `boolean` | — | no | Управляемое состояние выбранности айтема | | `className` | `string` | — | no | CSS-класс | | `content` | `ReactNode \| ItemContentProps` | — | no | Основной контент айтема | | `data-test-id` | `string` | — | no | | | `disabled` | `boolean` | — | no | Флаг неактивности элемента | | `groupId` | `ItemId` | — | no | Id группы-родителя (если строка внутри группы) — уходит в `data` `useSortable` (kind=row). | | `hidden` | `boolean` | — | no | Скрыть элемент из списка (не рендерится и выпадает из навигации) | | `id` | `ItemId` | — | no | Уникальный идентификатор | | `inactive` | `boolean` | — | no | Флаг отображения отключения реакции на любое css состояние (hover/focus и тд)
        Так же элемент пропадает из навигации с клавиатуры, и не может быть выбран (selection) | | `itemRef` | `RefObject` | — | no | Ссылка на DOM-элемент айтема | | `itemWrapRender` | `((item: ReactNode) => ReactNode)` | — | no | Рендер-обёртка вокруг айтема (например, для проксирования в `Tooltip`/`Link`) | | `items` | `ItemId[]` | — | yes | | | `onBlur` | `((e: FocusEvent) => void)` | — | no | Колбек обработки блюра | | `onClick` | `((e: MouseEvent) => void)` | — | no | Колбек обработки клика | | `onFocus` | `((e: FocusEvent) => void)` | — | no | Колбек обработки фокуса | | `onKeyDown` | `((e: KeyboardEvent) => void)` | — | no | Колбек обработки нажатия клавиши | | `onMouseDown` | `((e: MouseEvent) => void)` | — | no | Колбек обработки нажатия кнопки мыши | | `showSwitchIcon` | `boolean` | — | no | Флаг отображения иконки у чекбоксов | | `switch` | `boolean` | — | no | Флаг отображения состояния выбранного элемента через switch | ### SimpleItemOverlay | Prop | Type | Default | Required | Description | |------|------|---------|----------|-------------| | `afterContent` | `ReactElement` | — | no | Слот после основного контента | | `allChildIds` | `ItemId[]` | — | yes | | | `bare` | `boolean` | — | no | Строка внутри копии блока группы (`SimpleGroupBlockOverlay`): поверхность и тень несёт сам блок, поэтому у строки их не дублируем. | | `beforeContent` | `ReactElement` | — | no | Слот до основного контента | | `checked` | `boolean` | — | no | Управляемое состояние выбранности айтема | | `className` | `string` | — | no | CSS-класс | | `content` | `ReactNode \| ItemContentProps` | — | no | Основной контент айтема | | `data-test-id` | `string` | — | no | | | `disabled` | `boolean` | — | no | Флаг неактивности элемента | | `hidden` | `boolean` | — | no | Скрыть элемент из списка (не рендерится и выпадает из навигации) | | `id` | `ItemId` | — | no | Уникальный идентификатор | | `inactive` | `boolean` | — | no | Флаг отображения отключения реакции на любое css состояние (hover/focus и тд)
        Так же элемент пропадает из навигации с клавиатуры, и не может быть выбран (selection) | | `itemRef` | `RefObject` | — | no | Ссылка на DOM-элемент айтема | | `itemWrapRender` | `((item: ReactNode) => ReactNode)` | — | no | Рендер-обёртка вокруг айтема (например, для проксирования в `Tooltip`/`Link`) | | `items` | `ItemId[]` | — | yes | | | `onBlur` | `((e: FocusEvent) => void)` | — | no | Колбек обработки блюра | | `onClick` | `((e: MouseEvent) => void)` | — | no | Колбек обработки клика | | `onFocus` | `((e: FocusEvent) => void)` | — | no | Колбек обработки фокуса | | `onKeyDown` | `((e: KeyboardEvent) => void)` | — | no | Колбек обработки нажатия клавиши | | `onMouseDown` | `((e: MouseEvent) => void)` | — | no | Колбек обработки нажатия кнопки мыши | | `showSwitchIcon` | `boolean` | — | no | Флаг отображения иконки у чекбоксов | | `switch` | `boolean` | — | no | Флаг отображения состояния выбранного элемента через switch | ## Примеры ### BasicDroplist ```tsx import { Button } from '@cloud-ru/ds-button'; import { Droplist } from '@cloud-ru/ds-list'; import { useState } from 'react'; import styles from './styles.module.scss'; export function BasicDroplist() { const [value, setValue] = useState('rub'); return (
        ); } ``` ### BasicList ```tsx import { List } from '@cloud-ru/ds-list'; import styles from './styles.module.scss'; export function BasicList() { return (
        ); } ``` ### DroplistAsFormSelect ```tsx import { Button } from '@cloud-ru/ds-button'; import { Droplist } from '@cloud-ru/ds-list'; import { useState } from 'react'; import styles from './styles.module.scss'; export function DroplistAsFormSelect() { const [value, setValue] = useState('m'); const options = [ { id: 's', content: { label: 'Small (1 vCPU, 2 GB RAM)' } }, { id: 'm', content: { label: 'Medium (2 vCPU, 4 GB RAM)' } }, { id: 'l', content: { label: 'Large (4 vCPU, 8 GB RAM)' } }, { id: 'xl', content: { label: 'X-Large (8 vCPU, 16 GB RAM)' } }, ]; const label = options.find(o => o.id === value)?.content.label ?? 'Выбрать'; return (
        ); } ``` ### DroplistMultiple ```tsx import { Button } from '@cloud-ru/ds-button'; import { Droplist } from '@cloud-ru/ds-list'; import { useState } from 'react'; import styles from './styles.module.scss'; export function DroplistMultiple() { const [value, setValue] = useState<(string | number)[]>(['email']); return (
        ); } ``` ### DroplistWithHeader ```tsx import { Button } from '@cloud-ru/ds-button'; import { Droplist } from '@cloud-ru/ds-list'; import { useState } from 'react'; import styles from './styles.module.scss'; export function DroplistWithHeader() { const [value, setValue] = useState('relevance'); return (
        ); } ``` ### DroplistWithSearch ```tsx import { Button } from '@cloud-ru/ds-button'; import { Droplist } from '@cloud-ru/ds-list'; import { useMemo, useState } from 'react'; import styles from './styles.module.scss'; const COUNTRIES = [ 'Австрия', 'Армения', 'Беларусь', 'Бразилия', 'Германия', 'Грузия', 'Индия', 'Казахстан', 'Китай', 'Россия', 'США', 'Турция', ]; export function DroplistWithSearch() { const [value, setValue] = useState('Россия'); const [query, setQuery] = useState(''); const items = useMemo( () => COUNTRIES.filter(name => name.toLowerCase().includes(query.toLowerCase())).map(name => ({ id: name, content: { label: name }, })), [query], ); return (
        ); } ``` ### EmptyState ```tsx import { List } from '@cloud-ru/ds-list'; import { useState } from 'react'; import styles from './styles.module.scss'; export function EmptyState() { const [query, setQuery] = useState('qwerty'); const items = query.length > 3 ? [] : [{ id: 'a', content: { label: 'Пример результата' } }]; return (
        ); } ``` ### GroupsCollapsible ```tsx import { List } from '@cloud-ru/ds-list'; import styles from './styles.module.scss'; export function GroupsCollapsible() { return (
        ); } ``` ### ItemContentDescription ```tsx import { List } from '@cloud-ru/ds-list'; import styles from './styles.module.scss'; export function ItemContentDescription() { return (
        ); } ``` ### ItemContentSlots ```tsx import { FileSVG, FolderSVG, StarSVG } from '@cloud-ru/ds-icons/interface/system'; import { List } from '@cloud-ru/ds-list'; import styles from './styles.module.scss'; export function ItemContentSlots() { return (
        , content: { label: 'Отчёты', caption: '24' }, afterContent: , }, { id: 'invoice', beforeContent: , content: { label: 'invoice-2024.pdf', caption: '1.2 МБ' }, }, ]} />
        ); } ``` ### ItemContentTruncate ```tsx import { List } from '@cloud-ru/ds-list'; import styles from './styles.module.scss'; export function ItemContentTruncate() { return (
        ); } ``` ### LinkItems ```tsx import { List } from '@cloud-ru/ds-list'; import { ReactNode } from 'react'; import styles from './styles.module.scss'; const asLink = (href: string) => function (item: ReactNode) { return ( {item} ); }; export function LinkItems() { return (
        ); } ``` ### ListItemWrap ```tsx import { List } from '@cloud-ru/ds-list'; import { ReactNode } from 'react'; import styles from './styles.module.scss'; const asExternalLink = (href: string) => function (item: ReactNode) { return ( {item} ); }; export function ListItemWrap() { return (
        ); } ``` ### ListReorder ```tsx import { ReorderableList, SimpleItem } from '@cloud-ru/ds-list'; import { useState } from 'react'; import styles from './styles.module.scss'; const INITIAL_ITEMS: SimpleItem[] = [ { id: 'inbox', content: { label: 'Входящие', caption: '12' } }, { id: 'sent', content: { label: 'Отправленные' } }, { id: 'archive', content: { label: 'Архив', caption: '238' } }, { id: 'trash', content: { label: 'Корзина', description: 'Удаляется через 30 дней' } }, ]; export function ListReorder() { const [items, setItems] = useState(INITIAL_ITEMS); return (
        ); } ``` ### ListReorderGroups ```tsx import { ReorderableList, ReorderItem } from '@cloud-ru/ds-list'; import { useState } from 'react'; import styles from './styles.module.scss'; const INITIAL_ITEMS: ReorderItem[] = [ { id: 'group-1', type: 'group', label: 'Избранное', divider: true, items: [ { id: 'catalog', content: { label: 'Каталог' } }, { id: 'orders', content: { label: 'Заказы' } }, { id: 'favorites', content: { label: 'Избранное' } }, ], }, { id: 'group-2', type: 'group', label: 'Система', divider: true, items: [ { id: 'settings', content: { label: 'Настройки' } }, { id: 'trash', content: { label: 'Корзина' } }, ], }, ]; export function ListReorderGroups() { const [items, setItems] = useState(INITIAL_ITEMS); return (
        ); } ``` ### MobileDroplist ```tsx import { AdaptiveProvider, LAYOUT_TYPE } from '@cloud-ru/ds-adaptive'; import { Button } from '@cloud-ru/ds-button'; import { Droplist } from '@cloud-ru/ds-list'; import { useState } from 'react'; import styles from './styles.module.scss'; export function MobileDroplist() { const [value, setValue] = useState('rub'); return (
        ); } ``` ### MultipleSelection ```tsx import { List } from '@cloud-ru/ds-list'; import { useState } from 'react'; import styles from './styles.module.scss'; export function MultipleSelection() { const [value, setValue] = useState<(string | number)[]>(['email', 'push']); return (
        ); } ``` ### NestedLevels ```tsx import { List } from '@cloud-ru/ds-list'; import styles from './styles.module.scss'; export function NestedLevels() { return (
        ); } ``` ### PinnedItems ```tsx import { List } from '@cloud-ru/ds-list'; import styles from './styles.module.scss'; export function PinnedItems() { return (
        ); } ``` ### SingleSelection ```tsx import { List } from '@cloud-ru/ds-list'; import { useState } from 'react'; import styles from './styles.module.scss'; export function SingleSelection() { const [value, setValue] = useState('usd'); return (
        ); } ``` ### Virtualized ```tsx import { Button } from '@cloud-ru/ds-button'; import { List } from '@cloud-ru/ds-list'; import { useMemo, useState } from 'react'; import styles from './styles.module.scss'; const TOTAL = 10_000; export function Virtualized() { const items = useMemo( () => Array.from({ length: TOTAL }, (_, i) => ({ id: `row-${i}`, content: { label: `Запись #${i + 1}`, caption: i % 5 === 0 ? 'new' : undefined }, })), [], ); const [value, setValue] = useState('row-0'); return (
        ); } ``` ### WithSearch ```tsx import { List } from '@cloud-ru/ds-list'; import { useMemo, useState } from 'react'; import styles from './styles.module.scss'; const ALL = [ 'Антон', 'Борис', 'Виктор', 'Галина', 'Дарья', 'Евгений', 'Жанна', 'Зоя', 'Игорь', 'Ксения', 'Лев', 'Мария', ]; export function WithSearch() { const [query, setQuery] = useState(''); const items = useMemo( () => ALL.filter(name => name.toLowerCase().includes(query.toLowerCase())).map(name => ({ id: name, content: { label: name }, })), [query], ); return (
        ); } ```