Skip to content

Commit

Permalink
refactor ColumnPinningTable to use createColumnHelper for improved ty…
Browse files Browse the repository at this point in the history
…pe safety. Enhance TableGrid with stricter typing for columns and update fuzzy search implementation for better key handling.
  • Loading branch information
shakibdshy committed Dec 25, 2024
1 parent 7ca0b77 commit 2668553
Show file tree
Hide file tree
Showing 10 changed files with 268 additions and 101 deletions.
Binary file modified bun.lockb
Binary file not shown.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"dependencies": {
"clsx": "^2.1.1",
"fuse.js": "^7.0.0",
"jotai": "^2.11.0",
"next": "15.1.2",
"react": "^19.0.0",
"react-dom": "^19.0.0",
Expand Down
2 changes: 2 additions & 0 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import FuzzySearchFilter from "@/components/containers/fuzzy-search-filter";
import ColumnVisibilityTable from "@/components/containers/column-visibility-table";
import ColumnPinningTable from "@/components/containers/toggle-column-pinning-table";
import CustomizedTable from "@/components/containers/customized-table";
import ColumnResizingTable from "@/components/containers/column-resizing-table";

export default function Home() {
return (
<main className="min-h-screen p-4">
<ColumnResizingTable />
<div className="space-y-8">
<div className="border rounded-lg">
<CustomizedTable />
Expand Down
76 changes: 20 additions & 56 deletions src/components/containers/column-pinning-table.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
"use client";
import TableGrid from "@/components/ui/table-grid/table-grid";
import dummyData from "@/data/dummy.json";
import type { Column } from "@/components/ui/table-grid/table-grid";
import { useTableGrid } from "@/hooks/use-table-grid";
import { createColumnHelper } from "@/components/ui/table-grid/column-helper";

interface DataItem extends Record<string, unknown> {
interface DataItem {
id: number;
name: string;
age: number;
Expand All @@ -18,75 +18,39 @@ interface DataItem extends Record<string, unknown> {
phone: string;
}

const columns: Column<DataItem>[] = [
{
id: "id",
const columnHelper = createColumnHelper<DataItem>();

const columns = [
columnHelper.accessor("id", {
header: "ID",
accessorKey: "id",
sortable: true,
pinned: "left",
},
{
id: "name",
}),
columnHelper.accessor("name", {
header: "Name",
accessorKey: "name",
sortable: true,
},
{
id: "department",
header: "Department",
accessorKey: "department",
sortable: true,
},
{
id: "role",
header: "Role",
accessorKey: "role",
sortable: true,
},
{
id: "salary",
}),
columnHelper.accessor("salary", {
header: "Salary",
accessorKey: "salary",
sortable: true,
cell: ({ value }) => `$${(value as number).toLocaleString()}`,
},
{
id: "status",
}),
columnHelper.accessor("status", {
header: "Status",
accessorKey: "status",
sortable: true,
},
{
id: "location",
}),
columnHelper.accessor("location", {
header: "Location",
accessorKey: "location",
sortable: true,
},
{
id: "age",
header: "Age",
accessorKey: "age",
sortable: true,
},
{
id: "email",
header: "Email",
accessorKey: "email",
}),
columnHelper.accessor("joinDate", {
header: "Join Date",
sortable: true,
},
{
id: "phone",
}),
columnHelper.accessor("phone", {
header: "Phone",
accessorKey: "phone",
sortable: true,
},
{
id: "joinDate",
header: "Join Date",
accessorKey: "joinDate",
sortable: true,
},
}),
];

const ColumnPinningTable = () => {
Expand Down
89 changes: 89 additions & 0 deletions src/components/containers/column-resizing-table.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
"use client";
import { useMemo } from "react";
import TableGrid from "@/components/ui/table-grid/table-grid";
import dummyData from "@/data/dummy.json";
import { useTableGrid } from "@/hooks/use-table-grid";
import { createColumnHelper } from "@/components/ui/table-grid/column-helper";

interface DataItem {
id: number;
name: string;
age: number;
email: string;
department: string;
role: string;
salary: number;
status: string;
location: string;
joinDate: string;
phone: string;
[key: string]: unknown;
}

const columnHelper = createColumnHelper<DataItem>();

const ColumnResizingTable = () => {
const columns = useMemo(
() => [
columnHelper.accessor("id", {
header: "ID",
sortable: true,
}),
columnHelper.accessor("name", {
header: "Name",
sortable: true,
}),
columnHelper.accessor("email", {
header: "Email",
sortable: true,
}),
columnHelper.accessor("department", {
header: "Department",
sortable: true,
}),
columnHelper.accessor("role", {
header: "Role",
sortable: true,
}),
columnHelper.accessor("salary", {
header: "Salary",
sortable: true,
}),
],
[]
);

const { filteredData, handleSort, sortColumn, sortDirection } =
useTableGrid<DataItem>({
data: dummyData,
columns,
initialState: {
sortColumn: "name",
sortDirection: "asc",
},
onStateChange: (state) => {
console.log("Table state changed:", state);
},
});

return (
<div className="p-4">
<div className="flex justify-between items-center mb-4">
<h2 className="text-2xl font-bold">Column Resizing Table</h2>
</div>

<TableGrid<DataItem>
columns={columns}
data={filteredData}
gridTemplateColumns="1fr 1fr 1fr 1fr 1fr 1fr"
maxHeight="400px"
variant="classic"
onSort={handleSort}
sortColumn={sortColumn}
sortDirection={sortDirection}
/>
</div>
);
};

export default ColumnResizingTable;
52 changes: 52 additions & 0 deletions src/components/ui/table-grid/column-helper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import type { Column } from './types'

export type DisplayColumnDef<TData, TValue = unknown> = Omit<Column<TData>, 'id' | 'accessorKey'> & {
id?: keyof TData
cell?: (props: { value: TValue; row: TData }) => React.ReactNode
}

export type GroupColumnDef<TData, TValue = unknown> = DisplayColumnDef<TData, TValue> & {
columns?: Column<TData>[]
}

export interface ColumnHelper<TData> {
accessor: <TKey extends keyof TData>(
accessorKey: TKey,
columnDef?: DisplayColumnDef<TData, TData[TKey]>
) => Column<TData>

display: (columnDef: DisplayColumnDef<TData>) => Column<TData>

group: (columnDef: GroupColumnDef<TData>) => Column<TData>
}

export function createColumnHelper<TData>(): ColumnHelper<TData> {
return {
accessor: <TKey extends keyof TData>(
accessorKey: TKey,
columnDef: Partial<DisplayColumnDef<TData, TData[TKey]>> = {}
): Column<TData> => ({
id: accessorKey,
accessorKey,
header: columnDef.header ?? String(accessorKey),
sortable: columnDef.sortable ?? true,
className: columnDef.className,
width: columnDef.width,
group: columnDef.group,
pinned: columnDef.pinned,
cell: columnDef.cell,
}),

display: (columnDef) => ({
id: columnDef.id ?? String(Math.random()) as keyof TData,
accessorKey: '' as keyof TData,
...columnDef,
}),

group: (columnDef) => ({
id: columnDef.id ?? String(Math.random()) as keyof TData,
accessorKey: '' as keyof TData,
...columnDef,
}),
}
}
36 changes: 18 additions & 18 deletions src/components/ui/table-grid/table-grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,10 @@ function TableGridComponent<T extends Record<string, unknown>>(
// Fuzzy search setup
const fuse = useMemo(() => {
if (!enableFuzzySearch) return null
const searchKeys = (fuzzySearchKeys || columns.map(col => col.accessorKey))
.map(key => String(key)) as string[]
return new Fuse(data, {
keys: fuzzySearchKeys || columns.map(col => col.accessorKey as string),
keys: searchKeys,
threshold: fuzzySearchThreshold,
})
}, [data, enableFuzzySearch, fuzzySearchKeys, columns, fuzzySearchThreshold])
Expand Down Expand Up @@ -173,29 +175,26 @@ function TableGridComponent<T extends Record<string, unknown>>(
}

if (column.cell) {
const defaultUpdateData: UpdateDataFn<T> = () => undefined
const updateData = meta?.updateData || onRowChange || defaultUpdateData
const defaultUpdateData: UpdateDataFn<T> = () => undefined;
const updateData = meta?.updateData || onRowChange || defaultUpdateData;

return column.cell({
value: getRowValue(row, column.accessorKey),
row,
onChange: (value) => {
updateData(rowIndex, column.accessorKey, value)
updateData(rowIndex, column.accessorKey, value);
},
onDelete: () => onRowDelete?.(rowIndex),
row: {
original: row,
index: rowIndex,
},
table: {
table: meta && {
options: {
meta: {
updateData,
},
},
},
})
});
}
return String(getRowValue(row, column.accessorKey))
return String(getRowValue(row, column.accessorKey));
}

const renderEmptyState = () => {
Expand Down Expand Up @@ -306,7 +305,7 @@ function TableGridComponent<T extends Record<string, unknown>>(
{/* Left Pinned Columns */}
{columns.filter(col => col.pinned === 'left').map((column) => (
<div
key={`pin-left-${column.id}`}
key={`pin-left-${String(column.id)}`}
className={cn(
styles.headerCell(),
'sticky left-0 z-20 bg-gray-100 dark:bg-gray-700',
Expand All @@ -321,7 +320,7 @@ function TableGridComponent<T extends Record<string, unknown>>(
{/* Unpinned Columns */}
{columns.filter(col => !col.pinned).map((column) => (
<div
key={column.id}
key={String(column.id)}
className={cn(
styles.headerCell(),
column.className,
Expand All @@ -335,7 +334,7 @@ function TableGridComponent<T extends Record<string, unknown>>(
{/* Right Pinned Columns */}
{columns.filter(col => col.pinned === 'right').map((column) => (
<div
key={`pin-right-${column.id}`}
key={`pin-right-${String(column.id)}`}
className={cn(
styles.headerCell(),
'sticky right-0 z-20 bg-gray-100 dark:bg-gray-700',
Expand All @@ -362,7 +361,7 @@ function TableGridComponent<T extends Record<string, unknown>>(
{/* Left Pinned Cells */}
{columns.filter(col => col.pinned === 'left').map((column) => (
<div
key={`pin-left-${column.id}`}
key={`pin-left-${String(column.id)}`}
className={cn(
styles.cell(),
'sticky left-0 z-10 bg-white dark:bg-gray-800',
Expand All @@ -377,7 +376,7 @@ function TableGridComponent<T extends Record<string, unknown>>(
{/* Unpinned Cells */}
{columns.filter(col => !col.pinned).map((column) => (
<div
key={`cell-${column.id}`}
key={`cell-${String(column.id)}`}
className={cn(
styles.cell(),
column.className,
Expand All @@ -391,7 +390,7 @@ function TableGridComponent<T extends Record<string, unknown>>(
{/* Right Pinned Cells */}
{columns.filter(col => col.pinned === 'right').map((column) => (
<div
key={`pin-right-${column.id}`}
key={`pin-right-${String(column.id)}`}
className={cn(
styles.cell(),
'sticky right-0 z-10 bg-white dark:bg-gray-800',
Expand All @@ -415,7 +414,8 @@ function TableGridComponent<T extends Record<string, unknown>>(
)
}

const TableGrid = forwardRef(TableGridComponent) as unknown as (<T extends Record<string, unknown>>(
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-constraint, @typescript-eslint/no-explicit-any
const TableGrid = forwardRef(TableGridComponent) as unknown as (<T extends any>(
props: TableProps<T> & { ref?: React.ForwardedRef<HTMLDivElement> }
) => ReactNode) & { displayName?: string }

Expand Down
Loading

0 comments on commit 2668553

Please sign in to comment.