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