Replacing a static map with a dynamic

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
            data-plugins="transform-modules-umd"
            data-presets="react, typescript"
            type="text/babel"
            src="./common.ts"
        ></script>
        <script
            data-plugins="transform-modules-umd"
            data-presets="react, typescript"
            type="text/babel"
            src="../variables.ts"
        ></script>
        <script data-plugins="transform-modules-umd" data-presets="react, typescript" type="text/babel">
            import {loadMapScript} from './common';
            import {LOCATION, STATIC_API_URL} from '../variables';
            
            window.map = null;
            
            // Function to create the map after initializing the JS Map API
            async function createMap() {
                // Waiting for all api elements to be loaded
                await mappable.ready;
                const {MMap, MMapDefaultSchemeLayer, MMapControls, MMapComplexEntity, MMapControl} = mappable;
            
                // Initialize the map
                map = new MMap(
                    // Pass the link to the HTMLElement of the container
                    document.getElementById('map'),
                    // Pass the map initialization parameters
                    {location: LOCATION, showScaleInCopyrights: true},
                    // Add a map scheme layer
                    [new MMapDefaultSchemeLayer({})]
                );
            
                class FullscreenButton extends MMapComplexEntity<{}> {
                    private _element: HTMLButtonElement;
            
                    private _detachDom: () => void;
            
                    // Method for create a DOM control element
                    _createElement() {
                        // Create a div element that will be passed to the MMapControlButton
                        const fullScreenButtonElement = document.createElement('button');
                        fullScreenButtonElement.type = 'button';
                        fullScreenButtonElement.onclick = () => {
                            document.getElementById('map').classList.toggle('hidden');
                            document.getElementById('image-wrapper').classList.toggle('hidden');
                        };
                        fullScreenButtonElement.classList.add('button', 'exit-fullscreen');
                        return fullScreenButtonElement;
                    }
            
                    // 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;
                    }
                }
            
                // Add MMapControlButton that will enable or disable fullscreen mode
                const fullScreenBtn = new MMapControl();
                fullScreenBtn.addChild(new FullscreenButton({}));
            
                // Add a container for MMapControlButton and add it to the map
                map.addChild(new MMapControls({position: 'top right'}, [fullScreenBtn]));
            }
            
            // Function to async loading of JS Map API script.
            async function fetchScript() {
                // Load the JS Map API script
                await loadMapScript();
            }
            
            // Event handler for tab change
            function onResizeButtonClick() {
                document.getElementById('image-wrapper').classList.toggle('hidden');
                document.getElementById('map').classList.toggle('hidden');
            
                // If mappable is not defined, fetch the script and create the map
                if (typeof mappable === 'undefined') {
                    fetchScript().then(() => {
                        createMap();
                    });
                }
            }
            
            // Add event listener for button
            document.getElementById('button').addEventListener('click', onResizeButtonClick);
            
            (document.getElementById('static-img') as HTMLImageElement).src = STATIC_API_URL;
        </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" />
    </head>
    <body>
        <div class="container">
            <div id="image-wrapper" class="image-wrapper">
                <img id="static-img" src="" alt="img" class="image" />
                <button id="button" class="button fullscreen"></button>
            </div>
            <div id="map" class="map hidden"></div>
        </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
            data-plugins="transform-modules-umd"
            data-presets="react, typescript"
            type="text/babel"
            src="./common.ts"
        ></script>
        <script
            data-plugins="transform-modules-umd"
            data-presets="react, typescript"
            type="text/babel"
            src="../variables.ts"
        ></script>
        <script data-plugins="transform-modules-umd" data-presets="react, typescript" type="text/babel">
            import {loadMapScript} from './common';
            import {LOCATION, STATIC_API_URL} from '../variables';
            import type {JSX} from 'react';
            
            window.map = null;
            
            main();
            
            async function main() {
                const {useState, useCallback} = React;
                // Function for create a map component after initializing the JS Map API
                const createMapComponent = async (onClickHandler: () => void) => {
                    // 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);
            
                    // Return a functional React component representing the map
                    return () => {
                        return (
                            // Initialize the map and pass initialization parameters
                            <MMap location={LOCATION} showScaleInCopyrights={true} ref={useCallback((x) => (map = x), [])}>
                                {/* Add a map scheme layer */}
                                <MMapDefaultSchemeLayer />
            
                                {/* Add a container for MMapControlButton */}
                                <MMapControls position="top right">
                                    {/* Add MMapControlButton that will enable or disable fullscreen mode */}
                                    <MMapControl>
                                        <button type="button" onClick={onClickHandler} className="button exit-fullscreen" />
                                    </MMapControl>
                                </MMapControls>
                            </MMap>
                        );
                    };
                };
            
                // When the JS Map API is initialized, the Map variable will become a react component
                let Map: () => JSX.Element = null;
            
                function App() {
                    const [isStatic, setIsStatic] = useState(true);
            
                    const onResizeButtonClick = useCallback(async () => {
                        if (typeof mappable === 'undefined') {
                            try {
                                // Load the JS Map API script
                                await loadMapScript();
                                // Initialize the Map component
                                Map = await createMapComponent(() => setIsStatic(true));
                            } catch (error) {
                                // Log any errors that occur during script loading
                                console.error(error);
                            }
                        }
                        setIsStatic(false);
                    }, []);
            
                    return (
                        <div className="container">
                            {isStatic ? (
                                <div className="image-wrapper">
                                    <img src={STATIC_API_URL} alt="img" className="image" />
                                    <button className="button fullscreen" onClick={onResizeButtonClick} />
                                </div>
                            ) : (
                                <Map />
                            )}
                        </div>
                    );
                }
            
                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" />
    </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
            data-plugins="transform-modules-umd"
            data-presets="react, typescript"
            type="text/babel"
            src="./common.ts"
        ></script>
        <script
            data-plugins="transform-modules-umd"
            data-presets="react, typescript"
            type="text/babel"
            src="../variables.ts"
        ></script>
        <script data-plugins="transform-modules-umd" data-presets="typescript" type="text/babel">
            import {loadMapScript} from './common';
            import {LOCATION, STATIC_API_URL} from '../variables';
            import type {Component} from '@vue/runtime-core';
            
            window.map = null;
            
            async function main() {
                const Map = Vue.defineAsyncComponent<Component>({
                    loader: () => {
                        return new Promise(async (resolve, reject) => {
                            if (typeof mappable == 'undefined') {
                                await loadMapScript();
                            }
            
                            const [mappableVue] = await Promise.all([mappable.import('@mappable-world/mappable-vuefy'), mappable.ready]);
            
                            const vuefy = mappableVue.vuefy.bindTo(Vue);
                            const {MMap, MMapDefaultSchemeLayer, MMapControl, MMapControls} = vuefy.module(mappable);
            
                            resolve({
                                props: {
                                    onClickHandler: Function
                                },
                                setup() {
                                    const refMap = (ref) => {
                                        window.map = ref?.entity;
                                    };
                                    return {
                                        LOCATION,
                                        refMap
                                    };
                                },
                                components: {
                                    MMap,
                                    MMapDefaultSchemeLayer,
                                    MMapControls,
                                    MMapControl
                                },
                                template: `
                        <MMap :location="LOCATION" :showScaleInCopyrights="true" :ref="refMap">
                          <!-- Add a map scheme layer -->
                          <MMapDefaultSchemeLayer />
            
                          <MMapControls position="top right">
                            <MMapControl>
                              <button
                                type="button"
                                @click="onClickHandler"
                                class="button exit-fullscreen"
                              />
                            </MMapControl>
                          </MMapControls>
                        </MMap>
                      `
                            });
                        });
                    }
                });
            
                const app = Vue.createApp({
                    setup() {
                        const isStatic = Vue.ref(true);
            
                        const onResizeButtonClick = async () => {
                            isStatic.value = !isStatic.value;
                        };
            
                        return {
                            STATIC_API_URL,
                            isStatic,
                            onResizeButtonClick
                        };
                    },
                    components: {
                        Map
                    },
                    template: `
                  <div class="container">
                    <template v-if="isStatic">
                      <div class="image-wrapper">
                        <img :src="STATIC_API_URL" alt="img" class="image"/>
                        <button class="button fullscreen" @click="onResizeButtonClick"/>
                      </div>
                    </template>
                    <template v-else>
                      <Map :onClickHandler="onResizeButtonClick" />
                    </template>
                  </div>
                `
                });
                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" />
    </head>
    <body>
        <div id="app"></div>
    </body>
</html>
import type {MMapLocationRequest} from '@mappable-world/mappable-types';

export const LOCATION: MMapLocationRequest = {
    center: [55.276, 25.195], // starting position [lng, lat]
    zoom: 15.4 // starting zoom
};

export const STATIC_API_URL =
    'https://static.api.mappable.world/v1?ll=55.276012,25.195050&size=650,450&scale=2&z=15&apikey=<YOUR_APIKEY>&lang=en_US';
// Function to async load the JS Map API script.
export function loadMapScript() {
    return new Promise((resolve, reject) => {
        const script = document.createElement('script');
        script.src = 'https://js.api.mappable.world/v3/?apikey=<YOUR_APIKEY>&lang=en_US';
        script.onload = resolve;
        script.onerror = reject;
        document.head.appendChild(script);
    });
}
.container {
    width: 100%;
    height: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
}

.image {
    border-radius: 8px;
    width: 325px;
    height: 225px;
}

.map {
    width: 100%;
    height: 100%;
}

.hidden {
    display: none;
}

.image-wrapper {
    position: relative;
}

.button {
    all: unset;
    cursor: pointer;
    width: 32px;
    height: 32px;
    background-position: 50% 50%;
    background-repeat: no-repeat;
    box-shadow: 0px 2px 4px 0px #5f698333;
    border-radius: 8px;
    position: absolute;
    top: 8px;
    right: 8px;
    background-color: #ffffff;
}

.button.fullscreen {
    background-image: url('./fullscreen.svg');
}

.button.exit-fullscreen {
    background-image: url('./fullscreen-exit.svg');
}