Files
Oxicloud/static/js/modal.js
T
2026-02-03 17:59:04 +01:00

215 lines
6.7 KiB
JavaScript

/**
* Modal System for OxiCloud
* Provides modern, styled modals to replace browser prompts/alerts
*/
const Modal = {
// Modal element references
overlay: null,
container: null,
icon: null,
title: null,
label: null,
input: null,
cancelBtn: null,
confirmBtn: null,
closeBtn: null,
// Current callback
onConfirm: null,
onCancel: null,
/**
* Initialize modal system
*/
init() {
this.overlay = document.getElementById('input-modal');
if (!this.overlay) {
console.warn('Modal overlay not found');
return;
}
this.icon = document.getElementById('modal-icon');
this.title = document.getElementById('modal-title');
this.label = document.getElementById('modal-label');
this.input = document.getElementById('modal-input');
this.cancelBtn = document.getElementById('modal-cancel-btn');
this.confirmBtn = document.getElementById('modal-confirm-btn');
this.closeBtn = document.getElementById('modal-close-btn');
// Event listeners
this.cancelBtn.addEventListener('click', () => this.close(false));
this.closeBtn.addEventListener('click', () => this.close(false));
this.confirmBtn.addEventListener('click', () => this.confirm());
// Close on overlay click
this.overlay.addEventListener('click', (e) => {
if (e.target === this.overlay) {
this.close(false);
}
});
// Handle Enter and Escape keys
this.input.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
e.preventDefault();
this.confirm();
} else if (e.key === 'Escape') {
this.close(false);
}
});
},
/**
* Show input modal (replacement for prompt())
* @param {Object} options - Modal configuration
* @param {string} options.title - Modal title
* @param {string} options.label - Input label
* @param {string} options.placeholder - Input placeholder
* @param {string} options.value - Initial input value
* @param {string} options.icon - Font Awesome icon class (e.g., 'fa-folder-plus')
* @param {string} options.confirmText - Confirm button text
* @param {string} options.cancelText - Cancel button text
* @returns {Promise<string|null>} - Resolves with input value or null if cancelled
*/
prompt(options = {}) {
return new Promise((resolve) => {
const {
title = 'Input',
label = '',
placeholder = '',
value = '',
icon = 'fa-keyboard',
confirmText = null,
cancelText = null
} = options;
// Set modal content
this.icon.className = `fas ${icon}`;
this.title.textContent = title;
this.label.textContent = label;
this.input.placeholder = placeholder;
this.input.value = value;
// Set button text (use i18n if available)
if (confirmText) {
this.confirmBtn.textContent = confirmText;
} else if (window.i18n) {
this.confirmBtn.textContent = window.i18n.t('actions.confirm');
}
if (cancelText) {
this.cancelBtn.textContent = cancelText;
} else if (window.i18n) {
this.cancelBtn.textContent = window.i18n.t('actions.cancel');
}
// Set callbacks
this.onConfirm = () => {
const inputValue = this.input.value.trim();
resolve(inputValue || null);
};
this.onCancel = () => resolve(null);
// Show modal
this.open();
});
},
/**
* Show modal for creating new folder
* @returns {Promise<string|null>}
*/
promptNewFolder() {
const t = window.i18n ? window.i18n.t.bind(window.i18n) : (k) => k;
return this.prompt({
title: t('dialogs.new_folder_title') || 'Nueva carpeta',
label: t('dialogs.folder_name') || 'Nombre de la carpeta',
placeholder: t('dialogs.folder_placeholder') || 'Mi carpeta',
icon: 'fa-folder-plus',
confirmText: t('actions.create') || 'Crear'
});
},
/**
* Show modal for renaming
* @param {string} currentName - Current name of file/folder
* @param {boolean} isFolder - Whether it's a folder
* @returns {Promise<string|null>}
*/
promptRename(currentName, isFolder = false) {
const t = window.i18n ? window.i18n.t.bind(window.i18n) : (k) => k;
return this.prompt({
title: t('dialogs.rename_title') || 'Renombrar',
label: t('dialogs.new_name') || 'Nuevo nombre',
placeholder: '',
value: currentName,
icon: isFolder ? 'fa-folder' : 'fa-file',
confirmText: t('actions.rename') || 'Renombrar'
});
},
/**
* Open the modal
*/
open() {
if (!this.overlay) return;
// Show overlay
this.overlay.style.display = 'flex';
// Trigger animation
requestAnimationFrame(() => {
this.overlay.classList.add('active');
});
// Focus input after animation
setTimeout(() => {
this.input.focus();
this.input.select();
}, 100);
},
/**
* Close the modal
* @param {boolean} confirmed - Whether the action was confirmed
*/
close(confirmed = false) {
if (!this.overlay) return;
this.overlay.classList.remove('active');
setTimeout(() => {
this.overlay.style.display = 'none';
if (!confirmed && this.onCancel) {
this.onCancel();
}
// Clear callbacks
this.onConfirm = null;
this.onCancel = null;
}, 200);
},
/**
* Confirm the action
*/
confirm() {
if (this.onConfirm) {
this.onConfirm();
}
this.close(true);
}
};
// Initialize when DOM is ready
document.addEventListener('DOMContentLoaded', () => {
Modal.init();
});
// Export for use in other modules
window.Modal = Modal;