npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@smartimpact-it/locations-map

v1.3.13

Published

A library to display locations on a map.

Downloads

23

Readme

Locations Map

This library allows developers to create a map with store locations and apply filters, geolocate or search locations.

How to install

Install using npm or yarn

npm install --save @smartimpact-it/locations-map

How to use

HTML structure

Inside your HTML, create a structure similar to this:

<locations-map-container>
  <locations-map-header class="text-center">
    <div class="search-container">
      <form class="search-form" type="GET" action="#" data-location-search>
        <div class="search-wrapper">
          <div class="search-icon">Search</div>

          <input name="search_location" id="searchvalue" type="search" placeholder="Search" />
          <button type="reset" class="button-close">&times;</button>
          <button type="submit" class="button-search">Search</button>
        </div>
      </form>

      <button type="button" class="button-geolocate" data-geolocate-trigger>
        <span>Geolocate</span>
      </button>
    </div>
  </locations-map-header>

  <locations-map-popup>
    <!-- location popup will be added here by JS -->
  </locations-map-popup>

  <locations-map-list>
    <!-- locations will be added here by JS -->
  </locations-map-list>

  <locations-map-target>
    <!-- The map will be added here by JS -->
  </locations-map-target>

  <template class="template-results-single">
    <div class="results-count">{{ results }} result</div>
  </template>

  <template class="template-results-multiple">
    <div class="results-count">{{ results }} results</div>
  </template>

  <!-- this is the default location template -->
  <template class="template-location">
    <div class="location">
      <div class="location-info">
        <h3 class="location-title">{{ name }}</h3>
        <div class="address">
          {{ address1 }} <br />
          {{ postcode }} {{ city }}
        </div>
        <div class="phone-number">
          <a href="tel:{{ phone }}"> {{ phone }} </a>
        </div>
      </div>
    </div>
  </template>

  <!-- popup templates -->
  <template class="template-popup-location">
    <div class="location in-popup">
      <div class="location">
        <button class="close-popup" data-close-popup>&times;</button>
        <div class="location-info">
          <h3 class="location-title">{{ name }}</h3>
          <div class="address">
            {{ address1 }} <br />
            {{ postcode }} {{ city }}
          </div>
          <div class="phone-number">
            <a href="tel:{{ phone }}"> {{ phone }} </a>
          </div>
        </div>
      </div>
    </div>
  </template>
</locations-map-container>

Instead of using template tags, you can also use script tags with type="text/locations-map-template. Keep the same classes on those script tags.

These elements will be used as following:

  • locations-map-container (required) - this is the wrapper around all the UI elements
  • locations-map-target (required) - will be used to display the map
  • locations-map-list - will be used to display the list of stores/locations (and optionally with pagination)
  • locations-map-popup - will be used to display outside the map the popups when clicking on a location. For example, if you want to display the popup in a different place on the page, not over the map, you can use this element (there can be multiple such elements) and use preventDefault on the showPopupOnMap.locationsMap event.
  • template.template-results-single or script[type="text/locations-map-template"].template-results-single - this is the template which will be used to display the header on the locations-map-list, containing the number of stores/locations displayed, when there is only one result
  • template.template-results-multiple or script[type="text/locations-map-template"].template-results-multiple - this is the template which will be used to display the header on the locations-map-list, containing the number of stores/locations displayed, when there are more than 1 result
  • template.template-location or script[type="text/locations-map-template"].template-location - this is used for the HTML markup for the items displayed in the location-map-list. Only required if you also have the locations-map-list element.
  • template.template-popup-location or script[type="text/locations-map-template"].template-popup-location - used for the HTML markup for the popups. If you want to add a "close" button, add the data-close-popup attribute to it.
  • form.data-location-search - the form for searching for a location. It should also contain a input[type="search"] element, which is the input used for searching.
  • [data-geolocate-trigger] - buttons which can trigger the geolocation. They can be anywhere in the locations-map-container element, not necessarily in the form.

Only the locations-map-container, locations-map-target and the templates are required. If you don't want to display specific elements, you can remove them.

The placeholders above ({{ postcode }}, {{ name }} etc) will be replaced by the JavaScript library using the data from the stores/locations. These are keys that should also be present on the location objects.

Also, the placeholders {{ distance }} and {{ distance_km }} will be replaced with the distance from the client position (if geolocated, or if a search provider option has been selected).

The locations

For example, your locations array could look like this (the id, longitude and latitude fields are mandatory; also type (string) or filterTypes (string[]) if you want to use filtering):

