@cloud-ru/ds-fields 2.1.3 → 2.2.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/CHANGELOG.md CHANGED
@@ -3,6 +3,12 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # 2.2.0 (2026-09-03)
7
+
8
+ ### Features
9
+
10
+ - **FF-8959:** type the tag appearance on field select items ([9e053d8](https://github.com/cloud-ru-tech/snack-v2/commit/9e053d847a629bfca81fc4c6eee9c51b507bb529))
11
+
6
12
  ## 2.1.3 (2026-09-02)
7
13
 
8
14
  ### Bug Fixes
package/README.md CHANGED
@@ -460,7 +460,7 @@ export function Select() {
460
460
  | `iconBefore` | `ReactNode` | — | Иконка перед текстом |
461
461
  | `id` | `string` | — | HTML-атрибут `id` для input (и `for` у label) |
462
462
  | `innerRef` | `Ref<HTMLDivElement>` | — | Ref на корневой DOM-элемент |
463
- | `items` | `BaseItemWithoutNonGroup` \| `CommonGroupItem` \| `ItemProps` \| `ScrollProps` | — | Список айтемов дроплиста (формат `@cloud-ru/ds-list`) |
463
+ | `items` | `BaseItemWithoutNonGroup` \| `CommonGroupItem` \| `FieldSelectItem` \| `ScrollProps` | — | Список айтемов дроплиста (формат `@cloud-ru/ds-list`) |
464
464
  | `label` | `string` | — | Заголовок |
465
465
  | `labelFor` | `string` | — | HTML-атрибут `for` для `<label>` |
466
466
  | `labelTooltip` | `QuestionTooltipProps` | — | Подсказка (question-tooltip) у заголовка |
@@ -478,8 +478,8 @@ export function Select() {
478
478
  | `onKeyDown` | `((event: KeyboardEvent<HTMLInputElement>) => void)` | — | Колбек нажатия клавиши на input (вызывается после внутренней обработки навигации) |
479
479
  | `onOpenChange` | `DroplistProps` | — | Колбек смены открытия |
480
480
  | `open` | `DroplistProps` | — | Управляемое открытие дроплиста |
481
- | `pinBottom` | `BaseItemWithoutNonGroup` \| `CommonGroupItem` \| `ItemProps` \| `ScrollProps` | — | Пресет-айтемы снизу (формат `@cloud-ru/ds-list`) |
482
- | `pinTop` | `BaseItemWithoutNonGroup` \| `CommonGroupItem` \| `ItemProps` \| `ScrollProps` | — | Пресет-айтемы сверху (формат `@cloud-ru/ds-list`) |
481
+ | `pinBottom` | `BaseItemWithoutNonGroup` \| `CommonGroupItem` \| `FieldSelectItem` \| `ScrollProps` | — | Пресет-айтемы снизу (формат `@cloud-ru/ds-list`) |
482
+ | `pinTop` | `BaseItemWithoutNonGroup` \| `CommonGroupItem` \| `FieldSelectItem` \| `ScrollProps` | — | Пресет-айтемы сверху (формат `@cloud-ru/ds-list`) |
483
483
  | `placeholder` | `string` | — | Placeholder в триггере, когда нет выбранного значения |
484
484
  | `placement` | `"bottom"` \| `"bottom-end"` \| `"bottom-start"` \| `"left"` \| `"left-end"` \| `"left-start"` \| `"right"` \| `"right-end"` \| `"right-start"` \| `"top"` \| `"top-end"` \| `"top-start"` | — | Placement дроплиста |
485
485
  | `postfix` | `ReactNode` | — | Постфикс — текст или нода после значения (Figma `postfix`) |
@@ -514,6 +514,8 @@ export function Select() {
514
514
  | `mobile` | `any` | — | |
515
515
  | `tablet` | `any` | — | |
516
516
 
517
+ - `FieldSelectItem` = `({ 'data-test-id'?: string; } & AriaAttributes & { beforeContent?: ReactNode; afterContent?: ReactNode; content?: ItemContentProps | ReactNode; onClick?(e: MouseEvent<HTMLElement>): void; onMouseDown?(e: MouseEvent<HTMLElement>): void; onKeyDown?(e: KeyboardEvent<HTMLElement>): void; onFocus?(e: FocusEvent<HTMLElement>): void; onBlur?(e: FocusEvent<HTMLElement>): void; id?: ItemId; disabled?: boolean; hidden?: boolean; itemRef?: RefObject<HTMLElement>; className?: string; inactive?: boolean; switch?: boolean; showSwitchIcon?: boolean; itemWrapRender?(item: ReactNode): ReactNode; checked?: boolean; } & { appearance?: TagAppearance; }) | (Omit<GroupItem, "items"> & { appearance?: TagAppearance; items: WithTagAppearance<ItemProps>[]; }) | (Omit<GroupSelectItem, "items"> & { appearance?: TagAppearance; items: WithTagAppearance<ItemProps>[]; }) | (Omit<NextListItem, "items"> & { appearance?: TagAppearance; items: WithTagAppearance<ItemProps>[]; }) | (Omit<AccordionItem, "items"> & { appearance?: TagAppearance; items: WithTagAppearance<ItemProps>[]; })`
518
+
517
519
  ## FieldSlider
518
520
 
519
521
  ```tsx
@@ -1,11 +1,22 @@
1
1
  import { FieldDecoratorProps, ValidationState } from '@cloud-ru/ds-field-decorator';
2
2
  import { DroplistProps, ItemId, ItemProps } from '@cloud-ru/ds-list';
3
+ import { Appearance as TagAppearance } from '@cloud-ru/ds-tag';
3
4
  import { ValueOf } from '@cloud-ru/ds-utils';
4
5
  import { FocusEvent, KeyboardEvent, ReactNode } from 'react';
5
6
  import { FieldLayoutPresets } from '../../hooks';
6
7
  import { SELECTION_MODE } from './constants';
7
8
  /** Режим выбора FieldSelect: одно значение или несколько. */
8
9
  export type Selection = ValueOf<typeof SELECTION_MODE>;
10
+ type WithTagAppearance<T> = T extends {
11
+ items: ItemProps[];
12
+ } ? Omit<T, 'items'> & {
13
+ appearance?: TagAppearance;
14
+ items: WithTagAppearance<ItemProps>[];
15
+ } : T & {
16
+ appearance?: TagAppearance;
17
+ };
18
+ /** Айтем дроплиста: айтем `@cloud-ru/ds-list` плюс `appearance` — цвет тега выбранного значения при `selection='multiple'`. */
19
+ export type FieldSelectItem = WithTagAppearance<ItemProps>;
9
20
  type FieldSelectDecoratorProps = Omit<FieldDecoratorProps, 'children' | 'validationState' | 'size'>;
10
21
  type DroplistPassthrough = Pick<DroplistProps, 'footer' | 'footerActiveElementsRefs' | 'loading' | 'noDataState' | 'noResultsState' | 'errorDataState' | 'virtualized' | 'scrollToSelectedItem' | 'limitedScrollHeight' | 'untouchableScrollbars' | 'closeOnPopstate'> & {
11
22
  /**
@@ -29,11 +40,11 @@ type CommonSelectProps = FieldSelectDecoratorProps & DroplistPassthrough & {
29
40
  /** CSS-класс оболочки поля */
30
41
  fieldClassName?: string;
31
42
  /** Список айтемов дроплиста (формат `@cloud-ru/ds-list`) */
32
- items: ItemProps[];
43
+ items: FieldSelectItem[];
33
44
  /** Пресет-айтемы сверху (формат `@cloud-ru/ds-list`) */
34
- pinTop?: ItemProps[];
45
+ pinTop?: FieldSelectItem[];
35
46
  /** Пресет-айтемы снизу (формат `@cloud-ru/ds-list`) */
36
- pinBottom?: ItemProps[];
47
+ pinBottom?: FieldSelectItem[];
37
48
  /** Placeholder в триггере, когда нет выбранного значения */
38
49
  placeholder?: string;
39
50
  /** Иконка перед текстом */
@@ -1,19 +1,19 @@
1
1
  import { Size } from '@cloud-ru/ds-field-decorator';
2
- import { ItemId, ItemProps } from '@cloud-ru/ds-list';
2
+ import { ItemId } from '@cloud-ru/ds-list';
3
3
  import { Appearance as TagAppearance, Size as TagSize } from '@cloud-ru/ds-tag';
4
- import { FieldSelectMultipleProps, FieldSelectProps } from './types';
4
+ import { FieldSelectItem, FieldSelectMultipleProps, FieldSelectProps } from './types';
5
5
  export declare const TAG_SIZE_MAP: Record<Size, TagSize>;
6
6
  export type WithIdContent = {
7
7
  id?: ItemId;
8
8
  content?: unknown;
9
9
  disabled?: boolean;
10
- appearance?: unknown;
10
+ appearance?: TagAppearance;
11
11
  };
12
12
  export declare function extractLabel(item: WithIdContent): string;
13
13
  export declare function extractSearchText(item: WithIdContent): string;
14
14
  export declare function extractAppearance(item?: WithIdContent): TagAppearance | undefined;
15
- export declare function flatten(items: ItemProps[]): WithIdContent[];
16
- export declare function findItem(items: ItemProps[], id: ItemId): WithIdContent | undefined;
15
+ export declare function flatten(items: FieldSelectItem[]): WithIdContent[];
16
+ export declare function findItem(items: FieldSelectItem[], id: ItemId): WithIdContent | undefined;
17
17
  export declare function isFuzzyMatch(haystack: string, needle: string): boolean;
18
- export declare function filterItems(items: ItemProps[], query: string, fuzzy: boolean): ItemProps[];
18
+ export declare function filterItems(items: FieldSelectItem[], query: string, fuzzy: boolean): FieldSelectItem[];
19
19
  export declare function isMultiple(props: FieldSelectProps): props is FieldSelectMultipleProps;
@@ -40,11 +40,10 @@ function extractSearchText(item) {
40
40
  }
41
41
  return parts.join(' ');
42
42
  }
43
- // Цвет чипа выбранного значения (multiple) — паритет с легаси `option.appearance`.
44
- // Берётся с самого item'а (additive: потребитель задаёт `appearance` на элементе items).
43
+ // Цвет тега выбранного значения (multiple) — паритет с легаси `option.appearance`.
44
+ // Проверка типа нужна и при типизированном поле: items часто приходят из ответа бэкенда.
45
45
  function extractAppearance(item) {
46
- const a = item?.appearance;
47
- return typeof a === 'string' ? a : undefined;
46
+ return typeof item?.appearance === 'string' ? item.appearance : undefined;
48
47
  }
49
48
  function flatten(items) {
50
49
  const out = [];
@@ -1,11 +1,22 @@
1
1
  import { FieldDecoratorProps, ValidationState } from '@cloud-ru/ds-field-decorator';
2
2
  import { DroplistProps, ItemId, ItemProps } from '@cloud-ru/ds-list';
3
+ import { Appearance as TagAppearance } from '@cloud-ru/ds-tag';
3
4
  import { ValueOf } from '@cloud-ru/ds-utils';
4
5
  import { FocusEvent, KeyboardEvent, ReactNode } from 'react';
5
6
  import { FieldLayoutPresets } from '../../hooks/index.js';
6
7
  import { SELECTION_MODE } from './constants.js';
7
8
  /** Режим выбора FieldSelect: одно значение или несколько. */
8
9
  export type Selection = ValueOf<typeof SELECTION_MODE>;
10
+ type WithTagAppearance<T> = T extends {
11
+ items: ItemProps[];
12
+ } ? Omit<T, 'items'> & {
13
+ appearance?: TagAppearance;
14
+ items: WithTagAppearance<ItemProps>[];
15
+ } : T & {
16
+ appearance?: TagAppearance;
17
+ };
18
+ /** Айтем дроплиста: айтем `@cloud-ru/ds-list` плюс `appearance` — цвет тега выбранного значения при `selection='multiple'`. */
19
+ export type FieldSelectItem = WithTagAppearance<ItemProps>;
9
20
  type FieldSelectDecoratorProps = Omit<FieldDecoratorProps, 'children' | 'validationState' | 'size'>;
10
21
  type DroplistPassthrough = Pick<DroplistProps, 'footer' | 'footerActiveElementsRefs' | 'loading' | 'noDataState' | 'noResultsState' | 'errorDataState' | 'virtualized' | 'scrollToSelectedItem' | 'limitedScrollHeight' | 'untouchableScrollbars' | 'closeOnPopstate'> & {
11
22
  /**
@@ -29,11 +40,11 @@ type CommonSelectProps = FieldSelectDecoratorProps & DroplistPassthrough & {
29
40
  /** CSS-класс оболочки поля */
30
41
  fieldClassName?: string;
31
42
  /** Список айтемов дроплиста (формат `@cloud-ru/ds-list`) */
32
- items: ItemProps[];
43
+ items: FieldSelectItem[];
33
44
  /** Пресет-айтемы сверху (формат `@cloud-ru/ds-list`) */
34
- pinTop?: ItemProps[];
45
+ pinTop?: FieldSelectItem[];
35
46
  /** Пресет-айтемы снизу (формат `@cloud-ru/ds-list`) */
36
- pinBottom?: ItemProps[];
47
+ pinBottom?: FieldSelectItem[];
37
48
  /** Placeholder в триггере, когда нет выбранного значения */
38
49
  placeholder?: string;
39
50
  /** Иконка перед текстом */
@@ -1,19 +1,19 @@
1
1
  import { Size } from '@cloud-ru/ds-field-decorator';
2
- import { ItemId, ItemProps } from '@cloud-ru/ds-list';
2
+ import { ItemId } from '@cloud-ru/ds-list';
3
3
  import { Appearance as TagAppearance, Size as TagSize } from '@cloud-ru/ds-tag';
4
- import { FieldSelectMultipleProps, FieldSelectProps } from './types.js';
4
+ import { FieldSelectItem, FieldSelectMultipleProps, FieldSelectProps } from './types.js';
5
5
  export declare const TAG_SIZE_MAP: Record<Size, TagSize>;
6
6
  export type WithIdContent = {
7
7
  id?: ItemId;
8
8
  content?: unknown;
9
9
  disabled?: boolean;
10
- appearance?: unknown;
10
+ appearance?: TagAppearance;
11
11
  };
12
12
  export declare function extractLabel(item: WithIdContent): string;
13
13
  export declare function extractSearchText(item: WithIdContent): string;
14
14
  export declare function extractAppearance(item?: WithIdContent): TagAppearance | undefined;
15
- export declare function flatten(items: ItemProps[]): WithIdContent[];
16
- export declare function findItem(items: ItemProps[], id: ItemId): WithIdContent | undefined;
15
+ export declare function flatten(items: FieldSelectItem[]): WithIdContent[];
16
+ export declare function findItem(items: FieldSelectItem[], id: ItemId): WithIdContent | undefined;
17
17
  export declare function isFuzzyMatch(haystack: string, needle: string): boolean;
18
- export declare function filterItems(items: ItemProps[], query: string, fuzzy: boolean): ItemProps[];
18
+ export declare function filterItems(items: FieldSelectItem[], query: string, fuzzy: boolean): FieldSelectItem[];
19
19
  export declare function isMultiple(props: FieldSelectProps): props is FieldSelectMultipleProps;
@@ -29,11 +29,10 @@ export function extractSearchText(item) {
29
29
  }
30
30
  return parts.join(' ');
31
31
  }
32
- // Цвет чипа выбранного значения (multiple) — паритет с легаси `option.appearance`.
33
- // Берётся с самого item'а (additive: потребитель задаёт `appearance` на элементе items).
32
+ // Цвет тега выбранного значения (multiple) — паритет с легаси `option.appearance`.
33
+ // Проверка типа нужна и при типизированном поле: items часто приходят из ответа бэкенда.
34
34
  export function extractAppearance(item) {
35
- const a = item?.appearance;
36
- return typeof a === 'string' ? a : undefined;
35
+ return typeof item?.appearance === 'string' ? item.appearance : undefined;
37
36
  }
38
37
  export function flatten(items) {
39
38
  const out = [];