@snack-uikit/list 0.9.2-preview-c8f85f20.0 → 0.9.2-preview-0a44b2fc.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/README.md +51 -23
- package/dist/components/Items/AccordionItem/AccordionItem.d.ts +2 -2
- package/dist/components/Items/AccordionItem/AccordionItem.js +19 -16
- package/dist/components/Items/BaseItem/BaseItem.d.ts +5 -4
- package/dist/components/Items/BaseItem/BaseItem.js +22 -16
- package/dist/components/Items/BaseItem/styles.module.css +9 -122
- package/dist/components/Items/GroupItem/GroupItem.d.ts +2 -2
- package/dist/components/Items/GroupItem/GroupItem.js +3 -3
- package/dist/components/Items/NextListItem/NextListItem.d.ts +2 -2
- package/dist/components/Items/NextListItem/NextListItem.js +37 -36
- package/dist/components/Items/SearchItem/SearchItem.d.ts +3 -1
- package/dist/components/Items/SearchItem/SearchItem.js +7 -8
- package/dist/components/Items/hooks.d.ts +14 -9
- package/dist/components/Items/hooks.js +46 -30
- package/dist/components/Items/types.d.ts +41 -26
- package/dist/components/Items/utils.d.ts +7 -17
- package/dist/components/Items/utils.js +3 -31
- package/dist/components/Lists/Droplist/DropList.d.ts +2 -1
- package/dist/components/Lists/Droplist/DropList.js +85 -50
- package/dist/components/Lists/List/List.d.ts +7 -11
- package/dist/components/Lists/List/List.js +61 -53
- package/dist/components/Lists/ListPrivate/ListPrivate.d.ts +18 -20
- package/dist/components/Lists/ListPrivate/ListPrivate.js +10 -9
- package/dist/components/Lists/contexts/CollapseProvider.d.ts +13 -1
- package/dist/components/Lists/contexts/CollapseProvider.js +2 -0
- package/dist/components/Lists/contexts/FocusListProvider.d.ts +9 -0
- package/dist/components/Lists/contexts/FocusListProvider.js +5 -0
- package/dist/components/Lists/contexts/NewListProvider.d.ts +27 -0
- package/dist/components/Lists/contexts/{ListProvider.js → NewListProvider.js} +10 -5
- package/dist/components/Lists/contexts/SelectionProvider.d.ts +28 -15
- package/dist/components/Lists/contexts/SelectionProvider.js +14 -8
- package/dist/components/Lists/contexts/index.d.ts +2 -2
- package/dist/components/Lists/contexts/index.js +2 -2
- package/dist/components/Lists/hooks.d.ts +11 -11
- package/dist/components/Lists/hooks.js +80 -41
- package/dist/components/Lists/types.d.ts +19 -15
- package/dist/helperComponents/ItemContent/ItemContent.d.ts +15 -0
- package/dist/helperComponents/ItemContent/ItemContent.js +23 -0
- package/dist/helperComponents/ItemContent/index.d.ts +1 -0
- package/dist/helperComponents/ItemContent/index.js +1 -0
- package/dist/helperComponents/ItemContent/styles.module.css +117 -0
- package/dist/helperComponents/ListEmptyState/ListEmptyState.d.ts +2 -2
- package/dist/helperComponents/ListEmptyState/ListEmptyState.js +13 -4
- package/dist/helperComponents/index.d.ts +1 -0
- package/dist/helperComponents/index.js +1 -0
- package/dist/hooks.d.ts +1 -1
- package/dist/hooks.js +15 -10
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -2
- package/dist/legacy/components/Items/hooks.d.ts +12 -0
- package/dist/legacy/components/Items/hooks.js +33 -0
- package/dist/legacy/components/index.d.ts +1 -0
- package/dist/legacy/components/index.js +1 -0
- package/dist/legacy/hooks.d.ts +5 -0
- package/dist/legacy/hooks.js +15 -0
- package/dist/legacy/index.d.ts +3 -0
- package/dist/legacy/index.js +3 -0
- package/dist/legacy/utils.d.ts +29 -0
- package/dist/legacy/utils.js +107 -0
- package/dist/types.d.ts +1 -1
- package/dist/utils.d.ts +27 -25
- package/dist/utils.js +74 -103
- package/package.json +12 -12
- package/src/components/Items/AccordionItem/AccordionItem.tsx +26 -19
- package/src/components/Items/BaseItem/BaseItem.tsx +48 -46
- package/src/components/Items/BaseItem/styles.module.scss +8 -70
- package/src/components/Items/GroupItem/GroupItem.tsx +6 -5
- package/src/components/Items/NextListItem/NextListItem.tsx +67 -78
- package/src/components/Items/SearchItem/SearchItem.tsx +8 -8
- package/src/components/Items/hooks.tsx +76 -54
- package/src/components/Items/types.ts +61 -31
- package/src/components/Items/utils.ts +9 -58
- package/src/components/Lists/Droplist/DropList.tsx +155 -102
- package/src/components/Lists/List/List.tsx +120 -105
- package/src/components/Lists/ListPrivate/ListPrivate.tsx +26 -27
- package/src/components/Lists/contexts/CollapseProvider.tsx +22 -0
- package/src/components/Lists/contexts/FocusListProvider.tsx +16 -0
- package/src/components/Lists/contexts/NewListProvider.tsx +49 -0
- package/src/components/Lists/contexts/SelectionProvider.tsx +63 -36
- package/src/components/Lists/contexts/index.ts +3 -2
- package/src/components/Lists/hooks.ts +139 -75
- package/src/components/Lists/types.ts +29 -27
- package/src/helperComponents/ItemContent/ItemContent.tsx +63 -0
- package/src/helperComponents/ItemContent/index.ts +1 -0
- package/src/helperComponents/ItemContent/styles.module.scss +78 -0
- package/src/helperComponents/ListEmptyState/ListEmptyState.tsx +21 -13
- package/src/helperComponents/index.ts +1 -0
- package/src/hooks.ts +21 -15
- package/src/index.ts +9 -2
- package/src/legacy/components/Items/hooks.tsx +53 -0
- package/src/legacy/components/index.ts +1 -0
- package/src/legacy/hooks.ts +27 -0
- package/src/legacy/index.ts +3 -0
- package/src/legacy/utils.ts +166 -0
- package/src/types.ts +1 -1
- package/src/utils.ts +153 -139
- package/dist/components/Items/NextListItem/hooks.d.ts +0 -19
- package/dist/components/Items/NextListItem/hooks.js +0 -80
- package/dist/components/Lists/contexts/ListProvider.d.ts +0 -17
- package/dist/components/Lists/contexts/ParentListProvider.d.ts +0 -18
- package/dist/components/Lists/contexts/ParentListProvider.js +0 -12
- package/src/components/Items/NextListItem/hooks.ts +0 -105
- package/src/components/Lists/contexts/CollapseProvider.ts +0 -9
- package/src/components/Lists/contexts/ListProvider.tsx +0 -28
- package/src/components/Lists/contexts/ParentListProvider.tsx +0 -35
|
@@ -1,28 +1,26 @@
|
|
|
1
1
|
import { FocusEvent, KeyboardEvent, MouseEvent, ReactNode, RefObject } from 'react';
|
|
2
2
|
|
|
3
|
-
import { TruncateStringProps } from '@snack-uikit/truncate-string';
|
|
4
3
|
import { WithSupportProps } from '@snack-uikit/utils';
|
|
5
4
|
|
|
6
|
-
import { SeparatorProps } from '../../helperComponents';
|
|
5
|
+
import { ItemContentProps, SeparatorProps } from '../../helperComponents';
|
|
7
6
|
import { ScrollProps, SearchState } from '../../types';
|
|
8
7
|
|
|
9
|
-
export type ItemContentProps = {
|
|
10
|
-
option: string;
|
|
11
|
-
caption?: string;
|
|
12
|
-
description?: string;
|
|
13
|
-
truncate?: {
|
|
14
|
-
option?: number;
|
|
15
|
-
description?: number;
|
|
16
|
-
variant?: TruncateStringProps['variant'];
|
|
17
|
-
};
|
|
18
|
-
};
|
|
19
|
-
|
|
20
8
|
export type BaseItemPrivateProps = {
|
|
21
9
|
expandIcon?: ReactNode;
|
|
22
10
|
open?: boolean;
|
|
23
11
|
};
|
|
24
12
|
|
|
25
|
-
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
14
|
+
export type AnyType = any;
|
|
15
|
+
|
|
16
|
+
export type ContentRenderProps<Meta = AnyType> = {
|
|
17
|
+
label: string | number;
|
|
18
|
+
meta?: Meta;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type ItemId = string | number;
|
|
22
|
+
|
|
23
|
+
export type BaseItemProps<Meta = AnyType> = WithSupportProps<{
|
|
26
24
|
/**
|
|
27
25
|
* Слот до основного контента
|
|
28
26
|
* @type ReactElement
|
|
@@ -33,9 +31,10 @@ export type BaseItemProps = WithSupportProps<{
|
|
|
33
31
|
* @type ReactElement
|
|
34
32
|
*/
|
|
35
33
|
afterContent?: ReactNode;
|
|
36
|
-
/**
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Основной контент айтема
|
|
36
|
+
*/
|
|
37
|
+
content?: ItemContentProps | ReactNode;
|
|
39
38
|
/** Колбек обработки клика */
|
|
40
39
|
onClick?(e: MouseEvent<HTMLElement>): void;
|
|
41
40
|
/** Колбек обработки нажатия кнопки мыши */
|
|
@@ -46,10 +45,8 @@ export type BaseItemProps = WithSupportProps<{
|
|
|
46
45
|
onFocus?(e: FocusEvent<HTMLElement>): void;
|
|
47
46
|
/** Колбек обработки блюра */
|
|
48
47
|
onBlur?(e: FocusEvent<HTMLElement>): void;
|
|
49
|
-
|
|
50
48
|
/** Уникальный идентификатор */
|
|
51
|
-
id?:
|
|
52
|
-
|
|
49
|
+
id?: ItemId;
|
|
53
50
|
/** Флаг неактивности элемента */
|
|
54
51
|
disabled?: boolean;
|
|
55
52
|
|
|
@@ -70,27 +67,60 @@ export type BaseItemProps = WithSupportProps<{
|
|
|
70
67
|
key?: string | number;
|
|
71
68
|
|
|
72
69
|
itemWrapRender?(item: ReactNode): ReactNode;
|
|
73
|
-
}
|
|
70
|
+
}> & {
|
|
71
|
+
/**
|
|
72
|
+
* Дополнительные поля для айтема, которые можно использовать для contentRender функции или поиска
|
|
73
|
+
*/
|
|
74
|
+
meta?: Meta;
|
|
75
|
+
};
|
|
74
76
|
|
|
75
|
-
type BaseItemsWithoutNonGroupProps = Omit<BaseItemProps
|
|
77
|
+
type BaseItemsWithoutNonGroupProps<Meta = AnyType> = Omit<BaseItemProps<Meta>, 'switch' | 'inactive'>;
|
|
76
78
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
+
export type ItemProps<Meta = AnyType> =
|
|
80
|
+
| BaseItemProps<Meta>
|
|
81
|
+
// eslint-disable-next-line no-use-before-define
|
|
82
|
+
| AccordionItemProps<Meta>
|
|
83
|
+
// eslint-disable-next-line no-use-before-define
|
|
84
|
+
| NextListItemProps<Meta>
|
|
85
|
+
// eslint-disable-next-line no-use-before-define
|
|
86
|
+
| GroupItemProps<Meta>;
|
|
79
87
|
|
|
80
|
-
export type AccordionItemProps = BaseItemsWithoutNonGroupProps & {
|
|
81
|
-
items: ItemProps[];
|
|
88
|
+
export type AccordionItemProps<Meta = AnyType> = BaseItemsWithoutNonGroupProps<Meta> & {
|
|
89
|
+
items: ItemProps<Meta>[];
|
|
82
90
|
type: 'collapse';
|
|
83
91
|
};
|
|
84
92
|
|
|
85
|
-
export type NextListItemProps = BaseItemsWithoutNonGroupProps & {
|
|
86
|
-
items: ItemProps[];
|
|
93
|
+
export type NextListItemProps<Meta = AnyType> = BaseItemsWithoutNonGroupProps<Meta> & {
|
|
94
|
+
items: ItemProps<Meta>[];
|
|
87
95
|
type: 'next-list';
|
|
88
96
|
placement?: 'right-start' | 'left-start' | 'left' | 'right' | 'left-end' | 'right-end';
|
|
89
97
|
search?: SearchState;
|
|
90
|
-
onSublistOpenChanged?(open: boolean, id?: string | number): void;
|
|
91
98
|
} & ScrollProps;
|
|
92
99
|
|
|
93
|
-
export type GroupItemProps = Omit<SeparatorProps, 'size'> & {
|
|
94
|
-
items: ItemProps[];
|
|
100
|
+
export type GroupItemProps<Meta = AnyType> = Omit<SeparatorProps, 'size'> & {
|
|
101
|
+
items: ItemProps<Meta>[];
|
|
102
|
+
// type: 'group';
|
|
95
103
|
id?: string | number;
|
|
104
|
+
|
|
105
|
+
meta?: undefined;
|
|
96
106
|
};
|
|
107
|
+
|
|
108
|
+
type RequiredFields<T, K extends keyof T> = Required<Pick<T, K>> & Omit<T, K>;
|
|
109
|
+
export type Flatten<T, K extends keyof T> = RequiredFields<T, K>;
|
|
110
|
+
|
|
111
|
+
type CommonFlattenProps = { items: ItemId[]; allChildIds: ItemId[] };
|
|
112
|
+
|
|
113
|
+
export type FlattenBaseItem<Meta = AnyType> = Flatten<BaseItemProps<Meta>, 'id' | 'itemRef'>;
|
|
114
|
+
export type FlattenNextListItem<Meta = AnyType> = Flatten<Omit<NextListItemProps<Meta>, 'items'>, 'id' | 'itemRef'> &
|
|
115
|
+
CommonFlattenProps;
|
|
116
|
+
export type FlattenGroupListItem<Meta = AnyType> = Flatten<Omit<GroupItemProps<Meta>, 'items'>, 'id'> &
|
|
117
|
+
CommonFlattenProps;
|
|
118
|
+
export type FlattenAccordionItem<Meta = AnyType> = Flatten<Omit<AccordionItemProps<Meta>, 'items'>, 'id' | 'itemRef'> &
|
|
119
|
+
CommonFlattenProps;
|
|
120
|
+
|
|
121
|
+
export type FlattenItem<Meta = AnyType> = (
|
|
122
|
+
| FlattenBaseItem<Meta>
|
|
123
|
+
| FlattenNextListItem<Meta>
|
|
124
|
+
| FlattenGroupListItem<Meta>
|
|
125
|
+
| FlattenAccordionItem<Meta>
|
|
126
|
+
) & { parentId?: ItemId };
|
|
@@ -1,71 +1,22 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ItemContentProps } from '../../helperComponents';
|
|
2
|
+
import { AccordionItemProps, AnyType, BaseItemProps, GroupItemProps, NextListItemProps } from './types';
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6
|
-
export function isBaseItemProps(item: any): item is BaseItemProps {
|
|
7
|
-
return 'content' in item;
|
|
4
|
+
export function isBaseItemProps<Meta, ReturnType = BaseItemProps<Meta>>(item: AnyType): item is ReturnType {
|
|
5
|
+
return !('items' in item);
|
|
8
6
|
}
|
|
9
7
|
|
|
10
|
-
|
|
11
|
-
export function isAccordionItemProps(item: any): item is AccordionItemProps {
|
|
8
|
+
export function isAccordionItemProps<Meta, ReturnType = AccordionItemProps<Meta>>(item: AnyType): item is ReturnType {
|
|
12
9
|
return 'items' in item && item['type'] === 'collapse';
|
|
13
10
|
}
|
|
14
11
|
|
|
15
|
-
|
|
16
|
-
export function isNextListItemProps(item: any): item is NextListItemProps {
|
|
12
|
+
export function isNextListItemProps<Meta, ReturnType = NextListItemProps<Meta>>(item: AnyType): item is ReturnType {
|
|
17
13
|
return 'items' in item && item['type'] === 'next-list';
|
|
18
14
|
}
|
|
19
15
|
|
|
20
|
-
|
|
21
|
-
export function isGroupItemProps(item: any): item is GroupItemProps {
|
|
16
|
+
export function isGroupItemProps<Meta, ReturnType = GroupItemProps<Meta>>(item: AnyType): item is ReturnType {
|
|
22
17
|
return 'items' in item && item['type'] === undefined;
|
|
23
18
|
}
|
|
24
19
|
|
|
25
|
-
export function
|
|
26
|
-
|
|
27
|
-
hasSearch,
|
|
28
|
-
pinTop,
|
|
29
|
-
pinBottom,
|
|
30
|
-
footerRefs,
|
|
31
|
-
}: {
|
|
32
|
-
items: ItemProps[];
|
|
33
|
-
hasSearch: boolean;
|
|
34
|
-
footerRefs: ItemProps[];
|
|
35
|
-
pinTop?: ItemProps[];
|
|
36
|
-
pinBottom?: ItemProps[];
|
|
37
|
-
}) {
|
|
38
|
-
const searchShift = hasSearch ? 1 : 0;
|
|
39
|
-
const pinTopNumber = pinTop?.length ?? 0;
|
|
40
|
-
const pinBottomNumber = pinBottom?.length ?? 0;
|
|
41
|
-
const footerElementsNumber = footerRefs?.length ?? 0;
|
|
42
|
-
|
|
43
|
-
return {
|
|
44
|
-
pinTop: items.slice(searchShift, pinTopNumber + searchShift),
|
|
45
|
-
items: items.slice(pinTopNumber + searchShift, items.length - pinBottomNumber - footerElementsNumber),
|
|
46
|
-
pinBottom: items.slice(items.length - pinBottomNumber - footerElementsNumber, items.length - footerElementsNumber),
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export function addItemsIds(itemsProp: ItemProps[], prefix?: string | number): ItemProps[] {
|
|
51
|
-
return itemsProp.map((item, idx) => {
|
|
52
|
-
const autoId = prefix !== undefined ? [prefix, idx].join('-') : String(idx);
|
|
53
|
-
const itemId = item.id ?? autoId;
|
|
54
|
-
|
|
55
|
-
if (isGroupItemProps(item)) {
|
|
56
|
-
return { key: autoId, ...item, id: itemId, items: addItemsIds(item.items, itemId) };
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (isAccordionItemProps(item) || isNextListItemProps(item)) {
|
|
60
|
-
return {
|
|
61
|
-
key: autoId,
|
|
62
|
-
...item,
|
|
63
|
-
id: itemId,
|
|
64
|
-
items: addItemsIds(item.items, itemId),
|
|
65
|
-
itemRef: item.itemRef || createRef<HTMLElement>(),
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return { key: autoId, ...item, id: itemId, itemRef: item.itemRef || createRef<HTMLElement>() };
|
|
70
|
-
});
|
|
20
|
+
export function isContentItemProps(item: AnyType): item is ItemContentProps {
|
|
21
|
+
return typeof item === 'object' && item['option'] !== undefined;
|
|
71
22
|
}
|
|
@@ -1,20 +1,24 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import cn from 'classnames';
|
|
2
|
+
import { cloneElement, isValidElement, KeyboardEvent, ReactNode, RefObject, useCallback, useMemo, useRef } from 'react';
|
|
3
3
|
|
|
4
|
-
import { Dropdown } from '@snack-uikit/dropdown';
|
|
4
|
+
import { Dropdown, DropdownProps } from '@snack-uikit/dropdown';
|
|
5
|
+
import { useValueControl } from '@snack-uikit/utils';
|
|
5
6
|
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
7
|
+
import { extractActiveItems, kindFlattenItems } from '../../../utils';
|
|
8
|
+
import { AnyType, ItemId, useCreateBaseItems } from '../../Items';
|
|
9
|
+
import { CollapseContext, FocusListContext, NewListContextProvider, SelectionProvider } from '../contexts';
|
|
10
|
+
import { useNewKeyboardNavigation } from '../hooks';
|
|
10
11
|
import { ListPrivate } from '../ListPrivate';
|
|
12
|
+
import styles from '../styles.module.scss';
|
|
11
13
|
import { DroplistProps } from '../types';
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
const DEFAULT_FALLBACK_PLACEMENTS: DropdownProps['fallbackPlacements'] = ['top', 'right', 'bottom', 'left'];
|
|
16
|
+
|
|
17
|
+
export function Droplist<Meta = AnyType>({
|
|
14
18
|
items: itemsProp,
|
|
15
19
|
search,
|
|
16
|
-
pinBottom,
|
|
17
|
-
pinTop,
|
|
20
|
+
pinBottom: pinBottomProp = [],
|
|
21
|
+
pinTop: pinTopProp = [],
|
|
18
22
|
footerActiveElementsRefs,
|
|
19
23
|
children,
|
|
20
24
|
trigger,
|
|
@@ -24,65 +28,100 @@ export function Droplist({
|
|
|
24
28
|
open: openProp,
|
|
25
29
|
onOpenChange,
|
|
26
30
|
collapse = {},
|
|
31
|
+
triggerClassName,
|
|
32
|
+
selection,
|
|
33
|
+
contentRender,
|
|
34
|
+
size = 's',
|
|
35
|
+
marker = true,
|
|
36
|
+
className,
|
|
27
37
|
...props
|
|
28
|
-
}: DroplistProps) {
|
|
38
|
+
}: DroplistProps<Meta>) {
|
|
29
39
|
const hasSearch = useMemo(() => Boolean(search), [search]);
|
|
30
40
|
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
collapse.defaultValue ?? [],
|
|
39
|
-
collapse.onChange,
|
|
41
|
+
const [openCollapseItems = [], setOpenCollapsedItems] = useValueControl<ItemId[] | undefined>(collapse);
|
|
42
|
+
const toggleOpenCollapseItem = useCallback(
|
|
43
|
+
(id: ItemId) =>
|
|
44
|
+
setOpenCollapsedItems((items: ItemId[]) =>
|
|
45
|
+
items?.includes(id) ? items.filter(item => item !== id) : (items ?? []).concat([id]),
|
|
46
|
+
),
|
|
47
|
+
[setOpenCollapsedItems],
|
|
40
48
|
);
|
|
41
49
|
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
50
|
+
const [open = false, setOpen] = useValueControl<boolean>({
|
|
51
|
+
value: openProp,
|
|
52
|
+
defaultValue: false,
|
|
53
|
+
onChange: onOpenChange,
|
|
45
54
|
});
|
|
46
55
|
|
|
47
|
-
const {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
56
|
+
const { searchItem, footerItems } = useCreateBaseItems<Meta>({ footerActiveElementsRefs });
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Объект с пропсами всех вложенных айтемов; ключ id
|
|
60
|
+
*/
|
|
61
|
+
const { flattenedItems, ...memorizedItems } = useMemo(() => {
|
|
62
|
+
const pinTop = kindFlattenItems<Meta>({ items: pinTopProp, prefix: '~pinTop', parentId: '~main' });
|
|
63
|
+
const items = kindFlattenItems<Meta>({ items: itemsProp, prefix: '~main', parentId: '~main' });
|
|
64
|
+
const pinBottom = kindFlattenItems<Meta>({ items: pinBottomProp, prefix: '~pinBottom', parentId: '~main' });
|
|
65
|
+
|
|
66
|
+
const flattenedItems = { ...pinTop.flattenedItems, ...pinBottom.flattenedItems, ...items.flattenedItems };
|
|
67
|
+
|
|
68
|
+
[...footerItems, searchItem].forEach(item => {
|
|
69
|
+
flattenedItems[item.id] = item;
|
|
51
70
|
});
|
|
52
71
|
|
|
53
|
-
|
|
72
|
+
return { items, pinTop, pinBottom, flattenedItems };
|
|
73
|
+
}, [itemsProp, pinTopProp, pinBottomProp, searchItem, footerItems]);
|
|
74
|
+
|
|
75
|
+
const { itemRefs, ids, expandedIds } = useMemo(() => {
|
|
76
|
+
const { pinTop, items, pinBottom } = memorizedItems;
|
|
77
|
+
|
|
78
|
+
let ids: ItemId[] = [];
|
|
79
|
+
let expandedIds: ItemId[] = [];
|
|
80
|
+
let itemRefs: RefObject<HTMLElement>[] = [];
|
|
81
|
+
|
|
82
|
+
if (hasSearch) {
|
|
83
|
+
ids.push(searchItem.id);
|
|
84
|
+
itemRefs.push(searchItem.itemRef);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
[pinTop, items, pinBottom].forEach(({ flattenedItems, items }) => {
|
|
88
|
+
const activeItems = extractActiveItems<Meta>({ flattenedItems, items, openCollapseItems });
|
|
89
|
+
|
|
90
|
+
ids = ids.concat(activeItems.ids);
|
|
91
|
+
expandedIds = expandedIds.concat(activeItems.expandedIds);
|
|
92
|
+
itemRefs = itemRefs.concat(activeItems.itemRefs);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
footerItems.forEach(footerItem => {
|
|
96
|
+
ids.push(footerItem.id);
|
|
97
|
+
itemRefs.push(footerItem.itemRef);
|
|
98
|
+
});
|
|
54
99
|
|
|
55
100
|
return {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
expandedIds: res.expandedIds,
|
|
101
|
+
ids,
|
|
102
|
+
itemRefs,
|
|
103
|
+
expandedIds,
|
|
60
104
|
};
|
|
61
|
-
}, [
|
|
62
|
-
|
|
63
|
-
const slicedItems = useMemo(
|
|
64
|
-
() => getSlicedItems({ items, hasSearch, pinTop, pinBottom, footerRefs }),
|
|
65
|
-
[items, hasSearch, pinTop, pinBottom, footerRefs],
|
|
66
|
-
);
|
|
105
|
+
}, [footerItems, hasSearch, memorizedItems, openCollapseItems, searchItem.id, searchItem.itemRef]);
|
|
67
106
|
|
|
68
107
|
const triggerElemRef = useRef<HTMLElement>(null);
|
|
69
|
-
const listRef = useRef<
|
|
70
|
-
|
|
71
|
-
const {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
} = useKeyboardNavigation({ ids, expandedIds, parentRef: triggerElemRefProp ?? triggerElemRef, itemRefs });
|
|
108
|
+
const listRef = useRef<HTMLElement>(null);
|
|
109
|
+
|
|
110
|
+
const { handleListKeyDownFactory, resetActiveItemId, activeItemId, forceUpdateActiveItemId } =
|
|
111
|
+
useNewKeyboardNavigation({
|
|
112
|
+
mainRef: triggerElemRefProp ?? triggerElemRef,
|
|
113
|
+
itemRefs,
|
|
114
|
+
expandedIds,
|
|
115
|
+
flattenedItems,
|
|
116
|
+
});
|
|
79
117
|
|
|
80
|
-
const
|
|
118
|
+
const handleListKeyDown = useCallback(
|
|
119
|
+
(e: KeyboardEvent<HTMLElement>) => handleListKeyDownFactory(ids, expandedIds)(e),
|
|
120
|
+
[handleListKeyDownFactory, ids, expandedIds],
|
|
121
|
+
);
|
|
81
122
|
|
|
82
123
|
const handleOpenChange = (open: boolean) => {
|
|
83
|
-
|
|
84
|
-
resetActiveFocusIndex();
|
|
85
|
-
setOpenCollapsedItems([]);
|
|
124
|
+
resetActiveItemId();
|
|
86
125
|
|
|
87
126
|
setOpen(open);
|
|
88
127
|
};
|
|
@@ -94,14 +133,19 @@ export function Droplist({
|
|
|
94
133
|
setOpen(true);
|
|
95
134
|
|
|
96
135
|
setTimeout(() => {
|
|
136
|
+
resetActiveItemId();
|
|
97
137
|
listRef.current?.focus();
|
|
98
|
-
setActiveFocusIndex(0);
|
|
99
138
|
}, 0);
|
|
100
139
|
}
|
|
101
140
|
|
|
141
|
+
if (e.key === 'ArrowUp') {
|
|
142
|
+
// e.preventDefault();
|
|
143
|
+
setOpen(false);
|
|
144
|
+
}
|
|
145
|
+
|
|
102
146
|
cb?.(e);
|
|
103
147
|
},
|
|
104
|
-
[
|
|
148
|
+
[resetActiveItemId, setOpen],
|
|
105
149
|
);
|
|
106
150
|
|
|
107
151
|
const triggerElem: ReactNode = useMemo(() => {
|
|
@@ -123,55 +167,64 @@ export function Droplist({
|
|
|
123
167
|
return children;
|
|
124
168
|
}, [onKeyDown, children]);
|
|
125
169
|
|
|
170
|
+
// const handleOnFocus = e => {
|
|
171
|
+
// // resetActiveItemId();
|
|
172
|
+
// };
|
|
173
|
+
|
|
126
174
|
return (
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
parentExpandedIds: expandedIds,
|
|
135
|
-
parentItemRefs: itemRefs,
|
|
136
|
-
parentOpenNestedIndex: openNestedIndex,
|
|
137
|
-
triggerRef: triggerElemRefProp ?? triggerElemRef,
|
|
138
|
-
parentRef: listRef,
|
|
139
|
-
parentResetNestedIndex: resetNestedIndex,
|
|
140
|
-
parentResetActiveFocusIndex: resetActiveFocusIndex,
|
|
141
|
-
openCollapsedItems,
|
|
142
|
-
toggleOpenCollapsedItems: id =>
|
|
143
|
-
setOpenCollapsedItems((items: Array<string | number> | undefined = []) =>
|
|
144
|
-
items.includes(id) ? items.filter(item => item !== id) : items?.concat([id]),
|
|
145
|
-
),
|
|
146
|
-
}}
|
|
147
|
-
>
|
|
148
|
-
<Dropdown
|
|
149
|
-
content={
|
|
150
|
-
<ListPrivate
|
|
151
|
-
{...slicedItems}
|
|
152
|
-
{...props}
|
|
153
|
-
onKeyDown={handleListKeyDown}
|
|
154
|
-
tabIndex={0}
|
|
155
|
-
ref={listRef}
|
|
156
|
-
search={search}
|
|
157
|
-
limitedScrollHeight
|
|
158
|
-
parent='droplist'
|
|
159
|
-
/>
|
|
160
|
-
}
|
|
161
|
-
trigger={trigger}
|
|
162
|
-
placement={placement}
|
|
163
|
-
widthStrategy={widthStrategy}
|
|
164
|
-
{...(triggerElemRefProp
|
|
165
|
-
? {}
|
|
166
|
-
: {
|
|
167
|
-
triggerRef: triggerElemRef,
|
|
168
|
-
})}
|
|
169
|
-
open={open}
|
|
170
|
-
onOpenChange={handleOpenChange}
|
|
175
|
+
<NewListContextProvider flattenedItems={flattenedItems} contentRender={contentRender} size={size} marker={marker}>
|
|
176
|
+
<SelectionProvider {...selection}>
|
|
177
|
+
<CollapseContext.Provider
|
|
178
|
+
value={{
|
|
179
|
+
openCollapseItems,
|
|
180
|
+
toggleOpenCollapseItem,
|
|
181
|
+
}}
|
|
171
182
|
>
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
183
|
+
<FocusListContext.Provider
|
|
184
|
+
value={{
|
|
185
|
+
activeItemId,
|
|
186
|
+
handleListKeyDownFactory,
|
|
187
|
+
forceUpdateActiveItemId,
|
|
188
|
+
}}
|
|
189
|
+
>
|
|
190
|
+
<Dropdown
|
|
191
|
+
content={
|
|
192
|
+
<div className={cn(styles.wrapper, className)}>
|
|
193
|
+
<ListPrivate
|
|
194
|
+
{...props}
|
|
195
|
+
{...memorizedItems}
|
|
196
|
+
onKeyDown={handleListKeyDown}
|
|
197
|
+
tabIndex={0}
|
|
198
|
+
ref={listRef}
|
|
199
|
+
search={search}
|
|
200
|
+
// onFocus={handleOnFocus}
|
|
201
|
+
onFocus={e => {
|
|
202
|
+
e.stopPropagation();
|
|
203
|
+
|
|
204
|
+
forceUpdateActiveItemId?.(ids[0]);
|
|
205
|
+
}}
|
|
206
|
+
limitedScrollHeight
|
|
207
|
+
/>
|
|
208
|
+
</div>
|
|
209
|
+
}
|
|
210
|
+
triggerClassName={triggerClassName}
|
|
211
|
+
fallbackPlacements={DEFAULT_FALLBACK_PLACEMENTS}
|
|
212
|
+
trigger={trigger}
|
|
213
|
+
placement={placement}
|
|
214
|
+
widthStrategy={widthStrategy}
|
|
215
|
+
{...(triggerElemRefProp
|
|
216
|
+
? {}
|
|
217
|
+
: {
|
|
218
|
+
triggerRef: triggerElemRef,
|
|
219
|
+
})}
|
|
220
|
+
open={open}
|
|
221
|
+
onOpenChange={handleOpenChange}
|
|
222
|
+
>
|
|
223
|
+
{triggerElem}
|
|
224
|
+
</Dropdown>
|
|
225
|
+
</FocusListContext.Provider>
|
|
226
|
+
</CollapseContext.Provider>
|
|
227
|
+
</SelectionProvider>
|
|
228
|
+
</NewListContextProvider>
|
|
176
229
|
);
|
|
177
230
|
}
|