@cloud-ru/ds-list 2.1.5 → 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 +6 -0
- package/README.md +85 -1
- package/dist/cjs/components/Items/AccordionItem/AccordionItem.js +3 -3
- package/dist/cjs/components/Items/BaseItem/BaseItem.d.ts +10 -4
- package/dist/cjs/components/Items/BaseItem/BaseItem.js +17 -5
- package/dist/cjs/components/Items/BaseItem/styles.module.css +4 -0
- package/dist/cjs/components/Lists/List/List.js +1 -1
- package/dist/cjs/components/Lists/contexts/CollapseProvider.d.ts +11 -0
- package/dist/cjs/constants.d.ts +5 -0
- package/dist/cjs/constants.js +6 -1
- package/dist/cjs/helperComponents/DesktopDroplist/DesktopDroplist.js +1 -1
- package/dist/cjs/style.css +4 -0
- package/dist/cjs/types.d.ts +3 -1
- package/dist/esm/components/Items/AccordionItem/AccordionItem.js +4 -4
- package/dist/esm/components/Items/BaseItem/BaseItem.d.ts +10 -4
- package/dist/esm/components/Items/BaseItem/BaseItem.js +17 -5
- package/dist/esm/components/Items/BaseItem/styles.module.css +4 -0
- package/dist/esm/components/Lists/List/List.js +1 -1
- package/dist/esm/components/Lists/contexts/CollapseProvider.d.ts +11 -0
- package/dist/esm/constants.d.ts +5 -0
- package/dist/esm/constants.js +5 -0
- package/dist/esm/helperComponents/DesktopDroplist/DesktopDroplist.js +1 -1
- package/dist/esm/style.css +4 -0
- package/dist/esm/types.d.ts +3 -1
- package/dist/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/tsconfig.esm.tsbuildinfo +1 -1
- package/package.json +7 -7
- package/src/components/Items/AccordionItem/AccordionItem.tsx +9 -3
- package/src/components/Items/BaseItem/BaseItem.tsx +27 -5
- package/src/components/Items/BaseItem/styles.module.scss +10 -2
- package/src/components/Lists/List/List.tsx +2 -2
- package/src/components/Lists/contexts/CollapseProvider.tsx +11 -0
- package/src/constants.ts +7 -0
- package/src/helperComponents/DesktopDroplist/DesktopDroplist.tsx +2 -2
- package/src/types.ts +4 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloud-ru/ds-list",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "Списочный UI — компонент List для плоских/вложенных списков с выбором, группами и поиском и Droplist — тот же список в поповере.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -49,21 +49,21 @@
|
|
|
49
49
|
"classnames": "^2.5.1",
|
|
50
50
|
"merge-refs": "^2.0.0",
|
|
51
51
|
"@cloud-ru/ds-adaptive": "^1.0.1",
|
|
52
|
-
"@cloud-ru/ds-button": "^1.0.2",
|
|
53
52
|
"@cloud-ru/ds-bottom-sheet": "^1.1.2",
|
|
53
|
+
"@cloud-ru/ds-button": "^1.0.2",
|
|
54
|
+
"@cloud-ru/ds-icons": "^1.2.0",
|
|
54
55
|
"@cloud-ru/ds-divider": "^1.0.1",
|
|
55
56
|
"@cloud-ru/ds-dropdown": "^1.0.9",
|
|
56
57
|
"@cloud-ru/ds-drag-and-drop": "^1.1.1",
|
|
57
|
-
"@cloud-ru/ds-
|
|
58
|
+
"@cloud-ru/ds-info-block": "^1.0.1",
|
|
58
59
|
"@cloud-ru/ds-loader": "^1.0.1",
|
|
59
60
|
"@cloud-ru/ds-materials": "^1.0.1",
|
|
60
|
-
"@cloud-ru/ds-
|
|
61
|
+
"@cloud-ru/ds-search-private": "^1.0.8",
|
|
61
62
|
"@cloud-ru/ds-portal-context": "^1.0.1",
|
|
62
63
|
"@cloud-ru/ds-scroll": "^1.0.2",
|
|
63
|
-
"@cloud-ru/ds-search-private": "^1.0.8",
|
|
64
64
|
"@cloud-ru/ds-toggles": "^1.1.4",
|
|
65
|
-
"@cloud-ru/ds-
|
|
66
|
-
"@cloud-ru/ds-
|
|
65
|
+
"@cloud-ru/ds-truncate-string": "^1.0.5",
|
|
66
|
+
"@cloud-ru/ds-utils": "^1.1.0"
|
|
67
67
|
},
|
|
68
68
|
"peerDependencies": {
|
|
69
69
|
"@cloud-ru/ds-locale": "^1.0.0"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ChevronDownSVG, ChevronUpSVG } from '@cloud-ru/ds-icons/interface/system';
|
|
2
2
|
import { useCallback } from 'react';
|
|
3
3
|
|
|
4
|
-
import { TEST_IDS } from '../../../constants';
|
|
4
|
+
import { COLLAPSE_TOGGLE_ON, DEFAULT_COLLAPSE_TOGGLE_ON, TEST_IDS } from '../../../constants';
|
|
5
5
|
import { CollapseBlockPrivate } from '../../../helperComponents';
|
|
6
6
|
import {
|
|
7
7
|
CollapseLevelContext,
|
|
@@ -17,7 +17,11 @@ type AccordionItemProps = Omit<FlattenAccordionItem, 'type'> & CommonFlattenProp
|
|
|
17
17
|
|
|
18
18
|
export function AccordionItem({ id, disabled, allChildIds, items, ...option }: AccordionItemProps) {
|
|
19
19
|
const { level = 0 } = useCollapseLevelContext();
|
|
20
|
-
const {
|
|
20
|
+
const {
|
|
21
|
+
openCollapseItems = [],
|
|
22
|
+
toggleOpenCollapseItem,
|
|
23
|
+
toggleOn = DEFAULT_COLLAPSE_TOGGLE_ON,
|
|
24
|
+
} = useCollapseContext();
|
|
21
25
|
|
|
22
26
|
const { value, isSelectionSingle, isSelectionMultiple } = useSelectionContext();
|
|
23
27
|
|
|
@@ -39,11 +43,12 @@ export function AccordionItem({ id, disabled, allChildIds, items, ...option }: A
|
|
|
39
43
|
(isSelectionMultiple && checkedProp),
|
|
40
44
|
);
|
|
41
45
|
|
|
42
|
-
// Раскрытие переключает вся строка целиком: клик/Enter по телу и ArrowRight с клавиатуры.
|
|
43
46
|
const handleToggle = useCallback(() => {
|
|
44
47
|
toggleOpenCollapseItem?.(id ?? '');
|
|
45
48
|
}, [id, toggleOpenCollapseItem]);
|
|
46
49
|
|
|
50
|
+
const isExpandIconTrigger = toggleOn === COLLAPSE_TOGGLE_ON.ExpandIcon;
|
|
51
|
+
|
|
47
52
|
const itemsJSX = useRenderItems(items);
|
|
48
53
|
|
|
49
54
|
return (
|
|
@@ -56,6 +61,7 @@ export function AccordionItem({ id, disabled, allChildIds, items, ...option }: A
|
|
|
56
61
|
open={isOpen}
|
|
57
62
|
expandIcon={isOpen ? <ChevronUpSVG /> : <ChevronDownSVG />}
|
|
58
63
|
onToggleExpand={handleToggle}
|
|
64
|
+
onExpandIconClick={isExpandIconTrigger ? handleToggle : undefined}
|
|
59
65
|
isParentNode
|
|
60
66
|
onOpenNestedList={handleToggle}
|
|
61
67
|
checked={checked}
|
|
@@ -26,10 +26,16 @@ type AllBaseItemProps = FlattenBaseItem & {
|
|
|
26
26
|
onOpenNestedList?(e?: KeyboardEvent<HTMLElement>): void;
|
|
27
27
|
/**
|
|
28
28
|
* Переключение раскрытия вложенного списка. Триггер — вся строка целиком (клик или
|
|
29
|
-
* Enter/Space)
|
|
30
|
-
*
|
|
29
|
+
* Enter/Space). Если передан `onExpandIconClick`, клик по строке раскрытие больше не
|
|
30
|
+
* переключает и остаётся только клавиатурным триггером.
|
|
31
31
|
*/
|
|
32
32
|
onToggleExpand?(): void;
|
|
33
|
+
/**
|
|
34
|
+
* Переключение раскрытия кликом по шеврону `groupIndicator`. Наличие обработчика и есть
|
|
35
|
+
* режим: пока он не передан, шеврон — неинтерактивный индикатор состояния, а раскрытие
|
|
36
|
+
* переключает клик по всей строке (`onToggleExpand`).
|
|
37
|
+
*/
|
|
38
|
+
onExpandIconClick?(e: MouseEvent<HTMLElement>): void;
|
|
33
39
|
/**
|
|
34
40
|
* Слот ручки drag&drop (Figma `centeredWrapper`) — рендерится первым в строке, перед
|
|
35
41
|
* маркером/чекбоксом. Интерактивность (обработчики `@dnd-kit`) и содержимое (иконка)
|
|
@@ -58,6 +64,7 @@ export function BaseItem({
|
|
|
58
64
|
onSelect,
|
|
59
65
|
onOpenNestedList,
|
|
60
66
|
onToggleExpand,
|
|
67
|
+
onExpandIconClick,
|
|
61
68
|
isParentNode,
|
|
62
69
|
className,
|
|
63
70
|
inactive,
|
|
@@ -108,9 +115,12 @@ export function BaseItem({
|
|
|
108
115
|
onClick?.(e as MouseEvent<HTMLElement>);
|
|
109
116
|
|
|
110
117
|
if (isParentNode) {
|
|
111
|
-
// Раскрытие вложенного списка триггерит вся строка
|
|
112
|
-
//
|
|
113
|
-
|
|
118
|
+
// Раскрытие вложенного списка триггерит вся строка целиком — кроме режима, где тогл
|
|
119
|
+
// отдан шеврону (`onExpandIconClick`): там клик по строке принадлежит потребителю
|
|
120
|
+
// (`onClick`, ссылка из `itemWrapRender`) и раскрытия не меняет.
|
|
121
|
+
if (!onExpandIconClick) {
|
|
122
|
+
onToggleExpand?.();
|
|
123
|
+
}
|
|
114
124
|
} else if (interactive) {
|
|
115
125
|
handleChange();
|
|
116
126
|
}
|
|
@@ -152,6 +162,13 @@ export function BaseItem({
|
|
|
152
162
|
}
|
|
153
163
|
};
|
|
154
164
|
|
|
165
|
+
const handleExpandIconClick = (e: MouseEvent<HTMLElement>) => {
|
|
166
|
+
e.preventDefault();
|
|
167
|
+
e.stopPropagation();
|
|
168
|
+
|
|
169
|
+
onExpandIconClick?.(e);
|
|
170
|
+
};
|
|
171
|
+
|
|
155
172
|
const handleCheckboxClick = (e: MouseEvent) => {
|
|
156
173
|
if (isParentNode) {
|
|
157
174
|
e.stopPropagation();
|
|
@@ -249,10 +266,15 @@ export function BaseItem({
|
|
|
249
266
|
)}
|
|
250
267
|
|
|
251
268
|
{!switchProp && expandIcon && (
|
|
269
|
+
// Интерактивный шеврон не получает роль и фокус намеренно: клавиатурный путь идёт
|
|
270
|
+
// через строку, у неё же живёт `aria-expanded`. Лишний таб-стоп сломал бы порядок
|
|
271
|
+
// фокуса списка, а озвучивать AT недостижимый с клавиатуры элемент незачем.
|
|
252
272
|
<span
|
|
253
273
|
className={styles.groupIndicator}
|
|
254
274
|
data-open={open || undefined}
|
|
275
|
+
data-interactive={Boolean(onExpandIconClick) || undefined}
|
|
255
276
|
data-test-id={TEST_IDS.groupIndicator}
|
|
277
|
+
onClick={onExpandIconClick && handleExpandIconClick}
|
|
256
278
|
aria-hidden
|
|
257
279
|
>
|
|
258
280
|
{expandIcon}
|
|
@@ -81,8 +81,9 @@ $sizes: 's', 'm', 'l';
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
.groupIndicator {
|
|
84
|
-
//
|
|
85
|
-
// Своих hover/pressed/focus-состояний
|
|
84
|
+
// По умолчанию шеврон — индикатор состояния, а не кнопка: раскрытие переключает клик по
|
|
85
|
+
// всей строке, поэтому клики шеврон не перехватывает. Своих hover/pressed/focus-состояний
|
|
86
|
+
// у него нет ни в одном из режимов.
|
|
86
87
|
pointer-events: none;
|
|
87
88
|
|
|
88
89
|
display: flex;
|
|
@@ -100,6 +101,13 @@ $sizes: 's', 'm', 'l';
|
|
|
100
101
|
height: 100%;
|
|
101
102
|
}
|
|
102
103
|
|
|
104
|
+
// Режим `collapse.toggleOn: 'expandIcon'`: тогл висит на шевроне, значит он должен
|
|
105
|
+
// принимать клики. Атрибут ставит `BaseItem` ровно тогда, когда пришёл `onExpandIconClick`.
|
|
106
|
+
&[data-interactive] {
|
|
107
|
+
pointer-events: auto;
|
|
108
|
+
cursor: pointer;
|
|
109
|
+
}
|
|
110
|
+
|
|
103
111
|
// Раскрытая группа держит постоянную подложку `neutral/decorTransparent`
|
|
104
112
|
// (варианты мастера `groupIndicatorS/Xs`, `open=true`).
|
|
105
113
|
&[data-open] {
|
|
@@ -87,8 +87,8 @@ const ListImpl = forwardRef(
|
|
|
87
87
|
};
|
|
88
88
|
|
|
89
89
|
const collapseContextValue = useMemo(
|
|
90
|
-
() => ({ openCollapseItems, toggleOpenCollapseItem }),
|
|
91
|
-
[openCollapseItems, toggleOpenCollapseItem],
|
|
90
|
+
() => ({ openCollapseItems, toggleOpenCollapseItem, toggleOn: collapse.toggleOn }),
|
|
91
|
+
[openCollapseItems, toggleOpenCollapseItem, collapse.toggleOn],
|
|
92
92
|
);
|
|
93
93
|
|
|
94
94
|
const focusListContextValue = useMemo(
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createContext, useContext } from 'react';
|
|
2
2
|
|
|
3
|
+
import { CollapseToggleOn } from '../../../types';
|
|
3
4
|
import { ItemId } from '../../Items';
|
|
4
5
|
|
|
5
6
|
export type CollapseLevelContextType = {
|
|
@@ -11,6 +12,7 @@ export const useCollapseLevelContext = () => useContext(CollapseLevelContext);
|
|
|
11
12
|
export type CollapseContextType = {
|
|
12
13
|
openCollapseItems?: ItemId[];
|
|
13
14
|
toggleOpenCollapseItem?(id: ItemId): void;
|
|
15
|
+
toggleOn?: CollapseToggleOn;
|
|
14
16
|
};
|
|
15
17
|
export const CollapseContext = createContext<CollapseContextType>({});
|
|
16
18
|
export const useCollapseContext = () => useContext(CollapseContext);
|
|
@@ -19,4 +21,13 @@ export type CollapseState = {
|
|
|
19
21
|
value?: ItemId[];
|
|
20
22
|
onChange?(value?: ItemId[]): void;
|
|
21
23
|
defaultValue?: ItemId[];
|
|
24
|
+
/**
|
|
25
|
+
* Что переключает раскрытие вложенного списка:
|
|
26
|
+
* <br> - `item` — клик по всей строке (по умолчанию),
|
|
27
|
+
* <br> - `expandIcon` — только клик по шеврону; клик по строке остаётся потребителю
|
|
28
|
+
* (например, когда `itemWrapRender` оборачивает строку в ссылку).
|
|
29
|
+
*
|
|
30
|
+
* Клавиатура (`Enter` / `Space` / `ArrowRight` на строке) раскрывает группу в обоих режимах.
|
|
31
|
+
*/
|
|
32
|
+
toggleOn?: CollapseToggleOn;
|
|
22
33
|
};
|
package/src/constants.ts
CHANGED
|
@@ -21,6 +21,13 @@ export const MODE = {
|
|
|
21
21
|
Multiple: 'multiple',
|
|
22
22
|
} as const;
|
|
23
23
|
|
|
24
|
+
export const COLLAPSE_TOGGLE_ON = {
|
|
25
|
+
Item: 'item',
|
|
26
|
+
ExpandIcon: 'expandIcon',
|
|
27
|
+
} as const;
|
|
28
|
+
|
|
29
|
+
export const DEFAULT_COLLAPSE_TOGGLE_ON = COLLAPSE_TOGGLE_ON.Item;
|
|
30
|
+
|
|
24
31
|
export const ITEM_TYPE = {
|
|
25
32
|
NextList: 'next-list',
|
|
26
33
|
Collapse: 'collapse',
|
|
@@ -208,8 +208,8 @@ export function DesktopDroplist({
|
|
|
208
208
|
) : undefined;
|
|
209
209
|
|
|
210
210
|
const collapseContextValue = useMemo(
|
|
211
|
-
() => ({ openCollapseItems, toggleOpenCollapseItem }),
|
|
212
|
-
[openCollapseItems, toggleOpenCollapseItem],
|
|
211
|
+
() => ({ openCollapseItems, toggleOpenCollapseItem, toggleOn: collapse.toggleOn }),
|
|
212
|
+
[openCollapseItems, toggleOpenCollapseItem, collapse.toggleOn],
|
|
213
213
|
);
|
|
214
214
|
|
|
215
215
|
const focusListContextValue = useMemo(
|
package/src/types.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { ScrollProps as OriginalScrollProps } from '@cloud-ru/ds-scroll';
|
|
|
2
2
|
import { ValueOf } from '@cloud-ru/ds-utils';
|
|
3
3
|
import { ChangeEvent, KeyboardEvent, Ref } from 'react';
|
|
4
4
|
|
|
5
|
-
import { ITEM_TYPE, MODE, SIZE } from './constants';
|
|
5
|
+
import { COLLAPSE_TOGGLE_ON, ITEM_TYPE, MODE, SIZE } from './constants';
|
|
6
6
|
|
|
7
7
|
/** Размер списка и его элементов */
|
|
8
8
|
export type Size = ValueOf<typeof SIZE>;
|
|
@@ -13,6 +13,9 @@ export type Mode = ValueOf<typeof MODE>;
|
|
|
13
13
|
/** Тип группового / составного элемента списка */
|
|
14
14
|
export type ItemType = ValueOf<typeof ITEM_TYPE>;
|
|
15
15
|
|
|
16
|
+
/** Триггер раскрытия вложенного списка у айтема `type: 'collapse'` */
|
|
17
|
+
export type CollapseToggleOn = ValueOf<typeof COLLAPSE_TOGGLE_ON>;
|
|
18
|
+
|
|
16
19
|
/** Настройки поисковой строки списка */
|
|
17
20
|
export type SearchState = {
|
|
18
21
|
placeholder?: string;
|