From 84bd01087c1274959ef9f7c193ea24b8049c0596 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BE=9D=E7=91=AA=E8=B2=93?= Date: Sun, 19 Apr 2026 09:09:31 +0800 Subject: [PATCH] Fix Escape key navigation and dynamic parent targeting in stacked modals --- src/accounting/static/js/description-editor.js | 9 ++++++++- .../js/journal-entry-account-selector.js | 8 ++++++++ .../js/journal-entry-line-item-editor.js | 2 +- src/accounting/static/js/option-form.js | 8 ++++++++ .../static/js/original-line-item-selector.js | 18 +++++++++++++++++- .../include/account-selector-modal.html | 4 ++-- .../include/description-editor-modal.html | 4 ++-- .../original-line-item-selector-modal.html | 4 ++-- .../recurring-account-selector-modal.html | 4 ++-- 9 files changed, 50 insertions(+), 11 deletions(-) diff --git a/src/accounting/static/js/description-editor.js b/src/accounting/static/js/description-editor.js index 176b319..f497cf3 100644 --- a/src/accounting/static/js/description-editor.js +++ b/src/accounting/static/js/description-editor.js @@ -150,13 +150,20 @@ class DescriptionEditor extends BaseTablist { this.tabs = [this.#tabsByID.general, this.#tabsByID.travel, this.#tabsByID.bus, this.#tabsByID.recurring, this.#tabsByID.annotation]; this.currentTab = this.tabs[0]; this.#descriptionInput.onchange = () => this.#onDescriptionChange(); - this.#offsetButton.onclick = () => this.lineItemEditor.originalLineItemSelector.onOpen(); + this.#offsetButton.onclick = () => this.lineItemEditor.originalLineItemSelector.onOpen(this.#modal.id); this.#form.onsubmit = () => { if (this.currentTab.validate()) { this.#submit(); } return false; }; + + const closeButton = document.getElementById(`${prefix}-close`); + this.#modal.onkeydown = (event) => { + if (event.key === "Escape") { + closeButton.click(); + } + }; } /** diff --git a/src/accounting/static/js/journal-entry-account-selector.js b/src/accounting/static/js/journal-entry-account-selector.js index f387733..69cfccb 100644 --- a/src/accounting/static/js/journal-entry-account-selector.js +++ b/src/accounting/static/js/journal-entry-account-selector.js @@ -97,6 +97,14 @@ class JournalEntryAccountSelector extends BaseCombobox { this.#clearButton = document.getElementById(`${prefix}-btn-clear`); this.#clearButton.onclick = () => this.#lineItemEditor.clearAccount(); + + const modal = document.getElementById(`${prefix}-modal`); + const closeButton = document.getElementById(`${prefix}-close`); + modal.onkeydown = (event) => { + if (event.key === "Escape") { + closeButton.click(); + } + }; } /** diff --git a/src/accounting/static/js/journal-entry-line-item-editor.js b/src/accounting/static/js/journal-entry-line-item-editor.js index 7dee8dc..af51773 100644 --- a/src/accounting/static/js/journal-entry-line-item-editor.js +++ b/src/accounting/static/js/journal-entry-line-item-editor.js @@ -228,7 +228,7 @@ class JournalEntryLineItemEditor { this.#accountSelectors = JournalEntryAccountSelector.getInstances(this); this.originalLineItemSelector = new OriginalLineItemSelector(this); - this.#originalLineItemControl.onclick = () => this.originalLineItemSelector.onOpen() + this.#originalLineItemControl.onclick = () => this.originalLineItemSelector.onOpen(this.modal.id) this.#originalLineItemDelete.onclick = () => this.clearOriginalLineItem(); this.#descriptionControl.onclick = () => this.#descriptionEditors[this.debitCredit].onOpen(); this.#accountControl.onclick = () => this.#accountSelectors[this.debitCredit].onOpen(); diff --git a/src/accounting/static/js/option-form.js b/src/accounting/static/js/option-form.js index ef17850..fc8c290 100644 --- a/src/accounting/static/js/option-form.js +++ b/src/accounting/static/js/option-form.js @@ -895,6 +895,14 @@ class RecurringAccountSelector extends BaseCombobox { this.#clearButton = document.getElementById(`${prefix}-clear`); this.#clearButton.onclick = () => this.#editor.clearAccount(); + + const modal = document.getElementById(`${prefix}-modal`); + const closeButton = document.getElementById(`${prefix}-close`); + modal.onkeydown = (event) => { + if (event.key === "Escape") { + closeButton.click(); + } + }; } /** diff --git a/src/accounting/static/js/original-line-item-selector.js b/src/accounting/static/js/original-line-item-selector.js index a31a597..1c5a838 100644 --- a/src/accounting/static/js/original-line-item-selector.js +++ b/src/accounting/static/js/original-line-item-selector.js @@ -65,6 +65,12 @@ class OriginalLineItemSelector extends BaseCombobox { */ #debitCredit; + /** + * The close button. + * @type {HTMLButtonElement} + */ + #closeButton; + /** * Constructs an original line item selector. * @@ -82,6 +88,14 @@ class OriginalLineItemSelector extends BaseCombobox { for (const option of this.options) { this.#optionById[option.id] = option; } + this.#closeButton = document.getElementById(`${prefix}-close`); + + const modal = document.getElementById(`${prefix}-modal`); + modal.onkeydown = (event) => { + if (event.key === "Escape") { + this.#closeButton.click(); + } + }; } /** @@ -162,8 +176,10 @@ class OriginalLineItemSelector extends BaseCombobox { /** * The callback when the original line item selector is shown. * + * @param parentID {string} the ID of the parent element */ - onOpen() { + onOpen(parentID) { + this.#closeButton.dataset.bsTarget = `#${parentID}`; this.#currencyCode = this.#lineItemEditor.currencyCode; this.#debitCredit = this.#lineItemEditor.debitCredit; this.query.value = ""; diff --git a/src/accounting/templates/accounting/journal-entry/include/account-selector-modal.html b/src/accounting/templates/accounting/journal-entry/include/account-selector-modal.html index c4eaa9d..f1d721d 100644 --- a/src/accounting/templates/accounting/journal-entry/include/account-selector-modal.html +++ b/src/accounting/templates/accounting/journal-entry/include/account-selector-modal.html @@ -19,12 +19,12 @@ account-selector-modal.html: The modal for the account selector Author: imacat@mail.imacat.idv.tw (imacat) First written: 2023/2/25 #} -