Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
dbba330
Initial static size measurement
Mitch-At-Work Feb 16, 2023
bd18c96
Fix up algo to ensure algo is unidirectional when scroll is
Mitch-At-Work Feb 16, 2023
687f7be
Add scroll container and implement built in hooks
Mitch-At-Work Feb 22, 2023
cdbbc7d
Fix up container and scroll refs
Mitch-At-Work Feb 22, 2023
db2d20a
Fix up virtualizer length updates to ensure they always propogate
Mitch-At-Work Feb 22, 2023
74f6f25
Add new functionality and hooks
Mitch-At-Work Feb 22, 2023
c0cb6ad
Tidy up types and add change files
Mitch-At-Work Feb 27, 2023
5b3ea6b
Add fluent/react package as dev dep for demos
Mitch-At-Work Feb 27, 2023
67dbfe6
Fix up key indexing on multi-unbounded demo
Mitch-At-Work Feb 27, 2023
a019ecc
Add fluentui/react as dep
Mitch-At-Work Feb 27, 2023
9ed155d
Ensure resize observer is only enabled once mounted (SSR tests)
Mitch-At-Work Feb 28, 2023
a8ba178
Touch up PR comments
Mitch-At-Work Mar 14, 2023
9b0d725
Add ref callback and touch up PR comments
Mitch-At-Work Mar 14, 2023
8c0b331
Remove block
Mitch-At-Work Mar 14, 2023
3c0641e
Test render cycles
Mitch-At-Work Mar 14, 2023
74635fa
Add PR comments
Mitch-At-Work Mar 15, 2023
e0326cc
Rebase and touch up
Mitch-At-Work Mar 15, 2023
72adb3a
Update package dep
Mitch-At-Work Mar 15, 2023
02a142d
Fix up undefined ref
Mitch-At-Work Mar 15, 2023
b0e53cf
Fix up API
Mitch-At-Work Mar 15, 2023
7e6bd3c
Fix up example type
Mitch-At-Work Mar 15, 2023
e38bb49
Update api
Mitch-At-Work Mar 15, 2023
c4f92fe
Add document.body call
Mitch-At-Work Mar 16, 2023
6d9059d
Fix up ref callback to be an actual callback func
Mitch-At-Work Mar 20, 2023
1c27efd
Update api
Mitch-At-Work Mar 20, 2023
fd24239
PR Comments and update version
Mitch-At-Work Mar 20, 2023
214ca90
Remove I notation from types
Mitch-At-Work Mar 20, 2023
318abe1
Fix up virtualizer length
Mitch-At-Work Mar 20, 2023
99dac13
Remove dynamic type file
Mitch-At-Work Mar 20, 2023
2bca704
Ensure resize observer disconnects on unload
Mitch-At-Work Mar 20, 2023
1e8178e
Remove unused ref in horizontal example
Mitch-At-Work Mar 20, 2023
65ec330
Add PR comments
Mitch-At-Work Mar 22, 2023
3a81dc1
Update package deps
Mitch-At-Work Mar 22, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "feat: Add static measurement hooks and embedded scroll option to react-virtualizer",
"packageName": "@fluentui/react-components",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "[feat] Add static measurement hooks and embedded scroll option",
"packageName": "@fluentui/react-virtualizer",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import { renderTreeItem_unstable } from '@fluentui/react-tree';
import { renderTreeItemLayout_unstable } from '@fluentui/react-tree';
import { renderTreeItemPersonaLayout_unstable } from '@fluentui/react-tree';
import { renderVirtualizer_unstable } from '@fluentui/react-virtualizer';
import { renderVirtualizerScrollView_unstable } from '@fluentui/react-virtualizer';
import { SelectField_unstable as SelectField } from '@fluentui/react-select';
import { selectFieldClassNames } from '@fluentui/react-select';
import { SelectFieldProps_unstable as SelectFieldProps } from '@fluentui/react-select';
Expand Down Expand Up @@ -127,6 +128,7 @@ import { useSkeletonContext } from '@fluentui/react-skeleton';
import { useSkeletonItem_unstable } from '@fluentui/react-skeleton';
import { useSkeletonItemStyles_unstable } from '@fluentui/react-skeleton';
import { useSkeletonStyles_unstable } from '@fluentui/react-skeleton';
import { useStaticVirtualizerMeasure } from '@fluentui/react-virtualizer';
import { useTree_unstable } from '@fluentui/react-tree';
import { useTreeContext_unstable } from '@fluentui/react-tree';
import { useTreeItem_unstable } from '@fluentui/react-tree';
Expand All @@ -138,11 +140,18 @@ import { useTreeItemPersonaLayoutStyles_unstable } from '@fluentui/react-tree';
import { useTreeItemStyles_unstable } from '@fluentui/react-tree';
import { useTreeStyles_unstable } from '@fluentui/react-tree';
import { useVirtualizer_unstable } from '@fluentui/react-virtualizer';
import { useVirtualizerScrollView_unstable } from '@fluentui/react-virtualizer';
import { useVirtualizerScrollViewStyles_unstable } from '@fluentui/react-virtualizer';
import { useVirtualizerStyles_unstable } from '@fluentui/react-virtualizer';
import { Virtualizer } from '@fluentui/react-virtualizer';
import { VirtualizerChildRenderFunction } from '@fluentui/react-virtualizer';
import { virtualizerClassNames } from '@fluentui/react-virtualizer';
import { VirtualizerProps } from '@fluentui/react-virtualizer';
import { VirtualizerScrollView } from '@fluentui/react-virtualizer';
import { virtualizerScrollViewClassNames } from '@fluentui/react-virtualizer';
import { VirtualizerScrollViewProps } from '@fluentui/react-virtualizer';
import { VirtualizerScrollViewSlots } from '@fluentui/react-virtualizer';
import { VirtualizerScrollViewState } from '@fluentui/react-virtualizer';
import { VirtualizerSlots } from '@fluentui/react-virtualizer';
import { VirtualizerState } from '@fluentui/react-virtualizer';

