Rc Table
React table component with useful functions.
Installation
- Install the rc-table package.
npm i rc-table
- Create a table component, components/table.tsx
import React from "react";
import Table from "rc-table";
import { Empty, cn } from "rizzui";
export type ExtractProps<T> = T extends React.ComponentType<infer P> ? P : T;
const tableStyles = {
table:
"[&_.rc-table-content]:overflow-x-auto [&_table]:w-full [&_.rc-table-row:hover]:bg-muted/60",
thead:
"[&_thead]:text-left [&_thead]:rtl:text-right [&_th.rc-table-cell]:uppercase [&_th.rc-table-cell]:text-xs [&_th.rc-table-cell]:font-medium [&_th.rc-table-cell]:tracking-wide",
tCell:
"[&_.rc-table-cell]:px-3 [&_th.rc-table-cell]:py-3 [&_td.rc-table-cell]:py-4",
variants: {
classic:
"[&_thead]:bg-muted/50 [&_.rc-table-container]:border-x [&_.rc-table-container]:border-muted [&_td.rc-table-cell]:border-b [&_td.rc-table-cell]:border-muted [&_thead]:border-y [&_thead]:border-muted",
modern:
"[&_thead]:bg-muted/50 [&_td.rc-table-cell]:border-b [&_td.rc-table-cell]:border-muted",
minimal: "[&_thead]:bg-muted/50",
elegant:
"[&_thead]:border-y [&_thead]:border-muted [&_td.rc-table-cell]:border-b [&_td.rc-table-cell]:border-muted",
},
striped: "[&_.rc-table-row:nth-child(2n)_.rc-table-cell]:bg-muted/40",
};
type RCTableProps = ExtractProps<typeof Table>;
export interface TableProps
extends Omit<RCTableProps, "className" | "emptyText"> {
emptyText?: React.ReactElement;
variant?: keyof typeof tableStyles.variants;
striped?: boolean;
className?: string;
}
export default function RcTable({
striped,
variant = "classic",
emptyText,
className,
...props
}: TableProps) {
return (
<Table
className={cn(
tableStyles.table,
tableStyles.thead,
tableStyles.tCell,
tableStyles.variants[variant],
striped && tableStyles.striped,
className
)}
emptyText={emptyText ?? <Empty />}
{...props}
/>
);
}
RcTable.displayName = "Table";
- You need to create one more component for the table header cell
import React from "react";
import { cn } from "rizzui";
type TextAlign = "left" | "center" | "right";
export interface HeaderCellProps {
title: React.ReactNode;
width?: number;
align?: TextAlign;
ellipsis?: boolean;
sortable?: boolean;
ascending?: boolean;
iconClassName?: string;
className?: string;
}
function handleTextAlignment(align: TextAlign) {
if (align === "center") return "justify-center";
if (align === "right") return "justify-end rtl:justify-start";
return "";
}
export default function HeaderCell({
title,
align = "left",
width,
ellipsis,
sortable,
ascending,
iconClassName,
className,
}: HeaderCellProps) {
if (ellipsis && width === undefined) {
console.warn(
"When ellipsis is true make sure you are using the same column width in HeaderCell component too."
);
}
if (width !== undefined && ellipsis !== true) {
console.warn(
"width prop without ellipsis won't work, please set ellipsis prop true."
);
}
return (
<div
className={cn(
"flex items-center gap-1",
sortable && "cursor-pointer",
handleTextAlignment(align),
className
)}
>
<div
{...(ellipsis && { className: "truncate" })}
{...(ellipsis && width && { style: { width } })}
>
{title}
</div>
{sortable && (
<div className="inline-flex">
{ascending ? (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
className={cn("h-auto w-3", iconClassName)}
viewBox="0 0 16 16"
>
<path d="m7.247 4.86-4.796 5.481c-.566.647-.106 1.659.753 1.659h9.592a1 1 0 0 0 .753-1.659l-4.796-5.48a1 1 0 0 0-1.506 0z" />
</svg>
) : (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
className={cn("h-auto w-3", iconClassName)}
viewBox="0 0 16 16"
>
<path d="M7.247 11.14 2.451 5.658C1.885 5.013 2.345 4 3.204 4h9.592a1 1 0 0 1 .753 1.659l-4.796 5.48a1 1 0 0 1-1.506 0z" />
</svg>
)}
</div>
)}
</div>
);
}
HeaderCell.displayName = "HeaderCell";
Default
The default style of Table component.
Id | Employee | Designation | Phone Number | Email | Status | ||
---|---|---|---|---|---|---|---|
1 | Jon Brown @fredchaparro | Front-End Developer REDQ | +880136987584 | jhondoe@aegonui.com | Active | ||
2 | Jon Brown @fredchaparro | Front-End Developer REDQ | +880136987584 | jhondoe@aegonui.com | Active | ||
3 | Jon Brown @fredchaparro | Front-End Developer REDQ | +880136987584 | jhondoe@aegonui.com | Active | ||
4 | Jon Brown @fredchaparro | Front-End Developer REDQ | +880136987584 | jhondoe@aegonui.com | Active | ||
5 | Jon Brown @fredchaparro | Front-End Developer REDQ | +880136987584 | jhondoe@aegonui.com | Active | ||
6 | Jon Brown @fredchaparro | Front-End Developer REDQ | +880136987584 | jhondoe@aegonui.com | Active |
import React from "react";
import Table from "@components/table";
import HeaderCell from "@components/header-cell";
import { Checkbox, Avatar, Text, Badge } from "rizzui";
const initialData = [
{
id: "1",
employee: {
name: "Jon Brown",
userName: "@fredchaparro",
avatar: "https://randomuser.me/api/portraits/men/1.jpg",
},
designation: {
role: "Front-End Developer",
company: "REDQ",
},
phoneNumber: "+880136987584",
email: "jhondoe@aegonui.com",
status: "Active",
},
{
id: "2",
employee: {
name: "Jon Brown",
userName: "@fredchaparro",
avatar: "https://randomuser.me/api/portraits/men/2.jpg",
},
designation: {
role: "Front-End Developer",
company: "REDQ",
},
phoneNumber: "+880136987584",
email: "jhondoe@aegonui.com",
status: "Active",
},
{
id: "3",
employee: {
name: "Jon Brown",
userName: "@fredchaparro",
avatar: "https://randomuser.me/api/portraits/men/3.jpg",
},
designation: {
role: "Front-End Developer",
company: "REDQ",
},
phoneNumber: "+880136987584",
email: "jhondoe@aegonui.com",
status: "Active",
},
{
id: "4",
employee: {
name: "Jon Brown",
userName: "@fredchaparro",
avatar: "https://randomuser.me/api/portraits/men/4.jpg",
},
designation: {
role: "Front-End Developer",
company: "REDQ",
},
phoneNumber: "+880136987584",
email: "jhondoe@aegonui.com",
status: "Active",
},
{
id: "5",
employee: {
name: "Jon Brown",
userName: "@fredchaparro",
avatar: "https://randomuser.me/api/portraits/men/5.jpg",
},
designation: {
role: "Front-End Developer",
company: "REDQ",
},
phoneNumber: "+880136987584",
email: "jhondoe@aegonui.com",
status: "Active",
},
{
id: "6",
employee: {
name: "Jon Brown",
userName: "@fredchaparro",
avatar: "https://randomuser.me/api/portraits/men/6.jpg",
},
designation: {
role: "Front-End Developer",
company: "REDQ",
},
phoneNumber: "+880136987584",
email: "jhondoe@aegonui.com",
status: "Active",
},
];
function getStatusBadge(status: string) {
switch (status.toLowerCase()) {
case "pending":
return <Badge variant="flat">{status}</Badge>;
case "active":
return (
<Badge
variant="flat"
color="success"
>
{status}
</Badge>
);
case "warning":
return (
<Badge
variant="flat"
color="warning"
>
{status}
</Badge>
);
case "danger":
return (
<Badge
variant="flat"
color="danger"
>
{status}
</Badge>
);
default:
return null;
}
}
const getColumns = (order: string, column: string, onHeaderClick: (value: string) => any) => [
{
title: <></>,
dataIndex: "checked",
key: "checked",
width: 50,
render: () => (
<div className="inline-flex cursor-pointer">
<Checkbox variant="flat" />
</div>
),
},
{
title: (
<HeaderCell
title="Id"
sortable
ascending={order === "asc" && column === "id"}
/>
),
onHeaderCell: () => onHeaderClick("id"),
dataIndex: "id",
key: "id",
width: 50,
},
{
title: <HeaderCell title="Employee" />,
dataIndex: "employee",
key: "employee",
width: 250,
render: (employee: any) => (
<div className="flex items-center">
<Avatar
name="John Doe"
src={employee.avatar}
/>
<div className="ml-3 rtl:ml-0 rtl:mr-3">
<Text
as="h6"
className="mb-0.5 !text-sm font-medium"
>
{employee.name}
</Text>
<Text
as="p"
className="text-xs text-gray-400"
>
{employee.userName}
</Text>
</div>
</div>
),
},
{
title: <HeaderCell title="Designation" />,
dataIndex: "designation",
key: "designation",
width: 320,
render: (designation: any) => (
<div>
<Text
as="h6"
className="mb-0.5 !text-sm font-medium"
>
{designation.role}
</Text>
<Text
as="p"
className="text-xs text-gray-400"
>
{designation.company}
</Text>
</div>
),
},
{
title: <HeaderCell title="Phone Number" />,
dataIndex: "phoneNumber",
key: "phoneNumber",
width: 200,
},
{
title: <HeaderCell title="Email" />,
dataIndex: "email",
key: "email",
width: 200,
},
{
title: <HeaderCell title="Status" />,
dataIndex: "status",
key: "status",
width: 120,
render: (value: string) => getStatusBadge(value),
},
{
title: <></>,
dataIndex: "action",
key: "action",
width: 120,
render: (_: string, row: any) => (
<div className="flex items-center gap-2">
<button
type="button"
className="text-primary underline"
onClick={() => alert(`Edit item #${row.id}`)}
>
Edit
</button>
<button
type="button"
className="underline"
>
View
</button>
</div>
),
},
];
export default function App() {
const [order, setOrder] = React.useState<string>("desc");
const [column, setColumn] = React.useState<string>("");
const [data, setData] = React.useState<typeof initialData>(initialData);
const onHeaderClick = (value: string) => ({
onClick: () => {
setColumn(value);
setOrder(order === "desc" ? "asc" : "desc");
if (order === "desc") {
// @ts-ignore
setData([...data.sort((a, b) => (a[value] > b[value] ? -1 : 1))]);
} else {
// @ts-ignore
setData([...data.sort((a, b) => (a[value] > b[value] ? 1 : -1))]);
}
},
});
const columns: any = React.useMemo(
() => getColumns(order, column, onHeaderClick),
[order, column, onHeaderClick]
);
return (
<Table
data={data}
columns={columns}
className="text-sm"
/>
);
}
Modern
You can change the style of Table by changing property variant.
Id | Employee | Designation | Phone Number | Email | Status | ||
---|---|---|---|---|---|---|---|
1 | Jon Brown @fredchaparro | Front-End Developer REDQ | +880136987584 | jhondoe@aegonui.com | Active | ||
2 | Jon Brown @fredchaparro | Front-End Developer REDQ | +880136987584 | jhondoe@aegonui.com | Active | ||
3 | Jon Brown @fredchaparro | Front-End Developer REDQ | +880136987584 | jhondoe@aegonui.com | Active | ||
4 | Jon Brown @fredchaparro | Front-End Developer REDQ | +880136987584 | jhondoe@aegonui.com | Active | ||
5 | Jon Brown @fredchaparro | Front-End Developer REDQ | +880136987584 | jhondoe@aegonui.com | Active | ||
6 | Jon Brown @fredchaparro | Front-End Developer REDQ | +880136987584 | jhondoe@aegonui.com | Active |
import React from "react";
import Table from "@components/table";
import HeaderCell from "@components/header-cell";
import { Checkbox, Avatar, Text, Badge } from "rizzui";
const initialData = [
{
id: "1",
employee: {
name: "Jon Brown",
userName: "@fredchaparro",
avatar: "https://randomuser.me/api/portraits/men/1.jpg",
},
designation: {
role: "Front-End Developer",
company: "REDQ",
},
phoneNumber: "+880136987584",
email: "jhondoe@aegonui.com",
status: "Active",
},
{
id: "2",
employee: {
name: "Jon Brown",
userName: "@fredchaparro",
avatar: "https://randomuser.me/api/portraits/men/2.jpg",
},
designation: {
role: "Front-End Developer",
company: "REDQ",
},
phoneNumber: "+880136987584",
email: "jhondoe@aegonui.com",
status: "Active",
},
{
id: "3",
employee: {
name: "Jon Brown",
userName: "@fredchaparro",
avatar: "https://randomuser.me/api/portraits/men/3.jpg",
},
designation: {
role: "Front-End Developer",
company: "REDQ",
},
phoneNumber: "+880136987584",
email: "jhondoe@aegonui.com",
status: "Active",
},
{
id: "4",
employee: {
name: "Jon Brown",
userName: "@fredchaparro",
avatar: "https://randomuser.me/api/portraits/men/4.jpg",
},
designation: {
role: "Front-End Developer",
company: "REDQ",
},
phoneNumber: "+880136987584",
email: "jhondoe@aegonui.com",
status: "Active",
},
{
id: "5",
employee: {
name: "Jon Brown",
userName: "@fredchaparro",
avatar: "https://randomuser.me/api/portraits/men/5.jpg",
},
designation: {
role: "Front-End Developer",
company: "REDQ",
},
phoneNumber: "+880136987584",
email: "jhondoe@aegonui.com",
status: "Active",
},
{
id: "6",
employee: {
name: "Jon Brown",
userName: "@fredchaparro",
avatar: "https://randomuser.me/api/portraits/men/6.jpg",
},
designation: {
role: "Front-End Developer",
company: "REDQ",
},
phoneNumber: "+880136987584",
email: "jhondoe@aegonui.com",
status: "Active",
},
];
function getStatusBadge(status: string) {
switch (status.toLowerCase()) {
case "pending":
return <Badge variant="flat">{status}</Badge>;
case "active":
return (
<Badge
variant="flat"
color="success"
>
{status}
</Badge>
);
case "warning":
return (
<Badge
variant="flat"
color="warning"
>
{status}
</Badge>
);
case "danger":
return (
<Badge
variant="flat"
color="danger"
>
{status}
</Badge>
);
default:
return null;
}
}
const getColumns = (order: string, column: string, onHeaderClick: (value: string) => any) => [
{
title: <></>,
dataIndex: "checked",
key: "checked",
width: 50,
render: () => (
<div className="inline-flex cursor-pointer">
<Checkbox variant="flat" />
</div>
),
},
{
title: (
<HeaderCell
title="Id"
sortable
ascending={order === "asc" && column === "id"}
/>
),
onHeaderCell: () => onHeaderClick("id"),
dataIndex: "id",
key: "id",
width: 50,
},
{
title: <HeaderCell title="Employee" />,
dataIndex: "employee",
key: "employee",
width: 250,
render: (employee: any) => (
<div className="flex items-center">
<Avatar
name="John Doe"
src={employee.avatar}
/>
<div className="ml-3 rtl:ml-0 rtl:mr-3">
<Text
as="h6"
className="mb-0.5 !text-sm font-medium"
>
{employee.name}
</Text>
<Text
as="p"
className="text-xs text-gray-400"
>
{employee.userName}
</Text>
</div>
</div>
),
},
{
title: <HeaderCell title="Designation" />,
dataIndex: "designation",
key: "designation",
width: 320,
render: (designation: any) => (
<div>
<Text
as="h6"
className="mb-0.5 !text-sm font-medium"
>
{designation.role}
</Text>
<Text
as="p"
className="text-xs text-gray-400"
>
{designation.company}
</Text>
</div>
),
},
{
title: <HeaderCell title="Phone Number" />,
dataIndex: "phoneNumber",
key: "phoneNumber",
width: 200,
},
{
title: <HeaderCell title="Email" />,
dataIndex: "email",
key: "email",
width: 200,
},
{
title: <HeaderCell title="Status" />,
dataIndex: "status",
key: "status",
width: 120,
render: (value: string) => getStatusBadge(value),
},
{
title: <></>,
dataIndex: "action",
key: "action",
width: 120,
render: (_: string, row: any) => (
<div className="flex items-center gap-2">
<button
type="button"
className="text-primary underline"
onClick={() => alert(`Edit item #${row.id}`)}
>
Edit
</button>
<button
type="button"
className="underline"
>
View
</button>
</div>
),
},
];
export default function App() {
const [order, setOrder] = React.useState<string>("desc");
const [column, setColumn] = React.useState<string>("");
const [data, setData] = React.useState<typeof initialData>(initialData);
const onHeaderClick = (value: string) => ({
onClick: () => {
setColumn(value);
setOrder(order === "desc" ? "asc" : "desc");
if (order === "desc") {
// @ts-ignore
setData([...data.sort((a, b) => (a[value] > b[value] ? -1 : 1))]);
} else {
// @ts-ignore
setData([...data.sort((a, b) => (a[value] > b[value] ? 1 : -1))]);
}
},
});
const columns: any = React.useMemo(
() => getColumns(order, column, onHeaderClick),
[order, column, onHeaderClick]
);
return (
<Table
data={data}
columns={columns}
className="text-sm"
variant="modern"
/>
);
}
Elegant
You can change the style of Table by changing property variant.
Id | Employee | Designation | Phone Number | Email | Status | ||
---|---|---|---|---|---|---|---|
1 | Jon Brown @fredchaparro | Front-End Developer REDQ | +880136987584 | jhondoe@aegonui.com | Active | ||
2 | Jon Brown @fredchaparro | Front-End Developer REDQ | +880136987584 | jhondoe@aegonui.com | Active | ||
3 | Jon Brown @fredchaparro | Front-End Developer REDQ | +880136987584 | jhondoe@aegonui.com | Active | ||
4 | Jon Brown @fredchaparro | Front-End Developer REDQ | +880136987584 | jhondoe@aegonui.com | Active | ||
5 | Jon Brown @fredchaparro | Front-End Developer REDQ | +880136987584 | jhondoe@aegonui.com | Active | ||
6 | Jon Brown @fredchaparro | Front-End Developer REDQ | +880136987584 | jhondoe@aegonui.com | Active |
import React from "react";
import Table from "@components/table";
import HeaderCell from "@components/header-cell";
import { Checkbox, Avatar, Text, Badge } from "rizzui";
const initialData = [
{
id: "1",
employee: {
name: "Jon Brown",
userName: "@fredchaparro",
avatar: "https://randomuser.me/api/portraits/men/1.jpg",
},
designation: {
role: "Front-End Developer",
company: "REDQ",
},
phoneNumber: "+880136987584",
email: "jhondoe@aegonui.com",
status: "Active",
},
{
id: "2",
employee: {
name: "Jon Brown",
userName: "@fredchaparro",
avatar: "https://randomuser.me/api/portraits/men/2.jpg",
},
designation: {
role: "Front-End Developer",
company: "REDQ",
},
phoneNumber: "+880136987584",
email: "jhondoe@aegonui.com",
status: "Active",
},
{
id: "3",
employee: {
name: "Jon Brown",
userName: "@fredchaparro",
avatar: "https://randomuser.me/api/portraits/men/3.jpg",
},
designation: {
role: "Front-End Developer",
company: "REDQ",
},
phoneNumber: "+880136987584",
email: "jhondoe@aegonui.com",
status: "Active",
},
{
id: "4",
employee: {
name: "Jon Brown",
userName: "@fredchaparro",
avatar: "https://randomuser.me/api/portraits/men/4.jpg",
},
designation: {
role: "Front-End Developer",
company: "REDQ",
},
phoneNumber: "+880136987584",
email: "jhondoe@aegonui.com",
status: "Active",
},
{
id: "5",
employee: {
name: "Jon Brown",
userName: "@fredchaparro",
avatar: "https://randomuser.me/api/portraits/men/5.jpg",
},
designation: {
role: "Front-End Developer",
company: "REDQ",
},
phoneNumber: "+880136987584",
email: "jhondoe@aegonui.com",
status: "Active",
},
{
id: "6",
employee: {
name: "Jon Brown",
userName: "@fredchaparro",
avatar: "https://randomuser.me/api/portraits/men/6.jpg",
},
designation: {
role: "Front-End Developer",
company: "REDQ",
},
phoneNumber: "+880136987584",
email: "jhondoe@aegonui.com",
status: "Active",
},
];
function getStatusBadge(status: string) {
switch (status.toLowerCase()) {
case "pending":
return <Badge variant="flat">{status}</Badge>;
case "active":
return (
<Badge
variant="flat"
color="success"
>
{status}
</Badge>
);
case "warning":
return (
<Badge
variant="flat"
color="warning"
>
{status}
</Badge>
);
case "danger":
return (
<Badge
variant="flat"
color="danger"
>
{status}
</Badge>
);
default:
return null;
}
}
const getColumns = (order: string, column: string, onHeaderClick: (value: string) => any) => [
{
title: <></>,
dataIndex: "checked",
key: "checked",
width: 50,
render: () => (
<div className="inline-flex cursor-pointer">
<Checkbox variant="flat" />
</div>
),
},
{
title: (
<HeaderCell
title="Id"
sortable
ascending={order === "asc" && column === "id"}
/>
),
onHeaderCell: () => onHeaderClick("id"),
dataIndex: "id",
key: "id",
width: 50,
},
{
title: <HeaderCell title="Employee" />,
dataIndex: "employee",
key: "employee",
width: 250,
render: (employee: any) => (
<div className="flex items-center">
<Avatar
name="John Doe"
src={employee.avatar}
/>
<div className="ml-3 rtl:ml-0 rtl:mr-3">
<Text
as="h6"
className="mb-0.5 !text-sm font-medium"
>
{employee.name}
</Text>
<Text
as="p"
className="text-xs text-gray-400"
>
{employee.userName}
</Text>
</div>
</div>
),
},
{
title: <HeaderCell title="Designation" />,
dataIndex: "designation",
key: "designation",
width: 320,
render: (designation: any) => (
<div>
<Text
as="h6"
className="mb-0.5 !text-sm font-medium"
>
{designation.role}
</Text>
<Text
as="p"
className="text-xs text-gray-400"
>
{designation.company}
</Text>
</div>
),
},
{
title: <HeaderCell title="Phone Number" />,
dataIndex: "phoneNumber",
key: "phoneNumber",
width: 200,
},
{
title: <HeaderCell title="Email" />,
dataIndex: "email",
key: "email",
width: 200,
},
{
title: <HeaderCell title="Status" />,
dataIndex: "status",
key: "status",
width: 120,
render: (value: string) => getStatusBadge(value),
},
{
title: <></>,
dataIndex: "action",
key: "action",
width: 120,
render: (_: string, row: any) => (
<div className="flex items-center gap-2">
<button
type="button"
className="text-primary underline"
onClick={() => alert(`Edit item #${row.id}`)}
>
Edit
</button>
<button
type="button"
className="underline"
>
View
</button>
</div>
),
},
];
export default function App() {
const [order, setOrder] = React.useState<string>("desc");
const [column, setColumn] = React.useState<string>("");
const [data, setData] = React.useState<typeof initialData>(initialData);
const onHeaderClick = (value: string) => ({
onClick: () => {
setColumn(value);
setOrder(order === "desc" ? "asc" : "desc");
if (order === "desc") {
// @ts-ignore
setData([...data.sort((a, b) => (a[value] > b[value] ? -1 : 1))]);
} else {
// @ts-ignore
setData([...data.sort((a, b) => (a[value] > b[value] ? 1 : -1))]);
}
},
});
const columns: any = React.useMemo(
() => getColumns(order, column, onHeaderClick),
[order, column, onHeaderClick]
);
return (
<Table
data={data}
columns={columns}
className="text-sm"
variant="elegant"
/>
);
}
Minimal
You can change the style of Table by changing property variant.
Id | Employee | Designation | Phone Number | Email | Status | ||
---|---|---|---|---|---|---|---|
1 | Jon Brown @fredchaparro | Front-End Developer REDQ | +880136987584 | jhondoe@aegonui.com | Active | ||
2 | Jon Brown @fredchaparro | Front-End Developer REDQ | +880136987584 | jhondoe@aegonui.com | Active | ||
3 | Jon Brown @fredchaparro | Front-End Developer REDQ | +880136987584 | jhondoe@aegonui.com | Active | ||
4 | Jon Brown @fredchaparro | Front-End Developer REDQ | +880136987584 | jhondoe@aegonui.com | Active | ||
5 | Jon Brown @fredchaparro | Front-End Developer REDQ | +880136987584 | jhondoe@aegonui.com | Active | ||
6 | Jon Brown @fredchaparro | Front-End Developer REDQ | +880136987584 | jhondoe@aegonui.com | Active |
import React from "react";
import Table from "@components/table";
import HeaderCell from "@components/header-cell";
import { Checkbox, Avatar, Text, Badge } from "rizzui";
const initialData = [
{
id: "1",
employee: {
name: "Jon Brown",
userName: "@fredchaparro",
avatar: "https://randomuser.me/api/portraits/men/1.jpg",
},
designation: {
role: "Front-End Developer",
company: "REDQ",
},
phoneNumber: "+880136987584",
email: "jhondoe@aegonui.com",
status: "Active",
},
{
id: "2",
employee: {
name: "Jon Brown",
userName: "@fredchaparro",
avatar: "https://randomuser.me/api/portraits/men/2.jpg",
},
designation: {
role: "Front-End Developer",
company: "REDQ",
},
phoneNumber: "+880136987584",
email: "jhondoe@aegonui.com",
status: "Active",
},
{
id: "3",
employee: {
name: "Jon Brown",
userName: "@fredchaparro",
avatar: "https://randomuser.me/api/portraits/men/3.jpg",
},
designation: {
role: "Front-End Developer",
company: "REDQ",
},
phoneNumber: "+880136987584",
email: "jhondoe@aegonui.com",
status: "Active",
},
{
id: "4",
employee: {
name: "Jon Brown",
userName: "@fredchaparro",
avatar: "https://randomuser.me/api/portraits/men/4.jpg",
},
designation: {
role: "Front-End Developer",
company: "REDQ",
},
phoneNumber: "+880136987584",
email: "jhondoe@aegonui.com",
status: "Active",
},
{
id: "5",
employee: {
name: "Jon Brown",
userName: "@fredchaparro",
avatar: "https://randomuser.me/api/portraits/men/5.jpg",
},
designation: {
role: "Front-End Developer",
company: "REDQ",
},
phoneNumber: "+880136987584",
email: "jhondoe@aegonui.com",
status: "Active",
},
{
id: "6",
employee: {
name: "Jon Brown",
userName: "@fredchaparro",
avatar: "https://randomuser.me/api/portraits/men/6.jpg",
},
designation: {
role: "Front-End Developer",
company: "REDQ",
},
phoneNumber: "+880136987584",
email: "jhondoe@aegonui.com",
status: "Active",
},
];
function getStatusBadge(status: string) {
switch (status.toLowerCase()) {
case "pending":
return <Badge variant="flat">{status}</Badge>;
case "active":
return (
<Badge
variant="flat"
color="success"
>
{status}
</Badge>
);
case "warning":
return (
<Badge
variant="flat"
color="warning"
>
{status}
</Badge>
);
case "danger":
return (
<Badge
variant="flat"
color="danger"
>
{status}
</Badge>
);
default:
return null;
}
}
const getColumns = (order: string, column: string, onHeaderClick: (value: string) => any) => [
{
title: <></>,
dataIndex: "checked",
key: "checked",
width: 50,
render: () => (
<div className="inline-flex cursor-pointer">
<Checkbox variant="flat" />
</div>
),
},
{
title: (
<HeaderCell
title="Id"
sortable
ascending={order === "asc" && column === "id"}
/>
),
onHeaderCell: () => onHeaderClick("id"),
dataIndex: "id",
key: "id",
width: 50,
},
{
title: <HeaderCell title="Employee" />,
dataIndex: "employee",
key: "employee",
width: 250,
render: (employee: any) => (
<div className="flex items-center">
<Avatar
name="John Doe"
src={employee.avatar}
/>
<div className="ml-3 rtl:ml-0 rtl:mr-3">
<Text
as="h6"
className="mb-0.5 !text-sm font-medium"
>
{employee.name}
</Text>
<Text
as="p"
className="text-xs text-gray-400"
>
{employee.userName}
</Text>
</div>
</div>
),
},
{
title: <HeaderCell title="Designation" />,
dataIndex: "designation",
key: "designation",
width: 320,
render: (designation: any) => (
<div>
<Text
as="h6"
className="mb-0.5 !text-sm font-medium"
>
{designation.role}
</Text>
<Text
as="p"
className="text-xs text-gray-400"
>
{designation.company}
</Text>
</div>
),
},
{
title: <HeaderCell title="Phone Number" />,
dataIndex: "phoneNumber",
key: "phoneNumber",
width: 200,
},
{
title: <HeaderCell title="Email" />,
dataIndex: "email",
key: "email",
width: 200,
},
{
title: <HeaderCell title="Status" />,
dataIndex: "status",
key: "status",
width: 120,
render: (value: string) => getStatusBadge(value),
},
{
title: <></>,
dataIndex: "action",
key: "action",
width: 120,
render: (_: string, row: any) => (
<div className="flex items-center gap-2">
<button
type="button"
className="text-primary underline"
onClick={() => alert(`Edit item #${row.id}`)}
>
Edit
</button>
<button
type="button"
className="underline"
>
View
</button>
</div>
),
},
];
export default function App() {
const [order, setOrder] = React.useState<string>("desc");
const [column, setColumn] = React.useState<string>("");
const [data, setData] = React.useState<typeof initialData>(initialData);
const onHeaderClick = (value: string) => ({
onClick: () => {
setColumn(value);
setOrder(order === "desc" ? "asc" : "desc");
if (order === "desc") {
// @ts-ignore
setData([...data.sort((a, b) => (a[value] > b[value] ? -1 : 1))]);
} else {
// @ts-ignore
setData([...data.sort((a, b) => (a[value] > b[value] ? 1 : -1))]);
}
},
});
const columns: any = React.useMemo(
() => getColumns(order, column, onHeaderClick),
[order, column, onHeaderClick]
);
return (
<Table
data={data}
columns={columns}
className="text-sm"
variant="minimal"
/>
);
}
API Reference
Table Props
Here is the API documentation of the Table component.
Props | Type | Description | Default |
---|---|---|---|
emptyText | ReactNode | Set empty text, it will only appear when table has no data | __ |
variant | TableVariants | The variants of the component are: | "classic" |
striped | boolean | Add striping style | __ |
className | string | Add custom classes for extra style | __ |
id | string | __ | |
style | CSSProperties | __ | |
title | PanelRender<DefaultRecordType> | __ | |
caption | ReactNode | __ | |
data | readonly DefaultRecordType[] | __ | |
footer | PanelRender<DefaultRecordType> | __ | |
summary | ((data: readonly DefaultRecordType[]) => ReactNode) | __ | |
prefixCls | string | __ | |
direction | ltr rtl | __ | |
expandedRowKeys | Key[] | __ | |
defaultExpandedRowKeys | key[] | __ | |
expandedRowRender | ExpandedRowRender<DefaultRecordType> | __ | |
expandRowByClick | boolean | __ | |
expandIcon | RenderExpandIcon<DefaultRecordType> | __ | |
onExpand | ((expanded: boolean, record: DefaultRecordType) => void) | __ | |
onExpandedRowsChange | ((expandedKeys: Key[]) => void) | __ | |
defaultExpandAllRows | boolean | __ | |
indentSize | number | __ | |
expandIconColumnIndex | number | __ | |
expandedRowClassName | RowClassName<DefaultRecordType> | __ | |
childrenColumnName | string | __ | |
columns | ColumnsType<DefaultRecordType> | __ | |
rowKey | string GetRowKey<DefaultRecordType> | __ | |
tableLayout | fixed auto | __ | |
scroll | TableScroll | __ | |
expandable | ExpandableConfig<DefaultRecordType> | Config expand rows | __ |
rowClassName | string RowClassName<DefaultRecordType> | __ | |
showHeader | boolean | __ | |
components | TableComponents<DefaultRecordType> | __ | |
onRow | GetComponentProps<DefaultRecordType> | __ | |
onHeaderRow | GetComponentProps<readonly ColumnType<DefaultRecordType>[]> | __ | |
internalHooks | string | __ | |
transformColumns | TableTransformColumns | __ | |
internalRefs | { body: MutableRefObject<HTMLDivElement>; } | __ | |
sticky | sticky tableSticky | __ |
- Note: You can check rc-table documentation for more details.
Table Variants
type TableVariants = "classic" | "modern" | "minimal" | "elegant";
Table Scroll
type TableScroll = { x?: string | number | true; y?: string | number } | undefined;
Table Transform Columns
type TableTransformColumns = (
columns: ColumnsType<DefaultRecordType>
) => ColumnsType<DefaultRecordType>;