Initial commit: Coffee Map PWA
Vue 3 + Vite PWA backed by ATProto PDS (coffee.apoena.dev). Stores coffee spots as dev.apoena.coffeespot records with name, geolocation, note, and status. Map via MapLibre + OpenFreeMap, auth via ATProto OAuth, deploy via Docker + Nginx on Coolify. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
64
src/components/MapView.vue
Normal file
64
src/components/MapView.vue
Normal file
@@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<div ref="mapEl" class="w-full h-full" />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted, watch } from 'vue'
|
||||
import maplibregl from 'maplibre-gl'
|
||||
import 'maplibre-gl/dist/maplibre-gl.css'
|
||||
import type { Shop } from '@/stores/shops'
|
||||
|
||||
const props = defineProps<{ shops: Shop[] }>()
|
||||
const emit = defineEmits<{ select: [shop: Shop] }>()
|
||||
|
||||
const mapEl = ref<HTMLElement | null>(null)
|
||||
let map: maplibregl.Map | null = null
|
||||
const markers: maplibregl.Marker[] = []
|
||||
|
||||
onMounted(() => {
|
||||
if (!mapEl.value) return
|
||||
|
||||
map = new maplibregl.Map({
|
||||
container: mapEl.value,
|
||||
// OpenFreeMap — free, no API key, Google-free
|
||||
style: 'https://tiles.openfreemap.org/styles/liberty',
|
||||
center: [2.3522, 48.8566], // Paris
|
||||
zoom: 13,
|
||||
})
|
||||
|
||||
map.addControl(new maplibregl.NavigationControl(), 'top-right')
|
||||
map.addControl(new maplibregl.GeolocateControl({ trackUserLocation: true }), 'top-right')
|
||||
|
||||
map.on('load', () => renderMarkers())
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
markers.forEach((m) => m.remove())
|
||||
map?.remove()
|
||||
})
|
||||
|
||||
watch(() => props.shops, renderMarkers, { deep: true })
|
||||
|
||||
function renderMarkers() {
|
||||
if (!map) return
|
||||
markers.forEach((m) => m.remove())
|
||||
markers.length = 0
|
||||
|
||||
for (const shop of props.shops) {
|
||||
if (shop.record.lat == null || shop.record.lng == null) continue
|
||||
|
||||
const el = document.createElement('div')
|
||||
el.className = 'coffee-marker'
|
||||
el.textContent = shop.record.status === 'visited' ? '✅' : '☕'
|
||||
el.title = shop.record.name
|
||||
el.style.cssText = 'cursor:pointer;font-size:1.5rem;filter:drop-shadow(0 1px 2px rgba(0,0,0,.4))'
|
||||
|
||||
const marker = new maplibregl.Marker({ element: el })
|
||||
.setLngLat([shop.record.lng, shop.record.lat])
|
||||
.addTo(map!)
|
||||
|
||||
el.addEventListener('click', () => emit('select', shop))
|
||||
markers.push(marker)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user