ProvidersStep mobile: transparent control strip, icon-only sort, drawer header

- Drop the Paper container around the mobile-map floating controls —
  each control (Filters, Sort, view toggle) now carries its own white
  fill and reads over any map tile without a shared box
- Sort button becomes icon-only on mobile-map (the current sort is
  still communicated via the aria-label and the menu) — saves the
  row's horizontal budget
- Align all three controls to 32px height so Filters, Sort, and the
  List/Map toggle sit on a common baseline
- Move the drawer close X out of the image overlay area into a
  dedicated 40px drawer header bar; cluster header text ("N providers
  in this area") now lives in the same strip. No more overlap with the
  Verified badge on the card image.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-22 12:57:48 +10:00
parent 8c818fd5ac
commit 705e85b37c

View File

@@ -538,18 +538,15 @@ export const ProvidersStep: React.FC<ProvidersStepProps> = ({
/>
</Box>
{/* Floating control strip */}
<Paper
elevation={0}
{/* Floating control strip — no container chrome; each control has
its own fill/border so it reads cleanly over any map tile */}
<Box
sx={{
position: 'absolute',
top: 12,
left: 12,
right: 12,
zIndex: 2,
p: 1,
borderRadius: 2,
boxShadow: 'var(--fa-shadow-md)',
display: 'flex',
flexDirection: 'column',
gap: 1,
@@ -634,31 +631,46 @@ export const ProvidersStep: React.FC<ProvidersStepProps> = ({
}}
/>
{/* Control row: Filters, Sort, view toggle */}
{/* Control row: Filters, Sort (icon-only on mobile), view toggle.
Each control carries its own white fill so it reads cleanly
over any map tile — no shared container. Heights aligned at
32px to match Button small + ToggleButton small. */}
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<FilterPanel
activeCount={activeCount}
onClear={handleClear}
sx={{ '& .MuiButton-root:focus-visible': { outline: 'none' } }}
sx={{
'& .MuiButton-root': {
height: 32,
bgcolor: 'background.paper',
'&:hover': { bgcolor: 'background.paper' },
'&:focus-visible': { outline: 'none' },
},
}}
>
{filterDialogChildren}
</FilterPanel>
<Button
variant="outlined"
color="secondary"
size="small"
startIcon={<SwapVertIcon sx={{ fontSize: 16 }} />}
{/* Sort — icon-only on mobile (expansion of the current sort
surfaced through the menu + aria-label) */}
<IconButton
onClick={(e) => setSortAnchor(e.currentTarget)}
aria-haspopup="listbox"
aria-label={`Sort by ${SORT_OPTIONS.find((o) => o.value === sortBy)?.label ?? 'Recommended'}`}
sx={{ textTransform: 'none', '&:focus-visible': { outline: 'none' } }}
sx={{
width: 32,
height: 32,
borderRadius: 1,
border: '1px solid',
borderColor: 'divider',
bgcolor: 'background.paper',
color: 'text.secondary',
'&:hover': { bgcolor: 'background.paper' },
'&:focus-visible': { outline: 'none' },
}}
>
<Box component="span" sx={{ color: 'text.secondary', fontWeight: 400, mr: 0.5 }}>
Sort:
</Box>
{SORT_OPTIONS.find((o) => o.value === sortBy)?.label ?? 'Recommended'}
</Button>
<SwapVertIcon sx={{ fontSize: 18 }} />
</IconButton>
<Menu
anchorEl={sortAnchor}
open={Boolean(sortAnchor)}
@@ -681,7 +693,7 @@ export const ProvidersStep: React.FC<ProvidersStepProps> = ({
))}
</Menu>
{/* View toggle — icon-only on mobile to keep the row tight */}
{/* View toggle — icon-only, aligned height with the buttons */}
<ToggleButtonGroup
value={viewMode}
exclusive
@@ -692,8 +704,11 @@ export const ProvidersStep: React.FC<ProvidersStepProps> = ({
ml: 'auto',
flexShrink: 0,
'& .MuiToggleButton-root': {
height: 32,
px: 1,
py: 0.5,
py: 0,
bgcolor: 'background.paper',
'&:hover': { bgcolor: 'background.paper' },
'&.Mui-selected': {
bgcolor: 'var(--fa-color-brand-100)',
color: 'primary.main',
@@ -710,7 +725,7 @@ export const ProvidersStep: React.FC<ProvidersStepProps> = ({
</ToggleButton>
</ToggleButtonGroup>
</Box>
</Paper>
</Box>
{/* Bottom drawer — slides up when a pin/cluster is active */}
<Paper
@@ -741,28 +756,42 @@ export const ProvidersStep: React.FC<ProvidersStepProps> = ({
pointerEvents: drawerOpen ? 'auto' : 'none',
}}
>
{/* Close X */}
<IconButton
aria-label="Close"
onClick={() => mapRef.current?.clearActive()}
{/* Drawer header — holds the close X (and the cluster count when
applicable) so it doesn't sit over the card image */}
<Box
sx={{
position: 'absolute',
top: 8,
right: 8,
zIndex: 1,
width: 32,
height: 32,
bgcolor: 'background.paper',
boxShadow: 'var(--fa-shadow-sm)',
'&:hover': { bgcolor: 'background.paper' },
display: 'flex',
alignItems: 'center',
minHeight: 40,
px: 1.5,
py: 0.5,
gap: 1,
}}
>
<CloseRoundedIcon sx={{ fontSize: 18 }} />
</IconButton>
{drawerCluster && !drawerProvider && (
<Typography variant="labelLg" sx={{ color: 'text.secondary', display: 'block' }}>
{drawerCluster.providers.length} providers in this area
</Typography>
)}
<IconButton
aria-label="Close"
onClick={() => mapRef.current?.clearActive()}
size="small"
sx={{
ml: 'auto',
width: 32,
height: 32,
color: 'text.secondary',
'&:hover': { bgcolor: 'var(--fa-color-surface-subtle)' },
}}
>
<CloseRoundedIcon sx={{ fontSize: 20 }} />
</IconButton>
</Box>
{/* Single-provider drawer content — entire card clickable */}
{drawerProvider && (
<Box sx={{ p: 1.5 }}>
<Box sx={{ px: 1.5, pb: 1.5 }}>
<ProviderCard
name={drawerProvider.name}
location={drawerProvider.location}
@@ -780,13 +809,7 @@ export const ProvidersStep: React.FC<ProvidersStepProps> = ({
{/* Cluster list drawer content — tap row to drill in */}
{drawerCluster && !drawerProvider && (
<Box sx={{ pt: 5, pb: 1 }}>
<Typography
variant="labelLg"
sx={{ px: 2, pb: 1, color: 'text.secondary', display: 'block' }}
>
{drawerCluster.providers.length} providers in this area
</Typography>
<Box sx={{ pb: 1 }}>
<Box>
{[...drawerCluster.providers]
.sort((a, b) => Number(!!b.verified) - Number(!!a.verified))