const locations = [
  {
    id: 1,
    name: 'First location',
    address1: 'Paris',
    postcode: '12345',
    city: 'Nantes',
    longitude: 44.2,
    latitude: 40.2,
    filterTypes: ['location', 'forest'],
  },
  {
    id: 2,
    name: 'Second location',
    address1: 'Paris',
    postcode: '12345',
    city: 'Nantes',
    longitude: 44.2,
    latitude: 40.2,
    filterTypes: ['location'],
  },
];

The id, longitude and latitude fields are mandatory. If you want to use filtering, also include the type (string) or filterTypes (string[]) fields.

Google Maps with Google Geocoder

You have 2 types of Google maps providers: with and without clusters:

  • GoogleMapsClusteredWrapper
  • GoogleMapsWrapper
import LocationsMap, { GoogleMapsClusteredWrapper, GoogleMapsGeocoderProvider } from '@smartimpact-it/locations-map';

const container = document.querySelector('locations-map-container');
if (container) {
  const mapProvider = new GoogleMapsClusteredWrapper({
    apiSettings: {
      apiKey: 'api-key-here',
    },
    mapSettings: {
      disableDefaultUI: false,
      // styles, // array with the styles to apply on the map...
    },
    clusterSettings: {
      styles: [
        {
          width: 30,
          height: 30,
          className: 'custom-clustericon-1',
        },
      ],
      clusterClass: 'custom-clustericon',
    }, // settings for the clusterer
  });

  const searchProvider = new GoogleMapsGeocoderProvider();

  const locationsMapSettings = {
    latitude: 44.1,
    longitude: 10.3,
    zoom: 8,
    locations,
    displaySearch: true,
    filters: [],
    searchProvider,
    mapProvider,
  };

  const locationsMap = new LocationsMap(container, locationsMapSettings);
}

Leaflet with Nominatim Geocoder

You have 2 types of Leaflet maps: with and without clusters:

  • LeafletMapClusteredWrapper
  • LeafletMapWrapper
import LocationsMap, { LeafletMapClusteredWrapper, NominatimProvider } from '@smartimpact-it/locations-map';

const container = document.querySelector('locations-map-container');
if (container) {
  const mapProvider = new LeafletMapClusteredWrapper();
  const searchProvider = new NominatimProvider();

  const locationsMapSettings = {
    latitude: 44.1,
    longitude: 10.3,
    zoom: 8,
    locations,
    displaySearch: true,
    filters: [],
    searchProvider,
    mapProvider,
  };

  const locationsMap = new LocationsMap(container, locationsMapSettings);
}

Settings for the Locations Map class

| Setting | Default value | Description | | ---------------------- | ------------- | ---------------------------------------------------------------------------------------- | | latitude | | the position on the map which will be displayed when first loaded | | longitude | | the position on the map which will be displayed when first loaded | | mapProvider | | the map provider (an instance of the map providers) | | searchProvider | | the search provider (if search is to be used) | | paginationProvider | | the pagination provider (if pagination is to be used) | | autocompleteProvider | | the autocomplete provider (if pagination is to be used) | | zoom | 8 | initial zoom value | | focusedZoom | 17 | the zoom value when a location is focused | | geolocateOnStart | true | Try to geolocate the current user when the map is displayed | | scrollToGeolocation | false | Scroll to the geolocated position when we apply geolocation | | focusOnHover | false | Scroll to the hovered store on the map when hovering on the store in the list | | openOnListClick | false | Scroll to the store on the map and open the popup when clicking on the store in the list | | filters | [] | The initial set of active filters | | icon | | the icon to use, or a callback |

The settings can also be added directly on the locations-map-container element, inside a data-settings attribute (you should put a valid JSON here). The order of "precedence" for the settings are: 1) the default settings; 2) the settings added as data-settings; 3) the settings set directly when creating the LocationsMap object.

The icon parameter can accept a callback to dynamically set the icon (for example, if you have different types or different icons if stores are selected).

import iconMulti from '../img/icon-store-multi.svg';
import iconMultiSelected from '../img/icon-store-multi-selected.svg';
import iconForest from '../img/icon-forest.svg';
import iconForestSelected from '../img/icon-forest-selected.svg';

new LocationMap(element, {
  // ...
  icon: (store, selected = false) => {
    switch (store.type) {
      case 'forest':
        return {
          url: selected ? iconForestSelected : iconForest,
          anchor: { x: 10, y: 30 },
        };
      default:
        return {
          url: selected ? iconMultiSelected : iconMulti,
          anchor: { x: 10, y: 30 },
        };
    }
  },
});

