Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,7 @@ coverage

storybook-static
.terraform
terraform
terraform

# Ignore vs files
.vs
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- `virtualization` features.
- `pagingNavigationComponents` property to customize the paging navigation components

## [5.12.0] - 2025-09-15
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"@dnd-kit/sortable": "^8.0.0",
"@neolution-ch/react-pattern-ui": "^5.3.0",
"@tanstack/react-table": "^8.12.0",
"@tanstack/react-virtual": "^3.13.12",
"react-loading-skeleton": "^3.3.1"
},
"devDependencies": {
Expand Down
515 changes: 251 additions & 264 deletions src/lib/ReactDataTable/ReactDataTable.tsx

Large diffs are not rendered by default.

17 changes: 6 additions & 11 deletions src/lib/ReactDataTable/ReactDataTableProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { CSSProperties } from "react";
import { FilterModel } from "../types/TableState";
import { DragAndDropOptions } from "./DragAndDropOptions";
import { PagingNavigationComponents } from "@neolution-ch/react-pattern-ui";
import { VirtualizationsOptions } from "./VirtualizationOptions";

/**
* The props for the ReactDataTable component
Expand Down Expand Up @@ -98,6 +98,11 @@ export interface ReactDataTableProps<TData, TFilter extends FilterModel> {
*/
dragAndDropOptions?: DragAndDropOptions;

/**
* to define virtualizer options
*/
virtualizerOptions?: VirtualizationsOptions;
Comment thread
manni497 marked this conversation as resolved.
Outdated

/**
* to override the default message in case no entries is found
*/
Expand All @@ -114,14 +119,4 @@ export interface ReactDataTableProps<TData, TFilter extends FilterModel> {
* @default true
*/
showClearSearchButton?: boolean;

/**
* A component to render as a sub-row for a specific row, this will be rendered additionally to the subrows.
*/
subRowComponent?: (row: Row<TData>) => React.ReactNode;

/**
* Custom navigation components for the paging controls
*/
pagingNavigationComponents?: PagingNavigationComponents;
}
95 changes: 95 additions & 0 deletions src/lib/ReactDataTable/TableBody.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { DraggableRow, InternalTableRow } from "./TableRows";
import { SortableContext, verticalListSortingStrategy } from "@dnd-kit/sortable";
import { Row, Table } from "@tanstack/react-table";
import { CSSProperties, useMemo } from "react";
import { FilterModel } from "../types/TableState";
import { ReactDataTableProps } from "./ReactDataTableProps";
import { Virtualizer } from "@tanstack/react-virtual";

interface TableBodyProps<TData, TFilter extends FilterModel = Record<string, never>>
extends Pick<ReactDataTableProps<TData, TFilter>, "enableRowClick" | "onRowClick"> {
enableDragAndDrop: boolean;
table: Table<TData>;
rowStyle?: (row: TData) => CSSProperties;
virtualizer?: Virtualizer<HTMLDivElement, Element>;
}

interface InternalRow<TData> {
row: Row<TData>;
rowStyle?: CSSProperties;
}

const TableBody = <TData, TFilter extends FilterModel = Record<string, never>>(props: TableBodyProps<TData, TFilter>) => {
const { enableDragAndDrop, table, rowStyle, enableRowClick, onRowClick, virtualizer } = props;

if (enableDragAndDrop && !table.options.getRowId) {
throw new Error("You must provide 'getRowId()' to data-table options in order to use the drag-and-drop feature.");
}

const {
options: { enableRowSelection, enableExpanding, fullRowSelectable },
} = table;

const { rows } = table.getRowModel();

const rowsToRender: InternalRow<TData>[] = useMemo(
() =>
virtualizer
? virtualizer.getVirtualItems().map((virtualRow, index) => ({
row: rows[virtualRow.index],
rowStyle: {
height: `${virtualRow.size}px`,
transform: `translateY(${virtualRow.start - index * virtualRow.size}px)`,
},
}))
: rows.map((row) => ({ row })),
[rows, virtualizer],
);

return enableDragAndDrop ? (
<SortableContext items={table.getRowModel().rows.map((row) => row.id)} strategy={verticalListSortingStrategy}>
{rowsToRender.map((x, index) => {
const { row } = x;
return (
<DraggableRow<TData, TFilter>
key={index}
Comment thread
manni497 marked this conversation as resolved.
row={row}
enableRowClick={enableRowClick as ReactDataTableProps<TData, TFilter>["enableRowClick"]}
onRowClick={onRowClick as ReactDataTableProps<TData, TFilter>["onRowClick"]}
enableRowSelection={enableRowSelection as boolean | ((row: Row<TData>) => boolean)}
enableExpanding={enableExpanding as boolean | ((row: Row<TData>) => boolean)}
rowStyle={{
...(x.rowStyle ?? {}),
...(rowStyle ? rowStyle(row.original) : {}),
}}
fullRowSelectable={fullRowSelectable}
/>
);
})}
</SortableContext>
) : (
<>
{rowsToRender.map((x, index) => {
const { row } = x;
return (
<InternalTableRow<TData, TFilter>
key={index}
Comment thread
manni497 marked this conversation as resolved.
row={row}
enableRowClick={enableRowClick as ReactDataTableProps<TData, TFilter>["enableRowClick"]}
onRowClick={onRowClick as ReactDataTableProps<TData, TFilter>["onRowClick"]}
enableRowSelection={enableRowSelection as boolean | ((row: Row<TData>) => boolean)}
enableExpanding={enableExpanding as boolean | ((row: Row<TData>) => boolean)}
rowStyle={{
...(x.rowStyle ?? {}),
...(rowStyle ? rowStyle(row.original) : {}),
}}
fullRowSelectable={fullRowSelectable}
hasPinnedColumns={table.getIsSomeColumnsPinned()}
/>
);
})}
</>
);
};

export { TableBody };
62 changes: 29 additions & 33 deletions src/lib/ReactDataTable/TableRows.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { FilterModel } from "../types/TableState";
import { ReactDataTableProps } from "./ReactDataTableProps";

interface TableRowProps<TData, TFilter extends FilterModel = Record<string, never>>
extends Pick<ReactDataTableProps<TData, TFilter>, "onRowClick" | "enableRowClick" | "subRowComponent"> {
extends Pick<ReactDataTableProps<TData, TFilter>, "onRowClick" | "enableRowClick"> {
row: Row<TData>;
enableRowSelection?: boolean | ((row: Row<TData>) => boolean);
fullRowSelectable?: boolean;
Expand All @@ -27,43 +27,39 @@ const InternalTableRow = <TData, TFilter extends FilterModel = Record<string, ne
onRowClick,
enableRowClick,
hasPinnedColumns,
subRowComponent,
} = props;
const isRowSelectionEnabled =
(typeof enableRowSelection === "function" ? enableRowSelection(row) : enableRowSelection) && fullRowSelectable;
const isRowClickable = typeof enableRowClick === "function" ? enableRowClick(row) : enableRowClick;
return (
<>
<tr
key={row.id}
ref={setNodeRef}
onClick={async () => {
if (isRowSelectionEnabled) {
row.toggleSelected();
} else if (isRowClickable && onRowClick) {
await onRowClick(row);
} else {
// Nothing to execute
}
}}
className={isRowSelectionEnabled || isRowClickable ? "cursor-pointer" : undefined}
style={rowStyle}
>
{row.getVisibleCells().map((cell) => (
<td
key={cell.id}
style={{
...cell.column.columnDef.meta?.cellStyle,
...(hasPinnedColumns ? getCommonPinningStyles(cell.column) : {}),
}}
className={cell.column.columnDef.meta?.cellClassName}
>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</td>
))}
</tr>
{subRowComponent && row.getIsExpanded() && subRowComponent(row)}
</>
<tr
key={row.id}
ref={setNodeRef}
onClick={async () => {
if (isRowSelectionEnabled) {
row.toggleSelected();
} else if (isRowClickable && onRowClick) {
await onRowClick(row);
} else {
// Nothing to execute
}
}}
className={isRowSelectionEnabled || isRowClickable ? "cursor-pointer" : undefined}
style={rowStyle}
>
{row.getVisibleCells().map((cell) => (
<td
key={cell.id}
style={{
...cell.column.columnDef.meta?.cellStyle,
...(hasPinnedColumns ? getCommonPinningStyles(cell.column) : {}),
}}
className={cell.column.columnDef.meta?.cellClassName}
>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</td>
))}
</tr>
);
};

Expand Down
3 changes: 3 additions & 0 deletions src/lib/ReactDataTable/VirtualizationOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { useVirtualizer } from "@tanstack/react-virtual";

export type VirtualizationsOptions = Omit<Parameters<typeof useVirtualizer<HTMLDivElement, Element>>[0], "getScrollElement">;
Comment thread
manni497 marked this conversation as resolved.
Outdated
12 changes: 12 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3029,11 +3029,23 @@
dependencies:
"@tanstack/table-core" "8.20.5"

"@tanstack/react-virtual@^3.13.12":
version "3.13.12"
resolved "https://registry.yarnpkg.com/@tanstack/react-virtual/-/react-virtual-3.13.12.tgz#d372dc2783739cc04ec1a728ca8203937687a819"
integrity sha512-Gd13QdxPSukP8ZrkbgS2RwoZseTTbQPLnQEn7HY/rqtM+8Zt95f7xKC7N0EsKs7aoz0WzZ+fditZux+F8EzYxA==
dependencies:
"@tanstack/virtual-core" "3.13.12"

"@tanstack/table-core@8.20.5":
version "8.20.5"
resolved "https://registry.yarnpkg.com/@tanstack/table-core/-/table-core-8.20.5.tgz#3974f0b090bed11243d4107283824167a395cf1d"
integrity sha512-P9dF7XbibHph2PFRz8gfBKEXEY/HJPOhym8CHmjF8y3q5mWpKx9xtZapXQUWCgkqvsK0R46Azuz+VaxD4Xl+Tg==

"@tanstack/virtual-core@3.13.12":
version "3.13.12"
resolved "https://registry.yarnpkg.com/@tanstack/virtual-core/-/virtual-core-3.13.12.tgz#1dff176df9cc8f93c78c5e46bcea11079b397578"
integrity sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA==

"@testing-library/dom@^8.0.0", "@testing-library/dom@^8.13.0", "@testing-library/dom@^8.3.0":
version "8.20.0"
resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.20.0.tgz#914aa862cef0f5e89b98cc48e3445c4c921010f6"
Expand Down
Loading