import React, { useMemo } from 'react'; import ColumnResizer from './ColumnResizer'; import { formatHeaderYear } from '../utils/headerYearUtils'; import { getOrderedVisibleColumns } from '../utils/columnLayoutUtils'; import { endColumnResize, startColumnResize } from '../utils/columnResizeInfo'; const getColumnPinnedStyles = (column) => { const pinningPosition = column.getIsPinned(); return { isPinned: Boolean(pinningPosition), left: pinningPosition === 'left' ? column.getStart('left') : undefined, right: pinningPosition === 'right' ? column.getAfter('right') : undefined, }; }; const ColumnNumbersRow = ({ table, selectedColumnId, onColumnSelect }) => { const sortedColumns = getOrderedVisibleColumns(table); const tableState = table.getState(); const filteredColumns = sortedColumns.filter( (col) => !(col.id in tableState.columnVisibility) || (col.id in tableState.columnVisibility && tableState.columnVisibility[col.id]), ); return (
theme.zIndex.table + 1, backgroundColor: 'white', }}> {filteredColumns.map((column) => ( ))}
); }; const ColumnNumberCell = ({ column, table, selectedColumnId, onColumnSelect }) => { const { isPinned, left, right } = getColumnPinnedStyles(column); const allLeafColumns = table.getAllLeafColumns(); const originalIndex = allLeafColumns.findIndex((col) => col.id === column.id); const selected = selectedColumnId === column.id; return (
{ e.stopPropagation(); onColumnSelect?.(column.id); }} onMouseDown={(e) => e.stopPropagation()} style={{ width: column.getSize(), minWidth: column.getSize(), maxWidth: column.getSize(), padding: '2px 6px', textAlign: 'center', backgroundColor: selected ? '#589A2F' : '#FBFBFB', color: selected ? '#ffffff' : '#5E5E5E', boxSizing: 'border-box', display: 'flex', alignItems: 'center', justifyContent: 'center', height: '1.5rem', border: '.063rem solid rgba(209, 213, 220, 1)', borderBottom: 'none', position: isPinned ? 'sticky' : 'relative', left, right, zIndex: selected ? 16 : isPinned ? 20 : 'auto', cursor: 'pointer', userSelect: 'none', }}> {originalIndex}
); }; const getColorBrightness = (hexColor) => { if (!hexColor) return 255; const color = hexColor.replace('#', ''); let r, g, b; if (color.length === 3) { r = Number.parseInt(color[0] + color[0], 16); g = Number.parseInt(color[1] + color[1], 16); b = Number.parseInt(color[2] + color[2], 16); } else if (color.length === 6) { r = Number.parseInt(color.substring(0, 2), 16); g = Number.parseInt(color.substring(2, 4), 16); b = Number.parseInt(color.substring(4, 6), 16); } else { return 255; } return 0.299 * r + 0.587 * g + 0.114 * b; }; const HeaderCell = ({ header, table, pinningPosition, onClick, onChangeWidth, budgetYear }) => { const column = header.column; const headerSize = header.getSize(); const isPinned = Boolean(pinningPosition); const leftOffset = pinningPosition === 'left' ? header.getStart() : undefined; const rightOffset = pinningPosition === 'right' ? table.getRightTotalSize() - header.getStart() - headerSize : undefined; const stickyTextLeft = isPinned ? undefined : table.getLeftTotalSize(); const stickyTextRight = isPinned ? undefined : table.getRightTotalSize(); const customBgColor = column.columnDef?.muiTableHeadCellProps?.sx?.backgroundColor; const backgroundColor = customBgColor ? customBgColor : 'rgba(243, 244, 246, 1)'; const color = useMemo(() => { const brightness = getColorBrightness(backgroundColor); return brightness < 128 ? '#ffffff' : '#000000'; }, [backgroundColor]); const handleChangeWidth = (deltaWidth) => { const size = header.getSize(); onChangeWidth(header, size + deltaWidth); }; return (
onClick(header)} style={{ width: '100%', minWidth: '100%', maxWidth: '100%', position: 'relative', padding: '4px 6px', textAlign: 'center', fontWeight: 600, fontSize: '12px', lineHeight: 1, backgroundColor: backgroundColor, color: '#424242', boxSizing: 'border-box', display: 'flex', alignItems: 'center', justifyContent: 'center', height: '3rem', border: '.063rem solid rgba(209, 213, 220, 1)', }}>
{header.isPlaceholder ? null : formatHeaderYear(column.columnDef.header, budgetYear)}
table.setColumnSizingInfo(startColumnResize(column.id))} onDragEnd={() => table.setColumnSizingInfo(endColumnResize)} />
); }; const FilterRow = ({ table }) => { const sortedColumns = getOrderedVisibleColumns(table); const tableState = table.getState(); const filteredColumns = sortedColumns.filter( (col) => !(col.id in tableState.columnVisibility) || (col.id in tableState.columnVisibility && tableState.columnVisibility[col.id]), ); return (
{filteredColumns.map((column) => ( ))}
); }; // Компонент ячейки фильтра const FilterCell = ({ column }) => { const { isPinned, left, right } = getColumnPinnedStyles(column); // Определяем тип фильтра на основе колонки const filterVariant = column.columnDef.filterVariant || 'text'; // Для select фильтра const selectOptions = column.columnDef.filterSelectOptions || []; const handleFilterChange = (value) => { column.setFilterValue(value); }; // Рендер разных типов фильтров const renderFilterInput = () => { switch (filterVariant) { case 'select': return ( ); case 'range': { const [min, max] = column.getFilterValue() || ['', '']; return (
{' '} {/* 4px */} column.setFilterValue([e.target.value, max])} style={{ width: '50%', padding: '0.25rem', fontSize: '0.6875rem', }} /> column.setFilterValue([min, e.target.value])} style={{ width: '50%', padding: '0.25rem', fontSize: '0.6875rem', }} />
); } default: // 'text' return ( handleFilterChange(e.target.value)} placeholder='Поиск...' style={{ width: '100%', padding: '0.25rem 0.375rem', fontSize: '0.6875rem', border: '1px solid #ddd', borderRadius: '0.25rem', boxSizing: 'border-box', }} /> ); } }; return (
{renderFilterInput()}
); }; const TableHeadComponent = ({ table, selectedColumnId, onColumnSelect, onChangeWidth, isLoadingData, budgetYear }) => { const handleHeaderClick = (header) => { if (!header.subHeaders?.length && header.column?.id) { onColumnSelect?.(header.column.id); } }; if (!isLoadingData) { return
; } const headerSections = [ { groups: table.getLeftHeaderGroups(), pinningPosition: 'left' }, { groups: table.getCenterHeaderGroups(), pinningPosition: undefined }, { groups: table.getRightHeaderGroups(), pinningPosition: 'right' }, ]; const headerRowsCount = Math.max(...headerSections.map(({ groups }) => groups.length)); return ( <>
theme.zIndex.table, position: 'relative', width: 'max-content', }}> {/* Строка с номерами колонок */} {table.getState().showColumnFilters && } {/* Pinned и обычные заголовки строятся отдельными группами. */} {Array.from({ length: headerRowsCount }, (_, rowIndex) => (
{headerSections.flatMap(({ groups, pinningPosition }) => (groups[rowIndex]?.headers || []).map((header) => ( )), )}
))}
); }; export const TableHead = React.memo(TableHeadComponent);