Skip to content

Commit 9a01d45

Browse files
Virtualizer: Add gap property for calculation (#33275)
1 parent df2eb61 commit 9a01d45

File tree

5 files changed

+31
-13
lines changed

5 files changed

+31
-13
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "prerelease",
3+
"comment": "feat: Add gap property to simplify gap css virtualization",
4+
"packageName": "@fluentui/react-virtualizer",
5+
"email": "[email protected]",
6+
"dependentChangeType": "patch"
7+
}

packages/react-components/react-virtualizer/library/src/components/Virtualizer/Virtualizer.types.ts

+5
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,11 @@ export type VirtualizerConfigProps = {
199199
* this should be passed in from useDynamicVirtualizerMeasure
200200
*/
201201
updateScrollPosition?: (position: number) => void;
202+
203+
/**
204+
* Spacing between rendered children for calculation, should match the container's gap CSS value.
205+
*/
206+
gap?: number;
202207
};
203208

204209
export type VirtualizerProps = ComponentProps<Partial<VirtualizerSlots>> & VirtualizerConfigProps;

packages/react-components/react-virtualizer/library/src/components/Virtualizer/useVirtualizer.ts

+15-12
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export function useVirtualizer_unstable(props: VirtualizerProps): VirtualizerSta
2626
scrollViewRef,
2727
enableScrollLoad,
2828
updateScrollPosition,
29+
gap = 0,
2930
} = props;
3031

3132
/* The context is optional, it's useful for injecting additional index logic, or performing uniform state updates*/
@@ -86,7 +87,8 @@ export function useVirtualizer_unstable(props: VirtualizerProps): VirtualizerSta
8687
}
8788

8889
for (let index = 0; index < numItems; index++) {
89-
childSizes.current[index] = getItemSize(index);
90+
const _gap = index < numItems - 1 ? gap : 0;
91+
childSizes.current[index] = getItemSize(index) + _gap;
9092
if (index === 0) {
9193
childProgressiveSizes.current[index] = childSizes.current[index];
9294
} else {
@@ -162,7 +164,8 @@ export function useVirtualizer_unstable(props: VirtualizerProps): VirtualizerSta
162164

163165
let didUpdate = false;
164166
for (let i = startIndex; i < endIndex; i++) {
165-
const newSize = getItemSize(i);
167+
const _gap = i < numItems - 1 ? gap : 0;
168+
const newSize = getItemSize(i) + _gap;
166169
if (newSize !== childSizes.current[i]) {
167170
childSizes.current[i] = newSize;
168171
didUpdate = true;
@@ -177,7 +180,7 @@ export function useVirtualizer_unstable(props: VirtualizerProps): VirtualizerSta
177180
}
178181
}
179182
},
180-
[getItemSize, numItems, virtualizerLength],
183+
[getItemSize, numItems, virtualizerLength, gap],
181184
);
182185

183186
const batchUpdateNewIndex = React.useCallback(
@@ -243,29 +246,29 @@ export function useVirtualizer_unstable(props: VirtualizerProps): VirtualizerSta
243246
const getIndexFromScrollPosition = React.useCallback(
244247
(scrollPos: number) => {
245248
if (!getItemSize) {
246-
return Math.round(scrollPos / itemSize);
249+
return Math.round(scrollPos / (itemSize + gap));
247250
}
248251

249252
return getIndexFromSizeArray(scrollPos);
250253
},
251-
[getIndexFromSizeArray, getItemSize, itemSize],
254+
[getIndexFromSizeArray, getItemSize, itemSize, gap],
252255
);
253256

254257
const calculateTotalSize = React.useCallback(() => {
255258
if (!getItemSize) {
256-
return itemSize * numItems;
259+
return (itemSize + gap) * numItems;
257260
}
258261

259262
// Time for custom size calcs
260263
return childProgressiveSizes.current[numItems - 1];
261-
}, [getItemSize, itemSize, numItems]);
264+
}, [getItemSize, itemSize, numItems, gap]);
262265

263266
const calculateBefore = React.useCallback(() => {
264267
const currentIndex = Math.min(actualIndex, numItems - 1);
265268

266269
if (!getItemSize) {
267270
// The missing items from before virtualization starts height
268-
return currentIndex * itemSize;
271+
return currentIndex * (itemSize + gap);
269272
}
270273

271274
if (currentIndex <= 0) {
@@ -274,7 +277,7 @@ export function useVirtualizer_unstable(props: VirtualizerProps): VirtualizerSta
274277

275278
// Time for custom size calcs
276279
return childProgressiveSizes.current[currentIndex - 1];
277-
}, [actualIndex, getItemSize, itemSize, numItems]);
280+
}, [actualIndex, getItemSize, itemSize, numItems, gap]);
278281

279282
const calculateAfter = React.useCallback(() => {
280283
if (numItems === 0 || actualIndex + virtualizerLength >= numItems) {
@@ -285,12 +288,12 @@ export function useVirtualizer_unstable(props: VirtualizerProps): VirtualizerSta
285288
if (!getItemSize) {
286289
// The missing items from after virtualization ends height
287290
const remainingItems = numItems - lastItemIndex;
288-
return remainingItems * itemSize;
291+
return remainingItems * (itemSize + gap) - gap;
289292
}
290293

291294
// Time for custom size calcs
292295
return childProgressiveSizes.current[numItems - 1] - childProgressiveSizes.current[lastItemIndex - 1];
293-
}, [actualIndex, getItemSize, itemSize, numItems, virtualizerLength]);
296+
}, [actualIndex, getItemSize, itemSize, numItems, virtualizerLength, gap]);
294297

295298
// Observe intersections of virtualized components
296299
const { setObserverList } = useIntersectionObserver(
@@ -532,7 +535,7 @@ export function useVirtualizer_unstable(props: VirtualizerProps): VirtualizerSta
532535

533536
// We only run this effect on getItemSize change (recalc dynamic sizes)
534537
// eslint-disable-next-line react-hooks/exhaustive-deps
535-
}, [getItemSize]);
538+
}, [getItemSize, gap]);
536539

537540
// Effect to check flag index on updates
538541
React.useEffect(() => {

packages/react-components/react-virtualizer/stories/src/Virtualizer/Dynamic.stories.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const useStyles = makeStyles({
1313
width: '100%',
1414
height: '100%',
1515
maxHeight: '750px',
16+
gap: '20px',
1617
},
1718
child: {
1819
height: `${smallSize}px`,
@@ -87,6 +88,7 @@ export const Dynamic = () => {
8788
containerSizeRef={containerSizeRef}
8889
virtualizerContext={contextState}
8990
updateScrollPosition={updateScrollPosition}
91+
gap={20}
9092
>
9193
{React.useCallback(
9294
(index: number) => {

packages/react-components/react-virtualizer/stories/src/VirtualizerScrollViewDynamic/AutoMeasure.stories.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,10 @@ export const AutoMeasure = () => {
3737
numItems={childLength}
3838
// We can use itemSize to set average height and reduce unknown whitespace
3939
itemSize={minHeight + maxHeightIncrease / 2.0 + 100}
40-
container={{ role: 'list', style: { maxHeight: '80vh' } }}
40+
container={{ role: 'list', style: { maxHeight: '80vh', gap: '20px' } }}
4141
bufferItems={1}
4242
bufferSize={minHeight / 2.0}
43+
gap={20}
4344
>
4445
{(index: number) => {
4546
const backgroundColor = index % 2 ? '#CCCCCC' : '#ABABAB';

0 commit comments

Comments
 (0)