Phone legend: shrinking the detail sheet past its content no longer scrolls the page

Reported from a phone (Home and Pray, the people-group detail sheet): dragging the handle down worked until the
sheet became shorter than its content; from then on the page scrolled while the finger kept dragging.
- The sheet and the detail panel's scrollers (.people-group-detail, .detail-content) contain their overscroll,
  so a list that reaches its end can never chain a scroll to the page.
- The drag handlers on the sheet and its grab lip listen in the CAPTURE phase, so every touch reaches the drag
  first and no child's handler can stop one from being claimed.
This commit is contained in:
Doxa Maps 2026-10-02 09:21:04 +00:00
parent cf0146775b
commit e273d451cc
2 changed files with 18 additions and 12 deletions

View file

@ -119,7 +119,7 @@ import { getApiBaseUrl } from '../utils/apiBaseUrl.js';
const { t, locale } = useI18n();
useShadowStyles(`
.people-group-detail{display:flex;flex-direction:column;height:100%;overflow-y:auto;}
.people-group-detail{display:flex;flex-direction:column;height:100%;overflow-y:auto;overscroll-behavior:contain;}
.people-group-detail.dark-mode{background:#1a1a2e;color:#eee;}
.detail-header{display:flex;justify-content:space-between;align-items:flex-start;padding:16px;border-bottom:1px solid #e0e0e0;background:white;position:sticky;top:0;z-index:10;}
.dark-mode .detail-header{background:#16213e;border-bottom-color:#2d2d4e;}
@ -133,7 +133,7 @@ useShadowStyles(`
.dark-mode .close-button{color:#aaa;}
.close-button:hover{color:#333;}
.dark-mode .close-button:hover{color:#fff;}
.detail-content{padding:6px 12px 12px;flex:1;overflow-y:auto;}
.detail-content{padding:6px 12px 12px;flex:1;overflow-y:auto;overscroll-behavior:contain;}
.dark-mode .detail-content{background:#1a1a2e;}
.quick-stats{display:flex;flex-direction:column;gap:4px;margin-bottom:8px;}
.stat-item{display:flex;justify-content:space-between;align-items:baseline;font-size:13px;}
@ -467,6 +467,8 @@ function handleImageError(event) {
flex-direction: column;
height: 100%;
overflow-y: auto;
/* a scroll that reaches this panel's end never chains to the page (the mobile sheet's detail view) */
overscroll-behavior: contain;
}
/* Header Section */
@ -540,6 +542,7 @@ function handleImageError(event) {
padding: 6px 12px 12px;
flex: 1;
overflow-y: auto;
overscroll-behavior: contain;
}
/* Quick Stats */

View file

@ -118,7 +118,7 @@ useShadowStyles(`
max-height reads --legend-sheet-top-reserve, set on this element from the
TOP_RESERVE_PX constant in the script — the SAME number the touch-drag clamp
uses, so CSS and JS can never disagree about how tall the sheet may get. */
.legend-mobile-sheet{position:absolute;bottom:0;inset-inline-start:0;inset-inline-end:0;z-index:1000;background:white;border-top-left-radius:var(--map-radius,16px);border-top-right-radius:var(--map-radius,16px);box-shadow:0 -4px 20px rgba(0,0,0,0.2);transition:height 0.3s ease-out;display:flex;flex-direction:column;overflow:hidden;max-height:calc(100% - var(--legend-sheet-top-reserve,80px));}
.legend-mobile-sheet{overscroll-behavior:contain;position:absolute;bottom:0;inset-inline-start:0;inset-inline-end:0;z-index:1000;background:white;border-top-left-radius:var(--map-radius,16px);border-top-right-radius:var(--map-radius,16px);box-shadow:0 -4px 20px rgba(0,0,0,0.2);transition:height 0.3s ease-out;display:flex;flex-direction:column;overflow:hidden;max-height:calc(100% - var(--legend-sheet-top-reserve,80px));}
/* While a finger is resizing the sheet the height must be applied INSTANTLY —
with the 0.3s transition left on, the measured height lagged the finger by
14-30px (mobile-sheet-probe baseline 2026-09-16) and the release logic read a
@ -984,12 +984,15 @@ onBeforeUnmount(() => { if (_creditTimer) { clearTimeout(_creditTimer); _creditT
v-if="legendState !== 'collapsed'"
class="sheet-grab-lip"
aria-hidden="true"
@touchstart="onSheetTouchStart"
@touchmove="onSheetTouchMove"
@touchend="onSheetTouchEnd"
@touchcancel="onSheetTouchEnd"
@touchstart.capture="onSheetTouchStart"
@touchmove.capture="onSheetTouchMove"
@touchend.capture="onSheetTouchEnd"
@touchcancel.capture="onSheetTouchEnd"
></div>
<!-- Touch resize handlers sit on the SHEET: a drag may start anywhere in the
<!-- (.capture: the drag sees every touch FIRST, so no child's handler can stop one reaching it; and the sheet
contains overscroll, so a list inside it can never chain a scroll to the page: 2026-10-02, a phone shrinking the
detail sheet past its content scrolled the page.)
Touch resize handlers sit on the SHEET: a drag may start anywhere in the
top GRAB_ZONE_PX (strip + title row) — and, in detail mode, on the content
when it can't scroll that way. See onSheetTouchStart for the model. -->
<div
@ -997,10 +1000,10 @@ onBeforeUnmount(() => { if (_creditTimer) { clearTimeout(_creditTimer); _creditT
class="legend-mobile-sheet"
:class="[legendState, { 'sheet-dark': isDark, dragging: isDragging, fit: fitOpen }]"
:style="{ height: fitOpen ? 'auto' : legendHeight, '--legend-sheet-top-reserve': TOP_RESERVE_PX + 'px' }"
@touchstart="onSheetTouchStart"
@touchmove="onSheetTouchMove"
@touchend="onSheetTouchEnd"
@touchcancel="onSheetTouchEnd"
@touchstart.capture="onSheetTouchStart"
@touchmove.capture="onSheetTouchMove"
@touchend.capture="onSheetTouchEnd"
@touchcancel.capture="onSheetTouchEnd"
>
<!-- Thin drag strip at the very top: pull-tab visual + tap-to-toggle. -->
<div class="sheet-drag-strip" @click="handleCaretClick">