@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.
Files changed (105) hide show
  1. package/README.md +51 -23
  2. package/dist/components/Items/AccordionItem/AccordionItem.d.ts +2 -2
  3. package/dist/components/Items/AccordionItem/AccordionItem.js +19 -16
  4. package/dist/components/Items/BaseItem/BaseItem.d.ts +5 -4
  5. package/dist/components/Items/BaseItem/BaseItem.js +22 -16
  6. package/dist/components/Items/BaseItem/styles.module.css +9 -122
  7. package/dist/components/Items/GroupItem/GroupItem.d.ts +2 -2
  8. package/dist/components/Items/GroupItem/GroupItem.js +3 -3
  9. package/dist/components/Items/NextListItem/NextListItem.d.ts +2 -2
  10. package/dist/components/Items/NextListItem/NextListItem.js +37 -36
  11. package/dist/components/Items/SearchItem/SearchItem.d.ts +3 -1
  12. package/dist/components/Items/SearchItem/SearchItem.js +7 -8
  13. package/dist/components/Items/hooks.d.ts +14 -9
  14. package/dist/components/Items/hooks.js +46 -30
  15. package/dist/components/Items/types.d.ts +41 -26
  16. package/dist/components/Items/utils.d.ts +7 -17
  17. package/dist/components/Items/utils.js +3 -31
  18. package/dist/components/Lists/Droplist/DropList.d.ts +2 -1
  19. package/dist/components/Lists/Droplist/DropList.js +85 -50
  20. package/dist/components/Lists/List/List.d.ts +7 -11
  21. package/dist/components/Lists/List/List.js +61 -53
  22. package/dist/components/Lists/ListPrivate/ListPrivate.d.ts +18 -20
  23. package/dist/components/Lists/ListPrivate/ListPrivate.js +10 -9
  24. package/dist/components/Lists/contexts/CollapseProvider.d.ts +13 -1
  25. package/dist/components/Lists/contexts/CollapseProvider.js +2 -0
  26. package/dist/components/Lists/contexts/FocusListProvider.d.ts +9 -0
  27. package/dist/components/Lists/contexts/FocusListProvider.js +5 -0
  28. package/dist/components/Lists/contexts/NewListProvider.d.ts +27 -0
  29. package/dist/components/Lists/contexts/{ListProvider.js → NewListProvider.js} +10 -5
  30. package/dist/components/Lists/contexts/SelectionProvider.d.ts +28 -15
  31. package/dist/components/Lists/contexts/SelectionProvider.js +14 -8
  32. package/dist/components/Lists/contexts/index.d.ts +2 -2
  33. package/dist/components/Lists/contexts/index.js +2 -2
  34. package/dist/components/Lists/hooks.d.ts +11 -11
  35. package/dist/components/Lists/hooks.js +80 -41
  36. package/dist/components/Lists/types.d.ts +19 -15
  37. package/dist/helperComponents/ItemContent/ItemContent.d.ts +15 -0
  38. package/dist/helperComponents/ItemContent/ItemContent.js +23 -0
  39. package/dist/helperComponents/ItemContent/index.d.ts +1 -0
  40. package/dist/helperComponents/ItemContent/index.js +1 -0
  41. package/dist/helperComponents/ItemContent/styles.module.css +117 -0
  42. package/dist/helperComponents/ListEmptyState/ListEmptyState.d.ts +2 -2
  43. package/dist/helperComponents/ListEmptyState/ListEmptyState.js +13 -4
  44. package/dist/helperComponents/index.d.ts +1 -0
  45. package/dist/helperComponents/index.js +1 -0
  46. package/dist/hooks.d.ts +1 -1
  47. package/dist/hooks.js +15 -10
  48. package/dist/index.d.ts +3 -2
  49. package/dist/index.js +3 -2
  50. package/dist/legacy/components/Items/hooks.d.ts +12 -0
  51. package/dist/legacy/components/Items/hooks.js +33 -0
  52. package/dist/legacy/components/index.d.ts +1 -0
  53. package/dist/legacy/components/index.js +1 -0
  54. package/dist/legacy/hooks.d.ts +5 -0
  55. package/dist/legacy/hooks.js +15 -0
  56. package/dist/legacy/index.d.ts +3 -0
  57. package/dist/legacy/index.js +3 -0
  58. package/dist/legacy/utils.d.ts +29 -0
  59. package/dist/legacy/utils.js +107 -0
  60. package/dist/types.d.ts +1 -1
  61. package/dist/utils.d.ts +27 -25
  62. package/dist/utils.js +74 -103
  63. package/package.json +12 -12
  64. package/src/components/Items/AccordionItem/AccordionItem.tsx +26 -19
  65. package/src/components/Items/BaseItem/BaseItem.tsx +48 -46
  66. package/src/components/Items/BaseItem/styles.module.scss +8 -70
  67. package/src/components/Items/GroupItem/GroupItem.tsx +6 -5
  68. package/src/components/Items/NextListItem/NextListItem.tsx +67 -78
  69. package/src/components/Items/SearchItem/SearchItem.tsx +8 -8
  70. package/src/components/Items/hooks.tsx +76 -54
  71. package/src/components/Items/types.ts +61 -31
  72. package/src/components/Items/utils.ts +9 -58
  73. package/src/components/Lists/Droplist/DropList.tsx +155 -102
  74. package/src/components/Lists/List/List.tsx +120 -105
  75. package/src/components/Lists/ListPrivate/ListPrivate.tsx +26 -27
  76. package/src/components/Lists/contexts/CollapseProvider.tsx +22 -0
  77. package/src/components/Lists/contexts/FocusListProvider.tsx +16 -0
  78. package/src/components/Lists/contexts/NewListProvider.tsx +49 -0
  79. package/src/components/Lists/contexts/SelectionProvider.tsx +63 -36
  80. package/src/components/Lists/contexts/index.ts +3 -2
  81. package/src/components/Lists/hooks.ts +139 -75
  82. package/src/components/Lists/types.ts +29 -27
  83. package/src/helperComponents/ItemContent/ItemContent.tsx +63 -0
  84. package/src/helperComponents/ItemContent/index.ts +1 -0
  85. package/src/helperComponents/ItemContent/styles.module.scss +78 -0
  86. package/src/helperComponents/ListEmptyState/ListEmptyState.tsx +21 -13
  87. package/src/helperComponents/index.ts +1 -0
  88. package/src/hooks.ts +21 -15
  89. package/src/index.ts +9 -2
  90. package/src/legacy/components/Items/hooks.tsx +53 -0
  91. package/src/legacy/components/index.ts +1 -0
  92. package/src/legacy/hooks.ts +27 -0
  93. package/src/legacy/index.ts +3 -0
  94. package/src/legacy/utils.ts +166 -0
  95. package/src/types.ts +1 -1
  96. package/src/utils.ts +153 -139
  97. package/dist/components/Items/NextListItem/hooks.d.ts +0 -19
  98. package/dist/components/Items/NextListItem/hooks.js +0 -80
  99. package/dist/components/Lists/contexts/ListProvider.d.ts +0 -17
  100. package/dist/components/Lists/contexts/ParentListProvider.d.ts +0 -18
  101. package/dist/components/Lists/contexts/ParentListProvider.js +0 -12
  102. package/src/components/Items/NextListItem/hooks.ts +0 -105
  103. package/src/components/Lists/contexts/CollapseProvider.ts +0 -9
  104. package/src/components/Lists/contexts/ListProvider.tsx +0 -28
  105. 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
