FieldSelectCreate
Поле выбора поверх FieldSelect из @cloud-ru/ds-fields со встроенной формой создания опции. Кнопка + Создать <entityName> показывается под полем и в футере дроплиста; по клику открывается форма в Drawer или Modal. Пропсы поля передаются через selectProps, состояния списка и поиск — из @cloud-ru/ds-list.
Когда использовать
- Выбор из списка, где пользователь может тут же создать новый объект, если нужного нет.
- Асинхронная подгрузка с состояниями загрузки/ошибки/пустого результата.
Когда не нужен FieldSelectCreate:
- Простой выбор без создания — используйте
FieldSelectиз@cloud-ru/ds-fields.
Анатомия
Действие «Создать»
Кнопка + Создать <entityName> рендерится в двух местах: под полем и в футере дроплиста (SelectFooter). По клику открывается форма создания:
Drawer(по умолчанию) илиModal— задаётся черезcreateLayoutType.- Содержимое формы и заголовок задаются в
createLayoutProps(content— форма,title— заголовок). - Кнопки «Создать» / «Отмена» добавляет сам компонент; на «Создать» вызывается
submitHandler. submitHandlerвозвращаетvalueновой опции — в single-режиме она выбирается в поле;afterClose— колбэк после закрытия.
entityName
Название сущности в единственном (single, вин. падеж) и множественном (plural) числе. Из них собираются тексты: кнопка «Создать single», пустые состояния «plural не обнаружены / не найдены», ошибка «Не удалось загрузить plural».
Права (permission, default canCreate)
Ограничивает доступ к чтению и созданию:
canCreate— доступны и выбор, и создание.canRead— поле активно, но кнопки «Создать» недоступны (с tooltip причины).none— поле недоступно (с tooltip).
Состояния списка
Управляются @cloud-ru/ds-list по флагам loading / dataError / dataFiltered (передаются через selectProps):
data— список опций.error— «Не удалось загрузитьplural» + кнопка «Обновить» (onRefetch).noData— «pluralне обнаружены»; иконку задаётentityIcon(по умолчанию — поиск).noResult— «pluralне найдены. Измените запрос или создайтеsingle».
Режим выбора (selectProps.selection, default single)
Наследуется от FieldSelect: single — одна опция, multiple — несколько. В multiple возвращённое из submitHandler значение не выбирается автоматически — выбор оставляется потребителю.
Size (default m)
Размер поля задаётся через selectProps.size: s, m, l.
Открытие на выбранном элементе (selectProps.scrollToSelectedItem)
FieldSelect проксирует scrollToSelectedItem из @cloud-ru/ds-list в дроплист — при открытии список прокручивается до выбранной опции:
<FieldSelectCreate selectProps={{ scrollToSelectedItem: true }} … />
Работает и с selectProps={{ virtualized: true }}.
Примеры использования
Базовый
import { FieldText } from '@cloud-ru/ds-fields';
import { ItemId } from '@cloud-ru/ds-list';
import { FieldSelectCreate } from '@cloud-ru/ds-uikit-product-fields-predefined';
import { useState } from 'react';
export function FieldSelectCreateBasic() {
const [items, setItems] = useState([
{ id: '1', content: { label: 'Production' } },
{ id: '2', content: { label: 'Staging' } },
]);
const [value, setValue] = useState<ItemId>();
const [name, setName] = useState('');
const submitHandler = () => {
const id = String(items.length + 1);
setItems(prev => [...prev, { id, content: { label: name.trim() || `Окружение ${id}` } }]);
setName('');
return Promise.resolve(id);
};
return (
<div style={{ width: 320 }}>
<FieldSelectCreate
entityName={{ single: 'Окружение', plural: 'Окружения' }}
selectProps={{ label: 'Окружение', items, value, onChange: setValue }}
createLayoutProps={{
title: 'Создание окружения',
content: <FieldText label='Название' value={name} onChange={setName} />,
}}
submitHandler={submitHandler}
/>
</div>
);
}Асинхронная загрузка
import { ItemId } from '@cloud-ru/ds-list';
import { FieldSelectCreate } from '@cloud-ru/ds-uikit-product-fields-predefined';
import { useState } from 'react';
export function FieldSelectCreateAsync() {
const [value, setValue] = useState<ItemId>();
const [loading, setLoading] = useState(false);
const [dataError, setDataError] = useState(true);
const load = () => {
setDataError(false);
setLoading(true);
setTimeout(() => setLoading(false), 800);
};
return (
<div style={{ width: 320 }}>
<FieldSelectCreate
entityName={{ single: 'Окружение', plural: 'Окружения' }}
selectProps={{
label: 'Окружение',
items: [{ id: '1', content: { label: 'Production' } }],
value,
onChange: setValue,
loading,
dataError,
}}
onRefetch={load}
createLayoutProps={{ title: 'Создание окружения', content: 'Форма создания' }}
submitHandler={() => Promise.resolve('1')}
/>
</div>
);
}Форма в модальном окне
import { FieldText } from '@cloud-ru/ds-fields';
import { ItemId } from '@cloud-ru/ds-list';
import { CREATE_LAYOUT_TYPE, FieldSelectCreate } from '@cloud-ru/ds-uikit-product-fields-predefined';
import { useState } from 'react';
export function FieldSelectCreateModal() {
const [items, setItems] = useState([
{ id: '1', content: { label: 'Production' } },
{ id: '2', content: { label: 'Staging' } },
]);
const [value, setValue] = useState<ItemId>();
const [name, setName] = useState('');
const submitHandler = () => {
const id = String(items.length + 1);
setItems(prev => [...prev, { id, content: { label: name.trim() || `Окружение ${id}` } }]);
setName('');
return Promise.resolve(id);
};
return (
<div style={{ width: 320 }}>
<FieldSelectCreate
entityName={{ single: 'Окружение', plural: 'Окружения' }}
selectProps={{ label: 'Окружение', items, value, onChange: setValue }}
createLayoutType={CREATE_LAYOUT_TYPE.Modal}
createLayoutProps={{
title: 'Создание окружения',
content: <FieldText label='Название' value={name} onChange={setName} />,
}}
submitHandler={submitHandler}
/>
</div>
);
}Props
Types
FieldSelectCreateProps| Prop | Type | Default | Required | Description |
|---|---|---|---|---|
afterClose | (() => void) | — | no | Колбэк после закрытия модалки/дровера создания. |
className | string | — | no | CSS-класс корневой обёртки. |
createLayoutProps | Omit<ModalProps, ManagedLayoutKeys> | (Omit<DrawerProps, "position" | ManagedLayoutKeys> & { ...; }) | — | yes | Пропсы модалки создания (`content` — форма создания опции). Пропсы дровера создания (`content` — форма создания опции); `position` по умолчанию `right`. |
createLayoutType | "drawer" | "modal" | drawer | no | По клику на «Создать» открывается модальное окно. По клику на «Создать» открывается дровер (по умолчанию). |
data-test-id | string | — | no | |
entityIcon | JSXElementConstructor<{ size?: number; className?: string; }> | undefined | — | no | Иконка пустого состояния `noData` (по умолчанию — иконка поиска). |
entityName | EntityName | — | yes | Название сущности в единственном и множественном числе — для кнопок и пустых состояний. |
onRefetch | (() => void) | — | no | Повтор загрузки в состоянии ошибки (кнопка «Обновить» в фу тере `errorDataState`). |
permission | "canCreate" | "canRead" | "none" | canCreate | no | Права пользователя: - `canCreate` (по умолчанию) — доступны и выбор, и создание; - `canRead` — поле активно, но создание недоступно (кнопки «Создать» с tooltip); - `none` — поле недоступно (с tooltip). |
selectProps | SelectFieldProps | — | yes | Пропсы, прокидываемые в `FieldSelect` (`@cloud-ru/ds-fields`). |
submitHandler | () => Promise<string | void> | — | yes | Действие создания опции: возвращает `value` новой опции, которая выбирается в поле (single-режим). |
Unions
Types
FieldSelectCreateProps
EntityName
Unions
ManagedLayoutKeys
Related props
DroplistListProps
FieldSelectDecoratorProps
ModalProps
PublicListContextType
ScrollProps
SelectionState
Адаптивность
Дроплист — адаптивный Droplist из @cloud-ru/ds-list: на desktop анкорный popover, на mobile — список в BottomSheet. Раскладку даёт AdaptiveProvider (@cloud-ru/ds-adaptive); отдельного пропа layoutType нет.
size на всех раскладках — база m. Автофокус поля выключается на mobile (наследуется из FieldSelect / @cloud-ru/ds-fields); вернуть — через selectProps={{ layoutPresets: { mobile: { autoFocus: true } } }}.