Introduce a global dialog stack to correctly manage dismissal through the esc key.

This commit is contained in:
2025-08-21 13:21:17 +02:00
parent 52d2fc7d33
commit 55c7a5ef8e
2 changed files with 58 additions and 15 deletions

View File

@@ -1,6 +1,6 @@
{ {
"name": "@tp/tp-dialog", "name": "@tp/tp-dialog",
"version": "1.4.0", "version": "1.5.0",
"description": "", "description": "",
"main": "tp-dialog.js", "main": "tp-dialog.js",
"scripts": { "scripts": {

View File

@@ -9,6 +9,21 @@ import { LitElement, html, css, svg } from 'lit';
import { EventHelpers } from '@tp/helpers/event-helpers.js'; import { EventHelpers } from '@tp/helpers/event-helpers.js';
import { closest } from '@tp/helpers/closest.js'; import { closest } from '@tp/helpers/closest.js';
// Global stack to track opened dialogs with closeOnEsc
const dialogStack = [];
let escKeyListener = null;
// Global escape key handler
function handleGlobalEscKey(event) {
if (event.key === 'Escape' && dialogStack.length > 0) {
// Get the most recently opened dialog
const lastDialog = dialogStack[dialogStack.length - 1];
if (lastDialog && lastDialog.closeOnEsc) {
lastDialog.close();
}
}
}
class TpDialog extends EventHelpers(LitElement) { class TpDialog extends EventHelpers(LitElement) {
static get styles() { static get styles() {
return [ return [
@@ -83,7 +98,6 @@ class TpDialog extends EventHelpers(LitElement) {
constructor() { constructor() {
super(); super();
this._handleEscKey = this._handleEscKey.bind(this);
this._currentPromise = null; this._currentPromise = null;
this._resolvePromise = null; this._resolvePromise = null;
} }
@@ -103,9 +117,8 @@ class TpDialog extends EventHelpers(LitElement) {
this.unlisten(this, 'confirmed', '_handleConfirmed'); this.unlisten(this, 'confirmed', '_handleConfirmed');
this.unlisten(this, 'dismissed', '_handleDismissed'); this.unlisten(this, 'dismissed', '_handleDismissed');
if (this.closeOnEsc) { // Remove this dialog from the stack
document.removeEventListener('keydown', this._handleEscKey); this._removeFromDialogStack();
}
// Clean up promise if dialog is removed while open // Clean up promise if dialog is removed while open
if (this._currentPromise && this._resolvePromise) { if (this._currentPromise && this._resolvePromise) {
@@ -119,9 +132,8 @@ class TpDialog extends EventHelpers(LitElement) {
this.dialog.show(); this.dialog.show();
this.open = true; this.open = true;
if (this.closeOnEsc) { // Add to dialog stack if closeOnEsc is enabled
document.addEventListener('keydown', this._handleEscKey, { once: true }); this._addToDialogStack();
}
// Create and return a new promise // Create and return a new promise
this._currentPromise = new Promise((resolve) => { this._currentPromise = new Promise((resolve) => {
@@ -135,9 +147,8 @@ class TpDialog extends EventHelpers(LitElement) {
this.dialog.showModal(); this.dialog.showModal();
this.open = true; this.open = true;
if (this.closeOnEsc) { // Add to dialog stack if closeOnEsc is enabled
document.addEventListener('keydown', this._handleEscKey, { once: true }); this._addToDialogStack();
}
if (this.closeOnOutsideClick) { if (this.closeOnOutsideClick) {
this.addEventListener('click', this._handleOutsideClick, { once: true }); this.addEventListener('click', this._handleOutsideClick, { once: true });
@@ -161,6 +172,9 @@ class TpDialog extends EventHelpers(LitElement) {
this.dispatchEvent(new CustomEvent('closed', { detail: null, bubbles: true, composed: true })); this.dispatchEvent(new CustomEvent('closed', { detail: null, bubbles: true, composed: true }));
this.open = false; this.open = false;
// Remove from dialog stack
this._removeFromDialogStack();
// If closed without explicit confirm/dismiss (like ESC key), treat as dismissed // If closed without explicit confirm/dismiss (like ESC key), treat as dismissed
if (this._currentPromise && this._resolvePromise) { if (this._currentPromise && this._resolvePromise) {
this._resolvePromise('dismissed'); this._resolvePromise('dismissed');
@@ -185,6 +199,35 @@ class TpDialog extends EventHelpers(LitElement) {
} }
} }
_addToDialogStack() {
if (this.closeOnEsc) {
// Remove if already in stack (shouldn't happen, but just in case)
this._removeFromDialogStack();
// Add to the end of the stack
dialogStack.push(this);
// Set up global listener if this is the first dialog
if (dialogStack.length === 1 && !escKeyListener) {
escKeyListener = handleGlobalEscKey;
document.addEventListener('keydown', escKeyListener);
}
}
}
_removeFromDialogStack() {
const index = dialogStack.indexOf(this);
if (index > -1) {
dialogStack.splice(index, 1);
// Remove global listener if no more dialogs with closeOnEsc
if (dialogStack.length === 0 && escKeyListener) {
document.removeEventListener('keydown', escKeyListener);
escKeyListener = null;
}
}
}
_onDialogClick(event) { _onDialogClick(event) {
if (this.closeOnOutsideClick) { if (this.closeOnOutsideClick) {
const path = event.composedPath(); const path = event.composedPath();
@@ -217,11 +260,11 @@ class TpDialog extends EventHelpers(LitElement) {
} }
} }
_handleEscKey(event) { _handleOutsideClick(event) {
if (event.key === 'Escape' && this.closeOnEsc) { // This method should be implemented if closeOnOutsideClick functionality is needed
// For now, just close the dialog when clicking outside
this.close(); this.close();
} }
}
} }
window.customElements.define('tp-dialog', TpDialog); window.customElements.define('tp-dialog', TpDialog);