your personal website on atproto - mirror blento.app
at card-command-bar 161 lines 3.6 kB view raw
1<script lang="ts"> 2 import { onMount } from 'svelte'; 3 import 'mapbox-gl/dist/mapbox-gl.css'; 4 import mapboxgl from 'mapbox-gl'; 5 import { env } from '$env/dynamic/public'; 6 import type { Item } from '$lib/types'; 7 import { getHexOfCardColor } from '../helper'; 8 9 let { item = $bindable() }: { item: Item } = $props(); 10 11 // $inspect(item); 12 13 let mapContainer: HTMLElement | undefined = $state(); 14 let map: mapboxgl.Map | undefined = $state(); 15 16 onMount(() => { 17 if (!mapContainer || !env.PUBLIC_MAPBOX_TOKEN) { 18 console.log('no map container or no mapbox token'); 19 return; 20 } 21 22 try { 23 mapboxgl.accessToken = env.PUBLIC_MAPBOX_TOKEN; 24 25 const lat = parseFloat(item.cardData.lat); 26 const lon = parseFloat(item.cardData.lon); 27 const zoom = item.cardData.zoom ? parseFloat(item.cardData.zoom) : 0; 28 const lightPreset = item.cardData.lightPreset || 'day'; 29 30 map = new mapboxgl.Map({ 31 container: mapContainer, 32 style: 'mapbox://styles/mapbox/standard', 33 center: [lon, lat], 34 config: { 35 basemap: { 36 lightPreset: lightPreset, 37 showPointOfInterestLabels: false 38 } 39 }, 40 zoom: zoom, 41 attributionControl: false, 42 dragPan: false, 43 dragRotate: false, 44 keyboard: false, 45 doubleClickZoom: true, 46 touchZoomRotate: true, 47 scrollZoom: true, 48 boxZoom: false, 49 pitchWithRotate: false, 50 touchPitch: false 51 }); 52 53 // Keep location centered during zoom and save zoom level 54 map.on('zoom', () => { 55 if (map) { 56 map.setCenter([lon, lat]); 57 } 58 }); 59 60 map.on('zoomend', () => { 61 if (map) { 62 item.cardData.zoom = map.getZoom().toString(); 63 } 64 }); 65 66 map.on('load', () => { 67 if (!map) return; 68 69 map.resize(); 70 map.setCenter([lon, lat]); 71 72 const accentColor = getHexOfCardColor(item); 73 74 // Add location point source 75 map.addSource('location-point', { 76 type: 'geojson', 77 data: { 78 type: 'Feature', 79 geometry: { 80 type: 'Point', 81 coordinates: [lon, lat] 82 }, 83 properties: { 84 name: item.cardData.name || '' 85 } 86 } 87 }); 88 89 // Outer glow 90 map.addLayer({ 91 id: 'location-glow-outer', 92 type: 'circle', 93 source: 'location-point', 94 paint: { 95 'circle-radius': 20, 96 'circle-color': accentColor, 97 'circle-opacity': 0.15, 98 'circle-blur': 1 99 } 100 }); 101 102 // Middle glow 103 map.addLayer({ 104 id: 'location-glow-middle', 105 type: 'circle', 106 source: 'location-point', 107 paint: { 108 'circle-radius': 12, 109 'circle-color': accentColor, 110 'circle-opacity': 0.3, 111 'circle-blur': 0.5 112 } 113 }); 114 115 // White border 116 map.addLayer({ 117 id: 'location-dot-border', 118 type: 'circle', 119 source: 'location-point', 120 paint: { 121 'circle-radius': 8, 122 'circle-color': '#ffffff', 123 'circle-opacity': 1 124 } 125 }); 126 127 // Accent color center dot 128 map.addLayer({ 129 id: 'location-dot', 130 type: 'circle', 131 source: 'location-point', 132 paint: { 133 'circle-radius': 6, 134 'circle-color': accentColor, 135 'circle-opacity': 1 136 } 137 }); 138 }); 139 140 // Handle container resize 141 const resizeObserver = new ResizeObserver(() => { 142 if (map) { 143 map.resize(); 144 map.setCenter([lon, lat]); 145 } 146 }); 147 if (mapContainer) resizeObserver.observe(mapContainer); 148 149 return () => { 150 resizeObserver.disconnect(); 151 if (map) { 152 map.remove(); 153 } 154 }; 155 } catch (err) { 156 console.error(`Something went wrong trying to initialize the map`, err); 157 } 158 }); 159</script> 160 161<div bind:this={mapContainer} class="absolute inset-0 isolate h-full w-full"></div>