Phones: the legend sheet sits at the map's bottom, and the engine's credit goes to the top
Every bottom placement of the engine's credit failed a reader on a phone: under the open sheet it was covered, lifted above the sheet it floated mid-map, and in a strip below the sheet it pushed the legend off the map's bottom edge. On a phone both engines' bottom corners now move to the top corner on the start side, opposite the map's own control column (left; right in RTL), and the sheet sits flush at the map's bottom, starting beside the round menu button (--map-fab-beside) so the button covers none of it. The move is !important so a plugin's own lift of its bottom corners cannot push the credit off the top. Desktop is unchanged.
This commit is contained in:
parent
037dfad3bc
commit
7978e5e41a
1 changed files with 21 additions and 58 deletions
|
|
@ -21,21 +21,21 @@ useShadowStyles(`
|
|||
the SAME component desktop uses, with all the same default CSS. Mobile
|
||||
no longer overrides any .lrg-* rule — the bottom sheet is purely
|
||||
mobile chrome (pull tab, collapse/expand caret, touch drag). ── */
|
||||
/* bottom = the sheet's height (it shows only while the sheet is open, whose height now fits its
|
||||
content) + the credit strip under the sheet + a 16px gap. */
|
||||
.floating-mini-image{position:absolute;inset-inline-start:16px;bottom:calc(var(--legend-sheet-h,30%) + var(--legend-credit-strip,0px) + 16px);z-index:999;pointer-events:auto;cursor:pointer;}
|
||||
/* bottom = the sheet's height (it shows only while the sheet is open, whose height fits its content)
|
||||
+ a 16px gap. */
|
||||
.floating-mini-image{position:absolute;inset-inline-start:16px;bottom:calc(var(--legend-sheet-h,30%) + 16px);z-index:999;pointer-events:auto;cursor:pointer;}
|
||||
.mini-image{width:120px;height:150px;object-fit:cover;border-radius:8px;box-shadow:0 4px 12px rgba(0,0,0,0.25);display:block;}
|
||||
|
||||
/* Containment root: clamps absolute children to the map area. */
|
||||
.legend-sheet-root{position:absolute;inset:0;pointer-events:none;overflow:hidden;}
|
||||
.legend-sheet-root>*{pointer-events:auto;}
|
||||
/* ★ THE CREDIT STRIP (mobile pass 2026-09-30) REPLACES THE CAPPED LIFT BELOW. The corners used to rise by
|
||||
the sheet height capped at 48px, so an OPEN sheet (30%) still covered the credit and the Mapbox
|
||||
wordmark (measured at 390 on Simple Research: the top element at both was the sheet's content),
|
||||
while an uncapped lift would float the credit into the middle of the map. Now the corners stay at the
|
||||
map's bottom edge and the SHEET sits above them: --legend-credit-strip is measured from the real boxes
|
||||
of every visible bottom control (both engines' wordmark and credit, and our own credit line), so the
|
||||
credit is visible whatever the sheet does, and never mid-map. The notes below are history. */
|
||||
/* ★ ON A PHONE THE ENGINE'S CREDIT GOES TO THE TOP, AND THE SHEET SITS AT THE BOTTOM (mobile pass
|
||||
2026-09-30). Every bottom placement failed a reader: under the open sheet the credit and the Mapbox
|
||||
wordmark were covered; lifted above it they floated mid-map; in a strip below it they pushed the legend
|
||||
off the map's bottom edge. So on a phone both engines' bottom corners move to the TOP corner on the
|
||||
START side, opposite the map's own control column (left; right in RTL), compact and always visible,
|
||||
and the sheet sits flush at the map's bottom, beside the round menu button (see the @media block after
|
||||
the desktop rules). The desktop rules below are unchanged; the notes in them are history. */
|
||||
/* ── Attribution stays visible (bug 2026-09-16) ──
|
||||
The sheet is a bottom overlay, so the engine's bottom-left logo / bottom-right
|
||||
attribution corners ended up UNDER it in every state (even the 48px collapsed
|
||||
|
|
@ -72,6 +72,13 @@ useShadowStyles(`
|
|||
.mapboxgl-ctrl-bottom-right:hover,.maplibregl-ctrl-bottom-right:hover{opacity:1;}
|
||||
.mapboxgl-ctrl-bottom-right .mapboxgl-ctrl,.maplibregl-ctrl-bottom-right .maplibregl-ctrl{margin:0 6px 6px 0;}
|
||||
.mapboxgl-ctrl-bottom-right .mapboxgl-ctrl-attrib.mapboxgl-compact{margin-right:6px;}
|
||||
/* !important on the move: a plugin may lift its own bottom corners by the round button's height with a
|
||||
plain rule of the same specificity (a translateY on the corner container), and up here that lift would
|
||||
push the credit off the top of the map. The corners are not at the bottom on a phone any more. */
|
||||
@media (max-width:767.98px){
|
||||
.mapboxgl-ctrl-bottom-left,.mapboxgl-ctrl-bottom-right,.maplibregl-ctrl-bottom-left,.maplibregl-ctrl-bottom-right{top:0 !important;bottom:auto !important;inset-inline-start:0 !important;inset-inline-end:auto !important;transform:none !important;justify-content:flex-start;}
|
||||
.mapboxgl-ctrl-bottom-left .mapboxgl-ctrl,.mapboxgl-ctrl-bottom-right .mapboxgl-ctrl,.maplibregl-ctrl-bottom-left .maplibregl-ctrl,.maplibregl-ctrl-bottom-right .maplibregl-ctrl{margin:8px 0 0;margin-inline:8px 0;}
|
||||
}
|
||||
/* In the sheet the legend's rows fill the full width: the clear "x" column does not reserve
|
||||
space on the right — it floats over the row's right edge only when a row is selected. */
|
||||
.legend-mobile-sheet .stl-row{position:relative;}
|
||||
|
|
@ -84,7 +91,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:var(--legend-credit-strip,0px);left:0;right: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) - var(--legend-credit-strip,0px));}
|
||||
.legend-mobile-sheet{position:absolute;bottom:0;inset-inline-start:var(--map-fab-beside,0px);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
|
||||
|
|
@ -95,7 +102,7 @@ useShadowStyles(`
|
|||
empty sheet; a long tree scrolled inside one). A change to or from the fitted height snaps: a height
|
||||
transition cannot run to auto, and interpolate-size:allow-keywords left the sheet stuck at the 48px
|
||||
it started from (measured in Chrome 149, collapsed to open on Simple Research). */
|
||||
.legend-mobile-sheet.fit{max-height:min(45%, calc(100% - var(--legend-sheet-top-reserve,80px) - var(--legend-credit-strip,0px)));}
|
||||
.legend-mobile-sheet.fit{max-height:min(45%, calc(100% - var(--legend-sheet-top-reserve,80px)));}
|
||||
|
||||
/* Drag strip — thin top overlay holding the pull-tab handle + touch
|
||||
gestures. z-index puts it above content. */
|
||||
|
|
@ -163,7 +170,7 @@ useShadowStyles(`
|
|||
The layout publishes --map-fab-clearance only while that button is rendered, so this reserves
|
||||
space ONLY when there is something to clear: no circle, no wasted whitespace, and the padding
|
||||
is additive so nothing that works today moves. */
|
||||
.legend-content{flex:1;min-height:0;display:flex;flex-direction:column;overflow-y:auto;overflow-x:hidden;padding:12px 0 calc(12px + var(--map-fab-clearance, 0px));-webkit-overflow-scrolling:touch;overscroll-behavior:contain;}
|
||||
.legend-content{flex:1;min-height:0;display:flex;flex-direction:column;overflow-y:auto;overflow-x:hidden;padding:12px 0;-webkit-overflow-scrolling:touch;overscroll-behavior:contain;}
|
||||
/* overscroll-behavior:contain on the LIST, not on the pane: a fling that reaches the list's end stops
|
||||
there instead of scrolling the page behind the map (and, on a phone, collapsing its URL bar). */
|
||||
/* Constrain only the table/tree shells to the .legend-content viewport so
|
||||
|
|
@ -682,7 +689,7 @@ function onSheetTouchStart(event) {
|
|||
drag.lastH = drag.startH
|
||||
drag.startState = legendState.value
|
||||
drag.containerH = sheet.parentElement?.offsetHeight || window.innerHeight
|
||||
drag.maxH = drag.containerH - TOP_RESERVE_PX - creditStripPx
|
||||
drag.maxH = drag.containerH - TOP_RESERVE_PX
|
||||
}
|
||||
|
||||
function onSheetTouchMove(event) {
|
||||
|
|
@ -731,50 +738,6 @@ function snapTo(state) {
|
|||
else uiStore.openLegend()
|
||||
}
|
||||
|
||||
// ── Credit strip → host CSS var: the sheet sits ABOVE the engine's bottom controls ───
|
||||
// The strip is the distance from the map area's bottom edge to the top of the tallest visible bottom
|
||||
// control (each control's own box, so the corner container's margins count and an empty corner counts
|
||||
// nothing). The engines add and resize these after load (the attribution loads its sources, compacts,
|
||||
// expands on tap), so it is re-measured on settle timers and whenever an observed control resizes.
|
||||
const STRIP_VAR = '--legend-credit-strip'
|
||||
const CORNERS = '.maplibregl-ctrl-bottom-left, .maplibregl-ctrl-bottom-right, .mapboxgl-ctrl-bottom-left, .mapboxgl-ctrl-bottom-right'
|
||||
let creditStripPx = 0
|
||||
let _stripRO = null
|
||||
let _stripTimers = []
|
||||
const _stripObserved = new Set()
|
||||
function measureCreditStrip() {
|
||||
const sheet = sheetRef.value
|
||||
const area = sheet && sheet.parentElement
|
||||
const root = sheet && sheet.getRootNode()
|
||||
if (!area || !root || !root.querySelectorAll || !_sheetVarTarget) return
|
||||
const bottom = area.getBoundingClientRect().bottom
|
||||
let strip = 0
|
||||
for (const corner of root.querySelectorAll(CORNERS)) {
|
||||
for (const ctl of corner.children) {
|
||||
const r = ctl.getBoundingClientRect()
|
||||
if (r.width > 0 && r.height > 0) strip = Math.max(strip, bottom - r.top)
|
||||
if (_stripRO && !_stripObserved.has(ctl)) { _stripRO.observe(ctl); _stripObserved.add(ctl) }
|
||||
}
|
||||
}
|
||||
const px = strip > 0 ? Math.ceil(strip) + 2 : 0
|
||||
if (px !== creditStripPx) {
|
||||
creditStripPx = px
|
||||
_sheetVarTarget.style.setProperty(STRIP_VAR, px + 'px')
|
||||
}
|
||||
}
|
||||
onMounted(() => {
|
||||
if (typeof ResizeObserver !== 'undefined') _stripRO = new ResizeObserver(() => measureCreditStrip())
|
||||
_stripTimers = [0, 500, 1500, 3000, 6000, 12000].map((ms) => setTimeout(measureCreditStrip, ms))
|
||||
})
|
||||
onBeforeUnmount(() => {
|
||||
_stripTimers.forEach(clearTimeout); _stripTimers = []
|
||||
if (_stripRO) { _stripRO.disconnect(); _stripRO = null }
|
||||
_stripObserved.clear()
|
||||
if (_sheetVarTarget && _sheetVarTarget.__legendSheetVarOwner === _observedSheet) {
|
||||
_sheetVarTarget.style.removeProperty(STRIP_VAR)
|
||||
}
|
||||
})
|
||||
|
||||
// ── Sheet height → host CSS var (keeps the engine's attribution visible) ───
|
||||
// Writes --legend-sheet-h on the custom-element host (the CSS rule near the top
|
||||
// of the stylesheet lifts the bottom control corners by it). Only a sheet with a
|
||||
|
|
|
|||
Loading…
Reference in a new issue