diff --git a/src/FillSpace.jsx b/src/FillSpace.jsx
index 5262e0e..67cb1c1 100644
--- a/src/FillSpace.jsx
+++ b/src/FillSpace.jsx
@@ -6,7 +6,7 @@ import Typography from '@mui/material/Typography';
import { alpha } from '@mui/material/styles';
/**
- * FillSpace — fills remaining viewport height; optionally paginates `items`.
+ * FillSpace — fills remaining viewport height; paginates `items`.
*
* @component FillSpace
* @import dashgui/FillSpace
@@ -15,26 +15,21 @@ import { alpha } from '@mui/material/styles';
* @prop {React.Ref} [aboveRef] - Element(s) above; re-measure when they resize.
* @prop {React.Ref|React.Ref[]} [belowRef] - Footer element(s) below; their height is reserved.
* @prop {number} [reserveBottom=0] - Extra px to reserve below the spacer.
- * @prop {ReactNode} [measureElement] - Legacy mode: hidden sample row, repeated to fill leftover space.
- * @prop {number} [measureGap=0] - Gap (px) between repeated units.
- * @prop {(count: number) => void} [onMeasurement] - Legacy mode: called with fitting unit count.
* @prop {number} [minHeight=0] - Minimum height in px.
- * @prop {number} [minCount=0] - Minimum units that must fit (page scrolls instead of clipping).
- * @prop {Array} [items] - Paginated mode: page size is measured from row height, wheel flips pages.
- * @prop {React.ComponentType|Function} [renderItem] - Row renderer (component or callback).
+ * @prop {Array} items - Page size is measured from row height, wheel flips pages.
+ * @prop {React.ComponentType|Function} renderItem - Row renderer (component or callback).
* @prop {number} [minPageSize=10] - Minimum rows per page.
* @prop {(page: number) => void} [onPageChange] - Called on page change.
* @prop {number} [maxHeight] - Maximum height in px.
* @prop {boolean} [debug=false] - Dashed outline for layout debugging.
- * @prop {ReactNode} [children] - Legacy mode content rendered on top.
* @prop {object} [sx] - Extra styles.
*/
/** Minimum spacer height so at least `count` units fit (lets the page scroll on small windows). */
-function minHeightFromCount(count, unitHeight, gap) {
+function minHeightFromCount(count, unitHeight) {
const n = Number(count) || 0;
if (n <= 0 || !(unitHeight > 0)) return 0;
- return n * unitHeight + Math.max(0, n - 1) * (Number(gap) || 0);
+ return n * unitHeight;
}
/** True for class components (e.g. `renderItem={ArtikelMarke}`). Rendered as element, not called. */
@@ -77,18 +72,12 @@ class FillSpace extends Component {
// first visible frame is already full-size, no 0 → expanded jump.
height: null,
unitHeight: 0,
- contentHeight: 0,
- totalCount: 0,
- fillCount: 0,
- // Paginated mode (`items`): page size comes from the fit count.
- pageSize: Number(props.minPageSize ?? props.minCount) || 10,
+ // Page size comes from the fit count.
+ pageSize: Number(props.minPageSize) || 10,
currentPage: 1,
measured: false,
};
- this.lastTotalCount = null;
this.ref = null;
- this.contentRef = null;
- this.unitRef = null;
this.probeRef = null;
this.paginationRef = null;
this.wheelAcc = 0;
@@ -112,12 +101,10 @@ class FillSpace extends Component {
prevProps.containerRef !== this.props.containerRef ||
prevProps.reserveBottom !== this.props.reserveBottom ||
prevProps.minHeight !== this.props.minHeight ||
- prevProps.minCount !== this.props.minCount ||
prevProps.minPageSize !== this.props.minPageSize ||
prevProps.maxHeight !== this.props.maxHeight ||
- prevProps.measureElement !== this.props.measureElement ||
- prevProps.measureGap !== this.props.measureGap ||
- prevProps.items !== this.props.items
+ prevProps.items !== this.props.items ||
+ prevProps.renderItem !== this.props.renderItem
) {
this._observe();
this._measure();
@@ -166,11 +153,10 @@ class FillSpace extends Component {
this.observer = new ResizeObserver(() => this._onViewportChange());
// Never observe the spacer's own parent: resizing the spacer would resize the parent.
- // Paginated mode also skips contentRef: every flip swaps all rows, which
+ // Skip content: every flip swaps all rows, which
// would fire the observer -> _measure -> setState -> render in a loop.
// Unit height is read lazily in _measure, so no observer is needed for it.
- const { aboveRef, belowRef, containerRef, items, renderItem } = this.props;
- const paginated = Array.isArray(items) && typeof renderItem === 'function';
+ const { aboveRef, belowRef, containerRef } = this.props;
const aboveList = Array.isArray(aboveRef) ? aboveRef : [aboveRef];
const belowList = Array.isArray(belowRef) ? belowRef : [belowRef];
const containerTarget = containerRef && 'current' in containerRef ? containerRef.current : containerRef;
@@ -178,7 +164,6 @@ class FillSpace extends Component {
...aboveList.map((r) => (r && 'current' in r ? r.current : r)),
...belowList.map((r) => (r && 'current' in r ? r.current : r)),
containerTarget,
- ...(paginated ? [] : [this.contentRef]),
this.paginationRef,
];
for (const el of targets) {
@@ -199,15 +184,9 @@ class FillSpace extends Component {
belowRef,
containerRef,
reserveBottom = 0,
- measureElement,
- renderItem,
- measureGap = 0,
minHeight = 0,
- minCount = 0,
minPageSize,
maxHeight,
- onMeasurement,
- items,
} = this.props;
// Spacer top, document-relative: scrolling the page does not change the height.
@@ -243,70 +222,26 @@ class FillSpace extends Component {
maxHeight ?? Infinity,
);
- let contentHeight = 0;
- if (this.contentRef) {
- contentHeight = this.contentRef.offsetHeight;
- }
-
- // Paginated: hidden probe rendered from `renderItem` itself (no data).
- // Legacy: hidden copy.
- const paginated = Array.isArray(items) && typeof renderItem === 'function';
let unitHeight = this.state.unitHeight;
- if (paginated) {
- const probeH = this.probeRef?.offsetHeight;
- if (probeH > 0) {
- unitHeight = probeH;
- } else {
- const firstRow = this.contentRef?.querySelector?.('[data-fill-row]') ?? this.contentRef?.firstElementChild;
- const h = firstRow?.offsetHeight;
- if (h > 0) unitHeight = h;
- }
- if (!(unitHeight > 0) && measureElement != null && this.unitRef) {
- const fallback = this.unitRef.offsetHeight;
- if (fallback > 0) unitHeight = fallback;
- }
- } else if (measureElement != null && this.unitRef) {
- const h = this.unitRef.offsetHeight;
- if (h > 0) unitHeight = h;
- }
- const minSize = Math.max(1, Number(minPageSize ?? minCount) || 10);
- const effectiveMinCount = paginated ? Math.max(Number(minCount) || 0, minSize) : Number(minCount) || 0;
- const minHeightForCount = minHeightFromCount(effectiveMinCount, unitHeight, measureGap);
+ const probeH = this.probeRef?.offsetHeight;
+ if (probeH > 0) unitHeight = probeH;
+ const minSize = Math.max(1, Number(minPageSize) || 10);
+ const minHeightForCount = minHeightFromCount(minSize, unitHeight);
const finalHeight = Math.min(
Math.max(clamped, minHeightForCount),
maxHeight ?? Infinity,
);
- // Fitting unit count (drives page size) and leftover fill count.
+ // Fitting unit count drives page size.
let totalCount = 0;
- let fillCount = 0;
- if (unitHeight > 0 && (measureElement != null || paginated)) {
- totalCount = Math.floor((finalHeight + measureGap) / (unitHeight + measureGap));
- const fillHeight = Math.max(0, finalHeight - contentHeight);
- fillCount = Math.floor((fillHeight + measureGap) / (unitHeight + measureGap));
+ if (unitHeight > 0) {
+ totalCount = Math.floor(finalHeight / unitHeight);
}
- // Report total fitting count once per change (legacy mode + paginated
- // debug). Paginated callers use this to display the live measurement
- // in a side tile without polling internal state.
- if (
- typeof onMeasurement === 'function' &&
- (measureElement != null || paginated) &&
- totalCount !== this.lastTotalCount
- ) {
- this.lastTotalCount = totalCount;
- onMeasurement(totalCount, {
- height: finalHeight,
- unitHeight,
- pageSize: paginated && totalCount > 0 ? Math.max(minSize, totalCount) : this.state.pageSize,
- measured: paginated ? totalCount > 0 : this.state.measured,
- });
- }
-
- // Paginated: fit count drives page size (min minPageSize).
+ // Fit count drives page size (min minPageSize).
let nextPageSize = this.state.pageSize;
let measured = this.state.measured;
- if (paginated && totalCount > 0) {
+ if (totalCount > 0) {
nextPageSize = Math.max(minSize, totalCount);
measured = true;
}
@@ -314,18 +249,12 @@ class FillSpace extends Component {
if (
Math.abs(finalHeight - this.state.height) > 0.5 ||
Math.abs(unitHeight - this.state.unitHeight) > 0.5 ||
- Math.abs(contentHeight - this.state.contentHeight) > 0.5 ||
- totalCount !== this.state.totalCount ||
- fillCount !== this.state.fillCount ||
nextPageSize !== this.state.pageSize ||
measured !== this.state.measured
) {
this.setState({
height: finalHeight,
unitHeight,
- contentHeight,
- totalCount,
- fillCount,
pageSize: nextPageSize,
measured,
});
@@ -369,136 +298,21 @@ class FillSpace extends Component {
belowRef,
containerRef,
reserveBottom,
- onMeasurement,
onWheel,
onPageChange,
minHeight = 0,
- minCount,
minPageSize,
maxHeight,
debug = false,
- children,
items,
renderItem,
- measureElement,
- measureGap = 0,
sx,
...rest
} = this.props;
- // Paginated mode: FillSpace owns page size (from the rendered row
- // height), current page, wheel-pagination and the pagination header.
- // Caller feeds `items` plus one row element via `renderItem` (component
- // type or callback).
- if (Array.isArray(items) && typeof renderItem === 'function') {
- return this.renderPaginated({
- minHeight,
- maxHeight,
- debug,
- measureGap,
- sx,
- rest,
- });
- }
-
- const { height, fillCount } = this.state;
-
- let fill = null;
- if (measureElement != null) {
- const fillItems = [];
- for (let i = 0; i < fillCount; i++) {
- fillItems.push(
-
- {measureElement}
- ,
- );
- }
- fill = (
-
- {fillItems}
-
- );
- }
-
- return (
- <>
- {
- this.ref = el;
- }}
- onWheel={onWheel}
- {...rest}
- sx={[
- {
- position: 'relative',
- height: height ?? 'auto',
- minHeight,
- ...(maxHeight != null ? { maxHeight } : {}),
- },
- debug
- ? (theme) => ({
- outline: `2px dashed ${theme.palette.primary.main}`,
- })
- : undefined,
- sx,
- ]}
- >
- {children != null ? (
- {
- this.contentRef = el;
- }}
- sx={{ width: '100%', ...(measureElement == null ? { height: '100%' } : {}) }}
- >
- {children}
-
- ) : null}
- {fill}
- {measureElement != null ? (
- {
- this.unitRef = el;
- }}
- aria-hidden
- sx={{
- position: 'absolute',
- left: 0,
- top: 0,
- width: '100%',
- visibility: 'hidden',
- pointerEvents: 'none',
- }}
- >
- {measureElement}
-
- ) : null}
-
- {debug ? (
- ({
- position: 'fixed',
- inset: 0,
- pointerEvents: 'none',
- background: alpha(theme.palette.primary.main, 0.08),
- zIndex: 9999,
- })}
- />
- ) : null}
- >
- );
- }
-
- /** Pagination header + one page of rows. Wheel over the list flips pages. */
- renderPaginated({ minHeight, maxHeight, debug, measureGap, sx, rest }) {
- const { items, renderItem, measureElement, onWheel } = this.props;
+ // FillSpace owns page size (from the rendered row height), current page,
+ // wheel-pagination and the pagination header. Caller feeds `items` plus
+ // one row element via `renderItem` (component type or callback).
const { height, pageSize, currentPage, measured } = this.state;
const total = items.length;
const pageCount = Math.max(1, Math.ceil(total / Math.max(1, pageSize)));
@@ -509,7 +323,7 @@ class FillSpace extends Component {
const showPagination = measured && total > pageSize;
// Hidden data-free probe: measures `renderItem` itself (no data), so
// the first pass already uses the final row height — one pass, no flip.
- const probe = renderProbeElement(renderItem) ?? measureElement;
+ const probe = renderProbeElement(renderItem);
return (
<>
@@ -599,12 +413,7 @@ class FillSpace extends Component {
sx,
]}
>
- {
- this.contentRef = el;
- }}
- sx={{ width: '100%' }}
- >
+
{pageItems.map((item, i) => (