/**
* @desc A customizable HTML context menu.
*
* [
](https://xeokit.github.io/xeokit-sdk/examples/#ContextMenu_Canvas_TreeViewPlugin_Custom)
*
* * [[Run this example](https://xeokit.github.io/xeokit-sdk/examples/#ContextMenu_Canvas_TreeViewPlugin_Custom)]
*
* ## Overview
*
* * A pure JavaScript, lightweight context menu
* * Dynamically configure menu items
* * Dynamically enable or disable items
* * Supports cascading sub-menus
* * Configure custom style with custom CSS (see examples above)
*
* ## Usage
*
* In the example below we'll create a ````ContextMenu```` that pops up whenever we right-click on an {@link Entity} within
* our {@link Scene}.
*
* First, we'll create the ````ContextMenu````, configuring it with a list of menu items.
*
* Each item has:
*
* * a ````title```` for the item,
* * a ````doAction()```` callback to fire when the item's title is clicked, and
* * an optional ````getEnabled()```` callback that indicates if the item should enabled in the menu or not.
*
*
*
* The ````getEnabled()```` callbacks are invoked whenever the menu is shown. When an item's ````getEnabled()```` callback
* returns ````true````, then the item is enabled and clickable. When it returns ````false````, then the item is disabled
* and cannot be clicked. An item without a ````getEnabled()```` callback is always enabled and clickable.
*
* Note how the ````doAction()```` and ````getEnabled()```` callbacks accept a ````context````
* object. That must be set on the ````ContextMenu```` before we're able to we show it. The context object can be anything. In this example,
* we'll use the context object to provide the callbacks with the Entity that we right-clicked.
*
* We'll also initially enable the ````ContextMenu````.
*
* [[Run this example](https://xeokit.github.io/xeokit-sdk/examples/#ContextMenu_Canvas_Custom)]
*
* ````javascript
* const canvasContextMenu = new ContextMenu({
*
* enabled: true,
*
* items: [
* [
* {
* title: "Hide Object",
* getEnabled: (context) => {
* return context.entity.visible; // Can't hide entity if already hidden
* },
* doAction: function (context) {
* context.entity.visible = false;
* }
* }
* ],
* [
* {
* title: "Select Object",
* getEnabled: (context) => {
* return (!context.entity.selected); // Can't select an entity that's already selected
* },
* doAction: function (context) {
* context.entity.selected = true;
* }
* }
* ],
* [
* {
* title: "X-Ray Object",
* getEnabled: (context) => {
* return (!context.entity.xrayed); // Can't X-ray an entity that's already X-rayed
* },
* doAction: (context) => {
* context.entity.xrayed = true;
* }
* }
* ]
* ]
* });
* ````
*
* Next, we'll make the ````ContextMenu```` appear whenever we right-click on an Entity. Whenever we right-click
* on the canvas, we'll attempt to pick the Entity at those mouse coordinates. If we succeed, we'll feed the
* Entity into ````ContextMenu```` via the context object, then show the ````ContextMenu````.
*
* From there, each ````ContextMenu```` item's ````getEnabled()```` callback will be invoked (if provided), to determine if the item should
* be enabled. If we click an item, its ````doAction()```` callback will be invoked with our context object.
*
* Remember that we must set the context on our ````ContextMenu```` before we show it, otherwise it will log an error to the console,
* and ignore our attempt to show it.
*
* ````javascript*
* viewer.scene.canvas.canvas.oncontextmenu = (e) => { // Right-clicked on the canvas
*
* if (!objectContextMenu.enabled) {
* return;
* }
*
* var hit = viewer.scene.pick({ // Try to pick an Entity at the coordinates
* canvasPos: [e.pageX, e.pageY]
* });
*
* if (hit) { // Picked an Entity
*
* objectContextMenu.context = { // Feed entity to ContextMenu
* entity: hit.entity
* };
*
* objectContextMenu.show(e.pageX, e.pageY); // Show the ContextMenu
* }
*
* e.preventDefault();
* });
* ````
*
* Note how we only show the ````ContextMenu```` if it's enabled. We can use that mechanism to switch between multiple
* ````ContextMenu```` instances depending on what we clicked.
*
* ## Dynamic Item Titles
*
* To make an item dynamically regenerate its title text whenever we show the ````ContextMenu````, provide its title with a
* ````getTitle()```` callback. The callback will fire each time you show ````ContextMenu````, which will dynamically
* set the item title text.
*
* In the example below, we'll create a simple ````ContextMenu```` that allows us to toggle the selection of an object
* via its first item, which changes text depending on whether we are selecting or deselecting the object.
*
* [[Run an example](https://xeokit.github.io/xeokit-sdk/examples/#ContextMenu_dynamicItemTitles)]
*
* ````javascript
* const canvasContextMenu = new ContextMenu({
*
* enabled: true,
*
* items: [
* [
* {
* getTitle: (context) => {
* return (!context.entity.selected) ? "Select" : "Undo Select";
* },
* doAction: function (context) {
* context.entity.selected = !context.entity.selected;
* }
* },
* {
* title: "Clear Selection",
* getEnabled: function (context) {
* return (context.viewer.scene.numSelectedObjects > 0);
* },
* doAction: function (context) {
* context.viewer.scene.setObjectsSelected(context.viewer.scene.selectedObjectIds, false);
* }
* }
* ]
* ]
* });
* ````
*
* ## Sub-menus
*
* Each menu item can optionally have a sub-menu, which will appear when we hover over the item.
*
* In the example below, we'll create a much simpler ````ContextMenu```` that has only one item, called "Effects", which
* will open a cascading sub-menu whenever we hover over that item.
*
* Note that our "Effects" item has no ````doAction```` callback, because an item with a sub-menu performs no
* action of its own.
*
* [[Run this example](https://xeokit.github.io/xeokit-sdk/examples/#ContextMenu_subMenus)]
*
* ````javascript
* const canvasContextMenu = new ContextMenu({
* items: [ // Top level items
* [
* {
* getTitle: (context) => {
* return "Effects";
* },
*
* items: [ // Sub-menu
* [
* {
* getTitle: (context) => {
* return (!context.entity.visible) ? "Show" : "Hide";
* },
* doAction: function (context) {
* context.entity.visible = !context.entity.visible;
* }
* },
* {
* getTitle: (context) => {
* return (!context.entity.selected) ? "Select" : "Undo Select";
* },
* doAction: function (context) {
* context.entity.selected = !context.entity.selected;
* }
* },
* {
* getTitle: (context) => {
* return (!context.entity.highlighted) ? "Highlight" : "Undo Highlight";
* },
* doAction: function (context) {
* context.entity.highlighted = !context.entity.highlighted;
* }
* }
* ]
* ]
* }
* ]
* ]
* });
* ````
*/
export class ContextMenu {
/**
* Creates a ````ContextMenu````.
*
* The ````ContextMenu```` will be initially hidden.
*
* @param {Object} [cfg] ````ContextMenu```` configuration.
* @param {Object} [cfg.items] The context menu items. These can also be dynamically set on {@link ContextMenu#items}. See the class documentation for an example.
* @param {Object} [cfg.context] The context, which is passed into the item callbacks. This can also be dynamically set on {@link ContextMenu#context}. This must be set before calling {@link ContextMenu#show}.
* @param {Boolean} [cfg.enabled=true] Whether this ````ContextMenu```` is initially enabled. {@link ContextMenu#show} does nothing while this is ````false````.
* @param {Boolean} [cfg.hideOnMouseDown=true] Whether this ````ContextMenu```` automatically hides whenever we mouse-down or tap anywhere in the page.
*/
constructor(cfg?: {
items?: any;
context?: any;
enabled?: boolean;
hideOnMouseDown?: boolean;
});
_id: any;
_context: any;
_enabled: boolean;
_itemsCfg: any[];
_rootMenu: Menu;
_menuList: any[];
_menuMap: {};
_itemList: any[];
_itemMap: {};
_shown: boolean;
_nextId: number;
/**
* Subscriptions to events fired at this ContextMenu.
* @private
*/
private _eventSubs;
/**
* Sets the ````ContextMenu```` items.
*
* These can be updated dynamically at any time.
*
* See class documentation for an example.
*
* @type {Object[]}
*/
set items(arg: any[]);
/**
* Gets the ````ContextMenu```` items.
*
* @type {Object[]}
*/
get items(): any[];
/**
* Sets the ````ContextMenu```` context.
*
* The context can be any object that you need to be provides to the callbacks configured on {@link ContextMenu#items}.
*
* This must be set before calling {@link ContextMenu#show}.
*
* @type {Object}
*/
set context(arg: any);
/**
* Gets the ````ContextMenu```` context.
*
* @type {Object}
*/
get context(): any;
/**
* Sets whether this ````ContextMenu```` is enabled.
*
* Hides the menu when disabling.
*
* @type {Boolean}
*/
set enabled(arg: boolean);
/**
* Gets whether this ````ContextMenu```` is enabled.
*
* {@link ContextMenu#show} does nothing while this is ````false````.
*
* @type {Boolean}
*/
get enabled(): boolean;
/**
Subscribes to an event fired at this ````ContextMenu````.
@param {String} event The event
@param {Function} callback Callback fired on the event
*/
on(event: string, callback: Function): void;
/**
Fires an event at this ````ContextMenu````.
@param {String} event The event type name
@param {Object} value The event parameters
*/
fire(event: string, value: any): void;
/**
* Shows this ````ContextMenu```` at the given page coordinates.
*
* Does nothing when {@link ContextMenu#enabled} is ````false````.
*
* Logs error to console and does nothing if {@link ContextMenu#context} has not been set.
*
* Fires a "shown" event when shown.
*
* @param {Number} pageX Page X-coordinate.
* @param {Number} pageY Page Y-coordinate.
*/
show(pageX: number, pageY: number): void;
/**
* Gets whether this ````ContextMenu```` is currently shown or not.
*
* @returns {Boolean} Whether this ````ContextMenu```` is shown.
*/
get shown(): boolean;
/**
* Hides this ````ContextMenu````.
*
* Fires a "hidden" event when hidden.
*/
hide(): void;
/**
* Destroys this ````ContextMenu````.
*/
destroy(): void;
_clear(): void;
_parseItems(itemsCfg: any): void;
_getNextId(): string;
_createUI(): void;
_createMenuUI(menu: any): void;
_updateItemsTitles(): void;
_updateItemsEnabledStatus(): void;
_showMenu(menuId: any, pageX: any, pageY: any): void;
_hideMenu(menuId: any): void;
_hideAllMenus(): void;
_showMenuElement(menuElement: any, pageX: any, pageY: any): void;
_hideMenuElement(menuElement: any): void;
}
/**
* Internal data class that represents the state of a menu or a submenu.
* @private
*/
declare class Menu {
constructor(id: any);
id: any;
parentItem: any;
groups: any[];
menuElement: any;
shown: boolean;
mouseOver: number;
}
export {};