@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/CHANGELOG.md +4 -0
- package/README.md +2 -1
- package/dist/cjs/components/Lists/types.d.ts +2 -3
- package/dist/cjs/helperComponents/MobileDroplist/MobileDroplist.d.ts +1 -1
- package/dist/cjs/helperComponents/MobileDroplist/MobileDroplist.js +5 -10
- package/dist/cjs/helperComponents/MobileDroplist/utils.d.ts +2 -9
- package/dist/cjs/helperComponents/MobileDroplist/utils.js +5 -26
- package/dist/esm/components/Lists/types.d.ts +2 -3
- package/dist/esm/helperComponents/MobileDroplist/MobileDroplist.d.ts +1 -1
- package/dist/esm/helperComponents/MobileDroplist/MobileDroplist.js +6 -11
- package/dist/esm/helperComponents/MobileDroplist/utils.d.ts +2 -9
- package/dist/esm/helperComponents/MobileDroplist/utils.js +5 -25
- package/dist/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/tsconfig.esm.tsbuildinfo +1 -1
- package/package.json +18 -18
- package/src/components/Lists/types.ts +1 -3
- package/src/helperComponents/MobileDroplist/MobileDroplist.tsx +6 -14
- package/src/helperComponents/MobileDroplist/utils.tsx +6 -33
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloud-ru/ds-list",
|
|
3
|
-
"version": "2.0.4
|
|
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
|
|
53
|
-
"@cloud-ru/ds-divider": "1.0.1",
|
|
54
|
-
"@cloud-ru/ds-
|
|
55
|
-
"@cloud-ru/ds-
|
|
56
|
-
"@cloud-ru/ds-
|
|
57
|
-
"@cloud-ru/ds-
|
|
58
|
-
"@cloud-ru/ds-
|
|
59
|
-
"@cloud-ru/ds-loader": "1.0.1",
|
|
60
|
-
"@cloud-ru/ds-
|
|
61
|
-
"@cloud-ru/ds-
|
|
62
|
-
"@cloud-ru/ds-scroll": "1.0.2",
|
|
63
|
-
"@cloud-ru/ds-search-private": "1.0.
|
|
64
|
-
"@cloud-ru/ds-toggles": "1.1.
|
|
65
|
-
"@cloud-ru/ds-truncate-string": "1.0.
|
|
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
|
|
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
|
|
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(
|
|
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
|
-
|
|
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] :
|
|
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
|
|
32
|
-
const content = item
|
|
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: (
|
|
25
|
+
onDrill: (item: NextListItem) => void,
|
|
50
26
|
onClose: () => void,
|
|
51
27
|
closeOnClick: boolean,
|
|
52
|
-
basePath: number[] = [],
|
|
53
28
|
): Item[] {
|
|
54
|
-
return items.map(
|
|
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(
|
|
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
|
|
58
|
+
return { ...item, items: buildLevelItems(item.items, onDrill, onClose, closeOnClick) };
|
|
86
59
|
}
|
|
87
60
|
}
|
|
88
61
|
|