import React, { DragEvent, memo, MouseEvent, RefObject, useEffect, useMemo, useState, } from 'react'; import Skeleton from '@mui/material/Skeleton'; import TableCell from '@mui/material/TableCell'; import Box from '@mui/material/Box'; import { useTheme } from '@mui/material/styles'; import Tooltip from '@mui/material/Tooltip'; import { MRT_EditCellTextField } from '../inputs/MRT_EditCellTextField'; import { MRT_CopyButton } from '../buttons/MRT_CopyButton'; import { MRT_TableBodyRowGrabHandle } from './MRT_TableBodyRowGrabHandle'; import { MRT_TableBodyCellValue } from './MRT_TableBodyCellValue'; import { getCommonCellStyles, getIsFirstColumn, getIsLastColumn, } from '../column.utils'; import type { VirtualItem } from '@tanstack/react-virtual'; import type { MRT_Cell, MRT_TableInstance } from '..'; interface Props { cell: MRT_Cell; measureElement?: (element: HTMLTableCellElement) => void; numRows: number; rowIndex: number; rowRef: RefObject; table: MRT_TableInstance; virtualCell?: VirtualItem; } export const MRT_TableBodyCell = ({ cell, measureElement, numRows, rowIndex, rowRef, table, virtualCell, }: Props) => { const theme = useTheme(); const { getState, options: { editingMode, enableClickToCopy, enableColumnOrdering, enableEditing, enableGrouping, enableRowNumbers, layoutMode, muiTableBodyCellProps, muiTableBodyCellSkeletonProps, rowNumberMode, }, refs: { editInputRefs }, setEditingCell, setHoveredColumn, } = table; const { draggingColumn, draggingRow, editingCell, editingRow, hoveredColumn, hoveredRow, density, isLoading, showSkeletons, } = getState(); const { column, row } = cell; const { columnDef } = column; const { columnDefType } = columnDef; const mTableCellBodyProps = muiTableBodyCellProps instanceof Function ? muiTableBodyCellProps({ cell, column, row, table }) : muiTableBodyCellProps; const mcTableCellBodyProps = columnDef.muiTableBodyCellProps instanceof Function ? columnDef.muiTableBodyCellProps({ cell, column, row, table }) : columnDef.muiTableBodyCellProps; const tableCellProps = { ...mTableCellBodyProps, ...mcTableCellBodyProps, }; const skeletonProps = muiTableBodyCellSkeletonProps instanceof Function ? muiTableBodyCellSkeletonProps({ cell, column, row, table }) : muiTableBodyCellSkeletonProps; const [skeletonWidth, setSkeletonWidth] = useState(0); useEffect( () => setSkeletonWidth( isLoading || showSkeletons ? columnDefType === 'display' ? column.getSize() / 2 : Math.round( Math.random() * (column.getSize() - column.getSize() / 3) + column.getSize() / 3, ) : 100, ), [], ); const draggingBorders = useMemo(() => { const isDraggingColumn = draggingColumn?.id === column.id; const isHoveredColumn = hoveredColumn?.id === column.id; const isDraggingRow = draggingRow?.id === row.id; const isHoveredRow = hoveredRow?.id === row.id; const isFirstColumn = getIsFirstColumn(column, table); const isLastColumn = getIsLastColumn(column, table); const isLastRow = rowIndex === numRows - 1; const borderStyle = isDraggingColumn || isDraggingRow ? `1px dashed ${theme.palette.text.secondary} !important` : isHoveredColumn || isHoveredRow ? `2px dashed ${theme.palette.primary.main} !important` : undefined; return borderStyle ? { borderLeft: isDraggingColumn || isHoveredColumn || ((isDraggingRow || isHoveredRow) && isFirstColumn) ? borderStyle : undefined, borderRight: isDraggingColumn || isHoveredColumn || ((isDraggingRow || isHoveredRow) && isLastColumn) ? borderStyle : undefined, borderBottom: isDraggingRow || isHoveredRow || isLastRow ? borderStyle : undefined, borderTop: isDraggingRow || isHoveredRow ? borderStyle : undefined, } : undefined; }, [draggingColumn, draggingRow, hoveredColumn, hoveredRow, rowIndex]); const isEditable = (enableEditing instanceof Function ? enableEditing(row) : enableEditing) && (columnDef.enableEditing instanceof Function ? columnDef.enableEditing(row) : columnDef.enableEditing) !== false; const isEditing = isEditable && editingMode !== 'modal' && (editingMode === 'table' || editingRow?.id === row.id || editingCell?.id === cell.id) && !row.getIsGrouped(); const handleDoubleClick = (event: MouseEvent) => { tableCellProps?.onDoubleClick?.(event); if (isEditable && editingMode === 'cell') { setEditingCell(cell); queueMicrotask(() => { const textField = editInputRefs.current[column.id]; if (textField) { textField.focus(); textField.select?.(); } }); } }; const handleDragEnter = (e: DragEvent) => { tableCellProps?.onDragEnter?.(e); if (enableGrouping && hoveredColumn?.id === 'drop-zone') { setHoveredColumn(null); } if (enableColumnOrdering && draggingColumn) { setHoveredColumn( columnDef.enableColumnOrdering !== false ? column : null, ); } }; const saving = columnDef.getSaving?.(cell.row.original) ?? null; const error = columnDef.getError?.(cell.row.original) ?? null; return ( { if (node) { measureElement?.(node); } }} {...tableCellProps} onDragEnter={handleDragEnter} onDoubleClick={handleDoubleClick} sx={(theme) => ({ alignItems: layoutMode === 'grid' ? 'center' : undefined, cursor: isEditable && editingMode === 'cell' ? 'pointer' : 'inherit', justifyContent: layoutMode === 'grid' ? tableCellProps.align : undefined, overflow: 'hidden', p: density === 'compact' ? columnDefType === 'display' ? '0 0.5rem' : '0.5rem' : density === 'comfortable' ? columnDefType === 'display' ? '0.5rem 0.75rem' : '1rem' : columnDefType === 'display' ? '1rem 1.25rem' : '1.5rem', pl: column.id === 'mrt-row-expand' ? `${ row.depth + (density === 'compact' ? 0.5 : density === 'comfortable' ? 0.75 : 1.25) }rem` : undefined, textOverflow: columnDefType !== 'display' ? 'ellipsis' : undefined, whiteSpace: density === 'compact' ? 'nowrap' : 'normal', zIndex: draggingColumn?.id === column.id ? 2 : column.getIsPinned() ? 1 : 0, ...(saving && { '@keyframes savingEffect': { '0%': { backgroundColor: '#FAFAFA' }, '25%': { backgroundColor: '#81F79F' }, '50%': { backgroundColor: '#2EFE64' }, '75%': { backgroundColor: '#81F79F' }, '100%': { backgroundColor: '#CEF6D8' }, }, '&:after': { animation: 'savingEffect 1s ease-in-out infinite', background: theme.palette.primary.main, borderRadius: '50%', content: '""', display: 'block', height: '0.7rem', margin: '0 auto', width: '0.7rem', }, }), ...(!error && { '&:hover': { outline: ['table', 'cell'].includes(editingMode ?? '') ? `1px solid ${theme.palette.text.secondary}` : undefined, outlineOffset: '-1px', textOverflow: 'clip', }, }), ...getCommonCellStyles({ column, table, theme, tableCellProps, }), ...draggingBorders, ...(error && { outline: `1px solid ${ error.type === 'error' ? theme.palette.error.light : theme.palette.warning.light }`, outlineOffset: '-1px', }), })} > <> {cell.getIsPlaceholder() ? ( columnDef.PlaceholderCell?.({ cell, column, row, table }) ?? null ) : isLoading || showSkeletons ? ( ) : enableRowNumbers && rowNumberMode === 'static' && column.id === 'mrt-row-numbers' ? ( rowIndex + 1 ) : column.id === 'mrt-row-drag' ? ( ) : columnDefType === 'display' && (column.id === 'mrt-row-select' || column.id === 'mrt-row-expand' || !row.getIsGrouped()) ? ( columnDef.Cell?.({ cell, renderedCellValue: cell.renderValue() as any, column, row, table, }) ) : isEditing ? ( ) : (enableClickToCopy || columnDef.enableClickToCopy) && columnDef.enableClickToCopy !== false ? ( ) : ( )} {cell.getIsGrouped() && !columnDef.GroupedCell && ( <> ({row.subRows?.length}) )} ); }; export const Memo_MRT_TableBodyCell = memo( MRT_TableBodyCell, (prev, next) => next.cell === prev.cell, );