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);