mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-12 17:45:49 +08:00
TypeScriptify ui/SoftContextMenu.js
Bug: 1011811 Change-Id: Ib670d2715bae6213f1047eba401c2e15a2119a0e Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2550061 Commit-Queue: Jack Franklin <jacktfranklin@chromium.org> Reviewed-by: Tim van der Lippe <tvanderlippe@chromium.org>
This commit is contained in:
committed by
Commit Bot
parent
2477097c78
commit
67d6b84d9a
+141
-55
@@ -28,9 +28,6 @@
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
// @ts-nocheck
|
||||
// TODO(crbug.com/1011811): Enable TypeScript compiler checks
|
||||
|
||||
import * as Host from '../host/host.js';
|
||||
import * as ThemeSupport from '../theme_support/theme_support.js';
|
||||
|
||||
@@ -54,6 +51,11 @@ export class SoftContextMenu {
|
||||
this._parentMenu = parentMenu;
|
||||
/** @type {?HTMLElement} */
|
||||
this._highlightedMenuItemElement = null;
|
||||
|
||||
/**
|
||||
* @type {!WeakMap<!HTMLElement, !ElementMenuDetails>}
|
||||
*/
|
||||
this.detailsForElementMap = new WeakMap();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -154,7 +156,7 @@ export class SoftContextMenu {
|
||||
return this._createSubMenu(item);
|
||||
}
|
||||
|
||||
const menuItemElement = document.createElement('div');
|
||||
const menuItemElement = /** @type {!HTMLElement} */ (document.createElement('div'));
|
||||
menuItemElement.classList.add('soft-context-menu-item');
|
||||
menuItemElement.tabIndex = -1;
|
||||
ARIAUtils.markAsMenuItem(menuItemElement);
|
||||
@@ -163,11 +165,19 @@ export class SoftContextMenu {
|
||||
if (!item.checked) {
|
||||
checkMarkElement.style.opacity = '0';
|
||||
}
|
||||
/** @type {!ElementMenuDetails} */
|
||||
const detailsForElement = {
|
||||
actionId: undefined,
|
||||
isSeparator: undefined,
|
||||
customElement: undefined,
|
||||
subItems: undefined,
|
||||
subMenuTimer: undefined,
|
||||
};
|
||||
|
||||
if (item.element) {
|
||||
const wrapper = menuItemElement.createChild('div', 'soft-context-menu-custom-item');
|
||||
wrapper.appendChild(item.element);
|
||||
menuItemElement._customElement = item.element;
|
||||
detailsForElement.customElement = /** @type {!HTMLElement} */ (item.element);
|
||||
return menuItemElement;
|
||||
}
|
||||
|
||||
@@ -175,7 +185,7 @@ export class SoftContextMenu {
|
||||
menuItemElement.classList.add('soft-context-menu-disabled');
|
||||
}
|
||||
createTextChild(menuItemElement, item.label || '');
|
||||
menuItemElement.createChild('span', 'soft-context-menu-shortcut').textContent = item.shortcut;
|
||||
menuItemElement.createChild('span', 'soft-context-menu-shortcut').textContent = item.shortcut || '';
|
||||
|
||||
menuItemElement.addEventListener('mousedown', this._menuItemMouseDown.bind(this), false);
|
||||
menuItemElement.addEventListener('mouseup', this._menuItemMouseUp.bind(this), false);
|
||||
@@ -185,7 +195,7 @@ export class SoftContextMenu {
|
||||
menuItemElement.addEventListener(
|
||||
'mouseleave', /** @type {!EventListener} */ (this._menuItemMouseLeave.bind(this)), false);
|
||||
|
||||
menuItemElement._actionId = item.id;
|
||||
detailsForElement.actionId = item.id;
|
||||
|
||||
let accessibleName = item.label || '';
|
||||
|
||||
@@ -201,6 +211,7 @@ export class SoftContextMenu {
|
||||
}
|
||||
ARIAUtils.setAccessibleName(menuItemElement, accessibleName);
|
||||
|
||||
this.detailsForElementMap.set(menuItemElement, detailsForElement);
|
||||
return menuItemElement;
|
||||
}
|
||||
|
||||
@@ -208,11 +219,17 @@ export class SoftContextMenu {
|
||||
* @param {!SoftContextMenuDescriptor} item
|
||||
*/
|
||||
_createSubMenu(item) {
|
||||
const menuItemElement = document.createElement('div');
|
||||
const menuItemElement = /** @type {!HTMLElement} */ (document.createElement('div'));
|
||||
menuItemElement.classList.add('soft-context-menu-item');
|
||||
menuItemElement._subItems = item.subItems;
|
||||
menuItemElement.tabIndex = -1;
|
||||
ARIAUtils.markAsMenuItemSubMenu(menuItemElement);
|
||||
this.detailsForElementMap.set(menuItemElement, {
|
||||
subItems: item.subItems,
|
||||
actionId: undefined,
|
||||
isSeparator: undefined,
|
||||
customElement: undefined,
|
||||
subMenuTimer: undefined,
|
||||
});
|
||||
|
||||
// Occupy the same space on the left in all items.
|
||||
const checkMarkElement = Icon.create('smallicon-checkmark', 'soft-context-menu-item-checkmark');
|
||||
@@ -245,9 +262,15 @@ export class SoftContextMenu {
|
||||
}
|
||||
|
||||
_createSeparator() {
|
||||
const separatorElement = document.createElement('div');
|
||||
const separatorElement = /** @type {!HTMLElement} */ (document.createElement('div'));
|
||||
separatorElement.classList.add('soft-context-menu-separator');
|
||||
separatorElement._isSeparator = true;
|
||||
this.detailsForElementMap.set(separatorElement, {
|
||||
subItems: undefined,
|
||||
actionId: undefined,
|
||||
isSeparator: true,
|
||||
customElement: undefined,
|
||||
subMenuTimer: undefined,
|
||||
});
|
||||
separatorElement.createChild('div', 'separator-line');
|
||||
return separatorElement;
|
||||
}
|
||||
@@ -284,14 +307,17 @@ export class SoftContextMenu {
|
||||
* @param {!Event} event
|
||||
*/
|
||||
_triggerAction(menuItemElement, event) {
|
||||
if (!menuItemElement._subItems) {
|
||||
this._root().discard();
|
||||
event.consume(true);
|
||||
if (typeof menuItemElement._actionId !== 'undefined') {
|
||||
this._itemSelectedCallback(menuItemElement._actionId);
|
||||
delete menuItemElement._actionId;
|
||||
const detailsForElement = this.detailsForElementMap.get(menuItemElement);
|
||||
if (detailsForElement) {
|
||||
if (!detailsForElement.subItems) {
|
||||
this._root().discard();
|
||||
event.consume(true);
|
||||
if (typeof detailsForElement.actionId !== 'undefined') {
|
||||
this._itemSelectedCallback(detailsForElement.actionId);
|
||||
delete detailsForElement.actionId;
|
||||
}
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this._showSubMenu(menuItemElement);
|
||||
@@ -302,9 +328,13 @@ export class SoftContextMenu {
|
||||
* @param {!HTMLElement} menuItemElement
|
||||
*/
|
||||
_showSubMenu(menuItemElement) {
|
||||
if (menuItemElement._subMenuTimer) {
|
||||
clearTimeout(menuItemElement._subMenuTimer);
|
||||
delete menuItemElement._subMenuTimer;
|
||||
const detailsForElement = this.detailsForElementMap.get(menuItemElement);
|
||||
if (!detailsForElement) {
|
||||
return;
|
||||
}
|
||||
if (detailsForElement.subMenuTimer) {
|
||||
window.clearTimeout(detailsForElement.subMenuTimer);
|
||||
delete detailsForElement.subMenuTimer;
|
||||
}
|
||||
if (this._subMenu || !this._document) {
|
||||
return;
|
||||
@@ -312,7 +342,10 @@ export class SoftContextMenu {
|
||||
|
||||
this._activeSubMenuElement = menuItemElement;
|
||||
ARIAUtils.setExpanded(menuItemElement, true);
|
||||
this._subMenu = new SoftContextMenu(menuItemElement._subItems, this._itemSelectedCallback, this);
|
||||
if (!detailsForElement.subItems) {
|
||||
return;
|
||||
}
|
||||
this._subMenu = new SoftContextMenu(detailsForElement.subItems, this._itemSelectedCallback, this);
|
||||
const anchorBox = menuItemElement.boxInWindow();
|
||||
// Adjust for padding.
|
||||
anchorBox.y -= 5;
|
||||
@@ -357,58 +390,101 @@ export class SoftContextMenu {
|
||||
this._subMenu.discard();
|
||||
}
|
||||
if (this._highlightedMenuItemElement) {
|
||||
const detailsForElement = this.detailsForElementMap.get(this._highlightedMenuItemElement);
|
||||
this._highlightedMenuItemElement.classList.remove('force-white-icons');
|
||||
this._highlightedMenuItemElement.classList.remove('soft-context-menu-item-mouse-over');
|
||||
if (this._highlightedMenuItemElement._subItems && this._highlightedMenuItemElement._subMenuTimer) {
|
||||
clearTimeout(this._highlightedMenuItemElement._subMenuTimer);
|
||||
delete this._highlightedMenuItemElement._subMenuTimer;
|
||||
if (!detailsForElement) {
|
||||
return;
|
||||
}
|
||||
if (detailsForElement.subItems && detailsForElement.subMenuTimer) {
|
||||
window.clearTimeout(detailsForElement.subMenuTimer);
|
||||
delete detailsForElement.subMenuTimer;
|
||||
}
|
||||
}
|
||||
|
||||
this._highlightedMenuItemElement = menuItemElement;
|
||||
if (this._highlightedMenuItemElement) {
|
||||
if (ThemeSupport.ThemeSupport.instance().hasTheme() || Host.Platform.isMac()) {
|
||||
this._highlightedMenuItemElement.classList.add('force-white-icons');
|
||||
}
|
||||
this._highlightedMenuItemElement.classList.add('soft-context-menu-item-mouse-over');
|
||||
if (this._highlightedMenuItemElement._customElement) {
|
||||
this._highlightedMenuItemElement._customElement.focus();
|
||||
const detailsForElement = this.detailsForElementMap.get(this._highlightedMenuItemElement);
|
||||
if (!detailsForElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (detailsForElement.customElement) {
|
||||
detailsForElement.customElement.focus();
|
||||
} else {
|
||||
this._highlightedMenuItemElement.focus();
|
||||
}
|
||||
if (scheduleSubMenu && this._highlightedMenuItemElement._subItems &&
|
||||
!this._highlightedMenuItemElement._subMenuTimer) {
|
||||
this._highlightedMenuItemElement._subMenuTimer =
|
||||
setTimeout(this._showSubMenu.bind(this, this._highlightedMenuItemElement), 150);
|
||||
if (scheduleSubMenu && detailsForElement.subItems && !detailsForElement.subMenuTimer) {
|
||||
detailsForElement.subMenuTimer =
|
||||
window.setTimeout(this._showSubMenu.bind(this, this._highlightedMenuItemElement), 150);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_highlightPrevious() {
|
||||
let menuItemElement = this._highlightedMenuItemElement ? this._highlightedMenuItemElement.previousSibling :
|
||||
this._contextMenuElement.lastChild;
|
||||
while (menuItemElement &&
|
||||
(menuItemElement._isSeparator || menuItemElement.classList.contains('soft-context-menu-disabled'))) {
|
||||
let menuItemElement = this._highlightedMenuItemElement ?
|
||||
this._highlightedMenuItemElement.previousSibling :
|
||||
this._contextMenuElement ? this._contextMenuElement.lastChild : null;
|
||||
let menuItemDetails =
|
||||
menuItemElement ? this.detailsForElementMap.get(/** @type {!HTMLElement} */ (menuItemElement)) : undefined;
|
||||
while (menuItemElement && menuItemDetails &&
|
||||
(menuItemDetails.isSeparator ||
|
||||
/** @type {!HTMLElement} */ (menuItemElement).classList.contains('soft-context-menu-disabled'))) {
|
||||
menuItemElement = menuItemElement.previousSibling;
|
||||
menuItemDetails =
|
||||
menuItemElement ? this.detailsForElementMap.get(/** @type {!HTMLElement} */ (menuItemElement)) : undefined;
|
||||
}
|
||||
if (menuItemElement) {
|
||||
this._highlightMenuItem(menuItemElement, false);
|
||||
this._highlightMenuItem(/** @type {!HTMLElement} */ (menuItemElement), false);
|
||||
}
|
||||
}
|
||||
|
||||
_highlightNext() {
|
||||
let menuItemElement = this._highlightedMenuItemElement ? this._highlightedMenuItemElement.nextSibling :
|
||||
this._contextMenuElement.firstChild;
|
||||
let menuItemElement = this._highlightedMenuItemElement ?
|
||||
this._highlightedMenuItemElement.nextSibling :
|
||||
this._contextMenuElement ? this._contextMenuElement.firstChild : null;
|
||||
let menuItemDetails =
|
||||
menuItemElement ? this.detailsForElementMap.get(/** @type {!HTMLElement} */ (menuItemElement)) : undefined;
|
||||
while (menuItemElement &&
|
||||
(menuItemElement._isSeparator || menuItemElement.classList.contains('soft-context-menu-disabled'))) {
|
||||
(menuItemDetails && menuItemDetails.isSeparator ||
|
||||
/** @type {!HTMLElement} */ (menuItemElement).classList.contains('soft-context-menu-disabled'))) {
|
||||
menuItemElement = menuItemElement.nextSibling;
|
||||
menuItemDetails =
|
||||
menuItemElement ? this.detailsForElementMap.get(/** @type {!HTMLElement} */ (menuItemElement)) : undefined;
|
||||
}
|
||||
if (menuItemElement) {
|
||||
this._highlightMenuItem(menuItemElement, false);
|
||||
this._highlightMenuItem(/** @type {!HTMLElement} */ (menuItemElement), false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {!Event} event
|
||||
*/
|
||||
_menuKeyDown(event) {
|
||||
switch (event.key) {
|
||||
const keyboardEvent = /** @type {!KeyboardEvent} */ (event);
|
||||
/**
|
||||
* @this {!SoftContextMenu}
|
||||
*/
|
||||
function onEnterOrSpace() {
|
||||
if (!this._highlightedMenuItemElement) {
|
||||
return;
|
||||
}
|
||||
const detailsForElement = this.detailsForElementMap.get(this._highlightedMenuItemElement);
|
||||
if (!detailsForElement || detailsForElement.customElement) {
|
||||
return;
|
||||
}
|
||||
this._triggerAction(this._highlightedMenuItemElement, keyboardEvent);
|
||||
if (detailsForElement.subItems && this._subMenu) {
|
||||
this._subMenu._highlightNext();
|
||||
}
|
||||
}
|
||||
|
||||
switch (keyboardEvent.key) {
|
||||
case 'ArrowUp':
|
||||
this._highlightPrevious();
|
||||
break;
|
||||
@@ -421,34 +497,32 @@ export class SoftContextMenu {
|
||||
this.discard();
|
||||
}
|
||||
break;
|
||||
case 'ArrowRight':
|
||||
case 'ArrowRight': {
|
||||
if (!this._highlightedMenuItemElement) {
|
||||
break;
|
||||
}
|
||||
if (this._highlightedMenuItemElement._subItems) {
|
||||
const detailsForElement = this.detailsForElementMap.get(this._highlightedMenuItemElement);
|
||||
if (detailsForElement && detailsForElement.subItems) {
|
||||
this._showSubMenu(this._highlightedMenuItemElement);
|
||||
this._subMenu._highlightNext();
|
||||
if (this._subMenu) {
|
||||
this._subMenu._highlightNext();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'Escape':
|
||||
this.discard();
|
||||
break;
|
||||
case 'Enter':
|
||||
if (!isEnterKey(event)) {
|
||||
if (!isEnterKey(keyboardEvent)) {
|
||||
return;
|
||||
}
|
||||
// Fall through
|
||||
case ' ': // Space
|
||||
if (!this._highlightedMenuItemElement || this._highlightedMenuItemElement._customElement) {
|
||||
return;
|
||||
}
|
||||
this._triggerAction(this._highlightedMenuItemElement, event);
|
||||
if (this._highlightedMenuItemElement._subItems && this._subMenu) {
|
||||
this._subMenu._highlightNext();
|
||||
}
|
||||
onEnterOrSpace.call(this);
|
||||
break;
|
||||
case ' ':
|
||||
onEnterOrSpace.call(this);
|
||||
}
|
||||
event.consume(true);
|
||||
keyboardEvent.consume(true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -465,3 +539,15 @@ export class SoftContextMenu {
|
||||
}} */
|
||||
// @ts-ignore typedef
|
||||
export let SoftContextMenuDescriptor;
|
||||
|
||||
/** @typedef
|
||||
{{
|
||||
customElement: (HTMLElement|undefined),
|
||||
isSeparator: (boolean|undefined),
|
||||
subMenuTimer: (number|undefined),
|
||||
subItems: (!Array.<!SoftContextMenuDescriptor>|undefined),
|
||||
actionId: (number|undefined),
|
||||
}} */
|
||||
// @ts-ignore typedef
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
let ElementMenuDetails;
|
||||
|
||||
Reference in New Issue
Block a user