All files / src/core/utils toast.ts

0% Statements 0/63
100% Branches 1/1
100% Functions 1/1
0% Lines 0/63

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80                                                                                                                                                               
type ToastType = 'error' | 'warning' | 'success' | 'message';
 
export type ToastOptions = {
  timeoutMs?: number;
  fadeoutMs?: number;
  type?: ToastType;
};
 
let toastContainer: HTMLDivElement | null = null;
 
function getToastContainer(): HTMLDivElement {
  if (toastContainer) return toastContainer;
  toastContainer = document.createElement('div');
  toastContainer.style.position = 'fixed';
  toastContainer.style.top = '16px';
  toastContainer.style.right = '16px';
  toastContainer.style.display = 'flex';
  toastContainer.style.flexDirection = 'column';
  toastContainer.style.alignItems = 'flex-end';
  toastContainer.style.gap = '10px';
  toastContainer.style.zIndex = '9999';
  toastContainer.style.pointerEvents = 'none';
  document.body.appendChild(toastContainer);
  return toastContainer;
}
 
function getToastTheme(type: ToastType) {
  switch (type) {
    case 'error':
      return { icon: '❌', background: 'rgba(120, 20, 20, 0.92)', border: 'rgba(255, 120, 120, 0.6)' };
    case 'warning':
      return { icon: '⚠️', background: 'rgba(120, 90, 20, 0.92)', border: 'rgba(255, 220, 120, 0.6)' };
    case 'success':
      return { icon: '✅', background: 'rgba(20, 110, 50, 0.92)', border: 'rgba(120, 240, 160, 0.6)' };
    default:
      return { icon: 'ℹ️', background: 'rgba(20, 40, 120, 0.92)', border: 'rgba(120, 160, 255, 0.6)' };
  }
}
 
export function showToast(message: string, options: ToastOptions = {}) {
  const timeoutMs = options.timeoutMs ?? 5000;
  const fadeoutMs = options.fadeoutMs ?? 2000;
  const type = options.type ?? 'message';
  const theme = getToastTheme(type);
 
  const toast = document.createElement('div');
  toast.style.display = 'flex';
  toast.style.alignItems = 'center';
  toast.style.gap = '8px';
  toast.style.padding = '12px 14px';
  toast.style.background = theme.background;
  toast.style.color = '#fff';
  toast.style.border = `1px solid ${theme.border}`;
  toast.style.borderRadius = '8px';
  toast.style.fontSize = '13px';
  toast.style.minWidth = '220px';
  toast.style.boxShadow = '0 8px 18px rgba(0, 0, 0, 0.35)';
  toast.style.opacity = '1';
  toast.style.transition = `opacity ${fadeoutMs}ms ease`;
  toast.style.pointerEvents = 'auto';
 
  const icon = document.createElement('span');
  icon.textContent = theme.icon;
  icon.style.fontSize = '14px';
  const text = document.createElement('span');
  text.textContent = message;
 
  toast.append(icon, text);
 
  const container = getToastContainer();
  container.appendChild(toast);
 
  window.setTimeout(() => {
    toast.style.opacity = '0';
    window.setTimeout(() => {
      toast.remove();
    }, fadeoutMs);
  }, timeoutMs);
}