fix(dawarich): the panel opens to its full height and the rail scrolls past it

Capping it was the wrong half of the fix: the stays were reachable but the panel was cut
off at half the rail, which is not what opening something means.

It sits inside the places list's own scroller now, above the rows, so it simply grows to
whatever the day's stays need — 3350px for forty of them — and the rail scrolls past it
to the places. No second scrollbar, no fold. As a band of its own it could never do that:
the list is the flex child that scrolls, so anything unbounded above it squeezed the list
to nothing and took the rail's scrolling with it.
This commit is contained in:
Maurice
2026-09-17 15:05:29 +02:00
parent 1908d6f5f9
commit 2879433bd3
2 changed files with 35 additions and 30 deletions
+21 -28
View File
@@ -65,34 +65,27 @@ const PlacesSidebar = React.memo(function PlacesSidebar(props: PlacesSidebarProp
</div>
)}
{/* Stays Dawarich recorded on these dates, waiting to be reviewed (#2279).
Above the list because that is what they become, and the panel renders
nothing at all when there is nothing pending — an integration that is
connected and quiet should be invisible rather than a permanent empty
card. */}
{/* Capped and scrolling on its own. Open, the panel is as tall as the day has stays —
ten of them on a day of driving — and with nothing holding it back it pushed the
places list, which is the flex child that scrolls, down to nothing: the rail
stopped scrolling and half the stays sat below the bottom edge with no way to
reach them. Half the rail at most, so the list it sits above is always still a
list. */}
<div style={{ padding: '0 12px 8px', flexShrink: 0, minHeight: 0, maxHeight: '50%', overflowY: 'auto' }}>
<DawarichSuggestionsPanel
tripId={tripId}
trips={[{ id: tripId, label: t('dawarich.accept.thisTrip') }]}
daysForTrip={() => days.map(day => ({
id: day.id,
...formatDayOption(day.day_number, day.date, locale, t),
}))}
// The place it just created belongs on the map and in the list
// now, not after a reload.
onAccepted={() => { void refreshTripAfterAccept(tripId) }}
initiallyCollapsed
/>
</div>
{/* Liste */}
<PlacesList {...S} />
{/* Liste, with the Dawarich stays riding on top of it inside the same scroller —
see the `header` prop for why they are not a band of their own. */}
<PlacesList
{...S}
header={(
<div style={{ padding: '0 12px 8px' }}>
<DawarichSuggestionsPanel
tripId={tripId}
trips={[{ id: tripId, label: t('dawarich.accept.thisTrip') }]}
daysForTrip={() => days.map(day => ({
id: day.id,
...formatDayOption(day.day_number, day.date, locale, t),
}))}
// The place it just created belongs on the map and in the list
// now, not after a reload.
onAccepted={() => { void refreshTripAfterAccept(tripId) }}
initiallyCollapsed
/>
</div>
)}
/>
{dayPickerPlace && <MobileDayPickerSheet {...S} />}
{listImportOpen && <ListImportModal {...S} />}
@@ -1,10 +1,21 @@
import { Fragment } from 'react'
import { Fragment, type ReactNode } from 'react'
import EmptyState from '../shared/EmptyState'
import { MemoPlaceRow } from './PlacesSidebarRow'
import type { SidebarState } from './usePlacesSidebar'
import { usePluginViewContributions, PluginCardFooter } from '../Plugins/PluginContributions'
export function PlacesList(S: SidebarState) {
export function PlacesList({ header, ...S }: SidebarState & {
/**
* A block that sits above the places and scrolls WITH them.
*
* The Dawarich panel lives here rather than in a band of its own above the list. As its
* own band it could not grow: the list is the flex child that scrolls, so a panel with
* ten stays in it squeezed the list to nothing and took the rail's scrolling with it,
* and capping the panel left half its stays below a fold with no way to reach them.
* Inside the scroller it simply opens to its full height and the rail scrolls past it.
*/
header?: ReactNode
}) {
const {
filtered, scrollContainerRef, onScrollTopChange, filter, t, canEditPlaces, onAddPlace,
categories, selectedPlaceId, plannedIds, inDaySet, selectedIds, selectMode, selectedDayId,
@@ -14,6 +25,7 @@ export function PlacesList(S: SidebarState) {
const contribFor = usePluginViewContributions('places', tripId)
return (
<div className="trek-stagger" style={{ flex: 1, overflowY: 'auto', minHeight: 0 }} ref={scrollContainerRef} onScroll={(e) => onScrollTopChange?.((e.currentTarget as HTMLElement).scrollTop)}>
{header}
{filtered.length === 0 ? (
/* The mascot and one line, the shape every other empty state in TREK has.
The add link stays as the state's action: an empty list of places is