@snack-uikit/list 0.29.7-preview-c824da44.0 → 0.30.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 CHANGED
@@ -3,6 +3,17 @@
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.30.0 (2025-06-23)
7
+
8
+
9
+ ### Features
10
+
11
+ * **PDS-2461:** list virtualization scroll to initial value ([9ff5a87](https://github.com/cloud-ru-tech/snack-uikit/commit/9ff5a8781367fd3ea0bdc879fb4f5484228459be))
12
+
13
+
14
+
15
+
16
+
6
17
  ## 0.29.6 (2025-06-19)
7
18
 
8
19
  ### Only dependencies have been changed
@@ -1,3 +1,4 @@
1
+ import { ItemId } from '../../Items';
1
2
  export declare const ListPrivate: import("react").ForwardRefExoticComponent<Omit<import("../types").ListProps, "items" | "hasListInFocusChain" | "pinTop" | "pinBottom"> & {
2
3
  nested?: boolean;
3
4
  active?: boolean;
@@ -8,7 +9,7 @@ export declare const ListPrivate: import("react").ForwardRefExoticComponent<Omit
8
9
  onKeyDown?(e: import("react").KeyboardEvent<HTMLElement>): void;
9
10
  limitedScrollHeight?: boolean;
10
11
  searchItem?: import("../../Items").FlattenBaseItem;
11
- pinTop?: import("../../Items").ItemId[];
12
- items: import("../../Items").ItemId[];
13
- pinBottom?: import("../../Items").ItemId[];
12
+ pinTop?: ItemId[];
13
+ items: ItemId[];
14
+ pinBottom?: ItemId[];
14
15
  } & import("react").RefAttributes<HTMLElement>>;
@@ -84,7 +84,6 @@ exports.ListPrivate = (0, react_1.forwardRef)((_a, ref) => {
84
84
  browser: null,
85
85
  measured: false
86
86
  });
87
- console.info('[DEBUG] ScrollState', scrollState);
88
87
  const emptyStates = (0, helperComponents_1.useEmptyState)({
89
88
  noDataState,
90
89
  noResultsState,
@@ -137,35 +136,32 @@ exports.ListPrivate = (0, react_1.forwardRef)((_a, ref) => {
137
136
  (0, react_1.useEffect)(() => {
138
137
  var _a;
139
138
  if (isScrollToItemEnabled) {
140
- console.info('[DEBUG] selectedItemIndex', selectedItemIndex);
141
139
  if (!scrollState.measured) {
142
- console.info('[DEBUG] Not measured yet');
143
140
  return; // Not measured yet
144
141
  }
145
142
  if (selectedItemIndex < 0 || !selectedItem) {
146
- console.info('[DEBUG] Cannot scroll to non-existing item');
147
143
  return; // Cannot scroll to non-existing item
148
144
  }
149
- if (scrollState.virtualizer === selectedItemIndex) {
150
- console.info('[DEBUG] No need to re-scroll to the same item during re-renders');
145
+ if (scrollState.virtualizer === selectedItem.originalId) {
151
146
  return; // No need to re-scroll to the same item during re-renders
152
147
  }
153
148
  if ((selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.itemRef) && ((_a = innerScrollRef.current) === null || _a === void 0 ? void 0 : _a.contains(selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.itemRef.current))) {
154
- console.info('[DEBUG] No need to scroll to manually clicked item currently present in DOM');
155
149
  return; // No need to scroll to manually clicked item currently present in DOM
156
150
  }
157
- console.info('[DEBUG] SCROLLING BY VIRTUALIZER');
158
151
  virtualizer.scrollToIndex(selectedItemIndex, {
159
152
  align: 'center'
160
153
  });
161
154
  setScrollState(prevState => Object.assign(Object.assign({}, prevState), {
162
- virtualizer: selectedItemIndex
155
+ virtualizer: selectedItem.originalId
163
156
  }));
164
157
  }
165
158
  }, [isScrollToItemEnabled, scrollState, selectedItem, selectedItemIndex, virtualizer]);
166
159
  const isTargetPresentInDom = Boolean((_b = selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.itemRef) === null || _b === void 0 ? void 0 : _b.current);
167
160
  (0, react_1.useEffect)(() => {
168
- var _a, _b, _c;
161
+ var _a, _b;
162
+ if (!selectedItem) {
163
+ return;
164
+ }
169
165
  if (scrollState.virtualizer === null) {
170
166
  return; // Not scrolled by virtualizer yet, no need for additional scroll
171
167
  }
@@ -175,14 +171,13 @@ exports.ListPrivate = (0, react_1.forwardRef)((_a, ref) => {
175
171
  if (scrollState.virtualizer === scrollState.browser) {
176
172
  return; // Virtualizer scroll has not been executed => no need for additional scroll
177
173
  }
178
- console.info('[DEBUG] SCROLLING BY BROWSER', (_a = selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.itemRef) === null || _a === void 0 ? void 0 : _a.current);
179
- (_c = (_b = selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.itemRef) === null || _b === void 0 ? void 0 : _b.current) === null || _c === void 0 ? void 0 : _c.scrollIntoView({
174
+ (_b = (_a = selectedItem.itemRef) === null || _a === void 0 ? void 0 : _a.current) === null || _b === void 0 ? void 0 : _b.scrollIntoView({
180
175
  block: 'center'
181
176
  });
182
177
  setScrollState(prevState => Object.assign(Object.assign({}, prevState), {
183
- browser: selectedItemIndex
178
+ browser: selectedItem.originalId
184
179
  }));
185
- }, [scrollState, selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.itemRef, isTargetPresentInDom, selectedItemIndex]);
180
+ }, [scrollState, selectedItem, isTargetPresentInDom, selectedItemIndex]);
186
181
  const loadingJSX = (0, react_1.useMemo)(() => loading && (0, jsx_runtime_1.jsx)("div", {
187
182
  role: 'spinbutton',
188
183
  tabIndex: -1,
@@ -1,3 +1,4 @@
1
+ import { ItemId } from '../../Items';
1
2
  export declare const ListPrivate: import("react").ForwardRefExoticComponent<Omit<import("../types").ListProps, "items" | "hasListInFocusChain" | "pinTop" | "pinBottom"> & {
2
3
  nested?: boolean;
3
4
  active?: boolean;
@@ -8,7 +9,7 @@ export declare const ListPrivate: import("react").ForwardRefExoticComponent<Omit
8
9
  onKeyDown?(e: import("react").KeyboardEvent<HTMLElement>): void;
9
10
  limitedScrollHeight?: boolean;
10
11
  searchItem?: import("../../Items").FlattenBaseItem;
11
- pinTop?: import("../../Items").ItemId[];
12
- items: import("../../Items").ItemId[];
13
- pinBottom?: import("../../Items").ItemId[];
12
+ pinTop?: ItemId[];
13
+ items: ItemId[];
14
+ pinBottom?: ItemId[];
14
15
  } & import("react").RefAttributes<HTMLElement>>;
@@ -34,7 +34,6 @@ export const ListPrivate = forwardRef((_a, ref) => {
34
34
  const itemsPinTopJSX = useRenderItems(pinTop);
35
35
  const itemsPinBottomJSX = useRenderItems(pinBottom);
36
36
  const [scrollState, setScrollState] = useState({ virtualizer: null, browser: null, measured: false });
37
- console.info('[DEBUG] ScrollState', scrollState);
38
37
  const emptyStates = useEmptyState({ noDataState, noResultsState, errorDataState });
39
38
  const hasNoItems = items.length === 0;
40
39
  const { selectedItemIndex, selectedItem } = useMemo(() => {
@@ -78,31 +77,28 @@ export const ListPrivate = forwardRef((_a, ref) => {
78
77
  useEffect(() => {
79
78
  var _a;
80
79
  if (isScrollToItemEnabled) {
81
- console.info('[DEBUG] selectedItemIndex', selectedItemIndex);
82
80
  if (!scrollState.measured) {
83
- console.info('[DEBUG] Not measured yet');
84
81
  return; // Not measured yet
85
82
  }
86
83
  if (selectedItemIndex < 0 || !selectedItem) {
87
- console.info('[DEBUG] Cannot scroll to non-existing item');
88
84
  return; // Cannot scroll to non-existing item
89
85
  }
90
- if (scrollState.virtualizer === selectedItemIndex) {
91
- console.info('[DEBUG] No need to re-scroll to the same item during re-renders');
86
+ if (scrollState.virtualizer === selectedItem.originalId) {
92
87
  return; // No need to re-scroll to the same item during re-renders
93
88
  }
94
89
  if ((selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.itemRef) && ((_a = innerScrollRef.current) === null || _a === void 0 ? void 0 : _a.contains(selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.itemRef.current))) {
95
- console.info('[DEBUG] No need to scroll to manually clicked item currently present in DOM');
96
90
  return; // No need to scroll to manually clicked item currently present in DOM
97
91
  }
98
- console.info('[DEBUG] SCROLLING BY VIRTUALIZER');
99
92
  virtualizer.scrollToIndex(selectedItemIndex, { align: 'center' });
100
- setScrollState(prevState => (Object.assign(Object.assign({}, prevState), { virtualizer: selectedItemIndex })));
93
+ setScrollState(prevState => (Object.assign(Object.assign({}, prevState), { virtualizer: selectedItem.originalId })));
101
94
  }
102
95
  }, [isScrollToItemEnabled, scrollState, selectedItem, selectedItemIndex, virtualizer]);
103
96
  const isTargetPresentInDom = Boolean((_b = selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.itemRef) === null || _b === void 0 ? void 0 : _b.current);
104
97
  useEffect(() => {
105
- var _a, _b, _c;
98
+ var _a, _b;
99
+ if (!selectedItem) {
100
+ return;
101
+ }
106
102
  if (scrollState.virtualizer === null) {
107
103
  return; // Not scrolled by virtualizer yet, no need for additional scroll
108
104
  }
@@ -112,10 +108,9 @@ export const ListPrivate = forwardRef((_a, ref) => {
112
108
  if (scrollState.virtualizer === scrollState.browser) {
113
109
  return; // Virtualizer scroll has not been executed => no need for additional scroll
114
110
  }
115
- console.info('[DEBUG] SCROLLING BY BROWSER', (_a = selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.itemRef) === null || _a === void 0 ? void 0 : _a.current);
116
- (_c = (_b = selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.itemRef) === null || _b === void 0 ? void 0 : _b.current) === null || _c === void 0 ? void 0 : _c.scrollIntoView({ block: 'center' });
117
- setScrollState(prevState => (Object.assign(Object.assign({}, prevState), { browser: selectedItemIndex })));
118
- }, [scrollState, selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.itemRef, isTargetPresentInDom, selectedItemIndex]);
111
+ (_b = (_a = selectedItem.itemRef) === null || _a === void 0 ? void 0 : _a.current) === null || _b === void 0 ? void 0 : _b.scrollIntoView({ block: 'center' });
112
+ setScrollState(prevState => (Object.assign(Object.assign({}, prevState), { browser: selectedItem.originalId })));
113
+ }, [scrollState, selectedItem, isTargetPresentInDom, selectedItemIndex]);
119
114
  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]);
120
115
  const content = useMemo(() => (_jsxs(_Fragment, { children: [virtualized ? (_jsx("div", { className: styles.virtualizedContainer, style: { height: virtualizer.getTotalSize() }, tabIndex: -1, children: virtualItems.map(virtualRow => (_jsx("div", { "data-index": virtualRow.index, ref: virtualizer.measureElement, tabIndex: -1, className: styles.virtualizedPositionBox, style: {
121
116
  transform: `translateY(${virtualRow.start}px)`,
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "access": "public"
5
5
  },
6
6
  "title": "List",
7
- "version": "0.29.7-preview-c824da44.0",
7
+ "version": "0.30.0",
8
8
  "sideEffects": [
9
9
  "*.css",
10
10
  "*.woff",
@@ -54,5 +54,5 @@
54
54
  "peerDependencies": {
55
55
  "@snack-uikit/locale": "*"
56
56
  },
57
- "gitHead": "edea7e461de9f4f112e73ed1de8c1063240d3164"
57
+ "gitHead": "cab80a84247484f1f7a06635d6d3d15ae7628aac"
58
58
  }
@@ -9,7 +9,7 @@ import { extractSupportProps } from '@snack-uikit/utils';
9
9
 
10
10
  import { ListEmptyState, useEmptyState } from '../../../helperComponents';
11
11
  import { stopPropagation } from '../../../utils';
12
- import { PinBottomGroupItem, PinTopGroupItem, SearchItem, useRenderItems } from '../../Items';
12
+ import { ItemId, PinBottomGroupItem, PinTopGroupItem, SearchItem, useRenderItems } from '../../Items';
13
13
  import { useNewListContext, useSelectionContext } from '../contexts';
14
14
  import commonStyles from '../styles.module.scss';
15
15
  import { ListPrivateProps } from '../types';
@@ -17,8 +17,8 @@ import { ALL_SIZES } from './constants';
17
17
  import styles from './styles.module.scss';
18
18
 
19
19
  type ScrollState = {
20
- virtualizer: number | null;
21
- browser: number | null;
20
+ virtualizer: ItemId | null;
21
+ browser: ItemId | null;
22
22
  measured: boolean;
23
23
  };
24
24
 
@@ -67,7 +67,6 @@ export const ListPrivate = forwardRef(
67
67
  const itemsPinBottomJSX = useRenderItems(pinBottom);
68
68
 
69
69
  const [scrollState, setScrollState] = useState<ScrollState>({ virtualizer: null, browser: null, measured: false });
70
- console.info('[DEBUG] ScrollState', scrollState);
71
70
 
72
71
  const emptyStates = useEmptyState({ noDataState, noResultsState, errorDataState });
73
72
  const hasNoItems = items.length === 0;
@@ -125,30 +124,24 @@ export const ListPrivate = forwardRef(
125
124
 
126
125
  useEffect(() => {
127
126
  if (isScrollToItemEnabled) {
128
- console.info('[DEBUG] selectedItemIndex', selectedItemIndex);
129
127
  if (!scrollState.measured) {
130
- console.info('[DEBUG] Not measured yet');
131
128
  return; // Not measured yet
132
129
  }
133
130
  if (selectedItemIndex < 0 || !selectedItem) {
134
- console.info('[DEBUG] Cannot scroll to non-existing item');
135
131
  return; // Cannot scroll to non-existing item
136
132
  }
137
- if (scrollState.virtualizer === selectedItemIndex) {
138
- console.info('[DEBUG] No need to re-scroll to the same item during re-renders');
133
+ if (scrollState.virtualizer === selectedItem.originalId) {
139
134
  return; // No need to re-scroll to the same item during re-renders
140
135
  }
141
136
  if (selectedItem?.itemRef && innerScrollRef.current?.contains(selectedItem?.itemRef.current)) {
142
- console.info('[DEBUG] No need to scroll to manually clicked item currently present in DOM');
143
137
  return; // No need to scroll to manually clicked item currently present in DOM
144
138
  }
145
139
 
146
- console.info('[DEBUG] SCROLLING BY VIRTUALIZER');
147
140
  virtualizer.scrollToIndex(selectedItemIndex, { align: 'center' });
148
141
 
149
142
  setScrollState(prevState => ({
150
143
  ...prevState,
151
- virtualizer: selectedItemIndex,
144
+ virtualizer: selectedItem.originalId,
152
145
  }));
153
146
  }
154
147
  }, [isScrollToItemEnabled, scrollState, selectedItem, selectedItemIndex, virtualizer]);
@@ -156,27 +149,26 @@ export const ListPrivate = forwardRef(
156
149
  const isTargetPresentInDom = Boolean(selectedItem?.itemRef?.current);
157
150
 
158
151
  useEffect(() => {
152
+ if (!selectedItem) {
153
+ return;
154
+ }
159
155
  if (scrollState.virtualizer === null) {
160
156
  return; // Not scrolled by virtualizer yet, no need for additional scroll
161
157
  }
162
-
163
158
  if (!isTargetPresentInDom) {
164
159
  return; // Target element is not present in DOM yet, additional scroll does not work without it
165
160
  }
166
-
167
161
  if (scrollState.virtualizer === scrollState.browser) {
168
162
  return; // Virtualizer scroll has not been executed => no need for additional scroll
169
163
  }
170
164
 
171
- console.info('[DEBUG] SCROLLING BY BROWSER', selectedItem?.itemRef?.current);
172
-
173
- selectedItem?.itemRef?.current?.scrollIntoView({ block: 'center' });
165
+ selectedItem.itemRef?.current?.scrollIntoView({ block: 'center' });
174
166
 
175
167
  setScrollState(prevState => ({
176
168
  ...prevState,
177
- browser: selectedItemIndex,
169
+ browser: selectedItem.originalId,
178
170
  }));
179
- }, [scrollState, selectedItem?.itemRef, isTargetPresentInDom, selectedItemIndex]);
171
+ }, [scrollState, selectedItem, isTargetPresentInDom, selectedItemIndex]);
180
172
 
181
173
  const loadingJSX = useMemo(
182
174
  () =>