PuterJS menubar web component improvements

This commit is contained in:
Miika Kuisma
2026-05-12 12:00:15 +03:00
parent 36c9c478ec
commit e79abd43e3
2 changed files with 33 additions and 14 deletions
@@ -93,14 +93,17 @@ class PuterContextMenu extends PuterWebComponent {
.menu-item:hover:not(.disabled):not(.divider) .check,
.menu-item:hover:not(.disabled):not(.divider) .submenu-arrow,
.menu-item:hover:not(.disabled):not(.divider) .label,
.menu-item:hover:not(.disabled):not(.divider) .shortcut,
.menu-item.focused:not(.disabled):not(.divider) .icon,
.menu-item.focused:not(.disabled):not(.divider) .check,
.menu-item.focused:not(.disabled):not(.divider) .submenu-arrow,
.menu-item.focused:not(.disabled):not(.divider) .label,
.menu-item.focused:not(.disabled):not(.divider) .shortcut,
.menu-item.has-open-submenu .icon,
.menu-item.has-open-submenu .check,
.menu-item.has-open-submenu .submenu-arrow,
.menu-item.has-open-submenu .label {
.menu-item.has-open-submenu .label,
.menu-item.has-open-submenu .shortcut {
color: white;
}
.menu-item:hover:not(.disabled):not(.divider) .icon svg,
@@ -128,6 +131,9 @@ class PuterContextMenu extends PuterWebComponent {
.context-menu.safe-traverse .menu-item:hover:not(.has-open-submenu):not(.focused):not(.disabled):not(.divider) .label {
color: #333;
}
.context-menu.safe-traverse .menu-item:hover:not(.has-open-submenu):not(.focused):not(.disabled):not(.divider) .shortcut {
color: #999;
}
.context-menu.safe-traverse .menu-item:hover:not(.has-open-submenu):not(.focused):not(.disabled):not(.divider) .icon svg {
filter: none;
}
+26 -13
View File
@@ -105,6 +105,9 @@ class PuterMenubar extends PuterWebComponent {
if ( this._keyUpHandler ) {
document.removeEventListener('keyup', this._keyUpHandler, true);
}
if ( this._docPointerDownHandler ) {
document.removeEventListener('pointerdown', this._docPointerDownHandler, true);
}
const buttons = this.$$('.menu-button');
buttons.forEach((btn) => {
@@ -132,19 +135,6 @@ class PuterMenubar extends PuterWebComponent {
this._openDropdown(btn, item);
});
// pointerdown on this button while it owns the open dropdown:
// mark it so the outside-pointerdown close (about to fire) and
// the trailing click don't reopen.
btn.addEventListener('pointerdown', () => {
if ( this.#activeButtonEl === btn ) {
this._suppressClickFor = btn;
clearTimeout(this._suppressClickTimer);
this._suppressClickTimer = setTimeout(() => {
this._suppressClickFor = null;
}, 400);
}
});
// Hover-switch when a dropdown is already open
btn.addEventListener('mouseenter', () => {
if ( this.#activeDropdown && this.#activeButtonEl !== btn ) {
@@ -158,6 +148,26 @@ class PuterMenubar extends PuterWebComponent {
this._keyUpHandler = (e) => this._onGlobalKeyUp(e);
document.addEventListener('keydown', this._keyHandler, true);
document.addEventListener('keyup', this._keyUpHandler, true);
// Capture-phase pointerdown on document. The open context menu also
// listens in capture for outside-pointerdown to close itself; that
// listener registers later (when the dropdown opens) so ours runs
// first. If the press lands on the currently-active button, mark it
// so the trailing click — which would otherwise reopen the just-closed
// dropdown — is treated as a toggle-close. Uses composedPath because
// the button lives inside this shadow root.
this._docPointerDownHandler = (e) => {
if ( ! this.#activeButtonEl ) return;
const path = typeof e.composedPath === 'function' ? e.composedPath() : [];
if ( path.includes(this.#activeButtonEl) ) {
this._suppressClickFor = this.#activeButtonEl;
clearTimeout(this._suppressClickTimer);
this._suppressClickTimer = setTimeout(() => {
this._suppressClickFor = null;
}, 400);
}
};
document.addEventListener('pointerdown', this._docPointerDownHandler, true);
}
_onGlobalKeyDown (e) {
@@ -357,6 +367,9 @@ class PuterMenubar extends PuterWebComponent {
if ( this._keyUpHandler ) {
document.removeEventListener('keyup', this._keyUpHandler, true);
}
if ( this._docPointerDownHandler ) {
document.removeEventListener('pointerdown', this._docPointerDownHandler, true);
}
}
_escapeHTML (str) {