Skip to content
This repository has been archived by the owner on Nov 6, 2019. It is now read-only.

Smarter ContextMenu items: pass contextmenu event to ContextMenu items #355

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions packages/commands/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,72 @@ class CommandRegistry {
return this._keyBindings;
}

get contextEvent(): MouseEvent | null {
return this._contextEvent;
}

set contextEvent(event: MouseEvent | null) {
if (event === null) {
this._contextEvent = null;
this._contextEventCurrentTarget = null;
}
else {
this._contextEvent = event;
// event.currentTarget is nulled when the next event occurs, so record for later
this._contextEventCurrentTarget = event.currentTarget as (Element | null);
}
}

contextEventTarget(selector: string): Element | null {
// Validate the selector
if (selector.indexOf(',') !== -1) {
throw new Error(`Selector cannot contain commas: ${selector}`);
}
if (!Selector.isValid(selector)) {
throw new Error(`Invalid selector: ${selector}`);
}

let event = this._contextEvent;
let currentTarget = this._contextEventCurrentTarget;
if (!event || !currentTarget) {
return null;
}

// Look up the target of the event.
let target = event.target as (Element | null);

// Bail if there is no target.
if (!target) {
return null;
}

// There are some third party libraries that cause the `target` to
// be detached from the DOM before Phosphor can process the event.
if (!currentTarget.contains(target)) {
target = document.elementFromPoint(event.clientX, event.clientY);
if (!target || !currentTarget.contains(target)) {
return null;
}
}

while (target !== null) {
// Return the first Element that matches the selector
if (Selector.matches(target, selector)) {
return target;
}

// Stop searching at the limits of the DOM range.
if (target === currentTarget) {
return null;
}

// Step to the parent DOM level.
target = target.parentElement;
}

return null;
}

/**
* List the ids of the registered commands.
*
Expand Down Expand Up @@ -565,6 +631,8 @@ class CommandRegistry {
private _commandChanged = new Signal<this, CommandRegistry.ICommandChangedArgs>(this);
private _commandExecuted = new Signal<this, CommandRegistry.ICommandExecutedArgs>(this);
private _keyBindingChanged = new Signal<this, CommandRegistry.IKeyBindingChangedArgs>(this);
private _contextEvent: MouseEvent | null = null;
private _contextEventCurrentTarget: Element | null = null;
}


Expand Down
33 changes: 31 additions & 2 deletions packages/widgets/src/contextmenu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,32 @@ import {
Menu
} from './menu';

import {
Message
} from '@phosphor/messaging';

export
class MenuForContextMenu extends Menu {
triggerActiveItem(): void {
this._doCleanupContextEvent = false;
super.triggerActiveItem();
this._cleanupContextEvent();
this._doCleanupContextEvent = true;
}

protected onCloseRequest(msg: Message): void {
super.onCloseRequest(msg);
if (this._doCleanupContextEvent) {
this._cleanupContextEvent();
}
}

private _cleanupContextEvent(): void {
this.commands.contextEvent = null;
}

private _doCleanupContextEvent: boolean = true;
}

/**
* An object which implements a universal context menu.
Expand All @@ -43,13 +69,13 @@ class ContextMenu {
* @param options - The options for initializing the menu.
*/
constructor(options: ContextMenu.IOptions) {
this.menu = new Menu(options);
this.menu = new MenuForContextMenu(options);
}

/**
* The menu widget which displays the matched context items.
*/
readonly menu: Menu;
readonly menu: MenuForContextMenu;

/**
* Add an item to the context menu.
Expand Down Expand Up @@ -101,6 +127,9 @@ class ContextMenu {
return false;
}

// Record the initiating event for use in commands
this.menu.commands.contextEvent = event;

// Add the filtered items to the menu.
each(items, item => { this.menu.addItem(item); });

Expand Down