import * as d3 from 'd3'; export type ToastType = 'success' | 'error'; export type ToastController = { show: (message: string, type?: ToastType, duration?: number) => void; }; export function createToast(selector: string): ToastController { const toast = d3.select(selector); let dismissTimer: ReturnType | null = null; const dismiss = () => { toast.classed('show', false); setTimeout(() => { toast.selectAll('*').remove(); toast.text('').classed('success', false).classed('error', false).classed('dismissible', false); }, 300); }; const show = (message: string, type: ToastType = 'success', duration = 3000) => { if (dismissTimer) { clearTimeout(dismissTimer); dismissTimer = null; } toast.selectAll('*').remove(); toast .classed('success', type === 'success') .classed('error', type === 'error') .classed('dismissible', type === 'error') .classed('show', true); if (type === 'error') { toast.append('span').attr('class', 'toast-message').text(message); toast.append('button') .attr('class', 'toast-close') .attr('type', 'button') .attr('aria-label', 'Close') .text('×') .on('click', () => dismiss()); } else { toast.text(message); dismissTimer = setTimeout(dismiss, duration); } }; return { show }; }