Some checks failed
Deploy Application / deploy (push) Has been cancelled
287 lines
8.2 KiB
JavaScript
287 lines
8.2 KiB
JavaScript
/**
|
|
* LiveComponent UI Helper
|
|
*
|
|
* Provides unified API for UI components (Dialogs, Modals, Notifications)
|
|
* integrated with LiveComponents.
|
|
*/
|
|
|
|
import { UIManager } from '../ui/UIManager.js';
|
|
import { ToastQueue } from './ToastQueue.js';
|
|
import { ModalManager } from './ModalManager.js';
|
|
|
|
export class LiveComponentUIHelper {
|
|
constructor(liveComponentManager) {
|
|
this.manager = liveComponentManager;
|
|
this.uiManager = UIManager;
|
|
this.activeDialogs = new Map(); // componentId → dialog instances
|
|
this.activeNotifications = new Map(); // componentId → notification instances
|
|
this.toastQueue = new ToastQueue(5); // Max 5 toasts
|
|
|
|
// Use ModalManager with native <dialog> element
|
|
// Native <dialog> provides automatic backdrop, focus management, and ESC handling
|
|
this.modalManager = new ModalManager();
|
|
}
|
|
|
|
/**
|
|
* Show dialog from LiveComponent action
|
|
*
|
|
* @param {string} componentId - Component ID
|
|
* @param {Object} options - Dialog options
|
|
* @returns {Object} Dialog instance
|
|
*/
|
|
showDialog(componentId, options = {}) {
|
|
const {
|
|
title = '',
|
|
content = '',
|
|
size = 'medium',
|
|
buttons = [],
|
|
closeOnBackdrop = true,
|
|
closeOnEscape = true,
|
|
onClose = null,
|
|
onConfirm = null
|
|
} = options;
|
|
|
|
// Use ModalManager for better stack management
|
|
const modal = this.modalManager.open(componentId, {
|
|
title: title,
|
|
content: content,
|
|
size: size,
|
|
buttons: buttons,
|
|
closeOnBackdrop: closeOnBackdrop,
|
|
closeOnEscape: closeOnEscape
|
|
});
|
|
|
|
// Store reference
|
|
this.activeDialogs.set(componentId, modal);
|
|
|
|
// Setup onClose callback
|
|
if (onClose && modal.dialog) {
|
|
const originalClose = modal.close;
|
|
modal.close = () => {
|
|
originalClose.call(modal);
|
|
onClose();
|
|
this.activeDialogs.delete(componentId);
|
|
};
|
|
}
|
|
|
|
return modal;
|
|
}
|
|
|
|
/**
|
|
* Show confirmation dialog
|
|
*
|
|
* @param {string} componentId - Component ID
|
|
* @param {Object} options - Confirmation options
|
|
* @returns {Promise<boolean>} Promise resolving to true if confirmed
|
|
*/
|
|
showConfirm(componentId, options = {}) {
|
|
return new Promise((resolve) => {
|
|
const {
|
|
title = 'Confirm',
|
|
message = 'Are you sure?',
|
|
confirmText = 'Confirm',
|
|
cancelText = 'Cancel',
|
|
confirmClass = 'btn-primary',
|
|
cancelClass = 'btn-secondary'
|
|
} = options;
|
|
|
|
const buttons = [
|
|
{
|
|
text: cancelText,
|
|
class: cancelClass,
|
|
action: () => {
|
|
this.closeDialog(componentId);
|
|
resolve(false);
|
|
}
|
|
},
|
|
{
|
|
text: confirmText,
|
|
class: confirmClass,
|
|
action: () => {
|
|
this.closeDialog(componentId);
|
|
resolve(true);
|
|
}
|
|
}
|
|
];
|
|
|
|
this.showDialog(componentId, {
|
|
title,
|
|
content: `<p>${message}</p>`,
|
|
buttons,
|
|
size: 'small'
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Show alert dialog
|
|
*
|
|
* @param {string} componentId - Component ID
|
|
* @param {Object} options - Alert options
|
|
*/
|
|
showAlert(componentId, options = {}) {
|
|
const {
|
|
title = 'Alert',
|
|
message = '',
|
|
buttonText = 'OK',
|
|
type = 'info' // info, success, warning, error
|
|
} = options;
|
|
|
|
const buttons = [
|
|
{
|
|
text: buttonText,
|
|
class: `btn-${type}`,
|
|
action: () => {
|
|
this.closeDialog(componentId);
|
|
}
|
|
}
|
|
];
|
|
|
|
return this.showDialog(componentId, {
|
|
title,
|
|
content: `<p class="alert-message alert-message--${type}">${message}</p>`,
|
|
buttons,
|
|
size: 'small'
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Close dialog
|
|
*
|
|
* @param {string} componentId - Component ID
|
|
*/
|
|
closeDialog(componentId) {
|
|
// Use ModalManager to close
|
|
this.modalManager.close(componentId);
|
|
this.activeDialogs.delete(componentId);
|
|
}
|
|
|
|
/**
|
|
* Show notification
|
|
*
|
|
* @param {string} componentId - Component ID
|
|
* @param {Object} options - Notification options
|
|
*/
|
|
showNotification(componentId, options = {}) {
|
|
const {
|
|
message = '',
|
|
type = 'info', // info, success, warning, error
|
|
duration = 5000,
|
|
position = 'top-right',
|
|
action = null
|
|
} = options;
|
|
|
|
// Use ToastQueue for better management
|
|
const toast = this.toastQueue.add(componentId, {
|
|
message: message,
|
|
type: type,
|
|
duration: duration,
|
|
position: position
|
|
});
|
|
|
|
// Store reference for compatibility
|
|
if (toast) {
|
|
this.activeNotifications.set(componentId, toast);
|
|
}
|
|
|
|
return toast;
|
|
}
|
|
|
|
/**
|
|
* Hide notification
|
|
*
|
|
* @param {HTMLElement|string} notificationOrComponentId - Notification element or component ID
|
|
*/
|
|
hideNotification(notificationOrComponentId) {
|
|
const componentId = typeof notificationOrComponentId === 'string'
|
|
? notificationOrComponentId
|
|
: notificationOrComponentId.dataset?.componentId;
|
|
|
|
if (!componentId) {
|
|
return;
|
|
}
|
|
|
|
// Use ToastQueue to remove
|
|
this.toastQueue.remove(componentId);
|
|
this.activeNotifications.delete(componentId);
|
|
}
|
|
|
|
/**
|
|
* Create dialog content HTML
|
|
*
|
|
* @param {string} title - Dialog title
|
|
* @param {string} content - Dialog content
|
|
* @param {Array} buttons - Button configurations
|
|
* @param {Object} callbacks - Callback functions
|
|
* @returns {string} HTML content
|
|
*/
|
|
createDialogContent(title, content, buttons, callbacks) {
|
|
const buttonsHtml = buttons.map((btn, index) => {
|
|
const btnClass = btn.class || 'btn-secondary';
|
|
const btnAction = btn.action || (() => {});
|
|
return `<button class="btn ${btnClass}" data-dialog-action="${index}">${btn.text || 'Button'}</button>`;
|
|
}).join('');
|
|
|
|
return `
|
|
<div class="livecomponent-dialog-content">
|
|
${title ? `<div class="dialog-header"><h3>${title}</h3></div>` : ''}
|
|
<div class="dialog-body">${content}</div>
|
|
${buttons.length > 0 ? `<div class="dialog-footer">${buttonsHtml}</div>` : ''}
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
/**
|
|
* Get notification color by type
|
|
*
|
|
* @param {string} type - Notification type
|
|
* @returns {string} Color value
|
|
*/
|
|
getNotificationColor(type) {
|
|
const colors = {
|
|
info: '#3b82f6',
|
|
success: '#10b981',
|
|
warning: '#f59e0b',
|
|
error: '#ef4444'
|
|
};
|
|
return colors[type] || colors.info;
|
|
}
|
|
|
|
/**
|
|
* Show loading dialog
|
|
*
|
|
* @param {string} componentId - Component ID
|
|
* @param {string} message - Loading message
|
|
* @returns {Object} Dialog instance
|
|
*/
|
|
showLoadingDialog(componentId, message = 'Loading...') {
|
|
return this.showDialog(componentId, {
|
|
title: '',
|
|
content: `
|
|
<div class="loading-dialog">
|
|
<div class="spinner"></div>
|
|
<p>${message}</p>
|
|
</div>
|
|
`,
|
|
size: 'small',
|
|
buttons: [],
|
|
closeOnBackdrop: false,
|
|
closeOnEscape: false
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Cleanup all UI components for component
|
|
*
|
|
* @param {string} componentId - Component ID
|
|
*/
|
|
cleanup(componentId) {
|
|
// Close dialogs
|
|
this.closeDialog(componentId);
|
|
|
|
// Hide notifications
|
|
this.hideNotification(componentId);
|
|
}
|
|
}
|
|
|