@snack-uikit/list 0.1.3 → 0.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 +16 -0
- package/README.md +12 -12
- package/dist/components/Items/BaseItem/BaseItem.d.ts +3 -3
- package/dist/components/Items/BaseItem/BaseItem.js +9 -6
- package/dist/components/Items/BaseItem/styles.module.css +3 -0
- package/dist/components/Items/NextListItem/NextListItem.js +1 -1
- package/dist/components/Items/styles.module.css +10 -10
- package/dist/components/Items/types.d.ts +32 -7
- package/dist/components/Lists/Droplist/DropList.d.ts +1 -1
- package/dist/components/Lists/Droplist/DropList.js +11 -5
- package/dist/components/Lists/List/List.d.ts +10 -2
- package/dist/components/Lists/List/List.js +25 -11
- package/dist/components/Lists/ListPrivate/ListPrivate.d.ts +10 -2
- package/dist/components/Lists/ListPrivate/ListPrivate.js +8 -8
- package/dist/components/Lists/ListPrivate/styles.module.css +4 -0
- package/dist/components/Lists/contexts/SelectionProvider.d.ts +19 -12
- package/dist/components/Lists/contexts/SelectionProvider.js +37 -16
- package/dist/components/Lists/styles.module.css +4 -0
- package/dist/components/Lists/types.d.ts +22 -6
- package/dist/helperComponents/HiddenTabButton/HiddenTabButton.d.ts +1 -0
- package/dist/helperComponents/HiddenTabButton/HiddenTabButton.js +2 -2
- package/dist/utils.js +6 -2
- package/package.json +3 -2
- package/src/components/Items/BaseItem/BaseItem.tsx +20 -12
- package/src/components/Items/BaseItem/styles.module.scss +4 -0
- package/src/components/Items/NextListItem/NextListItem.tsx +1 -0
- package/src/components/Items/styles.module.scss +17 -15
- package/src/components/Items/types.ts +32 -10
- package/src/components/Lists/Droplist/DropList.tsx +14 -5
- package/src/components/Lists/List/List.tsx +50 -18
- package/src/components/Lists/ListPrivate/ListPrivate.tsx +11 -10
- package/src/components/Lists/ListPrivate/styles.module.scss +4 -0
- package/src/components/Lists/contexts/ListProvider.tsx +0 -2
- package/src/components/Lists/contexts/SelectionProvider.tsx +43 -29
- package/src/components/Lists/styles.module.scss +5 -0
- package/src/components/Lists/types.ts +22 -9
- package/src/helperComponents/HiddenTabButton/HiddenTabButton.tsx +12 -2
- package/src/utils.ts +11 -2
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,22 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# 0.2.0 (2024-02-02)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* **FF-4075:** add ref, onKeyDown props into list; add className, inactive props into items ([3ee860c](https://github.com/cloud-ru-tech/snack-uikit/commit/3ee860c5257be999ecf3a77d261d74240e7713f2))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### BREAKING CHANGES
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
* **FF-4075:** hide all selection props into 1 prop; add collapse contolled state; list fill container ([e1f8e66](https://github.com/cloud-ru-tech/snack-uikit/commit/e1f8e663a43a1261916513237f3b96b6c1146a67))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
6
22
|
## 0.1.3 (2024-02-01)
|
|
7
23
|
|
|
8
24
|
### Only dependencies have been changed
|
package/README.md
CHANGED
|
@@ -28,18 +28,17 @@
|
|
|
28
28
|
| widthStrategy | enum PopoverWidthStrategy: `"auto"`, `"gte"`, `"eq"` | auto | Стратегия управления шириной контейнера поповера <br> - `auto` - соответствует ширине контента, <br> - `gte` - Great Than or Equal, равен ширине таргета или больше ее, если контент в поповере шире, <br> - `eq` - Equal, строго равен ширине таргета. |
|
|
29
29
|
| trigger | enum Trigger: `"click"`, `"hover"`, `"focusVisible"`, `"focus"`, `"hoverAndFocusVisible"`, `"hoverAndFocus"`, `"clickAndFocusVisible"` | - | Условие отображения поповера: <br> - `click` - открывать по клику <br> - `hover` - открывать по ховеру <br> - `focusVisible` - открывать по focus-visible <br> - `focus` - открывать по фокусу <br> - `hoverAndFocusVisible` - открывать по ховеру и focus-visible <br> - `hoverAndFocus` - открывать по ховеру и фокусу <br> - `clickAndFocusVisible` - открывать по клику и focus-visible |
|
|
30
30
|
| placement | enum Placement: `"left"`, `"left-start"`, `"left-end"`, `"right"`, `"right-start"`, `"right-end"`, `"top"`, `"top-start"`, `"top-end"`, `"bottom"`, `"bottom-start"`, `"bottom-end"` | top | Положение поповера относительно своего триггера (children). |
|
|
31
|
+
| className | `string` | - | CSS-класс |
|
|
31
32
|
| pinTop | `ItemProps[]` | - | Элементы списка, закрепленные сверху |
|
|
32
33
|
| pinBottom | `ItemProps[]` | - | Элементы списка, закрепленные снизу |
|
|
33
|
-
| footer | `
|
|
34
|
-
| footerActiveElementsRefs | `RefObject<HTMLElement>[]` | - | Список ссылок на
|
|
34
|
+
| footer | `ReactElement` | - | Кастомизируемый элемент в конце списка |
|
|
35
|
+
| footerActiveElementsRefs | `RefObject<HTMLElement>[]` | - | Список ссылок на кастомные элементы, помещенные в специальную секцию внизу списка |
|
|
35
36
|
| search | `SearchState` | - | Настройки поисковой строки |
|
|
36
37
|
| loading | `boolean` | - | Флаг, отвещающий за состояние загрузки списка |
|
|
37
38
|
| noData | `string` | - | Текст для состояния "Отсутсвие данных" |
|
|
38
39
|
| noResults | `string` | - | Текст для состояния "Отсутсвие результата" при поиске |
|
|
39
|
-
|
|
|
40
|
-
|
|
|
41
|
-
| onChange | `((value: any) => void) \| ((value: any) => void)` | - | Controlled обработчик измения состояния |
|
|
42
|
-
| selection | "single" \| "multiple" | - | Режим выбора |
|
|
40
|
+
| collapse | `CollapseState` | {} | Настройки раскрытия элементов |
|
|
41
|
+
| selection | `SelectionSingleState \| SelectionMultipleState` | - | |
|
|
43
42
|
| size | "s" \| "m" \| "l" | - | Размер списка |
|
|
44
43
|
| marker | `boolean` | - | Отображать ли маркер у выбранного жлемента списка |
|
|
45
44
|
| scroll | `boolean` | - | Включить ли скролл для основной части списка |
|
|
@@ -52,16 +51,17 @@
|
|
|
52
51
|
| items* | `ItemProps[]` | - | Основные элементы списка |
|
|
53
52
|
| pinTop | `ItemProps[]` | - | Элементы списка, закрепленные сверху |
|
|
54
53
|
| pinBottom | `ItemProps[]` | - | Элементы списка, закрепленные снизу |
|
|
55
|
-
| footer | `
|
|
56
|
-
| footerActiveElementsRefs | `RefObject<HTMLElement>[]` | - | Список ссылок на
|
|
54
|
+
| footer | `ReactElement` | - | Кастомизируемый элемент в конце списка |
|
|
55
|
+
| footerActiveElementsRefs | `RefObject<HTMLElement>[]` | - | Список ссылок на кастомные элементы, помещенные в специальную секцию внизу списка |
|
|
57
56
|
| search | `SearchState` | - | Настройки поисковой строки |
|
|
58
57
|
| loading | `boolean` | - | Флаг, отвещающий за состояние загрузки списка |
|
|
59
58
|
| noData | `string` | - | Текст для состояния "Отсутсвие данных" |
|
|
60
59
|
| noResults | `string` | - | Текст для состояния "Отсутсвие результата" при поиске |
|
|
61
|
-
|
|
|
62
|
-
|
|
|
63
|
-
|
|
|
64
|
-
|
|
|
60
|
+
| tabIndex | `number` | - | Tab Index |
|
|
61
|
+
| collapse | `CollapseState` | {} | Настройки раскрытия элементов |
|
|
62
|
+
| className | `string` | - | CSS-класс |
|
|
63
|
+
| onKeyDown | `(e: KeyboardEvent<HTMLElement>) => void` | - | |
|
|
64
|
+
| selection | `SelectionSingleState \| SelectionMultipleState` | - | |
|
|
65
65
|
| size | "s" \| "m" \| "l" | - | Размер списка |
|
|
66
66
|
| marker | `boolean` | - | Отображать ли маркер у выбранного жлемента списка |
|
|
67
67
|
| scroll | `boolean` | - | Включить ли скролл для основной части списка |
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { BaseItemPrivateProps, BaseItemProps
|
|
2
|
-
type AllBaseItemProps = BaseItemProps & BaseItemPrivateProps &
|
|
1
|
+
import { BaseItemPrivateProps, BaseItemProps } from '../types';
|
|
2
|
+
type AllBaseItemProps = BaseItemProps & BaseItemPrivateProps & {
|
|
3
3
|
indeterminate?: boolean;
|
|
4
4
|
onSelect?(): void;
|
|
5
5
|
isParentNode?: boolean;
|
|
6
6
|
};
|
|
7
|
-
export declare function BaseItem({ beforeContent, afterContent, content, onClick, id, expandIcon, disabled, open, itemRef, switch: switchProp, onKeyDown, onFocus, indeterminate, onSelect, isParentNode, ...rest }: AllBaseItemProps): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export declare function BaseItem({ beforeContent, afterContent, content, onClick, id, expandIcon, disabled, open, itemRef, switch: switchProp, onKeyDown, onFocus, indeterminate, onSelect, isParentNode, className, inactive, ...rest }: AllBaseItemProps): import("react/jsx-runtime").JSX.Element;
|
|
8
8
|
export {};
|
|
@@ -19,20 +19,23 @@ import commonStyles from '../styles.module.css';
|
|
|
19
19
|
import { CHECKBOX_SIZE_MAP } from './constants';
|
|
20
20
|
import styles from './styles.module.css';
|
|
21
21
|
export function BaseItem(_a) {
|
|
22
|
-
var { beforeContent, afterContent, content, onClick, id, expandIcon, disabled, open, itemRef, switch: switchProp, onKeyDown, onFocus, indeterminate, onSelect, isParentNode } = _a, rest = __rest(_a, ["beforeContent", "afterContent", "content", "onClick", "id", "expandIcon", "disabled", "open", "itemRef", "switch", "onKeyDown", "onFocus", "indeterminate", "onSelect", "isParentNode"]);
|
|
22
|
+
var { beforeContent, afterContent, content, onClick, id, expandIcon, disabled, open, itemRef, switch: switchProp, onKeyDown, onFocus, indeterminate, onSelect, isParentNode, className, inactive } = _a, rest = __rest(_a, ["beforeContent", "afterContent", "content", "onClick", "id", "expandIcon", "disabled", "open", "itemRef", "switch", "onKeyDown", "onFocus", "indeterminate", "onSelect", "isParentNode", "className", "inactive"]);
|
|
23
23
|
const { option, caption, description } = content || {};
|
|
24
|
+
const interactive = !inactive;
|
|
24
25
|
const { parentResetActiveFocusIndex } = useParentListContext();
|
|
25
26
|
const { size, marker } = useListContext();
|
|
26
27
|
const { level = 0 } = useCollapseContext();
|
|
27
|
-
const { value, onChange,
|
|
28
|
+
const { value, onChange, mode, isSelectionSingle, isSelectionMultiple } = useSelectionContext();
|
|
28
29
|
const isChecked = isSelectionSingle ? value === id : value === null || value === void 0 ? void 0 : value.includes(id);
|
|
29
30
|
const handleChange = () => {
|
|
30
31
|
onChange === null || onChange === void 0 ? void 0 : onChange(id !== null && id !== void 0 ? id : '');
|
|
31
32
|
};
|
|
32
33
|
const handleItemClick = (e) => {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
if (interactive) {
|
|
35
|
+
parentResetActiveFocusIndex === null || parentResetActiveFocusIndex === void 0 ? void 0 : parentResetActiveFocusIndex();
|
|
36
|
+
if (!isParentNode) {
|
|
37
|
+
handleChange();
|
|
38
|
+
}
|
|
36
39
|
}
|
|
37
40
|
onClick === null || onClick === void 0 ? void 0 : onClick(e);
|
|
38
41
|
};
|
|
@@ -65,5 +68,5 @@ export function BaseItem(_a) {
|
|
|
65
68
|
e.stopPropagation();
|
|
66
69
|
};
|
|
67
70
|
const props = extractSupportProps(rest);
|
|
68
|
-
return (_jsx("li", { role: 'menuitem', "data-test-id": props['data-test-id'] || 'list__base-item_' + id, children: _jsxs("button", { ref: itemRef, className: cn(commonStyles.listItem, styles.droplistItem), "data-size": size, onClick: handleItemClick, tabIndex: -1, "data-checked": (isParentNode && (indeterminate || isChecked)) || (isChecked && !switchProp) || undefined, "data-variant":
|
|
71
|
+
return (_jsx("li", { role: 'menuitem', "data-test-id": props['data-test-id'] || 'list__base-item_' + id, children: _jsxs("button", { ref: itemRef, className: cn(commonStyles.listItem, styles.droplistItem, className), "data-size": size, onClick: handleItemClick, tabIndex: -1, "data-non-pointer": inactive && !onClick, "data-inactive": inactive || undefined, "data-checked": (isParentNode && (indeterminate || isChecked)) || (isChecked && !switchProp) || undefined, "data-variant": mode || undefined, "data-open": open || undefined, disabled: disabled, onKeyDown: handleItemKeyDown, onFocus: handleItemFocus, style: { '--level': level }, children: [!switchProp && isSelectionSingle && marker && !isParentNode && interactive && (_jsx("div", { className: styles.markerContainer, "data-test-id": 'list__base-item-marker' })), !switchProp && isSelectionMultiple && interactive && (_jsx("div", { className: styles.checkbox, children: _jsx(Checkbox, { size: CHECKBOX_SIZE_MAP[size !== null && size !== void 0 ? size : 's'], disabled: disabled, tabIndex: -1, onChange: handleCheckboxChange, checked: isChecked, "data-test-id": 'list__base-item-checkbox', onClick: handleCheckboxClick, indeterminate: indeterminate }) })), beforeContent && _jsx("div", { className: styles.beforeContent, children: beforeContent }), _jsxs("div", { className: styles.content, children: [_jsxs("div", { className: styles.headline, children: [_jsx("span", { className: styles.option, children: _jsx(TruncateString, { text: option, maxLines: 1 }) }), caption && _jsx("span", { className: styles.caption, children: caption })] }), description && (_jsx("div", { className: styles.description, children: _jsx(TruncateString, { text: description, maxLines: 2 }) }))] }), afterContent, switchProp && interactive && (_jsx(Switch, { disabled: disabled, checked: isChecked, "data-test-id": 'list__base-item-switch' })), !switchProp && expandIcon && _jsx("span", { className: styles.expandableIcon, children: expandIcon })] }) }));
|
|
69
72
|
}
|
|
@@ -170,6 +170,9 @@
|
|
|
170
170
|
letter-spacing:var(--sans-body-m-letter-spacing, 0.1px);
|
|
171
171
|
paragraph-spacing:var(--sans-body-m-paragraph-spacing, 7.7px);
|
|
172
172
|
}
|
|
173
|
+
.droplistItem[data-non-pointer]{
|
|
174
|
+
cursor:inherit;
|
|
175
|
+
}
|
|
173
176
|
.droplistItem:disabled .option,
|
|
174
177
|
.droplistItem:disabled .description,
|
|
175
178
|
.droplistItem:disabled .caption,
|
|
@@ -62,5 +62,5 @@ export function NextListItem(_a) {
|
|
|
62
62
|
parentResetNestedIndex: resetNestedIndex,
|
|
63
63
|
parentResetActiveFocusIndex: resetActiveFocusIndex,
|
|
64
64
|
toggleOpenCollapsedItems: id => setOpenCollapsedItems(items => items.includes(id) ? items.filter(item => item !== id) : items.concat([id])),
|
|
65
|
-
}, children: _jsx(ListPrivate, { onKeyDown: handleListKeyDown, items: items, nested: true, search: search, scroll: scroll, scrollRef: scrollRef }) }), trigger: 'hover', open: (open || parentIds[parentOpenNestedIndex] === id) && !disabled, onOpenChange: handleOpenChange, placement: placement, children: _jsx(BaseItem, Object.assign({}, option, { disabled: disabled, open: open, expandIcon: _jsx(ChevronRightSVG, {}), id: id, isParentNode: true, indeterminate: isIndeterminate && !checked, onSelect: handleOnSelect })) }) }));
|
|
65
|
+
}, children: _jsx(ListPrivate, { onKeyDown: handleListKeyDown, items: items, nested: true, search: search, scroll: scroll, scrollRef: scrollRef, limitedScrollHeight: true }) }), trigger: 'hover', open: (open || parentIds[parentOpenNestedIndex] === id) && !disabled, onOpenChange: handleOpenChange, placement: placement, children: _jsx(BaseItem, Object.assign({}, option, { disabled: disabled, open: open, expandIcon: _jsx(ChevronRightSVG, {}), id: id, isParentNode: true, indeterminate: isIndeterminate && !checked, onSelect: handleOnSelect })) }) }));
|
|
66
66
|
}
|
|
@@ -50,16 +50,6 @@
|
|
|
50
50
|
height:100%;
|
|
51
51
|
background-color:transparent;
|
|
52
52
|
}
|
|
53
|
-
.listItem[data-open]::after, .listItem[data-focused]::after, .listItem:hover::after, .listItem:focus-visible::after{
|
|
54
|
-
opacity:var(--opacity-a008, 0.08);
|
|
55
|
-
background-color:var(--sys-neutral-accent-default, #757575);
|
|
56
|
-
}
|
|
57
|
-
.listItem[data-focused], .listItem:focus-visible{
|
|
58
|
-
outline-width:var(--border-state-focus-s-border-width, 2px);
|
|
59
|
-
outline-style:var(--border-state-focus-s-border-style, solid);
|
|
60
|
-
outline-color:var(--border-state-focus-s-border-color, );
|
|
61
|
-
outline-offset:calc(var(--border-state-focus-s-border-width, 2px) * -1);
|
|
62
|
-
}
|
|
63
53
|
.listItem:disabled{
|
|
64
54
|
cursor:not-allowed;
|
|
65
55
|
background-color:transparent;
|
|
@@ -71,4 +61,14 @@
|
|
|
71
61
|
.listItem:disabled::after{
|
|
72
62
|
cursor:not-allowed;
|
|
73
63
|
background-color:transparent;
|
|
64
|
+
}
|
|
65
|
+
.listItem:not([data-inactive])[data-open]::after, .listItem:not([data-inactive])[data-focused]::after, .listItem:not([data-inactive]):hover::after, .listItem:not([data-inactive]):focus-visible::after{
|
|
66
|
+
opacity:var(--opacity-a008, 0.08);
|
|
67
|
+
background-color:var(--sys-neutral-accent-default, #757575);
|
|
68
|
+
}
|
|
69
|
+
.listItem:not([data-inactive])[data-focused], .listItem:not([data-inactive]):focus-visible{
|
|
70
|
+
outline-width:var(--border-state-focus-s-border-width, 2px);
|
|
71
|
+
outline-style:var(--border-state-focus-s-border-style, solid);
|
|
72
|
+
outline-color:var(--border-state-focus-s-border-color, );
|
|
73
|
+
outline-offset:calc(var(--border-state-focus-s-border-width, 2px) * -1);
|
|
74
74
|
}
|
|
@@ -12,32 +12,57 @@ export type BaseItemPrivateProps = {
|
|
|
12
12
|
open?: boolean;
|
|
13
13
|
};
|
|
14
14
|
export type BaseItemProps = WithSupportProps<{
|
|
15
|
+
/**
|
|
16
|
+
* Слот до основного контента
|
|
17
|
+
* @type ReactElement
|
|
18
|
+
*/
|
|
15
19
|
beforeContent?: ReactNode;
|
|
20
|
+
/**
|
|
21
|
+
* Слот после основного контента
|
|
22
|
+
* @type ReactElement
|
|
23
|
+
*/
|
|
16
24
|
afterContent?: ReactNode;
|
|
25
|
+
/** Основной контент айтема */
|
|
17
26
|
content: ItemContentProps;
|
|
27
|
+
/** Колбек обработки клика */
|
|
18
28
|
onClick?(e: MouseEvent<HTMLButtonElement>): void;
|
|
29
|
+
/** Колбек обработки нажатия клавиши */
|
|
19
30
|
onKeyDown?(e: KeyboardEvent<HTMLButtonElement>): void;
|
|
31
|
+
/** Колбек обработки фокуса */
|
|
20
32
|
onFocus?(e: FocusEvent<HTMLButtonElement>): void;
|
|
33
|
+
/** Колбек обработки блюра */
|
|
21
34
|
onBlur?(e: FocusEvent<HTMLButtonElement>): void;
|
|
35
|
+
/** Уникальный идентификатор */
|
|
22
36
|
id?: string | number;
|
|
37
|
+
/** Флаг неактивности элемента */
|
|
23
38
|
disabled?: boolean;
|
|
24
39
|
itemRef?: RefObject<HTMLButtonElement>;
|
|
25
|
-
|
|
26
|
-
|
|
40
|
+
className?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Флаг отображения отключения реакции на любое css состояние (hover/focus и тд)
|
|
43
|
+
* <br>
|
|
44
|
+
* Так же элемент пропадает из навигации с клавиатуры, и не может быть выбран (selection)
|
|
45
|
+
*/
|
|
46
|
+
inactive?: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Флаг отображения состояния выбранного элемента через switch
|
|
49
|
+
*/
|
|
27
50
|
switch?: boolean;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export type
|
|
51
|
+
}>;
|
|
52
|
+
type BaseItemsWithoutNonGroupProps = Omit<BaseItemProps, 'switch' | 'inactive'>;
|
|
53
|
+
export type ItemProps = BaseItemProps | AccordionItemProps | NextListItemProps | GroupItemProps;
|
|
54
|
+
export type AccordionItemProps = BaseItemsWithoutNonGroupProps & {
|
|
31
55
|
items: ItemProps[];
|
|
32
56
|
type: 'collapse';
|
|
33
57
|
};
|
|
34
|
-
export type NextListItemProps =
|
|
58
|
+
export type NextListItemProps = BaseItemsWithoutNonGroupProps & {
|
|
35
59
|
items: ItemProps[];
|
|
36
60
|
type: 'next-list';
|
|
37
61
|
placement?: 'right-start' | 'left-start' | 'left' | 'right' | 'left-end' | 'right-end';
|
|
38
62
|
search?: SearchState;
|
|
39
63
|
} & ScrollProps;
|
|
40
|
-
export type GroupItemProps = SeparatorProps & {
|
|
64
|
+
export type GroupItemProps = Omit<SeparatorProps, 'size'> & {
|
|
41
65
|
items: ItemProps[];
|
|
42
66
|
id?: string | number;
|
|
43
67
|
};
|
|
68
|
+
export {};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { DroplistProps } from '../types';
|
|
2
|
-
export declare function Droplist({ items: itemsProp, search, pinBottom, pinTop, footerActiveElementsRefs, children, trigger, placement, widthStrategy, triggerElemRef: triggerElemRefProp, open: openProp, onOpenChange, ...props }: DroplistProps): import("react/jsx-runtime").JSX.Element;
|
|
2
|
+
export declare function Droplist({ items: itemsProp, search, pinBottom, pinTop, footerActiveElementsRefs, children, trigger, placement, widthStrategy, triggerElemRef: triggerElemRefProp, open: openProp, onOpenChange, collapse, ...props }: DroplistProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -10,7 +10,7 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
10
10
|
return t;
|
|
11
11
|
};
|
|
12
12
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
13
|
-
import { cloneElement, isValidElement, useCallback, useMemo, useRef
|
|
13
|
+
import { cloneElement, isValidElement, useCallback, useMemo, useRef } from 'react';
|
|
14
14
|
import { useUncontrolledProp } from 'uncontrollable';
|
|
15
15
|
import { Dropdown } from '@snack-uikit/dropdown';
|
|
16
16
|
import { extractItemIds, extractItemRefs, withCollapsedItems } from '../../../utils';
|
|
@@ -19,10 +19,16 @@ import { extractSelectionProps, ParentListContext, SelectionProvider } from '../
|
|
|
19
19
|
import { useKeyboardNavigation } from '../hooks';
|
|
20
20
|
import { ListPrivate } from '../ListPrivate';
|
|
21
21
|
export function Droplist(_a) {
|
|
22
|
-
var
|
|
22
|
+
var _b;
|
|
23
|
+
var { items: itemsProp, search, pinBottom, pinTop, footerActiveElementsRefs, children, trigger, placement, widthStrategy, triggerElemRef: triggerElemRefProp, open: openProp, onOpenChange, collapse = {} } = _a, props = __rest(_a, ["items", "search", "pinBottom", "pinTop", "footerActiveElementsRefs", "children", "trigger", "placement", "widthStrategy", "triggerElemRef", "open", "onOpenChange", "collapse"]);
|
|
23
24
|
const hasSearch = useMemo(() => Boolean(search), [search]);
|
|
24
25
|
const memorizedItems = useMemo(() => addItemsIds((pinBottom !== null && pinBottom !== void 0 ? pinBottom : []).concat(itemsProp).concat(pinTop !== null && pinTop !== void 0 ? pinTop : [])), [itemsProp, pinBottom, pinTop]);
|
|
25
|
-
const [openCollapsedItems, setOpenCollapsedItems] =
|
|
26
|
+
const [openCollapsedItems, setOpenCollapsedItems] = useUncontrolledProp(collapse.value, (_b = collapse.defaultValue) !== null && _b !== void 0 ? _b : [], collapse.onChange
|
|
27
|
+
? cb => {
|
|
28
|
+
var _a;
|
|
29
|
+
(_a = collapse.onChange) === null || _a === void 0 ? void 0 : _a.call(collapse, cb(collapse.value));
|
|
30
|
+
}
|
|
31
|
+
: undefined);
|
|
26
32
|
const { search: searchItem, footerRefs } = useItemsWithIds({
|
|
27
33
|
search: hasSearch,
|
|
28
34
|
footerActiveElementsRefs,
|
|
@@ -88,8 +94,8 @@ export function Droplist(_a) {
|
|
|
88
94
|
parentRef: listRef,
|
|
89
95
|
parentResetNestedIndex: resetNestedIndex,
|
|
90
96
|
parentResetActiveFocusIndex: resetActiveFocusIndex,
|
|
91
|
-
toggleOpenCollapsedItems: id => setOpenCollapsedItems(items => items.includes(id) ? items.filter(item => item !== id) : items.concat([id])),
|
|
92
|
-
}, children: _jsx(Dropdown, Object.assign({ content: _jsx(ListPrivate, Object.assign({ onKeyDown: handleListKeyDown, tabIndex: 0, ref: listRef, search: search }, slicedItems, props)), trigger: trigger, placement: placement, widthStrategy: widthStrategy }, (triggerElemRefProp
|
|
97
|
+
toggleOpenCollapsedItems: id => setOpenCollapsedItems((items = []) => items.includes(id) ? items.filter(item => item !== id) : items === null || items === void 0 ? void 0 : items.concat([id])),
|
|
98
|
+
}, children: _jsx(Dropdown, Object.assign({ content: _jsx(ListPrivate, Object.assign({ onKeyDown: handleListKeyDown, tabIndex: 0, ref: listRef, search: search, limitedScrollHeight: true }, slicedItems, props)), trigger: trigger, placement: placement, widthStrategy: widthStrategy }, (triggerElemRefProp
|
|
93
99
|
? {}
|
|
94
100
|
: {
|
|
95
101
|
triggerRef: triggerElemRef,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import { KeyboardEvent } from 'react';
|
|
2
2
|
export declare const List: import("react").ForwardRefExoticComponent<{
|
|
3
3
|
'data-test-id'?: string | undefined;
|
|
4
4
|
} & import("react").AriaAttributes & {
|
|
@@ -11,4 +11,12 @@ export declare const List: import("react").ForwardRefExoticComponent<{
|
|
|
11
11
|
loading?: boolean | undefined;
|
|
12
12
|
noData?: string | undefined;
|
|
13
13
|
noResults?: string | undefined;
|
|
14
|
-
|
|
14
|
+
tabIndex?: number | undefined;
|
|
15
|
+
collapse?: {
|
|
16
|
+
value?: (string | number)[] | undefined;
|
|
17
|
+
onChange?(value: (string | number)[]): void;
|
|
18
|
+
defaultValue?: (string | number)[] | undefined;
|
|
19
|
+
} | undefined;
|
|
20
|
+
className?: string | undefined;
|
|
21
|
+
onKeyDown?(e: KeyboardEvent<HTMLElement>): void;
|
|
22
|
+
} & import("../contexts").SelectionState & import("../contexts").ListContextType & import("../../../types").ScrollProps & import("react").RefAttributes<HTMLElement>>;
|
|
@@ -10,7 +10,10 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
10
10
|
return t;
|
|
11
11
|
};
|
|
12
12
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
|
-
import
|
|
13
|
+
import cn from 'classnames';
|
|
14
|
+
import mergeRefs from 'merge-refs';
|
|
15
|
+
import { forwardRef, useMemo, useRef } from 'react';
|
|
16
|
+
import { useUncontrolledProp } from 'uncontrollable';
|
|
14
17
|
import { HiddenTabButton } from '../../../helperComponents';
|
|
15
18
|
import { extractItemIds, extractItemRefs, withCollapsedItems } from '../../../utils';
|
|
16
19
|
import { addItemsIds, getSlicedItems, useItemsWithIds } from '../../Items';
|
|
@@ -18,11 +21,17 @@ import { extractSelectionProps, ParentListContext, SelectionProvider, useParentL
|
|
|
18
21
|
import { useKeyboardNavigation } from '../hooks';
|
|
19
22
|
import { ListPrivate } from '../ListPrivate';
|
|
20
23
|
import styles from '../styles.module.css';
|
|
21
|
-
export const List = forwardRef((_a) => {
|
|
22
|
-
var
|
|
24
|
+
export const List = forwardRef((_a, ref) => {
|
|
25
|
+
var _b;
|
|
26
|
+
var { items: itemsProp, search, pinBottom, pinTop, footerActiveElementsRefs, onKeyDown, tabIndex = 0, className, collapse = {} } = _a, props = __rest(_a, ["items", "search", "pinBottom", "pinTop", "footerActiveElementsRefs", "onKeyDown", "tabIndex", "className", "collapse"]);
|
|
23
27
|
const hasSearch = useMemo(() => Boolean(search), [search]);
|
|
24
28
|
const memorizedItems = useMemo(() => addItemsIds((pinTop !== null && pinTop !== void 0 ? pinTop : []).concat(itemsProp).concat(pinBottom !== null && pinBottom !== void 0 ? pinBottom : [])), [itemsProp, pinBottom, pinTop]);
|
|
25
|
-
const [openCollapsedItems, setOpenCollapsedItems] =
|
|
29
|
+
const [openCollapsedItems, setOpenCollapsedItems] = useUncontrolledProp(collapse.value, (_b = collapse.defaultValue) !== null && _b !== void 0 ? _b : [], collapse.onChange
|
|
30
|
+
? cb => {
|
|
31
|
+
var _a;
|
|
32
|
+
(_a = collapse.onChange) === null || _a === void 0 ? void 0 : _a.call(collapse, cb(collapse.value));
|
|
33
|
+
}
|
|
34
|
+
: undefined);
|
|
26
35
|
const { search: searchItem, footerRefs } = useItemsWithIds({
|
|
27
36
|
search: hasSearch,
|
|
28
37
|
footerActiveElementsRefs,
|
|
@@ -52,6 +61,16 @@ export const List = forwardRef((_a) => {
|
|
|
52
61
|
itemRefs,
|
|
53
62
|
});
|
|
54
63
|
const isActive = listRef.current === document.activeElement && activeFocusIndex === -1 && openNestedIndex === -1;
|
|
64
|
+
const mergedHandlerKeyDown = (e) => {
|
|
65
|
+
onKeyDown === null || onKeyDown === void 0 ? void 0 : onKeyDown(e);
|
|
66
|
+
handleListKeyDown === null || handleListKeyDown === void 0 ? void 0 : handleListKeyDown(e);
|
|
67
|
+
};
|
|
68
|
+
const handleOnFocus = (e) => {
|
|
69
|
+
if (e.relatedTarget === null ||
|
|
70
|
+
(e.relatedTarget && itemRefs.every(({ current }) => current !== e.relatedTarget))) {
|
|
71
|
+
resetActiveFocusIndex();
|
|
72
|
+
}
|
|
73
|
+
};
|
|
55
74
|
return (
|
|
56
75
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
57
76
|
// @ts-ignore
|
|
@@ -67,11 +86,6 @@ export const List = forwardRef((_a) => {
|
|
|
67
86
|
parentResetNestedIndex: resetNestedIndex,
|
|
68
87
|
parentResetActiveFocusIndex: resetActiveFocusIndex,
|
|
69
88
|
openCollapsedItems,
|
|
70
|
-
toggleOpenCollapsedItems: id => setOpenCollapsedItems(items => items.includes(id) ? items.filter(item => item !== id) : items.concat([id])),
|
|
71
|
-
}, children: _jsxs("div", { className: styles.wrapper, "data-active": isActive || undefined, children: [_jsx(ListPrivate, Object.assign({}, props, slicedItems, { ref: listRef, onFocus:
|
|
72
|
-
if (e.relatedTarget === null ||
|
|
73
|
-
(e.relatedTarget && itemRefs.every(({ current }) => current !== e.relatedTarget))) {
|
|
74
|
-
resetActiveFocusIndex();
|
|
75
|
-
}
|
|
76
|
-
}, onKeyDown: handleListKeyDown, tabIndex: 0, search: search, nested: false })), _jsx(HiddenTabButton, { ref: btnRef, listRef: listRef })] }) }) })));
|
|
89
|
+
toggleOpenCollapsedItems: id => setOpenCollapsedItems((items = []) => items.includes(id) ? items.filter(item => item !== id) : items === null || items === void 0 ? void 0 : items.concat([id])),
|
|
90
|
+
}, children: _jsxs("div", { className: cn(styles.wrapper, className), "data-active": isActive || undefined, children: [_jsx(ListPrivate, Object.assign({}, props, slicedItems, { ref: mergeRefs(ref, listRef), onFocus: handleOnFocus, onKeyDown: mergedHandlerKeyDown, tabIndex: tabIndex, search: search, nested: false })), _jsx(HiddenTabButton, { ref: btnRef, listRef: listRef, tabIndex: tabIndex })] }) }) })));
|
|
77
91
|
});
|
|
@@ -11,12 +11,20 @@ export declare const ListPrivate: import("react").ForwardRefExoticComponent<{
|
|
|
11
11
|
loading?: boolean | undefined;
|
|
12
12
|
noData?: string | undefined;
|
|
13
13
|
noResults?: string | undefined;
|
|
14
|
-
|
|
14
|
+
tabIndex?: number | undefined;
|
|
15
|
+
collapse?: {
|
|
16
|
+
value?: (string | number)[] | undefined;
|
|
17
|
+
onChange?(value: (string | number)[]): void;
|
|
18
|
+
defaultValue?: (string | number)[] | undefined;
|
|
19
|
+
} | undefined;
|
|
20
|
+
className?: string | undefined;
|
|
21
|
+
onKeyDown?(e: import("react").KeyboardEvent<HTMLElement>): void;
|
|
22
|
+
} & import("../contexts").SelectionState & import("../contexts").ListContextType & import("../../../types").ScrollProps & {
|
|
15
23
|
nested?: boolean | undefined;
|
|
16
24
|
active?: boolean | undefined;
|
|
17
25
|
tabIndex?: number | undefined;
|
|
18
26
|
onFocus?(e: import("react").FocusEvent<HTMLElement, Element>): void;
|
|
19
27
|
onBlur?(e: import("react").FocusEvent<HTMLElement, Element>): void;
|
|
20
28
|
onKeyDown?(e: import("react").KeyboardEvent<HTMLElement>): void;
|
|
21
|
-
|
|
29
|
+
limitedScrollHeight?: boolean | undefined;
|
|
22
30
|
} & import("react").RefAttributes<HTMLElement>>;
|
|
@@ -9,7 +9,7 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
9
9
|
}
|
|
10
10
|
return t;
|
|
11
11
|
};
|
|
12
|
-
import { jsx as _jsx,
|
|
12
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
13
13
|
import cn from 'classnames';
|
|
14
14
|
import { forwardRef, useMemo } from 'react';
|
|
15
15
|
import { Spinner } from '@snack-uikit/loaders';
|
|
@@ -22,18 +22,18 @@ import commonStyles from '../styles.module.css';
|
|
|
22
22
|
import styles from './styles.module.css';
|
|
23
23
|
export const ListPrivate = forwardRef((_a, ref) => {
|
|
24
24
|
var _b, _c;
|
|
25
|
-
var { items, pinTop, pinBottom,
|
|
25
|
+
var { items, pinTop, pinBottom, onKeyDown, onBlur, onFocus, tabIndex, active, scroll, nested, search, scrollRef, scrollContainerRef, footer, loading, noData = 'No data', noResults = 'No results', size = 's', marker, limitedScrollHeight, className } = _a, props = __rest(_a, ["items", "pinTop", "pinBottom", "onKeyDown", "onBlur", "onFocus", "tabIndex", "active", "scroll", "nested", "search", "scrollRef", "scrollContainerRef", "footer", "loading", "noData", "noResults", "size", "marker", "limitedScrollHeight", "className"]);
|
|
26
26
|
const itemsJSX = useRenderItems(items);
|
|
27
27
|
const itemsPinTopJSX = useRenderItems(pinTop !== null && pinTop !== void 0 ? pinTop : []);
|
|
28
28
|
const itemsPinBottomJSX = useRenderItems(pinBottom !== null && pinBottom !== void 0 ? pinBottom : []);
|
|
29
29
|
const hasNoItems = items.length + ((_b = pinTop === null || pinTop === void 0 ? void 0 : pinTop.length) !== null && _b !== void 0 ? _b : 0) + ((_c = pinBottom === null || pinBottom === void 0 ? void 0 : pinBottom.length) !== null && _c !== void 0 ? _c : 0) === 0;
|
|
30
30
|
const loadingJSX = useMemo(() => loading && (_jsx("div", { role: 'spinbutton', tabIndex: -1, className: styles.loader, "data-size": size, "data-no-items": hasNoItems || undefined, "data-test-id": 'list__loader', children: _jsx(Spinner, { size: size === 'l' ? 's' : 'xs' }) })), [hasNoItems, loading, size]);
|
|
31
|
-
const content = useMemo(() => (_jsxs(
|
|
32
|
-
const listJSX = (_jsx("ul", Object.assign({ className: commonStyles.listContainer, ref: ref, onKeyDown: onKeyDown, tabIndex: tabIndex, onFocus: !nested ? onFocus : undefined, onBlur: onBlur, "data-active": active || undefined, role: 'menu' }, extractSupportProps(props), { children: _jsxs(ToggleGroup, { selectionMode:
|
|
33
|
-
[commonStyles.scrollContainerS]: scroll && size === 's',
|
|
34
|
-
[commonStyles.scrollContainerM]: scroll && size === 'm',
|
|
35
|
-
[commonStyles.scrollContainerL]: scroll && size === 'l',
|
|
36
|
-
}), barHideStrategy: '
|
|
31
|
+
const content = useMemo(() => (_jsxs("div", { className: styles.content, children: [itemsJSX, loadingJSX, hasNoItems && !(search === null || search === void 0 ? void 0 : search.value) && !loading && (_jsx("div", { className: commonStyles.infoBlock, "data-test-id": 'list__no-data', children: noData })), hasNoItems && (search === null || search === void 0 ? void 0 : search.value) && !loading && (_jsx("div", { className: commonStyles.infoBlock, "data-test-id": 'list__no-results', children: noResults }))] })), [hasNoItems, itemsJSX, loading, loadingJSX, noData, noResults, search === null || search === void 0 ? void 0 : search.value]);
|
|
32
|
+
const listJSX = (_jsx("ul", Object.assign({ className: cn(commonStyles.listContainer, className), ref: ref, onKeyDown: onKeyDown, tabIndex: tabIndex, onFocus: !nested ? onFocus : undefined, onBlur: onBlur, "data-active": active || undefined, role: 'menu' }, extractSupportProps(props), { children: _jsxs(ToggleGroup, { selectionMode: 'multiple', children: [(Number(pinTop === null || pinTop === void 0 ? void 0 : pinTop.length) > 0 || search) && (_jsxs(PinTopGroupItem, { children: [search && _jsx(SearchItem, { search: search }), Number(pinTop === null || pinTop === void 0 ? void 0 : pinTop.length) > 0 && itemsPinTopJSX] })), scroll ? (_jsxs(Scroll, { className: cn({
|
|
33
|
+
[commonStyles.scrollContainerS]: scroll && limitedScrollHeight && size === 's',
|
|
34
|
+
[commonStyles.scrollContainerM]: scroll && limitedScrollHeight && size === 'm',
|
|
35
|
+
[commonStyles.scrollContainerL]: scroll && limitedScrollHeight && size === 'l',
|
|
36
|
+
}), barHideStrategy: 'leave', size: size === 's' ? 's' : 'm', ref: scrollContainerRef, children: [content, _jsx("div", { className: styles.scrollStub, ref: scrollRef })] })) : (_jsx(_Fragment, { children: content })), (Number(pinBottom === null || pinBottom === void 0 ? void 0 : pinBottom.length) > 0 || footer) && (_jsxs(PinBottomGroupItem, { children: [Number(pinBottom === null || pinBottom === void 0 ? void 0 : pinBottom.length) > 0 && itemsPinBottomJSX, footer && _jsx("div", { className: styles.footer, children: footer })] }))] }) })));
|
|
37
37
|
if (!nested) {
|
|
38
38
|
return (_jsx(ListContextProvider, { size: size, marker: marker, children: listJSX }));
|
|
39
39
|
}
|
|
@@ -1,37 +1,41 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
2
|
type SelectionSingleValueType = string | number | undefined;
|
|
3
|
-
|
|
3
|
+
type SelectionSingleState = {
|
|
4
4
|
/** Начальное состояние */
|
|
5
5
|
defaultValue?: SelectionSingleValueType;
|
|
6
6
|
/** Controlled состояние */
|
|
7
7
|
value?: SelectionSingleValueType;
|
|
8
8
|
/** Controlled обработчик измения состояния */
|
|
9
9
|
onChange?(value: any): void;
|
|
10
|
-
setValue?(value: any): void;
|
|
11
10
|
/** Режим выбора */
|
|
12
|
-
|
|
11
|
+
mode: 'single';
|
|
12
|
+
};
|
|
13
|
+
export type SelectionSingleProps = {
|
|
14
|
+
setValue?(value: any): void;
|
|
13
15
|
/** Режим выбора single */
|
|
14
16
|
isSelectionSingle: true;
|
|
15
17
|
/** Режим выбора multi */
|
|
16
18
|
isSelectionMultiple: false;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
+
} & SelectionSingleState;
|
|
20
|
+
type SelectionMultipleState = {
|
|
19
21
|
/** Начальное состояние */
|
|
20
22
|
defaultValue?: SelectionSingleValueType[];
|
|
21
23
|
/** Controlled состояние */
|
|
22
24
|
value?: SelectionSingleValueType[];
|
|
23
25
|
/** Controlled обработчик измения состояния */
|
|
24
26
|
onChange?(value: any): void;
|
|
25
|
-
setValue?(value: any): void;
|
|
26
27
|
/** Режим выбора */
|
|
27
|
-
|
|
28
|
+
mode: 'multiple';
|
|
29
|
+
};
|
|
30
|
+
export type SelectionMultipleProps = {
|
|
31
|
+
setValue?(value: any): void;
|
|
28
32
|
/** Режим выбора single */
|
|
29
33
|
isSelectionSingle: false;
|
|
30
34
|
/** Режим выбора multi */
|
|
31
35
|
isSelectionMultiple: true;
|
|
32
|
-
};
|
|
36
|
+
} & SelectionMultipleState;
|
|
33
37
|
type SelectionNoneProps = {
|
|
34
|
-
|
|
38
|
+
mode?: undefined;
|
|
35
39
|
value?: undefined;
|
|
36
40
|
onChange?: undefined;
|
|
37
41
|
setValue?: undefined;
|
|
@@ -39,15 +43,18 @@ type SelectionNoneProps = {
|
|
|
39
43
|
isSelectionSingle?: undefined;
|
|
40
44
|
isSelectionMultiple?: undefined;
|
|
41
45
|
};
|
|
42
|
-
|
|
46
|
+
type SelectionProviderProps = SelectionSingleProps | SelectionMultipleProps | SelectionNoneProps;
|
|
43
47
|
type SelectionContextType = Omit<SelectionNoneProps, 'defaultValue'> | Omit<SelectionSingleProps, 'defaultValue'> | Omit<SelectionMultipleProps, 'defaultValue'>;
|
|
48
|
+
export type SelectionState = {
|
|
49
|
+
selection?: SelectionSingleState | SelectionMultipleState;
|
|
50
|
+
};
|
|
44
51
|
export declare const SelectionContext: import("react").Context<SelectionContextType>;
|
|
45
52
|
export declare function isSelectionMultipleProps(props: any): props is SelectionMultipleProps;
|
|
46
53
|
export declare function isSelectionSingleProps(props: any): props is SelectionSingleProps;
|
|
47
54
|
type Child = {
|
|
48
55
|
children: ReactNode;
|
|
49
56
|
};
|
|
50
|
-
export declare function SelectionProvider(props: SelectionProviderProps & Child): import("react/jsx-runtime").JSX.Element;
|
|
57
|
+
export declare function SelectionProvider({ children, ...props }: SelectionProviderProps & Child): import("react/jsx-runtime").JSX.Element;
|
|
51
58
|
export declare const useSelectionContext: () => SelectionContextType;
|
|
52
|
-
export declare function extractSelectionProps<T extends
|
|
59
|
+
export declare function extractSelectionProps<T extends SelectionState>({ selection }: T): SelectionProviderProps;
|
|
53
60
|
export {};
|