fix: DockerSecretsResolver - don't normalize absolute paths like /var/www/html/...
Some checks failed
Deploy Application / deploy (push) Has been cancelled
Some checks failed
Deploy Application / deploy (push) Has been cancelled
This commit is contained in:
@@ -6,6 +6,8 @@
|
||||
*/
|
||||
|
||||
import { UIManager } from '../ui/UIManager.js';
|
||||
import { ToastQueue } from './ToastQueue.js';
|
||||
import { ModalManager } from './ModalManager.js';
|
||||
|
||||
export class LiveComponentUIHelper {
|
||||
constructor(liveComponentManager) {
|
||||
@@ -13,6 +15,11 @@ export class LiveComponentUIHelper {
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -34,27 +41,29 @@ export class LiveComponentUIHelper {
|
||||
onConfirm = null
|
||||
} = options;
|
||||
|
||||
// Create dialog content
|
||||
const dialogContent = this.createDialogContent(title, content, buttons, {
|
||||
onClose,
|
||||
onConfirm
|
||||
});
|
||||
|
||||
// Show modal via UIManager
|
||||
const modal = this.uiManager.open('modal', {
|
||||
content: dialogContent,
|
||||
className: `livecomponent-dialog livecomponent-dialog--${size}`,
|
||||
onClose: () => {
|
||||
if (onClose) {
|
||||
onClose();
|
||||
}
|
||||
this.activeDialogs.delete(componentId);
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
|
||||
@@ -142,11 +151,9 @@ export class LiveComponentUIHelper {
|
||||
* @param {string} componentId - Component ID
|
||||
*/
|
||||
closeDialog(componentId) {
|
||||
const dialog = this.activeDialogs.get(componentId);
|
||||
if (dialog && typeof dialog.close === 'function') {
|
||||
dialog.close();
|
||||
this.activeDialogs.delete(componentId);
|
||||
}
|
||||
// Use ModalManager to close
|
||||
this.modalManager.close(componentId);
|
||||
this.activeDialogs.delete(componentId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -164,74 +171,20 @@ export class LiveComponentUIHelper {
|
||||
action = null
|
||||
} = options;
|
||||
|
||||
// Create notification element
|
||||
const notification = document.createElement('div');
|
||||
notification.className = `livecomponent-notification livecomponent-notification--${type} livecomponent-notification--${position}`;
|
||||
notification.setAttribute('role', 'alert');
|
||||
notification.setAttribute('aria-live', 'polite');
|
||||
|
||||
notification.innerHTML = `
|
||||
<div class="notification-content">
|
||||
<span class="notification-message">${message}</span>
|
||||
${action ? `<button class="notification-action">${action.text}</button>` : ''}
|
||||
<button class="notification-close" aria-label="Close">×</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Add styles
|
||||
notification.style.cssText = `
|
||||
position: fixed;
|
||||
${position.includes('top') ? 'top' : 'bottom'}: 1rem;
|
||||
${position.includes('left') ? 'left' : 'right'}: 1rem;
|
||||
max-width: 400px;
|
||||
padding: 1rem;
|
||||
background: ${this.getNotificationColor(type)};
|
||||
color: white;
|
||||
border-radius: 0.5rem;
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
||||
z-index: 10000;
|
||||
opacity: 0;
|
||||
transform: translateY(${position.includes('top') ? '-20px' : '20px'});
|
||||
transition: opacity 0.3s ease, transform 0.3s ease;
|
||||
`;
|
||||
|
||||
// Add to DOM
|
||||
document.body.appendChild(notification);
|
||||
|
||||
// Animate in
|
||||
requestAnimationFrame(() => {
|
||||
notification.style.opacity = '1';
|
||||
notification.style.transform = 'translateY(0)';
|
||||
// Use ToastQueue for better management
|
||||
const toast = this.toastQueue.add(componentId, {
|
||||
message: message,
|
||||
type: type,
|
||||
duration: duration,
|
||||
position: position
|
||||
});
|
||||
|
||||
// Setup close button
|
||||
const closeBtn = notification.querySelector('.notification-close');
|
||||
closeBtn.addEventListener('click', () => {
|
||||
this.hideNotification(notification);
|
||||
});
|
||||
|
||||
// Setup action button
|
||||
if (action) {
|
||||
const actionBtn = notification.querySelector('.notification-action');
|
||||
actionBtn.addEventListener('click', () => {
|
||||
if (action.handler) {
|
||||
action.handler();
|
||||
}
|
||||
this.hideNotification(notification);
|
||||
});
|
||||
// Store reference for compatibility
|
||||
if (toast) {
|
||||
this.activeNotifications.set(componentId, toast);
|
||||
}
|
||||
|
||||
// Auto-hide after duration
|
||||
if (duration > 0) {
|
||||
setTimeout(() => {
|
||||
this.hideNotification(notification);
|
||||
}, duration);
|
||||
}
|
||||
|
||||
// Store reference
|
||||
this.activeNotifications.set(componentId, notification);
|
||||
|
||||
return notification;
|
||||
return toast;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -240,31 +193,17 @@ export class LiveComponentUIHelper {
|
||||
* @param {HTMLElement|string} notificationOrComponentId - Notification element or component ID
|
||||
*/
|
||||
hideNotification(notificationOrComponentId) {
|
||||
const notification = typeof notificationOrComponentId === 'string'
|
||||
? this.activeNotifications.get(notificationOrComponentId)
|
||||
: notificationOrComponentId;
|
||||
const componentId = typeof notificationOrComponentId === 'string'
|
||||
? notificationOrComponentId
|
||||
: notificationOrComponentId.dataset?.componentId;
|
||||
|
||||
if (!notification) {
|
||||
if (!componentId) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Animate out
|
||||
notification.style.opacity = '0';
|
||||
notification.style.transform = `translateY(${notification.classList.contains('livecomponent-notification--top') ? '-20px' : '20px'})`;
|
||||
|
||||
setTimeout(() => {
|
||||
if (notification.parentNode) {
|
||||
notification.parentNode.removeChild(notification);
|
||||
}
|
||||
|
||||
// Remove from map
|
||||
for (const [componentId, notif] of this.activeNotifications.entries()) {
|
||||
if (notif === notification) {
|
||||
this.activeNotifications.delete(componentId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}, 300);
|
||||
// Use ToastQueue to remove
|
||||
this.toastQueue.remove(componentId);
|
||||
this.activeNotifications.delete(componentId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user