# Context Menu

ContextMenu is a context menu that displays a right-click menu interface.

# Common Methods

Method Description
add Add menu item
show Show menu
hide Hide menu
toggle Toggle menu visibility
Type Description
seperator Separator line
custom Custom menu item
multi Multiple buttons
submenu Submenu item
hovermenu Hover menu item
normal Default normal menu item

# Examples

# Map Context Menu

Map context menu (opens new window)

// Selected icon, or specify src path directly
const checkIcon = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAANpJREFUOE9jZKAQMFKon2FgDKjZHVHf4rqiEeR6kl1QuzOyn+E/4/lmj2WLSDagekdkASMjg1aL+/I0WNgR7YLaHRG2/xgZYlrdV6QjBzzRBtTsijzzl4nVvd1l0VucBtTsjlz5/z/jt1a3ZYnIimp2RZ7//+9fQ6vHyo3o0Q53Qc32CAcGpv88DIxMxQyMDB9aXJcHghTX7IxcyMjAeLTZfdksbGkGqxdqdkUuZ2BgkPjPyDCFgeG/W6srqr+JCoPqXZG9jAwM9iyszy0aHA/8wZViiQ7EYWwAAPShQBF/1IlVAAAAAElFTkSuQmCC";
map.setMenu(event => {
    return new vjmap.ContextMenu({
        event: event.originalEvent,
        theme: "dark", //light
        width: "250px",
        items: [
            {type: 'custom', markup: `<span style="color: #ffff00; padding-left: 30px">Context menu demo</span>`},
            {type: 'multi', items: [
                    {label: 'Fit to map', onClick: () => { map.fitMapBounds(); }},
                    {label: 'Reset rotation', onClick: () => { map.setBearing(0); }},
                    {label: 'Reset pitch', onClick: () => { map.setPitch(0); }},
                ]},
            {label: 'Get coordinates at this position', onClick: () => {
                    let point = map.fromLngLat(event.lngLat);
                    map.logInfo(`Current coordinates: x: ${point.x}, y: ${point.y}`);
                }},
            {type: 'seperator'},
            {type: 'submenu', label: 'Map zoom', items: [
                    {label: 'Zoom in one level', onClick: () => { map.setZoom(map.getZoom() + 1)}},
                    {label: 'Zoom out one level', onClick: () => { map.setZoom(map.getZoom() - 1)}, enabled: map.getZoom() - 1 > 0},
                    {label: 'Zoom to minimum level', onClick: () => { map.setZoom(0)}},
                    {label: 'Fly to this position', onClick: () => {
                            map.flyTo({
                                center: event.lngLat,
                                pitch: 60,
                                zoom: 5
                            })
                        }}
                ]},
            {type: 'hovermenu', label: 'Map settings', items: [
                    {
                        label: 'Double-click to zoom',
                        icon: map.doubleClickZoom.isEnabled() ? checkIcon : "",
                        onClick: () => {
                            if (map.doubleClickZoom.isEnabled()) {
                                map.doubleClickZoom.disable()
                            } else {
                                map.doubleClickZoom.enable()
                            }
                        }
                    },
                    {
                        label: 'Allow map rotation',
                        icon: map.dragRotate.isEnabled() ? checkIcon : "",
                        onClick: () => {
                            if (map.dragRotate.isEnabled()) {
                                map.dragRotate.disable();
                            } else {
                                map.dragRotate.enable();
                            }
                        }
                    },
                    {
                        label: 'Allow map pitch',
                        icon: map.getMaxPitch() != 0 ? checkIcon : "",
                        onClick: () => {
                            if (map.getMaxPitch() == 0) {
                                map.setMaxPitch(85)
                            } else {
                                map.setMaxPitch(0)
                            }
                        }
                    }
                ]}
        ]
    });
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
显示代码
全屏显示


# Overlay Layer Context Menu

Overlay layer context menu (opens new window)

// Show menu when clicking an overlay. The key can be customized. If the function returns null, the map context menu above is triggered. If the function returns non-null, this menu is shown
map.setMenu(event => {
    let features = map.queryRenderedFeatures(event.point, {
        layers: [polygon.layerId]
    });
    if (features.length == 0) {
        // If the click position has no features from the polygon layer above, return null to trigger the default map menu
        return null;
    }
    // If the click position has features from the polygon layer, show this layer's custom context menu
    return new vjmap.ContextMenu({
        event: event.originalEvent,
        theme: "dark", //light
        width: "250px",
        items: [
            {type: 'custom', markup: `<span style="color: #ffff00; padding-left: 30px">I am the polygon layer context menu</span>`},
            {
                label: 'View selected polygon info',
                onClick: () => {
                    map.logInfo(`Current polygon ID: ${features[0].properties.id}, Current polygon color: ${features[0].properties.color}`);
                }
            },
            {
                label: 'Delete',
                onClick: () => {
                    let data = polygon.getData();
                    data.features = data.features.filter(f => f.id != features[0].properties.id);
                    polygon.setData(data);
                }
            }
        ]
    });
}, "overlay")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
显示代码
全屏显示


# Marker Context Menu

Marker context menu (opens new window)

 // Unlike polygon and other overlay layers, marker is a div while overlay layers are canvas-rendered and share the same div as the map
// So marker right-click must be triggered by its own div's context menu
marker.getElement().addEventListener("mousedown", event => {
    if (event.button != 2) return;// Not right-click
    // Prevent default map context menu
    event.preventDefault();
    event.stopPropagation();
    return new vjmap.ContextMenu({
        event: event,
        theme: "dark", //light
        width: "250px",
        items: [
            {type: 'custom', markup: `<span style="color: #ffff00; padding-left: 30px">I am the Marker context menu</span>`},
            {
                label: 'Get this Marker info',
                onClick: () => {
                    map.logInfo(`Current Marker ID: ${marker.id}`);
                }
            }
        ]
    });
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
显示代码
全屏显示


# Type Definitions

/**
 * Context menu.
 *
 **/
export  class ContextMenu {
    private options;
    private menuControl;
    private position;
    /**
     * Creates a new ContextMenu menu
     * @param {object} opts options which build the menu e.g. position and items
     * @param {number} opts.width sets the width of the menu including children
     * @param {object} opts.event Event object
     * @param {theme} opts.theme Custom theme, supports dark and light, default dark
     * @param {boolean} opts.isSticky sets how the menu appears, follow the mouse or sticky
     * @param {Array<ContextMenuItem>} opts.items sets the default items in the menu
     */
    constructor(opts: ContextMenuOptions);
    /**
     * Adds item to this ContextMenu menu instance
     * @param {ContextMenuItem} item item to add to the ContextMenu menu
     */
    add(item: any): void;
    /**
     * Makes this ContextMenu menu visible
     */
    show(): void;
    /**
     * Hides this ContextMenu menu
     */
    hide(): void;
    /**
     * Toggle visibility of menu
     */
    toggle(): void;
}

/**
 * Context menu options.
 *
 **/
export  interface ContextMenuOptions {
    /** Event object. */
    event: Event;
    /** Menu width (including submenus), pixels, default 150px. */
    width?: string;
    /** Menu theme. (dark and light, default dark)*/
    theme?: string;
    /** Inner width of the menu outer container, used to auto-adjust position when menu exceeds bounds; default is window width. Can be set via map.getContainer().getBoundingClientRect().width */
    innerWidth?: number;
    /** Inner height of the menu outer container, used to auto-adjust position when menu exceeds bounds; default is window height. Can be set via map.getContainer().getBoundingClientRect().height */
    innerHeight?: number;
    /** Menu items */
    items: ContextMenuSubItemOptions[];
}

/**
 * Context menu sub-item options.
 *
 **/
export  interface ContextMenuSubItemOptions {
    /** Submenu type. */
    type?: "custom" | "multi" | "Button" | "seperator" | "submenu" | "hovermenu" | "normal";
    /** Custom HTML content when type is 'custom' */
    markup?: string;
    /** Submenu items when type is 'multi'*/
    items?: ContextMenuSubItemOptions[];
    /** Click handler*/
    onClick?: Function;
    /** Menu label*/
    label?: string;
    /** Shortcut key*/
    shortcut?: string;
    /** Whether enabled*/
    enabled?: boolean;
    /** CSS icon*/
    cssIcon?: string;
    /** Icon*/
    icon?: string;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81