diff --git a/.changeset/yummy-ends-knock.md b/.changeset/yummy-ends-knock.md
new file mode 100644
index 0000000..b677dc2
--- /dev/null
+++ b/.changeset/yummy-ends-knock.md
@@ -0,0 +1,5 @@
+---
+"@zenml-io/react-component-library": minor
+---
+
+port full functionality of DataTable component from pro
diff --git a/src/components/Table/DataTable.stories.tsx b/src/components/Table/DataTable.stories.tsx
index 399cbb0..a9eed33 100644
--- a/src/components/Table/DataTable.stories.tsx
+++ b/src/components/Table/DataTable.stories.tsx
@@ -1,8 +1,9 @@
import { Meta } from "@storybook/react";
import { StoryObj } from "@storybook/react";
-import React from "react";
-import { DataTable } from "./index";
-import { ColumnDef } from "@tanstack/react-table";
+import React, { useState } from "react";
+import { DataTable, injectSortingArrowIcons } from "./index";
+import { ColumnDef, RowSelectionState, SortingState } from "@tanstack/react-table";
+import { Checkbox } from "../Checkbox";
type DummyData = {
id: number;
@@ -80,3 +81,95 @@ export const DefaultVariant: Story = {
data: data
}
};
+
+export const CustomizedVariant: Story = {
+ name: "Customized",
+ render: () => ,
+ args: {
+ // provide in component below
+ columns: [],
+ data: []
+ }
+};
+
+injectSortingArrowIcons({
+ ArrowUp: () =>
↑
,
+ ArrowDown: () => ↓
+});
+
+const CustomizedDataTable = () => {
+ const [rowSelection, setRowSelection] = useState({});
+ const [sorting, setSorting] = useState([
+ {
+ id: "name",
+ desc: true
+ },
+ {
+ id: "age",
+ desc: false
+ }
+ ]);
+
+ return (
+
+
x.id.toString()}
+ rowSelection={rowSelection}
+ onRowSelectionChange={setRowSelection}
+ sorting={sorting}
+ onSortingChange={setSorting}
+ />
+
+
+
Selected rows:
+
+ {Object.keys(rowSelection).map((key) => (
+ {key}
+ ))}
+
+
+
+ );
+};
+
+const colsCustomized: ColumnDef[] = [
+ {
+ id: "select",
+ accessorKey: "select",
+ header: ({ table }) => (
+
+ table.toggleAllRowsSelected(state === "indeterminate" ? true : state)
+ }
+ />
+ ),
+ cell: ({ row }) => (
+
+ )
+ },
+ {
+ id: "id",
+ header: "ID",
+ accessorKey: "id"
+ },
+ {
+ id: "name",
+ header: "Name",
+ accessorKey: "name",
+ enableSorting: true
+ },
+ {
+ id: "age",
+ header: "Age",
+ accessorKey: "age",
+ enableSorting: true
+ }
+];
diff --git a/src/components/Table/DataTable.tsx b/src/components/Table/DataTable.tsx
index a2fd6d0..37cfd5e 100644
--- a/src/components/Table/DataTable.tsx
+++ b/src/components/Table/DataTable.tsx
@@ -1,23 +1,77 @@
"use client";
-import { ColumnDef, flexRender, getCoreRowModel, useReactTable } from "@tanstack/react-table";
-import React, { useState } from "react";
+import React from "react";
+
+import {
+ ColumnDef,
+ CoreOptions,
+ ExpandedState,
+ flexRender,
+ getCoreRowModel,
+ getExpandedRowModel,
+ OnChangeFn,
+ RowSelectionState,
+ SortingState,
+ useReactTable
+} from "@tanstack/react-table";
+
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "./Table";
+import { SortableHeader } from "./Sorting";
+import { cn } from "../..";
interface DataTableProps {
columns: ColumnDef[];
data: TData[];
+ filters?: Record;
+ resetFilters?: () => void;
+ getRowId?: CoreOptions["getRowId"];
+ rowSelection?: RowSelectionState;
+ onRowSelectionChange?: OnChangeFn;
+ sorting?: SortingState;
+ onSortingChange?: OnChangeFn;
+ expanded?: ExpandedState;
+ onExpandedChange?: OnChangeFn;
+ getSubRows?: (row: TData) => TData[];
}
-export function DataTable({ columns, data }: DataTableProps) {
- const [rowSelection, setRowSelection] = useState({});
+export function DataTable({
+ columns,
+ data,
+ filters,
+ resetFilters,
+ sorting,
+ onSortingChange,
+ expanded,
+ onExpandedChange,
+ getSubRows,
+ getRowId,
+ rowSelection = {},
+ onRowSelectionChange
+}: DataTableProps) {
const table = useReactTable({
data,
- columns,
- onRowSelectionChange: setRowSelection,
+ columns: columns.map((col) => {
+ // if col.enableSorting is not defined, set it to false
+ if (col.enableSorting === undefined) {
+ col.enableSorting = false;
+ }
+ return col;
+ }),
+ manualSorting: true,
+ onRowSelectionChange,
+ onSortingChange,
+ enableSortingRemoval: false,
+ enableMultiSort: false,
+ enableRowSelection: true,
+ onExpandedChange,
+ getRowId,
+ getSubRows,
getCoreRowModel: getCoreRowModel(),
+ getExpandedRowModel: getExpandedRowModel(),
state: {
- rowSelection
+ rowSelection,
+ sorting,
+ expanded
}
});
@@ -28,16 +82,29 @@ export function DataTable({ columns, data }: DataTableProps (
{headerGroup.headers.map((header) => {
+ const canSort = header.column.getCanSort();
return (
- {header.isPlaceholder
- ? null
- : flexRender(header.column.columnDef.header, header.getContext())}
+ {canSort ? (
+
+ {header.isPlaceholder
+ ? null
+ : flexRender(header.column.columnDef.header, header.getContext())}
+
+ ) : header.isPlaceholder ? null : (
+ flexRender(header.column.columnDef.header, header.getContext())
+ )}
);
})}
@@ -53,12 +120,40 @@ export function DataTable({ columns, data }: DataTableProps
{row.getVisibleCells().map((cell) => (
-
+
{flexRender(cell.column.columnDef.cell, cell.getContext())}
))}
))
+ ) : filters && Object.keys(filters).length ? (
+
+
+
+ No items match your current selection.
+
+
+ Refine your filters and try again.
+
+
+
+
) : (
() {
+ type CtxValue = {
+ rowSelection: RowSelectionState;
+ setRowSelection: Dispatch>;
+
+ selectedRowIDs: string[];
+ selectedRowCount: number;
+ };
+
+ const Context = createContext(null);
+
+ function useContext() {
+ const ctx = React.useContext(Context);
+ if (!ctx) throw new Error("DataTableConsumerContext must be used within its Provider");
+ return ctx;
+ }
+
+ function ContextProvider({ children }: CtxProviderProps) {
+ const [rowSelection, setRowSelection] = useState({});
+
+ const selectedRowIDs = getSelectedRowIDs(rowSelection);
+ const selectedRowCount = selectedRowIDs.length;
+
+ return (
+
+ {children}
+
+ );
+ }
+
+ return {
+ Context,
+ ContextProvider,
+ useContext
+ };
+}
+
+export function countSelectedRows(rowSelection: RowSelectionState): number {
+ return Object.values(rowSelection).reduce((acc, curr) => acc + Number(curr), 0);
+}
+
+export function getSelectedRowIDs(rowSelection: RowSelectionState): string[] {
+ return Object.entries(rowSelection)
+ .filter(([, selected]) => selected)
+ .map(([id]) => id);
+}
diff --git a/src/components/Table/Icons.tsx b/src/components/Table/Icons.tsx
new file mode 100644
index 0000000..3645734
--- /dev/null
+++ b/src/components/Table/Icons.tsx
@@ -0,0 +1,16 @@
+import React from "react";
+
+export type SortingArrowIcon = React.FC<{
+ className?: string;
+}>;
+
+export let ArrowUp: SortingArrowIcon = () => <>>;
+export let ArrowDown: SortingArrowIcon = () => <>>;
+
+export function injectSortingArrowIcons(icons: {
+ ArrowUp: SortingArrowIcon;
+ ArrowDown: SortingArrowIcon;
+}) {
+ ArrowUp = icons.ArrowUp;
+ ArrowDown = icons.ArrowDown;
+}
diff --git a/src/components/Table/Sorting.tsx b/src/components/Table/Sorting.tsx
new file mode 100644
index 0000000..a6212bb
--- /dev/null
+++ b/src/components/Table/Sorting.tsx
@@ -0,0 +1,37 @@
+import React from "react";
+import { Header, RowData, SortDirection } from "@tanstack/react-table";
+import { PropsWithChildren } from "react";
+import { ArrowUp, ArrowDown } from "./Icons";
+
+type Props = {
+ direction: SortDirection | false;
+};
+
+function SortingArrow({ direction }: Props) {
+ if (direction === false) return null;
+
+ const Comp = direction === "asc" ? ArrowUp : ArrowDown;
+
+ return ;
+}
+
+interface HeaderProps {
+ header: Header;
+}
+
+export function SortableHeader({
+ header,
+ children
+}: PropsWithChildren>) {
+ return (
+
+ );
+}
diff --git a/src/components/Table/index.tsx b/src/components/Table/index.tsx
index 3d55021..3941524 100644
--- a/src/components/Table/index.tsx
+++ b/src/components/Table/index.tsx
@@ -1,2 +1,5 @@
export * from "./Table";
export * from "./DataTable";
+export * from "./DataTableConsumerContext";
+export * from "./Sorting";
+export * from "./Icons";
diff --git a/src/components/client.ts b/src/components/client.ts
index de745b0..b72890e 100644
--- a/src/components/client.ts
+++ b/src/components/client.ts
@@ -2,7 +2,7 @@
export * from "./Sidebar";
export * from "./Avatar";
export * from "./Dropdown";
-export * from "./Table/DataTable";
+export * from "./Table";
export * from "./Sheet";
export * from "./Tabs";
export * from "./Collapsible";
diff --git a/src/table.d.ts b/src/table.d.ts
new file mode 100644
index 0000000..06967fa
--- /dev/null
+++ b/src/table.d.ts
@@ -0,0 +1,8 @@
+import "@tanstack/react-table"; //or vue, svelte, solid, qwik, etc.
+
+declare module "@tanstack/react-table" {
+ interface ColumnMeta {
+ width?: string;
+ className?: string;
+ }
+}