@snack-uikit/list 0.20.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +17 -1
  3. package/dist/cjs/components/Items/BaseItem/BaseItem.js +4 -2
  4. package/dist/cjs/components/Items/NextListItem/NextListItem.js +4 -3
  5. package/dist/cjs/components/Items/SearchItem/SearchItem.js +4 -2
  6. package/dist/cjs/components/Items/hooks.js +11 -9
  7. package/dist/cjs/components/Items/utils.js +5 -3
  8. package/dist/cjs/components/Lists/Droplist/DropList.js +12 -7
  9. package/dist/cjs/components/Lists/List/List.d.ts +7 -1
  10. package/dist/cjs/components/Lists/List/List.js +19 -11
  11. package/dist/cjs/components/Lists/ListPrivate/ListPrivate.d.ts +3 -2
  12. package/dist/cjs/components/Lists/ListPrivate/ListPrivate.js +17 -3
  13. package/dist/cjs/components/Lists/contexts/NewListProvider.d.ts +2 -4
  14. package/dist/cjs/components/Lists/contexts/NewListProvider.js +7 -3
  15. package/dist/cjs/components/Lists/hooks.d.ts +6 -1
  16. package/dist/cjs/components/Lists/hooks.js +32 -18
  17. package/dist/cjs/components/Lists/types.d.ts +12 -3
  18. package/dist/cjs/constants.d.ts +8 -0
  19. package/dist/cjs/constants.js +14 -0
  20. package/dist/cjs/index.d.ts +2 -0
  21. package/dist/cjs/index.js +3 -1
  22. package/dist/cjs/utils.d.ts +16 -0
  23. package/dist/cjs/utils.js +25 -0
  24. package/dist/esm/components/Items/BaseItem/BaseItem.js +3 -3
  25. package/dist/esm/components/Items/NextListItem/NextListItem.js +2 -1
  26. package/dist/esm/components/Items/SearchItem/SearchItem.js +3 -2
  27. package/dist/esm/components/Items/hooks.js +6 -4
  28. package/dist/esm/components/Items/utils.js +5 -3
  29. package/dist/esm/components/Lists/Droplist/DropList.js +20 -4
  30. package/dist/esm/components/Lists/List/List.d.ts +7 -1
  31. package/dist/esm/components/Lists/List/List.js +23 -6
  32. package/dist/esm/components/Lists/ListPrivate/ListPrivate.d.ts +3 -2
  33. package/dist/esm/components/Lists/ListPrivate/ListPrivate.js +12 -4
  34. package/dist/esm/components/Lists/contexts/NewListProvider.d.ts +2 -4
  35. package/dist/esm/components/Lists/contexts/NewListProvider.js +4 -2
  36. package/dist/esm/components/Lists/hooks.d.ts +6 -1
  37. package/dist/esm/components/Lists/hooks.js +29 -19
  38. package/dist/esm/components/Lists/types.d.ts +12 -3
  39. package/dist/esm/constants.d.ts +8 -0
  40. package/dist/esm/constants.js +8 -0
  41. package/dist/esm/index.d.ts +2 -0
  42. package/dist/esm/index.js +2 -0
  43. package/dist/esm/utils.d.ts +16 -0
  44. package/dist/esm/utils.js +16 -0
  45. package/package.json +7 -7
  46. package/src/components/Items/BaseItem/BaseItem.tsx +4 -4
  47. package/src/components/Items/NextListItem/NextListItem.tsx +2 -1
  48. package/src/components/Items/SearchItem/SearchItem.tsx +3 -2
  49. package/src/components/Items/hooks.tsx +6 -4
  50. package/src/components/Items/utils.ts +5 -3
  51. package/src/components/Lists/Droplist/DropList.tsx +20 -3
  52. package/src/components/Lists/List/List.tsx +25 -5
  53. package/src/components/Lists/ListPrivate/ListPrivate.tsx +13 -3
  54. package/src/components/Lists/contexts/NewListProvider.tsx +6 -2
  55. package/src/components/Lists/hooks.ts +35 -20
  56. package/src/components/Lists/types.ts +11 -3
  57. package/src/constants.ts +8 -0
  58. package/src/index.ts +3 -0
  59. package/src/utils.ts +20 -0
