Map event listeners

Open in CodeSandbox

<!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>

        <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>

        <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">
            import {BEHAVIOR, EventListener} from './common';
            import {LOCATION} from '../variables';
            
            window.map = null;
            
            async function main() {
                // For each object in the JS API, there is a Vue counterpart
                // To use the Vue version of the API, include the module @mappable-world/mappable-vuefy
                const [mappableVue] = await Promise.all([mappable.import('@mappable-world/mappable-vuefy'), mappable.ready]);
                const vuefy = mappableVue.vuefy.bindTo(Vue);
                const {MMap, MMapDefaultSchemeLayer, MMapListener, MMapControls, MMapControl} = vuefy.module(mappable);
            
                // Using mappable-vuefy, we turn a custom InfoMessage and EventListener into a Vue component
                const {EventListener: EventListenerV} = vuefy.module(
                    {EventListener},
                    {
                        EventListener: {title: String, events: Object}
                    }
                );
            
                const mapEvents = Vue.ref({
                    update: false,
                    resize: false
                });
            
                const updateHandler = (object) => {
                    mapEvents.value.update = !!object.mapInAction;
                    mapEvents.value = {...mapEvents.value};
                };
            
                const domEvents = Vue.ref({
                    click: false,
                    dblClick: false,
                    rightDblClick: false,
                    mouseMove: false,
                    mouseEnter: false,
                    mouseLeave: false,
                    mouseDown: false
                });
            
                const behaviorEvents = Vue.ref({
                    scrollZoom: false,
                    drag: false,
                    mouseRotate: false,
                    mouseTilt: false
                });
            
                const createResizeHandler = () => {
                    const disableResizeEvent = _.debounce(() => {
                        mapEvents.value.resize = false;
                        mapEvents.value = {...mapEvents.value};
                    }, 250);
            
                    return function (object) {
                        mapEvents.value.resize = true;
                        mapEvents.value = {...mapEvents.value};
                        disableResizeEvent();
                    };
                };
            
                const createDomEventHandler = (eventType) => {
                    const disableEvent = _.debounce(() => {
                        domEvents.value[eventType] = false;
                        domEvents.value = {...domEvents.value};
                    }, 250);
            
                    return function () {
                        domEvents.value[eventType] = true;
                        domEvents.value = {...domEvents.value};
                        disableEvent();
                    };
                };
            
                const createBehaviorEventHandler = (isStart) => {
                    return function (object) {
                        if (object.type === 'dblClick') return;
                        behaviorEvents.value[object.type] = isStart;
                        behaviorEvents.value = {...behaviorEvents.value};
                    };
                };
                const app = Vue.createApp({
                    components: {
                        MMap,
                        MMapDefaultSchemeLayer,
                        MMapListener,
                        MMapControls,
                        MMapControl,
                        EventListenerV
                    },
                    setup() {
                        const refMap = (ref) => {
                            window.map = ref?.entity;
                        };
                        return {
                            LOCATION,
                            BEHAVIOR,
                            refMap,
                            domEvents,
                            mapEvents,
                            behaviorEvents,
                            updateHandler,
                            createResizeHandler,
                            createDomEventHandler,
                            createBehaviorEventHandler
                        };
                    },
                    template: `
                  <!-- Initialize the map and pass initialization parameters -->
                  <MMap :location="LOCATION" :showScaleInCopyrights="true" :behaviors="BEHAVIOR" :ref="refMap">
                    <!-- 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 -->
                        <EventListenerV title="Map Events" :events="mapEvents"/>
                        <!-- Add an EventListener control to the map to display the status of behavior events -->
                        <EventListenerV title="Behavior Events" :events="behaviorEvents"/>
                      </MMapControl>
                      <MMapControl>
                        <!-- Add an EventListener control to the map to display the status of dom events -->
                        <EventListenerV 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
                      @update="updateHandler"
                      @resize="(o) => createResizeHandler()(o)"
                      @click="(o, e) => createDomEventHandler('click')(o, e)"
                      @dblClick="(o, e) => createDomEventHandler('dblClick')(o, e)"
                      @rightDblClick="(o, e) => createDomEventHandler('rightDblClick')(o, e)"
                      @mouseMove="(o, e) => createDomEventHandler('mouseMove')(o, e)"
                      @mouseEnter="(o, e) => createDomEventHandler('mouseEnter')(o, e)"
                      @mouseLeave="(o, e) => createDomEventHandler('mouseLeave')(o, e)"
                      @mouseDown="(o, e) => createDomEventHandler('mouseDown')(o, e)"
                      @actionStart="o => createBehaviorEventHandler(true)(o)"
                      @actionEnd="o => createBehaviorEventHandler(false)(o)"
                    />
                  </MMap>
                `
                });
                app.mount('#app');
            }
            
            main();
        </script>

        <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>
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
};
:root {
    --color-interact-component-chkd-hover: rgba(18, 45, 178, 0.06);
}
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;
});
.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%);
}