@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloud-ru/ds-fields",
3
- "version": "2.1.3",
3
+ "version": "2.2.0",
4
4
  "description": "Поля ввода с единой обёрткой label/caption/hint/error и общими осями size/validationState — текст, пароль, число, ползунок, дата, время, выбор, цвет.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -43,20 +43,20 @@
43
43
  "@cloud-ru/ds-bottom-sheet": "^1.1.1",
44
44
  "@cloud-ru/ds-button": "^1.0.2",
45
45
  "@cloud-ru/ds-calendar": "^1.0.13",
46
- "@cloud-ru/ds-color-picker": "^1.0.5",
46
+ "@cloud-ru/ds-color-picker": "^1.0.6",
47
47
  "@cloud-ru/ds-divider": "^1.0.1",
48
- "@cloud-ru/ds-field-decorator": "^1.0.5",
49
48
  "@cloud-ru/ds-dropdown": "^1.0.8",
50
- "@cloud-ru/ds-icons": "^1.1.0",
49
+ "@cloud-ru/ds-field-decorator": "^1.0.5",
51
50
  "@cloud-ru/ds-input-private": "^1.0.4",
51
+ "@cloud-ru/ds-icons": "^1.1.0",
52
52
  "@cloud-ru/ds-list": "^2.1.2",
53
53
  "@cloud-ru/ds-loader": "^1.0.1",
54
54
  "@cloud-ru/ds-materials": "^1.0.1",
55
55
  "@cloud-ru/ds-scroll": "^1.0.2",
56
56
  "@cloud-ru/ds-skeleton": "^1.0.1",
57
- "@cloud-ru/ds-tag": "^1.0.9",
57
+ "@cloud-ru/ds-slider": "^1.0.4",
58
+ "@cloud-ru/ds-tag": "^1.1.0",
58
59
  "@cloud-ru/ds-tooltip": "^1.0.4",
59
- "@cloud-ru/ds-utils": "^1.1.0",
60
- "@cloud-ru/ds-slider": "^1.0.4"
60
+ "@cloud-ru/ds-utils": "^1.1.0"
61
61
  }
62
62
  }
@@ -1,5 +1,6 @@
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
 
@@ -9,6 +10,14 @@ import { SELECTION_MODE } from './constants';
9
10
  /** Режим выбора FieldSelect: одно значение или несколько. */
10
11
  export type Selection = ValueOf<typeof SELECTION_MODE>;
11
12
 
13
+ // ItemProps — union с рекурсивными группами, поэтому раздаём поле по всем веткам.
14
+ type WithTagAppearance<T> = T extends { items: ItemProps[] }
15
+ ? Omit<T, 'items'> & { appearance?: TagAppearance; items: WithTagAppearance<ItemProps>[] }
16
+ : T & { appearance?: TagAppearance };
17
+
18
+ /** Айтем дроплиста: айтем `@cloud-ru/ds-list` плюс `appearance` — цвет тега выбранного значения при `selection='multiple'`. */
19
+ export type FieldSelectItem = WithTagAppearance<ItemProps>;
20
+
12
21
  type FieldSelectDecoratorProps = Omit<FieldDecoratorProps, 'children' | 'validationState' | 'size'>;
13
22
 
14
23
  type DroplistPassthrough = Pick<
@@ -48,11 +57,11 @@ type CommonSelectProps = FieldSelectDecoratorProps &
48
57
  /** CSS-класс оболочки поля */
49
58
  fieldClassName?: string;
50
59
  /** Список айтемов дроплиста (формат `@cloud-ru/ds-list`) */
51
- items: ItemProps[];
60
+ items: FieldSelectItem[];
52
61
  /** Пресет-айтемы сверху (формат `@cloud-ru/ds-list`) */
53
- pinTop?: ItemProps[];
62
+ pinTop?: FieldSelectItem[];
54
63
  /** Пресет-айтемы снизу (формат `@cloud-ru/ds-list`) */
55
- pinBottom?: ItemProps[];
64
+ pinBottom?: FieldSelectItem[];
56
65
  /** Placeholder в триггере, когда нет выбранного значения */
57
66
  placeholder?: string;
58
67
  /** Иконка перед текстом */
@@ -1,13 +1,13 @@
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
4
 
