From c7b6af0b4349d1d5812823e51612de13b94e75ff Mon Sep 17 00:00:00 2001 From: seb Date: Tue, 13 Oct 2026 23:20:59 +0200 Subject: [PATCH] u --- src/FillSpace.jsx | 70 +++++++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 33 deletions(-) diff --git a/src/FillSpace.jsx b/src/FillSpace.jsx index db38d9b..260888b 100644 --- a/src/FillSpace.jsx +++ b/src/FillSpace.jsx @@ -17,8 +17,9 @@ import { alpha } from '@mui/material/styles'; * @prop {React.ReactNode} [header] - Rendered below pagination controls and above the list items. * @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 {number} [totalCount] - Total items in current dataset (or filtered dataset when paginating). + * @prop {number} [unfilteredTotal] - Total items in unfiltered dataset. When unfilteredTotal > pageSize, pagination space is reserved even if current filtered count <= pageSize, preventing layout shift. + * @prop {number} [unfilteredCount] - Alias for unfilteredTotal. * @prop {(page: number) => void} [onPageChange] - Called on page change. * @prop {boolean} [virtualScrollbar=false] - Shows a synchronized scrollbar alongside pagination controls. * @prop {boolean} [reserveScrollbar] - Reserves raceway space even when items do not overflow (defaults to true when virtualScrollbar is true). @@ -84,13 +85,15 @@ class FillSpace extends Component { this.raf = null; this.dragStartY = 0; this.dragStartThumbTop = 0; - const explicitCount = props.unfilteredCount ?? props.totalCount; - this.maxSeenCount = - explicitCount != null - ? Number(explicitCount) + const rawUnfiltered = props.unfilteredTotal ?? props.unfilteredCount; + const unfilteredTotal = rawUnfiltered != null ? Number(rawUnfiltered) : null; + const total = + props.totalCount != null + ? Number(props.totalCount) : Array.isArray(props.items) ? props.items.length : 0; + this.maxSeenCount = unfilteredTotal != null ? unfilteredTotal : total; } componentDidMount() { @@ -123,20 +126,24 @@ 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); + const rawUnfiltered = this.props.unfilteredTotal ?? this.props.unfilteredCount; + const unfilteredTotal = rawUnfiltered != null ? Number(rawUnfiltered) : null; + const total = + this.props.totalCount != null + ? Number(this.props.totalCount) + : Array.isArray(this.props.items) + ? this.props.items.length + : 0; + if (unfilteredTotal != null) { + this.maxSeenCount = unfilteredTotal; + } else { + this.maxSeenCount = Math.max(this.maxSeenCount || 0, total); } // Clamp current page when the item list shrinks (e.g. brand deleted). if ( Array.isArray(this.props.items) && this.props.items !== prevProps.items ) { - const explicitTotal = this.props.unfilteredCount ?? this.props.totalCount; - const total = - explicitTotal != null ? Number(explicitTotal) : this.props.items.length; const pageCount = Math.max( 1, Math.ceil(total / Math.max(1, this.state.pageSize)), @@ -173,10 +180,9 @@ class FillSpace extends Component { _onThumbMouseDown = (e) => { e.preventDefault(); e.stopPropagation(); - const { totalCount, unfilteredCount, items } = this.props; + const { totalCount, items } = this.props; const { viewportHeight, pageSize, currentPage } = this.state; - const explicitTotal = unfilteredCount ?? totalCount; - const total = explicitTotal != null ? Number(explicitTotal) : (items?.length || 0); + const total = totalCount != null ? Number(totalCount) : (items?.length || 0); const pageCount = Math.max(1, Math.ceil(total / Math.max(1, pageSize))); const racewayBottom = Math.max(0, Number(this.props.racewayMarginBottom ?? RACEWAY_BOTTOM_MARGIN) || 0); const trackHeight = Math.max(0, (viewportHeight || 0) - racewayBottom); @@ -195,10 +201,9 @@ class FillSpace extends Component { _onThumbDragMove = (e) => { if (!this.state.isDragging) return; - const { totalCount, unfilteredCount, items } = this.props; + const { totalCount, items } = this.props; const { viewportHeight, pageSize } = this.state; - const explicitTotal = unfilteredCount ?? totalCount; - const total = explicitTotal != null ? Number(explicitTotal) : (items?.length || 0); + const total = totalCount != null ? Number(totalCount) : (items?.length || 0); const pageCount = Math.max(1, Math.ceil(total / Math.max(1, pageSize))); if (pageCount <= 1) return; @@ -232,9 +237,8 @@ class FillSpace extends Component { const trackRect = this.trackRef.getBoundingClientRect(); const clickY = e.clientY - trackRect.top; const { viewportHeight, pageSize } = this.state; - const { items, totalCount, unfilteredCount } = this.props; - const explicitTotal = unfilteredCount ?? totalCount; - const total = explicitTotal != null ? Number(explicitTotal) : (items?.length || 0); + const { items, totalCount } = this.props; + const total = totalCount != null ? Number(totalCount) : (items?.length || 0); const pageCount = Math.max(1, Math.ceil(total / Math.max(1, pageSize))); if (pageCount <= 1) return; @@ -356,12 +360,11 @@ class FillSpace extends Component { } _handleWheel = (event) => { - const { items, totalCount, unfilteredCount } = this.props; + const { items, totalCount } = this.props; if (!Array.isArray(items)) return; if (this.ref && event.target && !this.ref.contains(event.target)) return; const { pageSize, currentPage } = this.state; - const explicitTotal = unfilteredCount ?? totalCount; - const total = explicitTotal != null ? Number(explicitTotal) : items.length; + const total = totalCount != null ? Number(totalCount) : items.length; const pageCount = Math.max(1, Math.ceil(total / Math.max(1, pageSize))); const delta = event.deltaY || 0; if (pageCount <= 1) return; @@ -380,11 +383,10 @@ class FillSpace extends Component { }; _goToPage = (page) => { - const { items, onPageChange, totalCount, unfilteredCount } = this.props; - const explicitTotal = unfilteredCount ?? totalCount; + const { items, onPageChange, totalCount } = this.props; const total = - explicitTotal != null - ? Number(explicitTotal) + totalCount != null + ? Number(totalCount) : Array.isArray(items) ? items.length : 0; @@ -408,6 +410,7 @@ class FillSpace extends Component { maxPageSize, totalCount, unfilteredCount, + unfilteredTotal: propUnfilteredTotal, listRef, onPageChange, onWheel, @@ -428,9 +431,10 @@ class FillSpace extends Component { // type or callback). `items` may be undefined while data is loading. const { pageSize, currentPage, viewportHeight } = this.state; const list = Array.isArray(items) ? items : []; - const explicitTotal = unfilteredCount ?? totalCount; - const total = explicitTotal != null ? Number(explicitTotal) : list.length; - const fullTotal = Math.max(this.maxSeenCount || 0, total); + const total = totalCount != null ? Number(totalCount) : list.length; + const rawUnfiltered = propUnfilteredTotal ?? unfilteredCount; + const unfilteredTotal = rawUnfiltered != null ? Number(rawUnfiltered) : null; + const fullTotal = unfilteredTotal != null ? unfilteredTotal : 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)));