import clsx from 'clsx'; import classes from './MRT_TableBodyCell.module.css'; import { type CSSProperties, type DragEvent, memo, type MouseEvent, type RefObject, useEffect, useRef, useState, } from 'react'; import { Skeleton, TableTd, type TableTdProps, useDirection, } from '@mantine/core'; import { MRT_TableBodyCellValue } from './MRT_TableBodyCellValue'; import { type MRT_Cell, type MRT_CellValue, type MRT_RowData, type MRT_TableInstance, type MRT_VirtualItem, } from '../../types'; import { parseCSSVarId } from '../../utils/style.utils'; import { parseFromValuesOrFunc } from '../../utils/utils'; import { MRT_CopyButton } from '../buttons/MRT_CopyButton'; import { MRT_EditCellTextInput } from '../inputs/MRT_EditCellTextInput'; interface Props extends TableTdProps { cell: MRT_Cell; numRows?: number; renderedColumnIndex?: number; renderedRowIndex?: number; rowRef: RefObject; table: MRT_TableInstance; virtualCell?: MRT_VirtualItem; } export const MRT_TableBodyCell = ({ cell, numRows = 1, renderedColumnIndex = 0, renderedRowIndex = 0, rowRef, table, virtualCell, ...rest }: Props) => { 'use no memo'; const direction = useDirection(); const { getState, options: { columnResizeDirection, columnResizeMode, createDisplayMode, editDisplayMode, enableClickToCopy, enableColumnOrdering, enableColumnPinning, enableEditing, enableGrouping, layoutMode, mantineSkeletonProps, mantineTableBodyCellProps, }, refs: { editInputRefs }, setEditingCell, setHoveredColumn, } = table; const { columnSizingInfo, creatingRow, density, draggingColumn, editingCell, editingRow, hoveredColumn, isLoading, showSkeletons, } = getState(); const { column, row } = cell; const { columnDef } = column; const { columnDefType } = columnDef; const args = { cell, column, renderedColumnIndex, renderedRowIndex, row, table, }; const tableCellProps = { ...parseFromValuesOrFunc(mantineTableBodyCellProps, args), ...parseFromValuesOrFunc(columnDef.mantineTableBodyCellProps, args), ...rest, }; const skeletonProps = parseFromValuesOrFunc(mantineSkeletonProps, args); const [skeletonWidth, setSkeletonWidth] = useState(100); useEffect(() => { if ((!isLoading && !showSkeletons) || skeletonWidth !== 100) return; const size = column.getSize(); setSkeletonWidth( columnDefType === 'display' ? size / 2 : Math.round(Math.random() * (size - size / 3) + size / 3), ); }, [isLoading, showSkeletons]); const widthStyles: CSSProperties = { minWidth: `max(calc(var(--col-${parseCSSVarId( column?.id, )}-size) * 1px), ${columnDef.minSize ?? 30}px)`, width: `calc(var(--col-${parseCSSVarId(column.id)}-size) * 1px)`, }; if (layoutMode === 'grid') { widthStyles.flex = `${ [0, false].includes(columnDef.grow!) ? 0 : `var(--col-${parseCSSVarId(column.id)}-size)` } 0 auto`; } else if (layoutMode === 'grid-no-grow') { widthStyles.flex = `${+(columnDef.grow || 0)} 0 auto`; } const isDraggingColumn = draggingColumn?.id === column.id; const isHoveredColumn = hoveredColumn?.id === column.id; const isColumnPinned = enableColumnPinning && columnDef.columnDefType !== 'group' && column.getIsPinned(); const isEditable = !cell.getIsPlaceholder() && parseFromValuesOrFunc(enableEditing, row) && parseFromValuesOrFunc(columnDef.enableEditing, row) !== false; const isEditing = isEditable && !['custom', 'modal'].includes(editDisplayMode as string) && (editDisplayMode === 'table' || editingRow?.id === row.id || editingCell?.id === cell.id) && !row.getIsGrouped(); const isCreating = isEditable && createDisplayMode === 'row' && creatingRow?.id === row.id; const showClickToCopyButton = parseFromValuesOrFunc(enableClickToCopy, cell) || (parseFromValuesOrFunc(columnDef.enableClickToCopy, cell) && parseFromValuesOrFunc(columnDef.enableClickToCopy, cell) !== false); const handleDoubleClick = (event: MouseEvent) => { tableCellProps?.onDoubleClick?.(event); if (isEditable && editDisplayMode === 'cell') { setEditingCell(cell); setTimeout(() => { const textField = editInputRefs.current[cell.id]; if (textField) { textField.focus(); textField.select?.(); } }, 100); } }; 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 cellValueProps = { cell, renderedColumnIndex, renderedRowIndex, table, }; const cellHoverRevealDivRef = useRef(null); const [isCellContentOverflowing, setIsCellContentOverflowing] = useState(false); const onMouseEnter = () => { if (!columnDef.enableCellHoverReveal) return; const div = cellHoverRevealDivRef.current; if (div) { const isOverflow = div.scrollWidth > div.clientWidth; setIsCellContentOverflowing(isOverflow); } }; const onMouseLeave = () => { if (!columnDef.enableCellHoverReveal) return; setIsCellContentOverflowing(false); }; const renderCellContent = () => { if (cell.getIsPlaceholder()) { return columnDef.PlaceholderCell?.({ cell, column, row, table }) ?? null; } if (showSkeletons !== false && (isLoading || showSkeletons)) { return ; } if ( columnDefType === 'display' && (['mrt-row-expand', 'mrt-row-numbers', 'mrt-row-select'].includes( column.id, ) || !row.getIsGrouped()) ) { return columnDef.Cell?.({ column, renderedCellValue: cell.renderValue() as any, row, rowRef, ...cellValueProps, }); } if (isCreating || isEditing) { return ; } if (showClickToCopyButton && columnDef.enableClickToCopy !== false) { return ( ); } return ; }; return ( ({ ...widthStyles, ...parseFromValuesOrFunc(tableCellProps.style, theme), })} > <> {tableCellProps.children ?? (columnDef.enableCellHoverReveal ? (
{renderCellContent()} {cell.getIsGrouped() && !columnDef.GroupedCell && ( <> ({row.subRows?.length}) )}
) : ( <> {renderCellContent()} {cell.getIsGrouped() && !columnDef.GroupedCell && ( <> ({row.subRows?.length}) )} ))}
); }; export const Memo_MRT_TableBodyCell = memo( MRT_TableBodyCell, (prev, next) => next.cell === prev.cell, ) as typeof MRT_TableBodyCell;