@cloud-ru/ds-fields 1.0.8-preview-61ab169d.0 → 1.0.8-preview-4074f7f8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -216,24 +216,6 @@ export function Affixes() {
216
216
  | `variant` | `"after"` \| `"before"` | — | Положение относительно поля |
217
217
  | `withDropdownList` | `boolean \| undefined` | — | Показать шеврон раскрытия |
218
218
 
219
- **FieldElementDroplistProps**
220
-
221
- | Prop | Type | Default | Description |
222
- |------|------|---------|-------------|
223
- | `closeDroplistOnItemClick` | `boolean \| undefined` | — | Закрывать выпадающий список после клика на базовый айтем. <br/> Работает в режимах selection: 'none' \| 'single' |
224
- | `items` | `BaseItemWithoutNonGroup` \| `CommonGroupItem` \| `Item` \| `ScrollProps` | — | Основные элементы списка |
225
- | `onOpenChange` | `((open: boolean) => void) \| undefined` | — | Колбек смены состояния раскрытия |
226
- | `open` | `boolean \| undefined` | — | Контролируемое состояние раскрытия |
227
- | `pinBottom` | `BaseItemWithoutNonGroup` \| `CommonGroupItem` \| `Item` \| `ScrollProps` | — | Элементы списка, закрепленные снизу |
228
- | `pinTop` | `BaseItemWithoutNonGroup` \| `CommonGroupItem` \| `Item` \| `ScrollProps` | — | Элементы списка, закрепленные сверху |
229
- | `placement` | `"bottom"` \| `"bottom-end"` \| `"bottom-start"` \| `"left"` \| `"left-end"` \| `"left-start"` \| `"right"` \| `"right-end"` \| `"right-start"` \| `"top"` \| `"top-end"` \| `"top-start"` | — | Положение поповера относительно своего триггера (children). |
230
- | `scroll` | `boolean \| undefined` | — | Включить ли скролл для основной части списка |
231
- | `scrollToSelectedItem` | `boolean \| undefined` | — | Флаг, отвечающий за прокручивание до выбранного элемента |
232
- | `search` | `SearchState` | — | Настройки поисковой строки |
233
- | `selection` | `SelectionMultipleState` \| `SelectionSingleState` | — | Настройки выбора элементов. `mode: 'single'` — один выбранный элемент (`value: ItemId`), <br/> `mode: 'multiple'` — множественный выбор (`value: ItemId[]`). Без `selection` выбора нет — <br/> клик вызывает только `onClick` элемента. |
234
- | `virtualized` | `boolean \| undefined` | — | Включить виртуализацию элементов списка. Рекомендуется при количестве элементов от 1000. |
235
- | `widthStrategy` | `"auto"` \| `"eq"` \| `"gte"` | — | Стратегия управления шириной контейнера поповера <br/> - `auto` - соответствует ширине контента, <br/> - `gte` - Great Than or Equal, равен ширине таргета или больше ее, если контент в поповере шире, <br/> - `eq` - Equal, строго равен ширине таргета. |
236
-
237
219
  **FieldElementSlot**
238
220
 
239
221
  | Prop | Type | Default | Description |
@@ -249,6 +231,15 @@ export function Affixes() {
249
231
  | `tabIndex` | `number \| undefined` | — | HTML tabIndex (`-1` — исключить кнопку из tab-order, фокус по Tab уходит в поле) |
250
232
  | `withDropdownList` | `boolean \| undefined` | — | Показать шеврон раскрытия |
251
233
 
234
+ **FieldLayoutPresets**
235
+
236
+ | Prop | Type | Default | Description |
237
+ |------|------|---------|-------------|
238
+ | `desktop` | `any` | — | |
239
+ | `desktopSmall` | `any` | — | |
240
+ | `mobile` | `any` | — | |
241
+ | `tablet` | `any` | — | |
242
+
252
243
  - `Variant` = `"after"` \| `"before"`
253
244
 
254
245
  ## FieldDate
@@ -456,7 +447,7 @@ export function Select() {
456
447
  | `data-test-id` | `string` | — | Тестовый id корня |
457
448
  | `dataError` | `DroplistProps` | — | Флаг «ошибка загрузки данных» — при `true` дроплист рендерит `errorDataState` <br/> вместо списка (для асинхронной подгрузки с провалившимся запросом). |
458
449
  | `dataFiltered` | `DroplistProps` | — | Флаг «список отфильтрован» — при `true` и пустом результате дроплист рендерит <br/> `noResultsState`. По умолчанию выводится из строки поиска (`searchable` + ввод). |
459
- | `defaultValue` | `ItemId` | — | Неуправляемое значение по умолчанию <br/> Неуправляемые значения по умолчанию |
450
+ | `defaultValue` | `ItemId` | — | Неуправляемое значение по умолчанию. Пустая строка трактуется как «значение не выбрано». <br/> Неуправляемые значения по умолчанию |
460
451
  | `disabled` | `boolean` | — | Поле выключено <br/> Деактивировано |
461
452
  | `enableFuzzySearch` | `boolean` | `true` | Включить нечёткий поиск: символы запроса должны встречаться в лейбле в том же порядке <br/> (например, `lge` найдёт `Large`). Если `false` — простой substring-match. |
462
453
  | `error` | `string` | — | Ошибка (приоритетнее `hint`; форсит `validationState=error`) |
@@ -508,10 +499,21 @@ export function Select() {
508
499
  | `size` | `"l"` \| `"m"` \| `"s"` | — | Размер |
509
500
  | `untouchableScrollbars` | `boolean` | — | Отключает возможность взаимодействовать со скролбарами мышью. |
510
501
  | `validationState` | `"default"` \| `"error"` \| `"success"` \| `"warning"` | — | Состояние валидации |
511
- | `value` | `ItemId` | — | Управляемое значение <br/> Управляемые значения |
502
+ | `value` | `ItemId` | — | Управляемое значение. Пустая строка трактуется как «значение не выбрано». <br/> Управляемые значения |
512
503
  | `virtualized` | `boolean` | — | Включить виртуализацию элементов списка. Рекомендуется при количестве элементов от 1000. |
513
504
  | `widthStrategy` | `"auto"` \| `"eq"` \| `"gte"` | `'eq' — равна ширине триггера` | Стратегия ширины дроплиста. |
514
505
 
506
+ #### Related types
507
+
508
+ **FieldLayoutPresets**
509
+
510
+ | Prop | Type | Default | Description |
511
+ |------|------|---------|-------------|
512
+ | `desktop` | `any` | — | |
513
+ | `desktopSmall` | `any` | — | |
514
+ | `mobile` | `any` | — | |
515
+ | `tablet` | `any` | — | |
516
+
515
517
  ## FieldSlider
516
518
 
517
519
  ```tsx
@@ -802,7 +804,8 @@ export function TextArea() {
802
804
  | `required` | `boolean` | — | Показать знак обязательности `*` |
803
805
  | `resizable` | `boolean` | `false` | Можно ли менять высоту мышкой за нижний угол. Игнорируется при `disabled` или `readonly`. |
804
806
  | `showClearButton` | `boolean` | `true` | Кнопка очистки (видна при value && !readonly) |
805
- | `showCopyButton` | `boolean` | `true` | Кнопка копирования (видна при value && !disabled, независимо от readonly) |
807
+ | `showCopyButton` | `boolean` | `true` | Кнопка копирования (видна при непустом value в режиме readonly, как у остальных полей) |
808
+ | `showCopyButtonInEditMode` | `boolean` | `false` | Показывать кнопку копирования и в обычном (не readonly) режиме — рядом с кнопкой очистки. <br/> Опция только для textarea: в многострочном поле копирование значения осмысленно и при вводе. |
806
809
  | `showHintIcon` | `boolean` | — | Отображение статус-иконки у подсказки (по умолчанию `true`) |
807
810
  | `size` | `"l"` \| `"m"` \| `"s"` | `m` | Размер |
808
811
  | `spellCheck` | `boolean` | — | Проверка орфографии |
@@ -52,12 +52,33 @@ exports.FieldSelect = (0, react_1.forwardRef)(function FieldSelect(props, ref) {
52
52
  // но «расхлябанный» вызов (например, Storybook-spread, где single-default остаётся
53
53
  // строкой при переключении selection) не должен ронять компонент на `.map` строки.
54
54
  multiple && Array.isArray(props.defaultValue) ? props.defaultValue : []);
55
- const singleValue = (0, react_1.useMemo)(() => (!multiple ? (props.value ?? singleLocal) : undefined), [multiple, props.value, singleLocal]);
55
+ // Controlled-режим «залипает»: как только потребитель прислал `value`, локальный стейт больше
56
+ // не читается. Иначе очистка (потребитель ставит `undefined`) откатывалась бы на устаревший
57
+ // локальный выбор и воскрешала прошлое значение.
58
+ const singleControlled = (0, react_1.useRef)(false);
59
+ const multipleControlled = (0, react_1.useRef)(false);
60
+ if (!multiple && props.value !== undefined) {
61
+ singleControlled.current = true;
62
+ }
63
+ if (multiple && Array.isArray(props.value)) {
64
+ multipleControlled.current = true;
65
+ }
66
+ const singleValue = (0, react_1.useMemo)(() => {
67
+ if (multiple) {
68
+ return undefined;
69
+ }
70
+ const raw = singleControlled.current ? props.value : singleLocal;
71
+ // `''` = «значение не выбрано»: формы отдают пустое значение строкой.
72
+ return raw === '' ? undefined : raw;
73
+ }, [multiple, props.value, singleLocal]);
56
74
  const multipleValue = (0, react_1.useMemo)(() => {
57
75
  if (!multiple) {
58
76
  return [];
59
77
  }
60
- return Array.isArray(props.value) ? props.value : multipleLocal;
78
+ if (multipleControlled.current) {
79
+ return Array.isArray(props.value) ? props.value : [];
80
+ }
81
+ return multipleLocal;
61
82
  }, [multiple, props.value, multipleLocal]);
62
83
  const effectiveValidationState = (0, react_1.useMemo)(() => (error ? field_decorator_1.VALIDATION_STATE.Error : validationState), [error, validationState]);
63
84
  const allItems = (0, react_1.useMemo)(() => [...(pinTop ?? []), ...items, ...(pinBottom ?? [])], [items, pinTop, pinBottom]);
@@ -146,9 +146,9 @@ type CommonSelectProps = FieldSelectDecoratorProps & DroplistPassthrough & {
146
146
  export type FieldSelectSingleProps = CommonSelectProps & {
147
147
  /** Режим выбора. По умолчанию `'single'`. */
148
148
  selection?: typeof SELECTION_MODE.Single;
149
- /** Управляемое значение */
149
+ /** Управляемое значение. Пустая строка трактуется как «значение не выбрано». */
150
150
  value?: ItemId;
151
- /** Неуправляемое значение по умолчанию */
151
+ /** Неуправляемое значение по умолчанию. Пустая строка трактуется как «значение не выбрано». */
152
152
  defaultValue?: ItemId;
153
153
  /** Колбек смены значения */
154
154
  onChange?(value: ItemId | undefined): void;
@@ -46,12 +46,33 @@ export const FieldSelect = forwardRef(function FieldSelect(props, ref) {
46
46
  // но «расхлябанный» вызов (например, Storybook-spread, где single-default остаётся
47
47
  // строкой при переключении selection) не должен ронять компонент на `.map` строки.
48
48
  multiple && Array.isArray(props.defaultValue) ? props.defaultValue : []);
49
- const singleValue = useMemo(() => (!multiple ? (props.value ?? singleLocal) : undefined), [multiple, props.value, singleLocal]);
49
+ // Controlled-режим «залипает»: как только потребитель прислал `value`, локальный стейт больше
50
+ // не читается. Иначе очистка (потребитель ставит `undefined`) откатывалась бы на устаревший
51
+ // локальный выбор и воскрешала прошлое значение.
52
+ const singleControlled = useRef(false);
53
+ const multipleControlled = useRef(false);
54
+ if (!multiple && props.value !== undefined) {
55
+ singleControlled.current = true;
56
+ }
57
+ if (multiple && Array.isArray(props.value)) {
58
+ multipleControlled.current = true;
59
+ }
60
+ const singleValue = useMemo(() => {
61
+ if (multiple) {
62
+ return undefined;
63
+ }
64
+ const raw = singleControlled.current ? props.value : singleLocal;
65
+ // `''` = «значение не выбрано»: формы отдают пустое значение строкой.
66
+ return raw === '' ? undefined : raw;
67
+ }, [multiple, props.value, singleLocal]);
50
68
  const multipleValue = useMemo(() => {
51
69
  if (!multiple) {
52
70
  return [];
53
71
  }
54
- return Array.isArray(props.value) ? props.value : multipleLocal;
72
+ if (multipleControlled.current) {
73
+ return Array.isArray(props.value) ? props.value : [];
74
+ }
75
+ return multipleLocal;
55
76
  }, [multiple, props.value, multipleLocal]);
56
77
  const effectiveValidationState = useMemo(() => (error ? VALIDATION_STATE.Error : validationState), [error, validationState]);
57
78
  const allItems = useMemo(() => [...(pinTop ?? []), ...items, ...(pinBottom ?? [])], [items, pinTop, pinBottom]);
@@ -146,9 +146,9 @@ type CommonSelectProps = FieldSelectDecoratorProps & DroplistPassthrough & {
146
146
  export type FieldSelectSingleProps = CommonSelectProps & {
147
147
  /** Режим выбора. По умолчанию `'single'`. */
148
148
  selection?: typeof SELECTION_MODE.Single;
149
- /** Управляемое значение */
149
+ /** Управляемое значение. Пустая строка трактуется как «значение не выбрано». */
150
150
  value?: ItemId;
151
- /** Неуправляемое значение по умолчанию */
151
+ /** Неуправляемое значение по умолчанию. Пустая строка трактуется как «значение не выбрано». */
152
152
  defaultValue?: ItemId;
153
153
  /** Колбек смены значения */
154
154
  onChange?(value: ItemId | undefined): void;