Привет всем, мне нужно сделать таблицу с шапкой и боди, в таблице может быть сколько угодно колонок, жестко задавать количество колонок нельзя, ячейки не должны быть фиксированного размера, столбцы шапки и столбцы боди должны соответствовать друг другу, самое главное необходимо добавить вертикальный скролл на боди, что бы его можно было прокрутить. Так же шапка должна иметь поведение stiky. К сожалению не могу использовать библиотеки, т.к. нельзя попросту(
Вот такой код на данный момент имею, таблица выглядит как нужно, но скролл для боди у меня задать не получается.
<div
className={cn(
styles.wrapper,
{ [styles.whiteBlueMode]: whiteBlueMode },
[className],
)}
>
{HeaderContent}
<div className={styles.tableContainer}>
{tableTopRef && <div ref={tableTopRef} style={{ height: 0, width: '100%' }} />}
<section className={styles.table} style={{ gridTemplateColumns }}>
<div className={styles.header}>
<div className={styles.headerRow}>
{generatedColumns.map((column) => (
<div key={column.key} className={styles.column} style={getColumnWidthStyles(column)}>
<div className={styles.columnContent}>
{column.title}
{column.hasSorter && (
<SortButton
value={
sortModel?.field === column.key ? sortModel?.sort : null
}
onChange={(newValue) =>
handleSortChange({ sort: newValue, field: column.key })
}
/>
)}
</div>
</div>
))}
</div>
</div>
<div className={styles.body} style={{ overflowY: 'auto' }}>
{currentData.map((row, rowIndex) => (
<div
key={rowIndex}
className={cn(styles.row, { [styles.selected]: selectedRows.has(row.id) })}
ref={getRefForIndex?.(rowIndex, data.length)}
>
{generatedColumns.map((column) => (
<div
key={column.key}
className={styles.cell}
role="button"
tabIndex={0}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
column.onClick?.(row);
}
}}
onClick={(e) => {
e.stopPropagation();
column.onClick?.(row);
}}
>
{column.hasCheckbox && (
<Checkbox
checked={selectedRows.has(row.id)}
onChange={() => handleCheckboxChange(row.id)}
binaryCheckbox
/>
)}
{column.icon && <Icon icon={column.icon} size={'sm'} />}
{column.render ? column.render(row, rowIndex) : row[column.key]}
{column.buttons && (
<div className={styles.buttons}>
{column.buttons.map((button, index) => (
<Button
key={index}
{...button}
onClick={(e) => {
e.stopPropagation();
button.onClick(row);
}}
link={button.link?.(row)}
size={button.size || 'm'}
mode={button.mode || 'icon'}
/>
))}
</div>
)}
</div>
))}
</div>
))}
{loadMoreRef && <div ref={loadMoreRef} style={{ height: '20px' }} />}
</div>
</section>
</div>
$header-background-color: var(--white);
$body-background-color: var(--white);
$row-background-color-hover: var(--abbey50);
$row-background-color-selected: var(--abbey50);
$row-background-color-selected-hover: var(--abbey100);
$border-color: var(--abbey100);
$header-color: var(--blue100);
$header-color-text: var(--abbey400);
$row-color-text: var(--abbey900);
.wrapper {
display: flex;
flex-direction: column;
max-width: 100%;
width: max-content;
border: 1px solid $border-color;
border-radius: 16px;
padding: 24px;
overflow: hidden;
max-height: 100%;
&.whiteBlueMode {
border: none;
padding: 0;
border-radius: 0;
}
}
.tableContainer {
.table {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(120px, max-content));
overflow-x: auto;
width: 100%;
margin-bottom: 0;
.header, .body {
display: contents;
}
.headerRow, .row {
display: contents;
}
.column, .cell {
padding: 18px 16px;
box-sizing: border-box;
}
.header {
.headerRow {
.column {
position: sticky;
top: 0;
font: var(--font-s);
background: $header-background-color;
color: $header-color-text;
font-weight: bold;
z-index: 10;
box-shadow: 0 1px 0 0 $border-color;
&:first-of-type {
border-top-left-radius: 16px;
}
&:last-of-type {
border-top-right-radius: 16px;
}
.columnContent {
display: flex;
align-items: center;
gap: 8px;
}
}
}
}
.body {
overflow-y: auto;
.row {
.cell {
padding: 24px 16px;
font: var(--font-m);
font-weight: 400;
color: $row-color-text;
border-bottom: 1px solid $border-color;
word-break: break-word;
&:last-of-type {
border-bottom-right-radius: 0;
border-top-right-radius: 0;
}
.buttons {
display: flex;
gap: 8px;
}
}
&:last-child .cell {
border-bottom: none;
}
&.selected .cell {
background: $row-background-color-selected;
}
&:hover .cell {
background: $row-background-color-hover;
}
}
}
}
}
.wrapper.whiteBlueMode {
.tableContainer {
border-radius: 16px;
border: 1px solid var(--abbey100);
.headerRow .column {
background-color: var(--blue100);
padding-left: 32px;
border: none;
&:first-of-type {
border-top-left-radius: 16px;
}
}
.body .row {
&:nth-child(even) .cell {
background-color: var(--blue100);
}
.cell {
padding-left: 32px;
padding-right: 32px;
}
}
}
}
.pagination {
margin-top: 16px;
}
.footer {
display: flex;
justify-content: space-between;
align-items: end;
margin-top: 8px;
}
.noData {
font: var(--font-m);
text-align: center;
font-weight: 400;
padding: 8px;
color: $row-color-text;
}