@snack-uikit/list 0.19.0 → 0.21.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 +23 -0
- package/README.md +18 -2
- package/dist/cjs/components/Items/BaseItem/BaseItem.js +4 -2
- package/dist/cjs/components/Items/NextListItem/NextListItem.js +4 -3
- package/dist/cjs/components/Items/SearchItem/SearchItem.js +4 -2
- package/dist/cjs/components/Items/hooks.js +11 -9
- package/dist/cjs/components/Items/utils.js +5 -3
- package/dist/cjs/components/Lists/Droplist/DropList.js +12 -7
- package/dist/cjs/components/Lists/List/List.d.ts +7 -1
- package/dist/cjs/components/Lists/List/List.js +19 -11
- package/dist/cjs/components/Lists/ListPrivate/ListPrivate.d.ts +3 -2
- package/dist/cjs/components/Lists/ListPrivate/ListPrivate.js +17 -3
- package/dist/cjs/components/Lists/contexts/NewListProvider.d.ts +2 -4
- package/dist/cjs/components/Lists/contexts/NewListProvider.js +7 -3
- package/dist/cjs/components/Lists/hooks.d.ts +6 -1
- package/dist/cjs/components/Lists/hooks.js +32 -18
- package/dist/cjs/components/Lists/types.d.ts +12 -3
- package/dist/cjs/constants.d.ts +8 -0
- package/dist/cjs/constants.js +14 -0
- package/dist/cjs/index.d.ts +2 -0
- package/dist/cjs/index.js +3 -1
- package/dist/cjs/utils.d.ts +16 -0
- package/dist/cjs/utils.js +25 -0
- package/dist/esm/components/Items/BaseItem/BaseItem.js +3 -3
- package/dist/esm/components/Items/NextListItem/NextListItem.js +2 -1
- package/dist/esm/components/Items/SearchItem/SearchItem.js +3 -2
- package/dist/esm/components/Items/hooks.js +6 -4
- package/dist/esm/components/Items/utils.js +5 -3
- package/dist/esm/components/Lists/Droplist/DropList.js +20 -4
- package/dist/esm/components/Lists/List/List.d.ts +7 -1
- package/dist/esm/components/Lists/List/List.js +23 -6
- package/dist/esm/components/Lists/ListPrivate/ListPrivate.d.ts +3 -2
- package/dist/esm/components/Lists/ListPrivate/ListPrivate.js +12 -4
- package/dist/esm/components/Lists/contexts/NewListProvider.d.ts +2 -4
- package/dist/esm/components/Lists/contexts/NewListProvider.js +4 -2
- package/dist/esm/components/Lists/hooks.d.ts +6 -1
- package/dist/esm/components/Lists/hooks.js +29 -19
- package/dist/esm/components/Lists/types.d.ts +12 -3
- package/dist/esm/constants.d.ts +8 -0
- package/dist/esm/constants.js +8 -0
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/utils.d.ts +16 -0
- package/dist/esm/utils.js +16 -0
- package/package.json +12 -12
- package/src/components/Items/BaseItem/BaseItem.tsx +4 -4
- package/src/components/Items/BaseItem/styles.module.scss +21 -21
- package/src/components/Items/NextListItem/NextListItem.tsx +2 -1
- package/src/components/Items/PinGroupItem/styles.module.scss +3 -4
- package/src/components/Items/SearchItem/SearchItem.tsx +3 -2
- package/src/components/Items/SearchItem/styles.module.scss +3 -3
- package/src/components/Items/hooks.tsx +6 -4
- package/src/components/Items/styles.module.scss +9 -9
- package/src/components/Items/utils.ts +5 -3
- package/src/components/Lists/Droplist/DropList.tsx +20 -3
- package/src/components/Lists/List/List.tsx +25 -5
- package/src/components/Lists/ListPrivate/ListPrivate.tsx +13 -3
- package/src/components/Lists/ListPrivate/styles.module.scss +11 -11
- package/src/components/Lists/contexts/NewListProvider.tsx +6 -2
- package/src/components/Lists/hooks.ts +35 -20
- package/src/components/Lists/styles.module.scss +8 -8
- package/src/components/Lists/types.ts +11 -3
- package/src/constants.ts +8 -0
- package/src/helperComponents/CollapseBlockPrivate/styles.module.scss +3 -3
- package/src/helperComponents/ItemContent/styles.module.scss +21 -22
- package/src/helperComponents/ListEmptyState/styles.module.scss +2 -2
- package/src/helperComponents/Separator/styles.module.scss +20 -22
- package/src/index.ts +3 -0
- package/src/utils.ts +20 -0
|
@@ -29,6 +29,10 @@ export type ListProps = WithSupportProps<{
|
|
|
29
29
|
footer?: ReactNode;
|
|
30
30
|
/** Список ссылок на кастомные элементы, помещенные в специальную секцию внизу списка */
|
|
31
31
|
footerActiveElementsRefs?: RefObject<HTMLElement>[];
|
|
32
|
+
/** Ссылка на управление навигацией листа с клавиатуры */
|
|
33
|
+
keyboardNavigationRef?: RefObject<{
|
|
34
|
+
focusItem(id: ItemId): void;
|
|
35
|
+
}>;
|
|
32
36
|
/** Настройки поисковой строки */
|
|
33
37
|
search?: SearchState;
|
|
34
38
|
/** Tab Index */
|
|
@@ -37,9 +41,14 @@ export type ListProps = WithSupportProps<{
|
|
|
37
41
|
collapse?: CollapseState;
|
|
38
42
|
/** CSS-класс */
|
|
39
43
|
className?: string;
|
|
40
|
-
onKeyDown?(e: KeyboardEvent<HTMLElement>): void;
|
|
41
44
|
/** Флаг, отвещающий за состояние загрузки списка */
|
|
42
45
|
loading?: boolean;
|
|
46
|
+
/** Обработчик события по нажатию клавиш */
|
|
47
|
+
onKeyDown?(e: KeyboardEvent<HTMLElement>): void;
|
|
48
|
+
/** Флаг, отвещающий за включение самого родительского контейнера листа в цепочку фокусирующихся элементов */
|
|
49
|
+
hasListInFocusChain?: boolean;
|
|
50
|
+
/** Флаг, отвещающий за прокручивание до выбранного элемента */
|
|
51
|
+
scrollToSelectedItem?: boolean;
|
|
43
52
|
} & SelectionState & PublicListContextType & ScrollProps & EmptyState>;
|
|
44
53
|
export type DroplistProps = {
|
|
45
54
|
/** Ссылка на элемент-триггер для дроплиста */
|
|
@@ -62,8 +71,8 @@ export type DroplistProps = {
|
|
|
62
71
|
children: ReactNode | ((params: {
|
|
63
72
|
onKeyDown?: (e: KeyboardEvent<HTMLElement>) => void;
|
|
64
73
|
}) => ReactNode);
|
|
65
|
-
} & Pick<DropdownProps, 'trigger' | 'placement' | 'widthStrategy' | 'open' | 'onOpenChange' | 'triggerClassName'> & Omit<ListProps, 'tabIndex' | 'onKeyDown'>;
|
|
66
|
-
export type ListPrivateProps = Omit<ListProps, 'pinTop' | 'pinBottom' | 'items'> & {
|
|
74
|
+
} & Pick<DropdownProps, 'trigger' | 'placement' | 'widthStrategy' | 'open' | 'onOpenChange' | 'triggerClassName'> & Omit<ListProps, 'tabIndex' | 'onKeyDown' | 'hasListInFocusChain' | 'keyboardNavigationRef'>;
|
|
75
|
+
export type ListPrivateProps = Omit<ListProps, 'pinTop' | 'pinBottom' | 'items' | 'hasListInFocusChain'> & {
|
|
67
76
|
nested?: boolean;
|
|
68
77
|
active?: boolean;
|
|
69
78
|
tabIndex?: number;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.ITEM_PREFIXES = void 0;
|
|
7
|
+
exports.ITEM_PREFIXES = {
|
|
8
|
+
default: '~main',
|
|
9
|
+
pinTop: '~pinTop',
|
|
10
|
+
pinBottom: '~pinBottom',
|
|
11
|
+
footer: '~footer',
|
|
12
|
+
search: '~search',
|
|
13
|
+
dropFocus: '~dropFocus'
|
|
14
|
+
};
|
package/dist/cjs/index.d.ts
CHANGED
package/dist/cjs/index.js
CHANGED
|
@@ -31,4 +31,6 @@ Object.defineProperty(exports, "kindFlattenItems", {
|
|
|
31
31
|
return Items_1.kindFlattenItems;
|
|
32
32
|
}
|
|
33
33
|
});
|
|
34
|
-
__exportStar(require("./helperComponents/ItemContent"), exports);
|
|
34
|
+
__exportStar(require("./helperComponents/ItemContent"), exports);
|
|
35
|
+
__exportStar(require("./utils"), exports);
|
|
36
|
+
__exportStar(require("./constants"), exports);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ItemId } from './components';
|
|
2
|
+
/**
|
|
3
|
+
* Возвращает id для элемента футера
|
|
4
|
+
* @function helper
|
|
5
|
+
*/
|
|
6
|
+
export declare const getFooterItemId: (id: ItemId) => string;
|
|
7
|
+
/**
|
|
8
|
+
* Возвращает id для элемента, подставляя перфикс
|
|
9
|
+
* @function helper
|
|
10
|
+
*/
|
|
11
|
+
export declare const getItemAutoId: (prefix: ItemId, id: ItemId) => string;
|
|
12
|
+
/**
|
|
13
|
+
* Возвращает id для дефолтного элемента
|
|
14
|
+
* @function helper
|
|
15
|
+
*/
|
|
16
|
+
export declare const getDefaultItemId: (id: ItemId) => string;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.getDefaultItemId = exports.getItemAutoId = exports.getFooterItemId = void 0;
|
|
7
|
+
const constants_1 = require("./constants");
|
|
8
|
+
/**
|
|
9
|
+
* Возвращает id для элемента футера
|
|
10
|
+
* @function helper
|
|
11
|
+
*/
|
|
12
|
+
const getFooterItemId = id => `${constants_1.ITEM_PREFIXES.footer}__${id}`;
|
|
13
|
+
exports.getFooterItemId = getFooterItemId;
|
|
14
|
+
/**
|
|
15
|
+
* Возвращает id для элемента, подставляя перфикс
|
|
16
|
+
* @function helper
|
|
17
|
+
*/
|
|
18
|
+
const getItemAutoId = (prefix, id) => [prefix, id].join('-');
|
|
19
|
+
exports.getItemAutoId = getItemAutoId;
|
|
20
|
+
/**
|
|
21
|
+
* Возвращает id для дефолтного элемента
|
|
22
|
+
* @function helper
|
|
23
|
+
*/
|
|
24
|
+
const getDefaultItemId = id => (0, exports.getItemAutoId)(constants_1.ITEM_PREFIXES.default, id);
|
|
25
|
+
exports.getDefaultItemId = getDefaultItemId;
|
|
@@ -23,11 +23,11 @@ export function BaseItem(_a) {
|
|
|
23
23
|
var _b;
|
|
24
24
|
var { beforeContent, afterContent, content, onClick, onMouseDown, id, expandIcon, disabled, open, itemRef, switch: switchProp, onKeyDown, onFocus, indeterminate, checked: checkedProp, onSelect, onOpenNestedList, isParentNode, className, inactive, itemWrapRender } = _a, rest = __rest(_a, ["beforeContent", "afterContent", "content", "onClick", "onMouseDown", "id", "expandIcon", "disabled", "open", "itemRef", "switch", "onKeyDown", "onFocus", "indeterminate", "checked", "onSelect", "onOpenNestedList", "isParentNode", "className", "inactive", "itemWrapRender"]);
|
|
25
25
|
const interactive = !inactive;
|
|
26
|
-
const { size = 's', marker, contentRender } = useNewListContext();
|
|
26
|
+
const { size = 's', marker, contentRender, firstItemId, focusFlattenItems } = useNewListContext();
|
|
27
27
|
const { level = 0 } = useCollapseLevelContext();
|
|
28
28
|
const { closeDroplist, closeDroplistOnItemClick } = useOpenListContext();
|
|
29
29
|
const { value, onChange, mode, isSelectionSingle, isSelectionMultiple } = useSelectionContext();
|
|
30
|
-
const isChecked = isSelectionSingle ? checkedProp !== null && checkedProp !== void 0 ? checkedProp : value === id : checkedProp !== null && checkedProp !== void 0 ? checkedProp : value === null || value === void 0 ? void 0 : value.includes(id !== null && id !== void 0 ? id : '');
|
|
30
|
+
const isChecked = isSelectionSingle ? (checkedProp !== null && checkedProp !== void 0 ? checkedProp : value === id) : (checkedProp !== null && checkedProp !== void 0 ? checkedProp : value === null || value === void 0 ? void 0 : value.includes(id !== null && id !== void 0 ? id : ''));
|
|
31
31
|
const handleChange = () => {
|
|
32
32
|
onChange === null || onChange === void 0 ? void 0 : onChange(id);
|
|
33
33
|
};
|
|
@@ -86,7 +86,7 @@ export function BaseItem(_a) {
|
|
|
86
86
|
}
|
|
87
87
|
};
|
|
88
88
|
const props = extractSupportProps(rest);
|
|
89
|
-
const itemJSX = (_jsx("div", { className: cn(commonStyles.itemWrapper, styles.innerWrapper, className), "data-inactive": inactive || undefined, "data-disabled": disabled || undefined, "data-variant": mode || undefined, "data-checked": (isParentNode && (indeterminate || isChecked)) || (isChecked && !switchProp) || undefined, children: _jsxs("li", { "data-type": 'outside', role: 'menuitem', "data-test-id": props['data-test-id'] || 'list__base-item_' + id, "data-checked": (isParentNode && (indeterminate || isChecked)) || (isChecked && !switchProp) || undefined, ref: itemRef, className: cn(commonStyles.listItem, styles.droplistItem), "data-size": size, onClick: handleItemClick, onMouseDown: handleItemMouseDown, tabIndex: -1, "data-non-pointer": inactive && !onClick, "data-variant": mode || undefined, "data-open": open || undefined, 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: isParentNode ? handleCheckboxChange : undefined, checked: isChecked, "data-test-id": 'list__base-item-checkbox', onClick: handleCheckboxClick, indeterminate: indeterminate }) })), beforeContent && _jsx("div", { className: styles.beforeContent, children: beforeContent }), content && isContentItem(content) ? ((_b = contentRender === null || contentRender === void 0 ? void 0 : contentRender({ id, content, disabled })) !== null && _b !== void 0 ? _b : _jsx(ItemContent, Object.assign({ disabled: disabled }, content))) : (_jsxs("div", { className: styles.content, children: [" ", content, " "] })), 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 })] }) }));
|
|
89
|
+
const itemJSX = (_jsx("div", { className: cn(commonStyles.itemWrapper, styles.innerWrapper, className), "data-inactive": inactive || undefined, "data-disabled": disabled || undefined, "data-variant": mode || undefined, "data-checked": (isParentNode && (indeterminate || isChecked)) || (isChecked && !switchProp) || undefined, children: _jsxs("li", { "data-type": 'outside', role: 'menuitem', "data-test-id": props['data-test-id'] || 'list__base-item_' + id, "data-checked": (isParentNode && (indeterminate || isChecked)) || (isChecked && !switchProp) || undefined, ref: itemRef, className: cn(commonStyles.listItem, styles.droplistItem), "data-size": size, onClick: handleItemClick, onMouseDown: handleItemMouseDown, tabIndex: id === focusFlattenItems[firstItemId].originalId ? 0 : -1, "data-non-pointer": inactive && !onClick, "data-variant": mode || undefined, "data-open": open || undefined, 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: isParentNode ? handleCheckboxChange : undefined, checked: isChecked, "data-test-id": 'list__base-item-checkbox', onClick: handleCheckboxClick, indeterminate: indeterminate }) })), beforeContent && _jsx("div", { className: styles.beforeContent, children: beforeContent }), content && isContentItem(content) ? (((_b = contentRender === null || contentRender === void 0 ? void 0 : contentRender({ id, content, disabled })) !== null && _b !== void 0 ? _b : _jsx(ItemContent, Object.assign({ disabled: disabled }, content)))) : (_jsxs("div", { className: styles.content, children: [" ", content, " "] })), 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 })] }) }));
|
|
90
90
|
if (!itemWrapRender) {
|
|
91
91
|
return itemJSX;
|
|
92
92
|
}
|
|
@@ -13,6 +13,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
13
13
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
14
14
|
import { Dropdown } from '@snack-uikit/dropdown';
|
|
15
15
|
import { ChevronRightSVG } from '@snack-uikit/icons';
|
|
16
|
+
import { ITEM_PREFIXES } from '../../../constants';
|
|
16
17
|
import { useCollapseContext, useFocusListContext, useNewListContext, useSelectionContext } from '../../Lists/contexts';
|
|
17
18
|
import { ListPrivate } from '../../Lists/ListPrivate';
|
|
18
19
|
import { BaseItem } from '../BaseItem';
|
|
@@ -52,7 +53,7 @@ export function NextListItem(_a) {
|
|
|
52
53
|
allChildIds,
|
|
53
54
|
});
|
|
54
55
|
const handleOutsideClick = useCallback(() => {
|
|
55
|
-
forceUpdateActiveItemId === null || forceUpdateActiveItemId === void 0 ? void 0 : forceUpdateActiveItemId(
|
|
56
|
+
forceUpdateActiveItemId === null || forceUpdateActiveItemId === void 0 ? void 0 : forceUpdateActiveItemId(ITEM_PREFIXES.dropFocus);
|
|
56
57
|
setOpen(false);
|
|
57
58
|
return true;
|
|
58
59
|
}, [forceUpdateActiveItemId]);
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import cn from 'classnames';
|
|
3
3
|
import { SearchPrivate } from '@snack-uikit/search-private';
|
|
4
|
+
import { ITEM_PREFIXES } from '../../../constants';
|
|
4
5
|
import { useNewListContext } from '../../Lists/contexts';
|
|
5
6
|
import commonStyles from '../styles.module.css';
|
|
6
7
|
import styles from './styles.module.css';
|
|
7
8
|
export function SearchItem({ search, itemRef }) {
|
|
8
|
-
const { size = 's' } = useNewListContext();
|
|
9
|
+
const { size = 's', firstItemId } = useNewListContext();
|
|
9
10
|
const handleKeyDown = (e) => {
|
|
10
11
|
if (['ArrowDown', 'ArrowUp'].includes(e.key)) {
|
|
11
12
|
e.preventDefault();
|
|
@@ -17,5 +18,5 @@ export function SearchItem({ search, itemRef }) {
|
|
|
17
18
|
if (!Boolean(search)) {
|
|
18
19
|
return null;
|
|
19
20
|
}
|
|
20
|
-
return (_jsx("div", { className: cn(commonStyles.listItem, styles.searchItem), "data-size": size, "data-test-id": 'list__search-item', children: _jsx(SearchPrivate, Object.assign({ size: size, tabIndex: -1, onKeyDown: handleKeyDown, onFocus: handleFocus }, search, { ref: itemRef })) }));
|
|
21
|
+
return (_jsx("div", { className: cn(commonStyles.listItem, styles.searchItem), "data-size": size, "data-test-id": 'list__search-item', children: _jsx(SearchPrivate, Object.assign({ size: size, tabIndex: ITEM_PREFIXES.search === firstItemId ? 0 : -1, onKeyDown: handleKeyDown, onFocus: handleFocus }, search, { ref: itemRef })) }));
|
|
21
22
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { createElement as _createElement } from "react";
|
|
2
2
|
import { createRef, useMemo } from 'react';
|
|
3
|
+
import { ITEM_PREFIXES } from '../../constants';
|
|
4
|
+
import { getFooterItemId } from '../../utils';
|
|
3
5
|
import { useNewListContext, useSelectionContext } from '../Lists/contexts';
|
|
4
6
|
import { AccordionItem } from './AccordionItem';
|
|
5
7
|
import { BaseItem } from './BaseItem';
|
|
@@ -40,15 +42,15 @@ export function useCreateBaseItems({ footerActiveElementsRefs }) {
|
|
|
40
42
|
return ({
|
|
41
43
|
searchItem: {
|
|
42
44
|
itemRef: createRef(),
|
|
43
|
-
id:
|
|
44
|
-
parentId:
|
|
45
|
+
id: ITEM_PREFIXES.search,
|
|
46
|
+
parentId: ITEM_PREFIXES.default,
|
|
45
47
|
items: [],
|
|
46
48
|
allChildIds: [],
|
|
47
49
|
},
|
|
48
50
|
footerItems: (_a = footerActiveElementsRefs === null || footerActiveElementsRefs === void 0 ? void 0 : footerActiveElementsRefs.map((itemRef, idx) => ({
|
|
49
|
-
id:
|
|
51
|
+
id: getFooterItemId(idx),
|
|
50
52
|
itemRef,
|
|
51
|
-
parentId:
|
|
53
|
+
parentId: ITEM_PREFIXES.default,
|
|
52
54
|
items: [],
|
|
53
55
|
allChildIds: [],
|
|
54
56
|
}))) !== null && _a !== void 0 ? _a : [],
|
|
@@ -10,6 +10,8 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
10
10
|
return t;
|
|
11
11
|
};
|
|
12
12
|
import { createRef } from 'react';
|
|
13
|
+
import { ITEM_PREFIXES } from '../../constants';
|
|
14
|
+
import { getItemAutoId } from '../../utils';
|
|
13
15
|
export function isBaseItem(item) {
|
|
14
16
|
return item && !('items' in item);
|
|
15
17
|
}
|
|
@@ -35,9 +37,9 @@ export const isGroupItemProps = isGroupItem;
|
|
|
35
37
|
export function kindFlattenItems({ items, prefix, parentId }) {
|
|
36
38
|
const flattenItems = {};
|
|
37
39
|
const focusFlattenItems = {};
|
|
38
|
-
function flatten({ item, idx, prefix, parentId =
|
|
40
|
+
function flatten({ item, idx, prefix, parentId = ITEM_PREFIXES.default }) {
|
|
39
41
|
var _a, _b;
|
|
40
|
-
const autoId = prefix !== undefined ?
|
|
42
|
+
const autoId = prefix !== undefined ? getItemAutoId(prefix, idx) : String(idx);
|
|
41
43
|
const itemId = (_a = (!isGroupItem(item) ? item.id : undefined)) !== null && _a !== void 0 ? _a : autoId;
|
|
42
44
|
if (isBaseItem(item)) {
|
|
43
45
|
flattenItems[itemId] = Object.assign(Object.assign({}, item), { items: [], allChildIds: [], id: itemId });
|
|
@@ -58,7 +60,7 @@ export function kindFlattenItems({ items, prefix, parentId }) {
|
|
|
58
60
|
const closeChildIds = [];
|
|
59
61
|
const autoChildIds = [];
|
|
60
62
|
const { items } = item, rest = __rest(item, ["items"]);
|
|
61
|
-
const childActiveParent = isGroupItem(item) ? parentId !== null && parentId !== void 0 ? parentId :
|
|
63
|
+
const childActiveParent = isGroupItem(item) ? parentId !== null && parentId !== void 0 ? parentId : ITEM_PREFIXES.default : autoId;
|
|
62
64
|
const filteredItems = items.filter(item => !item.hidden);
|
|
63
65
|
for (let idx = 0; idx < filteredItems.length; idx++) {
|
|
64
66
|
const { id, children, autoId, focusChildren } = flatten({
|
|
@@ -15,6 +15,7 @@ import mergeRefs from 'merge-refs';
|
|
|
15
15
|
import { cloneElement, isValidElement, useCallback, useMemo, useRef } from 'react';
|
|
16
16
|
import { Dropdown } from '@snack-uikit/dropdown';
|
|
17
17
|
import { useValueControl } from '@snack-uikit/utils';
|
|
18
|
+
import { ITEM_PREFIXES } from '../../../constants';
|
|
18
19
|
import { extractActiveItems, kindFlattenItems, useCreateBaseItems } from '../../Items';
|
|
19
20
|
import { CollapseContext, FocusListContext, NewListContextProvider, OpenListContext, SelectionProvider, } from '../contexts';
|
|
20
21
|
import { useNewKeyboardNavigation } from '../hooks';
|
|
@@ -36,9 +37,21 @@ export function Droplist(_a) {
|
|
|
36
37
|
* Объект с пропсами всех вложенных айтемов; ключ id
|
|
37
38
|
*/
|
|
38
39
|
const _b = useMemo(() => {
|
|
39
|
-
const pinTop = kindFlattenItems({
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
const pinTop = kindFlattenItems({
|
|
41
|
+
items: pinTopProp,
|
|
42
|
+
prefix: ITEM_PREFIXES.pinTop,
|
|
43
|
+
parentId: ITEM_PREFIXES.default,
|
|
44
|
+
});
|
|
45
|
+
const items = kindFlattenItems({
|
|
46
|
+
items: itemsProp,
|
|
47
|
+
prefix: ITEM_PREFIXES.default,
|
|
48
|
+
parentId: ITEM_PREFIXES.default,
|
|
49
|
+
});
|
|
50
|
+
const pinBottom = kindFlattenItems({
|
|
51
|
+
items: pinBottomProp,
|
|
52
|
+
prefix: ITEM_PREFIXES.pinBottom,
|
|
53
|
+
parentId: ITEM_PREFIXES.default,
|
|
54
|
+
});
|
|
42
55
|
const flattenItems = Object.assign(Object.assign(Object.assign({}, pinTop.flattenItems), pinBottom.flattenItems), items.flattenItems);
|
|
43
56
|
const focusFlattenItems = Object.assign(Object.assign(Object.assign({}, pinTop.focusFlattenItems), pinBottom.focusFlattenItems), items.focusFlattenItems);
|
|
44
57
|
[...footerItems, searchItem].forEach(item => {
|
|
@@ -74,9 +87,12 @@ export function Droplist(_a) {
|
|
|
74
87
|
}, [footerItems, hasSearch, memorizedItems, openCollapseItems, searchItem.id, selection === null || selection === void 0 ? void 0 : selection.mode]);
|
|
75
88
|
const triggerElemRef = useRef(null);
|
|
76
89
|
const listRef = useRef(null);
|
|
90
|
+
const firstItemId = ids[0];
|
|
77
91
|
const { handleListKeyDownFactory, resetActiveItemId, activeItemId, forceUpdateActiveItemId } = useNewKeyboardNavigation({
|
|
78
92
|
mainRef: triggerElemRefProp !== null && triggerElemRefProp !== void 0 ? triggerElemRefProp : triggerElemRef,
|
|
79
93
|
focusFlattenItems,
|
|
94
|
+
hasListInFocusChain: true,
|
|
95
|
+
firstItemId,
|
|
80
96
|
});
|
|
81
97
|
const handleListKeyDown = useCallback((e) => handleListKeyDownFactory(ids, expandedIds)(e), [handleListKeyDownFactory, ids, expandedIds]);
|
|
82
98
|
const handleOpenChange = useCallback((open) => {
|
|
@@ -112,7 +128,7 @@ export function Droplist(_a) {
|
|
|
112
128
|
}
|
|
113
129
|
return children;
|
|
114
130
|
}, [onKeyDown, children]);
|
|
115
|
-
return (_jsx(NewListContextProvider, { flattenItems: flattenItems, focusFlattenItems: focusFlattenItems, contentRender: contentRender, size: size, marker: marker, children: _jsx(SelectionProvider, Object.assign({}, selection, { children: _jsx(CollapseContext.Provider, { value: {
|
|
131
|
+
return (_jsx(NewListContextProvider, { flattenItems: flattenItems, focusFlattenItems: focusFlattenItems, contentRender: contentRender, size: size, marker: marker, firstItemId: firstItemId, children: _jsx(SelectionProvider, Object.assign({}, selection, { children: _jsx(CollapseContext.Provider, { value: {
|
|
116
132
|
openCollapseItems,
|
|
117
133
|
toggleOpenCollapseItem,
|
|
118
134
|
}, children: _jsx(FocusListContext.Provider, { value: {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { KeyboardEvent } from 'react';
|
|
2
|
+
import { ItemId } from '../../Items';
|
|
2
3
|
export declare const List: import("react").ForwardRefExoticComponent<{
|
|
3
4
|
'data-test-id'?: string;
|
|
4
5
|
} & import("react").AriaAttributes & {
|
|
@@ -7,10 +8,15 @@ export declare const List: import("react").ForwardRefExoticComponent<{
|
|
|
7
8
|
pinBottom?: import("../../Items").Item[];
|
|
8
9
|
footer?: import("react").ReactNode;
|
|
9
10
|
footerActiveElementsRefs?: import("react").RefObject<HTMLElement>[];
|
|
11
|
+
keyboardNavigationRef?: import("react").RefObject<{
|
|
12
|
+
focusItem(id: ItemId): void;
|
|
13
|
+
}>;
|
|
10
14
|
search?: import("../../..").SearchState;
|
|
11
15
|
tabIndex?: number;
|
|
12
16
|
collapse?: import("../contexts").CollapseState;
|
|
13
17
|
className?: string;
|
|
14
|
-
onKeyDown?(e: KeyboardEvent<HTMLElement>): void;
|
|
15
18
|
loading?: boolean;
|
|
19
|
+
onKeyDown?(e: KeyboardEvent<HTMLElement>): void;
|
|
20
|
+
hasListInFocusChain?: boolean;
|
|
21
|
+
scrollToSelectedItem?: boolean;
|
|
16
22
|
} & import("../contexts").SelectionState & import("../contexts").PublicListContextType & import("../../../types").ScrollProps & import("../types").EmptyState & import("react").RefAttributes<HTMLElement>>;
|
|
@@ -14,6 +14,7 @@ import cn from 'classnames';
|
|
|
14
14
|
import mergeRefs from 'merge-refs';
|
|
15
15
|
import { forwardRef, useCallback, useMemo, useRef } from 'react';
|
|
16
16
|
import { isBrowser, useValueControl } from '@snack-uikit/utils';
|
|
17
|
+
import { ITEM_PREFIXES } from '../../../constants';
|
|
17
18
|
import { HiddenTabButton } from '../../../helperComponents';
|
|
18
19
|
import { extractActiveItems, kindFlattenItems, useCreateBaseItems } from '../../Items';
|
|
19
20
|
import { CollapseContext, FocusListContext, NewListContextProvider, SelectionProvider } from '../contexts';
|
|
@@ -21,7 +22,7 @@ import { useNewKeyboardNavigation } from '../hooks';
|
|
|
21
22
|
import { ListPrivate } from '../ListPrivate';
|
|
22
23
|
import styles from '../styles.module.css';
|
|
23
24
|
export const List = forwardRef((_a, ref) => {
|
|
24
|
-
var { items: itemsProp = [], search, pinBottom: pinBottomProp = [], pinTop: pinTopProp = [], footerActiveElementsRefs, onKeyDown, tabIndex = 0, className, collapse = {}, selection, contentRender, size = 's', marker = true } = _a, props = __rest(_a, ["items", "search", "pinBottom", "pinTop", "footerActiveElementsRefs", "onKeyDown", "tabIndex", "className", "collapse", "selection", "contentRender", "size", "marker"]);
|
|
25
|
+
var { items: itemsProp = [], search, pinBottom: pinBottomProp = [], pinTop: pinTopProp = [], footerActiveElementsRefs, onKeyDown, tabIndex = 0, className, collapse = {}, selection, contentRender, size = 's', marker = true, keyboardNavigationRef, hasListInFocusChain = true } = _a, props = __rest(_a, ["items", "search", "pinBottom", "pinTop", "footerActiveElementsRefs", "onKeyDown", "tabIndex", "className", "collapse", "selection", "contentRender", "size", "marker", "keyboardNavigationRef", "hasListInFocusChain"]);
|
|
25
26
|
const hasSearch = useMemo(() => Boolean(search), [search]);
|
|
26
27
|
const [openCollapseItems = [], setOpenCollapsedItems] = useValueControl(collapse);
|
|
27
28
|
const toggleOpenCollapseItem = useCallback((id) => setOpenCollapsedItems((items) => (items === null || items === void 0 ? void 0 : items.includes(id)) ? items.filter(item => item !== id) : (items !== null && items !== void 0 ? items : []).concat([id])), [setOpenCollapsedItems]);
|
|
@@ -30,9 +31,21 @@ export const List = forwardRef((_a, ref) => {
|
|
|
30
31
|
* Объект с пропсами всех вложенных айтемов; ключ id
|
|
31
32
|
*/
|
|
32
33
|
const _b = useMemo(() => {
|
|
33
|
-
const pinTop = kindFlattenItems({
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
const pinTop = kindFlattenItems({
|
|
35
|
+
items: pinTopProp,
|
|
36
|
+
prefix: ITEM_PREFIXES.pinTop,
|
|
37
|
+
parentId: ITEM_PREFIXES.default,
|
|
38
|
+
});
|
|
39
|
+
const items = kindFlattenItems({
|
|
40
|
+
items: itemsProp,
|
|
41
|
+
prefix: ITEM_PREFIXES.default,
|
|
42
|
+
parentId: ITEM_PREFIXES.default,
|
|
43
|
+
});
|
|
44
|
+
const pinBottom = kindFlattenItems({
|
|
45
|
+
items: pinBottomProp,
|
|
46
|
+
prefix: ITEM_PREFIXES.pinBottom,
|
|
47
|
+
parentId: ITEM_PREFIXES.default,
|
|
48
|
+
});
|
|
36
49
|
const flattenItems = Object.assign(Object.assign(Object.assign({}, pinTop.flattenItems), pinBottom.flattenItems), items.flattenItems);
|
|
37
50
|
const focusFlattenItems = Object.assign(Object.assign(Object.assign({}, pinTop.focusFlattenItems), pinBottom.focusFlattenItems), items.focusFlattenItems);
|
|
38
51
|
[...footerItems, searchItem].forEach(item => {
|
|
@@ -68,10 +81,14 @@ export const List = forwardRef((_a, ref) => {
|
|
|
68
81
|
}, [footerItems, hasSearch, memorizedItems, openCollapseItems, searchItem.id, selection === null || selection === void 0 ? void 0 : selection.mode]);
|
|
69
82
|
const listRef = useRef(null);
|
|
70
83
|
const btnRef = useRef(null);
|
|
84
|
+
const firstItemId = ids[0];
|
|
71
85
|
const { handleListKeyDownFactory, activeItemId, resetActiveItemId, forceUpdateActiveItemId } = useNewKeyboardNavigation({
|
|
72
86
|
mainRef: listRef,
|
|
73
87
|
btnRef,
|
|
74
88
|
focusFlattenItems,
|
|
89
|
+
keyboardNavigationRef,
|
|
90
|
+
hasListInFocusChain,
|
|
91
|
+
firstItemId,
|
|
75
92
|
});
|
|
76
93
|
const handleListKeyDown = useCallback((e) => handleListKeyDownFactory(ids, expandedIds)(e), [handleListKeyDownFactory, ids, expandedIds]);
|
|
77
94
|
const isActive = isBrowser() && listRef.current === document.activeElement && activeItemId === undefined;
|
|
@@ -82,12 +99,12 @@ export const List = forwardRef((_a, ref) => {
|
|
|
82
99
|
const handleOnFocus = () => {
|
|
83
100
|
resetActiveItemId();
|
|
84
101
|
};
|
|
85
|
-
return (_jsx(NewListContextProvider, { flattenItems: flattenItems, focusFlattenItems: focusFlattenItems, contentRender: contentRender, size: size, marker: marker, children: _jsx(SelectionProvider, Object.assign({}, selection, { children: _jsx(CollapseContext.Provider, { value: {
|
|
102
|
+
return (_jsx(NewListContextProvider, { flattenItems: flattenItems, focusFlattenItems: focusFlattenItems, contentRender: contentRender, size: size, marker: marker, firstItemId: firstItemId, children: _jsx(SelectionProvider, Object.assign({}, selection, { children: _jsx(CollapseContext.Provider, { value: {
|
|
86
103
|
openCollapseItems,
|
|
87
104
|
toggleOpenCollapseItem,
|
|
88
105
|
}, children: _jsx(FocusListContext.Provider, { value: {
|
|
89
106
|
activeItemId,
|
|
90
107
|
handleListKeyDownFactory,
|
|
91
108
|
forceUpdateActiveItemId,
|
|
92
|
-
}, children: _jsxs("div", { className: cn(styles.wrapper, className), "data-active": isActive || undefined, children: [_jsx(ListPrivate, Object.assign({}, props, { items: memorizedItems.items.focusCloseChildIds, pinTop: memorizedItems.pinTop.focusCloseChildIds, pinBottom: memorizedItems.pinBottom.focusCloseChildIds, searchItem: searchItem, ref: mergeRefs(ref, listRef), onFocus: handleOnFocus, onKeyDown: mergedHandlerKeyDown, tabIndex: tabIndex, search: search, nested: false })), _jsx(HiddenTabButton, { ref: btnRef, listRef: listRef, tabIndex: tabIndex })] }) }) }) })) }));
|
|
109
|
+
}, children: _jsxs("div", { className: cn(styles.wrapper, className), "data-active": isActive || undefined, children: [_jsx(ListPrivate, Object.assign({}, props, { items: memorizedItems.items.focusCloseChildIds, pinTop: memorizedItems.pinTop.focusCloseChildIds, pinBottom: memorizedItems.pinBottom.focusCloseChildIds, searchItem: searchItem, ref: mergeRefs(ref, listRef), onFocus: handleOnFocus, onKeyDown: mergedHandlerKeyDown, tabIndex: hasListInFocusChain ? tabIndex : undefined, search: search, nested: false })), hasListInFocusChain && _jsx(HiddenTabButton, { ref: btnRef, listRef: listRef, tabIndex: tabIndex })] }) }) }) })) }));
|
|
93
110
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
import { FlattenBaseItem } from '../../Items';
|
|
2
|
+
export declare const ListPrivate: import("react").ForwardRefExoticComponent<Omit<import("../types").ListProps, "items" | "hasListInFocusChain" | "pinTop" | "pinBottom"> & {
|
|
2
3
|
nested?: boolean;
|
|
3
4
|
active?: boolean;
|
|
4
5
|
tabIndex?: number;
|
|
@@ -6,7 +7,7 @@ export declare const ListPrivate: import("react").ForwardRefExoticComponent<Omit
|
|
|
6
7
|
onBlur?(e: import("react").FocusEvent<HTMLElement>): void;
|
|
7
8
|
onKeyDown?(e: import("react").KeyboardEvent<HTMLElement>): void;
|
|
8
9
|
limitedScrollHeight?: boolean;
|
|
9
|
-
searchItem?:
|
|
10
|
+
searchItem?: FlattenBaseItem;
|
|
10
11
|
pinTop?: import("../../Items").ItemId[];
|
|
11
12
|
items: import("../../Items").ItemId[];
|
|
12
13
|
pinBottom?: import("../../Items").ItemId[];
|
|
@@ -17,12 +17,14 @@ import { Scroll } from '@snack-uikit/scroll';
|
|
|
17
17
|
import { extractSupportProps } from '@snack-uikit/utils';
|
|
18
18
|
import { ListEmptyState, useEmptyState } from '../../../helperComponents';
|
|
19
19
|
import { PinBottomGroupItem, PinTopGroupItem, SearchItem, useRenderItems } from '../../Items';
|
|
20
|
-
import { useNewListContext } from '../contexts';
|
|
20
|
+
import { useNewListContext, useSelectionContext } from '../contexts';
|
|
21
21
|
import commonStyles from '../styles.module.css';
|
|
22
22
|
import styles from './styles.module.css';
|
|
23
23
|
export const ListPrivate = forwardRef((_a, ref) => {
|
|
24
|
-
var { items, pinTop, pinBottom, onKeyDown, onBlur, onFocus, tabIndex, active, scroll, nested, search, searchItem, scrollRef, scrollContainerRef, onScroll, footer, loading, limitedScrollHeight, untouchableScrollbars, className, noDataState, noResultsState, errorDataState, dataError, dataFiltered } = _a, props = __rest(_a, ["items", "pinTop", "pinBottom", "onKeyDown", "onBlur", "onFocus", "tabIndex", "active", "scroll", "nested", "search", "searchItem", "scrollRef", "scrollContainerRef", "onScroll", "footer", "loading", "limitedScrollHeight", "untouchableScrollbars", "className", "noDataState", "noResultsState", "errorDataState", "dataError", "dataFiltered"]);
|
|
25
|
-
const { size = 's' } = useNewListContext();
|
|
24
|
+
var { items, pinTop, pinBottom, onKeyDown, onBlur, onFocus, tabIndex, active, scroll, nested, search, searchItem, scrollRef, scrollContainerRef, onScroll, footer, loading, limitedScrollHeight, untouchableScrollbars, className, noDataState, noResultsState, errorDataState, dataError, dataFiltered, scrollToSelectedItem } = _a, props = __rest(_a, ["items", "pinTop", "pinBottom", "onKeyDown", "onBlur", "onFocus", "tabIndex", "active", "scroll", "nested", "search", "searchItem", "scrollRef", "scrollContainerRef", "onScroll", "footer", "loading", "limitedScrollHeight", "untouchableScrollbars", "className", "noDataState", "noResultsState", "errorDataState", "dataError", "dataFiltered", "scrollToSelectedItem"]);
|
|
25
|
+
const { size = 's', flattenItems } = useNewListContext();
|
|
26
|
+
const { value, isSelectionSingle } = useSelectionContext();
|
|
27
|
+
const selectedItem = isSelectionSingle && value ? flattenItems[value] : undefined;
|
|
26
28
|
const itemsJSX = useRenderItems(items);
|
|
27
29
|
const itemsPinTopJSX = useRenderItems(pinTop);
|
|
28
30
|
const itemsPinBottomJSX = useRenderItems(pinBottom);
|
|
@@ -30,11 +32,17 @@ export const ListPrivate = forwardRef((_a, ref) => {
|
|
|
30
32
|
const hasNoItems = items.length === 0;
|
|
31
33
|
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]);
|
|
32
34
|
const content = useMemo(() => (_jsxs("div", { className: styles.content, children: [itemsJSX, loadingJSX, _jsx(ListEmptyState, { loading: loading, dataError: dataError, emptyStates: emptyStates, hasNoItems: hasNoItems, dataFiltered: dataFiltered !== null && dataFiltered !== void 0 ? dataFiltered : Boolean(search === null || search === void 0 ? void 0 : search.value) })] })), [dataError, dataFiltered, emptyStates, hasNoItems, itemsJSX, loading, loadingJSX, search === null || search === void 0 ? void 0 : search.value]);
|
|
35
|
+
const onScrollInitialized = () => {
|
|
36
|
+
var _a, _b;
|
|
37
|
+
if (scrollToSelectedItem) {
|
|
38
|
+
(_b = (_a = selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.itemRef) === null || _a === void 0 ? void 0 : _a.current) === null || _b === void 0 ? void 0 : _b.scrollIntoView();
|
|
39
|
+
}
|
|
40
|
+
};
|
|
33
41
|
const listJSX = (_jsxs("ul", Object.assign({ className: cn(commonStyles.listContainer, className), ref: ref, onKeyDown: onKeyDown, tabIndex: tabIndex, onFocus: onFocus, onBlur: onBlur, "data-active": active || undefined, role: 'menu' }, extractSupportProps(props), { children: [(Number(pinTop === null || pinTop === void 0 ? void 0 : pinTop.length) > 0 || search) && (_jsxs(PinTopGroupItem, { children: [search && _jsx(SearchItem, Object.assign({ search: search }, searchItem)), Number(pinTop === null || pinTop === void 0 ? void 0 : pinTop.length) > 0 && itemsPinTopJSX] })), scroll ? (_jsxs(Scroll, { className: cn({
|
|
34
42
|
[commonStyles.scrollContainerS]: scroll && limitedScrollHeight && size === 's',
|
|
35
43
|
[commonStyles.scrollContainerM]: scroll && limitedScrollHeight && size === 'm',
|
|
36
44
|
[commonStyles.scrollContainerL]: scroll && limitedScrollHeight && size === 'l',
|
|
37
|
-
}), barHideStrategy: 'never', size: size === 's' ? 's' : 'm', ref: scrollContainerRef, untouchableScrollbars: untouchableScrollbars, onScroll: onScroll, children: [content, _jsx("div", { className: styles.scrollStub, ref: scrollRef })] })) : (_jsx(_Fragment, { children: content })), Number(pinBottom === null || pinBottom === void 0 ? void 0 : pinBottom.length) > 0 && _jsx(PinBottomGroupItem, { children: itemsPinBottomJSX }), footer && (_jsx("div", { className: styles.footer, onFocus: e => e.stopPropagation(), children: footer }))] })));
|
|
45
|
+
}), barHideStrategy: 'never', size: size === 's' ? 's' : 'm', ref: scrollContainerRef, untouchableScrollbars: untouchableScrollbars, onScroll: onScroll, onInitialized: onScrollInitialized, children: [content, _jsx("div", { className: styles.scrollStub, ref: scrollRef })] })) : (_jsx(_Fragment, { children: content })), Number(pinBottom === null || pinBottom === void 0 ? void 0 : pinBottom.length) > 0 && _jsx(PinBottomGroupItem, { children: itemsPinBottomJSX }), footer && (_jsx("div", { className: styles.footer, onFocus: e => e.stopPropagation(), children: footer }))] })));
|
|
38
46
|
if (!nested) {
|
|
39
47
|
return listJSX;
|
|
40
48
|
}
|
|
@@ -19,15 +19,13 @@ export type PublicListContextType = {
|
|
|
19
19
|
export type PrivateListContextType = {
|
|
20
20
|
flattenItems: Record<string, FlattenItem>;
|
|
21
21
|
focusFlattenItems: Record<string, FocusFlattenItem>;
|
|
22
|
+
firstItemId: ItemId;
|
|
22
23
|
};
|
|
23
24
|
type Child = {
|
|
24
25
|
children: ReactNode;
|
|
25
26
|
};
|
|
26
27
|
type ListContextType = PublicListContextType & PrivateListContextType;
|
|
27
|
-
export declare const ListContext: import("react").Context<
|
|
28
|
-
flattenItems: {};
|
|
29
|
-
focusFlattenItems: {};
|
|
30
|
-
}>;
|
|
28
|
+
export declare const ListContext: import("react").Context<ListContextType>;
|
|
31
29
|
export declare function useNewListContext(): ListContextType;
|
|
32
30
|
export declare function NewListContextProvider({ children, ...props }: ListContextType & Child): import("react/jsx-runtime").JSX.Element;
|
|
33
31
|
export {};
|
|
@@ -11,15 +11,17 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
11
11
|
};
|
|
12
12
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
13
13
|
import { createContext, useContext } from 'react';
|
|
14
|
+
import { ITEM_PREFIXES } from '../../../constants';
|
|
14
15
|
export const ListContext = createContext({
|
|
15
16
|
flattenItems: {},
|
|
16
17
|
focusFlattenItems: {},
|
|
18
|
+
firstItemId: ITEM_PREFIXES.default,
|
|
17
19
|
});
|
|
18
20
|
export function useNewListContext() {
|
|
19
21
|
return useContext(ListContext);
|
|
20
22
|
}
|
|
21
|
-
function extractListProps({ size, marker, flattenItems, focusFlattenItems, contentRender, }) {
|
|
22
|
-
return { size, marker, contentRender, flattenItems, focusFlattenItems };
|
|
23
|
+
function extractListProps({ size, marker, flattenItems, focusFlattenItems, contentRender, firstItemId, }) {
|
|
24
|
+
return { size, marker, contentRender, flattenItems, focusFlattenItems, firstItemId };
|
|
23
25
|
}
|
|
24
26
|
export function NewListContextProvider(_a) {
|
|
25
27
|
var { children } = _a, props = __rest(_a, ["children"]);
|
|
@@ -4,8 +4,13 @@ type UseNewKeyboardNavigationProps<T extends HTMLElement> = {
|
|
|
4
4
|
mainRef?: RefObject<T>;
|
|
5
5
|
btnRef?: RefObject<HTMLButtonElement>;
|
|
6
6
|
focusFlattenItems: Record<string, FocusFlattenItem>;
|
|
7
|
+
keyboardNavigationRef?: RefObject<{
|
|
8
|
+
focusItem(item: ItemId): void;
|
|
9
|
+
}>;
|
|
10
|
+
hasListInFocusChain: boolean;
|
|
11
|
+
firstItemId: ItemId;
|
|
7
12
|
};
|
|
8
|
-
export declare function useNewKeyboardNavigation<T extends HTMLElement>({ mainRef, btnRef, focusFlattenItems, }: UseNewKeyboardNavigationProps<T>): {
|
|
13
|
+
export declare function useNewKeyboardNavigation<T extends HTMLElement>({ mainRef, btnRef, focusFlattenItems, keyboardNavigationRef, hasListInFocusChain, firstItemId, }: UseNewKeyboardNavigationProps<T>): {
|
|
9
14
|
resetActiveItemId: () => void;
|
|
10
15
|
activeItemId: ItemId | undefined;
|
|
11
16
|
forceUpdateActiveItemId: (itemId: ItemId) => void;
|
|
@@ -1,7 +1,13 @@
|
|
|
1
|
-
import { useCallback, useRef, useState } from 'react';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const
|
|
1
|
+
import { useCallback, useImperativeHandle, useRef, useState } from 'react';
|
|
2
|
+
import { ITEM_PREFIXES } from '../../constants';
|
|
3
|
+
export function useNewKeyboardNavigation({ mainRef, btnRef, focusFlattenItems, keyboardNavigationRef, hasListInFocusChain, firstItemId, }) {
|
|
4
|
+
const defaultActiveItemId = hasListInFocusChain ? undefined : firstItemId;
|
|
5
|
+
const [activeItemId, setActiveItemId] = useState(() => defaultActiveItemId);
|
|
6
|
+
const activeItemIdRef = useRef(defaultActiveItemId);
|
|
7
|
+
const resetActiveItemId = useCallback(() => {
|
|
8
|
+
setActiveItemId(defaultActiveItemId);
|
|
9
|
+
activeItemIdRef.current = defaultActiveItemId;
|
|
10
|
+
}, [defaultActiveItemId]);
|
|
5
11
|
const handleListKeyDownFactory = useCallback((ids, expandedIds) => (e) => {
|
|
6
12
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
7
13
|
switch (e.key) {
|
|
@@ -32,11 +38,13 @@ export function useNewKeyboardNavigation({ mainRef, btnRef, focusFlattenItems, }
|
|
|
32
38
|
}
|
|
33
39
|
case 'ArrowUp': {
|
|
34
40
|
if (ids[0] === activeItemIdRef.current) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
41
|
+
if (hasListInFocusChain) {
|
|
42
|
+
const item = focusFlattenItems[ids[0]];
|
|
43
|
+
if (item.parentId === ITEM_PREFIXES.default) {
|
|
44
|
+
activeItemIdRef.current = undefined;
|
|
45
|
+
setActiveItemId(undefined);
|
|
46
|
+
(_e = mainRef === null || mainRef === void 0 ? void 0 : mainRef.current) === null || _e === void 0 ? void 0 : _e.focus();
|
|
47
|
+
}
|
|
40
48
|
}
|
|
41
49
|
}
|
|
42
50
|
else if (activeItemIdRef.current !== undefined) {
|
|
@@ -69,11 +77,16 @@ export function useNewKeyboardNavigation({ mainRef, btnRef, focusFlattenItems, }
|
|
|
69
77
|
}
|
|
70
78
|
case 'Tab': {
|
|
71
79
|
if (activeItemIdRef.current !== undefined) {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
80
|
+
if (hasListInFocusChain) {
|
|
81
|
+
e.preventDefault();
|
|
82
|
+
e.stopPropagation();
|
|
83
|
+
activeItemIdRef.current = undefined;
|
|
84
|
+
setActiveItemId(undefined);
|
|
85
|
+
(_h = mainRef === null || mainRef === void 0 ? void 0 : mainRef.current) === null || _h === void 0 ? void 0 : _h.focus();
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
resetActiveItemId();
|
|
89
|
+
}
|
|
77
90
|
}
|
|
78
91
|
else {
|
|
79
92
|
btnRef && !e.shiftKey ? (_j = btnRef === null || btnRef === void 0 ? void 0 : btnRef.current) === null || _j === void 0 ? void 0 : _j.focus() : (_k = mainRef === null || mainRef === void 0 ? void 0 : mainRef.current) === null || _k === void 0 ? void 0 : _k.focus();
|
|
@@ -84,11 +97,7 @@ export function useNewKeyboardNavigation({ mainRef, btnRef, focusFlattenItems, }
|
|
|
84
97
|
return;
|
|
85
98
|
}
|
|
86
99
|
}
|
|
87
|
-
}, [focusFlattenItems, mainRef, btnRef]);
|
|
88
|
-
const resetActiveItemId = useCallback(() => {
|
|
89
|
-
setActiveItemId(undefined);
|
|
90
|
-
activeItemIdRef.current = undefined;
|
|
91
|
-
}, []);
|
|
100
|
+
}, [focusFlattenItems, hasListInFocusChain, mainRef, resetActiveItemId, btnRef]);
|
|
92
101
|
const forceUpdateActiveItemId = useCallback((itemId) => {
|
|
93
102
|
var _a, _b;
|
|
94
103
|
setActiveItemId(itemId);
|
|
@@ -96,6 +105,7 @@ export function useNewKeyboardNavigation({ mainRef, btnRef, focusFlattenItems, }
|
|
|
96
105
|
const item = focusFlattenItems[itemId];
|
|
97
106
|
(_b = (_a = item === null || item === void 0 ? void 0 : item.itemRef) === null || _a === void 0 ? void 0 : _a.current) === null || _b === void 0 ? void 0 : _b.focus();
|
|
98
107
|
}, [focusFlattenItems]);
|
|
108
|
+
useImperativeHandle(keyboardNavigationRef, () => ({ focusItem: forceUpdateActiveItemId }), [forceUpdateActiveItemId]);
|
|
99
109
|
return {
|
|
100
110
|
resetActiveItemId,
|
|
101
111
|
activeItemId,
|