feat(artikel): fill results list viewport with FillSpace

Wrap the article search results list in FillSpace (from dashgui) so it
becomes a scrollable middle section that fills to the bottom of the
viewport. The pagination box is referenced via paginationRef as the
anchor above the spacer; measureElement is temporarily a debug unit to
visualize the repeating fill.
This commit is contained in:
seb
2026-08-12 02:44:32 +02:00
parent b7f60d51fd
commit 4df5110253

View File

@@ -1,4 +1,5 @@
import { Component } from 'react';
import { Component, createRef } from 'react';
import FillSpace from 'dashgui/FillSpace';
import Box from '@mui/material/Box';
import InputAdornment from '@mui/material/InputAdornment';
import List from '@mui/material/List';
@@ -85,6 +86,7 @@ class ArtikelSearchPage extends Component {
state = initialSearchState(this.props.searchParams);
listSlotRef = { current: null };
rowMeasureRef = { current: null };
paginationRef = createRef();
setListSlotRef = (node) => {
if (this.listSlotRef.current === node) return;
@@ -368,7 +370,7 @@ class ArtikelSearchPage extends Component {
)}
{/* Always mount real pagination height (invisible until needed) so pageSize is correct. */}
<Box sx={{ mb: 2 }}>
<Box ref={this.paginationRef} sx={{ mb: 2 }}>
<ResultsPagination
invisible={!showPagination}
offset={showPagination ? offset : 0}
@@ -380,54 +382,73 @@ class ArtikelSearchPage extends Component {
/>
</Box>
<Box ref={this.setListSlotRef}>
{rows.length > 0 && (
<List component={Paper} variant="outlined" disablePadding>
{rows.map((row, index) => (
<ListItemButton
key={row.id}
type="button"
divider={index < rows.length - 1}
data-result-row
onMouseDown={(e) => e.preventDefault()}
onClick={() => {
this.persistQuery();
this.props.navigate(artikelDetailPath(row));
}}
sx={resultRowSx}
>
<ListItemText
primary={row.name}
secondary={
<Typography
component="span"
variant="body2"
color="text.secondary"
noWrap
>
{row.sku}
{' · '}
Bestand {formatStock(row.stock)}
{' · '}
Verfügbar {formatStock(row.available)}
{row.categories
? ` · ${formatCategoryList(row.categories, categoryFacets ?? {})}`
: ''}
</Typography>
}
slotProps={{ primary: { fontWeight: 500, noWrap: true } }}
/>
<Typography
variant="body2"
sx={{ fontWeight: 600, flexShrink: 0, ml: 2 }}
<FillSpace
aboveRef={this.paginationRef}
debug
sx={{ overflow: 'auto' }}
// Temporary dummy unit so the repeating fill is visible.
measureElement={
<Box
sx={{
bgcolor: '#dbeafe',
p: 2,
borderRadius: 1,
textAlign: 'center',
}}
>
<Typography variant="body2">Einheit</Typography>
</Box>
}
>
<Box ref={this.setListSlotRef}>
{rows.length > 0 && (
<List component={Paper} variant="outlined" disablePadding>
{rows.map((row, index) => (
<ListItemButton
key={row.id}
type="button"
divider={index < rows.length - 1}
data-result-row
onMouseDown={(e) => e.preventDefault()}
onClick={() => {
this.persistQuery();
this.props.navigate(artikelDetailPath(row));
}}
sx={resultRowSx}
>
{row.price != null ? formatPrice(row.price) : '—'}
</Typography>
</ListItemButton>
))}
</List>
)}
</Box>
<ListItemText
primary={row.name}
secondary={
<Typography
component="span"
variant="body2"
color="text.secondary"
noWrap
>
{row.sku}
{' · '}
Bestand {formatStock(row.stock)}
{' · '}
Verfügbar {formatStock(row.available)}
{row.categories
? ` · ${formatCategoryList(row.categories, categoryFacets ?? {})}`
: ''}
</Typography>
}
slotProps={{ primary: { fontWeight: 500, noWrap: true } }}
/>
<Typography
variant="body2"
sx={{ fontWeight: 600, flexShrink: 0, ml: 2 }}
>
{row.price != null ? formatPrice(row.price) : '—'}
</Typography>
</ListItemButton>
))}
</List>
)}
</Box>
</FillSpace>
</Box>
</Box>
)}