Skip to content

Commit

Permalink
stabilized virtualization algo for horizontal layout
Browse files Browse the repository at this point in the history
  • Loading branch information
radubrehar committed Sep 18, 2024
1 parent 186b124 commit 54c9679
Show file tree
Hide file tree
Showing 13 changed files with 324 additions and 33 deletions.
5 changes: 5 additions & 0 deletions examples/src/pages/tests/horizontal-layout/default.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ const columns: InfiniteTablePropColumns<Developer> = {
field: 'id',
type: 'number',
/*xdefaultWidth: 80,*/ renderValue: ({ value }) => value - 1,
style: (options) => {
return {
// background : options.rowInfo.
};
},
},
preferredLanguage: { field: 'preferredLanguage' /*xdefaultWidth: 110 */ },
// age: { field: 'age' /*xdefaultWidth: 70 */ },
Expand Down
101 changes: 101 additions & 0 deletions examples/src/pages/tests/horizontal-layout/example.page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import * as React from 'react';

import {
InfiniteTable,
InfiniteTablePropColumns,
} from '@infinite-table/infinite-react';
import { DataSource } from '@infinite-table/infinite-react';

type Developer = {
id: number;

firstName: string;
lastName: string;

currency: string;
country: string;
preferredLanguage: string;
stack: string;
canDesign: 'yes' | 'no';

age: number;
salary: number;
};

const style = () => {
return {
background: `rgb(255 0 0 / 12%)`,
};
};

const columns: InfiniteTablePropColumns<Developer> = {
id: {
field: 'id',
type: 'number',
style,
},
canDesign: {
field: 'canDesign',
},
salary: {
field: 'salary',
type: 'number',
// style,
},
firstName: {
field: 'firstName',
},
age: {
field: 'age',
type: 'number',
// style,
},

stack: { field: 'stack', renderMenuIcon: false },
currency: { field: 'currency' },
country: { field: 'country' },
};

export default () => {
const dataSource = React.useCallback(() => {
return fetch(process.env.NEXT_PUBLIC_BASE_URL + '/developers100')
.then((r) => r.json())
.then((data) => {
return data;
// return new Promise((resolve) => {
// setTimeout(() => {
// resolve(data);
// }, 10);
// });
});
}, []);
return (
<>
<React.StrictMode>
<DataSource<Developer>
data={dataSource}
primaryKey="id"
defaultGroupBy={[
{
field: 'currency',
},
{
field: 'stack',
},
]}
>
<InfiniteTable<Developer>
columns={columns}
wrapRowsHorizontally={true}
columnDefaultWidth={120}
domProps={{
style: {
minHeight: '70vh',
},
}}
/>
</DataSource>
</React.StrictMode>
</>
);
};
35 changes: 30 additions & 5 deletions examples/src/pages/tests/horizontal-layout/test.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,51 @@ import { useState } from 'react';

type Developer = {
id: number;

firstName: string;
lastName: string;

currency: string;
country: string;
preferredLanguage: string;
stack: string;
canDesign: 'yes' | 'no';

age: number;
salary: number;
};

const columns: InfiniteTablePropColumns<Developer> = {
id: {
field: 'id',
type: 'number',
},
preferredLanguage: { field: 'preferredLanguage' },
// age: { field: 'age' },
// salary: { field: 'salary' },
canDesign: {
field: 'canDesign',
},
salary: {
field: 'salary',
type: 'number',
},
firstName: {
field: 'firstName',
},
age: {
field: 'age',
type: 'number',
},

stack: { field: 'stack', renderMenuIcon: false },
currency: { field: 'currency' },
country: { field: 'country' },
};

const domProps = {
// style: { height: 420 /*30px header, 420 body*/, width: 230 },
style: { height: '50vh' /*30px header, 420 body*/, width: '30vw' },
style: { height: '50vh' /*30px header, 420 body*/, width: '80vw' },
};

