fix(table): sticky header & content placement in virtualized table (#5184)

* fix(table): header sticky in virtualized table + topContent and bottom content placement fix

* fix(table): header sticky in virtualized table PR return

* chore(changeset): revise changeset msg

---------

Co-authored-by: աӄա <wingkwong.code@gmail.com>
This commit is contained in:
Hova25 2025-04-19 17:27:05 +02:00 committed by GitHub
parent 531518f2ef
commit 8bd878341e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 73 additions and 56 deletions

View File

@ -0,0 +1,5 @@
---
"@heroui/table": patch
---
fix(table): Header sticky in virtualized table + topContent and bottom content placement fix in virtualized table (#5149)

View File

@ -81,71 +81,75 @@ const VirtualizedTable = forwardRef<"table", TableProps>((props, ref) => {
overscan: 5, overscan: 5,
}); });
const tableProps = getTableProps();
return ( return (
<div {...getBaseProps()}> <div {...getBaseProps()}>
{/* We need to add p-1 to make the shadow-sm visible */} {/* We need to add p-1 to make the shadow-sm visible */}
{topContentPlacement === "outside" && topContent}
<Wrapper> <Wrapper>
<> <>
{topContentPlacement === "outside" && topContent} {topContentPlacement === "inside" && topContent}
<div style={{height: `calc(${rowVirtualizer.getTotalSize() + headerHeight}px)`}}> <Component
<> {...tableProps}
{topContentPlacement === "inside" && topContent} style={{
<Component {...getTableProps()}> height: `calc(${rowVirtualizer.getTotalSize() + headerHeight}px)`,
<TableRowGroup ref={headerRef} classNames={values.classNames} slots={values.slots}> ...tableProps.style,
{collection.headerRows.map((headerRow) => ( }}
<TableHeaderRow >
key={headerRow?.key} <TableRowGroup ref={headerRef} classNames={values.classNames} slots={values.slots}>
classNames={values.classNames} {collection.headerRows.map((headerRow) => (
node={headerRow} <TableHeaderRow
slots={values.slots} key={headerRow?.key}
state={values.state}
>
{[...headerRow.childNodes].map((column) =>
column?.props?.isSelectionCell ? (
<TableSelectAllCheckbox
key={column?.key}
checkboxesProps={values.checkboxesProps}
classNames={values.classNames}
color={values.color}
disableAnimation={values.disableAnimation}
node={column}
selectionMode={values.selectionMode}
slots={values.slots}
state={values.state}
/>
) : (
<TableColumnHeader
key={column?.key}
classNames={values.classNames}
node={column}
slots={values.slots}
state={values.state}
/>
),
)}
</TableHeaderRow>
))}
<Spacer as="tr" tabIndex={-1} y={1} />
</TableRowGroup>
<VirtualizedTableBody
checkboxesProps={values.checkboxesProps}
classNames={values.classNames} classNames={values.classNames}
collection={values.collection} node={headerRow}
color={values.color}
disableAnimation={values.disableAnimation}
isSelectable={values.isSelectable}
rowVirtualizer={rowVirtualizer}
selectionMode={values.selectionMode}
slots={values.slots} slots={values.slots}
state={values.state} state={values.state}
/> >
</Component> {[...headerRow.childNodes].map((column) =>
{bottomContentPlacement === "inside" && bottomContent} column?.props?.isSelectionCell ? (
</> <TableSelectAllCheckbox
</div> key={column?.key}
{bottomContentPlacement === "outside" && bottomContent} checkboxesProps={values.checkboxesProps}
classNames={values.classNames}
color={values.color}
disableAnimation={values.disableAnimation}
node={column}
selectionMode={values.selectionMode}
slots={values.slots}
state={values.state}
/>
) : (
<TableColumnHeader
key={column?.key}
classNames={values.classNames}
node={column}
slots={values.slots}
state={values.state}
/>
),
)}
</TableHeaderRow>
))}
<Spacer as="tr" tabIndex={-1} y={1} />
</TableRowGroup>
<VirtualizedTableBody
checkboxesProps={values.checkboxesProps}
classNames={values.classNames}
collection={values.collection}
color={values.color}
disableAnimation={values.disableAnimation}
isSelectable={values.isSelectable}
rowVirtualizer={rowVirtualizer}
selectionMode={values.selectionMode}
slots={values.slots}
state={values.state}
/>
</Component>
{bottomContentPlacement === "inside" && bottomContent}
</> </>
</Wrapper> </Wrapper>
{bottomContentPlacement === "outside" && bottomContent}
</div> </div>
); );
}); });

View File

@ -1160,6 +1160,14 @@ export const Virtualized = {
}, },
}; };
export const VirtualizedWithHeaderSticky = {
...Virtualized,
args: {
...Virtualized.args,
isHeaderSticky: true,
},
};
export const TenThousandRows = { export const TenThousandRows = {
render: VirtualizedTemplate, render: VirtualizedTemplate,
args: { args: {

View File

@ -55,7 +55,7 @@ const table = tv({
], ],
table: "min-w-full h-auto", table: "min-w-full h-auto",
thead: "[&>tr]:first:rounded-lg", thead: "[&>tr]:first:rounded-lg",
tbody: "", tbody: "after:block",
tr: ["group/tr", "outline-none", ...dataFocusVisibleClasses], tr: ["group/tr", "outline-none", ...dataFocusVisibleClasses],
th: [ th: [
"group/th", "group/th",