Refactored marker into component

This commit is contained in:
Alexis
2025-08-23 22:34:18 +02:00
parent 3188148b06
commit ce162c9ea6
6 changed files with 135 additions and 88 deletions

View File

@@ -1,4 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256">
<circle fill="#fff" cx="128" cy="128" r="128" /> <circle fill="#fff" cx="128" cy="128" r="128" />
<path fill="#000" transform="scale(0.6)" transform-origin="center" d="M216,88H200V72a40,40,0,0,0-40-40H148.82c-11.91-10.2-28-16-44.82-16C68.71,16,40,41.12,40,72V208a16,16,0,0,0,16,16H184a16,16,0,0,0,16-16v-8h16a24,24,0,0,0,24-24V112A24,24,0,0,0,216,88ZM104,184a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0Zm48,0a8,8,0,0,1-16,0V104a8,8,0,0,1,16,0ZM57,64c4.46-18.24,23.85-32,47-32,13.87,0,27.06,5,36.21,13.78A8,8,0,0,0,145.74,48H160a24,24,0,0,1,22.62,16ZM224,176a8,8,0,0,1-8,8H200V104h16a8,8,0,0,1,8,8Z"></path> <path fill="#000" transform="scale(0.55)" transform-origin="center" d="M237.66,45.66A8,8,0,0,0,232,32H24a8,8,0,0,0-5.66,13.66L120,147.31V208H88a8,8,0,0,0,0,16h80a8,8,0,0,0,0-16H136V147.31Zm-25,2.34-16,16H59.31l-16-16Z"></path>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 626 B

After

Width:  |  Height:  |  Size: 354 B

View File