The default settings are the following:

const defaultSettings = {
  latitude: 0,
  longitude: 0,
  zoom: 6,
  focusedZoom: 17,
  focusedAreaZoom: 10,
  locations: [],
  displaySearch: false,
  mapProvider: null,
  searchProvider: null,
  paginationProvider: null,
  autocompleteProvider: null,
  filters: [],
  autocomplete: true,
  icon: null,
  clusterSettings: {},
  geolocateOnStart: true,
  scrollToGeolocation: false,
  focusOnClick: true,
  focusOnHover: false,
  focusOnHoverTimeout: 1000,
  openOnListClick: false,
  templateDelimiters: ['{{', '}}'],
};

Pagination

You can setup pagination for the results list by using the paginationProvider argument on the LocationsMap instance.

The library contains a default Pagination class, which uses list.js, but you can create your own pagination provider by implementing the PaginationProvider typescript interface.

import { Pagination } from '@smartimpact-it/locations-map';

// These are the default settings for the list.js instance
const settings = {
  page: 5,
  pagination: {
    paginationClass: 'pagination',
    item: "<li><a class='page'></a></li>",
    outerWindow: 1,
  },
};

const paginationProvider = new Pagination(settings);

new LocationsMap({
  //...
  paginationProvider: new Pagination(),
  //...
});

Autocomplete

You can setup autocomplete for the search input field by using the autocompleteProvider argument on the LocationsMap instance.

The library contains a default Autocomplete class, using @tarekraafat/autocomplete.js, but you can create your own autocomplete provider by implementing the AutocompleteProvider typescript interface.

new LocationsMap({
  //...
  autocompleteProvider: new Autocomplete(),
  //...
});

Filtering

You can implement your own logic for creating filters on the page (for example, radio buttons or checkboxes). You can then filter the stores that are displayed, based on the their type property, using the setFilters method on the LocationsMap instance:

// display only the stores with the type 'big' or 'small'
locationsMap.setFilters(['big', 'small']);

Methods

You can use the following methods on the LocationsMap instance:

  • setFilters(newFilters) - set the current filters for the stores to be displayed
  • updateLocations(callback) - to update the existing of locations (for example, if you want to modify something in their info). This receives a callback as a parameter, and the callback will receive the existing locations and should return an updated list of locations.
  • setLocations(newLocations) - to replace the set of locations (for example, if you want to load them by AJAX)
  • closePopups() - close the active popup
  • focusOnLocation(location) - scroll the map to a specific location
  • setMapPosition(position) - set a new position for the map
  • geolocate() - try to geolocate the current user
  • getSearchResults(searchValue) (promise) - get the results for a specific search string
  • doSearch() (promise) - search for the search string in the search form
  • setZoom(value) - set the zoom level

Events

There are multiple events dispatched by the location map:

  • initializing.locationsMap before initialization. It allows you to change the settings.
  • initialized.locationsMap after the map has been initialized
  • parseLocations.locationsMap triggered after parsing the locations
  • appliedFilters.locationsMap after filters are updated
  • updatedLocations.locationsMap before the locations list is updated, for example after geolocation
  • updatedLocationListContent.locationsMap after the location list has been updated
  • search.locationsMap (cancelable) - when the search form is submitted, before starting the search
  • updatingFromSearch.locationsMap (cancelable) - when search is starting
  • updatedFromSearch.locationsMap after search has been done
  • geolocated.locationsMap (cancelable) - after geolocation has been successful.
  • showPopup.locationsMap (cancelable) - when a location is clicked and a popup should be displayed
  • showPopupOnMap.locationsMap (cancelable) - when a location is clicked and a popup should be displayed on the map
  • showPopupOutsideMap.locationsMap (cancelable) - when a location is clicked and a popup should be displayed outside the map
  • listClick.locationsMap (cancelable) - when a location is clicked in the list, and the map should scroll to it and close existing popups
  • listHover.locationsMap (cancelable) - when a location is hovered in the list, and the map should scroll to it
  • updatedMapPosition.locationsMap when the map position has been changed

More info

The stores/locations lists and its pagination is created using list.js. The autocomplete functionality is based on @tarekraafat/autocomplete.js.

For more info, see the TypeScript definitions and the existing code.

Peer dependencies

Starting with version 1.3, the optional packages have been moved to peerDependencies. This means you will need to install them in your project if you need them.

  • @googlemaps/js-api-loader
  • @googlemaps/markerclustererplus
  • @tarekraafat/autocomplete.js
  • jquery
  • leaflet
  • leaflet.markercluster
  • list.js