import type { Component as ComponentType } from 'svelte'; import { writable } from 'svelte/store'; export enum NotificationType { Info = 'Info', Error = 'Error', Warning = 'Warning', } export type NotificationButton = { text: string; onClick: () => unknown; }; export type Notification = { id: number; type: NotificationType; message: string; /** The action to take when the notification is clicked */ action: NotificationAction; button?: NotificationButton; /** Timeout in milliseconds */ timeout: number; }; type DiscardAction = { type: 'discard' }; type NoopAction = { type: 'noop' }; export type NotificationAction = DiscardAction | NoopAction; type Props = Record; type Component = { type: ComponentType; props: T; }; type BaseNotificationOptions = Partial> & Pick; export type NotificationOptions = BaseNotificationOptions; export type ComponentNotificationOptions = BaseNotificationOptions< ComponentNotification, 'component' >; // eslint-disable-next-line @typescript-eslint/no-explicit-any export type ComponentNotification = Omit & { component: Component; }; export const isComponentNotification = ( notification: Notification | ComponentNotification, ): notification is ComponentNotification => { return 'component' in notification; }; function createNotificationList() { const notificationList = writable<(Notification | ComponentNotification)[]>([]); let count = 1; const show = (options: T extends Props ? ComponentNotificationOptions : NotificationOptions) => { notificationList.update((currentList) => { currentList.push({ id: count++, type: NotificationType.Info, action: { type: options.button ? 'noop' : 'discard', }, timeout: 3000, ...options, }); return currentList; }); }; const removeNotificationById = (id: number) => { notificationList.update((currentList) => currentList.filter((n) => n.id !== id)); }; return { show, removeNotificationById, notificationList, }; } export const notificationController = createNotificationList();