const data = Array.from({ length: 10 }, (_, i) => ({
const data = Array.from({ length: 1000 }, (_, i) => ({
id: i,
preferredLanguage: `Lang ${i}`,
age: i * 10,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,37 @@ export class HorizontalLayoutTableRenderer extends ReactHeadlessTableRenderer {
});
}

isCellRenderedAndMappedCorrectly(row: number, col: number) {
const rendered = this.mappedCells.isCellRendered(row, col);

if (!rendered) {
return {
rendered,
mapped: false,
};
}

const cellAdditionalInfo = this.mappedCells.getCellAdditionalInfo(row, col);

if (!cellAdditionalInfo) {
return {
rendered,
mapped: false,
};
}

const info = this.getCellRealCoordinates(row, col);

const mapped =
info.colIndex === cellAdditionalInfo!.renderColIndex &&
info.rowIndex === cellAdditionalInfo!.renderRowIndex;

return {
rendered,
mapped,
};
}

setTransform = (
element: HTMLElement,
rowIndex: number,
Expand Down
38 changes: 34 additions & 4 deletions source/src/components/HeadlessTable/MappedCells.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { TableRenderRange } from '../VirtualBrain/MatrixBrain';
* This class has tests - see tests/mapped-cells.spec.ts
*/

export class MappedCells extends Logger {
export class MappedCells<T_ADDITIONAL_CELL_INFO = any> extends Logger {
/**
* This is the mapping from element index to cell info.
* The index in the array is the element index while the value at the position is an array where
Expand All @@ -29,14 +29,21 @@ export class MappedCells extends Logger {
*/
private cellToElementIndex!: DeepMap<number, number>;

private cellAdditionalInfo!: DeepMap<number, T_ADDITIONAL_CELL_INFO>;

/**
* Keeps the JSX of rendered elements in memory, so we can possibly reuse it later.
*/
private renderedElements!: Renderable[];

constructor() {
private withCellAdditionalInfo: boolean = false;

constructor(opts?: { withCellAdditionalInfo: boolean }) {
super(`MappedCells`);
this.init();
if (opts?.withCellAdditionalInfo) {
this.withCellAdditionalInfo = opts.withCellAdditionalInfo;
}

// if (__DEV__) {
// (globalThis as any).mappedCells = this;
Expand Down Expand Up @@ -69,6 +76,7 @@ export class MappedCells extends Logger {
init() {
this.elementIndexToCell = [];
this.cellToElementIndex = new DeepMap();
this.cellAdditionalInfo = new DeepMap();
this.renderedElements = [];
}

Expand All @@ -79,6 +87,7 @@ export class MappedCells extends Logger {
destroy() {
this.elementIndexToCell = [];
this.cellToElementIndex.clear();
this.cellAdditionalInfo.clear();
this.renderedElements = [];
}

Expand Down Expand Up @@ -129,6 +138,13 @@ export class MappedCells extends Logger {
return this.cellToElementIndex.has([rowIndex, columnIndex]);
};

getCellAdditionalInfo = (
rowIndex: number,
columnIndex: number,
): T_ADDITIONAL_CELL_INFO | undefined => {
return this.cellAdditionalInfo.get([rowIndex, columnIndex]);
};

isElementRendered = (elementIndex: number): boolean => {
return !!this.elementIndexToCell[elementIndex];
};
Expand Down Expand Up @@ -172,7 +188,8 @@ export class MappedCells extends Logger {
rowIndex: number,
colIndex: number,
elementIndex: number,
renderNode?: Renderable,
renderNode: Renderable | undefined,
cellAdditionalInfo?: T_ADDITIONAL_CELL_INFO,
) => {
if (__DEV__) {
this.debug(
Expand All @@ -184,14 +201,21 @@ export class MappedCells extends Logger {

const currentCell = this.elementIndexToCell[elementIndex];
if (currentCell) {
this.cellToElementIndex.delete([currentCell[0], currentCell[1]]);
const currentCellKey = [currentCell[0], currentCell[1]];
this.cellToElementIndex.delete(currentCellKey);
if (this.withCellAdditionalInfo) {
this.cellAdditionalInfo.delete(currentCellKey);
}
}
if (renderNode) {
this.renderedElements[elementIndex] = renderNode;
}

this.elementIndexToCell[elementIndex] = [rowIndex, colIndex];
this.cellToElementIndex.set(key, elementIndex);
if (this.withCellAdditionalInfo && cellAdditionalInfo !== undefined) {
this.cellAdditionalInfo.set(key, cellAdditionalInfo);
}
};

discardCell = (rowIndex: number, colIndex: number) => {
Expand All @@ -202,6 +226,9 @@ export class MappedCells extends Logger {
this.renderedElements[elementIndex] = null;
this.elementIndexToCell[elementIndex] = null;
this.cellToElementIndex.delete(key);
if (this.withCellAdditionalInfo) {
this.cellAdditionalInfo.delete(key);
}
}
};

Expand All @@ -213,6 +240,9 @@ export class MappedCells extends Logger {
this.renderedElements[elementIndex] = null;
this.elementIndexToCell[elementIndex] = null;
this.cellToElementIndex.delete(key);
if (this.withCellAdditionalInfo) {
this.cellAdditionalInfo.delete(key);
}

return cell;
}
Expand Down
Loading

0 comments on commit 54c9679

Please sign in to comment.