Expand Down Expand Up @@ -250,6 +259,8 @@ export { renderTreeItemPersonaLayout_unstable }

export { renderVirtualizer_unstable }

export { renderVirtualizerScrollView_unstable }

export { SelectField }

export { selectFieldClassNames }
Expand Down Expand Up @@ -392,6 +403,8 @@ export { useSkeletonItemStyles_unstable }

export { useSkeletonStyles_unstable }

export { useStaticVirtualizerMeasure }

export { useTree_unstable }

export { useTreeContext_unstable }
Expand All @@ -414,6 +427,10 @@ export { useTreeStyles_unstable }

export { useVirtualizer_unstable }

export { useVirtualizerScrollView_unstable }

export { useVirtualizerScrollViewStyles_unstable }

export { useVirtualizerStyles_unstable }

export { Virtualizer }
Expand All @@ -424,6 +441,16 @@ export { virtualizerClassNames }

export { VirtualizerProps }

export { VirtualizerScrollView }

export { virtualizerScrollViewClassNames }

export { VirtualizerScrollViewProps }

export { VirtualizerScrollViewSlots }

export { VirtualizerScrollViewState }

export { VirtualizerSlots }

export { VirtualizerState }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,21 @@ export {
renderVirtualizer_unstable,
useVirtualizerStyles_unstable,
useIntersectionObserver,
useStaticVirtualizerMeasure,
VirtualizerScrollView,
virtualizerScrollViewClassNames,
useVirtualizerScrollView_unstable,
renderVirtualizerScrollView_unstable,
useVirtualizerScrollViewStyles_unstable,
} from '@fluentui/react-virtualizer';
export type {
VirtualizerProps,
VirtualizerState,
VirtualizerSlots,
VirtualizerChildRenderFunction,
VirtualizerScrollViewProps,
VirtualizerScrollViewState,
VirtualizerScrollViewSlots,
} from '@fluentui/react-virtualizer';

