Share menu paints above every map layer: the toolbar lifts while a menu is open; the menu stays inside the map's bottom edge; phone band spans the map

This commit is contained in:
swift-river542 2026-09-16 22:14:27 -05:00
parent cb57fd6ed7
commit a391cfb65e
2 changed files with 29 additions and 5 deletions

View file

@ -40,6 +40,11 @@ useShadowStyles(`
gap: var(--map-toolbar-gap, 8px);
z-index: 10;
}
/* While a control's menu (the share menu) is open the toolbar is lifted above every other
map layer — search pill, legend, sheet — so the menu can never underlap them. */
.map-toolbar.has-open-menu {
z-index: 3000;
}
/* Slim every toolbar control to a fixed 32px (coder 2026-06-24: "buttons can be
smaller"): smaller but still tappable, and uniform so they all align. The icons
inside stay their own fixed px sizes (they don't scale with the button), so they

View file

@ -21,7 +21,7 @@
- Re-click the share button
-->
<script setup>
import { ref, computed, inject, unref, onMounted, onBeforeUnmount } from 'vue'
import { ref, computed, inject, unref, onMounted, onBeforeUnmount, watch, nextTick } from 'vue'
import { useI18n } from 'vue-i18n'
import { useShadowStyles } from '../../composables/useShadowStyles.js'
import { resolveShareUrl } from '../../utils/embedBase.js'
@ -58,7 +58,7 @@ useShadowStyles(`
border-radius:12px;
box-shadow:0 6px 24px rgba(0,0,0,0.45);
padding:0;
z-index:1100;
z-index:3000;
color:#e6edf3;
font-family:system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;
font-size:13px;
@ -368,9 +368,10 @@ useShadowStyles(`
@media(max-width:767px){
.share-pop{
position:fixed;
top:120px;
top:64px;
left:10px;
right:52px;
right:10px;
z-index:3000;
width:auto;
max-width:none;
max-height:calc(100vh - 96px);
@ -389,7 +390,24 @@ const props = defineProps({
})
/* ---- state ---- */
const rootEl = ref(null)
const isOpen = ref(false)
// The toolbar (this button's parent) is lifted above every map layer while the menu is open.
watch(isOpen, async (open) => {
const tb = rootEl.value?.closest('.map-toolbar'); if (tb) tb.classList.toggle('has-open-menu', open)
if (open) { await nextTick(); keepInsideMap() }
})
// Natural placement: top-aligned with the button, to its left; if the menu would run past the
// map's bottom edge it slides up just enough to stay inside (desktop only — phones use a band).
const popShift = ref(0)
function keepInsideMap() {
const pop = rootEl.value?.querySelector('.share-pop'); const host = rootEl.value?.getRootNode()?.host
if (!pop || !host || window.matchMedia('(max-width: 767px)').matches) { popShift.value = 0; return }
popShift.value = 0
const hb = host.getBoundingClientRect(), pr = pop.getBoundingClientRect()
const overflow = pr.bottom - (hb.bottom - 8)
if (overflow > 0) popShift.value = -Math.min(overflow, Math.max(0, pr.top - hb.top - 8))
}
const popoverEl = ref(null)
const btnEl = ref(null)
const activeTab = ref('link') // 'link' | 'embed'
@ -511,7 +529,7 @@ onBeforeUnmount(() => {
</script>
<template>
<div class="share-wrap">
<div ref="rootEl" class="share-wrap">
<MapControlButton
ref="btnEl"
:is-dark="isDark"
@ -536,6 +554,7 @@ onBeforeUnmount(() => {
ref="popoverEl"
class="share-pop"
:class="{ light: !isDark }"
:style="popShift ? { top: popShift + 'px' } : null"
>
<!-- Header -->
<div class="share-header">