package/CHANGELOG.md CHANGED
@@ -3,6 +3,18 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # 0.21.0 (2024-10-31)
7
+
8
+
9
+ ### Features
10
+
11
+ * **PDS-438:** add keyboardNavigationRef, hasListInFocusChain props; export ids for navigation ([4472173](https://github.com/cloud-ru-tech/snack-uikit/commit/447217313b3aaf71196f20ce3298ce129a49a750))
12
+ * **PDS-438:** add scrollToSelectedItem prop ([ffc168f](https://github.com/cloud-ru-tech/snack-uikit/commit/ffc168f2201891a48b50d638ee0f4de91df18aa6))
13
+
14
+
15
+
16
+
17
+
6
18
  # 0.20.0 (2024-10-28)
7
19
 
8
20
 
package/README.md CHANGED
@@ -74,6 +74,7 @@
74
74
  | search | `SearchState` | - | Настройки поисковой строки |
75
75
  | collapse | `CollapseState` | {} | Настройки раскрытия элементов |
76
76
  | loading | `boolean` | - | Флаг, отвещающий за состояние загрузки списка |
77
+ | scrollToSelectedItem | `boolean` | - | Флаг, отвещающий за прокручивание до выбранного элемента |
77
78
  | selection | `SelectionSingleState \| SelectionMultipleState` | - | |
78
79
  | size | "s" \| "m" \| "l" | s | Размер списка |
79
80
  | marker | `boolean` | true | Отображать ли маркер у выбранного жлемента списка |
@@ -97,12 +98,15 @@
97
98
  | pinBottom | `Item[]` | - | Элементы списка, закрепленные снизу |
98
99
  | footer | `ReactNode ;` | - | Кастомизируемый элемент в конце списка |
99
100
  | footerActiveElementsRefs | `RefObject<HTMLElement>[]` | - | Список ссылок на кастомные элементы, помещенные в специальную секцию внизу списка |
101
+ | keyboardNavigationRef | `RefObject<{ focusItem(id: ItemId): void; }>` | - | Ссылка на управление навигацией листа с клавиатуры |
100
102
  | search | `SearchState` | - | Настройки поисковой строки |
101
103
  | tabIndex | `number` | - | Tab Index |
102
104
  | collapse | `CollapseState` | {} | Настройки раскрытия элементов |
103
105
  | className | `string` | - | CSS-класс |
104
- | onKeyDown | `(e: KeyboardEvent<HTMLElement>) => void` | - | |
105
106
  | loading | `boolean` | - | Флаг, отвещающий за состояние загрузки списка |
107
+ | onKeyDown | `(e: KeyboardEvent<HTMLElement>) => void` | - | Обработчик события по нажатию клавиш |
108
+ | hasListInFocusChain | `boolean` | true | Флаг, отвещающий за включение самого родительского контейнера листа в цепочку фокусирующихся элементов |
109
+ | scrollToSelectedItem | `boolean` | - | Флаг, отвещающий за прокручивание до выбранного элемента |
106
110
  | selection | `SelectionSingleState \| SelectionMultipleState` | - | |
107
111
  | size | "s" \| "m" \| "l" | s | Размер списка |
108
112
  | marker | `boolean` | true | Отображать ли маркер у выбранного жлемента списка |
@@ -129,6 +133,18 @@
129
133
  | truncate | `TruncateProps` | - | |
130
134
  | disabled | `boolean` | - | |
131
135
  | className | `string` | - | CSS-класс |
136
+ ## getFooterItemId
137
+ `helper`
138
+
139
+ Возвращает id для элемента футера
140
+ ## getItemAutoId
141
+ `helper`
142
+
143
+ Возвращает id для элемента, подставляя перфикс
144
+ ## getDefaultItemId
145
+ `helper`
146
+
147
+ Возвращает id для дефолтного элемента
132
148
 
133
149
 
134
150
  [//]: DOCUMENTATION_SECTION_END
@@ -57,7 +57,9 @@ function BaseItem(_a) {
57
57
  const {
58
58
  size = 's',
59
59
  marker,
60
- contentRender
60
+ contentRender,
61
+ firstItemId,
62
+ focusFlattenItems
61
63
  } = (0, contexts_1.useNewListContext)();
62
64
  const {
63
65
  level = 0
@@ -146,7 +148,7 @@ function BaseItem(_a) {
146
148
  "data-size": size,
147
149
  onClick: handleItemClick,
148
150
  onMouseDown: handleItemMouseDown,
149
- tabIndex: -1,
151
+ tabIndex: id === focusFlattenItems[firstItemId].originalId ? 0 : -1,
150
152
  "data-non-pointer": inactive && !onClick,
151
153
  "data-variant": mode || undefined,
152
154
  "data-open": open || undefined,
@@ -16,12 +16,13 @@ const jsx_runtime_1 = require("react/jsx-runtime");
16
16
  const react_1 = require("react");
17
17
  const dropdown_1 = require("@snack-uikit/dropdown");
18
18
  const icons_1 = require("@snack-uikit/icons");
19
+ const constants_1 = require("../../../constants");
19
20
  const contexts_1 = require("../../Lists/contexts");
20
21
  const ListPrivate_1 = require("../../Lists/ListPrivate");
21
22
  const BaseItem_1 = require("../BaseItem");
22
23
  const hooks_1 = require("../hooks");
23
24
  const utils_1 = require("../utils");
24
- const constants_1 = require("./constants");
25
+ const constants_2 = require("./constants");
25
26
  function NextListItem(_a) {
26
27
  var {
27
28
  items,
@@ -92,7 +93,7 @@ function NextListItem(_a) {
92
93
  allChildIds
93
94
  });
94
95
  const handleOutsideClick = (0, react_1.useCallback)(() => {
95
- forceUpdateActiveItemId === null || forceUpdateActiveItemId === void 0 ? void 0 : forceUpdateActiveItemId('~drop-focus');
96
+ forceUpdateActiveItemId === null || forceUpdateActiveItemId === void 0 ? void 0 : forceUpdateActiveItemId(constants_1.ITEM_PREFIXES.dropFocus);
96
97
  setOpen(false);
97
98
  return true;
98
99
  }, [forceUpdateActiveItemId]);
@@ -103,7 +104,7 @@ function NextListItem(_a) {
103
104
  const listRef = (0, react_1.useRef)(null);
104
105
  return (0, jsx_runtime_1.jsx)(dropdown_1.Dropdown, {
105
106
  outsideClick: handleOutsideClick,
106
- fallbackPlacements: constants_1.FALLBACK_PLACEMENTS,
107
+ fallbackPlacements: constants_2.FALLBACK_PLACEMENTS,
107
108
  content: (0, jsx_runtime_1.jsx)(ListPrivate_1.ListPrivate, {
108
109
  onKeyDown: handleListKeyDown,
109
110
  items: items,
@@ -12,6 +12,7 @@ exports.SearchItem = SearchItem;
12
12
  const jsx_runtime_1 = require("react/jsx-runtime");
13
13
  const classnames_1 = __importDefault(require("classnames"));
14
14
  const search_private_1 = require("@snack-uikit/search-private");
15
+ const constants_1 = require("../../../constants");
15
16
  const contexts_1 = require("../../Lists/contexts");
16
17
  const styles_module_scss_1 = __importDefault(require('../styles.module.css'));
17
18
  const styles_module_scss_2 = __importDefault(require('./styles.module.css'));
@@ -21,7 +22,8 @@ function SearchItem(_ref) {
21
22
  itemRef
22
23
  } = _ref;
23
24
  const {
24
- size = 's'
25
+ size = 's',
26
+ firstItemId
25
27
  } = (0, contexts_1.useNewListContext)();
26
28
  const handleKeyDown = e => {
27
29
  if (['ArrowDown', 'ArrowUp'].includes(e.key)) {
@@ -40,7 +42,7 @@ function SearchItem(_ref) {
40
42
  "data-test-id": 'list__search-item',
41
43
  children: (0, jsx_runtime_1.jsx)(search_private_1.SearchPrivate, Object.assign({
42
44
  size: size,
43
- tabIndex: -1,
45
+ tabIndex: constants_1.ITEM_PREFIXES.search === firstItemId ? 0 : -1,
44
46
  onKeyDown: handleKeyDown,
45
47
  onFocus: handleFocus
46
48
  }, search, {
@@ -8,13 +8,15 @@ exports.useCreateBaseItems = useCreateBaseItems;
8
8
  exports.useGroupItemSelection = useGroupItemSelection;
9
9
  const react_1 = require("react");
10
10
  const react_2 = require("react");
11
+ const constants_1 = require("../../constants");
12
+ const utils_1 = require("../../utils");
11
13
  const contexts_1 = require("../Lists/contexts");
12
14
  const AccordionItem_1 = require("./AccordionItem");
13
15
  const BaseItem_1 = require("./BaseItem");
14
16
  const GroupItem_1 = require("./GroupItem");
15
17
  const GroupSelectItem_1 = require("./GroupSelectItem");
16
18
  const NextListItem_1 = require("./NextListItem");
17
- const utils_1 = require("./utils");
19
+ const utils_2 = require("./utils");
18
20
  function useRenderItems(focusCloseChildIds) {
19
21
  const {
20
22
  focusFlattenItems,
@@ -35,27 +37,27 @@ function useRenderItems(focusCloseChildIds) {
35
37
  items
36
38
  } = focusFlattenItems[id];
37
39
  const flattenItem = flattenItems[originalId];
38
- if ((0, utils_1.isGroupItem)(flattenItem) || !isSelectionMultiple && (0, utils_1.isGroupSelectItem)(flattenItem)) {
40
+ if ((0, utils_2.isGroupItem)(flattenItem) || !isSelectionMultiple && (0, utils_2.isGroupSelectItem)(flattenItem)) {
39
41
  return (0, react_1.createElement)(GroupItem_1.GroupItem, Object.assign({}, flattenItem, {
40
42
  items: items,
41
43
  key: key
42
44
  }));
43
45
  }
44
- if ((0, utils_1.isGroupSelectItem)(flattenItem)) {
46
+ if ((0, utils_2.isGroupSelectItem)(flattenItem)) {
45
47
  return (0, react_1.createElement)(GroupSelectItem_1.GroupSelectItem, Object.assign({}, flattenItem, {
46
48
  items: items,
47
49
  itemRef: itemRef,
48
50
  key: key
49
51
  }));
50
52
  }
51
- if ((0, utils_1.isAccordionItem)(flattenItem)) {
53
+ if ((0, utils_2.isAccordionItem)(flattenItem)) {
52
54
  return (0, react_1.createElement)(AccordionItem_1.AccordionItem, Object.assign({}, flattenItem, {
53
55
  items: items,
54
56
  itemRef: itemRef,
55
57
  key: key
56
58
  }));
57
59
  }
58
- if ((0, utils_1.isNextListItem)(flattenItem)) {
60
+ if ((0, utils_2.isNextListItem)(flattenItem)) {
59
61
  return (0, react_1.createElement)(NextListItem_1.NextListItem, Object.assign({}, flattenItem, {
60
62
  focusId: id,
61
63
  items: items,
@@ -79,15 +81,15 @@ function useCreateBaseItems(_ref) {
79
81
  return {
80
82
  searchItem: {
81
83
  itemRef: (0, react_2.createRef)(),
82
- id: '~search',
83
- parentId: '~main',
84
+ id: constants_1.ITEM_PREFIXES.search,
85
+ parentId: constants_1.ITEM_PREFIXES.default,
84
86
  items: [],
85
87
  allChildIds: []
86
88
  },
87
89
  footerItems: (_a = footerActiveElementsRefs === null || footerActiveElementsRefs === void 0 ? void 0 : footerActiveElementsRefs.map((itemRef, idx) => ({
88
- id: `~footer__${idx}`,
90
+ id: (0, utils_1.getFooterItemId)(idx),
89
91
  itemRef,
90
- parentId: '~main',
92
+ parentId: constants_1.ITEM_PREFIXES.default,
91
93
  items: [],
92
94
  allChildIds: []
93
95
  }))) !== null && _a !== void 0 ? _a : []
@@ -21,6 +21,8 @@ exports.isContentItem = isContentItem;
21
21
  exports.kindFlattenItems = kindFlattenItems;
22
22
  exports.extractActiveItems = extractActiveItems;
23
23
  const react_1 = require("react");
24
+ const constants_1 = require("../../constants");
25
+ const utils_1 = require("../../utils");
24
26
  function isBaseItem(item) {
25
27
  return item && !('items' in item);
26
28
  }
@@ -56,10 +58,10 @@ function kindFlattenItems(_ref) {
56
58
  item,
57
59
  idx,
58
60
  prefix,
59
- parentId = '~main'
61
+ parentId = constants_1.ITEM_PREFIXES.default
60
62
  } = _ref2;
61
63
  var _a, _b;
62
- const autoId = prefix !== undefined ? [prefix, idx].join('-') : String(idx);
64
+ const autoId = prefix !== undefined ? (0, utils_1.getItemAutoId)(prefix, idx) : String(idx);
63
65
  const itemId = (_a = !isGroupItem(item) ? item.id : undefined) !== null && _a !== void 0 ? _a : autoId;
64
66
  if (isBaseItem(item)) {
65
67
  flattenItems[itemId] = Object.assign(Object.assign({}, item), {
@@ -92,7 +94,7 @@ function kindFlattenItems(_ref) {
92
94
  items
93
95
  } = item,
94
96
  rest = __rest(item, ["items"]);
95
- const childActiveParent = isGroupItem(item) ? parentId !== null && parentId !== void 0 ? parentId : '~main' : autoId;
97
+ const childActiveParent = isGroupItem(item) ? parentId !== null && parentId !== void 0 ? parentId : constants_1.ITEM_PREFIXES.default : autoId;
96
98
  const filteredItems = items.filter(item => !item.hidden);
97
99
  for (let idx = 0; idx < filteredItems.length; idx++) {
98
100
  const {
@@ -23,6 +23,7 @@ const merge_refs_1 = __importDefault(require("merge-refs"));
23
23
  const react_1 = require("react");
24
24
  const dropdown_1 = require("@snack-uikit/dropdown");
25
25
  const utils_1 = require("@snack-uikit/utils");
26
+ const constants_1 = require("../../../constants");
26
27
  const Items_1 = require("../../Items");
27
28
  const contexts_1 = require("../contexts");
28
29
  const hooks_1 = require("../hooks");
@@ -75,18 +76,18 @@ function Droplist(_a) {
75
76
  const _b = (0, react_1.useMemo)(() => {
76
77
  const pinTop = (0, Items_1.kindFlattenItems)({
77
78
  items: pinTopProp,
78
- prefix: '~pinTop',
79
- parentId: '~main'
79
+ prefix: constants_1.ITEM_PREFIXES.pinTop,
80
+ parentId: constants_1.ITEM_PREFIXES.default
80
81
  });
81
82
  const items = (0, Items_1.kindFlattenItems)({
82
83
  items: itemsProp,
83
- prefix: '~main',
84
- parentId: '~main'
84
+ prefix: constants_1.ITEM_PREFIXES.default,
85
+ parentId: constants_1.ITEM_PREFIXES.default
85
86
  });
86
87
  const pinBottom = (0, Items_1.kindFlattenItems)({
87
88
  items: pinBottomProp,
88
- prefix: '~pinBottom',
89
- parentId: '~main'
89
+ prefix: constants_1.ITEM_PREFIXES.pinBottom,
90
+ parentId: constants_1.ITEM_PREFIXES.default
90
91
  });
91
92
  const flattenItems = Object.assign(Object.assign(Object.assign({}, pinTop.flattenItems), pinBottom.flattenItems), items.flattenItems);
92
93
  const focusFlattenItems = Object.assign(Object.assign(Object.assign({}, pinTop.focusFlattenItems), pinBottom.focusFlattenItems), items.focusFlattenItems);
@@ -150,6 +151,7 @@ function Droplist(_a) {
150
151
  }, [footerItems, hasSearch, memorizedItems, openCollapseItems, searchItem.id, selection === null || selection === void 0 ? void 0 : selection.mode]);
151
152
  const triggerElemRef = (0, react_1.useRef)(null);
152
153
  const listRef = (0, react_1.useRef)(null);
154
+ const firstItemId = ids[0];
153
155
  const {
154
156
  handleListKeyDownFactory,
155
157
  resetActiveItemId,
@@ -157,7 +159,9 @@ function Droplist(_a) {
157
159
  forceUpdateActiveItemId
158
160
  } = (0, hooks_1.useNewKeyboardNavigation)({
159
161
  mainRef: triggerElemRefProp !== null && triggerElemRefProp !== void 0 ? triggerElemRefProp : triggerElemRef,
160
- focusFlattenItems
162
+ focusFlattenItems,
163
+ hasListInFocusChain: true,
164
+ firstItemId
161
165
  });
162
166
  const handleListKeyDown = (0, react_1.useCallback)(e => handleListKeyDownFactory(ids, expandedIds)(e), [handleListKeyDownFactory, ids, expandedIds]);
163
167
  const handleOpenChange = (0, react_1.useCallback)(open => {
@@ -203,6 +207,7 @@ function Droplist(_a) {
203
207
  contentRender: contentRender,
204
208
  size: size,
205
209
  marker: marker,
210
+ firstItemId: firstItemId,
206
211
  children: (0, jsx_runtime_1.jsx)(contexts_1.SelectionProvider, Object.assign({}, selection, {
207
212
  children: (0, jsx_runtime_1.jsx)(contexts_1.CollapseContext.Provider, {
208
213
  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>>;
@@ -22,6 +22,7 @@ const classnames_1 = __importDefault(require("classnames"));
22
22
  const merge_refs_1 = __importDefault(require("merge-refs"));
23
23
  const react_1 = require("react");
24
24
  const utils_1 = require("@snack-uikit/utils");
25
+ const constants_1 = require("../../../constants");
25
26
  const helperComponents_1 = require("../../../helperComponents");
26
27
  const Items_1 = require("../../Items");
27
28
  const contexts_1 = require("../contexts");
@@ -42,9 +43,11 @@ exports.List = (0, react_1.forwardRef)((_a, ref) => {
42
43
  selection,
43
44
  contentRender,
44
45
  size = 's',
45
- marker = true
46
+ marker = true,
47
+ keyboardNavigationRef,
48
+ hasListInFocusChain = true
46
49
  } = _a,
47
- props = __rest(_a, ["items", "search", "pinBottom", "pinTop", "footerActiveElementsRefs", "onKeyDown", "tabIndex", "className", "collapse", "selection", "contentRender", "size", "marker"]);
50
+ props = __rest(_a, ["items", "search", "pinBottom", "pinTop", "footerActiveElementsRefs", "onKeyDown", "tabIndex", "className", "collapse", "selection", "contentRender", "size", "marker", "keyboardNavigationRef", "hasListInFocusChain"]);
48
51
  const hasSearch = (0, react_1.useMemo)(() => Boolean(search), [search]);
49
52
  const [openCollapseItems = [], setOpenCollapsedItems] = (0, utils_1.useValueControl)(collapse);
50
53
  const toggleOpenCollapseItem = (0, react_1.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]);
@@ -60,18 +63,18 @@ exports.List = (0, react_1.forwardRef)((_a, ref) => {
60
63
  const _b = (0, react_1.useMemo)(() => {
61
64
  const pinTop = (0, Items_1.kindFlattenItems)({
62
65
  items: pinTopProp,
63
- prefix: '~pinTop',
64
- parentId: '~main'
66
+ prefix: constants_1.ITEM_PREFIXES.pinTop,
67
+ parentId: constants_1.ITEM_PREFIXES.default
65
68
  });
66
69
  const items = (0, Items_1.kindFlattenItems)({
67
70
  items: itemsProp,
68
- prefix: '~main',
69
- parentId: '~main'
71
+ prefix: constants_1.ITEM_PREFIXES.default,
72
+ parentId: constants_1.ITEM_PREFIXES.default
70
73
  });
71
74
  const pinBottom = (0, Items_1.kindFlattenItems)({
72
75
  items: pinBottomProp,
73
- prefix: '~pinBottom',
74
- parentId: '~main'
76
+ prefix: constants_1.ITEM_PREFIXES.pinBottom,
77
+ parentId: constants_1.ITEM_PREFIXES.default
75
78
  });
76
79
  const flattenItems = Object.assign(Object.assign(Object.assign({}, pinTop.flattenItems), pinBottom.flattenItems), items.flattenItems);
77
80
  const focusFlattenItems = Object.assign(Object.assign(Object.assign({}, pinTop.focusFlattenItems), pinBottom.focusFlattenItems), items.focusFlattenItems);
@@ -135,6 +138,7 @@ exports.List = (0, react_1.forwardRef)((_a, ref) => {
135
138
  }, [footerItems, hasSearch, memorizedItems, openCollapseItems, searchItem.id, selection === null || selection === void 0 ? void 0 : selection.mode]);
136
139
  const listRef = (0, react_1.useRef)(null);
137
140
  const btnRef = (0, react_1.useRef)(null);
141
+ const firstItemId = ids[0];
138
142
  const {
139
143
  handleListKeyDownFactory,
140
144
  activeItemId,
@@ -143,7 +147,10 @@ exports.List = (0, react_1.forwardRef)((_a, ref) => {
143
147
  } = (0, hooks_1.useNewKeyboardNavigation)({
144
148
  mainRef: listRef,
145
149
  btnRef,
146
- focusFlattenItems
150
+ focusFlattenItems,
151
+ keyboardNavigationRef,
152
+ hasListInFocusChain,
153
+ firstItemId
147
154
  });
148
155
  const handleListKeyDown = (0, react_1.useCallback)(e => handleListKeyDownFactory(ids, expandedIds)(e), [handleListKeyDownFactory, ids, expandedIds]);
149
156
  const isActive = (0, utils_1.isBrowser)() && listRef.current === document.activeElement && activeItemId === undefined;
@@ -160,6 +167,7 @@ exports.List = (0, react_1.forwardRef)((_a, ref) => {
160
167
  contentRender: contentRender,
161
168
  size: size,
162
169
  marker: marker,
170
+ firstItemId: firstItemId,
163
171
  children: (0, jsx_runtime_1.jsx)(contexts_1.SelectionProvider, Object.assign({}, selection, {
164
172
  children: (0, jsx_runtime_1.jsx)(contexts_1.CollapseContext.Provider, {
165
173
  value: {
@@ -183,10 +191,10 @@ exports.List = (0, react_1.forwardRef)((_a, ref) => {
183
191
  ref: (0, merge_refs_1.default)(ref, listRef),
184
192
  onFocus: handleOnFocus,
185
193
  onKeyDown: mergedHandlerKeyDown,
186
- tabIndex: tabIndex,
194
+ tabIndex: hasListInFocusChain ? tabIndex : undefined,
187
195
  search: search,
188
196
  nested: false
189
- })), (0, jsx_runtime_1.jsx)(helperComponents_1.HiddenTabButton, {
197
+ })), hasListInFocusChain && (0, jsx_runtime_1.jsx)(helperComponents_1.HiddenTabButton, {
190
198
  ref: btnRef,
191
199
  listRef: listRef,
192
200
  tabIndex: tabIndex
@@ -1,4 +1,5 @@
1
- export declare const ListPrivate: import("react").ForwardRefExoticComponent<Omit<import("../types").ListProps, "items" | "pinTop" | "pinBottom"> & {
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?: import("../../Items").FlattenBaseItem;
10
+ searchItem?: FlattenBaseItem;
10
11
  pinTop?: import("../../Items").ItemId[];
11
12
  items: import("../../Items").ItemId[];
12
13
  pinBottom?: import("../../Items").ItemId[];
@@ -54,12 +54,19 @@ exports.ListPrivate = (0, react_1.forwardRef)((_a, ref) => {
54
54
  noResultsState,
55
55
  errorDataState,
56
56
  dataError,
57
- dataFiltered
57
+ dataFiltered,
58
+ scrollToSelectedItem
58
59
  } = _a,
59
- 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"]);
60
+ 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"]);
60
61
  const {
61
- size = 's'
62
+ size = 's',
63
+ flattenItems
62
64
  } = (0, contexts_1.useNewListContext)();
65
+ const {
66
+ value,
67
+ isSelectionSingle
68
+ } = (0, contexts_1.useSelectionContext)();
69
+ const selectedItem = isSelectionSingle && value ? flattenItems[value] : undefined;
63
70
  const itemsJSX = (0, Items_1.useRenderItems)(items);
64
71
  const itemsPinTopJSX = (0, Items_1.useRenderItems)(pinTop);
65
72
  const itemsPinBottomJSX = (0, Items_1.useRenderItems)(pinBottom);
@@ -90,6 +97,12 @@ exports.ListPrivate = (0, react_1.forwardRef)((_a, ref) => {
90
97
  dataFiltered: dataFiltered !== null && dataFiltered !== void 0 ? dataFiltered : Boolean(search === null || search === void 0 ? void 0 : search.value)
91
98
  })]
92
99
  }), [dataError, dataFiltered, emptyStates, hasNoItems, itemsJSX, loading, loadingJSX, search === null || search === void 0 ? void 0 : search.value]);
100
+ const onScrollInitialized = () => {
101
+ var _a, _b;
102
+ if (scrollToSelectedItem) {
103
+ (_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();
104
+ }
105
+ };
93
106
  const listJSX = (0, jsx_runtime_1.jsxs)("ul", Object.assign({
94
107
  className: (0, classnames_1.default)(styles_module_scss_1.default.listContainer, className),
95
108
  ref: ref,
@@ -115,6 +128,7 @@ exports.ListPrivate = (0, react_1.forwardRef)((_a, ref) => {
115
128
  ref: scrollContainerRef,
116
129
  untouchableScrollbars: untouchableScrollbars,
117
130
  onScroll: onScroll,
131
+ onInitialized: onScrollInitialized,
118
132
  children: [content, (0, jsx_runtime_1.jsx)("div", {
119
133
  className: styles_module_scss_2.default.scrollStub,
120
134
  ref: scrollRef
@@ -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 {};
@@ -16,9 +16,11 @@ exports.useNewListContext = useNewListContext;
16
16
  exports.NewListContextProvider = NewListContextProvider;
17
17
  const jsx_runtime_1 = require("react/jsx-runtime");
18
18
  const react_1 = require("react");
19
+ const constants_1 = require("../../../constants");
19
20
  exports.ListContext = (0, react_1.createContext)({
20
21
  flattenItems: {},
21
- focusFlattenItems: {}
22
+ focusFlattenItems: {},
23
+ firstItemId: constants_1.ITEM_PREFIXES.default
22
24
  });
23
25
  function useNewListContext() {
24
26
  return (0, react_1.useContext)(exports.ListContext);
@@ -29,14 +31,16 @@ function extractListProps(_ref) {
29
31
  marker,
30
32
  flattenItems,
31
33
  focusFlattenItems,
32
- contentRender
34
+ contentRender,
35
+ firstItemId
33
36
  } = _ref;
34
37
  return {
35
38
  size,
36
39
  marker,
37
40
  contentRender,
38
41
  flattenItems,
39
- focusFlattenItems
42
+ focusFlattenItems,
43
+ firstItemId
40
44
  };
41
45
  }
42
46
  function NewListContextProvider(_a) {
@@ -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;
@@ -5,14 +5,23 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.useNewKeyboardNavigation = useNewKeyboardNavigation;
7
7
  const react_1 = require("react");
8
+ const constants_1 = require("../../constants");
8
9
  function useNewKeyboardNavigation(_ref) {
9
10
  let {
10
11
  mainRef,
11
12
  btnRef,
12
- focusFlattenItems
13
+ focusFlattenItems,
14
+ keyboardNavigationRef,
15
+ hasListInFocusChain,
16
+ firstItemId
13
17
  } = _ref;
14
- const [activeItemId, setActiveItemId] = (0, react_1.useState)();
15
- const activeItemIdRef = (0, react_1.useRef)();
18
+ const defaultActiveItemId = hasListInFocusChain ? undefined : firstItemId;
19
+ const [activeItemId, setActiveItemId] = (0, react_1.useState)(() => defaultActiveItemId);
20
+ const activeItemIdRef = (0, react_1.useRef)(defaultActiveItemId);
21
+ const resetActiveItemId = (0, react_1.useCallback)(() => {
22
+ setActiveItemId(defaultActiveItemId);
23
+ activeItemIdRef.current = defaultActiveItemId;
24
+ }, [defaultActiveItemId]);
16
25
  const handleListKeyDownFactory = (0, react_1.useCallback)((ids, expandedIds) => e => {
17
26
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
18
27
  switch (e.key) {
@@ -44,11 +53,13 @@ function useNewKeyboardNavigation(_ref) {
44
53
  case 'ArrowUp':
45
54
  {
46
55
  if (ids[0] === activeItemIdRef.current) {
47
- const item = focusFlattenItems[ids[0]];
48
- if (item.parentId === '~main') {
49
- activeItemIdRef.current = undefined;
50
- setActiveItemId(undefined);
51
- (_e = mainRef === null || mainRef === void 0 ? void 0 : mainRef.current) === null || _e === void 0 ? void 0 : _e.focus();
56
+ if (hasListInFocusChain) {
57
+ const item = focusFlattenItems[ids[0]];
58
+ if (item.parentId === constants_1.ITEM_PREFIXES.default) {
59
+ activeItemIdRef.current = undefined;
60
+ setActiveItemId(undefined);
61
+ (_e = mainRef === null || mainRef === void 0 ? void 0 : mainRef.current) === null || _e === void 0 ? void 0 : _e.focus();
62
+ }
52
63
  }
53
64
  } else if (activeItemIdRef.current !== undefined) {
54
65
  const activeIndex = ids.findIndex(id => id === activeItemIdRef.current);
@@ -85,11 +96,15 @@ function useNewKeyboardNavigation(_ref) {
85
96
  case 'Tab':
86
97
  {
87
98
  if (activeItemIdRef.current !== undefined) {
88
- e.preventDefault();
89
- e.stopPropagation();
90
- activeItemIdRef.current = undefined;
91
- setActiveItemId(undefined);
92
- (_h = mainRef === null || mainRef === void 0 ? void 0 : mainRef.current) === null || _h === void 0 ? void 0 : _h.focus();
99
+ if (hasListInFocusChain) {
100
+ e.preventDefault();
101
+ e.stopPropagation();
102
+ activeItemIdRef.current = undefined;
103
+ setActiveItemId(undefined);
104
+ (_h = mainRef === null || mainRef === void 0 ? void 0 : mainRef.current) === null || _h === void 0 ? void 0 : _h.focus();
105
+ } else {
106
+ resetActiveItemId();
107
+ }
93
108
  } else {
94
109
  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();
95
110
  }
@@ -100,11 +115,7 @@ function useNewKeyboardNavigation(_ref) {
100
115
  return;
101
116
  }
102
117
  }
103
- }, [focusFlattenItems, mainRef, btnRef]);
104
- const resetActiveItemId = (0, react_1.useCallback)(() => {
105
- setActiveItemId(undefined);
106
- activeItemIdRef.current = undefined;
107
- }, []);
118
+ }, [focusFlattenItems, hasListInFocusChain, mainRef, resetActiveItemId, btnRef]);
108
119
  const forceUpdateActiveItemId = (0, react_1.useCallback)(itemId => {
109
120
  var _a, _b;
110
121
  setActiveItemId(itemId);
@@ -112,6 +123,9 @@ function useNewKeyboardNavigation(_ref) {
112
123
  const item = focusFlattenItems[itemId];
113
124
  (_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();
114
125
  }, [focusFlattenItems]);
126
+ (0, react_1.useImperativeHandle)(keyboardNavigationRef, () => ({
127
+ focusItem: forceUpdateActiveItemId
128
+ }), [forceUpdateActiveItemId]);
115
129
  return {
116
130
  resetActiveItemId,
117
131
  activeItemId,