Customizing your location map
This article is work in progress. Any questions please get in touch.
To allow you to create a completely customizable store locator we have used StimulusJS
StimulusJS allows you to only have to worry about the styling of your store locator, just add some data attributes and we will hook up the functionality to the app.
Map wrapper
<div data-controller="map"> ... </div>
Values
data-map-select-first-listing-on-results-value="false" data-map-bounds-padding-value='{ "left": 500, "right": 30, "top": 30, "bottom": 30 }' data-map-geolocate-on-load-value="{{map_settings.geolocate_on_load}}" data-map-filter-behaviour-value="{{map_settings.filter_behaviour}}" data-map-keep-search-in-view-value="{{map_settings.keep_search_in_view}}" data-map-max-distance-units-value="{{map_settings.distance_units}}" data-map-max-results-value="{{map_settings.max_results}}" data-map-max-distance-value="{{map_settings.max_distance}}" data-map-geocoder-options-value='{"placeholder": "{{language.map.search_placeholder}}"}' data-map-mapbox-options-value='{"cooperativeGestures": {{cooperative_gestures}}, "dragRotate": false}' data-map-animation-duration-value="1.2"
Using data-map-mapbox-options-value you can add custom propperties to your map. You can view the different properties on the mapbox
Map
<div data-map-target="map"></div>
Remember to put a height on this because it defaults to 0!
Access the map in Javascript
<div id="yourMapController" data-controller='map'
Then you can access it with
document.querySelector("#yourMapController").map
e.g. to add fullscreen control you could add the following to the bottom of the landing template
<script> const map = document.querySelector("#yourMapController").map.map map.addControl(new mapboxgl.FullscreenControl({container: document.querySelector('body')})) </script>
<script> function docReady(fn) { // see if DOM is already available if (document.readyState === "complete" || document.readyState === "interactive") { // call on next available tick setTimeout(fn, 1); } else { document.addEventListener("DOMContentLoaded", fn); } } docReady(function() { const map = document.querySelector("#yourMapController").map.map
map.addControl(new mapboxgl.FullscreenControl({container: document.querySelector('body')})) }) </script>
With this, you can now access the Mapbox API and can add any of the Mapbox examples.
Search input
<div data-map-target="search"></div>
Listings container
<div data-map-target="listingsContainer"></div>
This is where your listings will go once, they are received from the server.
Templates
Make sure to wrap the listing, marker, and popup templates in a {% raw %} liquid tag, so your liquid tags are not escaped and can be sent to the server.
The listingsLoading and listingsEmpty templates don't need to be wrapped in a {% raw %} tag.
<template data-map-target="listingTemplate"> ... </template>
When your listing template is rendered on the server it will be wrapped in a div with the class "listing" then inserted into your listings container element.
Marker (optional)
<template data-map-target="markerTemplate"> ... </template>
<template data-map-target="popupTemplate"> ... </template><br>
Inside these 3 templates, you have access to the {{ location }} object, and {{ distance }}
{{ distance.in_miles }} or {{ distance.in_kilometers }}
{% if distance != blank %} <p>{{distance.in_miles}} miles away</p> {% endif %}
<template data-map-target="listingsLoadingContent"> ... </template>
<template data-map-target="listingsEmptyContent"> ... </template><br>
Custom search marker
<template data-map-target="searchMarker"> <p>SEARCH MARKER CONTENT</p> </template><br>
Result data
e.g.
<p> Showing <span id="listingResultsFound">0</span> of <span id="listingResultsTotal">0</span> results </p><br>
Filtering by tag
data-action="click->map#toggleTag" data-map-tag-id-param="{{tag.id}}"
change->map#toggleTag<br>
Filter by open now
click->map#toggleOpenNow
<div data-action="click->map#toggleOpenNow">Open now</div>
The class active will be added on this element when it is active
Search current map view
data-action="click->map#searchCurrentMapView"
Toggle on search as I move the map
data-action="click->map#toggleSearchAsIMoveTheMap"
When search as i move the map is enabled, the class "search-on-map-move" will be added to the container element
Clear tags
data-action="click->map#clearTags"
Geolocating
you can hide this with
.mapboxgl-ctrl-geolocate{ display: none !important; }<br>
data-action="map#geolocate"
You can also add
data-map-geolocate-on-load="true"
Clear search
data-action="map#clearSearchCoords"
Classes added to map
no-results results-loading geolocate-loading search-focused has-active-tags has-open-now-filter search-on-map-move<br>
Example map
<div data-controller='map'> <div class="simple-map-container"> <div class="sidebar"> <div class="sidebar-content"> <h2>Find a cheeky store</h2> <div data-map-target="search" class="search"></div> </div> <p class="sidebar-results-text">Showing <span id="listingResultsFound">0</span> of <span id="listingResultsTotal">0</span> results</p> <div class="simple-listings" data-map-target='listingsContainer'></div> </div> <div class="map" data-map-target="map"></div> </div> {% comment %}Templates{% endcomment %} {% raw %} <template data-map-target="listingTemplate"> <h4>{{location.name}}</h4> <p>{{location.fields.description}}</p> {% if distance %} <p>{{ distance.in_miles }} miles away</p> {% endif %} </template> <template data-map-target="markerTemplate"> <img class="marker-image" src="data:image/svg+xml,%3Csvg width='36' height='36' viewBox='0 0 36 36' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M28.25 13.5044C28.25 19.3184 21.4985 28.8674 19.022 32.1734C18.5735 32.7734 17.6765 32.7734 17.228 32.1734C14.7515 28.8674 8 19.3184 8 13.5044C8 7.91239 12.533 3.37939 18.125 3.37939C23.717 3.37939 28.25 7.91239 28.25 13.5044Z' fill='%23111111'/%3E%3Ccircle cx='18' cy='13' r='4' fill='white'/%3E%3C/svg%3E" /> </template> <template data-map-target="popupTemplate"> <div class="popup-content"> <h1>{{location.name}}</h1> <p>{{location.fields.description}}</p> </div> </template> {% endraw %} <template data-map-target="listingsLoadingContent"> <div class="listings-loading-content"> <p>Loading...</p> </div> </template> <template data-map-target="listingsEmptyContent"> <div class="listings-empty-content"> <p>We're sorry, we cant find any locations for you search</p> <p>Try changing the filters or the search location</p> </div> </template> </div>