11<script setup lang="ts">
2- import { ref , computed , onMounted , onUnmounted } from ' vue' ;
2+ import { ref , onMounted , onUnmounted } from ' vue' ;
33import type { Node } from ' ../lib/nodes' ;
44import { makePopupContent } from ' ../lib/popup' ;
55import NodePanel from ' ./NodePanel.vue' ;
@@ -21,7 +21,6 @@ interface TileLayerConfig { url: string; options: Record<string, unknown>; }
2121interface MapStyle { id: string ; label: string ; layers: TileLayerConfig []; }
2222
2323const CARTO_ATTR = ' © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors © <a href="https://carto.com/attributions">CARTO</a>' ;
24- const ESRI_ATTR = ' Tiles © Esri — Esri, DeLorme, NAVTEQ, TomTom, Intermap, iPC, USGS, FAO, NPS, NRCAN, GeoBase, Kadaster NL, Ordnance Survey, Esri Japan, METI, Esri China (Hong Kong), and the GIS User Community' ;
2524
2625const MAP_STYLES: MapStyle [] = [
2726 {
@@ -52,21 +51,12 @@ const MAP_STYLES: MapStyle[] = [
5251 },
5352 ],
5453 },
55- {
56- id: ' fun' ,
57- label: ' Fun' ,
58- layers: [{
59- url: ' https://server.arcgisonline.com/ArcGIS/rest/services/NatGeo_World_Map/MapServer/tile/{z}/{y}/{x}' ,
60- options: { attribution: ESRI_ATTR , maxZoom: 16 },
61- }],
62- },
6354];
6455
6556const STORAGE_KEY = ' pcd-map-style' ;
6657let activeTileLayers: import (' leaflet' ).TileLayer [] = [];
6758
6859const currentStyle = ref <string >(' ' );
69- const availableStyles = computed (() => MAP_STYLES .map (s => ({ id: s .id , label: s .label })));
7060
7161function getInitialStyle(): string {
7262 const stored = localStorage .getItem (STORAGE_KEY );
@@ -89,8 +79,9 @@ function setMapStyle(styleId: string, map: import('leaflet').Map, L: typeof impo
8979 document .documentElement .dataset .theme = styleId === ' dark' ? ' dark' : ' light' ;
9080}
9181
92- function onStyleChange(styleId : string ) {
93- if (mapInstance && leafletRef ) setMapStyle (styleId , mapInstance , leafletRef );
82+ function toggleTheme() {
83+ const next = currentStyle .value === ' dark' ? ' light' : ' dark' ;
84+ if (mapInstance && leafletRef ) setMapStyle (next , mapInstance , leafletRef );
9485}
9586
9687function setActiveMarker(nodeId : string | null ) {
@@ -293,14 +284,25 @@ onUnmounted(() => {
293284 ☰
294285 </button >
295286 <NodePanel :node =" selectedNode " @close =" closePanel " />
287+ <button
288+ id =" theme-toggle"
289+ :aria-label =" currentStyle === 'dark' ? 'Switch to light mode' : 'Switch to dark mode'"
290+ :title =" currentStyle === 'dark' ? 'Switch to light mode' : 'Switch to dark mode'"
291+ @click =" toggleTheme"
292+ >
293+ <svg v-if =" currentStyle === 'dark'" xmlns =" http://www.w3.org/2000/svg" width =" 20" height =" 20" viewBox =" 0 0 24 24" fill =" none" stroke =" currentColor" stroke-width =" 2" stroke-linecap =" round" stroke-linejoin =" round" aria-hidden =" true" >
294+ <circle cx =" 12" cy =" 12" r =" 4" />
295+ <path d =" M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41" />
296+ </svg >
297+ <svg v-else xmlns =" http://www.w3.org/2000/svg" width =" 20" height =" 20" viewBox =" 0 0 24 24" fill =" none" stroke =" currentColor" stroke-width =" 2" stroke-linecap =" round" stroke-linejoin =" round" aria-hidden =" true" >
298+ <path d =" M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />
299+ </svg >
300+ </button >
296301 <NodeList
297302 :nodes =" nodes "
298303 :open =" listOpen "
299- :current-style =" currentStyle "
300- :available-styles =" availableStyles "
301304 @close =" closeList "
302305 @select =" onNodeSelect "
303- @style-change =" onStyleChange "
304306 />
305307</template >
306308
@@ -313,6 +315,36 @@ onUnmounted(() => {
313315 height : 100vh ;
314316 z-index : 0 ;
315317}
318+
319+ #theme-toggle {
320+ position : fixed ;
321+ top : 1rem ;
322+ right : 1rem ;
323+ z-index : var (--z-controls );
324+ width : 40px ;
325+ height : 40px ;
326+ display : flex ;
327+ align-items : center ;
328+ justify-content : center ;
329+ background : var (--color-bg-popup );
330+ border : 1px solid var (--color-border );
331+ border-radius : 8px ;
332+ cursor : pointer ;
333+ color : var (--color-text );
334+ transition : background-color 0.12s ease , color 0.12s ease , border-color 0.12s ease ;
335+ box-shadow : 0 1px 4px rgba (0 , 0 , 0 , 0.15 );
336+ }
337+
338+ #theme-toggle :hover {
339+ background : var (--color-primary );
340+ color : #fff ;
341+ border-color : var (--color-primary );
342+ }
343+
344+ #theme-toggle :focus-visible {
345+ outline : 2px solid var (--color-focus );
346+ outline-offset : 2px ;
347+ }
316348 </style >
317349
318350<style >
0 commit comments