Spaces:
Running
Running
| class ToastNotification extends HTMLElement { | |
| constructor() { | |
| super(); | |
| this.attachShadow({ mode: 'open' }); | |
| this._timeout = null; | |
| } | |
| static get observedAttributes() { | |
| return ['type', 'title', 'message', 'duration', 'visible']; | |
| } | |
| connectedCallback() { | |
| this.render(); | |
| } | |
| attributeChangedCallback(name, oldValue, newValue) { | |
| if (oldValue !== newValue) { | |
| this.render(); | |
| } | |
| } | |
| disconnectedCallback() { | |
| if (this._timeout) { | |
| clearTimeout(this._timeout); | |
| } | |
| } | |
| show() { | |
| this.setAttribute('visible', 'true'); | |
| this.render(); | |
| const duration = parseInt(this.getAttribute('duration')) || 5000; | |
| if (this._timeout) { | |
| clearTimeout(this._timeout); | |
| } | |
| this._timeout = setTimeout(() => { | |
| this.hide(); | |
| }, duration); | |
| } | |
| hide() { | |
| this.setAttribute('visible', 'false'); | |
| this.render(); | |
| if (this._timeout) { | |
| clearTimeout(this._timeout); | |
| this._timeout = null; | |
| } | |
| } | |
| render() { | |
| const type = this.getAttribute('type') || 'success'; | |
| const title = this.getAttribute('title') || 'Notification'; | |
| const message = this.getAttribute('message') || ''; | |
| const visible = this.getAttribute('visible') === 'true'; | |
| const typeConfig = { | |
| success: { | |
| iconBg: 'bg-green-100', | |
| iconColor: 'text-green-600', | |
| icon: '<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path><polyline points="22 4 12 14.01 9 11.01"></polyline>' | |
| }, | |
| error: { | |
| iconBg: 'bg-red-100', | |
| iconColor: 'text-red-600', | |
| icon: '<circle cx="12" cy="12" r="10"></circle><line x1="15" y1="9" x2="9" y2="15"></line><line x1="9" y1="9" x2="15" y2="15"></line>' | |
| }, | |
| warning: { | |
| iconBg: 'bg-yellow-100', | |
| iconColor: 'text-yellow-600', | |
| icon: '<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path><line x1="12" y1="9" x2="12" y2="13"></line><line x1="12" y1="17" x2="12.01" y2="17"></line>' | |
| }, | |
| info: { | |
| iconBg: 'bg-blue-100', | |
| iconColor: 'text-blue-600', | |
| icon: '<circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line>' | |
| } | |
| }; | |
| const config = typeConfig[type] || typeConfig.success; | |
| this.shadowRoot.innerHTML = ` | |
| <style> | |
| @import url('https://cdn.tailwindcss.com'); | |
| :host { | |
| display: block; | |
| position: fixed; | |
| bottom: 24px; | |
| right: 24px; | |
| z-index: 9999; | |
| } | |
| .toast { | |
| background: white; | |
| border-radius: 0.5rem; | |
| box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25); | |
| border: 1px solid #e5e7eb; | |
| padding: 1rem; | |
| display: flex; | |
| align-items: center; | |
| gap: 0.75rem; | |
| min-width: 300px; | |
| max-width: 400px; | |
| transform: translateY(100px); | |
| opacity: 0; | |
| transition: all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55); | |
| } | |
| .toast.visible { | |
| transform: translateY(0); | |
| opacity: 1; | |
| } | |
| .icon-container { | |
| width: 2.5rem; | |
| height: 2.5rem; | |
| border-radius: 9999px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| flex-shrink: 0; | |
| } | |
| .close-btn { | |
| color: #9ca3af; | |
| cursor: pointer; | |
| transition: color 0.2s; | |
| } | |
| .close-btn:hover { | |
| color: #6b7280; | |
| } | |
| </style> | |
| <div class="toast ${visible ? 'visible' : ''}"> | |
| <div class="icon-container ${config.iconBg}"> | |
| <svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 ${config.iconColor}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> | |
| ${config.icon} | |
| </svg> | |
| </div> | |
| <div class="flex-1 min-w-0"> | |
| <p class="font-medium text-gray-900 text-sm">${title}</p> | |
| ${message ? `<p class="text-sm text-gray-600 mt-0.5">${message}</p>` : ''} | |
| </div> | |
| <button class="close-btn" onclick="this.getRootNode().host.hide()"> | |
| <svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg> | |
| </button> | |
| </div> | |
| `; | |
| } | |
| } | |
| customElements.define('toast-notification', ToastNotification); |