Adding a default popup on the map

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 {MMapPopupPositionProps} from '@mappable-world/mappable-default-ui-theme';
            import {ACTION, CUSTOM_POPUP_COORDS, DESCRIPTION, POPUP_TEXT, TEXT_POPUP_COORDS, TITLE} from './common';
            import {LOCATION} from '../variables';
            
            window.map = null;
            
            main();
            async function main() {
                await mappable.ready;
                const {MMap, MMapDefaultSchemeLayer, MMapDefaultFeaturesLayer, MMapControls, MMapControlButton} = mappable;
            
                const {MMapPopupMarker} = await mappable.import('@mappable-world/mappable-default-ui-theme');
            
                map = new MMap(document.getElementById('app'), {location: LOCATION});
            
                map.addChild(new MMapDefaultSchemeLayer({}));
                map.addChild(new MMapDefaultFeaturesLayer({}));
            
                const updatePositions = (position: MMapPopupPositionProps) => {
                    textPopup.update({position});
                    customPopup.update({position});
                };
            
                map.addChild(
                    new MMapControls({position: 'top right'}, [
                        new MMapControlButton({text: 'Left', onClick: () => updatePositions('left')}),
                        new MMapControlButton({text: 'Left Top', onClick: () => updatePositions('left top')}),
                        new MMapControlButton({text: 'Left Bottom', onClick: () => updatePositions('left bottom')}),
                        new MMapControlButton({text: 'Bottom', onClick: () => updatePositions('bottom')}),
                        new MMapControlButton({text: 'Top', onClick: () => updatePositions('top')}),
                        new MMapControlButton({text: 'Right Top', onClick: () => updatePositions('right top')}),
                        new MMapControlButton({text: 'Right Bottom', onClick: () => updatePositions('right bottom')}),
                        new MMapControlButton({text: 'Right', onClick: () => updatePositions('right')})
                    ])
                );
            
                const textPopup = new MMapPopupMarker({coordinates: TEXT_POPUP_COORDS, draggable: true, content: POPUP_TEXT});
                map.addChild(textPopup);
            
                const customPopup = new MMapPopupMarker({
                    coordinates: CUSTOM_POPUP_COORDS,
                    draggable: true,
                    content: createDefaultPopup
                });
                map.addChild(customPopup);
            
                function createDefaultPopup(): HTMLElement {
                    const popupRootElement = document.createElement('span');
                    popupRootElement.classList.add('popup');
            
                    const popupHeaderElement = document.createElement('span');
                    popupHeaderElement.classList.add('header');
                    popupRootElement.appendChild(popupHeaderElement);
            
                    const titleElement = document.createElement('span');
                    titleElement.classList.add('header_title');
                    titleElement.textContent = TITLE;
                    popupHeaderElement.appendChild(titleElement);
            
                    const closeButton = document.createElement('button');
                    closeButton.classList.add('header_close');
                    closeButton.addEventListener('click', () => {
                        customPopup.update({show: false});
                    });
                    popupHeaderElement.appendChild(closeButton);
            
                    const descriptionElement = document.createElement('span');
                    descriptionElement.classList.add('description');
                    descriptionElement.textContent = DESCRIPTION;
                    popupRootElement.appendChild(descriptionElement);
            
                    const actionButton = document.createElement('button');
                    actionButton.classList.add('action');
                    actionButton.textContent = ACTION;
                    actionButton.addEventListener('click', () => {
                        alert('Click on action button!');
                    });
                    popupRootElement.appendChild(actionButton);
            
                    return popupRootElement;
                }
            }
        </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/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 {MMapPopupPositionProps} from '@mappable-world/mappable-default-ui-theme';
            import {ACTION, CUSTOM_POPUP_COORDS, DESCRIPTION, POPUP_TEXT, TEXT_POPUP_COORDS, TITLE} from './common';
            import {LOCATION} from '../variables';
            
            window.map = null;
            
            main();
            async function main() {
                const [mappableReact] = await Promise.all([mappable.import('@mappable-world/mappable-reactify'), mappable.ready]);
                const reactify = mappableReact.reactify.bindTo(React, ReactDOM);
            
                const {MMap, MMapDefaultSchemeLayer, MMapDefaultFeaturesLayer, MMapControls, MMapControlButton} =
                    reactify.module(mappable);
            
                const {useState, useCallback} = React;
            
                const {MMapPopupMarker} = reactify.module(await mappable.import('@mappable-world/mappable-default-ui-theme'));
            
                ReactDOM.render(
                    <React.StrictMode>
                        <App />
                    </React.StrictMode>,
                    document.getElementById('app')
                );
            
                function App() {
                    const [position, setPosition] = useState<MMapPopupPositionProps>(undefined);
                    const [showCustom, setShowCustom] = useState(true);
            
                    const positionLeft = useCallback(() => setPosition('left'), []);
                    const positionLeftTop = useCallback(() => setPosition('left top'), []);
                    const positionLeftBottom = useCallback(() => setPosition('left bottom'), []);
                    const positionBottom = useCallback(() => setPosition('bottom'), []);
                    const positionTop = useCallback(() => setPosition('top'), []);
                    const positionRightTop = useCallback(() => setPosition('right top'), []);
                    const positionRightBottom = useCallback(() => setPosition('right bottom'), []);
                    const positionRight = useCallback(() => setPosition('right'), []);
            
                    const customPopupContent = useCallback(
                        () => (
                            <span className="popup">
                                <span className="header">
                                    <span className="header_title">{TITLE}</span>
                                    <button className="header_close" onClick={() => setShowCustom(false)}></button>
                                </span>
                                <span className="description">{DESCRIPTION}</span>
                                <button className="action" onClick={() => alert('Click on action button!')}>
                                    {ACTION}
                                </button>
                            </span>
                        ),
                        []
                    );
            
                    return (
                        <MMap location={LOCATION} ref={(x) => (map = x)}>
                            <MMapDefaultSchemeLayer />
                            <MMapDefaultFeaturesLayer />
                            <MMapControls position="top right">
                                <MMapControlButton text="Left" onClick={positionLeft} />
                                <MMapControlButton text="Left Top" onClick={positionLeftTop} />
                                <MMapControlButton text="Left Bottom" onClick={positionLeftBottom} />
                                <MMapControlButton text="Bottom" onClick={positionBottom} />
                                <MMapControlButton text="Top" onClick={positionTop} />
                                <MMapControlButton text="Right Top" onClick={positionRightTop} />
                                <MMapControlButton text="Right Bottom" onClick={positionRightBottom} />
                                <MMapControlButton text="Right" onClick={positionRight} />
                            </MMapControls>
                            <MMapPopupMarker coordinates={TEXT_POPUP_COORDS} draggable position={position} content={POPUP_TEXT} />
                            <MMapPopupMarker
                                coordinates={CUSTOM_POPUP_COORDS}
                                draggable
                                position={position}
                                show={showCustom}
                                content={customPopupContent}
                            />
                        </MMap>
                    );
                }
            }
        </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 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 {MMapPopupPositionProps} from '@mappable-world/mappable-default-ui-theme';
            import {ACTION, CUSTOM_POPUP_COORDS, DESCRIPTION, POPUP_TEXT, TEXT_POPUP_COORDS, TITLE} from './common';
            import {LOCATION} from '../variables';
            
            window.map = null;
            
            main();
            async function main() {
                const [mappableVue] = await Promise.all([mappable.import('@mappable-world/mappable-vuefy'), mappable.ready]);
                const vuefy = mappableVue.vuefy.bindTo(Vue);
            
                const {MMap, MMapDefaultSchemeLayer, MMapDefaultFeaturesLayer, MMapControls, MMapControlButton} =
                    vuefy.module(mappable);
            
                const {MMapPopupMarker} = vuefy.module(await mappable.import('@mappable-world/mappable-default-ui-theme'));
            
                const app = Vue.createApp({
                    components: {
                        MMap,
                        MMapDefaultSchemeLayer,
                        MMapDefaultFeaturesLayer,
                        MMapControls,
                        MMapControlButton,
                        MMapPopupMarker
                    },
                    setup() {
                        const refMap = (ref: any) => {
                            window.map = ref?.entity;
                        };
                        const position = Vue.ref<MMapPopupPositionProps>(undefined);
                        const showCustom = Vue.ref(true);
            
                        const positionLeft = () => (position.value = 'left');
                        const positionLeftTop = () => (position.value = 'left top');
                        const positionLeftBottom = () => (position.value = 'left bottom');
                        const positionBottom = () => (position.value = 'bottom');
                        const positionTop = () => (position.value = 'top');
                        const positionRightTop = () => (position.value = 'right top');
                        const positionRightBottom = () => (position.value = 'right bottom');
                        const positionRight = () => (position.value = 'right');
            
                        const customPopupAction = () => {
                            alert('Click on action button!');
                        };
            
                        return {
                            ACTION,
                            CUSTOM_POPUP_COORDS,
                            DESCRIPTION,
                            LOCATION,
                            POPUP_TEXT,
                            TEXT_POPUP_COORDS,
                            TITLE,
                            position,
                            showCustom,
                            refMap,
                            positionLeft,
                            positionLeftTop,
                            positionLeftBottom,
                            positionBottom,
                            positionTop,
                            positionRightTop,
                            positionRightBottom,
                            positionRight,
                            customPopupAction
                        };
                    },
                    template: `
                        <MMap :location="LOCATION" :ref="refMap">
                            <MMapDefaultSchemeLayer />
                            <MMapDefaultFeaturesLayer />
                            <MMapControls position="top right">
                                <MMapControlButton text="Left" :onClick="positionLeft" />
                                <MMapControlButton text="Left Top" :onClick="positionLeftTop" />
                                <MMapControlButton text="Left Bottom" :onClick="positionLeftBottom" />
                                <MMapControlButton text="Bottom" :onClick="positionBottom" />
                                <MMapControlButton text="Top" :onClick="positionTop" />
                                <MMapControlButton text="Right Top" :onClick="positionRightTop" />
                                <MMapControlButton text="Right Bottom" :onClick="positionRightBottom" />
                                <MMapControlButton text="Right" :onClick="positionRight" />
                            </MMapControls>
            
                            <MMapPopupMarker :coordinates="TEXT_POPUP_COORDS" :draggable="true" :position="position">
                                <template #content>
                                    {{POPUP_TEXT}}
                                </template>
                            </MMapPopupMarker>
            
                            <MMapPopupMarker :coordinates="CUSTOM_POPUP_COORDS" :draggable="true" :position="position" :show="showCustom">
                                <template #content>
                                    <span class="popup">
                                        <span class="header">
                                            <span class="header_title">{{TITLE}}</span>
                                            <button class="header_close" @click="showCustom=false"></button>
                                        </span>
                                        <span class="description">{{DESCRIPTION}}</span>
                                        <button class="action" @click="customPopupAction">
                                            {{ACTION}}
                                        </button>
                                    </span>
                                </template>
                            </MMapPopupMarker>
                        </MMap>`
                });
                app.mount('#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>
import type {LngLat, MMapLocationRequest} from '@mappable-world/mappable-types';

export const CENTER: LngLat = [55.442795, 25.24107];
export const LOCATION: MMapLocationRequest = {center: CENTER, zoom: 14};
import type {LngLat} from '@mappable-world/mappable-types';
import {CENTER} from './variables';

mappable.ready.then(() => {
    mappable.import.registerCdn('https://cdn.jsdelivr.net/npm/{package}', ['@mappable-world/mappable-default-ui-theme@0.0']);
});

export const POPUP_TEXT = 'Default text popup';
export const CUSTOM_POPUP_COORDS: LngLat = [CENTER[0] - 0.02, CENTER[1]];
export const TEXT_POPUP_COORDS: LngLat = [CENTER[0] + 0.02, CENTER[1]];

export const TITLE = 'Default popup marker';
export const DESCRIPTION = 'Description for default popup';
export const ACTION = 'Make an action';
.popup {
    display: flex;
    flex-direction: column;
    row-gap: 8px;
}

.header {
    display: flex;
    flex-direction: row;
    justify-content: space-between;
    gap: 8px;
    padding-right: 20px;

    .header_title {
        font-size: 16px;
        font-weight: 400;
        line-height: 22px;
        color: #050d33;
    }
    .header_close {
        position: absolute;
        top: 0;
        right: 0;
        display: flex;
        justify-content: center;
        align-items: center;
        width: 32px;
        height: 32px;
        border: none;
        background: none;
        color: #c8c9cc;
        cursor: pointer;
        background-image: url('./close.svg');
        background-position: center;
        background-repeat: no-repeat;
    }
}

.description {
    font-size: 14px;
    font-weight: 400;
    line-height: 20px;
    color: #7b7d85;
}

.action {
    width: max-content;
    padding: 12px 16px;
    border-radius: 8px;
    border: none;
    background-color: #196dff;
    transition: background-color 0.1s ease-out;
    color: #ffffff;
    text-align: center;
    cursor: pointer;
    white-space: normal;

    font-size: 14px;
    font-style: normal;
    font-weight: 500;
    line-height: 16px;
}

.action:hover {
    background-color: #4183ff;
}