@@ -1,4 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" color="#000000" fill="none">
<circle fill="#fff" cx="128" cy="128" r="128" /> <circle fill="#fff" cx="12" cy="12" r="12" />
<path fill="#000" transform="scale(0.6)" transform-origin="center" d="M229.52,74.21a8,8,0,0,0-7.13-2A44,44,0,0,0,168,41.67a44,44,0,0,0-80,0,44,44,0,0,0-54.4,30.51,8,8,0,0,0-9.4,9.65L54.76,211.67A16,16,0,0,0,70.34,224H185.66a16,16,0,0,0,15.58-12.33L231.79,81.83A8,8,0,0,0,229.52,74.21ZM70.34,208,42.91,91.44l37.85,10.81L94.86,208ZM122.06,73.76,87.57,87.56,49,76.54a28,28,0,0,1,40.1-17.28,8,8,0,0,0,11.56-5.34,28,28,0,0,1,54.66,0,8,8,0,0,0,11.56,5.34A28,28,0,0,1,207,76.54l-38.56,11-34.49-13.8A16,16,0,0,0,122.06,73.76ZM185.66,208H161.14l14.1-105.75,37.85-10.81Z"></path> <path fill="#000" transform="scale(0.6)" transform-origin="center" d="M11.829,1.25 L12.171,1.25 L12.214,1.25 C12.608,1.25 12.973,1.25 13.271,1.288 C13.598,1.331 13.955,1.431 14.252,1.717 C14.554,2.008 14.663,2.362 14.709,2.691 C14.75,2.983 14.75,3.339 14.75,3.716 L14.75,6.925 L15.283,5.96 L15.304,5.921 C15.505,5.557 15.69,5.222 15.876,4.969 C16.08,4.693 16.354,4.419 16.768,4.303 C17.186,4.186 17.562,4.281 17.878,4.417 C18.163,4.539 18.486,4.735 18.833,4.944 L18.872,4.967 L19.069,5.087 L19.109,5.11 C19.477,5.333 19.818,5.539 20.072,5.745 C20.354,5.974 20.617,6.271 20.714,6.705 C20.81,7.135 20.702,7.516 20.55,7.846 C20.412,8.146 20.195,8.486 19.959,8.859 L19.934,8.897 L19.709,9.251 L20.596,9.254 L20.638,9.259 C21.336,9.34 21.849,9.982 21.735,10.696 L21.728,10.741 L19.492,18.56 L19.48,18.604 C19.239,19.444 19.04,20.138 18.812,20.681 C18.572,21.253 18.271,21.732 17.784,22.094 C17.298,22.456 16.75,22.61 16.129,22.682 C15.537,22.75 14.805,22.75 13.915,22.75 L10.086,22.75 C9.195,22.75 8.463,22.75 7.871,22.682 C7.25,22.61 6.703,22.456 6.217,22.094 C5.73,21.732 5.428,21.253 5.188,20.681 C4.96,20.138 4.761,19.444 4.521,18.604 L2.273,10.741 L2.266,10.696 C2.152,9.982 2.665,9.34 3.362,9.259 L3.405,9.254 L4.84,9.25 C4.989,9.25 5.138,9.257 5.285,9.27 L3.946,6.863 L3.925,6.825 C3.717,6.452 3.526,6.11 3.407,5.811 C3.277,5.482 3.191,5.107 3.299,4.693 C3.406,4.277 3.664,3.992 3.941,3.771 C4.191,3.572 4.523,3.374 4.882,3.159 L4.882,3.159 L4.92,3.136 L5.141,3.004 L5.181,2.98 L5.181,2.98 C5.559,2.755 5.906,2.547 6.211,2.418 C6.546,2.276 6.943,2.18 7.378,2.314 C7.811,2.447 8.087,2.747 8.29,3.049 C8.475,3.326 8.656,3.695 8.854,4.098 L8.854,4.098 L8.874,4.139 L9.25,4.905 L9.25,3.76 L9.25,3.716 L9.25,3.716 C9.25,3.339 9.25,2.983 9.291,2.691 C9.337,2.362 9.446,2.008 9.748,1.717 C10.045,1.431 10.402,1.331 10.729,1.288 C11.027,1.25 11.392,1.25 11.786,1.25 Z M4.841,10.75 L3.837,10.752 L5.95,18.147 C6.207,19.042 6.382,19.649 6.571,20.101 C6.753,20.533 6.918,20.746 7.112,20.891 C7.308,21.037 7.566,21.136 8.043,21.191 C8.539,21.249 9.184,21.25 10.13,21.25 L13.871,21.25 C14.816,21.25 15.461,21.249 15.958,21.191 C16.435,21.136 16.692,21.037 16.888,20.891 C17.083,20.746 17.248,20.533 17.429,20.101 C17.619,19.649 17.794,19.042 18.05,18.148 L20.164,10.752 L19.161,10.75 C17.659,10.75 16.351,11.728 15.944,13.126 C15.749,13.799 15.128,14.25 14.437,14.25 L9.564,14.25 C8.872,14.25 8.252,13.799 8.056,13.126 C7.649,11.728 6.342,10.75 4.841,10.75 Z M10.75,8.115 C10.75,8.128 10.75,8.14 10.75,8.153 L10.75,12.75 L13.25,12.75 L13.25,3.76 C13.25,3.323 13.248,3.074 13.224,2.898 C13.217,2.85 13.21,2.819 13.205,2.801 C13.183,2.794 13.144,2.784 13.078,2.776 C12.889,2.751 12.623,2.75 12.171,2.75 L11.829,2.75 C11.377,2.75 11.111,2.751 10.922,2.776 C10.856,2.784 10.817,2.794 10.795,2.801 C10.79,2.819 10.783,2.85 10.776,2.898 C10.752,3.074 10.75,3.323 10.75,3.76 Z M7.528,4.8 C7.303,4.342 7.166,4.067 7.043,3.884 C6.991,3.806 6.956,3.768 6.938,3.752 C6.916,3.756 6.871,3.768 6.794,3.8 C6.598,3.883 6.342,4.034 5.91,4.292 L5.69,4.424 C5.279,4.669 5.037,4.815 4.877,4.943 C4.804,5.001 4.772,5.038 4.759,5.055 C4.753,5.063 4.751,5.067 4.751,5.067 L4.751,5.069 L4.75,5.073 C4.75,5.073 4.75,5.08 4.751,5.091 C4.755,5.117 4.766,5.167 4.801,5.258 C4.879,5.454 5.02,5.708 5.256,6.133 L7.386,9.963 C8.191,10.453 8.847,11.172 9.25,12.047 L9.25,8.308 Z M14.75,10.028 L14.75,12.048 C15.339,10.769 16.468,9.822 17.813,9.438 L18.669,8.092 C18.937,7.669 19.097,7.415 19.188,7.217 C19.23,7.126 19.244,7.076 19.248,7.051 L19.25,7.034 L19.25,7.033 L19.25,7.032 C19.249,7.031 19.248,7.028 19.244,7.023 C19.232,7.006 19.201,6.969 19.126,6.909 C18.964,6.777 18.715,6.625 18.294,6.371 L18.097,6.251 C17.697,6.01 17.464,5.871 17.286,5.795 C17.23,5.771 17.194,5.76 17.173,5.754 C17.155,5.771 17.126,5.802 17.083,5.86 C16.963,6.023 16.826,6.269 16.596,6.685 Z M13.008,17 C13.008,17.552 12.56,18 12.008,18 L11.999,18 C11.446,18 10.999,17.552 10.999,17 C10.999,16.448 11.446,16 11.999,16 L12.008,16 C12.56,16 13.008,16.448 13.008,17 Z M6.957,3.75 C6.957,3.75 6.955,3.75 6.949,3.75 C6.954,3.751 6.957,3.75 6.957,3.75 Z M6.93,3.745 C6.925,3.741 6.923,3.741 6.923,3.741 C6.923,3.741 6.925,3.743 6.93,3.745 Z M17.154,5.751 C17.149,5.75 17.147,5.75 17.147,5.75 C17.147,5.75 17.15,5.751 17.154,5.751 Z M17.192,5.739 L17.193,5.738 C17.193,5.738 17.191,5.739 17.187,5.742 C17.19,5.741 17.191,5.74 17.192,5.739 Z M13.198,2.781 C13.197,2.777 13.196,2.775 13.195,2.776 Z M10.805,2.776 L10.802,2.781 C10.804,2.778 10.805,2.776 10.805,2.776 Z M10.777,2.807 C10.773,2.809 10.771,2.81 10.771,2.81 Z M13.229,2.81 L13.223,2.807 C13.226,2.809 13.228,2.81 13.229,2.81 Z" />
</svg> </svg>