- export type BaseItemProps = WithSupportProps<{
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
- content: ItemContentProps;
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?: string | number;
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, 'switch' | 'inactive'>;
77
+ type BaseItemsWithoutNonGroupProps<Meta = AnyType> = Omit<BaseItemProps<Meta>, 'switch' | 'inactive'>;
76
78
 
77
- // eslint-disable-next-line no-use-before-define
78
- export type ItemProps = BaseItemProps | AccordionItemProps | NextListItemProps | GroupItemProps;
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 { createRef } from 'react';
1
+ import { ItemContentProps } from '../../helperComponents';
2
+ import { AccordionItemProps, AnyType, BaseItemProps, GroupItemProps, NextListItemProps } from './types';
2
3
 
3
- import { AccordionItemProps, BaseItemProps, GroupItemProps, ItemProps, NextListItemProps } from './types';
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
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
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
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
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
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
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 getSlicedItems({
26
- items,
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 { cloneElement, isValidElement, KeyboardEvent, ReactNode, useCallback, useMemo, useRef } from 'react';
2
- import { useUncontrolledProp } from 'uncontrollable';
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 { extractItemIds, extractItemRefs, withCollapsedItems } from '../../../utils';
7
- import { addItemsIds, getSlicedItems, useItemsWithIds } from '../../Items';
8
- import { extractSelectionProps, ParentListContext, SelectionProvider } from '../contexts';
9
- import { useKeyboardNavigation } from '../hooks';
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
- export function Droplist({
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 memorizedItems = useMemo(
32
- () => addItemsIds((pinTop ?? []).concat(itemsProp).concat(pinBottom ?? [])),
33
- [itemsProp, pinBottom, pinTop],
34
- );
35
-
36
- const [openCollapsedItems, setOpenCollapsedItems] = useUncontrolledProp<Array<number | string>>(
37
- collapse.value,
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 { search: searchItem, footerRefs } = useItemsWithIds({
43
- search: hasSearch,
44
- footerActiveElementsRefs,
50
+ const [open = false, setOpen] = useValueControl<boolean>({
51
+ value: openProp,
52
+ defaultValue: false,
53
+ onChange: onOpenChange,
45
54
  });
46
55
 
47
- const { items, itemRefs, ids, expandedIds } = useMemo(() => {
48
- const res = withCollapsedItems({
49
- items: memorizedItems,
50
- openCollapsedItems,
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
- const items = searchItem.concat(res.items).concat(footerRefs);
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
- items,
57
- ids: extractItemIds(searchItem).concat(res.ids).concat(extractItemIds(footerRefs)),
58
- itemRefs: extractItemRefs(searchItem).concat(res.itemRefs).concat(extractItemRefs(footerRefs)),
59
- expandedIds: res.expandedIds,
101
+ ids,
102
+ itemRefs,
103
+ expandedIds,
60
104
  };
61
- }, [footerRefs, memorizedItems, openCollapsedItems, searchItem]);
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<HTMLUListElement>(null);
70
-
71
- const {
72
- activeFocusIndex,
73
- setActiveFocusIndex,
74
- openNestedIndex,
75
- handleListKeyDown,
76
- resetNestedIndex,
77
- resetActiveFocusIndex,
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 [open, setOpen] = useUncontrolledProp<boolean>(openProp, false, onOpenChange);
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
- resetNestedIndex();
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
- [setActiveFocusIndex, setOpen],
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
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
128
- // @ts-ignore
129
- <SelectionProvider {...extractSelectionProps(props)}>
130
- <ParentListContext.Provider
131
- value={{
132
- parentIds: ids,
133
- parentActiveFocusIndex: activeFocusIndex,
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
- {triggerElem}
173
- </Dropdown>
174
- </ParentListContext.Provider>
175
- </SelectionProvider>
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
  }