summaryrefslogtreecommitdiff
path: root/ext/js/dom/popup-menu.js
diff options
context:
space:
mode:
authortoasted-nutbread <toasted-nutbread@users.noreply.github.com>2023-11-27 12:48:14 -0500
committertoasted-nutbread <toasted-nutbread@users.noreply.github.com>2023-11-27 12:48:14 -0500
commit4da4827bcbcdd1ef163f635d9b29416ff272b0bb (patch)
treea8a0f1a8befdb78a554e1be91f2c6059ca3ad5f9 /ext/js/dom/popup-menu.js
parentfd6bba8a2a869eaf2b2c1fa49001f933fce3c618 (diff)
Add JSDoc type annotations to project (rebased)
Diffstat (limited to 'ext/js/dom/popup-menu.js')
-rw-r--r--ext/js/dom/popup-menu.js71
1 files changed, 63 insertions, 8 deletions
diff --git a/ext/js/dom/popup-menu.js b/ext/js/dom/popup-menu.js
index 7cae1dff..78394c93 100644
--- a/ext/js/dom/popup-menu.js
+++ b/ext/js/dom/popup-menu.js
@@ -18,38 +18,58 @@
import {EventDispatcher, EventListenerCollection} from '../core.js';
+/**
+ * @augments EventDispatcher<import('popup-menu').EventType>
+ */
export class PopupMenu extends EventDispatcher {
+ /**
+ * @param {HTMLElement} sourceElement
+ * @param {HTMLElement} containerNode
+ */
constructor(sourceElement, containerNode) {
super();
+ /** @type {HTMLElement} */
this._sourceElement = sourceElement;
+ /** @type {HTMLElement} */
this._containerNode = containerNode;
- this._node = containerNode.querySelector('.popup-menu');
- this._bodyNode = containerNode.querySelector('.popup-menu-body');
+ /** @type {HTMLElement} */
+ this._node = /** @type {HTMLElement} */ (containerNode.querySelector('.popup-menu'));
+ /** @type {HTMLElement} */
+ this._bodyNode = /** @type {HTMLElement} */ (containerNode.querySelector('.popup-menu-body'));
+ /** @type {boolean} */
this._isClosed = false;
+ /** @type {EventListenerCollection} */
this._eventListeners = new EventListenerCollection();
+ /** @type {EventListenerCollection} */
this._itemEventListeners = new EventListenerCollection();
}
+ /** @type {HTMLElement} */
get sourceElement() {
return this._sourceElement;
}
+ /** @type {HTMLElement} */
get containerNode() {
return this._containerNode;
}
+ /** @type {HTMLElement} */
get node() {
return this._node;
}
+ /** @type {HTMLElement} */
get bodyNode() {
return this._bodyNode;
}
+ /** @type {boolean} */
get isClosed() {
return this._isClosed;
}
+ /** */
prepare() {
this._setPosition();
this._containerNode.focus();
@@ -61,17 +81,25 @@ export class PopupMenu extends EventDispatcher {
PopupMenu.openMenus.add(this);
+ /** @type {import('popup-menu').MenuOpenEventDetails} */
+ const detail = {menu: this};
+
this._sourceElement.dispatchEvent(new CustomEvent('menuOpen', {
bubbles: false,
cancelable: false,
- detail: {menu: this}
+ detail
}));
}
+ /**
+ * @param {boolean} [cancelable]
+ * @returns {boolean}
+ */
close(cancelable=true) {
- return this._close(null, 'close', cancelable, {});
+ return this._close(null, 'close', cancelable, null);
}
+ /** */
updateMenuItems() {
this._itemEventListeners.removeAllEventListeners();
const items = this._bodyNode.querySelectorAll('.popup-menu-item');
@@ -81,12 +109,16 @@ export class PopupMenu extends EventDispatcher {
}
}
+ /** */
updatePosition() {
this._setPosition();
}
// Private
+ /**
+ * @param {MouseEvent} e
+ */
_onMenuContainerClick(e) {
if (e.currentTarget !== e.target) { return; }
if (this._close(null, 'outside', true, e)) {
@@ -95,8 +127,11 @@ export class PopupMenu extends EventDispatcher {
}
}
+ /**
+ * @param {MouseEvent} e
+ */
_onMenuItemClick(e) {
- const item = e.currentTarget;
+ const item = /** @type {HTMLButtonElement} */ (e.currentTarget);
if (item.disabled) { return; }
if (this._close(item, 'item', true, e)) {
e.stopPropagation();
@@ -104,10 +139,12 @@ export class PopupMenu extends EventDispatcher {
}
}
+ /** */
_onWindowResize() {
- this._close(null, 'resize', true, {});
+ this._close(null, 'resize', true, null);
}
+ /** */
_setPosition() {
// Get flags
let horizontal = 1;
@@ -187,11 +224,29 @@ export class PopupMenu extends EventDispatcher {
menu.style.top = `${y}px`;
}
+ /**
+ * @param {?HTMLElement} item
+ * @param {import('popup-menu').CloseReason} cause
+ * @param {boolean} cancelable
+ * @param {?MouseEvent} originalEvent
+ * @returns {boolean}
+ */
_close(item, cause, cancelable, originalEvent) {
if (this._isClosed) { return true; }
- const action = (item !== null ? item.dataset.menuAction : null);
+ /** @type {?string} */
+ let action = null;
+ if (item !== null) {
+ const {menuAction} = item.dataset;
+ if (typeof menuAction === 'string') { action = menuAction; }
+ }
+
+ const {altKey, ctrlKey, metaKey, shiftKey} = (
+ originalEvent !== null ?
+ originalEvent :
+ {altKey: false, ctrlKey: false, metaKey: false, shiftKey: false}
+ );
- const {altKey=false, ctrlKey=false, metaKey=false, shiftKey=false} = originalEvent;
+ /** @type {import('popup-menu').MenuCloseEventDetails} */
const detail = {
menu: this,
item,