table refactor

This commit is contained in:
hamid zarghami
2025-04-28 16:55:43 +03:30
parent d11837fd3f
commit da0e5e7b8c
3 changed files with 63 additions and 30 deletions
+30 -29
View File
@@ -1,34 +1,7 @@
import React, { Fragment, ReactNode } from 'react'; import React, { Fragment, useState } from 'react';
import DefaultTableSkeleton from './DefaultTableSkeleton'; import DefaultTableSkeleton from './DefaultTableSkeleton';
import Td from './Td'; import Td from './Td';
import { TableProps, RowDataType, ColumnType } from './types/TableTypes';
export type RowDataType = Record<string, unknown> & { id: string | number };
export interface ColumnType<T extends RowDataType = RowDataType> {
title: string;
key: string;
render?: (item: T) => React.ReactNode;
width?: string | number;
align?: 'left' | 'center' | 'right';
sortable?: boolean;
className?: string;
}
export interface TableProps<T extends RowDataType = RowDataType> {
columns: ColumnType<T>[];
data: T[];
isLoading?: boolean;
onRowClick?: (id: T['id'], item: T) => void;
className?: string;
rowClassName?: string | ((item: T, index: number) => string);
noDataMessage?: React.ReactNode;
headerClassName?: string;
emptyRowsCount?: number;
showHeader?: boolean;
actions?: ReactNode;
actionsClassName?: string;
actionsPosition?: 'top' | 'header-replace' | 'above-header';
}
const Table = <T extends RowDataType>({ const Table = <T extends RowDataType>({
columns, columns,
@@ -43,8 +16,11 @@ const Table = <T extends RowDataType>({
showHeader = true, showHeader = true,
actions = null, actions = null,
actionsPosition = 'top', actionsPosition = 'top',
selectable = false,
}: TableProps<T>): React.ReactElement => { }: TableProps<T>): React.ReactElement => {
const [selectedRows, setSelectedRows] = useState<T[]>([]);
const getRowClassName = (item: T, index: number): string => { const getRowClassName = (item: T, index: number): string => {
if (typeof rowClassName === 'function') { if (typeof rowClassName === 'function') {
return rowClassName(item, index); return rowClassName(item, index);
@@ -57,6 +33,16 @@ const Table = <T extends RowDataType>({
return `p-3 ${alignClass} ${column.className || ''}`.trim(); return `p-3 ${alignClass} ${column.className || ''}`.trim();
}; };
const handleRowClick = (item: T) => {
if (selectable) {
if (selectedRows.includes(item)) {
setSelectedRows(selectedRows.filter((row) => row !== item));
} else {
setSelectedRows([...selectedRows, item]);
}
}
};
const renderTableBody = () => { const renderTableBody = () => {
if (isLoading) { if (isLoading) {
return ( return (
@@ -82,6 +68,11 @@ const Table = <T extends RowDataType>({
className={`tr border-b hover:bg-gray-50 ${onRowClick ? 'cursor-pointer' : ''} ${getRowClassName(item, rowIndex)}`} className={`tr border-b hover:bg-gray-50 ${onRowClick ? 'cursor-pointer' : ''} ${getRowClassName(item, rowIndex)}`}
onClick={() => onRowClick && onRowClick(item.id, item)} onClick={() => onRowClick && onRowClick(item.id, item)}
> >
{selectable && (
<td className="p-3 w-10 relative z-[5]">
<input type="checkbox" checked={selectedRows.includes(item)} onChange={() => handleRowClick(item)} />
</td>
)}
{columns.map((col) => ( {columns.map((col) => (
<td key={`${item.id}-${col.key}`} className={getCellClassName(col)} style={{ width: col.width }}> <td key={`${item.id}-${col.key}`} className={getCellClassName(col)} style={{ width: col.width }}>
{col.render ? col.render(item) : item[col.key] as React.ReactNode} {col.render ? col.render(item) : item[col.key] as React.ReactNode}
@@ -97,6 +88,16 @@ const Table = <T extends RowDataType>({
return ( return (
<thead className={`thead ${headerClassName}`}> <thead className={`thead ${headerClassName}`}>
<tr> <tr>
{selectable && (
<th
className="p-3 w-10 relative z-[5]"
onClick={(e) => {
e.stopPropagation();
}}
>
<input type="checkbox" checked={false} onChange={() => { }} />
</th>
)}
{columns.map((col) => ( {columns.map((col) => (
<th <th
key={col.key} key={col.key}
+30
View File
@@ -0,0 +1,30 @@
import { ReactNode } from "react";
export type RowDataType = Record<string, unknown> & { id: string | number };
export interface ColumnType<T extends RowDataType = RowDataType> {
title: string;
key: string;
render?: (item: T) => React.ReactNode;
width?: string | number;
align?: "left" | "center" | "right";
sortable?: boolean;
className?: string;
}
export interface TableProps<T extends RowDataType = RowDataType> {
columns: ColumnType<T>[];
data: T[];
isLoading?: boolean;
onRowClick?: (id: T["id"], item: T) => void;
className?: string;
rowClassName?: string | ((item: T, index: number) => string);
noDataMessage?: React.ReactNode;
headerClassName?: string;
emptyRowsCount?: number;
showHeader?: boolean;
actions?: ReactNode;
actionsClassName?: string;
actionsPosition?: "top" | "header-replace" | "above-header";
selectable?: boolean;
}
+3 -1
View File
@@ -1,7 +1,8 @@
import Filters, { FilterValues } from '../../components/Filters'; import Filters, { FilterValues } from '../../components/Filters';
import { FC, useState } from 'react' import { FC, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import Table, { ColumnType } from '../../components/Table'; import Table from '../../components/Table';
import { ColumnType } from '../../components/types/TableTypes';
import { InfoCircle, More, Refresh2 } from 'iconsax-react'; import { InfoCircle, More, Refresh2 } from 'iconsax-react';
interface Message extends Record<string, unknown> { interface Message extends Record<string, unknown> {
@@ -86,6 +87,7 @@ const List: FC = () => {
<div>هیچ پیامی یافت نشد</div> <div>هیچ پیامی یافت نشد</div>
</div> </div>
} }
selectable={true}
/> />
</div> </div>