Change map behaviors

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>
        <!-- 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 {BehaviorType} from '@mappable-world/mappable-types';
            import {AVAILABLE_BEHAVIORS, INITIALLY_ENABLED_BEHAVIORS} from './common';
            import {LOCATION} from '../variables';
            
            window.map = null;
            
            main();
            
            async function main() {
                // Waiting for all api elements to be loaded
                await mappable.ready;
            
                // Create a custom control class for map behavior management
                interface BehaviorControlProps {
                    behaviorName: string;
            
                    behaviorType: string;
            
                    checked: boolean;
            
                    tooltip: boolean;
            
                    onChange: (event: Event) => void;
                }
            
                class BehaviorControl extends mappable.MMapComplexEntity<BehaviorControlProps> {
                    private _element: HTMLDivElement;
            
                    private _detachDom: () => void;
            
                    // Method for create a DOM control element
                    _createElement(props: BehaviorControlProps) {
                        const {behaviorName, checked, tooltip, behaviorType, onChange} = props;
            
                        // Create a root element
                        const behaviorControl = document.createElement('div');
                        behaviorControl.classList.add('behaviorControl');
            
                        const behaviorControlBlock = document.createElement('div');
                        behaviorControlBlock.classList.add('behaviorControl__block');
            
                        // Create a title element
                        const title = document.createElement('div');
                        title.classList.add('behaviorControl__title');
                        title.textContent = behaviorName;
                        behaviorControlBlock.appendChild(title);
            
                        // Create a label element
                        const label = document.createElement('label');
                        label.classList.add('behaviorControl__label');
                        label.htmlFor = behaviorType;
            
                        // Create an input element (checkbox)
                        const input = document.createElement('input');
                        input.type = 'checkbox';
                        input.id = behaviorType;
                        input.checked = checked;
                        input.addEventListener('change', onChange);
            
                        // Create a slider element
                        const slider = document.createElement('div');
                        slider.classList.add('behaviorControl__slider');
            
                        // Add elements to the label
                        label.appendChild(input);
                        label.appendChild(slider);
            
                        // Add the label to the root element
                        behaviorControlBlock.appendChild(label);
                        behaviorControl.appendChild(behaviorControlBlock);
            
                        // Create a tooltip element if necessary
                        if (tooltip) {
                            const tooltipElement = document.createElement('div');
                            tooltipElement.className = 'behaviorControl__tooltip';
                            tooltipElement.innerText = 'To implement it hold down [Ctrl] / [Cmd] + left mouse button';
            
                            behaviorControl.appendChild(tooltipElement);
                        }
            
                        return behaviorControl;
                    }
            
                    // Method for attaching the control to the map
                    _onAttach() {
                        this._element = this._createElement(this._props);
                        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;
                    }
                }
            
                class BehaviorGroup extends mappable.MMapGroupEntity<{}> {
                    private _element: HTMLDivElement;
            
                    private _detachDom: () => void;
            
                    // Method for create a DOM control element
                    _createElement() {
                        // Create a root element
                        const behaviorControl = document.createElement('div');
                        behaviorControl.className = 'behavior';
            
                        return behaviorControl;
                    }
            
                    // 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;
                    }
                }
            
                // State with active map behaviors
                let behaviors = INITIALLY_ENABLED_BEHAVIORS;
            
                const {MMap, MMapDefaultSchemeLayer, 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: behaviors},
                    // Add a map scheme layer
                    [new MMapDefaultSchemeLayer({})]
                );
            
                // Using MMapControls you can change the position of the control
                const controls = new MMapControls({position: 'right top'});
                map.addChild(controls);
            
                // Create a shared container for custom BehaviorControl's and add it to the map
                const control = new MMapControl();
                controls.addChild(control);
            
                const behaviorGroup = new BehaviorGroup({});
                control.addChild(behaviorGroup);
            
                // Create a BehaviorControls to change the behavior of the map
                AVAILABLE_BEHAVIORS.forEach((behavior) => {
                    behaviorGroup.addChild(
                        new BehaviorControl({
                            behaviorName: behavior.title,
                            behaviorType: behavior.type,
                            checked: behaviors.includes(behavior.type),
                            tooltip: behavior.tooltip,
                            onChange: onChangeBehaviorHandler
                        })
                    );
                });
            
                // Handler function for add or remove map behavior
                function onChangeBehaviorHandler(event: Event) {
                    const target = event.target as HTMLInputElement;
                    const behaviorName = target.id as BehaviorType;
                    const checked = target.checked;
            
                    if (checked) {
                        behaviors.push(behaviorName);
                    } else {
                        behaviors = behaviors.filter((behavior) => behavior !== behaviorName);
                    }
                    map.setBehaviors(behaviors);
                }
            }
        </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>
        <!-- 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 {BehaviorType} from '@mappable-world/mappable-types';
            import {AVAILABLE_BEHAVIORS, INITIALLY_ENABLED_BEHAVIORS} from './common';
            import {LOCATION} from '../variables';
            import type TReact from 'react';
            
            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, MMapControls, MMapControl} = reactify.module(mappable);
                const {useState, useCallback} = React;
            
                // Create a custom control component for map behavior management
                interface BehaviorControlProps {
                    behaviorName: string;
            
                    behaviorType: string;
            
                    checked: boolean;
            
                    tooltip: boolean;
            
                    onChange: (event: TReact.FormEvent<HTMLLabelElement>) => void;
                }
            
                function BehaviorControl(props: BehaviorControlProps) {
                    const {behaviorName, checked, behaviorType, tooltip, onChange} = props;
            
                    return (
                        <div className="behaviorControl">
                            <div className="behaviorControl__block">
                                <div className="behaviorControl__title">{behaviorName}</div>
                                <label className="behaviorControl__label" htmlFor={behaviorType} onChange={onChange}>
                                    <input type="checkbox" id={behaviorType} checked={checked} />
                                    <div className="behaviorControl__slider"></div>
                                </label>
                            </div>
                            {tooltip && (
                                <div className="behaviorControl__tooltip">
                                    To implement it hold down [Ctrl] / [Cmd] + left mouse button
                                </div>
                            )}
                        </div>
                    );
                }
            
                function App() {
                    // State with active map behaviors
                    const [behaviors, setBehaviors] = useState<BehaviorType[]>(INITIALLY_ENABLED_BEHAVIORS);
            
                    // Handler function for add or remove map behavior
                    const onChangeBehaviorHandler = useCallback(
                        (event: TReact.FormEvent<HTMLLabelElement>) => {
                            const target = event.target as HTMLInputElement;
                            const behaviorName = target.id as BehaviorType;
                            const checked = target.checked;
            
                            if (checked) {
                                setBehaviors([...behaviors, behaviorName]);
                            } else {
                                setBehaviors(behaviors.filter((behavior) => behavior !== behaviorName));
                            }
                        },
                        [behaviors]
                    );
            
                    return (
                        // Initialize the map and pass initialization parameters
                        <MMap location={LOCATION} showScaleInCopyrights={true} behaviors={behaviors} ref={(x) => (map = x)}>
                            {/* Add a map scheme layer */}
                            <MMapDefaultSchemeLayer />
            
                            {/* Using MMapControls you can change the position of the control */}
                            <MMapControls position="top right">
                                {/* Add a shared container to the map for custom BehaviorControl's */}
                                <MMapControl>
                                    <div className="behavior">
                                        {/* Add a BehaviorControls to the map to change the behavior of the map */}
                                        {AVAILABLE_BEHAVIORS.map((behavior) => (
                                            <BehaviorControl
                                                key={behavior.type}
                                                behaviorName={behavior.title}
                                                behaviorType={behavior.type}
                                                checked={behaviors.includes(behavior.type)}
                                                tooltip={behavior.tooltip}
                                                onChange={onChangeBehaviorHandler}
                                            />
                                        ))}
                                    </div>
                                </MMapControl>
                            </MMapControls>
                        </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>
        <!-- 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 {AVAILABLE_BEHAVIORS, INITIALLY_ENABLED_BEHAVIORS} 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, MMapControls, MMapControl} = vuefy.module(mappable);
            
                const BehaviorControl = Vue.defineComponent({
                    props: {
                        behaviorName: String,
                        behaviorType: String,
                        checked: Boolean,
                        tooltip: Boolean
                    },
                    emits: ['changeBehavior'],
                    template: `
                  <div class="behaviorControl">
                    <div class="behaviorControl__block">
                      <div class="behaviorControl__title">{{ behaviorName }}</div>
                      <label
                        class="behaviorControl__label"
                        :for="behaviorType"
                      >
                        <input type="checkbox" :id="behaviorType" :checked="checked"
                               @change="$emit('changeBehavior', $event.target.checked, behaviorType)"/>
                        <div class="behaviorControl__slider"></div>
                      </label>
                    </div>
                    <div v-if="tooltip" class="behaviorControl__tooltip">
                      <div class="behaviorControl__tooltip">
                        To implement it hold down [Ctrl] / [Cmd] + left mouse button
                      </div>
                    </div>
                  </div>
                `
                });
            
                const app = Vue.createApp({
                    components: {
                        MMap,
                        MMapDefaultSchemeLayer,
                        BehaviorControl,
                        MMapControls,
                        MMapControl
                    },
                    setup() {
                        const refMap = (ref) => {
                            window.map = ref?.entity;
                        };
                        const availableBehaviors = AVAILABLE_BEHAVIORS;
                        const behaviors = Vue.ref([...INITIALLY_ENABLED_BEHAVIORS]);
            
                        // Handler function for add or remove map behavior
                        const onChangeBehaviorHandler = (event, behaviorType) => {
                            const checked = event; //.target.checked;
                            if (checked) {
                                behaviors.value.push(behaviorType);
                            } else {
                                behaviors.value = behaviors.value.filter((b) => b !== behaviorType);
                            }
                            behaviors.value = [...behaviors.value];
                        };
            
                        return {
                            LOCATION,
                            refMap,
                            availableBehaviors,
                            behaviors,
                            onChangeBehaviorHandler
                        };
                    },
                    template: `
                  <!--Initialize the map and pass initialization parameters-->
                  <MMap :location="LOCATION" :showScaleInCopyrights="true" :behaviors="behaviors" :ref="refMap">
                    <!--Add a map scheme layer-->
                    <MMapDefaultSchemeLayer/>
            
                    <!-- Using MMapControls you can change the position of the control -->
                    <MMapControls position="top right">
            
                      <!-- Add a shared container to the map for custom BehaviorControl's -->
                      <MMapControl>
                        <div class="behavior">
            
                          <!-- Add a BehaviorControls to the map to change the behavior of the map -->
                          <BehaviorControl
                            v-for="behavior in availableBehaviors"
                            :key="behavior.type"
                            :behaviorName="behavior.title"
                            :behaviorType="behavior.type"
                            :checked="behaviors.includes(behavior.type)"
                            :tooltip="behavior.tooltip"
                            @changeBehavior="onChangeBehaviorHandler"
                          />
                        </div>
                      </MMapControl>
                    </MMapControls>
                  </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: [54.343, 24.483], // starting position [lng, lat]
    zoom: 14.4 // starting zoom
};
:root {
    --interact-action: #122db2;
}
import type {BehaviorType} from '@mappable-world/mappable-types';

// An array with initially enabled map behaviors
export const INITIALLY_ENABLED_BEHAVIORS: BehaviorType[] = ['drag', 'scrollZoom', 'dblClick'];

// An array of available map behaviors. These are just some of the behaviors, you can see them all in the documentation
export const AVAILABLE_BEHAVIORS: {type: BehaviorType; title: string; tooltip?: boolean}[] = [
    {
        title: 'Drag',
        type: 'drag'
    },
    {
        title: 'Scroll zoom',
        type: 'scrollZoom'
    },
    {
        title: 'Double click',
        type: 'dblClick'
    },
    {
        title: 'Mouse rotate',
        type: 'mouseRotate',
        tooltip: true
    },
    {
        title: 'Mouse tilt',
        type: 'mouseTilt',
        tooltip: true
    }
];
.behavior {
    padding: 14px 16px 8px 16px;
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.behaviorControl {
    padding: 6px 4px 0 0;
    display: flex;
    flex-direction: column;
    width: 228px;
    min-height: 32px;
    box-sizing: border-box;
    gap: 6px;
}

.behaviorControl__title {
    font-size: 16px;
    color: #000000;
    font-weight: 500;
}

.behaviorControl__block {
    width: 100%;
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.behaviorControl__tooltip {
    font-size: 12px;
    line-height: 16px;
    color: #898a8f;
    width: 213px;
}

.behaviorControl__label {
    margin-left: auto;

    height: 22px;
    width: 40px;

    display: inline-block;
    position: relative;
}

.behaviorControl__label input {
    display: none;
}

.behaviorControl__slider {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    top: 0;

    cursor: pointer;
    border-radius: 22px;
    background-color: #ccc;
    transition: 0.4s;
}

.behaviorControl__slider:before {
    content: '';
    width: 16px;
    height: 16px;

    position: absolute;
    bottom: 3px;
    left: 3px;

    border-radius: 50%;
    background-color: #fff;
    transition: 0.4s;
}

.behaviorControl__label input:checked + .behaviorControl__slider {
    background-color: var(--interact-action);
}

.behaviorControl__label input:checked + .behaviorControl__slider:before {
    transform: translateX(18px);
}