- Add functionality to create new collections via API - Implement copy and move operations between collections - Improve image rendering in markdown preview with relative path resolution - Add support for previewing binary files (images, PDFs) - Refactor modal styling to use flat buttons and improve accessibility
179 lines
6.6 KiB
JavaScript
179 lines
6.6 KiB
JavaScript
/**
|
|
* Unified Modal Manager
|
|
* Handles showing and hiding a Bootstrap modal for confirmations and prompts.
|
|
* Uses a single reusable modal element to prevent double-opening issues.
|
|
*/
|
|
class ModalManager {
|
|
constructor(modalId) {
|
|
this.modalElement = document.getElementById(modalId);
|
|
if (!this.modalElement) {
|
|
console.error(`Modal element with id "${modalId}" not found`);
|
|
return;
|
|
}
|
|
|
|
this.modal = new bootstrap.Modal(this.modalElement, {
|
|
backdrop: 'static',
|
|
keyboard: true
|
|
});
|
|
|
|
this.messageElement = this.modalElement.querySelector('#confirmationMessage');
|
|
this.inputElement = this.modalElement.querySelector('#confirmationInput');
|
|
this.confirmButton = this.modalElement.querySelector('#confirmButton');
|
|
this.cancelButton = this.modalElement.querySelector('[data-bs-dismiss="modal"]');
|
|
this.titleElement = this.modalElement.querySelector('.modal-title');
|
|
this.currentResolver = null;
|
|
this.isShowing = false;
|
|
}
|
|
|
|
/**
|
|
* Show a confirmation dialog
|
|
* @param {string} message - The message to display
|
|
* @param {string} title - The dialog title
|
|
* @param {boolean} isDangerous - Whether this is a dangerous action (shows red button)
|
|
* @returns {Promise<boolean>} - Resolves to true if confirmed, false/null if cancelled
|
|
*/
|
|
confirm(message, title = 'Confirmation', isDangerous = false) {
|
|
return new Promise((resolve) => {
|
|
// Prevent double-opening
|
|
if (this.isShowing) {
|
|
console.warn('Modal is already showing, ignoring duplicate request');
|
|
resolve(null);
|
|
return;
|
|
}
|
|
|
|
this.isShowing = true;
|
|
this.currentResolver = resolve;
|
|
this.titleElement.textContent = title;
|
|
this.messageElement.textContent = message;
|
|
this.inputElement.style.display = 'none';
|
|
|
|
// Update button styling based on danger level
|
|
if (isDangerous) {
|
|
this.confirmButton.className = 'btn-flat btn-flat-danger';
|
|
this.confirmButton.innerHTML = '<i class="bi bi-trash"></i> Delete';
|
|
} else {
|
|
this.confirmButton.className = 'btn-flat btn-flat-primary';
|
|
this.confirmButton.innerHTML = '<i class="bi bi-check-circle"></i> OK';
|
|
}
|
|
|
|
// Set up event handlers
|
|
this.confirmButton.onclick = (e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
this._handleConfirm(false);
|
|
};
|
|
|
|
// Handle modal hidden event for cleanup
|
|
this.modalElement.addEventListener('hidden.bs.modal', () => {
|
|
if (this.currentResolver) {
|
|
this._handleCancel();
|
|
}
|
|
}, { once: true });
|
|
|
|
this.modal.show();
|
|
|
|
// Focus confirm button after modal is shown
|
|
this.modalElement.addEventListener('shown.bs.modal', () => {
|
|
// Ensure aria-hidden is removed (Bootstrap should do this, but be explicit)
|
|
this.modalElement.removeAttribute('aria-hidden');
|
|
this.confirmButton.focus();
|
|
}, { once: true });
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Show a prompt dialog (input dialog)
|
|
* @param {string} message - The message/label to display
|
|
* @param {string} defaultValue - The default input value
|
|
* @param {string} title - The dialog title
|
|
* @returns {Promise<string|null>} - Resolves to input value if confirmed, null if cancelled
|
|
*/
|
|
prompt(message, defaultValue = '', title = 'Input') {
|
|
return new Promise((resolve) => {
|
|
// Prevent double-opening
|
|
if (this.isShowing) {
|
|
console.warn('Modal is already showing, ignoring duplicate request');
|
|
resolve(null);
|
|
return;
|
|
}
|
|
|
|
this.isShowing = true;
|
|
this.currentResolver = resolve;
|
|
this.titleElement.textContent = title;
|
|
this.messageElement.textContent = message;
|
|
this.inputElement.style.display = 'block';
|
|
this.inputElement.value = defaultValue;
|
|
|
|
// Reset button to primary style for prompts
|
|
this.confirmButton.className = 'btn-flat btn-flat-primary';
|
|
this.confirmButton.innerHTML = '<i class="bi bi-check-circle"></i> OK';
|
|
|
|
// Set up event handlers
|
|
this.confirmButton.onclick = (e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
this._handleConfirm(true);
|
|
};
|
|
|
|
// Handle Enter key in input
|
|
this.inputElement.onkeydown = (e) => {
|
|
if (e.key === 'Enter') {
|
|
e.preventDefault();
|
|
this._handleConfirm(true);
|
|
}
|
|
};
|
|
|
|
// Handle modal hidden event for cleanup
|
|
this.modalElement.addEventListener('hidden.bs.modal', () => {
|
|
if (this.currentResolver) {
|
|
this._handleCancel();
|
|
}
|
|
}, { once: true });
|
|
|
|
this.modal.show();
|
|
|
|
// Focus and select input after modal is shown
|
|
this.modalElement.addEventListener('shown.bs.modal', () => {
|
|
// Ensure aria-hidden is removed (Bootstrap should do this, but be explicit)
|
|
this.modalElement.removeAttribute('aria-hidden');
|
|
this.inputElement.focus();
|
|
this.inputElement.select();
|
|
}, { once: true });
|
|
});
|
|
}
|
|
|
|
_handleConfirm(isPrompt) {
|
|
if (this.currentResolver) {
|
|
const value = isPrompt ? this.inputElement.value.trim() : true;
|
|
const resolver = this.currentResolver;
|
|
this._cleanup();
|
|
resolver(value);
|
|
}
|
|
}
|
|
|
|
_handleCancel() {
|
|
if (this.currentResolver) {
|
|
const resolver = this.currentResolver;
|
|
this._cleanup();
|
|
resolver(null);
|
|
}
|
|
}
|
|
|
|
_cleanup() {
|
|
this.confirmButton.onclick = null;
|
|
this.inputElement.onkeydown = null;
|
|
this.currentResolver = null;
|
|
this.isShowing = false;
|
|
this.modal.hide();
|
|
|
|
// Restore aria-hidden after modal is hidden
|
|
this.modalElement.addEventListener('hidden.bs.modal', () => {
|
|
this.modalElement.setAttribute('aria-hidden', 'true');
|
|
}, { once: true });
|
|
}
|
|
}
|
|
|
|
// Make it globally available
|
|
window.ConfirmationManager = new ModalManager('confirmationModal');
|
|
window.ModalManager = window.ConfirmationManager; // Alias for clarity
|