diff --git a/tests/test_dom_patchers.mjs b/tests/test_dom_patchers.mjs index c9a5538..d0a426a 100644 --- a/tests/test_dom_patchers.mjs +++ b/tests/test_dom_patchers.mjs @@ -93,6 +93,37 @@ test('shared DOM helpers synchronize attributes, text, and one-time listeners', assert.equal(clicks, 1); }); +test('sidebar active helper recognizes supported state attributes and classes', () => { + assert.equal(typeof domPatchUtils.isSidebarButtonActive, 'function'); + + const window = new Window(); + const selectors = [ + '[aria-pressed="true"]', + '[aria-selected="true"]', + '[data-active="true"]', + '[data-selected="true"]', + '.active', + '.is-active', + '.selected', + '.p-highlight', + ]; + + for (const selector of selectors) { + const button = window.document.createElement('button'); + if (selector.startsWith('[')) { + const [attribute, value] = selector.slice(1, -1).split('='); + button.setAttribute(attribute, value.replaceAll('"', '')); + } else { + button.className = selector.slice(1); + } + assert.equal(domPatchUtils.isSidebarButtonActive(button), true, selector); + } + + const inactiveButton = window.document.createElement('button'); + assert.equal(domPatchUtils.isSidebarButtonActive(inactiveButton), false); + assert.equal(domPatchUtils.isSidebarButtonActive(null), false); +}); + test('instant actions handle pointer and click as one idempotent action', () => { assert.equal(typeof domPatchUtils.bindInstantAction, 'function'); diff --git a/web/resolver/model_resolver.js b/web/resolver/model_resolver.js index 822a4df..53d0599 100644 --- a/web/resolver/model_resolver.js +++ b/web/resolver/model_resolver.js @@ -4,6 +4,7 @@ import { $el } from "../../../../scripts/ui.js"; import { createModuleLogger } from "../log_system/log_funcs.js"; import { logger as frontendLogger } from "../log_system/logger.js"; import { loadStylesWhenNeeded } from "../utils/css_loader.js"; +import { isSidebarButtonActive } from "../utils/dom_patch_utils.js"; import { ResolverManagerDialog } from "./resolver_dialog.js"; import { showNotification } from "../utils/notification_utils.js"; import { @@ -763,26 +764,13 @@ export class ModelResolver { event.stopImmediatePropagation?.(); requestAnimationFrame(() => { - if (this.isSidebarButtonActive(button)) { + if (isSidebarButtonActive(button)) { button.click(); } }); } } - isSidebarButtonActive(button) { - return button.matches([ - '[aria-pressed="true"]', - '[aria-selected="true"]', - '[data-active="true"]', - '[data-selected="true"]', - '.active', - '.is-active', - '.selected', - '.p-highlight' - ].join(',')); - } - renderSidebarPanel(element) { element.style.height = "100%"; element.classList.add("mr-sidebar-dock-panel"); diff --git a/web/resolver/shell/dialog_shell_methods.js b/web/resolver/shell/dialog_shell_methods.js index 7cb3325..e167a92 100644 --- a/web/resolver/shell/dialog_shell_methods.js +++ b/web/resolver/shell/dialog_shell_methods.js @@ -1,6 +1,7 @@ import { app } from "../../../../../scripts/app.js"; import { $el } from "../../../../../scripts/ui.js"; import { getSvgIcon } from "../../utils/icon_utils.js"; +import { isSidebarButtonActive } from "../utils/dom_patch_utils.js"; import { safeStorage } from "../utils/html_utils.js"; export const dialogShellMethods = { createHeader() { @@ -555,16 +556,7 @@ export const dialogShellMethods = { const button = document.querySelector(`.${this.sidebarTabId}-tab-button`); if (button instanceof HTMLElement) { - const isActive = button.matches([ - '[aria-pressed="true"]', - '[aria-selected="true"]', - '[data-active="true"]', - '[data-selected="true"]', - '.active', - '.is-active', - '.selected', - '.p-highlight' - ].join(',')); + const isActive = isSidebarButtonActive(button); if (!isActive) { button.click(); @@ -670,16 +662,7 @@ export const dialogShellMethods = { const button = document.querySelector(`.${this.sidebarTabId}-tab-button`); if (!(button instanceof HTMLElement)) return; - const isActive = button.matches([ - '[aria-pressed="true"]', - '[aria-selected="true"]', - '[data-active="true"]', - '[data-selected="true"]', - '.active', - '.is-active', - '.selected', - '.p-highlight' - ].join(',')); + const isActive = isSidebarButtonActive(button); if (!closedByState || isActive) { button.click(); diff --git a/web/resolver/utils/dom_patch_utils.js b/web/resolver/utils/dom_patch_utils.js index e12048c..3e2c87f 100644 --- a/web/resolver/utils/dom_patch_utils.js +++ b/web/resolver/utils/dom_patch_utils.js @@ -1,4 +1,14 @@ const boundEventKeys = new WeakMap(); +const SIDEBAR_ACTIVE_SELECTOR = [ + '[aria-pressed="true"]', + '[aria-selected="true"]', + '[data-active="true"]', + '[data-selected="true"]', + '.active', + '.is-active', + '.selected', + '.p-highlight' +].join(','); export function syncElementAttributes(current, next) { if (!current || !next) return current; @@ -27,6 +37,10 @@ export function setTextIfChanged(element, value) { return true; } +export function isSidebarButtonActive(element) { + return Boolean(element?.matches?.(SIDEBAR_ACTIVE_SELECTOR)); +} + export function bindEventOnce(element, eventName, handler, key = eventName) { if (!element?.addEventListener || !eventName || typeof handler !== 'function') return false;