Before

Width:  |  Height:  |  Size: 697 B

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@@ -5,20 +5,17 @@ import 'vue-leaflet-markercluster/dist/style.css'
import L, { type PointTuple } from 'leaflet' import L, { type PointTuple } from 'leaflet'
globalThis.L = L globalThis.L = L
import { LMap, LTileLayer, LMarker, LPopup, LIcon, LControlZoom } from '@vue-leaflet/vue-leaflet' import { LMap, LTileLayer, LControlZoom } from '@vue-leaflet/vue-leaflet'
import { LMarkerClusterGroup } from 'vue-leaflet-markercluster' import { LMarkerClusterGroup } from 'vue-leaflet-markercluster'
import { computed, ref } from 'vue' import { ref } from 'vue'
import { useReviewStore } from './stores/reviews' import { useReviewStore } from './stores/reviews'
import ReviewMarker from './components/ReviewMarker.vue'
const { markers } = useReviewStore() const { markers } = useReviewStore()
const zoom = ref(7) const zoom = ref(7)
const minZoom = 4 const minZoom = 4
const center = ref<PointTuple>([47.809376, -0.637207]) const center = ref<PointTuple>([47.809376, -0.637207])
const iconSize = ref<PointTuple>([38, 38])
const iconAnchor = computed<PointTuple>(() => [iconSize.value[0] / 2, iconSize.value[1]])
const popupOffset = [0, iconSize.value[0] * -0.66]
</script> </script>
<template> <template>
@@ -40,66 +37,7 @@ const popupOffset = [0, iconSize.value[0] * -0.66]
/> />
<LMarkerClusterGroup :max-cluster-radius="18" :disable-clustering-at-zoom="14"> <LMarkerClusterGroup :max-cluster-radius="18" :disable-clustering-at-zoom="14">
<LMarker v-for="marker in markers" :key="marker.title" :lat-lng="marker.coords"> <ReviewMarker v-once v-for="marker in markers" :key="marker.title" :marker />
<LPopup :options="{ offset: popupOffset, maxWidth: 380, minWidth: 180 }">
<div class="spacing">
<h2 class="font-bold text-[1.2em] leading-5">
{{ marker.title }}
</h2>
<p v-html="marker.reviews?.[0].text" />
</div>
</LPopup>
<LIcon
:icon-size
:icon-anchor
:class-name="marker.closed ? 'grayscale' : ''"
>
<img v-if="marker.type === 'bar'" src="/map/icons/bar.svg" />
<img v-if="marker.type === 'bakery'" src="/map/icons/bakery.svg" />
<img v-if="marker.type === 'tea-shop'" src="/map/icons/tea-shop.svg" />
<img v-if="marker.type === 'coffee-shop'" src="/map/icons/coffee-shop.svg" />
<img v-if="marker.type === 'ice-cream'" src="/map/icons/ice-cream.svg" />
<template v-else-if="marker.type === 'restaurant'">
<img v-if="marker.food === 'asian'" src="/map/icons/asian.svg" />
<img v-else-if="marker.food === 'burgers'" src="/map/icons/burgers.svg" />
<!-- <img v-else-if="marker.food === 'creperie'" src="/map/icons/creperie.svg" /> -->
<!-- <img v-else-if="marker.food === 'eastern'" src="/map/icons/eastern.svg" /> -->
<img v-else-if="marker.food === 'fast-food'" src="/map/icons/fast-food.svg" />
<img v-else-if="marker.food === 'italian'" src="/map/icons/italian.svg" />
<img v-else-if="marker.food === 'seafood'" src="/map/icons/seafood.svg" />
<!-- <img v-else-if="marker.food === 'street-food'" src="/map/icons/street-food.svg" /> -->
<img v-else-if="marker.food === 'vegetarian'" src="/map/icons/vegetarian.svg" />
<img v-else src="/map/icons/restaurant.svg" />
</template>
<div
class="absolute -top-2 -right-2 size-5 text-center grid place-items-center rounded-full text-[0.65rem] font-bold tracking-[-0.075em]"
:class="{
'text-white bg-red-500': marker.reviews?.[0].grade! <= 1.5,
'text-black bg-amber-400': marker.reviews?.[0].grade! > 1.5 && marker.reviews?.[0].grade! <= 2.5,
'text-black bg-lime-400': marker.reviews?.[0].grade! > 2.5 && marker.reviews?.[0].grade! < 4.0,
'text-black bg-green-400': marker.reviews?.[0].grade! > 2.5 && marker.reviews?.[0].grade! < 4.7,
'text-black bg-sky-400': marker.reviews?.[0].grade! >= 4.7
}"
>
{{ marker.reviews?.[0].grade }}
</div>
<div
class="absolute -z-10 -inset-0.5 rounded-full"
:class="{
'bg-red-500': marker.reviews?.[0].grade! <= 1.5,
'bg-amber-400': marker.reviews?.[0].grade! > 1.5 && marker.reviews?.[0].grade! <= 2.5,
'bg-lime-400': marker.reviews?.[0].grade! > 2.5 && marker.reviews?.[0].grade! < 4.0,
'bg-green-400': marker.reviews?.[0].grade! > 2.5 && marker.reviews?.[0].grade! < 4.7,
'bg-sky-400': marker.reviews?.[0].grade! >= 4.7
}"
/>
</LIcon>
</LMarker>
</LMarkerClusterGroup> </LMarkerClusterGroup>
</LMap> </LMap>
</main> </main>
@@ -128,6 +66,10 @@ main {
} }
} }
.marker-cluster span {
font-weight: var(--font-weight-medium);
}
.marker-cluster-small { .marker-cluster-small {
background-color: color-mix(in srgb, var(--color-green-500) 25%, transparent); background-color: color-mix(in srgb, var(--color-green-500) 25%, transparent);

View File

@@ -0,0 +1,69 @@
<script setup lang="ts">
import type { ReviewMarker } from '@/models/Markers';
import { LMarker, LPopup, LIcon } from '@vue-leaflet/vue-leaflet'
import type { PointTuple } from 'leaflet';
import { computed, ref } from 'vue';
defineProps<{
marker: ReviewMarker
}>()
const iconSize = ref<PointTuple>([38, 38])
const iconAnchor = computed<PointTuple>(() => [iconSize.value[0] / 2, iconSize.value[1]])
const popupOffset = [0, iconSize.value[0] * -0.66]
</script>
<template>
<LMarker :lat-lng="marker.coords">
<LPopup :options="{ offset: popupOffset, maxWidth: 380, minWidth: 180 }">
<div class="spacing">
<h2 class="font-bold text-[1.2em] leading-5">
{{ marker.title }}
</h2>
<p v-html="marker.reviews?.[0].text" />
</div>
</LPopup>
<LIcon :icon-size :icon-anchor :class-name="marker.closed ? 'grayscale' : ''">
<img v-if="marker.type === 'bar'" src="/map/icons/bar.svg" />
<img v-if="marker.type === 'bakery'" src="/map/icons/bakery.svg" />
<img v-if="marker.type === 'tea-shop'" src="/map/icons/tea-shop.svg" />
<img v-if="marker.type === 'coffee-shop'" src="/map/icons/coffee-shop.svg" />
<img v-if="marker.type === 'ice-cream'" src="/map/icons/ice-cream.svg" />
<template v-else-if="marker.type === 'restaurant'">
<img v-if="marker.food === 'asian'" src="/map/icons/asian.svg" />
<img v-else-if="marker.food === 'burgers'" src="/map/icons/burgers.svg" />
<!-- <img v-else-if="marker.food === 'creperie'" src="/map/icons/creperie.svg" /> -->
<!-- <img v-else-if="marker.food === 'eastern'" src="/map/icons/eastern.svg" /> -->
<img v-else-if="marker.food === 'fast-food'" src="/map/icons/fast-food.svg" />
<img v-else-if="marker.food === 'italian'" src="/map/icons/italian.svg" />
<img v-else-if="marker.food === 'seafood'" src="/map/icons/seafood.svg" />
<!-- <img v-else-if="marker.food === 'street-food'" src="/map/icons/street-food.svg" /> -->
<img v-else-if="marker.food === 'vegetarian'" src="/map/icons/vegetarian.svg" />
<img v-else src="/map/icons/restaurant.svg" />
</template>
<div
class="absolute -top-2 -right-2 size-5 text-center grid place-items-center rounded-full text-[0.65rem] font-bold tracking-[-0.075em]"
:class="{
'text-white bg-red-500': marker.reviews?.[0].grade! <= 1.5,
'text-black bg-amber-400': marker.reviews?.[0].grade! > 1.5 && marker.reviews?.[0].grade! <= 2.5,
'text-black bg-lime-400': marker.reviews?.[0].grade! > 2.5 && marker.reviews?.[0].grade! < 4.0,
'text-black bg-green-400': marker.reviews?.[0].grade! > 2.5 && marker.reviews?.[0].grade! < 4.7,
'text-black bg-sky-400': marker.reviews?.[0].grade! >= 4.7
}">
{{ marker.reviews?.[0].grade }}
</div>
<div class="absolute -z-10 -inset-0.5 rounded-full" :class="{
'bg-red-500': marker.reviews?.[0].grade! <= 1.5,
'bg-amber-400': marker.reviews?.[0].grade! > 1.5 && marker.reviews?.[0].grade! <= 2.5,
'bg-lime-400': marker.reviews?.[0].grade! > 2.5 && marker.reviews?.[0].grade! < 4.0,
'bg-green-400': marker.reviews?.[0].grade! > 2.5 && marker.reviews?.[0].grade! < 4.7,
'bg-sky-400': marker.reviews?.[0].grade! >= 4.7
}" />
</LIcon>
</LMarker>
</template>

