[feat](trx-frontend): add map fullscreen toggle
Co-authored-by: OpenAI Codex <codex@openai.com> Signed-off-by: Stan Grams <sjg@haxx.space>
This commit is contained in:
@@ -3217,6 +3217,7 @@ let aprsMapBaseLayer = null;
|
||||
let aprsMapReceiverMarker = null;
|
||||
let aprsRadioPath = null;
|
||||
let selectedLocatorMarker = null;
|
||||
let mapFullscreenListenerBound = false;
|
||||
const stationMarkers = new Map();
|
||||
const locatorMarkers = new Map();
|
||||
const mapMarkers = new Set();
|
||||
@@ -3663,6 +3664,45 @@ function updateMapBaseLayerForTheme(theme) {
|
||||
aprsMapBaseLayer = L.tileLayer(spec.url, spec.options).addTo(aprsMap);
|
||||
}
|
||||
|
||||
function mapStageEl() {
|
||||
return document.getElementById("map-stage");
|
||||
}
|
||||
|
||||
function mapIsFullscreen() {
|
||||
const stage = mapStageEl();
|
||||
if (!stage) return false;
|
||||
return document.fullscreenElement === stage || document.webkitFullscreenElement === stage;
|
||||
}
|
||||
|
||||
function updateMapFullscreenButton() {
|
||||
const btn = document.getElementById("map-fullscreen-btn");
|
||||
if (!btn) return;
|
||||
btn.textContent = mapIsFullscreen() ? "Exit Fullscreen" : "Fullscreen";
|
||||
}
|
||||
|
||||
async function toggleMapFullscreen() {
|
||||
const stage = mapStageEl();
|
||||
if (!stage) return;
|
||||
try {
|
||||
if (mapIsFullscreen()) {
|
||||
if (document.exitFullscreen) {
|
||||
await document.exitFullscreen();
|
||||
} else if (document.webkitExitFullscreen) {
|
||||
await document.webkitExitFullscreen();
|
||||
}
|
||||
} else if (stage.requestFullscreen) {
|
||||
await stage.requestFullscreen();
|
||||
} else if (stage.webkitRequestFullscreen) {
|
||||
await stage.webkitRequestFullscreen();
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Map fullscreen toggle failed", err);
|
||||
} finally {
|
||||
updateMapFullscreenButton();
|
||||
sizeAprsMapToViewport();
|
||||
}
|
||||
}
|
||||
|
||||
function initAprsMap() {
|
||||
const mapEl = document.getElementById("aprs-map");
|
||||
if (!mapEl) return;
|
||||
@@ -3776,6 +3816,7 @@ function initAprsMap() {
|
||||
|
||||
const locatorPhaseEl = document.getElementById("map-locator-phase");
|
||||
const locatorChoiceEl = document.getElementById("map-locator-choice-filter");
|
||||
const fullscreenBtn = document.getElementById("map-fullscreen-btn");
|
||||
if (locatorPhaseEl) {
|
||||
locatorPhaseEl.addEventListener("click", (e) => {
|
||||
const btn = e.target.closest(".map-locator-phase-btn[data-phase]");
|
||||
@@ -3815,6 +3856,21 @@ function initAprsMap() {
|
||||
applyMapFilter();
|
||||
});
|
||||
}
|
||||
if (fullscreenBtn) {
|
||||
fullscreenBtn.addEventListener("click", () => {
|
||||
toggleMapFullscreen();
|
||||
});
|
||||
updateMapFullscreenButton();
|
||||
}
|
||||
if (!mapFullscreenListenerBound) {
|
||||
const onFullscreenChange = () => {
|
||||
updateMapFullscreenButton();
|
||||
sizeAprsMapToViewport();
|
||||
};
|
||||
document.addEventListener("fullscreenchange", onFullscreenChange);
|
||||
document.addEventListener("webkitfullscreenchange", onFullscreenChange);
|
||||
mapFullscreenListenerBound = true;
|
||||
}
|
||||
rebuildMapLocatorFilters();
|
||||
}
|
||||
|
||||
@@ -3822,16 +3878,21 @@ function sizeAprsMapToViewport() {
|
||||
const mapEl = document.getElementById("aprs-map");
|
||||
if (!mapEl) return;
|
||||
const mapRect = mapEl.getBoundingClientRect();
|
||||
const stage = mapStageEl();
|
||||
const width = mapEl.clientWidth || mapRect.width;
|
||||
const footer = document.querySelector(".footer");
|
||||
let bottom = window.innerHeight;
|
||||
if (footer) {
|
||||
let bottom = mapIsFullscreen() && stage
|
||||
? stage.getBoundingClientRect().bottom
|
||||
: window.innerHeight;
|
||||
if (!mapIsFullscreen() && footer) {
|
||||
const fr = footer.getBoundingClientRect();
|
||||
if (fr.top > mapRect.top + 50) bottom = fr.top;
|
||||
}
|
||||
const available = Math.max(0, Math.floor(bottom - mapRect.top - 8));
|
||||
const widthDriven = width > 0 ? Math.floor(width / 2.05) : available;
|
||||
const viewportCap = Math.floor(window.innerHeight * 0.56);
|
||||
const viewportCap = mapIsFullscreen()
|
||||
? Math.floor(window.innerHeight * 0.9)
|
||||
: Math.floor(window.innerHeight * 0.56);
|
||||
const minHeight = Math.min(260, available);
|
||||
const target = Math.max(minHeight, Math.min(available, viewportCap, widthDriven));
|
||||
mapEl.style.height = `${target}px`;
|
||||
|
||||
@@ -579,7 +579,10 @@
|
||||
<div id="map-locator-choice-filter" class="map-locator-chip-row"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="aprs-map"></div>
|
||||
<div id="map-stage">
|
||||
<button type="button" id="map-fullscreen-btn" class="map-fullscreen-btn">Fullscreen</button>
|
||||
<div id="aprs-map"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="tab-about" class="tab-panel" style="display:none;">
|
||||
<div id="auth-badge" style="display:none; margin-bottom: 1rem; padding: 0.5rem; background: var(--bg-secondary); border-radius: 0.25rem; color: var(--text-muted); font-size: 0.85rem;">Authenticated as: <strong id="auth-role-badge">--</strong></div>
|
||||
|
||||
@@ -1094,12 +1094,51 @@ small { color: var(--text-muted); }
|
||||
gap: 0.75rem;
|
||||
min-height: 0;
|
||||
}
|
||||
#aprs-map {
|
||||
#map-stage {
|
||||
position: relative;
|
||||
flex: 0 1 auto;
|
||||
min-height: 0;
|
||||
}
|
||||
#aprs-map {
|
||||
width: 100%;
|
||||
min-height: 0;
|
||||
border-radius: 6px;
|
||||
}
|
||||
.map-fullscreen-btn {
|
||||
position: absolute;
|
||||
top: 0.7rem;
|
||||
right: 0.7rem;
|
||||
z-index: 410;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 1.9rem;
|
||||
padding: 0.18rem 0.65rem;
|
||||
border-radius: 6px;
|
||||
border: 1px solid color-mix(in srgb, var(--border-light) 74%, transparent);
|
||||
background: color-mix(in srgb, var(--card-bg) 82%, transparent);
|
||||
color: var(--text);
|
||||
font-size: 0.76rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.02em;
|
||||
cursor: pointer;
|
||||
backdrop-filter: blur(8px);
|
||||
-webkit-backdrop-filter: blur(8px);
|
||||
}
|
||||
.map-fullscreen-btn:hover {
|
||||
border-color: color-mix(in srgb, var(--accent-green) 34%, var(--border-light));
|
||||
color: var(--text-heading);
|
||||
}
|
||||
#map-stage:fullscreen,
|
||||
#map-stage:-webkit-full-screen {
|
||||
background: var(--bg);
|
||||
padding: 0.75rem;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
#map-stage:fullscreen #aprs-map,
|
||||
#map-stage:-webkit-full-screen #aprs-map {
|
||||
border-radius: 8px;
|
||||
}
|
||||
.aprs-controls { display: flex; gap: 0.6rem; align-items: center; margin-bottom: 0.75rem; }
|
||||
.aprs-summary {
|
||||
display: grid;
|
||||
|
||||
Reference in New Issue
Block a user