Map event listeners
vanilla.html
react.html
vue.html
common.css
common.ts
variables.css
variables.ts
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
<script crossorigin src="https://cdn.jsdelivr.net/npm/@babel/standalone@7/babel.min.js"></script>
<script crossorigin src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
<!-- To make the map appear, you must add your apikey -->
<script src="https://js.api.mappable.world/v3/?apikey=<YOUR_APIKEY>&lang=en_US" type="text/javascript"></script>
<script
data-plugins="transform-modules-umd"
data-presets="typescript"
type="text/babel"
src="../variables.ts"
></script>
<script
data-plugins="transform-modules-umd"
data-presets="typescript"
type="text/babel"
src="./common.ts"
></script>
<script data-plugins="transform-modules-umd" data-presets="typescript" type="text/babel">
import type {
BehaviorMapEventHandler,
DomEventHandler,
MapEventResizeHandler,
MapEventUpdateHandler
} from '@mappable-world/mappable-types';
import {BEHAVIOR, EventListener} from './common';
import {LOCATION} from '../variables';
window.map = null;
main();
async function main() {
// Waiting for all api elements to be loaded
await mappable.ready;
const {MMap, MMapDefaultSchemeLayer, MMapListener, MMapControls, MMapControl} = mappable;
// Initialize the map
map = new MMap(
// Pass the link to the HTMLElement of the container
document.getElementById('app'),
// Pass the map initialization parameters
{location: LOCATION, showScaleInCopyrights: true, behaviors: BEHAVIOR},
// Add a map scheme layer
[new MMapDefaultSchemeLayer({})]
);
// Status of map events
const mapEvents = {
update: false,
resize: false
};
// Status of dom events
const domEvents = {
click: false,
dblClick: false,
rightDblClick: false,
mouseMove: false,
mouseEnter: false,
mouseLeave: false,
mouseDown: false
};
// Status of behavior events
const behaviorEvents = {
scrollZoom: false,
drag: false,
mouseRotate: false,
mouseTilt: false
};
// Create an EventListener control to display the status of map events
const mapEventsControl = new EventListener({
title: 'Map Events',
events: {...mapEvents}
});
// Create an EventListener control to display the status of dom events
const domEventsControl = new EventListener({
title: 'Dom Events',
events: {...domEvents}
});
// Create an EventListener control to display the status of behavior events
const behaviorEventsControl = new EventListener({
title: 'Behavior Events',
events: {...behaviorEvents}
});
/* Create and add a shared container for controls to the map.
Using MMapControls you can change the position of the control */
const topLeftControls = new MMapControls({position: 'top left', orientation: 'horizontal'});
map.addChild(topLeftControls);
// Add controls to the map
topLeftControls
.addChild(new MMapControl().addChild(mapEventsControl).addChild(behaviorEventsControl))
.addChild(new MMapControl().addChild(domEventsControl));
// Handler function for changing the status of the onUpdate event
const updateHandler: MapEventUpdateHandler = (object) => {
object.mapInAction ? (mapEvents.update = true) : (mapEvents.update = false);
mapEventsControl.update({events: {...mapEvents}});
};
// Function that creates a handler function to change the status of the onResize event
const createResizeHandler = (): MapEventResizeHandler => {
const disableResizeEvent = _.debounce(() => {
mapEvents.resize = false;
mapEventsControl.update({events: {...mapEvents}});
}, 250);
return function (object) {
mapEvents.resize = true;
mapEventsControl.update({events: {...mapEvents}});
disableResizeEvent();
};
};
// Function that creates a handler function to change the status of dom events
const createDomEventHandler = (eventType: keyof typeof domEvents): DomEventHandler => {
const disableEvent = _.debounce(() => {
domEvents[eventType] = false;
domEventsControl.update({
events: {
...domEvents
}
});
}, 250);
return function (object, event) {
domEvents[eventType] = true;
domEventsControl.update({
events: {
...domEvents
}
});
disableEvent();
};
};
// Function that creates a handler function to change the status of behavior event
const createBehaviorEventHandler = (isStart: boolean): BehaviorMapEventHandler => {
return function (object) {
if (object.type === 'dblClick') return;
behaviorEvents[object.type] = isStart;
behaviorEventsControl.update({events: {...behaviorEvents}});
};
};
/* Add a listener to the map and pass the handlers functions for the events you want to process
These are just some of the events, you can see them all in the documentation */
map.addChild(
new MMapListener({
onUpdate: updateHandler,
onResize: createResizeHandler(),
onClick: createDomEventHandler('click'),
onDblClick: createDomEventHandler('dblClick'),
onRightDblClick: createDomEventHandler('rightDblClick'),
onMouseMove: createDomEventHandler('mouseMove'),
onMouseEnter: createDomEventHandler('mouseEnter'),
onMouseLeave: createDomEventHandler('mouseLeave'),
onMouseDown: createDomEventHandler('mouseDown'),
onActionStart: createBehaviorEventHandler(true),
onActionEnd: createBehaviorEventHandler(false)
})
);
}
</script>
<!-- prettier-ignore -->
<style> html, body, #app { width: 100%; height: 100%; margin: 0; padding: 0; font-family: Arial, Helvetica, sans-serif; } .toolbar { position: absolute; z-index: 1000; top: 0; left: 0; display: flex; align-items: center; padding: 16px; } .toolbar a { padding: 16px; } </style>
<link rel="stylesheet" href="./common.css" />
<link rel="stylesheet" href="../variables.css" />
</head>
<body>
<div id="app"></div>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
<script crossorigin src="https://cdn.jsdelivr.net/npm/react@17/umd/react.production.min.js"></script>
<script crossorigin src="https://cdn.jsdelivr.net/npm/react-dom@17/umd/react-dom.production.min.js"></script>
<script crossorigin src="https://cdn.jsdelivr.net/npm/@babel/standalone@7/babel.min.js"></script>
<script crossorigin src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
<!-- To make the map appear, you must add your apikey -->
<script src="https://js.api.mappable.world/v3/?apikey=<YOUR_APIKEY>&lang=en_US" type="text/javascript"></script>
<script
data-plugins="transform-modules-umd"
data-presets="typescript"
type="text/babel"
src="../variables.ts"
></script>
<script
data-plugins="transform-modules-umd"
data-presets="typescript"
type="text/babel"
src="./common.ts"
></script>
<script data-plugins="transform-modules-umd" data-presets="react, typescript" type="text/babel">
import type {
BehaviorMapEventHandler,
DomEventHandler,
MapEventResizeHandler,
MapEventUpdateHandler
} from '@mappable-world/mappable-types';
import {BEHAVIOR, EventListener} from './common';
import {LOCATION} from '../variables';
window.map = null;
main();
async function main() {
// For each object in the JS API, there is a React counterpart
// To use the React version of the API, include the module @mappable-world/mappable-reactify
const [mappableReact] = await Promise.all([mappable.import('@mappable-world/mappable-reactify'), mappable.ready]);
const reactify = mappableReact.reactify.bindTo(React, ReactDOM);
const {MMap, MMapDefaultSchemeLayer, MMapListener, MMapControls, MMapControl} = reactify.module(mappable);
// Using mappable-rectify, we turn a custom InfoMessage and EventListener into a React component
const {EventListener: EventListenerR} = reactify.module({EventListener});
const {useState, useCallback, useMemo} = React;
function App() {
// Status of map events
const [mapEvents, setMapEvents] = useState({
update: false,
resize: false
});
// Status of dom events
const [domEvents, setDomEvents] = useState({
click: false,
dblClick: false,
rightDblClick: false,
mouseMove: false,
mouseEnter: false,
mouseLeave: false,
mouseDown: false
});
// Status of behavior events
const [behaviorEvents, setBehaviorEvents] = useState({
scrollZoom: false,
drag: false,
mouseRotate: false,
mouseTilt: false
});
// Handler function for changing the status of the onUpdate event
const updateHandler: MapEventUpdateHandler = useCallback((object) => {
setMapEvents({...mapEvents, update: !!object.mapInAction});
}, []);
// Function that creates a handler function to change the status of the onResize event
const createResizeHandler = useCallback((): MapEventResizeHandler => {
const disableResizeEvent = _.debounce(() => {
setMapEvents({...mapEvents, resize: false});
}, 250);
return function () {
setMapEvents({...mapEvents, resize: true});
disableResizeEvent();
};
}, []);
// Function that creates a handler function to change the status of dom events
const createDomEventHandler = useCallback((eventType: keyof typeof domEvents): DomEventHandler => {
const disableEvent = _.debounce(() => {
setDomEvents({...domEvents, [eventType]: false});
}, 250);
return function () {
setDomEvents({...domEvents, [eventType]: true});
disableEvent();
};
}, []);
// Function that creates a handler function to change the status of behavior event
const createBehaviorEventHandler = useCallback((isStart: boolean): BehaviorMapEventHandler => {
return function (object) {
if (object.type === 'dblClick') return;
behaviorEvents[object.type] = isStart;
setBehaviorEvents({...behaviorEvents});
};
}, []);
return (
// Initialize the map and pass initialization parameters
<MMap location={LOCATION} showScaleInCopyrights={true} behaviors={BEHAVIOR} ref={(x) => (map = x)}>
{/* Add a map scheme layer */}
<MMapDefaultSchemeLayer />
{/* Add a shared container for controls to the map.
Using MMapControls you can change the position of the control */}
<MMapControls position="top left" orientation="horizontal">
<MMapControl>
{/* Add an EventListener control to the map to display the status of map events */}
<EventListenerR title="Map events" events={mapEvents} />
{/* Add an EventListener control to the map to display the status of behavior events */}
<EventListenerR title="Behavior Events" events={behaviorEvents} />
</MMapControl>
<MMapControl>
{/* Add an EventListener control to the map to display the status of dom events */}
<EventListenerR title="Dom Events" events={domEvents} />
</MMapControl>
</MMapControls>
{/* Add a listener to the map and pass the handlers functions for the events you want to process
These are just some of the events, you can see them all in the documentation */}
<MMapListener
onUpdate={updateHandler}
onResize={useMemo(() => createResizeHandler(), [])}
onClick={useMemo(() => createDomEventHandler('click'), [])}
onDblClick={useMemo(() => createDomEventHandler('dblClick'), [])}
onRightDblClick={useMemo(() => createDomEventHandler('rightDblClick'), [])}
onMouseMove={useMemo(() => createDomEventHandler('mouseMove'), [])}
onMouseEnter={useMemo(() => createDomEventHandler('mouseEnter'), [])}
onMouseLeave={useMemo(() => createDomEventHandler('mouseLeave'), [])}
onMouseDown={useMemo(() => createDomEventHandler('mouseDown'), [])}
onActionStart={useMemo(() => createBehaviorEventHandler(true), [])}
onActionEnd={useMemo(() => createBehaviorEventHandler(false), [])}
/>
</MMap>
);
}
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('app')
);
}
</script>
<!-- prettier-ignore -->
<style> html, body, #app { width: 100%; height: 100%; margin: 0; padding: 0; font-family: Arial, Helvetica, sans-serif; } .toolbar { position: absolute; z-index: 1000; top: 0; left: 0; display: flex; align-items: center; padding: 16px; } .toolbar a { padding: 16px; } </style>
<link rel="stylesheet" href="./common.css" />
<link rel="stylesheet" href="../variables.css" />
</head>
<body>
<div id="app"></div>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
<script crossorigin src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.js"></script>
<script crossorigin src="https://cdn.jsdelivr.net/npm/@babel/standalone@7/babel.min.js"></script>
<script crossorigin src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
<!-- To make the map appear, you must add your apikey -->
<script src="https://js.api.mappable.world/v3/?apikey=<YOUR_APIKEY>&lang=en_US" type="text/javascript"></script>
<script
data-plugins="transform-modules-umd"
data-presets="typescript"
type="text/babel"
src="../variables.ts"
></script>
<script
data-plugins="transform-modules-umd"
data-presets="typescript"
type="text/babel"
src="./common.ts"
></script>
<script data-plugins="transform-modules-umd" data-presets="typescript" type="text/babel"></script>
<!-- prettier-ignore -->
<style> html, body, #app { width: 100%; height: 100%; margin: 0; padding: 0; font-family: Arial, Helvetica, sans-serif; } .toolbar { position: absolute; z-index: 1000; top: 0; left: 0; display: flex; align-items: center; padding: 16px; } .toolbar a { padding: 16px; } </style>
<link rel="stylesheet" href="./common.css" />
<link rel="stylesheet" href="../variables.css" />
</head>
<body>
<div id="app"></div>
</body>
</html>
.events {
width: 200px;
padding: 8px;
display: flex;
flex-direction: column;
}
.events:nth-child(2)::before {
content: '';
position: relative;
top: -8px;
display: block;
border-top: 1px solid #5c5e661a;
justify-content: center;
width: 90%;
margin: 0 auto;
}
.events__title {
font-size: 14px;
width: 184px;
height: 20px;
padding: 0 8px;
align-content: center;
color: #7b7d85;
}
.events__list {
margin: 0;
padding: 0;
list-style: none;
display: flex;
flex-direction: column;
}
.events__item {
color: #050d33;
font-size: 16px;
font-style: normal;
font-weight: 500;
line-height: 20px;
padding: 8px 8px 8px 36px;
border-radius: 8px;
align-content: center;
background-repeat: no-repeat;
background-position: 5% 50%;
}
.events__item.update {
background-image: url('./update.svg');
}
.events__item.resize {
background-image: url('./resize.svg');
}
.events__item.scrollZoom {
background-image: url('./scrollZoom.svg');
}
.events__item.drag {
background-image: url('./drag.svg');
}
.events__item.mouseRotate {
background-image: url('./mouseRotate.svg');
}
.events__item.mouseTilt {
background-image: url('./mouseTilt.svg');
}
.events__item.click {
background-image: url('./click.svg');
}
.events__item.dblClick {
background-image: url('./dblClick.svg');
}
.events__item.rightDblClick {
background-image: url('./rightDblClick.svg');
}
.events__item.mouseMove {
background-image: url('./mouseMove.svg');
}
.events__item.mouseEnter {
background-image: url('./mouseEnter.svg');
}
.events__item.mouseLeave {
background-image: url('./mouseLeave.svg');
}
.events__item.mouseDown {
background-image: url('./mouseDown.svg');
}
.events__item_active {
background-color: var(--color-interact-component-chkd-hover);
}
.infoWindow {
width: 300px;
padding: 15px;
padding-left: 50px;
position: relative;
border-radius: 15px;
background-color: rgba(255, 255, 255, 0.9);
font-size: 16px;
}
.infoWindow__icon {
width: 30px;
height: 30px;
position: absolute;
top: 50%;
left: 10px;
transform: translateY(-50%);
}
import type {BehaviorType} from '@mappable-world/mappable-types';
import type lodash from 'lodash';
declare global {
// @ts-ignore
const _: typeof lodash;
}
// An array of enabled map behaviors
export const BEHAVIOR: BehaviorType[] = ['drag', 'scrollZoom', 'dblClick', 'mouseRotate', 'mouseTilt'];
/* Initialize a custom EventListener control
Assign a value to it after loading the map api */
export let EventListener = null;
type mapEventsType = {
title: string;
events: {
update: boolean;
resize: boolean;
};
};
type domEventsType = {
title: string;
events: {
click: boolean;
fastClick: boolean;
dblClick: boolean;
rightClick: boolean;
rightDblClick: boolean;
mouseMove: boolean;
mouseEnter: boolean;
mouseLeave: boolean;
mouseDown: boolean;
};
};
type behaviorEventsType = {
title: string;
events: {
scrollZoom: boolean;
drag: boolean;
mouseRotate: boolean;
mouseTilt: boolean;
};
};
type EventListenerProps = mapEventsType | domEventsType | behaviorEventsType;
// Wait for the api to load to access the entity system (MMapComplexEntity)
mappable.ready.then(() => {
class EventListenerClass extends mappable.MMapComplexEntity<EventListenerProps> {
private _element: HTMLDivElement;
private _detachDom: () => void;
// Method for create a DOM control element
_createElement() {
const {title, events} = this._props;
// Create a root element
const rootElement = document.createElement('div');
rootElement.classList.add('events');
// Create an events title element
const eventsTitle = document.createElement('div');
eventsTitle.textContent = title;
eventsTitle.classList.add('events__title');
rootElement.appendChild(eventsTitle);
// Create an events list element
const eventsList = document.createElement('ul');
eventsList.classList.add('events__list');
rootElement.appendChild(eventsList);
// Iterate through the events and create list items
for (let event in events) {
const eventElement = document.createElement('li');
eventElement.classList.add('events__item', event);
eventElement.textContent = event;
eventElement.id = event;
eventsList.appendChild(eventElement);
}
return rootElement;
}
// Method that is used when updating the parameters of control
_onUpdate() {
const {events} = this._props;
for (let event in events) {
const eventElement = document.getElementById(event);
if (!eventElement) return;
if (events[event]) {
eventElement.classList.add('events__item_active');
} else {
eventElement.classList.remove('events__item_active');
}
}
}
// Method for attaching the control to the map
_onAttach() {
this._element = this._createElement();
this._detachDom = mappable.useDomContext(this, this._element, this._element);
}
// Method for detaching control from the map
_onDetach() {
this._detachDom();
this._detachDom = null;
this._element = null;
}
}
EventListener = EventListenerClass;
});
:root {
--color-interact-component-chkd-hover: rgba(18, 45, 178, 0.06);
}
import type {MMapLocationRequest} from '@mappable-world/mappable-types';
export const LOCATION: MMapLocationRequest = {
center: [55.5358, 25.3176], // starting position [lng, lat]
zoom: 11.7 // starting zoom
};