Контекстное меню

<!DOCTYPE html>
<html>
    <head>
        <title>Vanilla example ymaps3-context-menu</title>
        <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://api-maps.yandex.ru/v3/?apikey=<YOUR_APIKEY>&lang=en_US" type="text/javascript"></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"
            src="../variables.ts"
        ></script>
        <script data-plugins="transform-modules-umd" data-presets="typescript" type="text/babel">
            import type {LngLat} from '@yandex/ymaps3-types';
            import {InfoPanelControl} from './common';
            import {LOCATION, MARKER_LOCATION} from '../variables';
            window.map = null;
            
            main();
            async function main() {
                await ymaps3.ready;
                const {YMap, YMapDefaultSchemeLayer, YMapDefaultFeaturesLayer, YMapListener, YMapControls} = ymaps3;
            
                const {YMapContextMenu, YMapContextMenuItem} = await ymaps3.import('@yandex/ymaps3-context-menu');
                const {YMapDefaultMarker} = await ymaps3.import('@yandex/ymaps3-default-ui-theme');
            
                let contextMenuLngLat: LngLat = null;
                map = new YMap(document.getElementById('app'), {location: LOCATION});
            
                map.addChild(new YMapDefaultSchemeLayer({}));
                map.addChild(new YMapDefaultFeaturesLayer({}));
            
                const contextMenu = new YMapContextMenu({
                    visible: false,
                    screenCoordinates: [0, 0]
                });
                contextMenu
                    .addChild(
                        new YMapContextMenuItem({
                            text: 'Zoom in',
                            visible: true,
                            icon: '../zoomInIcon.svg',
                            onClick: () => {
                                map.update({location: {zoom: map.zoom + 1, duration: 500, easing: 'ease-in-out'}});
                                markerMenuItem.update({visible: false});
                                contextMenu.update({visible: false});
                            }
                        })
                    )
                    .addChild(
                        new YMapContextMenuItem({
                            text: 'Zoom out',
                            visible: true,
                            icon: '../zoomOutIcon.svg',
                            onClick: () => {
                                map.update({location: {zoom: map.zoom - 1, duration: 600, easing: 'ease-in-out'}});
                                markerMenuItem.update({visible: false});
                                contextMenu.update({visible: false});
                            }
                        })
                    )
                    .addChild(
                        new YMapContextMenuItem({
                            text: 'Center by it',
                            visible: true,
                            icon: '../centerByItIcon.svg',
                            onClick: () => {
                                map.setLocation({center: contextMenuLngLat, duration: 400, easing: 'ease-in-out'});
                                markerMenuItem.update({visible: false});
                                contextMenu.update({visible: false});
                            }
                        })
                    );
            
                const markerMenuItem = new YMapContextMenuItem({
                    text: 'Marker item',
                    visible: false,
                    order: 1,
                    onClick: () => {
                        contextMenu.update({visible: false});
                        markerMenuItem.update({visible: false});
                        alert('click on marker!');
                    }
                });
                contextMenu.addChild(markerMenuItem);
            
                map.addChild(contextMenu);
            
                map.addChild(
                    new YMapDefaultMarker({
                        coordinates: MARKER_LOCATION,
                        iconName: 'fallback',
                        size: 'normal'
                    })
                );
            
                const listener = new YMapListener({
                    onContextMenu: (object, event) => {
                        if (object && object.entity instanceof ymaps3.YMapMarker) {
                            markerMenuItem.update({visible: true});
                        } else {
                            markerMenuItem.update({visible: false});
                        }
            
                        contextMenuLngLat = event.coordinates;
                        contextMenu.update({
                            visible: true,
                            screenCoordinates: event.screenCoordinates
                        });
                    },
                    onActionStart: () => {
                        contextMenu.update({visible: false});
                    }
                });
                map.addChild(listener);
            
                map.addChild(new YMapControls({position: 'top left'}, [new InfoPanelControl()]));
            }
        </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="../variables.css" />
        <link rel="stylesheet" href="./common.css" />
    </head>
    <body>
        <div id="app"></div>
    </body>