export {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,46 @@

```ts

import type { ComponentProps } from '@fluentui/react-utilities';
import type { ComponentState } from '@fluentui/react-utilities';
import { ComponentProps } from '@fluentui/react-utilities';
import { ComponentState } from '@fluentui/react-utilities';
import type { Dispatch } from 'react';
import type { FC } from 'react';
import type { MutableRefObject } from 'react';
import * as React_2 from 'react';
import type { SetStateAction } from 'react';
import type { Slot } from '@fluentui/react-utilities';
import { Slot } from '@fluentui/react-utilities';
import type { SlotClassNames } from '@fluentui/react-utilities';

// @public (undocumented)
export const renderVirtualizer_unstable: (state: VirtualizerState) => JSX.Element;

// @public (undocumented)
export const renderVirtualizerScrollView_unstable: (state: VirtualizerScrollViewState) => JSX.Element;

// @public
export const useIntersectionObserver: (callback: IntersectionObserverCallback, options?: IntersectionObserverInit | undefined) => {
setObserverList: Dispatch<SetStateAction<Element[] | undefined>>;
setObserverInit: Dispatch<SetStateAction<IntersectionObserverInit | undefined>>;
observer: MutableRefObject<IntersectionObserver | undefined>;
};

// @public
export const useStaticVirtualizerMeasure: (virtualizerProps: VirtualizerMeasureProps) => {
virtualizerLength: number;
bufferItems: number;
bufferSize: number;
scrollRef: (instance: HTMLElement | HTMLDivElement | null) => void;
};

// @public (undocumented)
export function useVirtualizer_unstable(props: VirtualizerProps): VirtualizerState;

// @public (undocumented)
export function useVirtualizerScrollView_unstable(props: VirtualizerScrollViewProps): VirtualizerScrollViewState;

// @public
export const useVirtualizerScrollViewStyles_unstable: (state: VirtualizerScrollViewState) => VirtualizerScrollViewState;

// @public
export const useVirtualizerStyles_unstable: (state: VirtualizerState) => VirtualizerState;

Expand All @@ -40,21 +57,29 @@ export type VirtualizerChildRenderFunction = (index: number) => React_2.ReactNod
export const virtualizerClassNames: SlotClassNames<VirtualizerSlots>;

// @public (undocumented)
export type VirtualizerProps = ComponentProps<Partial<VirtualizerSlots>> & {
children: VirtualizerChildRenderFunction;
export type VirtualizerProps = ComponentProps<Partial<VirtualizerSlots>> & VirtualizerConfigProps;

// @public
export const VirtualizerScrollView: React_2.FC<VirtualizerScrollViewProps>;

// @public (undocumented)
export const virtualizerScrollViewClassNames: SlotClassNames<VirtualizerScrollViewSlots>;

// @public (undocumented)
export type VirtualizerScrollViewProps = ComponentProps<Partial<VirtualizerScrollViewSlots>> & Partial<Omit<VirtualizerConfigProps, 'itemSize' | 'numItems' | 'getItemSize' | 'children'>> & {
itemSize: number;
numItems: number;
virtualizerLength: number;
bufferItems?: number;
bufferSize?: number;
intersectionObserverRoot?: React_2.MutableRefObject<HTMLElement | null>;
axis?: 'vertical' | 'horizontal';
reversed?: boolean;
getItemSize?: (index: number) => number;
onUpdateIndex?: (index: number, prevIndex: number) => void;
onCalculateIndex?: (newIndex: number) => number;
children: VirtualizerChildRenderFunction;
};

// @public (undocumented)
export type VirtualizerScrollViewSlots = VirtualizerSlots & {
container: NonNullable<Slot<'div'>>;
};

// @public (undocumented)
export type VirtualizerScrollViewState = ComponentState<VirtualizerScrollViewSlots> & VirtualizerConfigState;

// @public (undocumented)
export type VirtualizerSlots = {
before: NonNullable<Slot<'div', 'td'>>;
Expand All @@ -64,16 +89,7 @@ export type VirtualizerSlots = {
};

// @public (undocumented)
export type VirtualizerState = ComponentState<VirtualizerSlots> & {
virtualizedChildren: React_2.ReactNode[];
virtualizerStartIndex: number;
afterBufferHeight: number;
beforeBufferHeight: number;
totalVirtualizerHeight: number;
axis?: 'vertical' | 'horizontal';
reversed?: boolean;
bufferSize: number;
};
export type VirtualizerState = ComponentState<VirtualizerSlots> & VirtualizerConfigState;

// (No @packageDocumentation comment for this package)

Expand Down
1 change: 1 addition & 0 deletions packages/react-components/react-virtualizer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
},
"dependencies": {
"@fluentui/react-utilities": "^9.7.2",
"@fluentui/react": "^8.106.8",
"@griffel/react": "^1.5.2",
"@swc/helpers": "^0.4.14"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './components/VirtualizerScrollView/index';
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export type VirtualizerSlots = {
afterContainer: NonNullable<Slot<'div', 'tr'>>;
};

export type VirtualizerState = ComponentState<VirtualizerSlots> & {
export type VirtualizerConfigState = {
/**
* The current virtualized array of children to show in the DOM.
*/
Expand Down Expand Up @@ -51,16 +51,19 @@ export type VirtualizerState = ComponentState<VirtualizerSlots> & {
*/
reversed?: boolean;
/**
* Tells the virtualizer how much
* Pixel size of intersection observers and how much they 'cross over' into the bufferItems index.
* Minimum 1px.
*/
bufferSize: number;
};

export type VirtualizerState = ComponentState<VirtualizerSlots> & VirtualizerConfigState;

// Virtualizer render function to procedurally generate children elements as rows or columns via index.
// Q: Use generic typing and passing through object data or a simple index system?
export type VirtualizerChildRenderFunction = (index: number) => React.ReactNode;

export type VirtualizerProps = ComponentProps<Partial<VirtualizerSlots>> & {
export type VirtualizerConfigProps = {
/**
* Child render function.
* Iteratively called to return current virtualizer DOM children.
Expand Down Expand Up @@ -110,9 +113,8 @@ export type VirtualizerProps = ComponentProps<Partial<VirtualizerSlots>> & {

/**
* Enables users to override the intersectionObserverRoot.
* @default null
*/
intersectionObserverRoot?: React.MutableRefObject<HTMLElement | null>;
scrollViewRef?: React.MutableRefObject<HTMLElement | null>;

/**
* The scroll direction
Expand Down Expand Up @@ -142,3 +144,5 @@ export type VirtualizerProps = ComponentProps<Partial<VirtualizerSlots>> & {
*/
onCalculateIndex?: (newIndex: number) => number;
};

export type VirtualizerProps = ComponentProps<Partial<VirtualizerSlots>> & VirtualizerConfigProps;
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export function useVirtualizer_unstable(props: VirtualizerProps): VirtualizerSta
getItemSize,
bufferItems = Math.round(virtualizerLength / 4.0),
bufferSize = Math.floor(bufferItems / 2.0) * itemSize,
intersectionObserverRoot,
scrollViewRef,
axis = 'vertical',
reversed = false,
onUpdateIndex,
Expand Down Expand Up @@ -180,7 +180,7 @@ export function useVirtualizer_unstable(props: VirtualizerProps): VirtualizerSta
}
},
{
root: intersectionObserverRoot ? intersectionObserverRoot?.current : null,
root: scrollViewRef ? scrollViewRef?.current : null,
rootMargin: '0px',
threshold: 0,
},
Expand Down Expand Up @@ -276,23 +276,26 @@ export function useVirtualizer_unstable(props: VirtualizerProps): VirtualizerSta
return childProgressiveSizes.current[numItems - 1] - childProgressiveSizes.current[lastItemIndex];
};

const updateChildRows = (newIndex: number) => {
if (numItems === 0) {
/* Nothing to virtualize */
const updateChildRows = useCallback(
(newIndex: number) => {
if (numItems === 0) {
/* Nothing to virtualize */

return [];
}
return [];
}

if (childArray.current.length !== numItems) {
childArray.current = new Array(virtualizerLength);
}
const actualIndex = Math.max(newIndex, 0);
const end = Math.min(actualIndex + virtualizerLength, numItems);
if (childArray.current.length !== numItems) {
childArray.current = new Array(virtualizerLength);
}
const actualIndex = Math.max(newIndex, 0);
const end = Math.min(actualIndex + virtualizerLength, numItems);

for (let i = actualIndex; i < end; i++) {
childArray.current[i - actualIndex] = renderChild(i);
}
};
for (let i = actualIndex; i < end; i++) {
childArray.current[i - actualIndex] = renderChild(i);
}
},
[numItems, renderChild, virtualizerLength],
);

const setBeforeRef = useCallback(
(element: HTMLDivElement) => {
Expand Down Expand Up @@ -387,7 +390,7 @@ export function useVirtualizer_unstable(props: VirtualizerProps): VirtualizerSta
forceUpdate();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [renderChild]);
}, [renderChild, updateChildRows]);

// Ensure we have run through and updated the whole size list array at least once.
initializeSizeArray();
Expand All @@ -397,6 +400,12 @@ export function useVirtualizer_unstable(props: VirtualizerProps): VirtualizerSta
populateSizeArrays();
}

// Ensure we recalc if virtualizer length changes
const maxCompare = Math.min(virtualizerLength, numItems);
if (childArray.current.length !== maxCompare && virtualizerStartIndex + childArray.current.length < numItems) {
updateChildRows(virtualizerStartIndex);
}

const isFullyInitialized = hasInitialized.current && virtualizerStartIndex >= 0;
return {
components: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { VirtualizerScrollViewProps } from './VirtualizerScrollView.types';
import { useVirtualizerScrollView_unstable } from './useVirtualizerScrollView';
import { renderVirtualizerScrollView_unstable } from './renderVirtualizerScrollView';
import { useVirtualizerScrollViewStyles_unstable } from './useVirtualizerScrollViewStyles';
import * as React from 'react';

/**
* Virtualizer ScrollView
*/

export const VirtualizerScrollView: React.FC<VirtualizerScrollViewProps> = (props: VirtualizerScrollViewProps) => {
const state = useVirtualizerScrollView_unstable(props);

useVirtualizerScrollViewStyles_unstable(state);

return renderVirtualizerScrollView_unstable(state);
};

VirtualizerScrollView.displayName = 'VirtualizerScrollView';
Loading