templates/vitekit-claude/src/scripts/components/toast.js
2026-04-12 21:03:18 +03:00

268 lines
7.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import Toastify from 'toastify-js';
export class ToastComponent {
static defaultOptions = {
duration: 4000,
position: 'right',
gravity: 'top',
close: true,
stopOnFocus: true,
style: {
background: 'transparent',
boxShadow: 'none',
padding: '0',
borderRadius: '0'
},
offset: {
x: 16,
y: 16
}
};
static types = {
success: {
className: 'toast-success',
icon: '✓'
},
error: {
className: 'toast-error',
icon: '✕'
},
warning: {
className: 'toast-warning',
icon: '⚠'
},
info: {
className: 'toast-info',
icon: ''
},
default: {
className: 'toast-default',
icon: '●'
}
};
static show(message, type = 'default', options = {}) {
if (!message) {
console.warn('ToastComponent: Message is required');
return null;
}
const typeConfig = this.types[type] || this.types.default;
const config = { ...this.defaultOptions, ...options };
// Build toast content
const content = this.buildToastContent(message, config.title, typeConfig.icon);
const toastOptions = {
text: content,
duration: config.duration,
gravity: config.gravity,
position: config.position,
stopOnFocus: config.stopOnFocus,
className: `toastify ${typeConfig.className}`,
escapeMarkup: false,
offset: config.offset,
style: {
...this.defaultOptions.style,
...config.style
},
onClick: config.onClick,
onClose: config.onClose
};
// Create and show toast
const toast = Toastify(toastOptions);
toast.showToast();
// Add progress bar if duration is set
if (config.duration > 0 && config.showProgress !== false) {
this.addProgressBar(toast, config.duration);
}
return toast;
}
static buildToastContent(message, title, icon) {
let content = '<div class="toast-enhanced">';
if (icon) {
content += `<div class="toast-enhanced__icon">${icon}</div>`;
}
content += '<div class="toast-enhanced__content">';
if (title) {
content += `<div class="toast-enhanced__title">${this.escapeHtml(title)}</div>`;
}
content += `<div class="toast-enhanced__message">${this.escapeHtml(message)}</div>`;
content += '</div>';
content += '<button class="toast-enhanced__close" onclick="this.closest(\'.toastify\').remove()"></button>';
content += '</div>';
return content;
}
static addProgressBar(toast, duration) {
const toastElement = toast.toastElement;
if (!toastElement) return;
const progressBar = document.createElement('div');
progressBar.className = 'toast-progress';
progressBar.innerHTML = '<div class="toast-progress__bar"></div>';
toastElement.appendChild(progressBar);
const bar = progressBar.querySelector('.toast-progress__bar');
if (bar) {
bar.style.animationDuration = `${duration}ms`;
}
}
static escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// Convenience methods
static success(message, options = {}) {
return this.show(message, 'success', options);
}
static error(message, options = {}) {
return this.show(message, 'error', options);
}
static warning(message, options = {}) {
return this.show(message, 'warning', options);
}
static info(message, options = {}) {
return this.show(message, 'info', options);
}
// Advanced toast types
static promise(promise, messages = {}, options = {}) {
const {
loading = 'Загрузка...',
success = 'Успешно!',
error = 'Ошибка!'
} = messages;
// Show loading toast
const loadingToast = this.show(loading, 'info', {
duration: 0, // Don't auto-close
showProgress: false,
...options
});
return promise
.then(result => {
// Close loading toast
if (loadingToast && loadingToast.toastElement) {
loadingToast.toastElement.remove();
}
// Show success toast
const successMessage = typeof success === 'function' ? success(result) : success;
this.success(successMessage, options);
return result;
})
.catch(err => {
// Close loading toast
if (loadingToast && loadingToast.toastElement) {
loadingToast.toastElement.remove();
}
// Show error toast
const errorMessage = typeof error === 'function' ? error(err) : error;
this.error(errorMessage, options);
throw err;
});
}
static confirm(message, options = {}) {
return new Promise((resolve) => {
const confirmOptions = {
duration: 0,
title: 'Подтверждение',
showProgress: false,
...options
};
const content = `
<div class="toast-enhanced__content">
<div class="toast-enhanced__icon">?</div>
<div class="toast-enhanced__text">
<div class="toast-enhanced__title">${this.escapeHtml(confirmOptions.title)}</div>
<div class="toast-enhanced__message">${this.escapeHtml(message)}</div>
<div class="toast-enhanced__actions" style="margin-top: 12px; display: flex; gap: 8px;">
<button class="btn btn--sm btn--primary toast-confirm-yes">Да</button>
<button class="btn btn--sm btn--secondary toast-confirm-no">Нет</button>
</div>
</div>
</div>
`;
const toast = Toastify({
text: content,
gravity: 'top',
position: 'center',
className: 'toastify toast-warning toast-enhanced toast-confirm',
escapeMarkup: false,
duration: 0,
stopOnFocus: true
});
toast.showToast();
// Add event listeners
const toastElement = toast.toastElement;
if (toastElement) {
const yesBtn = toastElement.querySelector('.toast-confirm-yes');
const noBtn = toastElement.querySelector('.toast-confirm-no');
if (yesBtn) {
yesBtn.addEventListener('click', () => {
toastElement.remove();
resolve(true);
});
}
if (noBtn) {
noBtn.addEventListener('click', () => {
toastElement.remove();
resolve(false);
});
}
}
});
}
// Utility method to remove all toasts
static clear() {
const toasts = document.querySelectorAll('.toastify');
toasts.forEach(toast => toast.remove());
}
// Method to update toast position for responsive design
static updatePosition() {
const isMobile = window.innerWidth < 768;
this.defaultOptions.position = isMobile ? 'center' : 'right';
}
}
// Update position on resize
if (typeof window !== 'undefined') {
window.addEventListener('resize', () => {
ToastComponent.updatePosition();
});
// Set initial position
ToastComponent.updatePosition();
}