import { html, render, TemplateResult } from 'lit-html' export interface KvMessageOptions { type?: 'success' | 'error' | 'loading' message: string duration?: number closable?: boolean position?: 'center' | 'right' } class KvMessage extends HTMLElement { private options: KvMessageOptions private timer: number | null = null constructor() { super() this.options = { type: 'success', message: '', duration: 2000, closable: true } } connectedCallback() { this.render() } setOptions(options: KvMessageOptions) { this.options = { ...this.options, ...options } this.render() } private render() { const { type, message, closable } = this.options const getTypeIcon = () => { switch (type) { case 'success': return '✓' case 'error': return '✕' case 'loading': return html`
` default: return '' } } const template: TemplateResult = html` ` render(template, this) if (type !== 'loading' && this.options.duration && this.options.duration > 0) { this.setTimer() } } private setTimer() { if (this.timer) { clearTimeout(this.timer) } this.timer = window.setTimeout(() => { this.remove() }, this.options.duration) } remove() { if (this.timer) { clearTimeout(this.timer) this.timer = null } this.classList.add('removing') setTimeout(() => { if (this.parentNode) { this.parentNode.removeChild(this) } }, 300) } disconnectedCallback() { if (this.timer) { clearTimeout(this.timer) this.timer = null } } } customElements.define('kv-message', KvMessage) export class KvMessageManager { private static instance: KvMessageManager private container: HTMLElement | null = null private defaultPosition: 'center' | 'right' = 'center' static getInstance(): KvMessageManager { if (!KvMessageManager.instance) { KvMessageManager.instance = new KvMessageManager() } return KvMessageManager.instance } setDefaultPosition(position: 'center' | 'right') { this.defaultPosition = position } private getContainer(position?: 'center' | 'right'): HTMLElement { const finalPosition = position || this.defaultPosition if (!this.container) { this.container = document.getElementById('messages') if (!this.container) { this.container = document.createElement('div') this.container.id = 'messages' if (finalPosition === 'center') { this.container.style.cssText = ` position: fixed; top: 20px; left: 50%; transform: translateX(-50%); z-index: 9999; display: flex; gap: 8px; flex-direction: column; align-items: center; pointer-events: none; ` } else { this.container.style.cssText = ` position: fixed; top: 20px; right: 20px; z-index: 9999; display: flex; gap: 8px; flex-direction: column; align-items: flex-end; pointer-events: none; ` } document.body.appendChild(this.container) } } return this.container } show(options: KvMessageOptions): KvMessage { const container = this.getContainer(options.position) const message = document.createElement('kv-message') as KvMessage message.setOptions(options) message.style.cssText = 'pointer-events: auto;' container.appendChild(message) return message } success(message: string, options?: { duration?: number; position?: 'center' | 'right'; closable?: boolean }): KvMessage { return this.show({ type: 'success', message, duration: options?.duration || 2000, position: options?.position, closable: options?.closable }) } error(message: string, options?: { duration?: number; position?: 'center' | 'right'; closable?: boolean }): KvMessage { return this.show({ type: 'error', message, duration: options?.duration || 3000, position: options?.position, closable: options?.closable }) } loading(message: string, options?: { position?: 'center' | 'right'; closable?: boolean }): KvMessage { return this.show({ type: 'loading', message, duration: 0, position: options?.position, closable: options?.closable }) } remove(message: KvMessage) { message.remove() } clear() { const container = this.getContainer() const messages = container.querySelectorAll('kv-message') messages.forEach(message => { (message as KvMessage).remove() }) } } export const createMessage = () => KvMessageManager.getInstance() // 将 createMessage 暴露到全局,以便 HTML 中的 JavaScript 可以使用 declare global { interface Window { createMessage: typeof createMessage } } window.createMessage = createMessage