18
src/models/Markers.ts Normal file
View File

@@ -0,0 +1,18 @@
import type { PointTuple } from "leaflet"
export type MarkerType = "restaurant" | "bar" | "bakery" | "tea-shop" | "coffee-shop" | "ice-cream"
export type FoodType = "mixed" | "asian" | "vegetarian" | "italian" | "fast-food" | "street-food" | "burgers" | "seafood" | "creperie" | "eastern"
export interface ReviewMarker {
coords: PointTuple
title?: string
type?: MarkerType
food?: FoodType
reviews?: Review[]
closed?: boolean
}
export interface Review {
text?: string
grade: number
}

View File

@@ -1,23 +1,6 @@
import type { PointTuple } from 'leaflet' import type { ReviewMarker } from '@/models/Markers'
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
type MarkerType = "restaurant" | "bar" | "bakery" | "tea-shop" | "coffee-shop" | "ice-cream"
type FoodType = "mixed" | "asian" | "vegetarian" | "italian" | "fast-food" | "street-food" | "burgers" | "seafood" | "creperie" | "eastern"
interface ReviewMarker {
coords: PointTuple
title?: string
type?: MarkerType
food?: FoodType
reviews?: Review[]
closed?: boolean
}
interface Review {
text?: string
grade: number
}
export const useReviewStore = defineStore('reviews', () => { export const useReviewStore = defineStore('reviews', () => {
const markers: ReviewMarker[] = [ const markers: ReviewMarker[] = [
{ {
@@ -475,6 +458,18 @@ const markers: ReviewMarker[] = [
} }
] ]
}, },
{
title: "La Belle Normande",
coords: [48.63542201422258, -1.5098922028521766],
type: "restaurant",
food: "creperie",
reviews: [
{
grade: 0.8,
text: "De la bouffe industrielle dans un attrape-touriste avec une marge multipliée par trois ? C'est ici."
}
]
},
{ {
title: "Saïgon Cà phê", title: "Saïgon Cà phê",
coords: [49.254911553043485, 4.027036477700138], coords: [49.254911553043485, 4.027036477700138],
@@ -487,6 +482,18 @@ const markers: ReviewMarker[] = [
} }
] ]
}, },
{
title: "Tacos Kebab 51",
coords: [48.72660636228893, 4.584080588216441],
type: "restaurant",
food: "fast-food",
reviews: [
{
grade: 1.8,
text: "Un kebab correct, rien de plus."
}
]
},
{ {
title: "La Calabraise d'Erlon", title: "La Calabraise d'Erlon",
coords: [49.256709879454554, 4.025919584640479], coords: [49.256709879454554, 4.025919584640479],
@@ -523,6 +530,17 @@ const markers: ReviewMarker[] = [
} }
] ]
}, },
{
title: "Roadside",
coords: [48.646920384150576, -2.0086050666279425],
type: "restaurant",
food: "street-food",
reviews: [
{
grade: 2.9
}
]
},
{ {
title: "Pitaya Les Lices", title: "Pitaya Les Lices",
coords: [48.11275152309647, -1.681332848723036], coords: [48.11275152309647, -1.681332848723036],