import { css, html, LitElement } from 'lit'; import { property } from 'lit/decorators.js'; import './icons/icon-loading'; import { classMap } from 'lit/directives/class-map.js'; import baseStyles from './styles/base'; type ToastType = 'success' | 'error' | 'warn'; export class LitToast extends LitElement { @property({ type: String }) message = ''; @property({ type: String }) type = 'success' as ToastType; override render() { return html`
${ this.type === 'success' ? html`` : html`` } ${this.message}
`; } static override styles = [ ...baseStyles, css` .toast { border-radius: var(--halo-cw-base-rounded, 0.5em); font-size: 0.875em; display: inline-flex; align-items: center; justify-content: center; padding: 0.5em 0.625em; justify-content: space-between; overflow: hidden; color: #fff; gap: 0.5em; box-shadow: 0 0 #0000, 0 0 #0000, 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); animation: slideInDown 0.3s ease-out forwards; } .toast--exit { animation: slideOutUp 0.3s ease-in forwards; } .toast--error { background-color: #d71d1d; } .toast--success { background-color: #4ccba0; } .toast--warn { background-color: #f5a623; } @keyframes slideInDown { from { transform: translateY(0); opacity: 0; } to { transform: translateY(100%); opacity: 1; } } @keyframes slideOutUp { from { transform: translateY(100%); opacity: 1; } to { transform: translateY(0); opacity: 0; } } @unocss-placeholder; `, ]; } export class LitToastContainer extends LitElement { override render() { return html``; } static override styles = [ ...baseStyles, css` :host { position: fixed; top: 1em; z-index: 1000; display: flex; width: 100%; justify-content: center; align-items: center; flex-direction: column; gap: 0.5em; } `, ]; } customElements.get('lit-toast') || customElements.define('lit-toast', LitToast); customElements.get('lit-toast-container') || customElements.define('lit-toast-container', LitToastContainer); declare global { interface HTMLElementTagNameMap { 'lit-toast': LitToast; 'lit-toast-container': LitToastContainer; } } export class ToastManager { private body: HTMLBodyElement = document.body as HTMLBodyElement; private toastContainer: LitToastContainer; constructor() { const container = this.body.querySelector('lit-toast-container'); if (!container) { this.toastContainer = new LitToastContainer(); this.body.appendChild(this.toastContainer); } else { this.toastContainer = container as LitToastContainer; } } show(message: string, type: ToastType) { const toast = new LitToast(); toast.message = message; toast.type = type; this.toastContainer.appendChild(toast); setTimeout(() => { toast.classList.add('toast--exit'); setTimeout(() => { this.toastContainer?.removeChild(toast); }, 300); }, 3000); } success(message: string) { this.show(message, 'success'); } error(message: string) { this.show(message, 'error'); } warn(message: string) { this.show(message, 'warn'); } }