From da0e5e7b8c798a139ec18e2e59d49459226308c6 Mon Sep 17 00:00:00 2001 From: hamid zarghami Date: Mon, 28 Apr 2025 16:55:43 +0330 Subject: [PATCH] table refactor --- src/components/Table.tsx | 59 +++++++++++++++--------------- src/components/types/TableTypes.ts | 30 +++++++++++++++ src/pages/received/List.tsx | 4 +- 3 files changed, 63 insertions(+), 30 deletions(-) create mode 100644 src/components/types/TableTypes.ts diff --git a/src/components/Table.tsx b/src/components/Table.tsx index b26fdc0..98f3451 100644 --- a/src/components/Table.tsx +++ b/src/components/Table.tsx @@ -1,34 +1,7 @@ -import React, { Fragment, ReactNode } from 'react'; +import React, { Fragment, useState } from 'react'; import DefaultTableSkeleton from './DefaultTableSkeleton'; import Td from './Td'; - -export type RowDataType = Record & { id: string | number }; - -export interface ColumnType { - title: string; - key: string; - render?: (item: T) => React.ReactNode; - width?: string | number; - align?: 'left' | 'center' | 'right'; - sortable?: boolean; - className?: string; -} - -export interface TableProps { - columns: ColumnType[]; - 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'; -} +import { TableProps, RowDataType, ColumnType } from './types/TableTypes'; const Table = ({ columns, @@ -43,8 +16,11 @@ const Table = ({ showHeader = true, actions = null, actionsPosition = 'top', + selectable = false, }: TableProps): React.ReactElement => { + const [selectedRows, setSelectedRows] = useState([]); + const getRowClassName = (item: T, index: number): string => { if (typeof rowClassName === 'function') { return rowClassName(item, index); @@ -57,6 +33,16 @@ const Table = ({ 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 = () => { if (isLoading) { return ( @@ -82,6 +68,11 @@ const Table = ({ className={`tr border-b hover:bg-gray-50 ${onRowClick ? 'cursor-pointer' : ''} ${getRowClassName(item, rowIndex)}`} onClick={() => onRowClick && onRowClick(item.id, item)} > + {selectable && ( + + handleRowClick(item)} /> + + )} {columns.map((col) => ( {col.render ? col.render(item) : item[col.key] as React.ReactNode} @@ -97,6 +88,16 @@ const Table = ({ return ( + {selectable && ( + { + e.stopPropagation(); + }} + > + { }} /> + + )} {columns.map((col) => ( & { id: string | number }; + +export interface ColumnType { + title: string; + key: string; + render?: (item: T) => React.ReactNode; + width?: string | number; + align?: "left" | "center" | "right"; + sortable?: boolean; + className?: string; +} + +export interface TableProps { + columns: ColumnType[]; + 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; +} diff --git a/src/pages/received/List.tsx b/src/pages/received/List.tsx index d6be73a..8d3afdc 100644 --- a/src/pages/received/List.tsx +++ b/src/pages/received/List.tsx @@ -1,7 +1,8 @@ import Filters, { FilterValues } from '../../components/Filters'; import { FC, useState } from 'react' 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'; interface Message extends Record { @@ -86,6 +87,7 @@ const List: FC = () => {
هیچ پیامی یافت نشد
} + selectable={true} />