Skip to content

Commit c85d278

Browse files
committed
feat(googleMaps): unified styling of static image and map
Fixes #202
1 parent 63e6041 commit c85d278

File tree

3 files changed

+85
-3
lines changed

3 files changed

+85
-3
lines changed

Diff for: docs/content/scripts/content/google-maps.md

+17
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,23 @@ If you want to load the Google Maps immediately, you can use the `trigger` prop.
248248
</template>
249249
```
250250

251+
#### Map Styling
252+
253+
You can style the map by using the `mapOptions.styles` prop. You can find pre-made styles on [Snazzy Maps](https://snazzymaps.com/).
254+
255+
This will automatically work for both the static map placeholder and the interactive map.
256+
257+
```vue
258+
<script setup lang="ts">
259+
const mapOptions = {
260+
styles: [{ elementType: 'labels', stylers: [{ visibility: 'off' }, { color: '#f49f53' }] }, { featureType: 'landscape', stylers: [{ color: '#f9ddc5' }, { lightness: -7 }] }, { featureType: 'road', stylers: [{ color: '#813033' }, { lightness: 43 }] }, { featureType: 'poi.business', stylers: [{ color: '#645c20' }, { lightness: 38 }] }, { featureType: 'water', stylers: [{ color: '#1994bf' }, { saturation: -69 }, { gamma: 0.99 }, { lightness: 43 }] }, { featureType: 'road.local', elementType: 'geometry.fill', stylers: [{ color: '#f19f53' }, { weight: 1.3 }, { visibility: 'on' }, { lightness: 16 }] }, { featureType: 'poi.business' }, { featureType: 'poi.park', stylers: [{ color: '#645c20' }, { lightness: 39 }] }, { featureType: 'poi.school', stylers: [{ color: '#a95521' }, { lightness: 35 }] }, {}, { featureType: 'poi.medical', elementType: 'geometry.fill', stylers: [{ color: '#813033' }, { lightness: 38 }, { visibility: 'off' }] },
261+
}
262+
</script>
263+
<template>
264+
<ScriptGoogleMaps :mapOptions="mapOptions" />
265+
</template>
266+
```
267+
251268
### Component API
252269

253270
See the [Facade Component API](/docs/guides/facade-components#facade-components-api) for full props, events, and slots.
+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<script setup lang="ts">
2+
const mapOptions = {
3+
center: { lat: -34.397, lng: 150.644 },
4+
styles: [{ elementType: 'labels', stylers: [{ visibility: 'off' }, { color: '#f49f53' }] }, { featureType: 'landscape', stylers: [{ color: '#f9ddc5' }, { lightness: -7 }] }, { featureType: 'road', stylers: [{ color: '#813033' }, { lightness: 43 }] }, { featureType: 'poi.business', stylers: [{ color: '#645c20' }, { lightness: 38 }] }, { featureType: 'water', stylers: [{ color: '#1994bf' }, { saturation: -69 }, { gamma: 0.99 }, { lightness: 43 }] }, { featureType: 'road.local', elementType: 'geometry.fill', stylers: [{ color: '#f19f53' }, { weight: 1.3 }, { visibility: 'on' }, { lightness: 16 }] }, { featureType: 'poi.business' }, { featureType: 'poi.park', stylers: [{ color: '#645c20' }, { lightness: 39 }] }, { featureType: 'poi.school', stylers: [{ color: '#a95521' }, { lightness: 35 }] }, {}, { featureType: 'poi.medical', elementType: 'geometry.fill', stylers: [{ color: '#813033' }, { lightness: 38 }, { visibility: 'off' }] }, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, { elementType: 'labels' }, { featureType: 'poi.sports_complex', stylers: [{ color: '#9e5916' }, { lightness: 32 }] }, {}, { featureType: 'poi.government', stylers: [{ color: '#9e5916' }, { lightness: 46 }] }, { featureType: 'transit.station', stylers: [{ visibility: 'off' }] }, { featureType: 'transit.line', stylers: [{ color: '#813033' }, { lightness: 22 }] }, { featureType: 'transit', stylers: [{ lightness: 38 }] }, { featureType: 'road.local', elementType: 'geometry.stroke', stylers: [{ color: '#f19f53' }, { lightness: -10 }] }, {}, {}, {}],
5+
} satisfies google.maps.MapOptions
6+
</script>
7+
8+
<template>
9+
<div>
10+
<div>
11+
<ScriptGoogleMaps
12+
api-key="AIzaSyAOEIQ_xOdLx2dNwnFMzyJoswwvPCTcGzU"
13+
:width="1200"
14+
:height="600"
15+
above-the-fold
16+
:map-options="mapOptions"
17+
/>
18+
</div>
19+
<div class="button-container">
20+
<button
21+
class="button"
22+
@click="changeQuery"
23+
>
24+
change query
25+
</button>
26+
</div>
27+
</div>
28+
</template>
29+
30+
<style>
31+
.button-container {
32+
margin: 20px 0;
33+
}
34+
35+
.button {
36+
background-color: orange;
37+
border-radius: 8px;
38+
padding: 4px 8px;
39+
cursor: pointer;
40+
}
41+
42+
.button:not(:last-child) {
43+
margin-right: 8px;
44+
}
45+
</style>

Diff for: src/runtime/components/ScriptGoogleMaps.vue

+23-3
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,6 @@ const props = withDefaults(defineProps<{
8989
trigger: ['mouseenter', 'mouseover', 'mousedown'],
9090
width: 640,
9191
height: 400,
92-
centerMarker: true,
9392
})
9493
9594
const emits = defineEmits<{
@@ -124,7 +123,7 @@ const options = computed(() => {
124123
return defu({ center: centerOverride.value }, props.mapOptions, {
125124
center: props.center,
126125
zoom: 15,
127-
mapId: 'map',
126+
mapId: props.mapOptions?.styles ? undefined : 'map',
128127
})
129128
})
130129
const ready = ref(false)
@@ -292,7 +291,11 @@ onMounted(() => {
292291
center = await resolveQueryToLatLang(center as string)
293292
}
294293
map.value!.setCenter(center as google.maps.LatLng)
295-
if (props.centerMarker) {
294+
if (typeof props.centerMarker === 'undefined' || props.centerMarker) {
295+
if (options.value.mapId) {
296+
// not allowed to use advanced markers with styles
297+
return
298+
}
296299
if (prev[0]) {
297300
const prevCenterHash = hash({ position: prev[0] })
298301
// @ts-expect-error broken upstream type
@@ -335,6 +338,22 @@ if (import.meta.server) {
335338
})
336339
}
337340
341+
function transformMapStyles(styles: google.maps.MapTypeStyle[]) {
342+
return styles.map((style) => {
343+
const feature = style.featureType ? `feature:${style.featureType}` : ''
344+
const element = style.elementType ? `element:${style.elementType}` : ''
345+
const rules = (style.stylers || []).map((styler) => {
346+
return Object.entries(styler).map(([key, value]) => {
347+
if (key === 'color' && typeof value === 'string') {
348+
value = value.replace('#', '0x')
349+
}
350+
return `${key}:${value}`
351+
}).join('|')
352+
}).filter(Boolean).join('|')
353+
return [feature, element, rules].filter(Boolean).join('|')
354+
}).filter(Boolean)
355+
}
356+
338357
const placeholder = computed(() => {
339358
let center = options.value.center
340359
if (center && typeof center === 'object') {
@@ -349,6 +368,7 @@ const placeholder = computed(() => {
349368
size: `${props.width}x${props.height}`,
350369
key: apiKey,
351370
scale: 2, // we assume a high DPI to avoid hydration issues
371+
style: props.mapOptions?.styles ? transformMapStyles(props.mapOptions.styles) : undefined,
352372
markers: [
353373
...(props.markers || []),
354374
center,

0 commit comments

Comments
 (0)