Files
portal-api/resources/js/components/micromodal.js
T
2026-02-02 17:27:33 +07:00

300 lines
9.4 KiB
JavaScript

/**
* MicroModal Global Utilities
* Provides helper functions for MicroModal management
*/
class ModalManager {
constructor() {
this.initialized = false;
this.modals = new Map();
this.init();
}
/**
* Initialize MicroModal with default settings
*/
init() {
if (typeof MicroModal === 'undefined') {
console.warn('MicroModal is not loaded');
return;
}
if (this.initialized) {
return;
}
MicroModal.init({
onShow: (modal) => {
this.onModalShow(modal);
},
onClose: (modal) => {
this.onModalClose(modal);
},
openTrigger: 'data-micromodal-trigger',
closeTrigger: 'data-micromodal-close',
openClass: 'is-open',
disableScroll: true,
disableFocus: false,
awaitOpenAnimation: false,
awaitCloseAnimation: false,
debugMode: false
});
this.initialized = true;
console.log('MicroModal initialized');
}
/**
* Called when modal is shown
*/
onModalShow(modal) {
// Re-initialize Lucide icons when modal opens
if (typeof lucide !== 'undefined') {
lucide.createIcons();
}
// Add body class to prevent scrolling
document.body.classList.add('modal-open');
// Execute custom show callback if exists
if (modal && this.modals.has(modal.id)) {
const config = this.modals.get(modal.id);
if (config.onShow && typeof config.onShow === 'function') {
config.onShow(modal);
}
}
}
/**
* Called when modal is closed
*/
onModalClose(modal) {
// Remove body class
document.body.classList.remove('modal-open');
// Execute custom close callback if exists
if (modal && this.modals.has(modal.id)) {
const config = this.modals.get(modal.id);
if (config.onClose && typeof config.onClose === 'function') {
config.onClose(modal);
}
}
}
/**
* Show modal with optional data
*/
show(modalId, data = {}) {
if (typeof MicroModal === 'undefined') {
console.error('MicroModal is not available');
return;
}
// Update modal content if data is provided
if (Object.keys(data).length > 0) {
this.updateModalContent(modalId, data);
}
MicroModal.show(modalId);
}
/**
* Close modal
*/
close(modalId) {
if (typeof MicroModal === 'undefined') {
console.error('MicroModal is not available');
return;
}
MicroModal.close(modalId);
}
/**
* Register a modal with custom configuration
*/
register(modalId, config = {}) {
this.modals.set(modalId, config);
}
/**
* Update modal content dynamically
*/
updateModalContent(modalId, data) {
const modal = document.getElementById(modalId);
if (!modal) {
console.error(`Modal ${modalId} not found`);
return;
}
// Update text content for elements with data-modal-* attributes
Object.keys(data).forEach(key => {
const element = modal.querySelector(`[data-modal-${key}]`);
if (element) {
if (element.tagName === 'INPUT' || element.tagName === 'TEXTAREA') {
element.value = data[key];
} else {
element.textContent = data[key];
}
}
// Also support getElementById for backward compatibility
const elementById = modal.querySelector(`#${key}`);
if (elementById) {
if (elementById.tagName === 'INPUT' || elementById.tagName === 'TEXTAREA') {
elementById.value = data[key];
} else {
elementById.textContent = data[key];
}
}
});
// Update form action if provided
if (data.formAction) {
const form = modal.querySelector('form');
if (form) {
form.action = data.formAction;
}
}
}
/**
* Create a confirmation modal dynamically
*/
showConfirmation(options = {}) {
const defaults = {
modalId: 'confirmationModal',
title: 'Confirm Action',
message: 'Are you sure?',
confirmText: 'Confirm',
cancelText: 'Cancel',
confirmClass: 'btn btn-primary btn-sm',
cancelClass: 'btn btn-outline-secondary btn-sm',
onConfirm: null,
onCancel: null
};
const config = { ...defaults, ...options };
// Create modal HTML if it doesn't exist
let modal = document.getElementById(config.modalId);
if (!modal) {
modal = this.createConfirmationModal(config);
document.body.appendChild(modal);
} else {
// Update existing modal
this.updateConfirmationModal(modal, config);
}
// Register callbacks
this.register(config.modalId, {
onShow: () => {
const confirmBtn = modal.querySelector('.modal__confirm');
const cancelBtn = modal.querySelector('.modal__cancel');
// Remove existing event listeners
confirmBtn.replaceWith(confirmBtn.cloneNode(true));
cancelBtn.replaceWith(cancelBtn.cloneNode(true));
// Add new event listeners
modal.querySelector('.modal__confirm').addEventListener('click', (e) => {
if (config.onConfirm && typeof config.onConfirm === 'function') {
config.onConfirm(e);
}
this.close(config.modalId);
});
modal.querySelector('.modal__cancel').addEventListener('click', (e) => {
if (config.onCancel && typeof config.onCancel === 'function') {
config.onCancel(e);
}
this.close(config.modalId);
});
}
});
this.show(config.modalId);
}
/**
* Create confirmation modal HTML
*/
createConfirmationModal(config) {
const modalHtml = `
<div class="modal micromodal-slide" id="${config.modalId}" aria-hidden="true">
<div class="modal__overlay" tabindex="-1" data-micromodal-close>
<div class="modal__container modal--sm" role="dialog" aria-modal="true">
<header class="modal__header bg-primary text-white">
<h6 class="modal__title">${config.title}</h6>
<button class="modal__close" aria-label="Close modal" data-micromodal-close>
<i data-lucide="x"></i>
</button>
</header>
<main class="modal__content">
<div class="text-center">
<p class="mb-0">${config.message}</p>
</div>
</main>
<footer class="modal__footer justify-content-center">
<button type="button" class="modal__cancel ${config.cancelClass}" data-micromodal-close>
${config.cancelText}
</button>
<button type="button" class="modal__confirm ${config.confirmClass}">
${config.confirmText}
</button>
</footer>
</div>
</div>
</div>
`;
const div = document.createElement('div');
div.innerHTML = modalHtml;
return div.firstElementChild;
}
/**
* Update existing confirmation modal
*/
updateConfirmationModal(modal, config) {
modal.querySelector('.modal__title').textContent = config.title;
modal.querySelector('.modal__content p').textContent = config.message;
modal.querySelector('.modal__confirm').textContent = config.confirmText;
modal.querySelector('.modal__cancel').textContent = config.cancelText;
// Update button classes
modal.querySelector('.modal__confirm').className = `modal__confirm ${config.confirmClass}`;
modal.querySelector('.modal__cancel').className = `modal__cancel ${config.cancelClass}`;
}
}
// Global instance
window.ModalManager = new ModalManager();
// Helper functions for backward compatibility
window.showModal = (modalId, data = {}) => {
window.ModalManager.show(modalId, data);
};
window.closeModal = (modalId) => {
window.ModalManager.close(modalId);
};
window.showConfirmation = (options) => {
window.ModalManager.showConfirmation(options);
};
// Auto-initialize when DOM is loaded
document.addEventListener('DOMContentLoaded', function() {
// Initialize Lucide icons
if (typeof lucide !== 'undefined') {
lucide.createIcons();
}
console.log('Modal utilities loaded');
});
// Export for ES modules
if (typeof module !== 'undefined' && module.exports) {
module.exports = ModalManager;
}