656 lines
22 KiB
JavaScript
656 lines
22 KiB
JavaScript
import { Component } from 'react';
|
||
import Box from '@mui/material/Box';
|
||
import List from '@mui/material/List';
|
||
import Pagination from '@mui/material/Pagination';
|
||
import Typography from '@mui/material/Typography';
|
||
import { alpha } from '@mui/material/styles';
|
||
|
||
/**
|
||
* FillSpace — fills remaining viewport height; optionally paginates `items`.
|
||
*
|
||
* @component FillSpace
|
||
* @import dashgui/FillSpace
|
||
* @defaultExport
|
||
*
|
||
* @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 {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) {
|
||
const n = Number(count) || 0;
|
||
if (n <= 0 || !(unitHeight > 0)) return 0;
|
||
return n * unitHeight + Math.max(0, n - 1) * (Number(gap) || 0);
|
||
}
|
||
|
||
/** True for class components (e.g. `renderItem={ArtikelMarke}`). Rendered as element, not called. */
|
||
function isRowComponent(renderItem) {
|
||
return (
|
||
typeof renderItem === 'function' &&
|
||
(renderItem.prototype?.isReactComponent || renderItem.isFillRowComponent === true)
|
||
);
|
||
}
|
||
|
||
/** Renders one row: component types become `<Row brand item index />`, callbacks are called. */
|
||
function renderRowElement(renderItem, item, index) {
|
||
if (isRowComponent(renderItem)) {
|
||
const Row = renderItem;
|
||
return <Row brand={item} item={item} index={index} />;
|
||
}
|
||
return renderItem(item, index);
|
||
}
|
||
|
||
/**
|
||
* Data-free probe of the row height, rendered hidden. Class components
|
||
* (e.g. `renderItem={ArtikelMarke}`) tolerate empty props
|
||
* (`brand`/`item` undefined) and keep a fixed height (40px thumb /
|
||
* text field), so the probe measures the row without depending on the
|
||
* first data element. Plain callbacks are NOT probed — calling arbitrary
|
||
* user code with `undefined` could throw — they fall back to the first
|
||
* rendered data row.
|
||
*/
|
||
function renderProbeElement(renderItem) {
|
||
if (!isRowComponent(renderItem)) return null;
|
||
const Row = renderItem;
|
||
return <Row brand={undefined} item={undefined} index={0} disabled />;
|
||
}
|
||
|
||
class FillSpace extends Component {
|
||
constructor(props) {
|
||
super(props);
|
||
this.state = {
|
||
// null = not measured yet: render at natural content height so the
|
||
// 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,
|
||
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;
|
||
this.observer = null;
|
||
this.raf = null;
|
||
}
|
||
|
||
componentDidMount() {
|
||
// Measure synchronously (no rAF): setState here is batched before the
|
||
// browser paints, so the first visible frame already has the full
|
||
// viewport height — no small-box → expanded-box layout shift.
|
||
this._measure();
|
||
this._observe();
|
||
window.addEventListener('resize', this._onViewportChange);
|
||
}
|
||
|
||
componentDidUpdate(prevProps) {
|
||
if (
|
||
prevProps.aboveRef !== this.props.aboveRef ||
|
||
prevProps.belowRef !== this.props.belowRef ||
|
||
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
|
||
) {
|
||
this._observe();
|
||
this._measure();
|
||
}
|
||
// Clamp current page when the item list shrinks (e.g. brand deleted).
|
||
if (
|
||
Array.isArray(this.props.items) &&
|
||
this.props.items !== prevProps.items
|
||
) {
|
||
const pageCount = Math.max(
|
||
1,
|
||
Math.ceil(this.props.items.length / Math.max(1, this.state.pageSize)),
|
||
);
|
||
if (this.state.currentPage > pageCount) {
|
||
this.setState({ currentPage: pageCount });
|
||
this.props.onPageChange?.(pageCount);
|
||
}
|
||
}
|
||
}
|
||
|
||
componentWillUnmount() {
|
||
window.removeEventListener('resize', this._onViewportChange);
|
||
if (this.raf) {
|
||
cancelAnimationFrame(this.raf);
|
||
this.raf = null;
|
||
}
|
||
if (this.observer) {
|
||
this.observer.disconnect();
|
||
this.observer = null;
|
||
}
|
||
}
|
||
|
||
_onViewportChange = () => {
|
||
if (this.raf) return;
|
||
this.raf = requestAnimationFrame(() => {
|
||
this.raf = null;
|
||
this._measure();
|
||
});
|
||
};
|
||
|
||
_observe() {
|
||
if (this.observer) {
|
||
this.observer.disconnect();
|
||
}
|
||
|
||
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
|
||
// 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 aboveList = Array.isArray(aboveRef) ? aboveRef : [aboveRef];
|
||
const belowList = Array.isArray(belowRef) ? belowRef : [belowRef];
|
||
const containerTarget = containerRef && 'current' in containerRef ? containerRef.current : containerRef;
|
||
const targets = [
|
||
...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) {
|
||
if (el && el.offsetParent !== null) {
|
||
this.observer.observe(el);
|
||
}
|
||
}
|
||
}
|
||
|
||
measure = () => {
|
||
this._measure();
|
||
};
|
||
|
||
_measure() {
|
||
if (!this.ref) return;
|
||
|
||
const {
|
||
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.
|
||
const rect = this.ref.getBoundingClientRect();
|
||
const abovePx = rect.top + window.scrollY;
|
||
|
||
// Footer below (box + top margin) plus extra trailing space.
|
||
const belowList = Array.isArray(belowRef) ? belowRef : [belowRef];
|
||
let belowPx = 0;
|
||
for (const ref of belowList) {
|
||
const belowEl = ref?.current;
|
||
if (belowEl && belowEl.offsetParent !== null) {
|
||
belowPx += belowEl.offsetHeight;
|
||
const marginTop = parseFloat(window.getComputedStyle(belowEl).marginTop);
|
||
belowPx += Number.isFinite(marginTop) ? marginTop : 0;
|
||
}
|
||
}
|
||
// Extra trailing space (app-shell padding, tile padding) that must stay visible.
|
||
belowPx += Number(reserveBottom) || 0;
|
||
|
||
const containerEl = containerRef && 'current' in containerRef ? containerRef.current : containerRef;
|
||
let available;
|
||
if (containerEl && typeof containerEl.getBoundingClientRect === 'function') {
|
||
const cRect = containerEl.getBoundingClientRect();
|
||
const paddingBottom = parseFloat(window.getComputedStyle(containerEl).paddingBottom) || 0;
|
||
available = Math.max(0, cRect.bottom - rect.top - belowPx - paddingBottom);
|
||
} else {
|
||
available = Math.max(0, window.innerHeight - abovePx - belowPx);
|
||
}
|
||
|
||
const clamped = Math.min(
|
||
Math.max(available, minHeight),
|
||
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 finalHeight = Math.min(
|
||
Math.max(clamped, minHeightForCount),
|
||
maxHeight ?? Infinity,
|
||
);
|
||
|
||
// Fitting unit count (drives page size) and leftover fill count.
|
||
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));
|
||
}
|
||
|
||
// 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).
|
||
let nextPageSize = this.state.pageSize;
|
||
let measured = this.state.measured;
|
||
if (paginated && totalCount > 0) {
|
||
nextPageSize = Math.max(minSize, totalCount);
|
||
measured = true;
|
||
}
|
||
|
||
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,
|
||
});
|
||
}
|
||
}
|
||
|
||
_handleWheel = (event) => {
|
||
const { items } = this.props;
|
||
if (!Array.isArray(items)) return;
|
||
const { pageSize, currentPage } = this.state;
|
||
const pageCount = Math.max(1, Math.ceil(items.length / Math.max(1, pageSize)));
|
||
const delta = event.deltaY || 0;
|
||
if (pageCount <= 1) return;
|
||
if (delta === 0) return;
|
||
if (Math.sign(delta) !== Math.sign(this.wheelAcc || 0)) this.wheelAcc = 0;
|
||
this.wheelAcc = (this.wheelAcc || 0) + delta;
|
||
const threshold = Math.max(50, 800 / pageCount);
|
||
if (Math.abs(this.wheelAcc) < threshold) return;
|
||
const dir = this.wheelAcc > 0 ? 1 : -1;
|
||
this.wheelAcc = 0;
|
||
const next = dir > 0
|
||
? Math.min(pageCount, currentPage + 1)
|
||
: Math.max(1, currentPage - 1);
|
||
if (next === currentPage) return;
|
||
this._goToPage(next);
|
||
};
|
||
|
||
_goToPage = (page) => {
|
||
const { items, onPageChange } = this.props;
|
||
const total = Array.isArray(items) ? items.length : 0;
|
||
const pageCount = Math.max(1, Math.ceil(total / Math.max(1, this.state.pageSize)));
|
||
const next = Math.min(Math.max(1, Number(page) || 1), pageCount);
|
||
if (next === this.state.currentPage) return;
|
||
this.setState({ currentPage: next });
|
||
onPageChange?.(next);
|
||
};
|
||
|
||
render() {
|
||
const {
|
||
aboveRef,
|
||
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(
|
||
<Box key={i} sx={{ width: '100%' }}>
|
||
{measureElement}
|
||
</Box>,
|
||
);
|
||
}
|
||
fill = (
|
||
<Box
|
||
sx={{
|
||
display: 'flex',
|
||
flexDirection: 'column',
|
||
gap: measureGap,
|
||
width: '100%',
|
||
overflow: 'hidden',
|
||
}}
|
||
>
|
||
{fillItems}
|
||
</Box>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<>
|
||
<Box
|
||
ref={(el) => {
|
||
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 ? (
|
||
<Box
|
||
ref={(el) => {
|
||
this.contentRef = el;
|
||
}}
|
||
sx={{ width: '100%', ...(measureElement == null ? { height: '100%' } : {}) }}
|
||
>
|
||
{children}
|
||
</Box>
|
||
) : null}
|
||
{fill}
|
||
{measureElement != null ? (
|
||
<Box
|
||
ref={(el) => {
|
||
this.unitRef = el;
|
||
}}
|
||
aria-hidden
|
||
sx={{
|
||
position: 'absolute',
|
||
left: 0,
|
||
top: 0,
|
||
width: '100%',
|
||
visibility: 'hidden',
|
||
pointerEvents: 'none',
|
||
}}
|
||
>
|
||
{measureElement}
|
||
</Box>
|
||
) : null}
|
||
</Box>
|
||
{debug ? (
|
||
<Box
|
||
sx={(theme) => ({
|
||
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;
|
||
const { height, pageSize, currentPage, measured } = this.state;
|
||
const total = items.length;
|
||
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 = items.slice(offset, offset + pageSize);
|
||
// Hidden until the fit is measured — no "1–10" flash on load.
|
||
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;
|
||
|
||
return (
|
||
<>
|
||
<Box
|
||
ref={(el) => {
|
||
this.paginationRef = el;
|
||
}}
|
||
// Fixed single-line bar: the header height must not depend on the
|
||
// page count. Otherwise the first fit (e.g. "1-17") pushes the
|
||
// spacer down, the available height shrinks, and a second pass
|
||
// flips to "1-15". One pass: constant 44px, real values always
|
||
// rendered (only visibility is gated), no placeholder numbers.
|
||
sx={{ mb: 1, height: 44, overflow: 'hidden' }}
|
||
>
|
||
<Box
|
||
aria-hidden={!showPagination || undefined}
|
||
sx={{
|
||
display: 'flex',
|
||
justifyContent: 'space-between',
|
||
alignItems: 'center',
|
||
flexWrap: 'nowrap',
|
||
gap: 1,
|
||
width: '100%',
|
||
height: 44,
|
||
visibility: showPagination ? 'visible' : 'hidden',
|
||
pointerEvents: showPagination ? 'auto' : 'none',
|
||
}}
|
||
>
|
||
<Typography
|
||
variant="body2"
|
||
color="text.secondary"
|
||
noWrap
|
||
sx={{ flexShrink: 1, minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis' }}
|
||
>
|
||
{total === 0 ? '0' : `${offset + 1}–${Math.min(offset + pageSize, total)}`} von{' '}
|
||
{total}
|
||
</Typography>
|
||
<Pagination
|
||
count={pageCount}
|
||
page={safePage}
|
||
onChange={(_e, page) => this._goToPage(page)}
|
||
color="primary"
|
||
size="large"
|
||
onMouseDown={(e) => e.preventDefault()}
|
||
sx={{
|
||
flexShrink: 0,
|
||
maxWidth: '100%',
|
||
overflowX: 'auto',
|
||
scrollbarWidth: 'none',
|
||
'&::-webkit-scrollbar': { display: 'none' },
|
||
'& .MuiPagination-ul': { flexWrap: 'nowrap' },
|
||
'& .MuiPaginationItem-root': {
|
||
minWidth: 44,
|
||
height: 44,
|
||
fontSize: '1rem',
|
||
},
|
||
}}
|
||
/>
|
||
</Box>
|
||
</Box>
|
||
<Box
|
||
ref={(el) => {
|
||
this.ref = el;
|
||
}}
|
||
onWheel={(e) => {
|
||
this._handleWheel(e);
|
||
onWheel?.(e);
|
||
}}
|
||
{...rest}
|
||
sx={[
|
||
{
|
||
position: 'relative',
|
||
height: height ?? 'auto',
|
||
minHeight,
|
||
...(maxHeight != null ? { maxHeight } : {}),
|
||
overflow: 'hidden',
|
||
width: '100%',
|
||
// Keep wheel ticks from scrolling the page behind the list.
|
||
// (No preventDefault needed — React wheel is passive.)
|
||
overscrollBehavior: 'contain',
|
||
},
|
||
debug
|
||
? (theme) => ({
|
||
outline: `2px dashed ${theme.palette.primary.main}`,
|
||
})
|
||
: undefined,
|
||
sx,
|
||
]}
|
||
>
|
||
<Box
|
||
ref={(el) => {
|
||
this.contentRef = el;
|
||
}}
|
||
sx={{ width: '100%' }}
|
||
>
|
||
<List dense disablePadding sx={{ width: '100%' }}>
|
||
{pageItems.map((item, i) => (
|
||
<Box key={item?.id ?? offset + i} data-fill-row sx={{ width: '100%' }}>
|
||
{renderRowElement(renderItem, item, offset + i)}
|
||
</Box>
|
||
))}
|
||
</List>
|
||
</Box>
|
||
{probe != null ? (
|
||
<Box
|
||
ref={(el) => {
|
||
this.probeRef = el;
|
||
}}
|
||
aria-hidden
|
||
sx={{
|
||
position: 'absolute',
|
||
left: 0,
|
||
top: 0,
|
||
width: '100%',
|
||
visibility: 'hidden',
|
||
pointerEvents: 'none',
|
||
}}
|
||
>
|
||
<List dense disablePadding sx={{ width: '100%' }}>
|
||
<Box data-fill-row sx={{ width: '100%' }}>
|
||
{probe}
|
||
</Box>
|
||
</List>
|
||
</Box>
|
||
) : null}
|
||
</Box>
|
||
{debug ? (
|
||
<Box
|
||
sx={(theme) => ({
|
||
position: 'fixed',
|
||
inset: 0,
|
||
pointerEvents: 'none',
|
||
background: alpha(theme.palette.primary.main, 0.08),
|
||
zIndex: 9999,
|
||
})}
|
||
/>
|
||
) : null}
|
||
</>
|
||
);
|
||
}
|
||
}
|
||
|
||
export default FillSpace;
|