WIP: Begin unified map refactor - HTML structure, storage, CSS, state updates
This commit is contained in:
1 parent
f4505b5650
commit
98c45bb26f
20 files changed
+7354
-88
No files matched your search
+36
-10
@@ -31,6 +31,16 @@ const STORAGE_KEYS = {
|
||||
autoDetect: 'weather.autoDetect',
|
||||
showLightning: 'weather.showLightning',
|
||||
showWind: 'weather.showWind',
|
||||
showRadarLayer: 'weather.map.radar',
|
||||
showSatelliteLayer: 'weather.map.satellite',
|
||||
showLightningLayer: 'weather.map.lightning',
|
||||
showWindLayer: 'weather.map.wind',
|
||||
showAqiLayer: 'weather.map.aqi',
|
||||
showAlertsLayer: 'weather.map.alerts',
|
||||
showTempMarker: 'weather.map.temp',
|
||||
showSunMoonMarkers: 'weather.map.sunMoon',
|
||||
unifiedMapBaseLayer: 'weather.map.baseLayer',
|
||||
unifiedMapRadarMode: 'weather.map.radarMode',
|
||||
};
|
||||
|
||||
const state = {
|
||||
@@ -48,7 +58,23 @@ const state = {
|
||||
lastRefreshAt: 0,
|
||||
lightningMarkers: [],
|
||||
windArrows: [],
|
||||
aqiMarkers: [],
|
||||
alertMarkers: [],
|
||||
tempMarker: null,
|
||||
sunMoonMarkers: [],
|
||||
showExtendedForecast: false,
|
||||
unifiedMapLayers: {
|
||||
radar: localStorage.getItem(STORAGE_KEYS.showRadarLayer) !== 'false',
|
||||
satellite: localStorage.getItem(STORAGE_KEYS.showSatelliteLayer) !== 'false',
|
||||
lightning: localStorage.getItem(STORAGE_KEYS.showLightningLayer) !== 'false',
|
||||
wind: localStorage.getItem(STORAGE_KEYS.showWindLayer) !== 'false',
|
||||
aqi: localStorage.getItem(STORAGE_KEYS.showAqiLayer) !== 'false',
|
||||
alerts: localStorage.getItem(STORAGE_KEYS.showAlertsLayer) !== 'false',
|
||||
temp: localStorage.getItem(STORAGE_KEYS.showTempMarker) !== 'false',
|
||||
sunMoon: localStorage.getItem(STORAGE_KEYS.showSunMoonMarkers) !== 'false',
|
||||
},
|
||||
unifiedMapBaseLayer: localStorage.getItem(STORAGE_KEYS.unifiedMapBaseLayer) || 'dark',
|
||||
unifiedMapRadarMode: localStorage.getItem(STORAGE_KEYS.unifiedMapRadarMode) || 'radar',
|
||||
};
|
||||
|
||||
// ── DOM ──────────────────────────────────────────────────────────────────────
|
||||
@@ -315,6 +341,7 @@ function renderSavedCities() {
|
||||
}
|
||||
|
||||
// ── Tab switching ─────────────────────────────────────────────────────────────
|
||||
|
||||
function activateTab(tabName) {
|
||||
document.querySelectorAll('.tab, .bottom-tab').forEach(t => t.classList.remove('active'));
|
||||
document.querySelectorAll('.tab-panel').forEach(p => hide(p));
|
||||
@@ -322,24 +349,23 @@ function activateTab(tabName) {
|
||||
show(document.getElementById('tab-' + tabName));
|
||||
|
||||
if (tabName === 'conditions') {
|
||||
setTimeout(() => { radarEmbed.refresh && radarEmbed.refresh(); }, 60);
|
||||
setTimeout(() => { radarEmbed && radarEmbed.refresh && radarEmbed.refresh(); }, 60);
|
||||
}
|
||||
if (tabName === 'radar-full') {
|
||||
setTimeout(() => { radarFull.refresh && radarFull.refresh(); }, 50);
|
||||
}
|
||||
if (tabName === 'maps') {
|
||||
setTimeout(refreshMapsOverview, 80);
|
||||
if (tabName === 'unified-map') {
|
||||
setTimeout(() => { unifiedMap && unifiedMap.refresh && unifiedMap.refresh(); }, 50);
|
||||
}
|
||||
}
|
||||
|
||||
document.querySelectorAll('.tab, .bottom-tab').forEach(btn => {
|
||||
btn.addEventListener('click', () => activateTab(btn.dataset.tab));
|
||||
});
|
||||
|
||||
document.getElementById('open-radar-tab').addEventListener('click', () => {
|
||||
activateTab('radar-full');
|
||||
});
|
||||
|
||||
const openMapTabBtn = document.getElementById('open-map-tab');
|
||||
if (openMapTabBtn) {
|
||||
openMapTabBtn.addEventListener('click', () => {
|
||||
activateTab('unified-map');
|
||||
});
|
||||
}
|
||||
// ── APIs ─────────────────────────────────────────────────────────────────────
|
||||
async function geocode(q) {
|
||||
const r = await fetch(`/api/geocode?q=${encodeURIComponent(q)}`);
|
||||
|
||||
+226
-77
@@ -3,17 +3,15 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
|
||||
<title>Weather</title>
|
||||
<title>NeCloud Weather</title>
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
<link rel="apple-touch-icon" href="/icons/icon.svg">
|
||||
<meta name="theme-color" content="#f5f2ec" media="(prefers-color-scheme: light)">
|
||||
<meta name="theme-color" content="#191510" media="(prefers-color-scheme: dark)">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||
<meta name="apple-mobile-web-app-title" content="Weather">
|
||||
<meta name="apple-mobile-web-app-title" content="NeCloud Weather">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link href="https://fonts.googleapis.com/css2?family=DM+Mono:wght@300;400;500&family=Playfair+Display:wght@400;500&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
@@ -21,11 +19,10 @@
|
||||
<div class="app">
|
||||
|
||||
<header class="masthead">
|
||||
<h1>Weather</h1>
|
||||
<h1>NeCloud Weather</h1>
|
||||
<nav class="tabs">
|
||||
<button class="tab active" data-tab="conditions">Conditions</button>
|
||||
<button class="tab" data-tab="radar-full">Radar</button>
|
||||
<button class="tab" data-tab="maps">Maps</button>
|
||||
<button class="tab" data-tab="unified-map">Map</button>
|
||||
<button class="tab" data-tab="alerts">Alerts</button>
|
||||
<button class="tab" data-tab="settings">Settings</button>
|
||||
</nav>
|
||||
@@ -46,7 +43,13 @@
|
||||
|
||||
<!-- Loading -->
|
||||
<div id="loading" class="loading hidden">
|
||||
<span class="loading-dot"></span>Fetching weather data…
|
||||
<div class="loading-container">
|
||||
<div class="loading-icon">🌤️</div>
|
||||
<div class="loading-text">Fetching weather data…</div>
|
||||
<div class="loading-dots">
|
||||
<span></span><span></span><span></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── TAB: CONDITIONS ── -->
|
||||
@@ -64,47 +67,132 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Current hero -->
|
||||
<div class="current-grid">
|
||||
<div class="current-temp">
|
||||
<div class="temp-big"><span id="cur-temp">--</span><span class="deg">°F</span></div>
|
||||
<!-- ── HERO ── -->
|
||||
<div class="hero">
|
||||
<div class="hero-left">
|
||||
<div id="cur-icon" class="hero-icon"></div>
|
||||
<div id="cur-desc" class="hero-desc"></div>
|
||||
<div id="cur-precip" class="hero-sub"></div>
|
||||
</div>
|
||||
<div class="hero-center">
|
||||
<div class="hero-temp"><span id="cur-temp">--</span><span class="deg">°F</span></div>
|
||||
<div class="feels">Feels like <span id="cur-feels">--</span>°F</div>
|
||||
<div class="hi-lo">↑<span id="cur-hi">--</span>° · ↓<span id="cur-lo">--</span>°</div>
|
||||
</div>
|
||||
<div class="current-cond">
|
||||
<div id="cur-icon" class="cond-icon"></div>
|
||||
<div id="cur-desc" class="cond-desc"></div>
|
||||
<div id="cur-precip" class="cond-sub"></div>
|
||||
<div class="hero-right">
|
||||
<div class="hero-hilo">
|
||||
<div class="hilo-item">
|
||||
<span class="hilo-arrow">↑</span>
|
||||
<span id="cur-hi">--</span>°
|
||||
<div class="hilo-label">High</div>
|
||||
</div>
|
||||
<div class="hilo-divider"></div>
|
||||
<div class="hilo-item">
|
||||
<span class="hilo-arrow">↓</span>
|
||||
<span id="cur-lo">--</span>°
|
||||
<div class="hilo-label">Low</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Stats -->
|
||||
<div class="stats-bar">
|
||||
<div class="stat"><div class="stat-label">Humidity</div><div class="stat-val"><span id="cur-hum">--</span><span class="unit">%</span></div></div>
|
||||
<div class="stat"><div class="stat-label">Wind</div><div class="stat-val"><span id="cur-wind">--</span><span class="unit">mph</span></div></div>
|
||||
<div class="stat"><div class="stat-label">Pressure</div><div class="stat-val"><span id="cur-pres">--</span><span class="unit">hPa</span></div></div>
|
||||
<div class="stat"><div class="stat-label">Visibility</div><div class="stat-val"><span id="cur-vis">--</span><span class="unit">mi</span></div></div>
|
||||
<div class="stat"><div class="stat-label">UV Index</div><div class="stat-val"><span id="cur-uv">--</span></div></div>
|
||||
<div class="stat"><div class="stat-label">Cloud Cover</div><div class="stat-val"><span id="cur-cloud">--</span><span class="unit">%</span></div></div>
|
||||
<!-- ── SUN & MOON TIMELINE ── -->
|
||||
<div class="sun-section">
|
||||
<div class="sun-timeline-wrap">
|
||||
<div class="sun-endpoint">
|
||||
<span class="sun-ep-icon">🌅</span>
|
||||
<span id="sunrise-val" class="sun-ep-time">--</span>
|
||||
</div>
|
||||
<div class="sun-track-wrap">
|
||||
<div class="sun-track">
|
||||
<div class="sun-progress" id="sun-progress"></div>
|
||||
<div class="sun-dot" id="sun-dot">☀️</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sun-endpoint">
|
||||
<span class="sun-ep-icon">🌇</span>
|
||||
<span id="sunset-val" class="sun-ep-time">--</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="moon-row">
|
||||
<span class="moon-emoji">🌙</span>
|
||||
<span id="moon-phase-val">--</span>
|
||||
<span class="daylight-info" id="daylight-info">--</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="astro-grid">
|
||||
<div class="astro-card"><div class="stat-label">Sunrise</div><div id="sunrise-val" class="astro-val">--</div></div>
|
||||
<div class="astro-card"><div class="stat-label">Sunset</div><div id="sunset-val" class="astro-val">--</div></div>
|
||||
<div class="astro-card"><div class="stat-label">Moon Phase</div><div id="moon-phase-val" class="astro-val">--</div></div>
|
||||
<!-- ── WIND & ATMOSPHERE ── -->
|
||||
<div class="section-head">
|
||||
<span class="section-label">Wind & Atmosphere</span>
|
||||
</div>
|
||||
<div class="card-group">
|
||||
<div class="stat-card">
|
||||
<div class="stat-label">Wind</div>
|
||||
<div class="stat-val"><span id="cur-wind">--</span> <span class="stat-unit wind-unit">mph</span></div>
|
||||
<div class="stat-sub" id="cur-wind-dir">--</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-label">Gusts</div>
|
||||
<div class="stat-val"><span id="cur-gust">--</span> <span class="stat-unit gust-unit">mph</span></div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-label">Humidity</div>
|
||||
<div class="stat-val"><span id="cur-hum">--</span><span class="stat-unit">%</span></div>
|
||||
<div class="stat-sub" id="hum-feel"></div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-label">Dewpoint</div>
|
||||
<div class="stat-val"><span id="cur-dew">--</span>°</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-label">Pressure</div>
|
||||
<div class="stat-val"><span id="cur-pres">--</span> <span class="stat-unit">hPa</span></div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-label">Visibility</div>
|
||||
<div class="stat-val"><span id="cur-vis">--</span> <span class="stat-unit vis-unit">mi</span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="aq-grid">
|
||||
<div class="aq-card"><div class="stat-label">US AQI</div><div id="aqi-us" class="aq-val">--</div></div>
|
||||
<div class="aq-card"><div class="stat-label">PM2.5</div><div id="pm25" class="aq-val">--</div></div>
|
||||
<div class="aq-card"><div class="stat-label">Pollen</div><div id="pollen" class="aq-val">--</div></div>
|
||||
<!-- ── MORE DETAILS DRAWER ── -->
|
||||
<details class="details-drawer">
|
||||
<summary class="drawer-summary">More Details <span class="drawer-chevron">›</span></summary>
|
||||
<div class="drawer-body">
|
||||
<div class="card-group drawer-card-group">
|
||||
<div class="stat-card">
|
||||
<div class="stat-label">UV Index</div>
|
||||
<div class="stat-val"><span id="cur-uv">--</span></div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-label">Cloud Cover</div>
|
||||
<div class="stat-val"><span id="cur-cloud">--</span><span class="stat-unit">%</span></div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-label">US AQI</div>
|
||||
<div class="stat-val"><span id="aqi-us">--</span></div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-label">PM2.5</div>
|
||||
<div class="stat-val"><span id="pm25">--</span></div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-label">Pollen</div>
|
||||
<div class="stat-val"><span id="pollen">--</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<!-- ── WHAT TO BRING ── -->
|
||||
<div class="section-head" style="margin-top:1.5rem">
|
||||
<span class="section-label">What to Bring</span>
|
||||
</div>
|
||||
<div id="recommendations" class="recs-grid"></div>
|
||||
|
||||
<!-- Embedded radar panel -->
|
||||
<div class="section-head">
|
||||
<span class="section-label">Radar</span>
|
||||
<span class="section-note">
|
||||
<button class="link-btn" id="open-radar-tab">Open full radar →</button>
|
||||
<button class="link-btn" id="open-map-tab">Open full map →</button>
|
||||
</span>
|
||||
</div>
|
||||
<div class="radar-embed-wrap">
|
||||
@@ -128,7 +216,8 @@
|
||||
|
||||
<!-- 7-day -->
|
||||
<div class="section-head">
|
||||
<span class="section-label">7-Day Forecast</span>
|
||||
<span id="daily-forecast-label" class="section-label">7-Day Forecast</span>
|
||||
<button id="daily-toggle-btn" class="link-btn" type="button">Show 16-day</button>
|
||||
</div>
|
||||
<div id="daily-list" class="daily-list"></div>
|
||||
|
||||
@@ -140,40 +229,97 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── TAB: FULL RADAR ── -->
|
||||
<div id="tab-radar-full" class="tab-panel hidden">
|
||||
<div class="radar-full-wrap">
|
||||
<div id="radar-full" class="radar-map radar-map-full"></div>
|
||||
<div class="radar-controls radar-controls-full">
|
||||
<button id="full-play" class="radar-btn play-btn" title="Play/Pause">▶</button>
|
||||
<button id="full-layer" class="radar-btn layer-btn" title="Toggle radar/satellite">R</button>
|
||||
<div class="radar-timeline" id="full-timeline"></div>
|
||||
<span id="full-timestamp" class="radar-ts">--</span>
|
||||
<!-- ── TAB: UNIFIED MAP ── -->
|
||||
<div id="tab-unified-map" class="tab-panel hidden">
|
||||
<div class="unified-map-wrap">
|
||||
<!-- Map Controls -->
|
||||
<div class="map-controls-header">
|
||||
<div class="map-controls-row">
|
||||
<label for="base-map-select" class="control-label">Base Map:</label>
|
||||
<select id="base-map-select" class="map-select">
|
||||
<option value="dark">Dark</option>
|
||||
<option value="light">Light</option>
|
||||
<option value="satellite">Satellite</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="map-controls-toggle-grid">
|
||||
<label class="toggle-control">
|
||||
<input type="checkbox" id="toggle-radar" data-layer="radar" checked>
|
||||
<span class="toggle-label">📡 Radar</span>
|
||||
</label>
|
||||
<label class="toggle-control">
|
||||
<input type="checkbox" id="toggle-satellite" data-layer="satellite">
|
||||
<span class="toggle-label">🛰️ Satellite</span>
|
||||
</label>
|
||||
<label class="toggle-control">
|
||||
<input type="checkbox" id="toggle-lightning" data-layer="lightning">
|
||||
<span class="toggle-label">⚡ Lightning</span>
|
||||
</label>
|
||||
<label class="toggle-control">
|
||||
<input type="checkbox" id="toggle-wind" data-layer="wind" checked>
|
||||
<span class="toggle-label">💨 Wind</span>
|
||||
</label>
|
||||
<label class="toggle-control">
|
||||
<input type="checkbox" id="toggle-aqi" data-layer="aqi">
|
||||
<span class="toggle-label">💨 Air Quality</span>
|
||||
</label>
|
||||
<label class="toggle-control">
|
||||
<input type="checkbox" id="toggle-alerts" data-layer="alerts">
|
||||
<span class="toggle-label">⚠️ Alerts</span>
|
||||
</label>
|
||||
<label class="toggle-control">
|
||||
<input type="checkbox" id="toggle-temp" data-layer="temp" checked>
|
||||
<span class="toggle-label">🌡️ Temperature</span>
|
||||
</label>
|
||||
<label class="toggle-control">
|
||||
<input type="checkbox" id="toggle-sun-moon" data-layer="sun-moon">
|
||||
<span class="toggle-label">☀️ Sun/Moon</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main Map -->
|
||||
<div id="unified-map" class="unified-map-container"></div>
|
||||
|
||||
<!-- Radar/Satellite Timeline Controls -->
|
||||
<div class="map-timeline-wrap hidden" id="timeline-controls">
|
||||
<div class="radar-controls">
|
||||
<button id="timeline-play" class="radar-btn play-btn" title="Play/Pause">▶</button>
|
||||
<div class="radar-timeline" id="timeline-slider"></div>
|
||||
<span id="timeline-timestamp" class="radar-ts">--</span>
|
||||
</div>
|
||||
<div class="radar-legend">
|
||||
<span class="legend-label">Light</span>
|
||||
<div class="legend-bar"></div>
|
||||
<span class="legend-label">Heavy</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="radar-attribution">Radar tiles © <a href="https://www.rainviewer.com" target="_blank">RainViewer</a> · Map © <a href="https://www.openstreetmap.org/copyright" target="_blank">OpenStreetMap</a></p>
|
||||
</div>
|
||||
|
||||
<!-- ── TAB: MAPS ── -->
|
||||
<div id="tab-maps" class="tab-panel hidden">
|
||||
<div class="maps-wrap">
|
||||
<div id="map-overview" class="radar-map map-overview"></div>
|
||||
<div class="maps-meta">
|
||||
<div class="maps-card">
|
||||
<div class="stat-label">Data Source Location</div>
|
||||
<div id="maps-location" class="maps-value">--</div>
|
||||
<!-- Data Panel -->
|
||||
<div class="map-data-panel hidden" id="data-panel">
|
||||
<div class="panel-location">
|
||||
<span id="panel-location-name">--</span>
|
||||
</div>
|
||||
<div class="maps-card">
|
||||
<div class="stat-label">Current Wind</div>
|
||||
<div id="maps-wind" class="maps-value">--</div>
|
||||
<div class="panel-data">
|
||||
<div class="data-item">
|
||||
<span class="data-label">Wind</span>
|
||||
<span id="panel-wind-speed">--</span>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<span class="data-label">Direction</span>
|
||||
<span id="panel-wind-dir">--</span>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<span class="data-label">Temp</span>
|
||||
<span id="panel-temp">--</span>
|
||||
</div>
|
||||
<div class="data-item">
|
||||
<span class="data-label">AQI</span>
|
||||
<span id="panel-aqi">--</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="panel-note">Toggle lightning strikes and wind overlay in Settings.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -202,9 +348,9 @@
|
||||
|
||||
<div class="settings-row">
|
||||
<span class="settings-label">Auto-detect location on startup</span>
|
||||
<label class="auto-detect-toggle" for="auto-detect-toggle">
|
||||
<label class="switch-toggle">
|
||||
<input id="auto-detect-toggle" type="checkbox" checked>
|
||||
<span>Enabled</span>
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@@ -217,27 +363,30 @@
|
||||
<span class="settings-label">Units</span>
|
||||
<button id="units-btn-settings" class="settings-btn" type="button">US</button>
|
||||
</div>
|
||||
|
||||
<div class="settings-row">
|
||||
<span class="settings-label">Lightning strikes on map</span>
|
||||
<label class="toggle-label" for="lightning-toggle">
|
||||
<input id="lightning-toggle" type="checkbox">
|
||||
<span id="lightning-status">Off</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="settings-row">
|
||||
<span class="settings-label">Wind overlay on map</span>
|
||||
<label class="toggle-label" for="wind-toggle">
|
||||
<input id="wind-toggle" type="checkbox">
|
||||
<span id="wind-status">Off</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div><!-- .app -->
|
||||
|
||||
<nav class="bottom-nav" aria-label="Mobile navigation">
|
||||
<button class="bottom-tab active" data-tab="conditions" type="button">
|
||||
<span class="bottom-icon">⛅</span>
|
||||
<span class="bottom-label">Now</span>
|
||||
</button>
|
||||
<button class="bottom-tab" data-tab="unified-map" type="button">
|
||||
<span class="bottom-icon">🗺️</span>
|
||||
<span class="bottom-label">Map</span>
|
||||
</button>
|
||||
<button class="bottom-tab" data-tab="alerts" type="button">
|
||||
<span class="bottom-icon">⚠️</span>
|
||||
<span class="bottom-label">Alerts</span>
|
||||
</button>
|
||||
<button class="bottom-tab" data-tab="settings" type="button">
|
||||
<span class="bottom-icon">⚙️</span>
|
||||
<span class="bottom-label">Settings</span>
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
||||
<script src="app.js"></script>
|
||||
<script>
|
||||
|
||||
@@ -64,6 +64,17 @@ a:hover { text-decoration: underline; }
|
||||
gap: 0.8rem;
|
||||
border: var(--surface-border);
|
||||
border-radius: var(--surface-radius-lg);
|
||||
/* ── Unified Map ── */
|
||||
.unified-map-wrap {
|
||||
border: var(--surface-border);
|
||||
border-radius: var(--surface-radius-md);
|
||||
background: var(--card-bg);
|
||||
padding: 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
background: linear-gradient(180deg, var(--card-bg-soft), var(--paper-dark));
|
||||
color: var(--card-ink);
|
||||
padding: 1rem 1.1rem;
|
||||
@@ -93,6 +104,116 @@ a:hover { text-decoration: underline; }
|
||||
.tabs {
|
||||
display: flex;
|
||||
gap: 0;
|
||||
/* ── Map Controls ── */
|
||||
.map-controls-header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
padding-bottom: 0.75rem;
|
||||
border-bottom: var(--surface-border);
|
||||
}
|
||||
|
||||
.map-controls-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.control-label {
|
||||
font-size: 0.9rem;
|
||||
font-weight: 500;
|
||||
color: var(--card-ink);
|
||||
}
|
||||
|
||||
.map-select {
|
||||
padding: 0.4rem 0.6rem;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--surface-radius-sm);
|
||||
background: var(--card-bg-soft);
|
||||
color: var(--card-ink);
|
||||
font-size: 0.85rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.map-controls-toggle-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.toggle-control {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.4rem 0.6rem;
|
||||
cursor: pointer;
|
||||
border-radius: var(--surface-radius-sm);
|
||||
transition: background 0.15s;
|
||||
}
|
||||
|
||||
.toggle-control:hover {
|
||||
background: var(--card-bg-soft);
|
||||
}
|
||||
|
||||
.toggle-control input[type="checkbox"] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.toggle-label {
|
||||
font-size: 0.85rem;
|
||||
color: var(--card-sub);
|
||||
}
|
||||
|
||||
.unified-map-container {
|
||||
height: calc(100vh - 490px);
|
||||
min-height: 420px;
|
||||
border: var(--surface-border);
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.map-timeline-wrap {
|
||||
padding: 0.6rem 0.75rem;
|
||||
background: var(--paper-darker);
|
||||
border-top: 1px solid var(--border);
|
||||
border-radius: 0 0 10px 10px;
|
||||
}
|
||||
|
||||
.map-data-panel {
|
||||
background: var(--card-bg-soft);
|
||||
border: var(--surface-border);
|
||||
border-radius: var(--surface-radius-sm);
|
||||
padding: 1rem;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.panel-location {
|
||||
font-weight: 600;
|
||||
color: var(--card-ink);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.panel-data {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.data-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.data-label {
|
||||
font-size: 0.75rem;
|
||||
color: var(--card-sub);
|
||||
text-transform: uppercase;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
|
||||
Reference in new issue
Block a user