</html>
<!DOCTYPE html>
<html>
    <head>
        <title>React example ymaps3-context-menu</title>
        <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://api-maps.yandex.ru/v3/?apikey=<YOUR_APIKEY>&lang=en_US" type="text/javascript"></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"
            src="../variables.ts"
        ></script>
        <script data-plugins="transform-modules-umd" data-presets="react, typescript" type="text/babel">
            import type {BehaviorMapEventHandler, DomEventHandler, LngLat} from '@yandex/ymaps3-types';
            import {LOCATION, MARKER_LOCATION} from '../variables';
            import {InfoPanelControl} from './common';
            window.map = null;
            
            main();
            async function main() {
                const [ymaps3React] = await Promise.all([ymaps3.import('@yandex/ymaps3-reactify'), ymaps3.ready]);
                const reactify = ymaps3React.reactify.bindTo(React, ReactDOM);
            
                const {YMap, YMapDefaultSchemeLayer, YMapDefaultFeaturesLayer, YMapListener, YMapControls} =
                    reactify.module(ymaps3);
            
                const {useState} = React;
            
                const {YMapContextMenu, YMapContextMenuItem} = reactify.module(await ymaps3.import('@yandex/ymaps3-context-menu'));
                const {YMapDefaultMarker} = reactify.module(await ymaps3.import('@yandex/ymaps3-default-ui-theme'));
                const InfoPanelControlR = reactify.entity(InfoPanelControl);
            
                const App = () => {
                    const [location, setLocation] = useState(LOCATION);
                    const [visibleContextMenu, setVisibleContextMenu] = useState(false);
                    const [visibleMarkerItem, setVisibleMarkerItem] = useState(false);
                    const [screenCoordinates, setScreenCoordinates] = useState<[number, number]>();
                    const [contextMenuLngLat, setContextMenuLngLat] = useState<LngLat>();
            
                    const onContextMenu: DomEventHandler = (object, event) => {
                        if (object && object.entity instanceof ymaps3.YMapMarker) {
                            setVisibleMarkerItem(true);
                        } else {
                            setVisibleMarkerItem(false);
                        }
            
                        setContextMenuLngLat(event.coordinates);
                        setVisibleContextMenu(true);
                        setScreenCoordinates(event.screenCoordinates);
                    };
            
                    const onActionStart: BehaviorMapEventHandler = () => {
                        setVisibleContextMenu(false);
                    };
            
                    const zoomIn = () => {
                        setLocation({zoom: map.zoom + 1, duration: 600, easing: 'ease-in-out'});
                        setVisibleContextMenu(false);
                    };
            
                    const zoomOut = () => {
                        setLocation({zoom: map.zoom - 1, duration: 400, easing: 'ease-in-out'});
                        setVisibleContextMenu(false);
                    };
            
                    const centerByIt = () => {
                        setLocation({center: contextMenuLngLat, duration: 500, easing: 'ease-in-out'});
                        setVisibleContextMenu(false);
                    };
            
                    const clickOnMarker = () => {
                        setVisibleContextMenu(false);
                        alert('click on marker!');
                    };
            
                    return (
                        <YMap location={location} ref={(x) => (map = x)}>
                            <YMapDefaultSchemeLayer />
            
                            <YMapDefaultFeaturesLayer />
                            <YMapContextMenu visible={visibleContextMenu} screenCoordinates={screenCoordinates}>
                                <YMapContextMenuItem icon="../zoomInIcon.svg" text="Zoom in" visible={true} onClick={zoomIn} />
                                <YMapContextMenuItem icon="../zoomOutIcon.svg" text="Zoom out" visible={true} onClick={zoomOut} />
                                <YMapContextMenuItem
                                    icon="../centerByItIcon.svg"
                                    text="Center by it"
                                    visible={true}
                                    onClick={centerByIt}
                                />
                                <YMapContextMenuItem
                                    text="Marker item"
                                    visible={visibleMarkerItem}
                                    order={1}
                                    onClick={clickOnMarker}
                                />
                            </YMapContextMenu>
            
                            <YMapDefaultMarker iconName="fallback" size="normal" coordinates={MARKER_LOCATION} />
            
                            <YMapListener onContextMenu={onContextMenu} onActionStart={onActionStart} />
            
                            <YMapControls position="top left">
                                <InfoPanelControlR />
                            </YMapControls>
                        </YMap>
                    );
                };
            
                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="../variables.css" />
        <link rel="stylesheet" href="./common.css" />
    </head>
    <body>
        <div id="app"></div>
    </body>
</html>
<!DOCTYPE html>
<html>
    <head>
        <title>Vue example ymaps3-context-menu</title>
        <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://api-maps.yandex.ru/v3/?apikey=<YOUR_APIKEY>&lang=en_US" type="text/javascript"></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"
            src="../variables.ts"
        ></script>
        <script data-plugins="transform-modules-umd" data-presets="typescript" type="text/babel">
            import type {BehaviorMapEventHandler, DomEventHandler, LngLat} from '@yandex/ymaps3-types';
            import {LOCATION, MARKER_LOCATION} from '../variables';
            import {InfoPanelControl} from './common';
            
            window.map = null;
            
            main();
            async function main() {
                const [ymaps3Vue] = await Promise.all([ymaps3.import('@yandex/ymaps3-vuefy'), ymaps3.ready]);
                const vuefy = ymaps3Vue.vuefy.bindTo(Vue);
            
                const {YMap, YMapDefaultSchemeLayer, YMapDefaultFeaturesLayer, YMapListener, YMapControls} = vuefy.module(ymaps3);
            
                const {YMapContextMenu, YMapContextMenuItem} = vuefy.module(await ymaps3.import('@yandex/ymaps3-context-menu'));
                const {YMapDefaultMarker} = vuefy.module(await ymaps3.import('@yandex/ymaps3-default-ui-theme'));
                const InfoPanelControlV = vuefy.entity(InfoPanelControl);
            
                const app = Vue.createApp({
                    components: {
                        YMap,
                        YMapDefaultSchemeLayer,
                        YMapDefaultFeaturesLayer,
                        YMapListener,
                        YMapContextMenu,
                        YMapContextMenuItem,
                        YMapDefaultMarker,
                        YMapControls,
                        InfoPanelControlV
                    },
                    setup() {
                        const refMap = (ref) => {
                            window.map = ref?.entity;
                        };
                        const location = Vue.ref(LOCATION);
                        const visibleContextMenu = Vue.ref(false);
                        const visibleMarkerItem = Vue.ref(false);
                        const screenCoordinates = Vue.ref<[number, number]>();
                        const contextMenuLngLat = Vue.ref<LngLat>();
            
                        const onContextMenu: DomEventHandler = (object, event) => {
                            if (object && object.entity instanceof ymaps3.YMapMarker) {
                                visibleMarkerItem.value = true;
                            } else {
                                visibleMarkerItem.value = false;
                            }
            
                            contextMenuLngLat.value = event.coordinates;
                            visibleContextMenu.value = true;
                            screenCoordinates.value = event.screenCoordinates;
                        };
            
                        const onActionStart: BehaviorMapEventHandler = () => {
                            visibleContextMenu.value = false;
                        };
            
                        const zoomIn = () => {
                            location.value = {
                                zoom: map.zoom + 1,
                                duration: 400,
                                easing: 'ease-in-out'
                            };
                            visibleContextMenu.value = false;
                        };
            
                        const zoomOut = () => {
                            location.value = {
                                zoom: map.zoom - 1,
                                duration: 600,
                                easing: 'ease-in-out'
                            };
                            visibleContextMenu.value = false;
                        };
            
                        const centerByIt = () => {
                            location.value = {center: contextMenuLngLat.value, duration: 500, easing: 'ease-in-out'};
                            visibleContextMenu.value = false;
                        };
            
                        const clickOnMarker = () => {
                            visibleContextMenu.value = false;
                            alert('click on marker!');
                        };
            
                        return {
                            location,
                            visibleContextMenu,
                            refMap,
                            visibleMarkerItem,
                            screenCoordinates,
                            contextMenuLngLat,
                            clickOnMarker,
                            centerByIt,
                            zoomOut,
                            zoomIn,
                            onActionStart,
                            onContextMenu,
                            MARKER_LOCATION
                        };
                    },
                    template: `
                        <YMap :location="location" :ref="refMap">
                            <YMapDefaultSchemeLayer />
            
                            <YMapDefaultFeaturesLayer />
            
                            <YMapContextMenu :visible="visibleContextMenu" :screenCoordinates="screenCoordinates">
                                <YMapContextMenuItem icon="../zoomInIcon.svg" text="Zoom in" :visible="true" :onClick="zoomIn" />
                                <YMapContextMenuItem icon="../zoomOutIcon.svg" text="Zoom out" :visible="true" :onClick="zoomOut" />
                                <YMapContextMenuItem icon="../centerByItIcon.svg" text="Center by it" :visible="true" :onClick="centerByIt" />
                                <YMapContextMenuItem text="Marker item" :visible="visibleMarkerItem" :order="1" :onClick="clickOnMarker" />
                            </YMapContextMenu>
            
                            <YMapDefaultMarker :coordinates="MARKER_LOCATION" iconName="fallback" size="normal" />
            
                            <YMapListener :onContextMenu="onContextMenu" :onActionStart="onActionStart" />
            
                            <YMapControls position="top left">
                                <InfoPanelControlV />
                            </YMapControls>
                        </YMap>`
                });
                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="../variables.css" />
        <link rel="stylesheet" href="./common.css" />
    </head>
    <body>
        <div id="app"></div>
    </body>
</html>
import type {LngLat, YMapLocationRequest} from '@yandex/ymaps3-types';

export const MARKER_LOCATION: LngLat = [37.623082, 55.75254];
export const LOCATION: YMapLocationRequest = {
    center: [37.623082, 55.75254], // starting position [lng, lat]
    zoom: 9 // starting zoom
};
:root {
    --panel-background-color: #212326;
    --panel-color: #fff;
}
import type {LngLat, YMapLocationRequest} from '@yandex/ymaps3-types';

ymaps3.import.registerCdn('https://cdn.jsdelivr.net/npm/{package}', [
    '@yandex/ymaps3-default-ui-theme@0.0',
    '@yandex/ymaps3-context-menu@0.0'
]);

export let InfoPanelControl = null;

ymaps3.ready.then(() => {
    class InfoPanelControlClass extends ymaps3.YMapComplexEntity<{}> {
        private readonly __text = 'Click on marker with right button';

        constructor() {
            super({});
            const container = document.createElement('div');
            container.classList.add('info');

            const iconElement = document.createElement('div');
            iconElement.classList.add('icon');
            container.appendChild(iconElement);

            const textElement = document.createElement('div');
            textElement.textContent = this.__text;
            container.appendChild(textElement);

            this.addChild(new ymaps3.YMapControl({transparent: true}, container));
        }
    }
    InfoPanelControl = InfoPanelControlClass;
});
.info {
    display: flex;
    flex-direction: row;
    align-items: center;

    padding: 8px 12px;

    font-size: 14px;
    font-style: normal;
    line-height: 20px;

    color: var(--panel-color);
    border-radius: 12px;
    background-color: var(--panel-background-color);
    box-shadow: 0 0 2px 0 rgba(95, 105, 131, 0.08), 0 2px 4px 0 rgba(95, 105, 131, 0.2);
    gap: 6px;
}

.icon {
    display: block;

    width: 16px;
    height: 16px;

    background-image: url(./iconRightButton.svg);
}