mirror of
https://github.com/yggdrasil-network/yggdrasil-go.git
synced 2025-08-24 16:05:07 +03:00
415 lines
No EOL
11 KiB
JavaScript
415 lines
No EOL
11 KiB
JavaScript
/**
|
|
* Modal System for Yggdrasil Web Interface
|
|
* Provides flexible modal dialogs with multiple action buttons and input forms
|
|
*/
|
|
|
|
// Global modal state
|
|
let currentModal = null;
|
|
let modalCallbacks = {};
|
|
|
|
/**
|
|
* Show a modal dialog
|
|
* @param {Object} options - Modal configuration
|
|
* @param {string} options.title - Modal title (supports localization key)
|
|
* @param {string|HTMLElement} options.content - Modal content (supports localization key)
|
|
* @param {Array} options.buttons - Array of button configurations
|
|
* @param {Array} options.inputs - Array of input field configurations
|
|
* @param {Function} options.onClose - Callback when modal is closed
|
|
* @param {boolean} options.closable - Whether modal can be closed by clicking overlay or X button
|
|
* @param {string} options.size - Modal size: 'small', 'medium', 'large'
|
|
*/
|
|
function showModal(options = {}) {
|
|
const {
|
|
title = 'Modal',
|
|
content = '',
|
|
buttons = [{ text: 'modal_close', type: 'secondary', action: 'close' }],
|
|
inputs = [],
|
|
onClose = null,
|
|
closable = true,
|
|
size = 'medium'
|
|
} = options;
|
|
|
|
const overlay = document.getElementById('modal-overlay');
|
|
const container = document.getElementById('modal-container');
|
|
const titleElement = document.getElementById('modal-title');
|
|
const contentElement = document.getElementById('modal-content');
|
|
const footerElement = document.getElementById('modal-footer');
|
|
const closeBtn = document.getElementById('modal-close-btn');
|
|
|
|
if (!overlay || !container) {
|
|
console.error('Modal elements not found in DOM');
|
|
return;
|
|
}
|
|
|
|
// Set modal size
|
|
container.className = `modal-container modal-${size}`;
|
|
|
|
// Set title (with localization support)
|
|
titleElement.textContent = getLocalizedText(title);
|
|
|
|
// Set content
|
|
if (typeof content === 'string') {
|
|
contentElement.innerHTML = `<p>${getLocalizedText(content)}</p>`;
|
|
} else if (content instanceof HTMLElement) {
|
|
contentElement.innerHTML = '';
|
|
contentElement.appendChild(content);
|
|
} else {
|
|
contentElement.innerHTML = content;
|
|
}
|
|
|
|
// Add input fields if provided
|
|
if (inputs && inputs.length > 0) {
|
|
const formContainer = document.createElement('div');
|
|
formContainer.className = 'modal-form-container';
|
|
|
|
inputs.forEach((input, index) => {
|
|
const formGroup = createFormGroup(input, index);
|
|
formContainer.appendChild(formGroup);
|
|
});
|
|
|
|
contentElement.appendChild(formContainer);
|
|
}
|
|
|
|
// Create buttons
|
|
footerElement.innerHTML = '';
|
|
buttons.forEach((button, index) => {
|
|
const btn = createModalButton(button, index);
|
|
footerElement.appendChild(btn);
|
|
});
|
|
|
|
// Configure close button
|
|
closeBtn.style.display = closable ? 'flex' : 'none';
|
|
|
|
// Set up event handlers
|
|
modalCallbacks.onClose = onClose;
|
|
|
|
// Close on overlay click (if closable)
|
|
if (closable) {
|
|
overlay.onclick = (e) => {
|
|
if (e.target === overlay) {
|
|
closeModal();
|
|
}
|
|
};
|
|
} else {
|
|
overlay.onclick = null;
|
|
}
|
|
|
|
// Close on Escape key (if closable)
|
|
if (closable) {
|
|
document.addEventListener('keydown', handleEscapeKey);
|
|
}
|
|
|
|
// Show modal
|
|
currentModal = options;
|
|
overlay.classList.add('show');
|
|
|
|
// Focus first input if available
|
|
setTimeout(() => {
|
|
const firstInput = contentElement.querySelector('.modal-form-input');
|
|
if (firstInput) {
|
|
firstInput.focus();
|
|
}
|
|
}, 100);
|
|
}
|
|
|
|
/**
|
|
* Close the current modal
|
|
*/
|
|
function closeModal() {
|
|
const overlay = document.getElementById('modal-overlay');
|
|
if (!overlay) return;
|
|
|
|
overlay.classList.remove('show');
|
|
|
|
// Clean up event listeners
|
|
document.removeEventListener('keydown', handleEscapeKey);
|
|
overlay.onclick = null;
|
|
|
|
// Call onClose callback if provided
|
|
if (modalCallbacks.onClose) {
|
|
modalCallbacks.onClose();
|
|
}
|
|
|
|
// Clear state
|
|
currentModal = null;
|
|
modalCallbacks = {};
|
|
}
|
|
|
|
/**
|
|
* Create a form group element
|
|
*/
|
|
function createFormGroup(input, index) {
|
|
const {
|
|
type = 'text',
|
|
name = `input_${index}`,
|
|
label = '',
|
|
placeholder = '',
|
|
value = '',
|
|
required = false,
|
|
help = '',
|
|
options = [] // for select inputs
|
|
} = input;
|
|
|
|
const formGroup = document.createElement('div');
|
|
formGroup.className = 'modal-form-group';
|
|
|
|
// Create label
|
|
if (label) {
|
|
const labelElement = document.createElement('label');
|
|
labelElement.className = 'modal-form-label';
|
|
labelElement.textContent = getLocalizedText(label);
|
|
labelElement.setAttribute('for', name);
|
|
formGroup.appendChild(labelElement);
|
|
}
|
|
|
|
// Create input element
|
|
let inputElement;
|
|
|
|
if (type === 'textarea') {
|
|
inputElement = document.createElement('textarea');
|
|
inputElement.className = 'modal-form-textarea';
|
|
} else if (type === 'select') {
|
|
inputElement = document.createElement('select');
|
|
inputElement.className = 'modal-form-select';
|
|
|
|
// Add options
|
|
options.forEach(option => {
|
|
const optionElement = document.createElement('option');
|
|
optionElement.value = option.value || option;
|
|
optionElement.textContent = getLocalizedText(option.text || option);
|
|
if (option.selected || option.value === value) {
|
|
optionElement.selected = true;
|
|
}
|
|
inputElement.appendChild(optionElement);
|
|
});
|
|
} else {
|
|
inputElement = document.createElement('input');
|
|
inputElement.type = type;
|
|
inputElement.className = 'modal-form-input';
|
|
inputElement.value = value;
|
|
}
|
|
|
|
inputElement.name = name;
|
|
inputElement.id = name;
|
|
|
|
if (placeholder) {
|
|
inputElement.placeholder = getLocalizedText(placeholder);
|
|
}
|
|
|
|
if (required) {
|
|
inputElement.required = true;
|
|
}
|
|
|
|
formGroup.appendChild(inputElement);
|
|
|
|
// Create help text
|
|
if (help) {
|
|
const helpElement = document.createElement('div');
|
|
helpElement.className = 'modal-form-help';
|
|
helpElement.textContent = getLocalizedText(help);
|
|
formGroup.appendChild(helpElement);
|
|
}
|
|
|
|
return formGroup;
|
|
}
|
|
|
|
/**
|
|
* Create a modal button
|
|
*/
|
|
function createModalButton(button, index) {
|
|
const {
|
|
text = 'Button',
|
|
type = 'secondary',
|
|
action = null,
|
|
callback = null,
|
|
disabled = false
|
|
} = button;
|
|
|
|
const btn = document.createElement('button');
|
|
btn.className = `modal-btn modal-btn-${type}`;
|
|
btn.textContent = getLocalizedText(text);
|
|
btn.disabled = disabled;
|
|
|
|
btn.onclick = () => {
|
|
if (action === 'close') {
|
|
closeModal();
|
|
} else if (callback) {
|
|
const formData = getModalFormData();
|
|
const result = callback(formData);
|
|
|
|
// If callback returns false, don't close modal
|
|
if (result !== false) {
|
|
closeModal();
|
|
}
|
|
}
|
|
};
|
|
|
|
return btn;
|
|
}
|
|
|
|
/**
|
|
* Get form data from modal inputs
|
|
*/
|
|
function getModalFormData() {
|
|
const formData = {};
|
|
const inputs = document.querySelectorAll('#modal-content .modal-form-input, #modal-content .modal-form-textarea, #modal-content .modal-form-select');
|
|
|
|
inputs.forEach(input => {
|
|
formData[input.name] = input.value;
|
|
});
|
|
|
|
return formData;
|
|
}
|
|
|
|
/**
|
|
* Handle Escape key press
|
|
*/
|
|
function handleEscapeKey(e) {
|
|
if (e.key === 'Escape' && currentModal) {
|
|
closeModal();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get localized text or return original if not found
|
|
*/
|
|
function getLocalizedText(key) {
|
|
if (typeof key !== 'string') return key;
|
|
|
|
const currentLang = window.getCurrentLanguage ? window.getCurrentLanguage() : 'en';
|
|
|
|
if (window.translations &&
|
|
window.translations[currentLang] &&
|
|
window.translations[currentLang][key]) {
|
|
return window.translations[currentLang][key];
|
|
}
|
|
|
|
return key;
|
|
}
|
|
|
|
// Convenience functions for common modal types
|
|
|
|
/**
|
|
* Show a confirmation dialog
|
|
*/
|
|
function showConfirmModal(options = {}) {
|
|
const {
|
|
title = 'modal_confirm',
|
|
message = 'modal_confirm_message',
|
|
confirmText = 'modal_confirm_yes',
|
|
cancelText = 'modal_cancel',
|
|
onConfirm = null,
|
|
onCancel = null,
|
|
type = 'danger' // danger, primary, success
|
|
} = options;
|
|
|
|
showModal({
|
|
title,
|
|
content: message,
|
|
closable: true,
|
|
buttons: [
|
|
{
|
|
text: cancelText,
|
|
type: 'secondary',
|
|
action: 'close',
|
|
callback: onCancel
|
|
},
|
|
{
|
|
text: confirmText,
|
|
type: type,
|
|
callback: () => {
|
|
if (onConfirm) onConfirm();
|
|
return true; // Close modal
|
|
}
|
|
}
|
|
]
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Show an alert dialog
|
|
*/
|
|
function showAlertModal(options = {}) {
|
|
const {
|
|
title = 'modal_alert',
|
|
message = '',
|
|
buttonText = 'modal_ok',
|
|
type = 'primary',
|
|
onClose = null
|
|
} = options;
|
|
|
|
showModal({
|
|
title,
|
|
content: message,
|
|
closable: true,
|
|
onClose,
|
|
buttons: [
|
|
{
|
|
text: buttonText,
|
|
type: type,
|
|
action: 'close'
|
|
}
|
|
]
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Show a prompt dialog with input
|
|
*/
|
|
function showPromptModal(options = {}) {
|
|
const {
|
|
title = 'modal_input',
|
|
message = '',
|
|
inputLabel = '',
|
|
inputPlaceholder = '',
|
|
inputValue = '',
|
|
inputType = 'text',
|
|
inputRequired = true,
|
|
confirmText = 'modal_ok',
|
|
cancelText = 'modal_cancel',
|
|
onConfirm = null,
|
|
onCancel = null
|
|
} = options;
|
|
|
|
showModal({
|
|
title,
|
|
content: message,
|
|
closable: true,
|
|
inputs: [
|
|
{
|
|
type: inputType,
|
|
name: 'input_value',
|
|
label: inputLabel,
|
|
placeholder: inputPlaceholder,
|
|
value: inputValue,
|
|
required: inputRequired
|
|
}
|
|
],
|
|
buttons: [
|
|
{
|
|
text: cancelText,
|
|
type: 'secondary',
|
|
action: 'close',
|
|
callback: onCancel
|
|
},
|
|
{
|
|
text: confirmText,
|
|
type: 'primary',
|
|
callback: (formData) => {
|
|
if (inputRequired && !formData.input_value.trim()) {
|
|
return false; // Don't close modal
|
|
}
|
|
if (onConfirm) onConfirm(formData.input_value);
|
|
return true; // Close modal
|
|
}
|
|
}
|
|
]
|
|
});
|
|
}
|
|
|
|
// Export functions to global scope
|
|
window.showModal = showModal;
|
|
window.closeModal = closeModal;
|
|
window.showConfirmModal = showConfirmModal;
|
|
window.showAlertModal = showAlertModal;
|
|
window.showPromptModal = showPromptModal;
|