@cloud-ru/ds-list 2.0.3-preview-6a131ef8.0 → 2.0.3

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-list",
3
- "version": "2.0.3-preview-6a131ef8.0",
3
+ "version": "2.0.3",
4
4
  "description": "Списочный UI — компонент List для плоских/вложенных списков с выбором, группами и поиском и Droplist — тот же список в поповере.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -48,24 +48,24 @@
48
48
  "@tanstack/react-virtual": "^3.14.8",
49
49
  "classnames": "^2.5.1",
50
50
  "merge-refs": "^2.0.0",
51
- "@cloud-ru/ds-adaptive": "1.0.1",
52
- "@cloud-ru/ds-button": "1.0.1",
53
- "@cloud-ru/ds-divider": "1.0.1",
54
- "@cloud-ru/ds-bottom-sheet": "1.0.3",
55
- "@cloud-ru/ds-icons": "1.0.2",
56
- "@cloud-ru/ds-dropdown": "1.0.4",
57
- "@cloud-ru/ds-info-block": "1.0.1",
58
- "@cloud-ru/ds-loader": "1.0.1",
59
- "@cloud-ru/ds-materials": "1.0.1",
60
- "@cloud-ru/ds-drag-and-drop": "1.1.1",
61
- "@cloud-ru/ds-scroll": "1.0.2",
62
- "@cloud-ru/ds-portal-context": "1.0.1",
63
- "@cloud-ru/ds-truncate-string": "1.0.3",
64
- "@cloud-ru/ds-search-private": "1.0.3",
65
- "@cloud-ru/ds-toggles": "1.1.2",
66
- "@cloud-ru/ds-utils": "1.1.0"
51
+ "@cloud-ru/ds-adaptive": "^1.0.1",
52
+ "@cloud-ru/ds-bottom-sheet": "^1.0.4",
53
+ "@cloud-ru/ds-drag-and-drop": "^1.1.1",
54
+ "@cloud-ru/ds-button": "^1.0.1",
55
+ "@cloud-ru/ds-divider": "^1.0.1",
56
+ "@cloud-ru/ds-icons": "^1.0.2",
57
+ "@cloud-ru/ds-info-block": "^1.0.1",
58
+ "@cloud-ru/ds-materials": "^1.0.1",
59
+ "@cloud-ru/ds-dropdown": "^1.0.5",
60
+ "@cloud-ru/ds-search-private": "^1.0.4",
61
+ "@cloud-ru/ds-truncate-string": "^1.0.3",
62
+ "@cloud-ru/ds-scroll": "^1.0.2",
63
+ "@cloud-ru/ds-utils": "^1.1.0",
64
+ "@cloud-ru/ds-toggles": "^1.1.2",
65
+ "@cloud-ru/ds-portal-context": "^1.0.1",
66
+ "@cloud-ru/ds-loader": "^1.0.1"
67
67
  },
68
68
  "peerDependencies": {
69
- "@cloud-ru/ds-locale": "1.0.0"
69
+ "@cloud-ru/ds-locale": "^1.0.0"
70
70
  }
71
71
  }
@@ -9,7 +9,7 @@ import { List, ReorderableList } from '../../components/Lists/List';
9
9
  import { BaseDroplistProps, MobileDroplistProps } from '../../components/Lists/types';
10
10
  import { TEST_IDS } from '../../constants';
11
11
  import styles from './MobileDroplist.module.scss';
12
- import { buildLevelItems, nextListOption, resolveItemByPath } from './utils';
12
+ import { buildLevelItems, nextListOption } from './utils';
13
13
 
