feat-update
This commit is contained in:
@@ -10,7 +10,7 @@ import {
|
|||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
|
|
||||||
/* ================= TYPES ================= */
|
/* ================= TYPES ================= */
|
||||||
interface ColumnType<T> {
|
export interface ColumnType<T> {
|
||||||
title: string | React.ReactNode;
|
title: string | React.ReactNode;
|
||||||
render: (row: T, index: number, path?: number[]) => React.ReactNode;
|
render: (row: T, index: number, path?: number[]) => React.ReactNode;
|
||||||
className?: string;
|
className?: string;
|
||||||
@@ -23,7 +23,8 @@ interface ColumnType<T> {
|
|||||||
|
|
||||||
interface PropsTable<T> {
|
interface PropsTable<T> {
|
||||||
data: T[];
|
data: T[];
|
||||||
column: ColumnType<T>[];
|
// Cập nhật kiểu dữ liệu ở đây để chấp nhận thêm false | null | undefined
|
||||||
|
column: (ColumnType<T> | false | null | undefined)[];
|
||||||
fixedHeader?: boolean;
|
fixedHeader?: boolean;
|
||||||
activeHeader?: boolean;
|
activeHeader?: boolean;
|
||||||
handleCheckedAll?: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
handleCheckedAll?: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||||
@@ -38,7 +39,7 @@ interface PropsTable<T> {
|
|||||||
/* ================= COMPONENT ================= */
|
/* ================= COMPONENT ================= */
|
||||||
export default function Table<T>({
|
export default function Table<T>({
|
||||||
data,
|
data,
|
||||||
column,
|
column: rawColumn, // Đổi tên prop column thành rawColumn
|
||||||
fixedHeader,
|
fixedHeader,
|
||||||
activeHeader,
|
activeHeader,
|
||||||
handleCheckedAll,
|
handleCheckedAll,
|
||||||
@@ -52,6 +53,12 @@ export default function Table<T>({
|
|||||||
const tableRef = useRef<HTMLDivElement>(null);
|
const tableRef = useRef<HTMLDivElement>(null);
|
||||||
const thRefs = useRef<(HTMLTableCellElement | null)[]>([]);
|
const thRefs = useRef<(HTMLTableCellElement | null)[]>([]);
|
||||||
|
|
||||||
|
// Lọc bỏ các giá trị falsy (false, null, undefined) để thu được mảng ColumnType<T>[] hợp lệ
|
||||||
|
const column = useMemo(
|
||||||
|
() => rawColumn.filter((col): col is ColumnType<T> => Boolean(col)),
|
||||||
|
[rawColumn],
|
||||||
|
);
|
||||||
|
|
||||||
const [expandedRows, setExpandedRows] = useState<React.Key[]>([]);
|
const [expandedRows, setExpandedRows] = useState<React.Key[]>([]);
|
||||||
const [sortConfig, setSortConfig] = useState<{
|
const [sortConfig, setSortConfig] = useState<{
|
||||||
key: string;
|
key: string;
|
||||||
@@ -172,7 +179,7 @@ export default function Table<T>({
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
/* ================= MOBILE CARDS (KHUÔN BẢNG GIỐNG ẢNH MẪU) ================= */
|
/* ================= MOBILE CARDS ================= */
|
||||||
const renderMobileCards = (rows: T[], path: number[] = []): React.ReactNode =>
|
const renderMobileCards = (rows: T[], path: number[] = []): React.ReactNode =>
|
||||||
rows.map((row, i) => {
|
rows.map((row, i) => {
|
||||||
const currentPath = [...path, i];
|
const currentPath = [...path, i];
|
||||||
@@ -182,15 +189,12 @@ export default function Table<T>({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment key={String(keyPath)}>
|
<Fragment key={String(keyPath)}>
|
||||||
{/* Khung Card cho mỗi bệnh nhân / dòng */}
|
|
||||||
<div className="border border-gray-500 bg-white overflow-hidden text-sm rounded-none mb-3">
|
<div className="border border-gray-500 bg-white overflow-hidden text-sm rounded-none mb-3">
|
||||||
{column.map((col, idx) => (
|
{column.map((col, idx) => (
|
||||||
<div
|
<div
|
||||||
key={idx}
|
key={idx}
|
||||||
/* Chia 40% - 60% giúp cột chứa button Action rộng rãi hơn */
|
|
||||||
className="grid grid-cols-[2fr_3fr] border-b border-gray-500 last:border-b-0 divide-x divide-gray-500 min-h-[44px]"
|
className="grid grid-cols-[2fr_3fr] border-b border-gray-500 last:border-b-0 divide-x divide-gray-500 min-h-[44px]"
|
||||||
>
|
>
|
||||||
{/* Tiêu đề bên trái (TÊN BỆNH NHÂN, MÃ PHIÊN KHÁM,...) */}
|
|
||||||
<div className="p-2 font-bold flex items-center justify-center text-center bg-white uppercase text-xs tracking-tight text-gray-900 leading-tight min-w-0 break-words">
|
<div className="p-2 font-bold flex items-center justify-center text-center bg-white uppercase text-xs tracking-tight text-gray-900 leading-tight min-w-0 break-words">
|
||||||
{col.checkBox && (
|
{col.checkBox && (
|
||||||
<input
|
<input
|
||||||
@@ -203,14 +207,12 @@ export default function Table<T>({
|
|||||||
{col.title}
|
{col.title}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Giá trị / Nút bấm bên phải */}
|
|
||||||
<div className="p-2 flex flex-wrap items-center justify-center text-center gap-1.5 min-w-0 break-words overflow-hidden text-gray-800 font-normal">
|
<div className="p-2 flex flex-wrap items-center justify-center text-center gap-1.5 min-w-0 break-words overflow-hidden text-gray-800 font-normal">
|
||||||
{col.render(row, i, currentPath)}
|
{col.render(row, i, currentPath)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{/* Xem thêm nếu có hàng con */}
|
|
||||||
{getChildren && !!children?.length && (
|
{getChildren && !!children?.length && (
|
||||||
<div className="p-2 bg-gray-50 border-t border-gray-500 text-center">
|
<div className="p-2 bg-gray-50 border-t border-gray-500 text-center">
|
||||||
<button
|
<button
|
||||||
@@ -230,7 +232,6 @@ export default function Table<T>({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Render hàng con nếu có */}
|
|
||||||
{expanded && children && (
|
{expanded && children && (
|
||||||
<div className="pl-2 border-l-2 border-blue-500 space-y-1">
|
<div className="pl-2 border-l-2 border-blue-500 space-y-1">
|
||||||
{renderMobileCards(children, currentPath)}
|
{renderMobileCards(children, currentPath)}
|
||||||
|
|||||||
Reference in New Issue
Block a user