feat: rollout

This commit is contained in:
2025-10-21 21:01:56 +02:00
parent 7def7bce81
commit cdb42c459a
7 changed files with 1059 additions and 175 deletions

View File

@@ -9,6 +9,8 @@ class OverlayDialogComponent extends Component {
this.message = '';
this.confirmText = 'Yes';
this.cancelText = 'No';
this.confirmClass = 'overlay-dialog-btn-confirm';
this.showCloseButton = true;
}
mount() {
@@ -38,6 +40,8 @@ class OverlayDialogComponent extends Component {
message = 'Are you sure you want to proceed?',
confirmText = 'Yes',
cancelText = 'No',
confirmClass = 'overlay-dialog-btn-confirm',
showCloseButton = true,
onConfirm = null,
onCancel = null
} = options;
@@ -46,53 +50,74 @@ class OverlayDialogComponent extends Component {
this.message = message;
this.confirmText = confirmText;
this.cancelText = cancelText;
this.confirmClass = confirmClass;
this.showCloseButton = showCloseButton;
this.onConfirm = onConfirm;
this.onCancel = onCancel;
this.render();
this.container.classList.add('visible');
// Add visible class with small delay for animation
setTimeout(() => {
this.container.classList.add('visible');
}, 10);
this.isVisible = true;
}
hide() {
this.container.classList.remove('visible');
this.isVisible = false;
// Call cancel callback if provided
if (this.onCancel) {
this.onCancel();
}
setTimeout(() => {
this.isVisible = false;
// Call cancel callback if provided
if (this.onCancel) {
this.onCancel();
this.onCancel = null;
}
}, 300);
}
handleConfirm() {
this.hide();
this.container.classList.remove('visible');
// Call confirm callback if provided
if (this.onConfirm) {
this.onConfirm();
}
setTimeout(() => {
this.isVisible = false;
// Call confirm callback if provided
if (this.onConfirm) {
this.onConfirm();
this.onConfirm = null;
}
}, 300);
}
render() {
this.container.innerHTML = `
<div class="overlay-dialog-content">
<div class="overlay-dialog-header">
<h3 class="overlay-dialog-title">${this.title}</h3>
<button class="overlay-dialog-close" type="button">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="20" height="20">
<path d="M18 6L6 18M6 6l12 12"/>
</svg>
</button>
<h3 class="overlay-dialog-title">${this.escapeHtml(this.title)}</h3>
${this.showCloseButton ? `
<button class="overlay-dialog-close" type="button" aria-label="Close">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="20" height="20">
<line x1="18" y1="6" x2="6" y2="18"/>
<line x1="6" y1="6" x2="18" y2="18"/>
</svg>
</button>
` : ''}
</div>
<div class="overlay-dialog-body">
<div class="overlay-dialog-message">${this.message}</div>
<p class="overlay-dialog-message">${this.message}</p>
</div>
<div class="overlay-dialog-footer">
<button class="overlay-dialog-btn overlay-dialog-btn-cancel" type="button">
${this.cancelText}
</button>
<button class="overlay-dialog-btn overlay-dialog-btn-confirm" type="button">
${this.confirmText}
${this.cancelText ? `
<button class="overlay-dialog-btn overlay-dialog-btn-cancel" type="button">
${this.escapeHtml(this.cancelText)}
</button>
` : ''}
<button class="overlay-dialog-btn ${this.confirmClass}" type="button">
${this.escapeHtml(this.confirmText)}
</button>
</div>
</div>
@@ -101,7 +126,7 @@ class OverlayDialogComponent extends Component {
// Add event listeners to buttons
const closeBtn = this.container.querySelector('.overlay-dialog-close');
const cancelBtn = this.container.querySelector('.overlay-dialog-btn-cancel');
const confirmBtn = this.container.querySelector('.overlay-dialog-btn-confirm');
const confirmBtn = this.container.querySelector(`.${this.confirmClass}`);
if (closeBtn) {
this.addEventListener(closeBtn, 'click', () => this.hide());
@@ -116,6 +141,13 @@ class OverlayDialogComponent extends Component {
}
}
escapeHtml(text) {
if (typeof text !== 'string') return text;
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
unmount() {
// Clean up event listeners
this.removeAllEventListeners();
@@ -124,3 +156,68 @@ class OverlayDialogComponent extends Component {
super.unmount();
}
}
// Static utility methods for easy usage without mounting
OverlayDialogComponent.show = function(options) {
// Create a temporary container
const container = document.createElement('div');
container.className = 'overlay-dialog';
document.body.appendChild(container);
// Create component instance
const dialog = new OverlayDialogComponent(container, null, null);
// Override hide to clean up container
const originalHide = dialog.hide.bind(dialog);
dialog.hide = function() {
originalHide();
setTimeout(() => {
if (container.parentNode) {
document.body.removeChild(container);
}
}, 350);
};
// Override handleConfirm to clean up container
const originalHandleConfirm = dialog.handleConfirm.bind(dialog);
dialog.handleConfirm = function() {
originalHandleConfirm();
setTimeout(() => {
if (container.parentNode) {
document.body.removeChild(container);
}
}, 350);
};
dialog.mount();
dialog.show(options);
return dialog;
};
// Convenience method for confirmation dialogs
OverlayDialogComponent.confirm = function(options) {
return OverlayDialogComponent.show({
...options,
confirmClass: options.confirmClass || 'overlay-dialog-btn-confirm'
});
};
// Convenience method for danger/delete confirmations
OverlayDialogComponent.danger = function(options) {
return OverlayDialogComponent.show({
...options,
confirmClass: 'overlay-dialog-btn-danger'
});
};
// Convenience method for alerts
OverlayDialogComponent.alert = function(message, title = 'Notice') {
return OverlayDialogComponent.show({
title,
message,
confirmText: 'OK',
cancelText: null,
showCloseButton: false
});
};