import DsMap from "#core/design-system/components/DsMap.vue";
Demo
Use it wherever the site shows a map: the results on a map, a listing's position, the agencies in a zone. It works like the live map: drag, zoom, and click a marker to select it.
The tiles are the site's MapTiler style when there's a key, and OpenStreetMap's otherwise: same streets and places, other colors. The credits in the corner say which (© MapTiler or only © OpenStreetMap contributors). The key goes in .env as VITE_MAPTILER_KEY (copy .env.example, then restart the dev server); devs have it.
It doesn't group nearby markers into clusters: show the state with fewer markers, and describe the behaviour in the handoff.
Drawing an area
People can draw the area to search on the map, as on the site. Two ways, the site's two:
points: click each corner, then the first one again (or double click) to close. The site does this on desktop.freehand: drag a finger (or the mouse) around the area. The site does this on phones.
The page decides everything around it, so each step can be a mockup state: start drawing with v-model:draw (set it to a mode; it goes back to false when the area is closed, or when Esc cancels), read and set the area with v-model:area, and put the buttons and hints over the map. editable lets people drag the corners once it's drawn; invalid draws it red ("area troppo grande"); mask dims everything outside it, like the results for a drawn area.
<script setup lang="ts">
import DsMap, { type DsMapArea, type DsMapDrawMode } from "#core/design-system/components/DsMap.vue";
const area = ref<DsMapArea | null>(null);
const draw = ref<DsMapDrawMode | false>(false);
</script>
<template>
<DsMap v-model:area="area" v-model:draw="draw" label="Disegna l'area della ricerca" editable class="tw-h-full">
<BaseButton v-if="!area && !draw" class="tw-absolute tw-bottom-4 tw-left-1/2 -tw-translate-x-1/2" @click="draw = 'points'">
Disegna area
</BaseButton>
</DsMap>
</template>
For a state with an area already drawn, give area corners from the mocks ([[45.4548, 9.1655], …]). Drawing has no keyboard version, on the site either: design another way to choose the place (a search, "vicino a me") next to it.
Usage
<script setup lang="ts">
import DsMap, { type DsMapMarker } from "#core/design-system/components/DsMap.vue";
const selected = ref<string | number | null>(null);
const markers: DsMapMarker[] = [
{ id: 1, lat: 45.4523, lng: 9.1637, label: "385.000 €", name: "Trilocale in via Tortona, 385.000 €" },
{ id: 2, lat: 45.4581, lng: 9.1808, label: "520.000 €", name: "Quadrilocale in corso Genova, 520.000 €" },
];
</script>
<template>
<DsMap v-model:selected="selected" label="Mappa degli annunci a Milano" :markers="markers" />
</template>
- Size: full width and 16:9 by default. A class on
<DsMap>changes it:tw-h-fullto fill a panel that has a height,tw-aspect-squarefor a square,tw-w-1/2for half the width. - Where it looks: without
center, it fits its markers; with no markers either, it shows Milan, like the site. - Markers:
labelis what the marker shows (the price); without it, the marker is a dot.nameis what a screen reader says. - On phones there are no zoom buttons, as on every map of the site: people pinch. On a phone, the site's map usually fills the screen (
tw-h-full). - Controls over the map: what you put inside
<DsMap>sits over it, above the tiles and markers: the round "my position" button, "Disegna area" and "Salva ricerca", the strip of cards for the selected marker. Position each control with classes (tw-absolute tw-bottom-4 tw-left-3); the map still drags everywhere else.
<DsMap label="Mappa degli annunci" :markers="markers" class="tw-h-full">
<button type="button" aria-label="Usa la mia posizione" class="tw-absolute tw-bottom-4 tw-left-3 …">
<GpsIcon :width="24" :height="24" />
</button>
</DsMap>
Do / Don't
- Do give every marker a
namethat says what it is, not only its price. - Do design what happens on selection (the listing card opens, the list scrolls to it) as a state of the page, with
v-model:selected. - Don't use a screenshot of a map: designers can't move it, and devs can't tell which markers it shows.
- Don't put the map in a box with no height: it needs one to show.
API
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | | What the map shows, for screen readers ("Mappa degli annunci a Milano"). |
markers? | DsMapMarker[] | [] | The markers. Changing the list redraws them. |
center? | [number, number] | null | null | [latitude, longitude]. Without it, the map fits its area or its markers, or shows Milan like the site. |
zoom? | number | 13 | Zoom level with `center`: 13 shows a city, 16 a few streets. |
editable? | boolean | false | The area's corners can be dragged, like the site's on desktop once it's drawn. |
invalid? | boolean | false | The area is drawn red: it can't be used (too large, outside Italy…). |
mask? | boolean | false | Everything outside the area is dimmed, like the site's results for a drawn area. |
selected? | string | number | null | null | The selected marker's id (`v-model:selected`). It's drawn dark; clicking a marker selects it. |
area? | DsMapArea | null | null | The drawn area (`v-model:area`), or null. Set it to show an area already drawn. |
draw? | false | DsMapDrawMode | false | Drawing (`v-model:draw`): set it to a mode to start. It goes back to `false` once the area is closed, or when Esc cancels. While drawing, a new area replaces the old one. |
Slots: default