This commit is contained in:
seb
2026-10-11 13:44:39 +02:00
parent 8325402bc6
commit c34f86f762

View File

@@ -15,6 +15,9 @@ import { alpha } from '@mui/material/styles';
* @prop {Array} items - Wheel flips pages.
* @prop {React.ComponentType|Function} renderItem - Row renderer (component or callback).
* @prop {number} [minPageSize=10] - Rows per page.
* @prop {number} [maxPageSize] - Maximum rows per page.
* @prop {number} [totalCount] - Total items in unfiltered dataset. When unfiltered count <= pageSize, pagination header is removed entirely. When filtering, the header height is preserved to prevent layout shift.
* @prop {number} [unfilteredCount] - Alias for totalCount.
* @prop {(page: number) => void} [onPageChange] - Called on page change.
* @prop {boolean} [debug=false] - Dashed outline for layout debugging.
* @prop {object} [sx] - Extra styles.
@@ -68,6 +71,13 @@ class FillSpace extends Component {
this.wheelAcc = 0;
this.observer = null;
this.raf = null;
const explicitCount = props.unfilteredCount ?? props.totalCount;
this.maxSeenCount =
explicitCount != null
? Number(explicitCount)
: Array.isArray(props.items)
? props.items.length
: 0;
}
componentDidMount() {
@@ -99,6 +109,12 @@ class FillSpace extends Component {
this._observe();
this._viewportMeasure();
}
const explicitCount = this.props.unfilteredCount ?? this.props.totalCount;
if (explicitCount != null) {
this.maxSeenCount = Number(explicitCount);
} else if (Array.isArray(this.props.items)) {
this.maxSeenCount = Math.max(this.maxSeenCount || 0, this.props.items.length);
}
// Clamp current page when the item list shrinks (e.g. brand deleted).
if (
Array.isArray(this.props.items) &&
@@ -265,6 +281,8 @@ class FillSpace extends Component {
maxHeight,
minPageSize,
maxPageSize,
totalCount,
unfilteredCount,
listRef,
onPageChange,
onWheel,
@@ -281,21 +299,26 @@ class FillSpace extends Component {
const { pageSize, currentPage, viewportHeight } = this.state;
const list = Array.isArray(items) ? items : [];
const total = list.length;
const fullTotal = unfilteredCount ?? totalCount ?? Math.max(this.maxSeenCount || 0, total);
const needsPaginationContainer = fullTotal > pageSize;
const showPaginationControls = total > pageSize;
const pageCount = Math.max(1, Math.ceil(total / Math.max(1, pageSize)));
const safePage = Math.min(Math.max(1, currentPage), pageCount);
const offset = (safePage - 1) * pageSize;
const pageItems = list.slice(offset, offset + pageSize);
const showPagination = total > pageSize;
// Hidden data-free probe: measures `renderItem` itself (no data).
const probe = renderProbeElement(renderItem);
return (
<>
{showPagination ? (
{needsPaginationContainer ? (
<Box
// Fixed single-line bar: the header height must not depend on the
// filtered count so the content never shifts when filtering.
sx={{ mb: 1, height: 44, overflow: 'hidden' }}
>
<Box
aria-hidden={!showPaginationControls || undefined}
sx={{
display: 'flex',
justifyContent: 'space-between',
@@ -304,6 +327,8 @@ class FillSpace extends Component {
gap: 1,
width: '100%',
height: 44,
visibility: showPaginationControls ? 'visible' : 'hidden',
pointerEvents: showPaginationControls ? 'auto' : 'none',
}}
>
<Typography