@ackplus/react-tanstack-data-table
Version:
A powerful React data table component built with MUI and TanStack Table
265 lines (264 loc) • 23.9 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AdvancedFeaturesExample = AdvancedFeaturesExample;
const jsx_runtime_1 = require("react/jsx-runtime");
const react_1 = require("react");
const material_1 = require("@mui/material");
const icons_material_1 = require("@mui/icons-material");
const components_1 = require("../components");
const generateEmployeeData = () => {
const departments = [
{ id: 1, name: 'Engineering', color: '#2196F3' },
{ id: 2, name: 'Design', color: '#9C27B0' },
{ id: 3, name: 'Marketing', color: '#FF9800' },
{ id: 4, name: 'Sales', color: '#4CAF50' },
{ id: 5, name: 'HR', color: '#F44336' },
];
const positions = ['Senior', 'Mid-level', 'Junior', 'Lead', 'Manager'];
const skills = ['React', 'TypeScript', 'Node.js', 'Python', 'Design', 'Analytics', 'Leadership'];
const names = ['Alice Johnson', 'Bob Smith', 'Charlie Brown', 'Diana Prince', 'Eva Garcia', 'Frank Miller', 'Grace Lee', 'Henry Davis'];
return Array.from({ length: 20 }, (_, index) => {
const dept = departments[index % departments.length];
const employeeSkills = skills.sort(() => 0.5 - Math.random()).slice(0, Math.floor(Math.random() * 4) + 2);
return {
id: index + 1,
name: names[index % names.length] || `Employee ${index + 1}`,
email: `employee${index + 1}@company.com`,
avatar: `https://i.pravatar.cc/40?img=${index + 1}`,
department: dept,
position: `${positions[index % positions.length]} ${dept.name.slice(0, -3)}er`,
salary: 60000 + (index * 5000) + Math.floor(Math.random() * 20000),
performance: {
rating: Math.floor(Math.random() * 5) + 1,
trend: ['up', 'down', 'stable'][Math.floor(Math.random() * 3)],
score: Math.floor(Math.random() * 40) + 60,
},
skills: employeeSkills,
startDate: new Date(2020 + Math.floor(Math.random() * 4), Math.floor(Math.random() * 12), Math.floor(Math.random() * 28) + 1).toISOString().split('T')[0],
isActive: Math.random() > 0.1,
projects: Array.from({ length: Math.floor(Math.random() * 3) + 1 }, (_, projIndex) => ({
id: projIndex + 1,
name: `Project ${String.fromCharCode(65 + projIndex)}`,
progress: Math.floor(Math.random() * 100),
priority: ['high', 'medium', 'low'][Math.floor(Math.random() * 3)],
})),
metadata: {
lastLogin: new Date(Date.now() - Math.floor(Math.random() * 30) * 24 * 60 * 60 * 1000).toISOString(),
vacationDays: Math.floor(Math.random() * 25),
certifications: Math.floor(Math.random() * 8),
},
};
});
};
function AdvancedFeaturesExample() {
const [employees, setEmployees] = (0, react_1.useState)(generateEmployeeData());
const [editingRows, setEditingRows] = (0, react_1.useState)(new Set());
const [showAdvancedFeatures, setShowAdvancedFeatures] = (0, react_1.useState)(true);
const [showNestedData, setShowNestedData] = (0, react_1.useState)(false);
const handleStartEdit = (0, react_1.useCallback)((employeeId) => {
setEditingRows(prev => new Set([...prev, employeeId]));
}, []);
const handleSaveEdit = (0, react_1.useCallback)((employeeId) => {
setEditingRows(prev => {
const newSet = new Set(prev);
newSet.delete(employeeId);
return newSet;
});
}, []);
const handleCancelEdit = (0, react_1.useCallback)((employeeId) => {
setEditingRows(prev => {
const newSet = new Set(prev);
newSet.delete(employeeId);
return newSet;
});
}, []);
const handleFieldChange = (0, react_1.useCallback)((employeeId, field, value) => {
setEmployees(prev => prev.map(emp => emp.id === employeeId
? Object.assign(Object.assign({}, emp), { [field]: value }) : emp));
}, []);
const NameCell = ({ row }) => ((0, jsx_runtime_1.jsxs)(material_1.Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 }, children: [(0, jsx_runtime_1.jsx)(material_1.Avatar, { src: row.avatar, sx: { width: 32, height: 32 } }), (0, jsx_runtime_1.jsxs)(material_1.Box, { children: [(0, jsx_runtime_1.jsx)(material_1.Typography, { variant: "body2", fontWeight: "medium", children: row.name }), (0, jsx_runtime_1.jsx)(material_1.Typography, { variant: "caption", color: "text.secondary", children: row.email })] })] }));
const DepartmentCell = ({ row }) => ((0, jsx_runtime_1.jsx)(material_1.Chip, { label: row.department.name, size: "small", sx: {
backgroundColor: row.department.color,
color: 'white',
fontWeight: 'medium',
} }));
const SalaryCell = ({ row }) => {
const isEditing = editingRows.has(row.id);
if (isEditing) {
return ((0, jsx_runtime_1.jsx)(material_1.TextField, { size: "small", type: "number", value: row.salary, onChange: (e) => handleFieldChange(row.id, 'salary', parseInt(e.target.value)), sx: { width: 120 } }));
}
return ((0, jsx_runtime_1.jsxs)(material_1.Typography, { variant: "body2", fontWeight: "medium", children: ["$", row.salary.toLocaleString()] }));
};
const PerformanceCell = ({ row }) => {
const trendIcon = {
up: (0, jsx_runtime_1.jsx)(icons_material_1.TrendingUp, { sx: { color: 'success.main', fontSize: 16 } }),
down: (0, jsx_runtime_1.jsx)(icons_material_1.TrendingDown, { sx: { color: 'error.main', fontSize: 16 } }),
stable: (0, jsx_runtime_1.jsx)(icons_material_1.Star, { sx: { color: 'warning.main', fontSize: 16 } }),
};
return ((0, jsx_runtime_1.jsxs)(material_1.Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 }, children: [(0, jsx_runtime_1.jsx)(material_1.Rating, { value: row.performance.rating, size: "small", readOnly: true }), trendIcon[row.performance.trend], (0, jsx_runtime_1.jsxs)(material_1.Typography, { variant: "caption", children: [row.performance.score, "%"] })] }));
};
const SkillsCell = ({ row }) => ((0, jsx_runtime_1.jsxs)(material_1.Box, { sx: { display: 'flex', flexWrap: 'wrap', gap: 0.5, maxWidth: 200 }, children: [row.skills.slice(0, 3).map((skill, index) => ((0, jsx_runtime_1.jsx)(material_1.Chip, { label: skill, size: "small", variant: "outlined", sx: { fontSize: '0.7rem', height: 20 } }, index))), row.skills.length > 3 && ((0, jsx_runtime_1.jsx)(material_1.Chip, { label: `+${row.skills.length - 3}`, size: "small", variant: "filled", sx: { fontSize: '0.7rem', height: 20 } }))] }));
const ProjectsCell = ({ row }) => ((0, jsx_runtime_1.jsx)(material_1.Box, { sx: { minWidth: 150 }, children: row.projects.map((project, index) => ((0, jsx_runtime_1.jsxs)(material_1.Box, { sx: { mb: 0.5 }, children: [(0, jsx_runtime_1.jsxs)(material_1.Box, { sx: { display: 'flex', justifyContent: 'space-between', alignItems: 'center' }, children: [(0, jsx_runtime_1.jsx)(material_1.Typography, { variant: "caption", fontWeight: "medium", children: project.name }), (0, jsx_runtime_1.jsx)(material_1.Chip, { label: project.priority, size: "small", color: project.priority === 'high' ? 'error' : project.priority === 'medium' ? 'warning' : 'default', sx: { fontSize: '0.6rem', height: 16 } })] }), (0, jsx_runtime_1.jsx)(material_1.LinearProgress, { variant: "determinate", value: project.progress, sx: { height: 4, borderRadius: 2 }, color: project.priority === 'high' ? 'error' : project.priority === 'medium' ? 'warning' : 'primary' })] }, index))) }));
const ActionsCell = ({ row }) => {
const isEditing = editingRows.has(row.id);
if (isEditing) {
return ((0, jsx_runtime_1.jsxs)(material_1.Box, { sx: { display: 'flex', gap: 0.5 }, children: [(0, jsx_runtime_1.jsx)(material_1.Tooltip, { title: "Save changes", children: (0, jsx_runtime_1.jsx)(material_1.IconButton, { size: "small", onClick: () => handleSaveEdit(row.id), color: "primary", children: (0, jsx_runtime_1.jsx)(icons_material_1.Save, { fontSize: "small" }) }) }), (0, jsx_runtime_1.jsx)(material_1.Tooltip, { title: "Cancel editing", children: (0, jsx_runtime_1.jsx)(material_1.IconButton, { size: "small", onClick: () => handleCancelEdit(row.id), color: "secondary", children: (0, jsx_runtime_1.jsx)(icons_material_1.Cancel, { fontSize: "small" }) }) })] }));
}
return ((0, jsx_runtime_1.jsxs)(material_1.Box, { sx: { display: 'flex', gap: 0.5 }, children: [(0, jsx_runtime_1.jsx)(material_1.Tooltip, { title: "Edit employee", children: (0, jsx_runtime_1.jsx)(material_1.IconButton, { size: "small", onClick: () => handleStartEdit(row.id), color: "primary", children: (0, jsx_runtime_1.jsx)(icons_material_1.Edit, { fontSize: "small" }) }) }), (0, jsx_runtime_1.jsx)(material_1.Tooltip, { title: "Delete employee", children: (0, jsx_runtime_1.jsx)(material_1.IconButton, { size: "small", onClick: () => {
setEmployees(prev => prev.filter(emp => emp.id !== row.id));
}, color: "error", children: (0, jsx_runtime_1.jsx)(icons_material_1.Delete, { fontSize: "small" }) }) })] }));
};
const renderSubComponent = (0, react_1.useCallback)(({ row }) => ((0, jsx_runtime_1.jsx)(material_1.Box, { sx: { p: 2, backgroundColor: 'grey.50', borderRadius: 1 }, children: (0, jsx_runtime_1.jsxs)(material_1.Grid, { container: true, spacing: 2, children: [(0, jsx_runtime_1.jsxs)(material_1.Grid, { size: { xs: 12, md: 6 }, children: [(0, jsx_runtime_1.jsx)(material_1.Typography, { variant: "subtitle2", gutterBottom: true, children: "Contact Information" }), (0, jsx_runtime_1.jsxs)(material_1.Typography, { variant: "body2", children: [(0, jsx_runtime_1.jsx)("strong", { children: "Email:" }), " ", row.email] }), (0, jsx_runtime_1.jsxs)(material_1.Typography, { variant: "body2", children: [(0, jsx_runtime_1.jsx)("strong", { children: "Position:" }), " ", row.position] }), (0, jsx_runtime_1.jsxs)(material_1.Typography, { variant: "body2", children: [(0, jsx_runtime_1.jsx)("strong", { children: "Start Date:" }), " ", new Date(row.startDate).toLocaleDateString()] })] }), (0, jsx_runtime_1.jsxs)(material_1.Grid, { size: { xs: 12, md: 6 }, children: [(0, jsx_runtime_1.jsx)(material_1.Typography, { variant: "subtitle2", gutterBottom: true, children: "Metadata" }), (0, jsx_runtime_1.jsxs)(material_1.Typography, { variant: "body2", children: [(0, jsx_runtime_1.jsx)("strong", { children: "Last Login:" }), " ", new Date(row.metadata.lastLogin).toLocaleDateString()] }), (0, jsx_runtime_1.jsxs)(material_1.Typography, { variant: "body2", children: [(0, jsx_runtime_1.jsx)("strong", { children: "Vacation Days:" }), " ", row.metadata.vacationDays] }), (0, jsx_runtime_1.jsxs)(material_1.Typography, { variant: "body2", children: [(0, jsx_runtime_1.jsx)("strong", { children: "Certifications:" }), " ", row.metadata.certifications] })] }), (0, jsx_runtime_1.jsxs)(material_1.Grid, { size: { xs: 12 }, children: [(0, jsx_runtime_1.jsx)(material_1.Typography, { variant: "subtitle2", gutterBottom: true, children: "All Skills" }), (0, jsx_runtime_1.jsx)(material_1.Box, { sx: { display: 'flex', flexWrap: 'wrap', gap: 0.5 }, children: row.skills.map((skill, index) => ((0, jsx_runtime_1.jsx)(material_1.Chip, { label: skill, size: "small", color: "primary", variant: "outlined" }, index))) })] })] }) })), []);
const columns = (0, react_1.useMemo)(() => {
const baseColumns = [
{
accessorKey: 'name',
header: 'Employee',
size: 200,
cell: ({ row }) => (0, jsx_runtime_1.jsx)(NameCell, { row: row.original }),
enableSorting: true,
filterable: true,
type: 'text',
},
{
accessorKey: 'department.name',
header: 'Department',
size: 120,
cell: ({ row }) => (0, jsx_runtime_1.jsx)(DepartmentCell, { row: row.original }),
enableSorting: true,
filterable: true,
type: 'select',
options: [
{ value: 'Engineering', label: 'Engineering' },
{ value: 'Design', label: 'Design' },
{ value: 'Marketing', label: 'Marketing' },
{ value: 'Sales', label: 'Sales' },
{ value: 'HR', label: 'HR' },
],
},
{
accessorKey: 'salary',
header: 'Salary',
size: 120,
cell: ({ row }) => (0, jsx_runtime_1.jsx)(SalaryCell, { row: row.original }),
enableSorting: true,
filterable: true,
type: 'number',
},
{
accessorKey: 'startDate',
header: 'Start Date',
size: 140,
enableSorting: true,
filterable: true,
type: 'date',
cell: ({ row }) => ((0, jsx_runtime_1.jsx)(material_1.Typography, { variant: "body2", children: new Date(row.original.startDate).toLocaleDateString() })),
},
{
accessorKey: 'performance.rating',
header: 'Performance',
size: 200,
cell: ({ row }) => (0, jsx_runtime_1.jsx)(PerformanceCell, { row: row.original }),
enableSorting: true,
},
{
accessorKey: 'skills',
header: 'Skills',
size: 250,
cell: ({ row }) => (0, jsx_runtime_1.jsx)(SkillsCell, { row: row.original }),
enableSorting: false,
},
];
if (showNestedData) {
baseColumns.push({
accessorKey: 'projects',
header: 'Projects',
size: 200,
cell: ({ row }) => (0, jsx_runtime_1.jsx)(ProjectsCell, { row: row.original }),
enableSorting: false,
});
}
if (showAdvancedFeatures) {
baseColumns.push({
id: 'actions',
header: 'Actions',
size: 100,
cell: ({ row }) => (0, jsx_runtime_1.jsx)(ActionsCell, { row: row.original }),
enableSorting: false,
filterable: false,
});
}
return baseColumns;
}, [showAdvancedFeatures, showNestedData, editingRows]);
const addNewEmployee = (0, react_1.useCallback)(() => {
const newEmployee = {
id: Math.max(...employees.map(e => e.id)) + 1,
name: 'New Employee',
email: 'new@company.com',
avatar: 'https://i.pravatar.cc/40?img=1',
department: { id: 1, name: 'Engineering', color: '#2196F3' },
position: 'Junior Developer',
salary: 60000,
performance: { rating: 3, trend: 'stable', score: 75 },
skills: ['React'],
startDate: new Date().toISOString().split('T')[0],
isActive: true,
projects: [],
metadata: {
lastLogin: new Date().toISOString(),
vacationDays: 20,
certifications: 0,
},
};
setEmployees(prev => [...prev, newEmployee]);
handleStartEdit(newEmployee.id);
}, [employees, handleStartEdit]);
return ((0, jsx_runtime_1.jsxs)(material_1.Box, { sx: { p: 3 }, children: [(0, jsx_runtime_1.jsx)(material_1.Typography, { variant: "h4", gutterBottom: true, children: "Advanced Features Demo" }), (0, jsx_runtime_1.jsx)(material_1.Typography, { variant: "body1", color: "text.secondary", sx: { mb: 3 }, children: "A comprehensive example showcasing inline editing, custom cell renderers, row expansion, dynamic columns, and complex local data operations." }), (0, jsx_runtime_1.jsxs)(material_1.Paper, { sx: { p: 2, mb: 3 }, children: [(0, jsx_runtime_1.jsx)(material_1.Typography, { variant: "h6", gutterBottom: true, children: "Feature Controls" }), (0, jsx_runtime_1.jsxs)(material_1.Box, { sx: { display: 'flex', gap: 2, flexWrap: 'wrap', alignItems: 'center' }, children: [(0, jsx_runtime_1.jsx)(material_1.FormControlLabel, { control: (0, jsx_runtime_1.jsx)(material_1.Switch, { checked: showAdvancedFeatures, onChange: (e) => setShowAdvancedFeatures(e.target.checked) }), label: "Inline Editing & Actions" }), (0, jsx_runtime_1.jsx)(material_1.FormControlLabel, { control: (0, jsx_runtime_1.jsx)(material_1.Switch, { checked: showNestedData, onChange: (e) => setShowNestedData(e.target.checked) }), label: "Show Project Data" }), (0, jsx_runtime_1.jsx)(material_1.Button, { variant: "contained", startIcon: (0, jsx_runtime_1.jsx)(icons_material_1.Add, {}), onClick: addNewEmployee, size: "small", children: "Add Employee" }), (0, jsx_runtime_1.jsx)(material_1.Badge, { badgeContent: editingRows.size, color: "primary", children: (0, jsx_runtime_1.jsx)(material_1.Typography, { variant: "body2", children: "Editing Rows" }) })] })] }), (0, jsx_runtime_1.jsxs)(material_1.Paper, { sx: { p: 2, mb: 3 }, children: [(0, jsx_runtime_1.jsx)(material_1.Typography, { variant: "h6", gutterBottom: true, children: "Statistics" }), (0, jsx_runtime_1.jsxs)(material_1.Grid, { container: true, spacing: 2, children: [(0, jsx_runtime_1.jsxs)(material_1.Grid, { size: { xs: 6, sm: 3 }, children: [(0, jsx_runtime_1.jsx)(material_1.Typography, { variant: "body2", color: "text.secondary", children: "Total Employees" }), (0, jsx_runtime_1.jsx)(material_1.Typography, { variant: "h6", children: employees.length })] }), (0, jsx_runtime_1.jsxs)(material_1.Grid, { size: { xs: 6, sm: 3 }, children: [(0, jsx_runtime_1.jsx)(material_1.Typography, { variant: "body2", color: "text.secondary", children: "Active Employees" }), (0, jsx_runtime_1.jsx)(material_1.Typography, { variant: "h6", children: employees.filter(e => e.isActive).length })] }), (0, jsx_runtime_1.jsxs)(material_1.Grid, { size: { xs: 6, sm: 3 }, children: [(0, jsx_runtime_1.jsx)(material_1.Typography, { variant: "body2", color: "text.secondary", children: "Avg Salary" }), (0, jsx_runtime_1.jsxs)(material_1.Typography, { variant: "h6", children: ["$", Math.round(employees.reduce((sum, e) => sum + e.salary, 0) / employees.length).toLocaleString()] })] }), (0, jsx_runtime_1.jsxs)(material_1.Grid, { size: { xs: 6, sm: 3 }, children: [(0, jsx_runtime_1.jsx)(material_1.Typography, { variant: "body2", color: "text.secondary", children: "Currently Editing" }), (0, jsx_runtime_1.jsx)(material_1.Typography, { variant: "h6", children: editingRows.size })] })] })] }), (0, jsx_runtime_1.jsx)(material_1.Divider, { sx: { my: 2 } }), (0, jsx_runtime_1.jsx)(components_1.DataTable, { data: employees, totalRow: employees.length, columns: columns, enableRowSelection: true, enableMultiRowSelection: true, enableSorting: true, enableGlobalFilter: true, enableColumnFilter: true, enableColumnDragging: true, enableColumnPinning: true, enablePagination: true, getRowCanExpand: () => true, renderSubComponent: (row) => renderSubComponent({ row: row.original }), initialState: {
pagination: {
pageIndex: 0,
pageSize: 10,
},
columnOrder: ['name', 'department.name', 'salary', 'performance.rating', 'skills'],
}, tableContainerProps: {
sx: {
'& .MuiTableRow-root:hover': {
backgroundColor: 'action.hover',
},
'& .MuiTableCell-root': {
borderBottom: '1px solid',
borderColor: 'divider',
},
}
}, enableBulkActions: true, bulkActions: (selectionState) => ((0, jsx_runtime_1.jsxs)(material_1.Box, { sx: { display: 'flex', gap: 1 }, children: [(0, jsx_runtime_1.jsx)(material_1.Button, { variant: "outlined", size: "small", onClick: () => {
let selectedEmployees;
if (selectionState.type === 'include') {
selectedEmployees = employees.filter(emp => selectionState.ids.includes(emp.id.toString()));
}
else {
selectedEmployees = employees.filter(emp => !selectionState.ids.includes(emp.id.toString()));
}
const avgSalary = selectedEmployees.reduce((sum, emp) => sum + emp.salary, 0) / selectedEmployees.length;
alert(`Average salary of ${selectedEmployees.length} selected employees: $${Math.round(avgSalary).toLocaleString()}`);
}, children: "\uD83D\uDCCA Calculate Avg Salary" }), (0, jsx_runtime_1.jsx)(material_1.Button, { variant: "outlined", size: "small", onClick: () => {
const updatedEmployees = employees.map(emp => {
const isSelected = selectionState.type === 'include'
? selectionState.ids.includes(emp.id.toString())
: !selectionState.ids.includes(emp.id.toString());
return isSelected
? Object.assign(Object.assign({}, emp), { performance: Object.assign(Object.assign({}, emp.performance), { rating: 5 }) }) : emp;
});
setEmployees(updatedEmployees);
}, children: "\u2B50 Boost Performance" }), (0, jsx_runtime_1.jsx)(material_1.Button, { variant: "outlined", size: "small", color: "error", onClick: () => {
let selectedEmployees;
if (selectionState.type === 'include') {
selectedEmployees = employees.filter(emp => selectionState.ids.includes(emp.id.toString()));
}
else {
selectedEmployees = employees.filter(emp => !selectionState.ids.includes(emp.id.toString()));
}
if (window.confirm(`Delete ${selectedEmployees.length} selected employees?`)) {
const selectedIds = selectedEmployees.map(emp => emp.id);
setEmployees(prev => prev.filter(emp => !selectedIds.includes(emp.id)));
}
}, children: "\uD83D\uDDD1\uFE0F Delete Selected" })] })), fitToScreen: true }), (0, jsx_runtime_1.jsx)(material_1.Box, { sx: { mt: 2 }, children: (0, jsx_runtime_1.jsxs)(material_1.Typography, { variant: "body2", color: "text.secondary", children: ["\uD83D\uDCA1 ", (0, jsx_runtime_1.jsx)("strong", { children: "Features demonstrated:" }), (0, jsx_runtime_1.jsx)("br", {}), "\u2022 Inline editing with save/cancel actions", (0, jsx_runtime_1.jsx)("br", {}), "\u2022 Custom cell renderers with rich content (avatars, ratings, progress bars)", (0, jsx_runtime_1.jsx)("br", {}), "\u2022 Row expansion with detailed information", (0, jsx_runtime_1.jsx)("br", {}), "\u2022 Dynamic column configuration", (0, jsx_runtime_1.jsx)("br", {}), "\u2022 Complex nested data structures", (0, jsx_runtime_1.jsx)("br", {}), "\u2022 Real-time statistics and bulk operations", (0, jsx_runtime_1.jsx)("br", {}), "\u2022 Advanced styling and theming"] }) })] }));
}