Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Virtualizer): custom sizes management #46

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@decathlon/react-table",
"version": "2.1.1",
"version": "3.0.0",
"description": "React components for efficiently rendering large tabular data",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
13 changes: 6 additions & 7 deletions src/components/scroller.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,16 +104,15 @@ class Scroller extends React.Component<IScrollerProps> {
if (this.scrollerContainer.current && horizontalPartWidth) {
const { scrollLeft } = this.scrollerContainer.current;
if (prevProps.virtualWidth !== virtualWidth && scrollLeft) {
const diff = prevProps.virtualWidth - virtualWidth;
const percentageDiff = (100 * diff) / prevProps.virtualWidth;
const newRelativeDiff = (virtualWidth * percentageDiff) / 100;
amen-souissi marked this conversation as resolved.
Show resolved Hide resolved
const nbIgnoredHorizontalParts = ignoredHorizontalParts?.length || 0;
const nbPrevIgnoredHorizontalParts = this.prevIgnoredHorizontalParts?.length || 0;
const nbRemovedParts = nbPrevIgnoredHorizontalParts - nbIgnoredHorizontalParts;
const scrollablePartsAreChanged =
this.prevIgnoredHorizontalParts && ignoredHorizontalParts !== this.prevIgnoredHorizontalParts;
// 1 if added or 0 if scrollable parts are changed ;
const changeKind = scrollablePartsAreChanged ? 1 : 0;
const oldPartScrollIndex = Math.floor(scrollLeft / horizontalPartWidth);
// 1 nb changed parts
const newLeft = (oldPartScrollIndex + changeKind * nbRemovedParts) * horizontalPartWidth;
// 1 if added or -1 if scrollable parts are removed ;
const changeDirection = diff >= 0 ? -1 : 1;
const newLeft = scrollLeft - newRelativeDiff + (changeDirection * newRelativeDiff) / nbRemovedParts;
this.scrollToLeft(newLeft);
}
}
Expand Down
16 changes: 13 additions & 3 deletions src/components/table/elementary-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ export interface IElementaryTable<IDataCoordinates = any> {
elevatedRowIndexes?: IElevateds;
/** Options to customize any columns, such as size or align */
columns?: IColumns;
/** Options to customize any rows, such as size */
rowsProps?: Record<number, IRowOptions>;
/** Options to customize any row, such as size */
globalRowProps?: IRowOptions;
/** Options to customize any column, such as size */
Expand All @@ -78,10 +80,11 @@ export interface IElementaryTableProps<IDataCoordinates = any> extends IElementa

class ElementaryTable extends React.Component<IElementaryTableProps> {
static defaultProps = {
elevatedColumnIndexes: {},
elevatedRowIndexes: {},
elevatedColumnIndexes: { elevations: {}, absoluteEndPositions: {} },
elevatedRowIndexes: { elevations: {}, absoluteEndPositions: {} },
openedTrees: {},
selectedCells: {},
rowsProps: {},
};

/** An utility of the table that return the length of the visible sub-rows
Expand Down Expand Up @@ -112,6 +115,7 @@ class ElementaryTable extends React.Component<IElementaryTableProps> {
isSpan,
rows,
columns,
rowsProps,
globalRowProps,
globalColumnProps,
visibleColumnIndexes,
Expand All @@ -137,13 +141,14 @@ class ElementaryTable extends React.Component<IElementaryTableProps> {
return result;
}
const rowIndex = relativeIndexes ? relativeIndexes[index] : index;
const rowProps = rowsProps ? rowsProps[rowIndex] : {};
const { subItems, index: rowAbsoluteIndex } = indexesMapping.relative[rowIndex];
const isVisible = !visibleRowIndexes || visibleRowIndexes.includes(rowAbsoluteIndex);
// @ts-ignore we have a default value for openedTrees
const rowOpenedTree = openedTrees[rowIndex];

// @ts-ignore we have a default value for openedTrees
const elevation = elevatedRowIndexes[rowAbsoluteIndex];
const elevation = elevatedRowIndexes?.elevations[rowAbsoluteIndex];
let rowSelectedCells = (subItems || selectedCells[rowAbsoluteIndex]) && selectedCells;
if (rowSelectedCells) {
const nextRowMap = indexesMapping.relative && indexesMapping.relative[rowIndex + 1];
Expand All @@ -152,20 +157,25 @@ class ElementaryTable extends React.Component<IElementaryTableProps> {
? filterIndexes(rowSelectedCells, rowAbsoluteIndex, nextRowAbsoluteIndex)
: rowSelectedCells;
}
const absolutePosition = elevatedRowIndexes?.absoluteEndPositions[rowAbsoluteIndex];
const rowStyle = absolutePosition != null ? { bottom: absolutePosition } : undefined;
const renderedRow = (
<Row
key={`row-${id}-${row.id}`}
{...globalRowProps}
{...row}
{...rowProps}
className={classnames(row.className, {
[`elevated-${elevation}`]: elevation,
})}
style={rowStyle}
absoluteIndex={rowAbsoluteIndex}
index={rowIndex}
isVisible={isVisible}
isSpan={isSpan}
columns={columns}
elevatedColumnIndexes={elevatedColumnIndexes}
elevatedRowIndexes={elevatedRowIndexes}
globalColumnProps={globalColumnProps}
visibleColumnIndexes={visibleColumnIndexes}
visibleRowIndexes={visibleRowIndexes}
Expand Down
59 changes: 44 additions & 15 deletions src/components/table/row.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ export interface IRowProps extends IRow {
visibleColumnIndexes?: number[];
/** Index of columns that need to be "elevated" by displaying a shadow on its right side */
elevatedColumnIndexes?: IElevateds;
/** Index of rows that need to be "elevated" by displaying a shadow on its bottom side */
elevatedRowIndexes?: IElevateds;
/** TODO with tree update */
openedTree?: ITree;
/**
Expand All @@ -70,6 +72,7 @@ export interface IRowProps extends IRow {
delegatedSpan?: JSX.Element;
/** Determine if the row needs to be displayed */
isVisible?: boolean;
style?: React.CSSProperties;
/** Column options to apply to the right cells of the row */
columns?: { [index: number]: IColumn };
/** Properties shared between cells belonging to the same columns */
Expand Down Expand Up @@ -109,7 +112,8 @@ export default class Row extends React.Component<IRowProps, IState> {
isVisible: true,
isSelectable: true,
columns: {},
elevatedColumnIndexes: [],
elevatedColumnIndexes: { elevations: {}, absoluteEndPositions: {} },
elevatedRowIndexes: { elevations: {}, absoluteEndPositions: {} },
selectedCells: [],
getVisibleRows: (rows: IRow[]) => [null, rows],
relativeSubIndexesMapping: {},
Expand Down Expand Up @@ -143,13 +147,20 @@ export default class Row extends React.Component<IRowProps, IState> {
delete nextRowProps.visibleRowIndexes;
delete rowProps.visibleRowIndexes;
}
const nextSelectCells = nextRowProps.selectedCells;
// @ts-ignore
delete nextRowProps.selectedCells;
const { selectedCells } = rowProps;
// @ts-ignore
delete rowProps.selectedCells;
return !shallowEqual(nextRowProps, rowProps) || !isEqual(nextSelectCells, selectedCells);
const {
selectedCells: nextSelectCells,
elevatedRowIndexes: nextElevatedRowIndexes,
elevatedColumnIndexes: nextElevatedColumnIndexes,
...otherNextProps
} = nextRowProps;
const { selectedCells, elevatedRowIndexes, elevatedColumnIndexes, ...otherProps } = rowProps;

return (
!shallowEqual(otherNextProps, otherProps) ||
!isEqual(nextSelectCells, selectedCells) ||
!isEqual(nextElevatedRowIndexes, elevatedRowIndexes) ||
!isEqual(nextElevatedColumnIndexes, elevatedColumnIndexes)
);
}

private updateOpenedCell = (cellIndex: number) => {
Expand Down Expand Up @@ -260,6 +271,7 @@ export default class Row extends React.Component<IRowProps, IState> {
visibleColumnIndexes,
relativeSubIndexesMapping,
elevatedColumnIndexes,
elevatedRowIndexes,
globalColumnProps,
onCellMouseUp,
onCellContextMenu,
Expand All @@ -285,6 +297,9 @@ export default class Row extends React.Component<IRowProps, IState> {
const { subItems, index: rowAbsoluteIndex } = relativeSubIndexesMapping[subRowIndex] || defaultRelativeIndex;
const isVisible = !visibleRowIndexes || (rowAbsoluteIndex !== undefined && visibleRowIndexes.includes(rowAbsoluteIndex));
const openedSubTree = subOpenedTrees[subRowIndex];
const elevation = elevatedRowIndexes?.elevations[rowAbsoluteIndex];
const absolutePosition = elevatedRowIndexes?.absoluteEndPositions[rowAbsoluteIndex];
const rowStyle = absolutePosition != null ? { bottom: absolutePosition } : undefined;
// get selected cells
let rowSelectedCells = (subItems || selectedCells[rowAbsoluteIndex]) && selectedCells;
const nextRowMap = relativeSubIndexesMapping && relativeSubIndexesMapping[subRowIndex + 1];
Expand All @@ -303,7 +318,9 @@ export default class Row extends React.Component<IRowProps, IState> {
id={subrowId}
className={classNames(subRow.className, `sub-row sub-row__${minLevel}`, {
"last-sub-row": subRows.length === subRowIndex + 1,
[`elevated-${elevation}`]: elevation,
})}
style={rowStyle}
absoluteIndex={rowAbsoluteIndex}
index={subRowIndex}
level={subLevel}
Expand All @@ -314,6 +331,7 @@ export default class Row extends React.Component<IRowProps, IState> {
visibleRowIndexes={visibleRowIndexes}
openedTree={openedSubTree}
elevatedColumnIndexes={elevatedColumnIndexes}
elevatedRowIndexes={elevatedRowIndexes}
relativeSubIndexesMapping={subItems}
delegatedSpan={subDelegatedSpan}
getVisibleRows={getVisibleRows}
Expand Down Expand Up @@ -353,7 +371,9 @@ export default class Row extends React.Component<IRowProps, IState> {
onCellContextMenu,
selectedCells,
isSelectable,
style,
} = this.props;

const openedCellIndex = openedTree ? openedTree.columnIndex : null;
const openedCell = openedCellIndex !== null ? cells[openedCellIndex] : null;
const firstCellIndexWithSubItems: number = isSpan ? this.getFirstCellIndexWithSubItems() : -1;
Expand Down Expand Up @@ -382,7 +402,7 @@ export default class Row extends React.Component<IRowProps, IState> {
opened: openedCellIndex !== null,
})}
// @ts-ignore
style={computeRowStyle(options)}
style={computeRowStyle(options, style)}
>
{delegatedSpan}
{isSpan && !delegatedSpan ? this.renderRowSpan(firstCellIndexWithSubItems >= 0) : null}
Expand All @@ -392,17 +412,26 @@ export default class Row extends React.Component<IRowProps, IState> {
}
const cellIndex = (visibleColumnIndexesAfterMapping && visibleColumnIndexesAfterMapping[index]) || index;
const cellColumn = columns ? columns[cellIndex] || {} : {};

const elevationIndex = mappingCellsWithColspan.indexToColspan[cellIndex].find(
(index) => !!(elevatedColumnIndexes && elevatedColumnIndexes.elevations[index])
);
// @ts-ignore elevationIndex !== undefined => elevatedColumnIndexes !== undefined
const elevation = elevatedColumnIndexes?.elevations[elevationIndex];
// @ts-ignore elevationIndex !== undefined => elevatedColumnIndexes !== undefined
const absolutePosition = elevatedColumnIndexes?.absoluteEndPositions[elevationIndex] || 0;

const column = {
isSelectable: true,
...globalColumnProps,
...cellColumn,
style: { ...globalColumnProps.style, ...cellColumn.style },
style: {
...globalColumnProps.style,
...cellColumn.style,
//@ts-ignore
right: absolutePosition,
},
};
const elevationIndex = mappingCellsWithColspan.indexToColspan[cellIndex].find(
(index) => !!(elevatedColumnIndexes && elevatedColumnIndexes[index])
);
// @ts-ignore elevationIndex !== undefined => elevatedColumnIndexes !== undefined
const elevation = elevationIndex !== undefined && elevatedColumnIndexes[elevationIndex];

const isSelected = (selectedRowCells && selectedRowCells.includes(cellIndex)) || false;
// By default, columns, rows and cells are selectable
Expand Down
51 changes: 37 additions & 14 deletions src/components/table/table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
getTreesLength,
getAllIndexesMap,
IIndexesMap,
getFixedElementsWithCustomSize,
getItemsCustomSizes,
relativeToAbsoluteIndexes,
getIndexesIdsMapping,
IIndexesIdsMapping,
Expand All @@ -18,7 +18,8 @@ import {
IRelativeIndexesMap,
IElevateds,
getDenseColumns,
FixedCustomSizesElements,
CustomSizesElements,
relativeToAbsoluteObject,
} from "../utils/table";
import SelectionHandler, { ISelection, ISelectionHandlerOptionalProps } from "../table-selection/selection-handler";
import { ROW_SPAN_WIDTH } from "../constants";
Expand Down Expand Up @@ -68,15 +69,27 @@ class Table<IDataCoordinates = any> extends React.Component<ITableProps<IDataCoo

private globalColumnProps?: IColumnOptions;

private fixedCellsHeight: FixedCustomSizesElements = {
sum: 0,
count: 0,
private customCellsHeight: CustomSizesElements = {
fixed: {
sum: 0,
count: 0,
},
scrollable: {
sum: 0,
count: 0,
},
customSizes: {},
};

private fixedCellsWidth: FixedCustomSizesElements = {
sum: 0,
count: 0,
private customCellsWidth: CustomSizesElements = {
fixed: {
sum: 0,
count: 0,
},
scrollable: {
sum: 0,
count: 0,
},
customSizes: {},
};

Expand All @@ -90,6 +103,7 @@ class Table<IDataCoordinates = any> extends React.Component<ITableProps<IDataCoo
initialOpenedTrees,
rows,
columns,
rowsProps,
isVirtualized,
virtualizerProps: { fixedRows, fixedColumns, hiddenRows, hiddenColumns },
} = this.props;
Expand All @@ -103,8 +117,8 @@ class Table<IDataCoordinates = any> extends React.Component<ITableProps<IDataCoo
fixedRowsIndexes: this.getFixedRowsIndexes(initialOpenedTrees, indexesMapping.relative),
};
if (isVirtualized) {
this.fixedCellsHeight = getFixedElementsWithCustomSize(rows, fixedRows, hiddenRows);
this.fixedCellsWidth = getFixedElementsWithCustomSize(columns, fixedColumns, hiddenColumns);
this.customCellsHeight = getItemsCustomSizes(rowsProps, fixedRows, hiddenRows);
this.customCellsWidth = getItemsCustomSizes(columns, fixedColumns, hiddenColumns);
}
}

Expand Down Expand Up @@ -347,13 +361,22 @@ class Table<IDataCoordinates = any> extends React.Component<ITableProps<IDataCoo
rowsLength={rowsLength}
width={width}
height={height}
fixedCellsHeight={this.fixedCellsHeight}
fixedCellsWidth={this.fixedCellsWidth}
customCellsHeight={{
...this.customCellsHeight,
customSizes: relativeToAbsoluteObject(this.customCellsHeight.customSizes, indexesMapping.relative),
}}
customCellsWidth={this.customCellsWidth}
verticalPadding={isSpan ? ROW_SPAN_WIDTH : 0}
>
{({ visibleColumnIndexes, visibleRowIndexes, elevatedColumnIndexes, elevatedRowIndexes, cellHeight, cellWidth }) => {
const tableWidth = this.fixedCellsWidth.sum + (visibleColumnIndexes.length - this.fixedCellsWidth.count) * cellWidth;
const adjustedColumns = getDenseColumns(tableWidth, width, this.columnsLength, columns);
const tableWidth =
this.customCellsWidth.fixed.sum +
this.customCellsWidth.scrollable.sum +
(visibleColumnIndexes.length - this.customCellsWidth.fixed.count - this.customCellsWidth.scrollable.count) *
cellWidth;
const adjustedColumns = !virtualizerProps.fixedColumns?.includes(visibleColumnIndexes[visibleColumnIndexes.length - 1])
? getDenseColumns(tableWidth, width, this.columnsLength, columns)
: columns;

return this.renderTable(
visibleColumnIndexes,
Expand Down
Loading