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) => (
))}
))}
>
);
};