table refactor
This commit is contained in:
+30
-29
@@ -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}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user