5
5
  import { SELECTION_MODE } from './constants';
6
- import { FieldSelectMultipleProps, FieldSelectProps } from './types';
6
+ import { FieldSelectItem, FieldSelectMultipleProps, FieldSelectProps } from './types';
7
7
 
8
8
  export const TAG_SIZE_MAP: Record<Size, TagSize> = { s: 'xs', m: 'xs', l: 's' };
9
9
 
10
- export type WithIdContent = { id?: ItemId; content?: unknown; disabled?: boolean; appearance?: unknown };
10
+ export type WithIdContent = { id?: ItemId; content?: unknown; disabled?: boolean; appearance?: TagAppearance };
11
11
 
12
12
  export function extractLabel(item: WithIdContent): string {
13
13
  const { content, id } = item;
@@ -48,29 +48,27 @@ export function extractSearchText(item: WithIdContent): string {
48
48
  return parts.join(' ');
49
49
  }
50
50
 
51
- // Цвет чипа выбранного значения (multiple) — паритет с легаси `option.appearance`.
52
- // Берётся с самого item'а (additive: потребитель задаёт `appearance` на элементе items).
51
+ // Цвет тега выбранного значения (multiple) — паритет с легаси `option.appearance`.
52
+ // Проверка типа нужна и при типизированном поле: items часто приходят из ответа бэкенда.
53
53
  export function extractAppearance(item?: WithIdContent): TagAppearance | undefined {
54
- const a = item?.appearance;
55
-
56
- return typeof a === 'string' ? (a as TagAppearance) : undefined;
54
+ return typeof item?.appearance === 'string' ? item.appearance : undefined;
57
55
  }
58
56
 
59
- export function flatten(items: ItemProps[]): WithIdContent[] {
57
+ export function flatten(items: FieldSelectItem[]): WithIdContent[] {
60
58
  const out: WithIdContent[] = [];
61
59
 
62
60
  for (const item of items) {
63
61
  out.push(item as WithIdContent);
64
62
 
65
63
  if ('items' in item && Array.isArray(item.items)) {
66
- out.push(...flatten(item.items as ItemProps[]));
64
+ out.push(...flatten(item.items as FieldSelectItem[]));
67
65
  }
68
66
  }
69
67
 
70
68
  return out;
71
69
  }
72
70
 
73
- export function findItem(items: ItemProps[], id: ItemId): WithIdContent | undefined {
71
+ export function findItem(items: FieldSelectItem[], id: ItemId): WithIdContent | undefined {
74
72
  for (const item of flatten(items)) {
75
73
  if (item.id === id) {
76
74
  return item;
@@ -102,25 +100,25 @@ export function isFuzzyMatch(haystack: string, needle: string): boolean {
102
100
  return false;
103
101
  }
104
102
 
105
- export function filterItems(items: ItemProps[], query: string, fuzzy: boolean): ItemProps[] {
103
+ export function filterItems(items: FieldSelectItem[], query: string, fuzzy: boolean): FieldSelectItem[] {
106
104
  if (!query) {
107
105
  return items;
108
106
  }
109
107
 
110
108
  const q = query.toLowerCase();
111
109
 
112
- const matches = (item: ItemProps): boolean => {
110
+ const matches = (item: FieldSelectItem): boolean => {
113
111
  const text = extractSearchText(item as WithIdContent).toLowerCase();
114
112
 
115
113
  return fuzzy ? isFuzzyMatch(text, q) : text.includes(q);
116
114
  };
117
115
 
118
- const walk = (list: ItemProps[]): ItemProps[] =>
116
+ const walk = (list: FieldSelectItem[]): FieldSelectItem[] =>
119
117
  list.flatMap(item => {
120
118
  if ('items' in item && Array.isArray(item.items)) {
121
- const filteredChildren = walk(item.items as ItemProps[]);
119
+ const filteredChildren = walk(item.items as FieldSelectItem[]);
122
120
 
123
- return filteredChildren.length > 0 ? [{ ...item, items: filteredChildren } as ItemProps] : [];
121
+ return filteredChildren.length > 0 ? [{ ...item, items: filteredChildren } as FieldSelectItem] : [];
124
122
  }
125
123
 
126
124
  return matches(item) ? [item] : [];