Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
53 changes: 19 additions & 34 deletions apps/storybook/src/stories/useTable.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,27 +84,24 @@ const columns = [
];

export const Default = () => {
const table = useTable({});
console.log(table);
const table = useTable({ columns, data: invoices });

return (
<Table>
<TableCaption>A list of your recent invoices.</TableCaption>
<TableHeader>
<TableRow>
<TableHead className="w-[100px]">Invoice</TableHead>
<TableHead>Status</TableHead>
<TableHead>Method</TableHead>
<TableHead className="text-right">Amount</TableHead>
{table.getAllColumns().map((column) => (
<TableHead key={column.id}>{column.id}</TableHead>
))}
</TableRow>
</TableHeader>
<TableBody>
{invoices.map((invoice) => (
<TableRow key={invoice.invoice}>
<TableCell className="font-medium">{invoice.invoice}</TableCell>
<TableCell>{invoice.paymentStatus}</TableCell>
<TableCell>{invoice.paymentMethod}</TableCell>
<TableCell className="text-right">{invoice.totalAmount}</TableCell>
{table.getRows().map((row) => (
<TableRow key={row.id}>
{row.getAllCells().map((cell) => (
<TableCell className="font-medium">{cell.getValue()}</TableCell>
))}
</TableRow>
))}
</TableBody>
Expand All @@ -119,14 +116,11 @@ export const Default = () => {
};

export const WithPagination = () => {
const table = useTable({});
const table = useTable({ columns, data: invoices });
const {
pagination: { pageIndex },
} = table.getState();

console.log(table);
console.log(pageIndex);

return (
<>
<Table>
Expand Down Expand Up @@ -199,7 +193,7 @@ export const WithPagination = () => {
};

export const ColumnVisibility = () => {
const table = useTable({ columns });
const table = useTable({ columns, data: invoices });

return (
<>
Expand All @@ -219,26 +213,17 @@ export const ColumnVisibility = () => {
<TableCaption>A list of your recent invoices.</TableCaption>
<TableHeader>
<TableRow>
{/*todo: work on header model*/}
{table.getAllColumns().map((column) => {
return column.isVisible() ? (
<TableHead key={column.id}>{column.id}</TableHead>
) : null;
})}
{table.getAllVisibleColumns().map((column) => (
<TableHead key={column.id}>{column.id}</TableHead>
))}
</TableRow>
</TableHeader>
<TableBody>
{invoices.map((invoice) => (
<TableRow key={invoice.invoice}>
{table.getAllColumns().map((column) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore / todo: work on row model
const value = invoice?.[column.id];
const isVisible = column.isVisible();
return isVisible ? (
<TableCell key={column.id}>{value}</TableCell>
) : null;
})}
{table.getRows().map((row) => (
<TableRow key={row.id}>
{row.getAllVisibleCells().map((cell) => (
<TableCell key={cell.id}>{cell.getValue()}</TableCell>
))}
</TableRow>
))}
</TableBody>
Expand Down
26 changes: 26 additions & 0 deletions packages/table/src/core/cell.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Column, Row, Table, Cell } from "../types/table";

export type BaseCell<TData extends any> = {
column: Column<TData>;
getValue: () => any;
id: string;
row: Row<TData>;
};
export function initCell<TData extends any>(
table: Table<TData>,
column: Column<TData>,
row: Row<TData>
) {
let cell: Cell<TData> = {
id: `${row.id}_${column.id}`,
row,
column,
getValue: () => (row.data as any)[column.id],
};

for (const feature of table.features) {
feature.initCell?.(table, cell, column, row);
}

return cell;
}
4 changes: 2 additions & 2 deletions packages/table/src/core/column.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { ColumnDef, Column, Table } from "../types/table";

export function initColumn<TData extends any>(
table: Table,
columnDef: Column<TData>
table: Table<TData>,
columnDef: ColumnDef
) {
let column: ColumnDef = {
id: columnDef.id,
Expand Down
29 changes: 29 additions & 0 deletions packages/table/src/core/row.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Cell, Row, Table } from "../types/table";
import { initCell } from "./cell";

export type BaseRow<TData extends any> = {
getAllCells: () => Cell<TData>[];
id: string;
data: TData;
};

export function initRow<TData extends any>(
table: Table<TData>,
id: string,
data: TData
) {
let row: BaseRow<TData> = {
id,
data,
getAllCells: () =>
table
.getAllColumns()
.map((column) => initCell(table, column, row as Row<TData>)),
};

for (const feature of table.features) {
feature.initRow?.(table, row as Row<TData>);
}

return row;
}
15 changes: 11 additions & 4 deletions packages/table/src/core/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,22 @@ import { functionalUpdate } from "../utils/functional-update";
import { pagination } from "../features/pagination";
import { initColumn } from "./column";
import { columnVisibility } from "../features/column-visibility";
import { initRow } from "./row";

const features = [pagination, columnVisibility];
export function initTable(options: TableOptions): Table {
export function initTable<TData extends any>(
options: TableOptions<TData>
): Table<TData> {
let initialState = options?.initialState ?? {};
features.forEach((feature) => {
initialState = feature.getInitialState?.(initialState) ?? initialState;
});

let table = { features } as Table;
let table = { features } as Table<TData>;

const defaultOptions = features.reduce(
(obj, feature) => Object.assign(obj, feature.getDefaultOptions?.(table)),
{} as TableResolvedOptions
{} as TableResolvedOptions<TData>
);

let instance = {
Expand All @@ -33,7 +36,11 @@ export function initTable(options: TableOptions): Table {
},
getAllColumns: () =>
table.options.columns?.map((column) => initColumn(table, column)) ?? [],
} as Table;
getRows: () =>
table.options.data?.map((data, index) =>
initRow(table, index.toString(), data)
),
} as Table<TData>;

Object.assign(table, instance);

Expand Down
14 changes: 13 additions & 1 deletion packages/table/src/features/column-visibility.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { TableFeature } from "../types/feature";
import { Cell, Column } from "../types/table";

export type ColumnVisibilityState = Record<string, boolean>;

Expand All @@ -10,15 +11,20 @@ export type ColumnVisibilityOptions = {
onColumnVisibilityChange: (columnVisibility: ColumnVisibilityState) => void;
};

export type ColumnVisibilityInstance = {
export type ColumnVisibilityInstance<TData extends any> = {
setColumnVisibility: (columnVisibility: ColumnVisibilityState) => void;
getAllVisibleColumns: () => Column<TData>[];
};

export type ColumnVisibilityColumn = {
isVisible: () => boolean;
toggleVisibility: (value?: boolean) => void;
};

export type ColumnVisibilityRow<TData extends any> = {
getAllVisibleCells: () => Cell<TData>[];
};

export const columnVisibility: TableFeature = {
getDefaultOptions: (table) => ({
onColumnVisibilityChange: (columnVisibility) =>
Expand All @@ -31,6 +37,8 @@ export const columnVisibility: TableFeature = {
init: (table) => {
table.setColumnVisibility = (columnVisibility) =>
table.options.onColumnVisibilityChange?.(columnVisibility);
table.getAllVisibleColumns = () =>
table.getAllColumns().filter((column) => column.isVisible());
},
initColumn: (table, column) => {
column.isVisible = () =>
Expand All @@ -41,4 +49,8 @@ export const columnVisibility: TableFeature = {
[column.id]: value ?? !column.isVisible(),
});
},
initRow: (table, row) => {
row.getAllVisibleCells = () =>
row.getAllCells().filter((cell) => cell.column.isVisible());
},
};
2 changes: 2 additions & 0 deletions packages/table/src/features/pagination.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// pagination is currently manual only

import { TableFeature } from "../types/feature";

const DEFAULT_PAGE_SIZE = 10;
Expand Down
2 changes: 1 addition & 1 deletion packages/table/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as React from "react";
import { TableOptions } from "./types/table";
import { initTable } from "./core/table";

export function useTable(options: TableOptions) {
export function useTable<TData extends any>(options: TableOptions<TData>) {
const [table] = React.useState(() =>
initTable({
state: {},
Expand Down
26 changes: 21 additions & 5 deletions packages/table/src/types/feature.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,24 @@
import { Column, Table, TableResolvedOptions, TableState } from "./table";
import {
Cell,
Column,
Row,
Table,
TableResolvedOptions,
TableState,
} from "./table";

export type TableFeature = {
export type TableFeature<TData extends any = any> = {
getInitialState?: (state?: Partial<TableState>) => Partial<TableState>;
init: (table: Table) => void;
getDefaultOptions?: (table: Table) => Partial<TableResolvedOptions>;
initColumn?: (table: Table, column: Column<any>) => void;
init: (table: Table<TData>) => void;
getDefaultOptions?: (
table: Table<TData>
) => Partial<TableResolvedOptions<TData>>;
initColumn?: (table: Table<TData>, column: Column<any>) => void;
initRow?: (table: Table<TData>, row: Row<TData>) => void;
initCell?: (
table: Table<TData>,
cell: Cell<TData>,
column: Column<TData>,
row: Row<TData>
) => void;
};
29 changes: 21 additions & 8 deletions packages/table/src/types/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,40 +8,53 @@ import {
ColumnVisibilityColumn,
ColumnVisibilityInstance,
ColumnVisibilityOptions,
ColumnVisibilityRow,
ColumnVisibilityTableState,
} from "../features/column-visibility";
import { TableFeature } from "./feature";
import { BaseRow } from "../core/row";
import { BaseCell } from "../core/cell";

export type TableState = PaginationTableState & ColumnVisibilityTableState;

export type TableBaseOptions = {
export type TableBaseOptions<TData extends any> = {
columns: ColumnDef[];
state: Partial<TableState>;
onStateChange: React.Dispatch<React.SetStateAction<TableState>>;
initialState?: TableState;
data: TData[];
};

export type TableResolvedOptions = TableBaseOptions &
export type TableResolvedOptions<TData extends any> = TableBaseOptions<TData> &
PaginationOptions &
ColumnVisibilityOptions;
export type TableOptions = Partial<TableResolvedOptions>;

export type TableBaseInstance = {
export type TableOptions<TData extends any> = Partial<
TableResolvedOptions<TData>
>;

export type TableBaseInstance<TData extends any> = {
features: TableFeature[];
setOptions: React.Dispatch<React.SetStateAction<TableOptions>>;
options: TableOptions;
setOptions: React.Dispatch<React.SetStateAction<TableOptions<TData>>>;
options: TableOptions<TData>;
initialState: TableState;
getState: () => TableState;
setState: React.Dispatch<React.SetStateAction<TableState>>;
getAllColumns: () => Column<any>[];
getRows: () => Row<TData>[];
};

export type Table = TableBaseInstance &
export type Table<TData extends any> = TableBaseInstance<TData> &
PaginationInstance &
ColumnVisibilityInstance;
ColumnVisibilityInstance<TData>;

export type ColumnDef = {
id: string;
};

export type Row<TData extends any> = BaseRow<TData> &
ColumnVisibilityRow<TData>;

export type Cell<TData extends any> = BaseCell<TData>;

export type Column<TData extends any> = ColumnDef & ColumnVisibilityColumn;