From 6a00d4ce1fc124149ecdc671cd50260c42413f58 Mon Sep 17 00:00:00 2001 From: radubrehar Date: Thu, 25 Jul 2024 12:45:37 +0300 Subject: [PATCH] add onRenderRangeChange and release version patch --- .../scrolling/onRenderRangeChange.page.tsx | 95 +++++++++++++++++++ .../scrolling/onRenderRangeChange.spec.ts | 29 ++++++ .../InfiniteTable/hooks/useCellRendering.tsx | 6 ++ .../InfiniteTable/state/getInitialState.ts | 2 + .../InfiniteTable/types/InfiniteTableProps.ts | 2 + .../InfiniteTable/types/InfiniteTableState.ts | 3 + .../reference/infinite-table-props.page.md | 50 +++++++++- 7 files changed, 186 insertions(+), 1 deletion(-) create mode 100644 examples/src/pages/tests/table/props/scrolling/onRenderRangeChange.page.tsx create mode 100644 examples/src/pages/tests/table/props/scrolling/onRenderRangeChange.spec.ts diff --git a/examples/src/pages/tests/table/props/scrolling/onRenderRangeChange.page.tsx b/examples/src/pages/tests/table/props/scrolling/onRenderRangeChange.page.tsx new file mode 100644 index 00000000..d40116ce --- /dev/null +++ b/examples/src/pages/tests/table/props/scrolling/onRenderRangeChange.page.tsx @@ -0,0 +1,95 @@ +import * as React from 'react'; + +import { + InfiniteTable, + InfiniteTablePropColumns, + DataSourceData, + ScrollStopInfo, + debounce, +} from '@infinite-table/infinite-react'; +import { DataSource } from '@infinite-table/infinite-react'; +import { TableRenderRange } from '@src/components/VirtualBrain/MatrixBrain'; + +type Developer = { + id: number; + firstName: string; + lastName: string; + country: string; + city: string; + currency: string; + + email: string; + preferredLanguage: string; + stack: string; + canDesign: 'yes' | 'no'; + hobby: string; + salary: number; + age: number; +}; + +const columns: InfiniteTablePropColumns = { + index: { + renderValue: ({ rowInfo }) => { + return `${rowInfo.indexInAll}`; + }, + }, + preferredLanguage: { + field: 'preferredLanguage', + }, + salary: { + field: 'salary', + type: 'number', + }, + age: { field: 'age' }, + city: { field: 'city' }, + email: { field: 'email' }, + canDesign: { field: 'canDesign' }, + stack: { field: 'stack' }, +}; + +const dataSource: DataSourceData = ({}) => { + return fetch(process.env.NEXT_PUBLIC_BASE_URL + `/developers10k-sql`) + .then((r) => r.json()) + .then((data: Developer[]) => data); +}; + +const sinon = require('sinon'); + +const onRenderRangeChange = sinon.spy((_scrollInfo: ScrollStopInfo) => {}); + +(globalThis as any).onRenderRangeChange = onRenderRangeChange; + +export default () => { + const fn = React.useMemo(() => { + return debounce( + (range: TableRenderRange) => { + console.log(range); + onRenderRangeChange(range); + }, + { wait: 10 }, + ); + }, []); + return ( + + <> + data={dataSource} primaryKey="id"> + + domProps={{ + style: { + margin: '5px', + height: '60vh', + width: '80vw', + border: '1px solid gray', + position: 'relative', + }, + }} + columnMinWidth={50} + columnDefaultWidth={350} + onRenderRangeChange={fn} + columns={columns} + /> + + + + ); +}; diff --git a/examples/src/pages/tests/table/props/scrolling/onRenderRangeChange.spec.ts b/examples/src/pages/tests/table/props/scrolling/onRenderRangeChange.spec.ts new file mode 100644 index 00000000..8ccbd4ad --- /dev/null +++ b/examples/src/pages/tests/table/props/scrolling/onRenderRangeChange.spec.ts @@ -0,0 +1,29 @@ +import { getFnCalls } from '../../../testUtils/getFnCalls'; +import { test, expect } from '@testing'; + +export default test.describe.parallel('onRenderRangeChange', () => { + test('should correctly be called', async ({ page, apiModel }) => { + await page.waitForInfinite(); + + let calls = await getFnCalls('onRenderRangeChange', { page }); + expect(calls.length).toEqual(1); + + await apiModel.evaluate((api) => { + return api.scrollRowIntoView(100); + }); + await page.waitForTimeout(50); + + calls = await getFnCalls('onRenderRangeChange', { page }); + expect(calls.length).toEqual(2); + + await page.setViewportSize({ + width: 500, + height: 500, + }); + + await page.waitForTimeout(100); + + calls = await getFnCalls('onRenderRangeChange', { page }); + expect(calls.length).toEqual(3); + }); +}); diff --git a/source/src/components/InfiniteTable/hooks/useCellRendering.tsx b/source/src/components/InfiniteTable/hooks/useCellRendering.tsx index 5a502a14..662e9572 100644 --- a/source/src/components/InfiniteTable/hooks/useCellRendering.tsx +++ b/source/src/components/InfiniteTable/hooks/useCellRendering.tsx @@ -113,6 +113,12 @@ export function useCellRendering( }); }, [brain]); + useEffect(() => { + return brain.onRenderRangeChange((range) => { + getState().onRenderRangeChange?.(range); + }); + }, [brain]); + useEffect(() => { return brain.onScrollStop((scrollPosition) => { const { scrollTop, scrollLeft } = scrollPosition; diff --git a/source/src/components/InfiniteTable/state/getInitialState.ts b/source/src/components/InfiniteTable/state/getInitialState.ts index 7cb0e72e..dad6d9de 100644 --- a/source/src/components/InfiniteTable/state/getInitialState.ts +++ b/source/src/components/InfiniteTable/state/getInitialState.ts @@ -226,6 +226,8 @@ export const forwardProps = ( onContextMenu: 1, onCellContextMenu: 1, + onRenderRangeChange: 1, + onScrollToTop: 1, onScrollToBottom: 1, onScrollStop: 1, diff --git a/source/src/components/InfiniteTable/types/InfiniteTableProps.ts b/source/src/components/InfiniteTable/types/InfiniteTableProps.ts index 81fce81b..5a71c6ff 100644 --- a/source/src/components/InfiniteTable/types/InfiniteTableProps.ts +++ b/source/src/components/InfiniteTable/types/InfiniteTableProps.ts @@ -813,6 +813,8 @@ export interface InfiniteTableProps { onScrollStop?: (param: ScrollStopInfo) => void; scrollToBottomOffset?: number; + onRenderRangeChange?: (range: TableRenderRange) => void; + defaultColumnOrder?: InfiniteTablePropColumnOrder; columnOrder?: InfiniteTablePropColumnOrder; onColumnOrderChange?: ( diff --git a/source/src/components/InfiniteTable/types/InfiniteTableState.ts b/source/src/components/InfiniteTable/types/InfiniteTableState.ts index bfa9ad5a..3e22d9ba 100644 --- a/source/src/components/InfiniteTable/types/InfiniteTableState.ts +++ b/source/src/components/InfiniteTable/types/InfiniteTableState.ts @@ -200,6 +200,9 @@ export interface InfiniteTableMappedState { activeRowIndex: InfiniteTableProps['activeRowIndex']; activeCellIndex: InfiniteTableProps['activeCellIndex']; + + onRenderRangeChange: InfiniteTableProps['onRenderRangeChange']; + scrollStopDelay: NonUndefined['scrollStopDelay']>; onScrollToTop: InfiniteTableProps['onScrollToTop']; onScrollToBottom: InfiniteTableProps['onScrollToBottom']; diff --git a/www/content/docs/reference/infinite-table-props.page.md b/www/content/docs/reference/infinite-table-props.page.md index 4e582237..29aaf9e2 100644 --- a/www/content/docs/reference/infinite-table-props.page.md +++ b/www/content/docs/reference/infinite-table-props.page.md @@ -2903,6 +2903,54 @@ It will never be called again after the component is ready. + +> Called whenever the render range changes, that is, additional rows or columns come into view. + +The first (and only) argument is an object with `{start, end}` where both `start` and `end` are arrays of `[rowIndex, colIndex]` pairs. + + So if you want to get the start and end indexes, you can do + + ```ts + const [startRow, startCol] = renderRange.start; + const [endRow, endCol] = renderRange.end; + ``` + + + +This callback is not debounced or throttled, so it can be called multiple times in a short period of time, especially while scrolling. Make sure your function is fast, or attach a debounced function, in order to avoid performance issues. + +```tsx +import { + debounce, + InfiniteTable, + DataSource +} from '@infinite-table/infinite-react'; + +function App() { + const onRenderRangeChange = useMemo(() => { + return debounce((range) => { + console.log(range.start, range.end); + }, {wait: 100}); + }, []); + + return + primaryKey="id" + data={/*data*/} + > + + onRenderRangeChange={onRenderRangeChange} + columns={/*columns*/} + /> + +} +``` + + +Unlike , this function is also called when the DataGrid is resized and also when initially rendered. + + + + > Triggered when the user has stopped scrolling (after milliseconds). @@ -2922,7 +2970,7 @@ The function is called with an object that has the following properties: - `scrollLeft` - the scrollLeft position of the viewport - `number` -Also see and . +Also see , and .