14
14
  /**
15
15
  * Mobile-вариант `Droplist`: рендерит `List` (в переданном `size`) внутри `BottomSheet` из `@cloud-ru/ds-bottom-sheet`.
@@ -47,10 +47,8 @@ export function MobileDroplist({
47
47
  const scrollRef = useRef<HTMLDivElement>(null);
48
48
  const [open = false, setIsOpen] = useValueControl({ value: openProp, onChange: onOpenChange });
49
49
 
50
- // Стек заходов во вложенные next-list'ы: индексный путь от корня на каждый уровень.
51
- // Пустой корневой уровень. Хранится путь, а не сам айтем: айтемы пересобираются на каждом
52
- // рендере, и сохранённая копия оставила бы открытый sheet в устаревшем состоянии.
53
- const [path, setPath] = useState<number[][]>([]);
50
+ // Стек заходов во вложенные next-list'ы. Пустой корневой уровень.
51
+ const [path, setPath] = useState<NextListItem[]>([]);
54
52
 
55
53
  const handleClose = () => {
56
54
  setIsOpen(false);
@@ -137,11 +135,7 @@ export function MobileDroplist({
137
135
  // Каскад sheet'ов: корень + по одному sheet'у на каждый заход в next-list. Каждый накладывается
138
136
  // поверх предыдущего (свой backdrop), а не заменяет его содержимое. Назад — закрывает верхний.
139
137
  const sheets = [{ source: items, title: label, item: undefined as NextListItem | undefined }].concat(
140
- path.map(indexPath => {
141
- const item = resolveItemByPath(items as Item[], indexPath);
142
-
143
- return { source: (item?.items ?? []) as typeof items, title: nextListOption(item), item };
144
- }),
138
+ path.map(item => ({ source: item.items, title: nextListOption(item), item })),
145
139
  );
146
140
 
147
141
  return (
@@ -157,8 +151,7 @@ export function MobileDroplist({
157
151
  ? undefined
158
152
  : buildLevelItems(
159
153
  source as Item[],
160
- relativePath =>
161
- setPath(prev => [...prev.slice(0, levelIndex), [...(prev[levelIndex - 1] ?? []), ...relativePath]]),
154
+ next => setPath(prev => [...prev.slice(0, levelIndex), next]),
162
155
  handleClose,
163
156
  closeOnClick,
164
157
  );
@@ -4,32 +4,9 @@ import { MouseEvent } from 'react';
4
4
  import { BaseItemProps, Item, NextListItem } from '../../components/Items';
5
5
  import { ITEM_TYPE } from '../../constants';
6
6
 
7
- /**
8
- * Айтем уровня по индексному пути от корня. Путь разрешается на каждом рендере по актуальным
9
- * `items`: иначе открытый sheet сохранял бы копию айтема, полученную при заходе во вложенный
10
- * список, и не отражал бы её последующие изменения (например, смену выбранной строки).
11
- */
12
- export function resolveItemByPath(items: Item[], indexPath: number[]): NextListItem | undefined {
13
- let source: Item[] | undefined = items;
14
- let node: Item | undefined;
15
-
16
- for (const index of indexPath) {
17
- node = source?.[index];
18
-
19
- if (!node || typeof node !== 'object' || !('items' in node)) {
20
- source = undefined;
21
- continue;
22
- }
23
-
24
- source = node.items as Item[];
25
- }
26
-
27
- return node as NextListItem | undefined;
28
- }
29
-
30
7
  /** Текст `label` next-list-айтема — заголовок шапки sheet'а при заходе во вложенный список. */
31
- export function nextListOption(item?: NextListItem): string | undefined {
32
- const content = item?.content;
8
+ export function nextListOption(item: NextListItem): string | undefined {
9
+ const { content } = item;
33
10
  if (content && typeof content === 'object' && 'label' in content) {
34
11
  return String((content as { label: string | number }).label);
35
12
  }
@@ -40,20 +17,16 @@ export function nextListOption(item?: NextListItem): string | undefined {
40
17
  * Готовит айтемы текущего уровня sheet'а:
41
18
  * - `next-list` превращается в базовый айтем с шевроном `>`, клик по которому уводит на уровень вложенного
42
19
  * списка (drill-down), а не открывает второй BottomSheet (десктопный nested-popover на mobile неприменим);
43
- * позиция айтема передаётся в `onDrill` индексным путём — уровень разрешается по актуальным `items`;
44
20
  * - `group` / `group-select` / `collapse` рекурсивно обрабатываются (внутри них тоже может быть `next-list`);
45
21
  * - базовые айтемы (action-меню без `selection`) при `closeOnClick` закрывают sheet по клику.
46
22
  */
47
23
  export function buildLevelItems(
48
24
  items: Item[],
49
- onDrill: (indexPath: number[]) => void,
25
+ onDrill: (item: NextListItem) => void,
50
26
  onClose: () => void,
51
27
  closeOnClick: boolean,
52
- basePath: number[] = [],
53
28
  ): Item[] {
54
- return items.map((item, index) => {
55
- const indexPath = [...basePath, index];
56
-
29
+ return items.map(item => {
57
30
  if (item && typeof item === 'object' && 'type' in item) {
58
31
  if (item.type === ITEM_TYPE.NextList) {
59
32
  const next = item;
@@ -76,13 +49,13 @@ export function buildLevelItems(
76
49
  afterContent: <ChevronRightSVG />,
77
50
  onClick: (event: MouseEvent<HTMLElement>) => {
78
51
  next.onClick?.(event);
79
- onDrill(indexPath);
52
+ onDrill(next);
80
53
  },
81
54
  };
82
55
  }
83
56
 
84
57
  if (item.type === ITEM_TYPE.Group || item.type === ITEM_TYPE.GroupSelect || item.type === ITEM_TYPE.Collapse) {
85
- return { ...item, items: buildLevelItems(item.items, onDrill, onClose, closeOnClick, indexPath) };
58
+ return { ...item, items: buildLevelItems(item.items, onDrill, onClose, closeOnClick) };
86
59
  }
87
60
  }
88
61