@snack-uikit/list 0.20.0 → 0.21.1-preview-d1a63029.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 +12 -0
- package/README.md +17 -1
- 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 +8 -8
- package/src/components/Items/BaseItem/BaseItem.tsx +4 -4
- package/src/components/Items/NextListItem/NextListItem.tsx +2 -1
- package/src/components/Items/SearchItem/SearchItem.tsx +3 -2
- package/src/components/Items/hooks.tsx +6 -4
- 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/contexts/NewListProvider.tsx +6 -2
- package/src/components/Lists/hooks.ts +35 -20
- package/src/components/Lists/types.ts +11 -3
- package/src/constants.ts +8 -0
- 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,
|