import React from 'react'; import ColumnResizer from './ColumnResizer'; import { columnPinningDefault } from '../constants/columnConfig'; const getPinnedColumnIds = (table) => (table.getState().columnPinning.left || []).filter(Boolean); const getSortedColumns = (table) => { const allLeafColumns = table.getAllLeafColumns(); const pinnedColumns = getPinnedColumnIds(table); return [...allLeafColumns].sort((a, b) => { const aIsPinned = pinnedColumns.includes(a.id); const bIsPinned = pinnedColumns.includes(b.id); if (aIsPinned && bIsPinned) { return pinnedColumns.indexOf(a.id) - pinnedColumns.indexOf(b.id); } if (aIsPinned) return -1; if (bIsPinned) return 1; return 0; }); }; const getColumnId = (header) => { //в header.id содержаться колонки по типу depth_idColParent_idChild, а в pinningColumn только idCol, которые закреплены //header.id.split('_').at(-1) - получение самой нижней колонки let columnId = header?.id?.match(/data\..*$/)?.[0] || header?.id || header; columnPinningDefault.left.forEach(colPinned => { if (columnId.includes(colPinned)) { columnId = colPinned; } }) return columnId; } // Ширина закреплённых дочерних колонок группового заголовка const getPinnedColumnsWidth = (header, pinningColumn) => { let width = 0; for (const leaf of header.getLeafHeaders()) { if (pinningColumn.includes(leaf.column.id)) { width += leaf.column.getSize(); } else { break; } } return width; }; const calculateLeftOffset = (table, columnOrHeader) => { const columnId = getColumnId(columnOrHeader); const pinnedColumns = getPinnedColumnIds(table); const currentPinnedIndex = pinnedColumns.indexOf(columnId); let leftOffset = 0; if (currentPinnedIndex > 0) { for (let i = 0; i < currentPinnedIndex; i++) { const prevColumn = table.getColumn(pinnedColumns[i]); if (prevColumn) { leftOffset += prevColumn.getSize(); } } } return leftOffset; }; const isColumnPin = (table, header) => { const pinningColumn = getPinnedColumnIds(table); const columnId = getColumnId(header); const isPinned = pinningColumn.includes(columnId); return isPinned; }; const ColumnNumbersRow = ({ table, selectedColumnId, onColumnSelect }) => { const sortedColumns = getSortedColumns(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 = isColumnPin(table, column); const allLeafColumns = table.getAllLeafColumns(); const originalIndex = allLeafColumns.findIndex((col) => col.id === column.id); const leftOffset = calculateLeftOffset(table, column); 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: isPinned ? leftOffset : 'auto', zIndex: selected ? 16 : isPinned ? 20 : 'auto', cursor: 'pointer', userSelect: 'none', }} > {originalIndex}
); }; const HeaderCell = ({ header, table, onClick, onChangeWidth }) => { const column = header.column; const pinningColumn = getPinnedColumnIds(table); const isGroup = header.subHeaders?.length > 0; const isPinned = isColumnPin(table, header); const leftOffset = calculateLeftOffset(table, header); const headerSize = header.getSize(); const pinnedBandWidth = isGroup ? getPinnedColumnsWidth(header, pinningColumn) : 0; const splitGroup = isGroup && isPinned && pinnedBandWidth > 0 && pinnedBandWidth < headerSize; const customBgColor = column.columnDef?.muiTableHeadCellProps?.sx?.backgroundColor; const color = column.columnDef?.muiTableHeadCellProps?.sx?.color || '#000000'; const backgroundColor = customBgColor ? customBgColor : 'rgba(243, 244, 246, 1)'; const handleChangeWidth = (deltaWidth) => { const size = header.getSize(); onChangeWidth(header, size + deltaWidth); }; // Вычисляем отступ для текста внутри ячейки // Если колонка закреплена, текст должен быть привязан к левому краю ячейки // Если колонка не закреплена, текст должен начинаться после всех закрепленных колонок const getTextLeftOffset = () => { if (isPinned) { return '0'; } // Для незакрепленных колонок текст должен начинаться после всех закрепленных // Получаем общую ширину всех закрепленных колонок const pinnedColumns = getPinnedColumnIds(table); let totalPinnedWidth = 0; for (const pinnedId of pinnedColumns) { const pinnedCol = table.getColumn(pinnedId); if (pinnedCol) { totalPinnedWidth += pinnedCol.getSize(); } } return `${totalPinnedWidth}px`; }; const renderCell = (width, textLeft) => (
onClick(header)} style={{ width: width + 'px', minWidth: width + 'px', maxWidth: width + 'px', 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 : column.columnDef.header}
); if (splitGroup) { return ( <>
{renderCell(pinnedBandWidth, '0')}
{renderCell(headerSize - pinnedBandWidth, getTextLeftOffset(true))}
); } return (
{renderCell(headerSize, getTextLeftOffset())}
); }; const FilterRow = ({ table }) => { const sortedColumns = getSortedColumns(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, table }) => { const isPinned = isColumnPin(table, column); const leftOffset = calculateLeftOffset(table, 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 (
column.setFilterValue([e.target.value, max])} style={{ width: '50%', padding: '4px', fontSize: '11px' }} /> column.setFilterValue([min, e.target.value])} style={{ width: '50%', padding: '4px', fontSize: '11px' }} />
); } default: // 'text' return ( handleFilterChange(e.target.value)} placeholder="Поиск..." style={{ width: '100%', padding: '4px 6px', fontSize: '11px', border: '1px solid #ddd', borderRadius: '4px', boxSizing: 'border-box', }} /> ); } }; return (
{renderFilterInput()}
); }; export const TableHead = ({ table, selectedColumnId, onColumnSelect, onChangeWidth, isLoadingData, }) => { const handleHeaderClick = (header) => { if (!header.subHeaders?.length && header.column?.id) { onColumnSelect?.(header.column.id); } }; if (!isLoadingData) { return
} return ( <>
theme.zIndex.table, position: 'relative', width: 'max-content', }} > {/* Строка с номерами колонок */} {table.getState().showColumnFilters && ( )} {/* Существующие строки заголовков */} {table.getHeaderGroups().map((headerGroup) => (
{headerGroup.headers.map((header) => ( ))}
))}
); };