feat: Support row virtualization using react-window in Table widget (#16872)
This commit is contained in:
parent
4bae04ea64
commit
81458035d5
|
|
@ -0,0 +1,73 @@
|
||||||
|
import { ObjectsRegistry } from "../../../../../support/Objects/Registry";
|
||||||
|
|
||||||
|
const PropertyPane = ObjectsRegistry.PropertyPane;
|
||||||
|
const totalRows = 100;
|
||||||
|
|
||||||
|
describe("Table Widget Virtualized Row", function() {
|
||||||
|
before(() => {
|
||||||
|
cy.dragAndDropToCanvas("tablewidgetv2", { x: 300, y: 600 });
|
||||||
|
const row = {
|
||||||
|
step: "#3",
|
||||||
|
task: "Bind the query using => fetch_users.data",
|
||||||
|
status: "--",
|
||||||
|
action: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
const rows = new Array(totalRows).fill("").map((d, i) => ({
|
||||||
|
...row,
|
||||||
|
step: i,
|
||||||
|
}));
|
||||||
|
|
||||||
|
PropertyPane.UpdatePropertyFieldValue("Table Data", JSON.stringify(rows));
|
||||||
|
PropertyPane.ToggleOnOrOff("Server Side Pagination", "On");
|
||||||
|
PropertyPane.ToggleOnOrOff("Show Pagination", "Off");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("1. should check that row is getting rendered", () => {
|
||||||
|
cy.get(".tr[data-rowindex]").should("exist");
|
||||||
|
cy.get(".td[data-rowindex]").should("exist");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("2. should check that virtual rows are getting rendered when scrolling through the table", () => {
|
||||||
|
cy.get(".tr[data-rowindex]").should("not.have.length", totalRows);
|
||||||
|
cy.get(".tr[data-rowindex='0']").should("exist");
|
||||||
|
cy.get(".tbody > div").scrollTo("bottom");
|
||||||
|
cy.wait(500);
|
||||||
|
cy.get(".tr[data-rowindex='0']").should("not.exist");
|
||||||
|
cy.get(".tr[data-rowindex='98']").should("exist");
|
||||||
|
cy.get(".tbody > div").scrollTo("top");
|
||||||
|
cy.wait(500);
|
||||||
|
cy.get(".tr[data-rowindex='0']").should("exist");
|
||||||
|
cy.get(".tr[data-rowindex='98']").should("not.exist");
|
||||||
|
cy.get(".t--virtual-row").should("exist");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("3. should check that virtual rows feature is turned off when cell wrapping is enabled", () => {
|
||||||
|
cy.editColumn("step");
|
||||||
|
cy.wait(500);
|
||||||
|
PropertyPane.ToggleOnOrOff("Cell Wrapping", "On");
|
||||||
|
cy.get(".tr[data-rowindex]").should("have.length", totalRows);
|
||||||
|
cy.get(".tr[data-rowindex='0']").should("exist");
|
||||||
|
cy.get(".tr[data-rowindex='98']").should("exist");
|
||||||
|
cy.get(".tbody").scrollTo("bottom");
|
||||||
|
cy.wait(500);
|
||||||
|
cy.get(".tr[data-rowindex='0']").should("exist");
|
||||||
|
cy.get(".tr[data-rowindex='98']").should("exist");
|
||||||
|
cy.get(".tbody").scrollTo("top");
|
||||||
|
cy.wait(500);
|
||||||
|
cy.get(".tr[data-rowindex='0']").should("exist");
|
||||||
|
cy.get(".tr[data-rowindex='98']").should("exist");
|
||||||
|
cy.get(".t--virtual-row").should("not.exist");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("4. should check that virtual rows feature is turned off when server side pagination is disabled", () => {
|
||||||
|
PropertyPane.ToggleOnOrOff("Cell Wrapping", "Off");
|
||||||
|
PropertyPane.NavigateBackToPropertyPane();
|
||||||
|
cy.wait(500);
|
||||||
|
PropertyPane.ToggleOnOrOff("Show Pagination", "On");
|
||||||
|
cy.wait(500);
|
||||||
|
PropertyPane.ToggleOnOrOff("Server Side Pagination", "Off");
|
||||||
|
cy.get(".tr[data-rowindex]").should("have.length", 5);
|
||||||
|
cy.get(".t--virtual-row").should("not.exist");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -53,6 +53,7 @@
|
||||||
"exceljs-lightweight": "^1.14.0",
|
"exceljs-lightweight": "^1.14.0",
|
||||||
"fast-deep-equal": "^3.1.3",
|
"fast-deep-equal": "^3.1.3",
|
||||||
"fast-xml-parser": "^3.17.5",
|
"fast-xml-parser": "^3.17.5",
|
||||||
|
"fastdom": "^1.0.11",
|
||||||
"flow-bin": "^0.148.0",
|
"flow-bin": "^0.148.0",
|
||||||
"focus-trap-react": "^8.9.2",
|
"focus-trap-react": "^8.9.2",
|
||||||
"fuse.js": "^3.4.5",
|
"fuse.js": "^3.4.5",
|
||||||
|
|
|
||||||
|
|
@ -464,3 +464,5 @@ export const scrollbarOnHoverCSS = `
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const MULTISELECT_CHECKBOX_WIDTH = 40;
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import {
|
||||||
useBlockLayout,
|
useBlockLayout,
|
||||||
useResizeColumns,
|
useResizeColumns,
|
||||||
useRowSelect,
|
useRowSelect,
|
||||||
Row,
|
Row as ReactTableRowType,
|
||||||
} from "react-table";
|
} from "react-table";
|
||||||
import {
|
import {
|
||||||
TableWrapper,
|
TableWrapper,
|
||||||
|
|
@ -28,12 +28,10 @@ import { ScrollIndicator } from "design-system";
|
||||||
import { EventType } from "constants/AppsmithActionConstants/ActionConstants";
|
import { EventType } from "constants/AppsmithActionConstants/ActionConstants";
|
||||||
import { Scrollbars } from "react-custom-scrollbars";
|
import { Scrollbars } from "react-custom-scrollbars";
|
||||||
import { renderEmptyRows } from "./cellComponents/EmptyCell";
|
import { renderEmptyRows } from "./cellComponents/EmptyCell";
|
||||||
import {
|
import { renderHeaderCheckBoxCell } from "./cellComponents/SelectionCheckboxCell";
|
||||||
renderBodyCheckBoxCell,
|
|
||||||
renderHeaderCheckBoxCell,
|
|
||||||
} from "./cellComponents/SelectionCheckboxCell";
|
|
||||||
import { HeaderCell } from "./cellComponents/HeaderCell";
|
import { HeaderCell } from "./cellComponents/HeaderCell";
|
||||||
import { EditableCell } from "../constants";
|
import { EditableCell } from "../constants";
|
||||||
|
import { TableBody } from "./TableBody";
|
||||||
|
|
||||||
interface TableProps {
|
interface TableProps {
|
||||||
width: number;
|
width: number;
|
||||||
|
|
@ -68,7 +66,7 @@ interface TableProps {
|
||||||
enableDrag: () => void;
|
enableDrag: () => void;
|
||||||
toggleAllRowSelect: (
|
toggleAllRowSelect: (
|
||||||
isSelect: boolean,
|
isSelect: boolean,
|
||||||
pageData: Row<Record<string, unknown>>[],
|
pageData: ReactTableRowType<Record<string, unknown>>[],
|
||||||
) => void;
|
) => void;
|
||||||
triggerRowSelection: boolean;
|
triggerRowSelection: boolean;
|
||||||
searchTableData: (searchKey: any) => void;
|
searchTableData: (searchKey: any) => void;
|
||||||
|
|
@ -85,6 +83,7 @@ interface TableProps {
|
||||||
boxShadow?: string;
|
boxShadow?: string;
|
||||||
onBulkEditDiscard: () => void;
|
onBulkEditDiscard: () => void;
|
||||||
onBulkEditSave: () => void;
|
onBulkEditSave: () => void;
|
||||||
|
primaryColumnId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultColumn = {
|
const defaultColumn = {
|
||||||
|
|
@ -185,7 +184,6 @@ export function Table(props: TableProps) {
|
||||||
endIndex = props.data.length;
|
endIndex = props.data.length;
|
||||||
}
|
}
|
||||||
const subPage = page.slice(startIndex, endIndex);
|
const subPage = page.slice(startIndex, endIndex);
|
||||||
const selectedRowIndex = props.selectedRowIndex;
|
|
||||||
const selectedRowIndices = props.selectedRowIndices || [];
|
const selectedRowIndices = props.selectedRowIndices || [];
|
||||||
const tableSizes = TABLE_SIZES[props.compactMode || CompactModeTypes.DEFAULT];
|
const tableSizes = TABLE_SIZES[props.compactMode || CompactModeTypes.DEFAULT];
|
||||||
const tableWrapperRef = useRef<HTMLDivElement | null>(null);
|
const tableWrapperRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
|
@ -228,6 +226,12 @@ export function Table(props: TableProps) {
|
||||||
[props.width],
|
[props.width],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const shouldUseVirtual =
|
||||||
|
props.serverSidePaginationEnabled &&
|
||||||
|
!props.columns.some(
|
||||||
|
(column) => !!column.columnProperties.allowCellWrapping,
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableWrapper
|
<TableWrapper
|
||||||
accentColor={props.accentColor}
|
accentColor={props.accentColor}
|
||||||
|
|
@ -354,73 +358,32 @@ export function Table(props: TableProps) {
|
||||||
props.columns,
|
props.columns,
|
||||||
props.width,
|
props.width,
|
||||||
subPage,
|
subPage,
|
||||||
prepareRow,
|
|
||||||
props.multiRowSelection,
|
props.multiRowSelection,
|
||||||
props.accentColor,
|
props.accentColor,
|
||||||
props.borderRadius,
|
props.borderRadius,
|
||||||
|
{},
|
||||||
|
prepareRow,
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<TableBody
|
||||||
{...getTableBodyProps()}
|
accentColor={props.accentColor}
|
||||||
className={`tbody ${
|
borderRadius={props.borderRadius}
|
||||||
props.pageSize > subPage.length ? "no-scroll" : ""
|
columns={props.columns}
|
||||||
}`}
|
getTableBodyProps={getTableBodyProps}
|
||||||
|
height={props.height}
|
||||||
|
multiRowSelection={!!props.multiRowSelection}
|
||||||
|
pageSize={props.pageSize}
|
||||||
|
prepareRow={prepareRow}
|
||||||
|
primaryColumnId={props.primaryColumnId}
|
||||||
ref={tableBodyRef}
|
ref={tableBodyRef}
|
||||||
>
|
rows={subPage}
|
||||||
{subPage.map((row, rowIndex) => {
|
selectTableRow={props.selectTableRow}
|
||||||
prepareRow(row);
|
selectedRowIndex={props.selectedRowIndex}
|
||||||
const rowProps = {
|
selectedRowIndices={props.selectedRowIndices}
|
||||||
...row.getRowProps(),
|
tableSizes={tableSizes}
|
||||||
style: { display: "flex" },
|
useVirtual={shouldUseVirtual}
|
||||||
};
|
width={props.width}
|
||||||
const isRowSelected = props.multiRowSelection
|
/>
|
||||||
? selectedRowIndices.includes(row.index)
|
|
||||||
: row.index === selectedRowIndex;
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
{...rowProps}
|
|
||||||
className={"tr" + `${isRowSelected ? " selected-row" : ""}`}
|
|
||||||
key={rowIndex}
|
|
||||||
onClick={(e) => {
|
|
||||||
row.toggleRowSelected();
|
|
||||||
props.selectTableRow(row);
|
|
||||||
e.stopPropagation();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{props.multiRowSelection &&
|
|
||||||
renderBodyCheckBoxCell(
|
|
||||||
isRowSelected,
|
|
||||||
props.accentColor,
|
|
||||||
props.borderRadius,
|
|
||||||
)}
|
|
||||||
{row.cells.map((cell, cellIndex) => {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
{...cell.getCellProps()}
|
|
||||||
className="td"
|
|
||||||
data-colindex={cellIndex}
|
|
||||||
data-rowindex={rowIndex}
|
|
||||||
key={cellIndex}
|
|
||||||
>
|
|
||||||
{cell.render("Cell")}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
{props.pageSize > subPage.length &&
|
|
||||||
renderEmptyRows(
|
|
||||||
props.pageSize - subPage.length,
|
|
||||||
props.columns,
|
|
||||||
props.width,
|
|
||||||
subPage,
|
|
||||||
prepareRow,
|
|
||||||
props.multiRowSelection,
|
|
||||||
props.accentColor,
|
|
||||||
props.borderRadius,
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</Scrollbars>
|
</Scrollbars>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
129
app/client/src/widgets/TableWidgetV2/component/TableBody/Row.tsx
Normal file
129
app/client/src/widgets/TableWidgetV2/component/TableBody/Row.tsx
Normal file
|
|
@ -0,0 +1,129 @@
|
||||||
|
import React, { CSSProperties, Key, useContext } from "react";
|
||||||
|
import { Row as ReactTableRowType } from "react-table";
|
||||||
|
import { ListChildComponentProps } from "react-window";
|
||||||
|
import { BodyContext } from ".";
|
||||||
|
import { renderEmptyRows } from "../cellComponents/EmptyCell";
|
||||||
|
import { renderBodyCheckBoxCell } from "../cellComponents/SelectionCheckboxCell";
|
||||||
|
|
||||||
|
type RowType = {
|
||||||
|
className?: string;
|
||||||
|
index: number;
|
||||||
|
row: ReactTableRowType<Record<string, unknown>>;
|
||||||
|
style?: ListChildComponentProps["style"];
|
||||||
|
};
|
||||||
|
|
||||||
|
export function Row(props: RowType) {
|
||||||
|
const {
|
||||||
|
accentColor,
|
||||||
|
borderRadius,
|
||||||
|
multiRowSelection,
|
||||||
|
prepareRow,
|
||||||
|
primaryColumnId,
|
||||||
|
selectedRowIndex,
|
||||||
|
selectedRowIndices,
|
||||||
|
selectTableRow,
|
||||||
|
} = useContext(BodyContext);
|
||||||
|
|
||||||
|
prepareRow?.(props.row);
|
||||||
|
const rowProps = {
|
||||||
|
...props.row.getRowProps(),
|
||||||
|
style: {
|
||||||
|
display: "flex",
|
||||||
|
...(props.style || {}),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const isRowSelected = multiRowSelection
|
||||||
|
? selectedRowIndices.includes(props.row.index)
|
||||||
|
: props.row.index === selectedRowIndex;
|
||||||
|
|
||||||
|
const key =
|
||||||
|
(primaryColumnId && (props.row.original[primaryColumnId] as Key)) ||
|
||||||
|
props.index;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
{...rowProps}
|
||||||
|
className={`tr ${isRowSelected ? "selected-row" : ""} ${props.className ||
|
||||||
|
""}`}
|
||||||
|
data-rowindex={props.index}
|
||||||
|
key={key}
|
||||||
|
onClick={(e) => {
|
||||||
|
props.row.toggleRowSelected();
|
||||||
|
selectTableRow?.(props.row);
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
|
role="button"
|
||||||
|
>
|
||||||
|
{multiRowSelection &&
|
||||||
|
renderBodyCheckBoxCell(isRowSelected, accentColor, borderRadius)}
|
||||||
|
{props.row.cells.map((cell, cellIndex) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
{...cell.getCellProps()}
|
||||||
|
className="td"
|
||||||
|
data-colindex={cellIndex}
|
||||||
|
data-rowindex={props.index}
|
||||||
|
key={cellIndex}
|
||||||
|
>
|
||||||
|
{cell.render("Cell")}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const EmptyRows = (props: {
|
||||||
|
style?: CSSProperties;
|
||||||
|
rowCount: number;
|
||||||
|
}) => {
|
||||||
|
const {
|
||||||
|
accentColor,
|
||||||
|
borderRadius,
|
||||||
|
columns,
|
||||||
|
multiRowSelection,
|
||||||
|
prepareRow,
|
||||||
|
rows,
|
||||||
|
width,
|
||||||
|
} = useContext(BodyContext);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{renderEmptyRows(
|
||||||
|
props.rowCount,
|
||||||
|
columns,
|
||||||
|
width,
|
||||||
|
rows,
|
||||||
|
multiRowSelection,
|
||||||
|
accentColor,
|
||||||
|
borderRadius,
|
||||||
|
props.style,
|
||||||
|
prepareRow,
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const EmptyRow = (props: { style?: CSSProperties }) => {
|
||||||
|
const {
|
||||||
|
accentColor,
|
||||||
|
borderRadius,
|
||||||
|
columns,
|
||||||
|
multiRowSelection,
|
||||||
|
prepareRow,
|
||||||
|
rows,
|
||||||
|
width,
|
||||||
|
} = useContext(BodyContext);
|
||||||
|
|
||||||
|
return renderEmptyRows(
|
||||||
|
1,
|
||||||
|
columns,
|
||||||
|
width,
|
||||||
|
rows,
|
||||||
|
multiRowSelection,
|
||||||
|
accentColor,
|
||||||
|
borderRadius,
|
||||||
|
props.style,
|
||||||
|
prepareRow,
|
||||||
|
)?.[0];
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,155 @@
|
||||||
|
import React, { Ref } from "react";
|
||||||
|
import {
|
||||||
|
Row as ReactTableRowType,
|
||||||
|
TableBodyPropGetter,
|
||||||
|
TableBodyProps,
|
||||||
|
} from "react-table";
|
||||||
|
import { FixedSizeList, ListChildComponentProps, areEqual } from "react-window";
|
||||||
|
import { WIDGET_PADDING } from "constants/WidgetConstants";
|
||||||
|
import { EmptyRows, EmptyRow, Row } from "./Row";
|
||||||
|
import { ReactTableColumnProps, TableSizes } from "../Constants";
|
||||||
|
|
||||||
|
type BodyContextType = {
|
||||||
|
accentColor: string;
|
||||||
|
borderRadius: string;
|
||||||
|
multiRowSelection: boolean;
|
||||||
|
prepareRow?(row: ReactTableRowType<Record<string, unknown>>): void;
|
||||||
|
selectTableRow?: (row: {
|
||||||
|
original: Record<string, unknown>;
|
||||||
|
index: number;
|
||||||
|
}) => void;
|
||||||
|
selectedRowIndex: number;
|
||||||
|
selectedRowIndices: number[];
|
||||||
|
columns: ReactTableColumnProps[];
|
||||||
|
width: number;
|
||||||
|
rows: ReactTableRowType<Record<string, unknown>>[];
|
||||||
|
primaryColumnId?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const BodyContext = React.createContext<BodyContextType>({
|
||||||
|
accentColor: "",
|
||||||
|
borderRadius: "",
|
||||||
|
multiRowSelection: false,
|
||||||
|
selectedRowIndex: -1,
|
||||||
|
selectedRowIndices: [],
|
||||||
|
columns: [],
|
||||||
|
width: 0,
|
||||||
|
rows: [],
|
||||||
|
primaryColumnId: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
const rowRenderer = React.memo((rowProps: ListChildComponentProps) => {
|
||||||
|
const { data, index, style } = rowProps;
|
||||||
|
|
||||||
|
if (index < data.length) {
|
||||||
|
const row = data[index];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Row
|
||||||
|
className="t--virtual-row"
|
||||||
|
index={index}
|
||||||
|
key={index}
|
||||||
|
row={row}
|
||||||
|
style={style}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return <EmptyRow style={style} />;
|
||||||
|
}
|
||||||
|
}, areEqual);
|
||||||
|
|
||||||
|
type BodyPropsType = {
|
||||||
|
getTableBodyProps(
|
||||||
|
propGetter?: TableBodyPropGetter<Record<string, unknown>> | undefined,
|
||||||
|
): TableBodyProps;
|
||||||
|
pageSize: number;
|
||||||
|
rows: ReactTableRowType<Record<string, unknown>>[];
|
||||||
|
height: number;
|
||||||
|
tableSizes: TableSizes;
|
||||||
|
};
|
||||||
|
|
||||||
|
const TableVirtualBodyComponent = React.forwardRef(
|
||||||
|
(props: BodyPropsType, ref: Ref<HTMLDivElement>) => {
|
||||||
|
return (
|
||||||
|
<div {...props.getTableBodyProps()} className="tbody no-scroll">
|
||||||
|
<FixedSizeList
|
||||||
|
height={
|
||||||
|
props.height -
|
||||||
|
props.tableSizes.TABLE_HEADER_HEIGHT -
|
||||||
|
props.tableSizes.COLUMN_HEADER_HEIGHT -
|
||||||
|
2 * WIDGET_PADDING // Top and bottom padding
|
||||||
|
}
|
||||||
|
itemCount={Math.max(props.rows.length, props.pageSize)}
|
||||||
|
itemData={props.rows}
|
||||||
|
itemSize={props.tableSizes.ROW_HEIGHT}
|
||||||
|
outerRef={ref}
|
||||||
|
width={`calc(100% + ${2 * WIDGET_PADDING}px`}
|
||||||
|
>
|
||||||
|
{rowRenderer}
|
||||||
|
</FixedSizeList>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const TableBodyComponent = React.forwardRef(
|
||||||
|
(props: BodyPropsType, ref: Ref<HTMLDivElement>) => {
|
||||||
|
return (
|
||||||
|
<div {...props.getTableBodyProps()} className="tbody" ref={ref}>
|
||||||
|
{props.rows.map((row, index) => {
|
||||||
|
return <Row index={index} key={index} row={row} />;
|
||||||
|
})}
|
||||||
|
{props.pageSize > props.rows.length && (
|
||||||
|
<EmptyRows rowCount={props.pageSize - props.rows.length} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export const TableBody = React.forwardRef(
|
||||||
|
(
|
||||||
|
props: BodyPropsType & BodyContextType & { useVirtual: boolean },
|
||||||
|
ref: Ref<HTMLDivElement>,
|
||||||
|
) => {
|
||||||
|
const {
|
||||||
|
accentColor,
|
||||||
|
borderRadius,
|
||||||
|
columns,
|
||||||
|
multiRowSelection,
|
||||||
|
prepareRow,
|
||||||
|
primaryColumnId,
|
||||||
|
rows,
|
||||||
|
selectedRowIndex,
|
||||||
|
selectedRowIndices,
|
||||||
|
selectTableRow,
|
||||||
|
useVirtual,
|
||||||
|
width,
|
||||||
|
...restOfProps
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BodyContext.Provider
|
||||||
|
value={{
|
||||||
|
accentColor,
|
||||||
|
borderRadius,
|
||||||
|
multiRowSelection,
|
||||||
|
prepareRow,
|
||||||
|
primaryColumnId,
|
||||||
|
selectTableRow,
|
||||||
|
selectedRowIndex,
|
||||||
|
selectedRowIndices,
|
||||||
|
columns,
|
||||||
|
width,
|
||||||
|
rows,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{useVirtual ? (
|
||||||
|
<TableVirtualBodyComponent ref={ref} rows={rows} {...restOfProps} />
|
||||||
|
) : (
|
||||||
|
<TableBodyComponent ref={ref} rows={rows} {...restOfProps} />
|
||||||
|
)}
|
||||||
|
</BodyContext.Provider>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
@ -11,6 +11,7 @@ import {
|
||||||
CellAlignment,
|
CellAlignment,
|
||||||
VerticalAlignment,
|
VerticalAlignment,
|
||||||
scrollbarOnHoverCSS,
|
scrollbarOnHoverCSS,
|
||||||
|
MULTISELECT_CHECKBOX_WIDTH,
|
||||||
} from "./Constants";
|
} from "./Constants";
|
||||||
import { Colors, Color } from "constants/Colors";
|
import { Colors, Color } from "constants/Colors";
|
||||||
import { hideScrollbar, invisible } from "constants/DefaultTheme";
|
import { hideScrollbar, invisible } from "constants/DefaultTheme";
|
||||||
|
|
@ -81,6 +82,9 @@ export const TableWrapper = styled.div<{
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
${hideScrollbar};
|
${hideScrollbar};
|
||||||
}
|
}
|
||||||
|
.tbody.no-scroll {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
.tr {
|
.tr {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
cursor: ${(props) => props.triggerRowSelection && "pointer"};
|
cursor: ${(props) => props.triggerRowSelection && "pointer"};
|
||||||
|
|
@ -429,11 +433,11 @@ export const CellWrapper = styled.div<{
|
||||||
|
|
||||||
export const CellCheckboxWrapper = styled(CellWrapper)<{
|
export const CellCheckboxWrapper = styled(CellWrapper)<{
|
||||||
isChecked?: boolean;
|
isChecked?: boolean;
|
||||||
accentColor: string;
|
accentColor?: string;
|
||||||
borderRadius: string;
|
borderRadius?: string;
|
||||||
}>`
|
}>`
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 40px;
|
width: ${MULTISELECT_CHECKBOX_WIDTH}px;
|
||||||
height: auto;
|
height: auto;
|
||||||
& > div {
|
& > div {
|
||||||
border-radius: ${({ borderRadius }) => borderRadius};
|
border-radius: ${({ borderRadius }) => borderRadius};
|
||||||
|
|
|
||||||
|
|
@ -39,15 +39,14 @@ function useToolTip(
|
||||||
const [showTooltip, updateToolTip] = useState(false);
|
const [showTooltip, updateToolTip] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
requestAnimationFrame(() => {
|
const element = ref.current?.querySelector("div") as HTMLDivElement;
|
||||||
const element = ref.current?.querySelector("div") as HTMLDivElement;
|
|
||||||
if (element && element.offsetWidth < element.scrollWidth) {
|
if (element && element.offsetWidth < element.scrollWidth) {
|
||||||
updateToolTip(true);
|
updateToolTip(true);
|
||||||
} else {
|
} else {
|
||||||
updateToolTip(false);
|
updateToolTip(false);
|
||||||
}
|
}
|
||||||
});
|
}, [children]);
|
||||||
}, [children, ref.current]);
|
|
||||||
|
|
||||||
return showTooltip && children ? (
|
return showTooltip && children ? (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import React from "react";
|
import React, { CSSProperties } from "react";
|
||||||
import { Cell, Row } from "react-table";
|
import { Cell, Row } from "react-table";
|
||||||
import { ReactTableColumnProps } from "../Constants";
|
import { ReactTableColumnProps } from "../Constants";
|
||||||
import { EmptyCell, EmptyRow } from "../TableStyledWrappers";
|
import { EmptyCell, EmptyRow } from "../TableStyledWrappers";
|
||||||
|
|
@ -9,10 +9,11 @@ export const renderEmptyRows = (
|
||||||
columns: ReactTableColumnProps[],
|
columns: ReactTableColumnProps[],
|
||||||
tableWidth: number,
|
tableWidth: number,
|
||||||
page: Row<Record<string, unknown>>[],
|
page: Row<Record<string, unknown>>[],
|
||||||
prepareRow: (row: Row<Record<string, unknown>>) => void,
|
|
||||||
multiRowSelection = false,
|
multiRowSelection = false,
|
||||||
accentColor: string,
|
accentColor: string,
|
||||||
borderRadius: string,
|
borderRadius: string,
|
||||||
|
style?: CSSProperties,
|
||||||
|
prepareRow?: (row: Row<Record<string, unknown>>) => void,
|
||||||
) => {
|
) => {
|
||||||
const rows: string[] = new Array(rowCount).fill("");
|
const rows: string[] = new Array(rowCount).fill("");
|
||||||
|
|
||||||
|
|
@ -20,10 +21,13 @@ export const renderEmptyRows = (
|
||||||
const row = page[0];
|
const row = page[0];
|
||||||
|
|
||||||
return rows.map((item: string, index: number) => {
|
return rows.map((item: string, index: number) => {
|
||||||
prepareRow(row);
|
prepareRow?.(row);
|
||||||
const rowProps = {
|
const rowProps = {
|
||||||
...row.getRowProps(),
|
...row.getRowProps(),
|
||||||
style: { display: "flex" },
|
style: {
|
||||||
|
display: "flex",
|
||||||
|
...style,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div {...rowProps} className="tr" key={index}>
|
<div {...rowProps} className="tr" key={index}>
|
||||||
|
|
@ -41,26 +45,18 @@ export const renderEmptyRows = (
|
||||||
? columns
|
? columns
|
||||||
: new Array(3).fill({ width: tableWidth / 3, isHidden: false });
|
: new Array(3).fill({ width: tableWidth / 3, isHidden: false });
|
||||||
|
|
||||||
return (
|
return rows.map((row: string, index: number) => {
|
||||||
<>
|
return (
|
||||||
{rows.map((row: string, index: number) => {
|
<EmptyRow className="tr" key={index} style={style}>
|
||||||
return (
|
{multiRowSelection &&
|
||||||
<EmptyRow className="tr" key={index}>
|
renderBodyCheckBoxCell(false, accentColor, borderRadius)}
|
||||||
{multiRowSelection &&
|
{tableColumns.map((column: any, colIndex: number) => {
|
||||||
renderBodyCheckBoxCell(false, accentColor, borderRadius)}
|
return (
|
||||||
{tableColumns.map((column: any, colIndex: number) => {
|
<EmptyCell className="td" key={colIndex} width={column.width} />
|
||||||
return (
|
);
|
||||||
<EmptyCell
|
})}
|
||||||
className="td"
|
</EmptyRow>
|
||||||
key={colIndex}
|
);
|
||||||
width={column.width}
|
});
|
||||||
/>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</EmptyRow>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ import { CheckboxState } from "../Constants";
|
||||||
|
|
||||||
export const renderBodyCheckBoxCell = (
|
export const renderBodyCheckBoxCell = (
|
||||||
isChecked: boolean,
|
isChecked: boolean,
|
||||||
accentColor: string,
|
accentColor?: string,
|
||||||
borderRadius: string,
|
borderRadius?: string,
|
||||||
) => (
|
) => (
|
||||||
<CellCheckboxWrapper
|
<CellCheckboxWrapper
|
||||||
accentColor={accentColor}
|
accentColor={accentColor}
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,7 @@ interface ReactTableComponentProps {
|
||||||
borderRadius: string;
|
borderRadius: string;
|
||||||
boxShadow?: string;
|
boxShadow?: string;
|
||||||
isEditableCellValid?: boolean;
|
isEditableCellValid?: boolean;
|
||||||
|
primaryColumnId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function ReactTableComponent(props: ReactTableComponentProps) {
|
function ReactTableComponent(props: ReactTableComponentProps) {
|
||||||
|
|
@ -113,6 +114,7 @@ function ReactTableComponent(props: ReactTableComponentProps) {
|
||||||
pageNo,
|
pageNo,
|
||||||
pageSize,
|
pageSize,
|
||||||
prevPageClick,
|
prevPageClick,
|
||||||
|
primaryColumnId,
|
||||||
searchKey,
|
searchKey,
|
||||||
searchTableData,
|
searchTableData,
|
||||||
selectAllRow,
|
selectAllRow,
|
||||||
|
|
@ -293,6 +295,7 @@ function ReactTableComponent(props: ReactTableComponentProps) {
|
||||||
pageNo={pageNo - 1}
|
pageNo={pageNo - 1}
|
||||||
pageSize={pageSize || 1}
|
pageSize={pageSize || 1}
|
||||||
prevPageClick={prevPageClick}
|
prevPageClick={prevPageClick}
|
||||||
|
primaryColumnId={primaryColumnId}
|
||||||
searchKey={searchKey}
|
searchKey={searchKey}
|
||||||
searchTableData={searchTableData}
|
searchTableData={searchTableData}
|
||||||
selectTableRow={selectTableRow}
|
selectTableRow={selectTableRow}
|
||||||
|
|
@ -354,6 +357,7 @@ export default React.memo(ReactTableComponent, (prev, next) => {
|
||||||
// and we are not changing the columns manually.
|
// and we are not changing the columns manually.
|
||||||
JSON.stringify(prev.columns) === JSON.stringify(next.columns) &&
|
JSON.stringify(prev.columns) === JSON.stringify(next.columns) &&
|
||||||
equal(prev.editableCell, next.editableCell) &&
|
equal(prev.editableCell, next.editableCell) &&
|
||||||
prev.isEditableCellValid === next.isEditableCellValid
|
prev.isEditableCellValid === next.isEditableCellValid &&
|
||||||
|
prev.primaryColumnId === next.primaryColumnId
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -858,6 +858,7 @@ class TableWidgetV2 extends BaseWidget<TableWidgetProps, WidgetState> {
|
||||||
isVisibleHeaderOptions ? Math.max(1, pageSize) : pageSize + 1
|
isVisibleHeaderOptions ? Math.max(1, pageSize) : pageSize + 1
|
||||||
}
|
}
|
||||||
prevPageClick={this.handlePrevPageClick}
|
prevPageClick={this.handlePrevPageClick}
|
||||||
|
primaryColumnId={this.props.primaryColumnId}
|
||||||
searchKey={this.props.searchText}
|
searchKey={this.props.searchText}
|
||||||
searchTableData={this.handleSearchTable}
|
searchTableData={this.handleSearchTable}
|
||||||
selectAllRow={this.handleAllRowSelect}
|
selectAllRow={this.handleAllRowSelect}
|
||||||
|
|
|
||||||
|
|
@ -7376,6 +7376,13 @@ fast-xml-parser@^3.17.5:
|
||||||
version "3.17.5"
|
version "3.17.5"
|
||||||
resolved "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-3.17.5.tgz"
|
resolved "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-3.17.5.tgz"
|
||||||
|
|
||||||
|
fastdom@^1.0.11:
|
||||||
|
version "1.0.11"
|
||||||
|
resolved "https://registry.yarnpkg.com/fastdom/-/fastdom-1.0.11.tgz#f22984f9df6b9a6081e5ce2e49cfb5525daf198a"
|
||||||
|
integrity sha512-jl9MwXDFxhg354W4E3s1UMsLh3HWFuVMQiRUlXpHckcHRXQvUe76yzBf1Z7b+x5Ci4TUJ1KmynI9alGUXG95IQ==
|
||||||
|
dependencies:
|
||||||
|
strictdom "^1.0.1"
|
||||||
|
|
||||||
fastq@^1.6.0:
|
fastq@^1.6.0:
|
||||||
version "1.13.0"
|
version "1.13.0"
|
||||||
resolved "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz"
|
resolved "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz"
|
||||||
|
|
@ -14262,6 +14269,11 @@ strict-event-emitter@^0.2.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
events "^3.3.0"
|
events "^3.3.0"
|
||||||
|
|
||||||
|
strictdom@^1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/strictdom/-/strictdom-1.0.1.tgz#189de91649f73d44d59b8432efa68ef9d2659460"
|
||||||
|
integrity sha512-cEmp9QeXXRmjj/rVp9oyiqcvyocWab/HaoN4+bwFeZ7QzykJD6L3yD4v12K1x0tHpqRqVpJevN3gW7kyM39Bqg==
|
||||||
|
|
||||||
string-argv@^0.3.1:
|
string-argv@^0.3.1:
|
||||||
version "0.3.1"
|
version "0.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da"
|
resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user