UNPKG

@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
"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"] }) })] })); }