UNPKG

create-nexaui-electron

Version:

Create Nexa App - Tool untuk membuat aplikasi Nexa Electron

1,070 lines (894 loc) 30.9 kB
export function NexaModal(callback) { const originalNxModal = window.nxModal; }; export function nxModalShow(modalId, order = "") { const modal = document.getElementById(modalId); if (!modal) return; // Reset any previous styles const modalContent = modal.querySelector(".nx-modal-content"); const modalBody = modal.querySelector(".nx-modal-body"); const modalFooter = modal.querySelector(".nx-modal-footer"); if (modalContent) { modalContent.style.cssText = ""; // Reset inline styles } if (modalBody) { modalBody.style.cssText = ""; // Reset inline styles } if (modalFooter) { modalFooter.style.cssText = ""; // Reset inline styles } modal.style.display = "flex"; // Use flex instead of block modal.classList.add("show"); const restoreFocus = trapFocus(modal); modal.style.display = "block"; modal.setAttribute("aria-modal", "true"); // Khusus untuk modal fullscreen if (modal.classList.contains("nx-modal-fullscreen")) { document.body.style.overflow = "hidden"; // Mencegah scroll pada body modal.style.padding = "0"; const modalContent = modal.querySelector(".nx-modal-content"); if (modalContent) { // Reset style yang mungkin diset sebelumnya Object.assign(modalContent.style, { width: "100vw", height: "100vh", margin: "0", padding: "0", border: "0", borderRadius: "0", position: "fixed", top: "0", left: "0", right: "0", bottom: "0", transform: "none", }); // Atur tinggi body modal const modalBody = modalContent.querySelector(".nx-modal-body"); const modalHeader = modalContent.querySelector(".nx-modal-header"); const modalFooter = modalContent.querySelector(".nx-modal-footer"); if (modalBody && modalHeader && modalFooter) { const headerHeight = modalHeader.offsetHeight; const footerHeight = modalFooter.offsetHeight; modalBody.style.height = `calc(100vh - ${ headerHeight + footerHeight }px)`; modalBody.style.overflowY = "auto"; } } } // Inisialisasi fitur interaksi if (modal.classList.contains("nx-modal-draggable")) { makeDraggable(modalId); } if (modal.classList.contains("nx-modal-resizable")) { makeResizable(modalId); } if (modal.classList.contains("nx-modal-stacking")) { handleModalStacking(modalId); } // Simpan fungsi restore focus modal.restoreFocus = restoreFocus; // ARIA attributes modal.setAttribute("role", "dialog"); modal.setAttribute("aria-labelledby", `${modalId}-title`); // Center modal jika perlu if (modal.classList.contains("nx-modal-centered")) { centerModal(modalId); // Tambahkan event listener untuk resize window.addEventListener("resize", () => centerModal(modalId)); } // Animasi requestAnimationFrame(() => { modal.classList.add("show"); }); // Check overflow untuk modal scrollable if (modal.classList.contains("nx-modal-scrollable")) { checkModalOverflow(modalId); } // Center modal if needed centerModal(modalId); // Trap focus trapFocus(modal); // Disable scroll pada body kecuali untuk modal scrollable if (!modal.classList.contains("nx-modal-scrollable")) { document.body.style.overflow = "hidden"; } // Handle custom animations if (modal.classList.contains("nx-modal-custom-animation")) { modal.addEventListener( "animationend", () => { modal.classList.add("animation-completed"); }, { once: true } ); } // Handle transition timing if (modal.classList.contains("nx-modal-transition-timing")) { const timing = modal.getAttribute("data-timing") || "ease"; setModalTiming(modalId, timing); } // Handle effects if (modal.classList.contains("nx-modal-effects")) { const effect = modal.getAttribute("data-effect") || "blur"; setModalEffect(modalId, effect); } // Add mobile optimizations if ("ontouchstart" in window) { optimizeForMobile(modalId); const cleanup = enableTouchGestures(modalId); modal.addEventListener("modal:afterClose", cleanup, { once: true }); } // Add responsive behavior const cleanupResponsive = handleResponsiveBehavior(modalId); modal.addEventListener("modal:afterClose", cleanupResponsive, { once: true, }); // Tambahkan window controls jika modal draggable if (modal.classList.contains("nx-modal-draggable")) { const originalSize = addWindowControls(modalId); modal.originalSize = originalSize; } $(".nx-modal-content").draggable({ handle: ".nx-modal-header", scroll: false, start: function () { $(this).css({ transform: "none", }); }, }); // Prevent form submission const form = modal.querySelector("form"); if (form) { form.addEventListener("submit", function (e) { e.preventDefault(); e.stopPropagation(); return false; }); // Prevent button clicks from submitting form const buttons = form.querySelectorAll("button"); buttons.forEach((button) => { if (!button.hasAttribute("type")) { button.setAttribute("type", "button"); } button.addEventListener("click", function (e) { e.preventDefault(); e.stopPropagation(); }); }); } // Dispatch event untuk modal aktif window.dispatchEvent( new CustomEvent("modalactiv", { detail: { modalId: modalId, order: order, element: modal, timestamp: Date.now() } }) ); } // Event handler untuk click di luar modal window.onclick = function (event) { if (event.target.className.includes("nx-modal")) { const modal = event.target; const modalId = modal.id; // Jika bukan modal static, tutup modal if (!modal.classList.contains("nx-modal-static")) { nxMdClose(modalId); } else { // Animasi shake untuk modal static const modalContent = modal.querySelector(".nx-modal-content"); if (modalContent) { modalContent.style.animation = "none"; setTimeout(() => { modalContent.style.animation = "modalShake 0.3s ease-in-out"; }, 10); } } } }; // Keyboard navigation document.addEventListener("keydown", function (event) { if (event.key === "Escape") { const nxModals = document.querySelectorAll( '.nx-modal[style*="display: block"]' ); nxModals.forEach((modal) => { nxMdClose(modal.id); }); } }); // Function untuk trap focus di dalam modal function trapFocus(modal) { const focusableElements = modal.querySelectorAll( 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])' ); const firstFocusable = focusableElements[0]; const lastFocusable = focusableElements[focusableElements.length - 1]; // Simpan elemen yang sebelumnya difokus const previouslyFocused = document.activeElement; firstFocusable.focus(); modal.addEventListener("keydown", function (e) { if (e.key === "Tab") { if (e.shiftKey) { if (document.activeElement === firstFocusable) { e.preventDefault(); lastFocusable.focus(); } } else { if (document.activeElement === lastFocusable) { e.preventDefault(); firstFocusable.focus(); } } } }); // Kembalikan fokus saat modal ditutup return function restoreFocus() { previouslyFocused.focus(); }; } // Centered modal handling function centerModal(modalId) { const modal = document.getElementById(modalId); const modalContent = modal.querySelector(".nx-modal-content"); if (modal.classList.contains("nx-modal-centered")) { // Reset margin yang mungkin diset sebelumnya modalContent.style.margin = "0"; // Pastikan modal tidak melebihi viewport const windowHeight = window.innerHeight; const modalHeight = modalContent.offsetHeight; if (modalHeight > windowHeight * 0.9) { modalContent.style.height = "90vh"; modalContent.style.overflowY = "auto"; } else { modalContent.style.height = "auto"; modalContent.style.overflowY = "visible"; } } } // Handle window resize for centered modals window.addEventListener("resize", function () { const nxModals = document.querySelectorAll( '.nx-modal-centered[style*="display: block"]' ); nxModals.forEach((modal) => { centerModal(modal.id); }); }); // Tambahkan fungsi untuk mengecek overflow function checkModalOverflow(modalId) { const modal = document.getElementById(modalId); const modalBody = modal.querySelector(".nx-modal-body"); if (modalBody.scrollHeight > modalBody.clientHeight) { modal.classList.add("has-scroll"); } else { modal.classList.remove("has-scroll"); } } // Helper function untuk mendapatkan durasi transisi function getTransitionDuration(element) { const style = window.getComputedStyle(element); const duration = style.transitionDuration; return parseFloat(duration) * 1000; // Convert to milliseconds } // Modal Events & Callbacks function nxModalWithCallback(modalId) { nxModal(modalId); const status = document.getElementById("callback-status"); if (status) { status.innerHTML = '<div class="alert alert-success">Modal telah dibuka!</div>'; } } function nxMdCloseWithCallback(modalId) { const status = document.getElementById("callback-status"); if (status) { status.innerHTML = '<div class="alert alert-info">Modal akan ditutup...</div>'; } setTimeout(() => { nxMdClose(modalId); }, 1000); } // Event Listeners document.addEventListener("DOMContentLoaded", function () { // Tambahkan event listener untuk semua modal const modals = document.querySelectorAll(".nx-modal"); modals.forEach((modal) => { // Before Open Event modal.addEventListener("modal:beforeOpen", function (e) { console.log("Modal akan dibuka:", e.target.id); }); // After Open Event modal.addEventListener("modal:afterOpen", function (e) { console.log("Modal telah dibuka:", e.target.id); }); // Before Close Event modal.addEventListener("modal:beforeClose", function (e) { console.log("Modal akan ditutup:", e.target.id); }); // After Close Event modal.addEventListener("modal:afterClose", function (e) { console.log("Modal telah ditutup:", e.target.id); }); }); }); // Update fungsi nxModal dan nxMdClose untuk trigger events function triggerModalEvent(modal, eventName) { const event = new CustomEvent(eventName, { bubbles: true, cancelable: true, }); modal.dispatchEvent(event); } // Event Modal Functions window.nxModalWithCallback = function (modalId) { nxModal(modalId); const status = document.getElementById("callback-status"); if (status) { status.innerHTML = '<div class="alert alert-success">Modal telah dibuka!</div>'; } }; window.nxMdCloseWithCallback = function (modalId) { const status = document.getElementById("callback-status"); if (status) { status.innerHTML = '<div class="alert alert-info">Modal akan ditutup...</div>'; } setTimeout(() => { nxMdClose(modalId); }, 1000); }; // Method Modal Functions window.openMethodModal = function (modalId) { nxModal(modalId); // Tambahkan inisialisasi khusus untuk method modal jika diperlukan }; // Draggable Modal dengan jQuery UI dan snap to edges function makeDraggable(modalId) { const modal = document.getElementById(modalId); const modalContent = modal.querySelector(".nx-modal-content"); // Posisikan modal di tengah saat pertama dibuka const centerModal = () => { const windowWidth = $(window).width(); const windowHeight = $(window).height(); const modalWidth = $(modalContent).outerWidth(); const modalHeight = $(modalContent).outerHeight(); $(modalContent).css({ position: "fixed", left: (windowWidth - modalWidth) / 2, top: (windowHeight - modalHeight) / 2, }); }; // Panggil centerModal saat pertama kali centerModal(); // Inisialisasi draggable dengan jQuery UI $(modalContent).draggable({ handle: ".nx-modal-header", cursor: "move", snap: true, snapTolerance: 20, start: function (event, ui) { $(this).addClass("dragging"); }, drag: function (event, ui) { // Batasi area drag const windowWidth = $(window).width(); const windowHeight = $(window).height(); const modalWidth = $(this).outerWidth(); const modalHeight = $(this).outerHeight(); ui.position.left = Math.max( 0, Math.min(ui.position.left, windowWidth - modalWidth) ); ui.position.top = Math.max( 0, Math.min(ui.position.top, windowHeight - modalHeight) ); }, stop: function (event, ui) { $(this).removeClass("dragging"); }, }); // Update posisi saat window resize $(window).on("resize", centerModal); // Cleanup function return () => { try { $(modalContent).draggable("destroy"); $(window).off("resize", centerModal); } catch (e) { console.warn("Error destroying draggable:", e); } }; } // Modal Stacking let currentZIndex = 1050; function handleModalStacking(modalId) { const modal = document.getElementById(modalId); const allModals = document.querySelectorAll(".nx-modal-stacking"); allModals.forEach((m) => m.classList.remove("active")); modal.classList.add("active"); modal.style.zIndex = ++currentZIndex; } // Resizable Modal function makeResizable(modalId) { const modal = document.getElementById(modalId); const modalContent = modal.querySelector(".nx-modal-content"); // ResizeObserver untuk memantau perubahan ukuran const resizeObserver = new ResizeObserver((entries) => { for (let entry of entries) { const { width, height } = entry.contentRect; // Trigger event saat ukuran berubah const event = new CustomEvent("modal:resize", { detail: { width, height }, }); modal.dispatchEvent(event); } }); resizeObserver.observe(modalContent); } // Event listener untuk modal resize document.addEventListener("modal:resize", function (e) { const { width, height } = e.detail; console.log(`Modal resized to: ${width}x${height}`); }); // Custom Animation Controller function setModalAnimation(modalId, animationType) { const modal = document.getElementById(modalId); const currentAnimation = modal.getAttribute("data-animation"); // Remove current animation class if exists if (currentAnimation) { modal.classList.remove(currentAnimation); } // Add new animation class modal.classList.add(animationType); modal.setAttribute("data-animation", animationType); } // Transition Timing Controller function setModalTiming(modalId, timing) { const modal = document.getElementById(modalId); const modalContent = modal.querySelector(".nx-modal-content"); modalContent.style.setProperty("--animation-timing", timing); } // Special Effects Controller function setModalEffect(modalId, effect) { const modal = document.getElementById(modalId); const currentEffect = modal.getAttribute("data-effect"); if (currentEffect) { modal.classList.remove(currentEffect); } modal.classList.add(effect); modal.setAttribute("data-effect", effect); } // Animation Control Functions window.flipModal = function (modalId) { setModalAnimation(modalId, "flip"); }; window.swingModal = function (modalId) { setModalAnimation(modalId, "swing"); }; window.bounceModal = function (modalId) { setModalAnimation(modalId, "bounce"); }; // Timing Control Functions window.setModalTiming = function (modalId, timing) { const timings = { slow: "0.8s", normal: "0.5s", fast: "0.3s", }; setModalTiming(modalId, timings[timing] || timing); }; // Effect Control Functions window.setModalEffect = function (modalId, effect) { const effects = { blur: "blur", glass: "glass", neon: "neon", "shadow-pulse": "shadow-pulse", }; setModalEffect(modalId, effects[effect] || effect); }; // Mobile & Touch Functions // Touch Gesture Controller function enableTouchGestures(modalId) { const modal = document.getElementById(modalId); const content = modal.querySelector(".nx-modal-content"); let startY = 0; let currentY = 0; let isDragging = false; // Touch event handlers function handleTouchStart(e) { const touch = e.touches[0]; startY = touch.clientY; isDragging = true; modal.classList.add("swiping"); // Capture initial position const transform = window.getComputedStyle(content).transform; currentY = transform !== "none" ? parseInt(transform.split(",")[5]) : 0; } function handleTouchMove(e) { if (!isDragging) return; const touch = e.touches[0]; const deltaY = touch.clientY - startY; // Only allow swipe down if (deltaY < 0) return; // Add resistance to swipe const resistance = 0.4; const newY = currentY + deltaY * resistance; content.style.transform = `translateY(${newY}px)`; // Add opacity effect const opacity = 1 - newY / (window.innerHeight * 0.5); modal.style.backgroundColor = `rgba(0,0,0,${opacity * 0.5})`; } function handleTouchEnd(e) { if (!isDragging) return; isDragging = false; modal.classList.remove("swiping"); const transform = window.getComputedStyle(content).transform; const finalY = transform !== "none" ? parseInt(transform.split(",")[5]) : 0; // If swipe distance is greater than threshold, close modal if (finalY > window.innerHeight * 0.25) { modal.classList.add("swipe-close"); setTimeout(() => nxMdClose(modalId), 300); } else { // Reset position content.style.transform = ""; modal.style.backgroundColor = ""; } } // Add touch event listeners content.addEventListener("touchstart", handleTouchStart, { passive: true }); content.addEventListener("touchmove", handleTouchMove, { passive: false }); content.addEventListener("touchend", handleTouchEnd); // Clean up function return () => { content.removeEventListener("touchstart", handleTouchStart); content.removeEventListener("touchmove", handleTouchMove); content.removeEventListener("touchend", handleTouchEnd); }; } // Mobile Optimization Controller function optimizeForMobile(modalId) { const modal = document.getElementById(modalId); // Add mobile-specific classes modal.classList.add("nx-modal-touch"); // Enable bottom sheet behavior on mobile if (window.innerWidth <= 576) { modal.classList.add("nx-modal-bottom-sheet"); } // Handle orientation changes window.addEventListener("orientationchange", () => { setTimeout(() => { centerModal(modalId); }, 100); }); // Handle keyboard appearance on iOS if (/iPad|iPhone|iPod/.test(navigator.userAgent)) { const inputs = modal.querySelectorAll("input, textarea"); inputs.forEach((input) => { input.addEventListener("focus", () => { modal.classList.add("keyboard-open"); }); input.addEventListener("blur", () => { modal.classList.remove("keyboard-open"); }); }); } // Add fastclick for better touch response if ("addEventListener" in document) { document.addEventListener( "DOMContentLoaded", function () { FastClick.attach(modal); }, false ); } } // Responsive Behavior Controller function handleResponsiveBehavior(modalId) { const modal = document.getElementById(modalId); const content = modal.querySelector(".nx-modal-content"); // Handle resize events const resizeObserver = new ResizeObserver((entries) => { for (let entry of entries) { const { width } = entry.contentRect; // Adjust modal based on screen size if (width <= 576) { content.style.width = "100%"; modal.classList.add("mobile-view"); } else { content.style.width = ""; modal.classList.remove("mobile-view"); } } }); resizeObserver.observe(document.body); // Return cleanup function return () => resizeObserver.disconnect(); } // Tambahkan fungsi untuk minimize/maximize function addWindowControls(modalId) { const modal = document.getElementById(modalId); const modalContent = modal.querySelector(".nx-modal-content"); const header = modal.querySelector(".nx-modal-header"); // Cek apakah controls sudah ada if (header.querySelector(".nx-modal-controls")) { return; // Jika sudah ada, jangan tambahkan lagi } // Simpan ukuran asli untuk restore let originalSize = { width: modalContent.style.width, height: modalContent.style.height, top: modalContent.style.top, left: modalContent.style.left, }; // Tambahkan tombol controls const controls = document.createElement("div"); controls.className = "nx-modal-controls"; controls.innerHTML = ` <button class="nx-btn-minimize" title="Minimize">─</button> <button class="nx-btn-maximize" title="Maximize">□</button> <button class="nx-btn-restore" title="Restore" style="display:none">❐</button> `; header.insertBefore(controls, header.querySelector(".nx-close")); // Event handlers controls.querySelector(".nx-btn-minimize").onclick = (e) => { e.stopPropagation(); // Prevent event bubbling minimizeModal(modalId); }; controls.querySelector(".nx-btn-maximize").onclick = (e) => { e.stopPropagation(); // Prevent event bubbling maximizeModal(modalId); }; controls.querySelector(".nx-btn-restore").onclick = (e) => { e.stopPropagation(); // Prevent event bubbling restoreModal(modalId); }; return originalSize; } // Minimize Modal function minimizeModal(modalId) { const modal = document.getElementById(modalId); const btnMinimize = modal.querySelector(".nx-btn-minimize"); const btnMaximize = modal.querySelector(".nx-btn-maximize"); const btnRestore = modal.querySelector(".nx-btn-restore"); modal.classList.add("minimized"); modal.classList.remove("maximized"); btnMinimize.style.display = "none"; btnMaximize.style.display = "block"; btnRestore.style.display = "block"; } // Maximize Modal function maximizeModal(modalId) { const modal = document.getElementById(modalId); const modalContent = modal.querySelector(".nx-modal-content"); const btnMinimize = modal.querySelector(".nx-btn-minimize"); const btnMaximize = modal.querySelector(".nx-btn-maximize"); const btnRestore = modal.querySelector(".nx-btn-restore"); // Simpan posisi dan ukuran sebelum maximize if (!modal.originalState) { modal.originalState = { width: modalContent.style.width, height: modalContent.style.height, top: modalContent.style.top, left: modalContent.style.left, transform: modalContent.style.transform, margin: modalContent.style.margin, padding: modalContent.style.padding, borderRadius: modalContent.style.borderRadius, position: modalContent.style.position, }; } // Set fullscreen styles Object.assign(modalContent.style, { position: "fixed", top: "0", left: "0", right: "0", bottom: "0", width: "100%", height: "100%", maxWidth: "100%", maxHeight: "100%", margin: "0", padding: "0", borderRadius: "0", transform: "none", transition: "all 0.3s ease-in-out", }); // Adjust body height for scrolling const modalBody = modalContent.querySelector(".nx-modal-body"); if (modalBody) { const headerHeight = modalContent.querySelector(".nx-modal-header")?.offsetHeight || 0; const footerHeight = modalContent.querySelector(".nx-modal-footer")?.offsetHeight || 0; modalBody.style.height = `calc(100vh - ${headerHeight + footerHeight}px)`; modalBody.style.overflowY = "auto"; } modal.classList.add("maximized"); modal.classList.remove("minimized"); btnMinimize.style.display = "block"; btnMaximize.style.display = "none"; btnRestore.style.display = "block"; // Disable draggable when maximized if ($(modalContent).hasClass("ui-draggable")) { $(modalContent).draggable("disable"); } } // Restore Modal function restoreModal(modalId) { const modal = document.getElementById(modalId); const modalContent = modal.querySelector(".nx-modal-content"); const btnMinimize = modal.querySelector(".nx-btn-minimize"); const btnMaximize = modal.querySelector(".nx-btn-maximize"); const btnRestore = modal.querySelector(".nx-btn-restore"); // Reset semua style yang mungkin ditambahkan modalContent.style.cssText = ""; // Set style default Object.assign(modalContent.style, { position: "fixed", width: "50%", height: "auto", maxWidth: "90%", maxHeight: "90vh", transform: "translate(-50%, -50%)", top: "50%", left: "50%", transition: "all 0.3s ease-in-out", }); // Reset modal body const modalBody = modalContent.querySelector(".nx-modal-body"); if (modalBody) { modalBody.style.height = "auto"; modalBody.style.maxHeight = "calc(90vh - 120px)"; modalBody.style.overflowY = "auto"; } // Reset modal footer const modalFooter = modalContent.querySelector(".nx-modal-footer"); if (modalFooter) { modalFooter.style.position = "relative"; modalFooter.style.bottom = "auto"; } modal.classList.remove("minimized", "maximized"); btnMinimize.style.display = "block"; btnMaximize.style.display = "block"; btnRestore.style.display = "none"; // Re-enable draggable if ($(modalContent).hasClass("ui-draggable")) { $(modalContent).draggable("enable"); // Reset draggable position $(modalContent).draggable("option", "position", { my: "center", at: "center", of: window, }); } } // Tambahkan fungsi untuk mengelola multiple modals let modalStack = []; window.openMultiModal = function (modalId) { const modal = document.getElementById(modalId); const zIndex = 1050 + modalStack.length; modal.style.zIndex = zIndex; modal.style.display = "block"; // Add backdrop for each modal const backdrop = document.createElement("div"); backdrop.className = "nx-modal-backdrop"; backdrop.style.zIndex = zIndex - 1; document.body.appendChild(backdrop); modalStack.push({ modal: modal, backdrop: backdrop, }); requestAnimationFrame(() => { modal.classList.add("show"); backdrop.classList.add("show"); }); }; window.closeMultiModal = function (modalId) { const modalData = modalStack.pop(); if (!modalData) return; const { modal, backdrop } = modalData; modal.classList.remove("show"); backdrop.classList.remove("show"); setTimeout(() => { modal.style.display = "none"; backdrop.remove(); }, 300); }; // Tambahkan tracking untuk modal yang sedang aktif const activeModals = new Map(); // Menggunakan Map untuk menyimpan data modal window.addEventListener("modalLogged", (event) => { const { timestamp, modals } = event.detail; modals.forEach((modal) => { const modalId = modal.id; const order = modal.order; // Cek apakah modal sudah ada dan masih terbuka const existingModal = activeModals.get(modalId); const modalElement = document.getElementById(modalId); if (!existingModal || !modalElement || modalElement.style.display !== "block") { // Simpan data modal dan tampilkan activeModals.set(modalId, { order: order, timestamp: timestamp, }); // Tampilkan modal dengan timer untuk memastikan tetap terbuka const showModal = () => { const element = document.getElementById(modalId); if (element) { nxModalShow(modalId, order); // Set interval untuk memastikan modal tetap terbuka const keepAliveInterval = setInterval(() => { if (activeModals.has(modalId)) { const modalEl = document.getElementById(modalId); if (modalEl && modalEl.style.display !== "block") { nxModalShow(modalId, order); } } else { clearInterval(keepAliveInterval); } }, 100); // Simpan interval ID ke dalam Map activeModals.set(modalId, { ...activeModals.get(modalId), intervalId: keepAliveInterval, }); } }; // Delay sedikit untuk memastikan DOM sudah siap setTimeout(showModal, 50); } else { // Update order jika perlu modalElement.setAttribute("data-order", order); } }); }); // Single nxMdClose function definition window.nxMdClose = function (modalId) { const modalData = activeModals.get(modalId); if (modalData && modalData.intervalId) { clearInterval(modalData.intervalId); } activeModals.delete(modalId); const modalElement = document.getElementById(modalId); if (modalElement) { modalElement.style.display = "none"; modalElement.classList.remove("show"); // Reset body style to remove overflow: hidden document.body.removeAttribute('style'); // Reset styles const modalContent = modalElement.querySelector(".nx-modal-content"); if (modalContent) { modalContent.style.cssText = ""; } // Trigger event window.dispatchEvent( new CustomEvent("modalClosed", { detail: { modalId }, }) ); } }; // Cleanup saat halaman unload window.addEventListener("unload", () => { activeModals.forEach((data, modalId) => { if (data.intervalId) { clearInterval(data.intervalId); } }); activeModals.clear(); }); // Tambahkan fungsi nxModal ke window object window.nxModal = function(modalId, order = "") { nxModalShow(modalId, order); };