@cloud-ru/ds-list 2.0.4-preview-5926424e.0 → 2.0.4

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.4-preview-5926424e.0",
3
+ "version": "2.0.4",
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-bottom-sheet": "1.0.5-preview-5926424e.0",
53
- "@cloud-ru/ds-divider": "1.0.1",
54
- "@cloud-ru/ds-drag-and-drop": "1.1.1",
55
- "@cloud-ru/ds-dropdown": "1.0.6-preview-5926424e.0",
56
- "@cloud-ru/ds-button": "1.0.1",
57
- "@cloud-ru/ds-info-block": "1.0.1",
58
- "@cloud-ru/ds-icons": "1.0.2",
59
- "@cloud-ru/ds-loader": "1.0.1",
60
- "@cloud-ru/ds-portal-context": "1.0.1",
61
- "@cloud-ru/ds-materials": "1.0.1",
62
- "@cloud-ru/ds-scroll": "1.0.2",
63
- "@cloud-ru/ds-search-private": "1.0.4",
64
- "@cloud-ru/ds-toggles": "1.1.2",
65
- "@cloud-ru/ds-truncate-string": "1.0.3",
66
- "@cloud-ru/ds-utils": "1.1.0"
51
+ "@cloud-ru/ds-adaptive": "^1.0.1",
52
+ "@cloud-ru/ds-bottom-sheet": "^1.0.5",
53
+ "@cloud-ru/ds-divider": "^1.0.1",
54
+ "@cloud-ru/ds-button": "^1.0.1",
55
+ "@cloud-ru/ds-drag-and-drop": "^1.1.1",
56
+ "@cloud-ru/ds-dropdown": "^1.0.6",
57
+ "@cloud-ru/ds-icons": "^1.1.0",
58
+ "@cloud-ru/ds-info-block": "^1.0.1",
59
+ "@cloud-ru/ds-loader": "^1.0.1",
60
+ "@cloud-ru/ds-materials": "^1.0.1",
61
+ "@cloud-ru/ds-portal-context": "^1.0.1",
62
+ "@cloud-ru/ds-scroll": "^1.0.2",
63
+ "@cloud-ru/ds-search-private": "^1.0.5",
64
+ "@cloud-ru/ds-toggles": "^1.1.3",
65
+ "@cloud-ru/ds-truncate-string": "^1.0.4",
66
+ "@cloud-ru/ds-utils": "^1.1.0"
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
  }
@@ -1,5 +1,4 @@
1
1
  import { DragEndEvent } from '@dnd-kit/core';
2
- import { BottomSheetProps } from '@cloud-ru/ds-bottom-sheet';
3
2
  import { DropdownProps } from '@cloud-ru/ds-dropdown';
4
3
  import { WithSupportProps } from '@cloud-ru/ds-utils';
5
4
  import { FocusEvent, KeyboardEvent, ReactNode, RefObject } from 'react';
@@ -183,7 +182,7 @@ type DroplistMobileSlots = {
183
182
  * (список size `l` в `BottomSheet`), иначе — `DesktopDroplist` (анкорный popover). Mobile-слоты
184
183
  * применяются только на mobile.
185
184
  */
186
- export type DroplistProps = BaseDroplistProps & DroplistMobileSlots & Pick<BottomSheetProps, 'snapPoints'>;
185
+ export type DroplistProps = BaseDroplistProps & DroplistMobileSlots;
187
186
 
188
187
  /**
189
188
  * Props адаптивного `ReorderableDroplist`: props `ReorderableList` + popover/mobile-обвязка
@@ -202,7 +201,6 @@ export type MobileDroplistProps = Omit<
202
201
  DesktopDroplistProps,
203
202
  'trigger' | 'placement' | 'widthStrategy' | 'triggerElemRef' | 'listRef' | 'triggerClassName'
204
203
  > &
205
- Pick<BottomSheetProps, 'snapPoints'> &
206
204
  DroplistMobileSlots;
207
205
 
208
206
  export type ListPrivateProps = Omit<
@@ -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`.
@@ -35,7 +35,6 @@ export function MobileDroplist({
35
35
  container,
36
36
  closeOnPopstate,
37
37
  size,
38
- snapPoints,
39
38
  // `footer` уводим в sticky-футер `BottomSheet` (кнопки действия прилипают к низу sheet'а, а не
40
39
  // скроллятся вместе со списком). `header` + `headerDivider` / `footerDivider` НЕ деструктурируем —
41
40
  // они уходят в `...rest` на корневой `List` (он рендерит шапку и её divider).
@@ -48,10 +47,8 @@ export function MobileDroplist({
48
47
  const scrollRef = useRef<HTMLDivElement>(null);
49
48
  const [open = false, setIsOpen] = useValueControl({ value: openProp, onChange: onOpenChange });
50
49
 
51
- // Стек заходов во вложенные next-list'ы: индексный путь от корня на каждый уровень.
52
- // Пустой корневой уровень. Хранится путь, а не сам айтем: айтемы пересобираются на каждом
53
- // рендере, и сохранённая копия оставила бы открытый sheet в устаревшем состоянии.
54
- const [path, setPath] = useState<number[][]>([]);
50
+ // Стек заходов во вложенные next-list'ы. Пустой корневой уровень.
51
+ const [path, setPath] = useState<NextListItem[]>([]);
55
52
 
56
53
  const handleClose = () => {
57
54
  setIsOpen(false);
@@ -138,11 +135,7 @@ export function MobileDroplist({
138
135
  // Каскад sheet'ов: корень + по одному sheet'у на каждый заход в next-list. Каждый накладывается
139
136
  // поверх предыдущего (свой backdrop), а не заменяет его содержимое. Назад — закрывает верхний.
140
137
  const sheets = [{ source: items, title: label, item: undefined as NextListItem | undefined }].concat(
141
- path.map(indexPath => {
142
- const item = resolveItemByPath(items as Item[], indexPath);
143
-
144
- return { source: (item?.items ?? []) as typeof items, title: nextListOption(item), item };
145
- }),
138
+ path.map(item => ({ source: item.items, title: nextListOption(item), item })),
146
139
  );
147
140
 
148
141
  return (
@@ -158,8 +151,7 @@ export function MobileDroplist({
158
151
  ? undefined
159
152
  : buildLevelItems(
160
153
  source as Item[],
161
- relativePath =>
162
- setPath(prev => [...prev.slice(0, levelIndex), [...(prev[levelIndex - 1] ?? []), ...relativePath]]),
154
+ next => setPath(prev => [...prev.slice(0, levelIndex), next]),
163
155
  handleClose,
164
156
  closeOnClick,
165
157
  );
@@ -228,7 +220,7 @@ export function MobileDroplist({
228
220
  slotAfterTitle={isRoot ? slotAfterTitle : undefined}
229
221
  content={content}
230
222
  footer={isRoot ? footer : undefined}
231
- snapPoints={expanded ? [1] : snapPoints}
223
+ snapPoints={expanded ? [1] : undefined}
232
224
  closeOnPopstate={closeOnPopstate ?? true}
233
225
  />
234
226
  );
@@ -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