@opentui-ui/toast 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +660 -0
- package/dist/icons-DXx_5j_z.mjs +181 -0
- package/dist/icons-DXx_5j_z.mjs.map +1 -0
- package/dist/icons.d.mts +116 -0
- package/dist/icons.d.mts.map +1 -0
- package/dist/icons.mjs +3 -0
- package/dist/index.d.mts +53 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +4 -0
- package/dist/react.d.mts +13 -0
- package/dist/react.d.mts.map +1 -0
- package/dist/react.mjs +14 -0
- package/dist/react.mjs.map +1 -0
- package/dist/state-CoqnQZsz.d.mts +157 -0
- package/dist/state-CoqnQZsz.d.mts.map +1 -0
- package/dist/themes.d.mts +69 -0
- package/dist/themes.d.mts.map +1 -0
- package/dist/themes.mjs +118 -0
- package/dist/themes.mjs.map +1 -0
- package/dist/toaster-CQ5RySDh.mjs +1118 -0
- package/dist/toaster-CQ5RySDh.mjs.map +1 -0
- package/dist/types-BnTc6iEw.d.mts +342 -0
- package/dist/types-BnTc6iEw.d.mts.map +1 -0
- package/package.json +70 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"toaster-CQ5RySDh.mjs","names":["DEFAULT_OFFSET: ToasterOffset","DEFAULT_STYLE: ToastStyle","toast","id: string | number | undefined","result: [\"resolve\", ToastData] | [\"reject\", unknown]","toastSettings: PromiseExtendedResult","styles: Partial<BoxOptions>","result: ToastStyle","toast","toast"],"sources":["../src/constants.ts","../src/state.ts","../src/utils/position.ts","../src/utils/style.ts","../src/renderables/toast.ts","../src/renderables/toaster.ts"],"sourcesContent":["import type { ToasterOffset, ToastOptions, ToastStyle } from \"./types\";\n\n/**\n * Common toast duration presets (in milliseconds)\n *\n * Use these for consistent, readable duration values across your app.\n *\n * | Preset | Duration | Use Case |\n * |--------------|------------|---------------------------|\n * | `SHORT` | 2000ms | Brief confirmations |\n * | `DEFAULT` | 4000ms | Standard notifications |\n * | `LONG` | 6000ms | Important messages |\n * | `EXTENDED` | 10000ms | Critical information |\n * | `PERSISTENT` | Infinity | Requires manual dismissal |\n *\n * @example\n * ```ts\n * import { toast, TOAST_DURATION } from '@opentui-ui/toast';\n *\n * // Quick confirmation\n * toast.success('Copied!', { duration: TOAST_DURATION.SHORT });\n *\n * // Important warning\n * toast.warning('Check your settings', { duration: TOAST_DURATION.LONG });\n *\n * // Critical error that requires acknowledgment\n * toast.error('Connection lost', { duration: TOAST_DURATION.PERSISTENT });\n *\n * // Set as default for all toasts\n * const toaster = new ToasterRenderable(ctx, {\n * toastOptions: { duration: TOAST_DURATION.LONG },\n * });\n * ```\n */\nexport const TOAST_DURATION = {\n /** 2 seconds - for brief confirmations */\n SHORT: 2000,\n /** 4 seconds - default duration */\n DEFAULT: 4000,\n /** 6 seconds - for important messages */\n LONG: 6000,\n /** 10 seconds - for critical information */\n EXTENDED: 10000,\n /** Never auto-dismiss - requires manual dismissal */\n PERSISTENT: Infinity,\n} as const;\n\n/**\n * Time to wait before unmounting a dismissed toast (ms)\n * This allows for any exit effects\n *\n * @internal\n */\nexport const TIME_BEFORE_UNMOUNT = 200;\n\n/**\n * Default toast width (in terminal columns)\n *\n * @internal - Users should set maxWidth in ToasterOptions instead\n */\nexport const TOAST_WIDTH = 60;\n\n/**\n * Default offset from screen edges\n *\n * @internal\n */\nexport const DEFAULT_OFFSET: ToasterOffset = {\n top: 1,\n right: 2,\n bottom: 1,\n left: 2,\n};\n\n/**\n * Default base style for all toasts\n *\n * @internal\n */\nexport const DEFAULT_STYLE: ToastStyle = {\n border: true,\n borderStyle: \"single\",\n borderColor: \"#333333\",\n minHeight: 3,\n paddingX: 1,\n paddingY: 0,\n backgroundColor: \"#1a1a1a\",\n foregroundColor: \"#ffffff\",\n mutedColor: \"#6b7280\",\n};\n\n/**\n * Default toast options including base style and per-type overrides\n *\n * @internal\n */\nexport const DEFAULT_TOAST_OPTIONS = {\n style: DEFAULT_STYLE,\n duration: TOAST_DURATION.DEFAULT,\n default: {\n style: { borderColor: \"#333333\" },\n },\n success: {\n style: { borderColor: \"#22c55e\" },\n },\n error: {\n style: { borderColor: \"#ef4444\" },\n },\n warning: {\n style: { borderColor: \"#f59e0b\" },\n },\n info: {\n style: { borderColor: \"#3b82f6\" },\n },\n loading: {\n style: { borderColor: \"#6b7280\" },\n },\n} satisfies ToastOptions;\n","import type {\n ExternalToast,\n PromiseData,\n PromiseExtendedResult,\n PromiseT,\n Toast,\n ToastToDismiss,\n ToastType,\n} from \"./types\";\n\nlet toastsCounter = 1;\n\ntype TitleT = string | (() => string);\n\n/**\n * Check if data is an HTTP Response object\n */\nfunction isHttpResponse(data: unknown): data is Response {\n return (\n data !== null &&\n typeof data === \"object\" &&\n \"ok\" in data &&\n typeof (data as Response).ok === \"boolean\" &&\n \"status\" in data &&\n typeof (data as Response).status === \"number\"\n );\n}\n\n/**\n * Observer class implementing the pub/sub pattern for toast state management.\n * This is the core of the Sonner-compatible API.\n */\nclass Observer {\n subscribers: Array<(toast: Toast | ToastToDismiss) => void> = [];\n toasts: Toast[] = [];\n dismissedToasts: Set<string | number> = new Set();\n\n /**\n * Subscribe to toast state changes\n */\n subscribe = (\n subscriber: (toast: Toast | ToastToDismiss) => void,\n ): (() => void) => {\n this.subscribers.push(subscriber);\n\n return () => {\n const index = this.subscribers.indexOf(subscriber);\n if (index > -1) {\n this.subscribers.splice(index, 1);\n }\n };\n };\n\n /**\n * Publish a toast to all subscribers\n */\n publish = (data: Toast): void => {\n for (const subscriber of this.subscribers) {\n subscriber(data);\n }\n };\n\n /**\n * Add a new toast\n */\n addToast = (data: Toast): void => {\n this.publish(data);\n this.toasts = [...this.toasts, data];\n };\n\n /**\n * Create a toast (internal method)\n */\n create = (\n data: ExternalToast & {\n message?: TitleT;\n type?: ToastType;\n },\n ): string | number => {\n const { message, ...rest } = data;\n const id =\n typeof data.id === \"number\" || (data.id && data.id.length > 0)\n ? data.id\n : toastsCounter++;\n\n const alreadyExists = this.toasts.find((toast) => toast.id === id);\n const dismissible =\n data.dismissible === undefined ? true : data.dismissible;\n\n // If was dismissed, allow it to be re-shown\n if (this.dismissedToasts.has(id)) {\n this.dismissedToasts.delete(id);\n }\n\n if (alreadyExists) {\n // Update existing toast\n this.toasts = this.toasts.map((toast) => {\n if (toast.id === id) {\n this.publish({ ...toast, ...data, id, title: message });\n return {\n ...toast,\n ...data,\n id,\n dismissible,\n title: message,\n };\n }\n return toast;\n });\n } else {\n // Add new toast\n this.addToast({\n title: message,\n ...rest,\n dismissible,\n id,\n type: data.type ?? \"default\",\n } as Toast);\n }\n\n return id;\n };\n\n /**\n * Dismiss a toast by ID, or all toasts if no ID provided\n *\n * @example\n * ```ts\n * // Dismiss a specific toast\n * const id = toast('Hello');\n * toast.dismiss(id);\n *\n * // Dismiss all toasts\n * toast.dismiss();\n * ```\n */\n dismiss = (id?: string | number): string | number | undefined => {\n if (id !== undefined) {\n this.dismissedToasts.add(id);\n // Use requestAnimationFrame equivalent for terminal (setTimeout with 0)\n setTimeout(() => {\n for (const subscriber of this.subscribers) {\n subscriber({ id, dismiss: true });\n }\n }, 0);\n } else {\n // Dismiss all toasts\n for (const toast of this.toasts) {\n for (const subscriber of this.subscribers) {\n subscriber({ id: toast.id, dismiss: true });\n }\n }\n }\n\n return id;\n };\n\n /**\n * Create a basic message toast\n *\n * @example\n * ```ts\n * toast.message('Hello World');\n * toast.message('With description', { description: 'More details here' });\n * ```\n */\n message = (message: TitleT, data?: ExternalToast): string | number => {\n return this.create({ ...data, message, type: \"default\" });\n };\n\n /**\n * Create an error toast\n *\n * @example\n * ```ts\n * toast.error('Something went wrong');\n * toast.error('Failed to save', { description: 'Please try again' });\n * ```\n */\n error = (message: TitleT, data?: ExternalToast): string | number => {\n return this.create({ ...data, message, type: \"error\" });\n };\n\n /**\n * Create a success toast\n *\n * @example\n * ```ts\n * toast.success('Operation completed!');\n * toast.success('File uploaded', { description: 'document.pdf saved' });\n * ```\n */\n success = (message: TitleT, data?: ExternalToast): string | number => {\n return this.create({ ...data, message, type: \"success\" });\n };\n\n /**\n * Create an info toast\n *\n * @example\n * ```ts\n * toast.info('Did you know?');\n * toast.info('Tip', { description: 'Press Ctrl+S to save' });\n * ```\n */\n info = (message: TitleT, data?: ExternalToast): string | number => {\n return this.create({ ...data, message, type: \"info\" });\n };\n\n /**\n * Create a warning toast\n *\n * @example\n * ```ts\n * toast.warning('Be careful!');\n * toast.warning('Unsaved changes', { description: 'Your work may be lost' });\n * ```\n */\n warning = (message: TitleT, data?: ExternalToast): string | number => {\n return this.create({ ...data, message, type: \"warning\" });\n };\n\n /**\n * Create a loading toast with an animated spinner\n *\n * @example\n * ```ts\n * // Basic loading toast\n * const id = toast.loading('Processing...');\n *\n * // Update to success when done\n * toast.success('Done!', { id });\n *\n * // Or update to error on failure\n * toast.error('Failed', { id });\n * ```\n */\n loading = (message: TitleT, data?: ExternalToast): string | number => {\n return this.create({ ...data, message, type: \"loading\" });\n };\n\n /**\n * Create a promise toast that auto-updates based on promise state\n *\n * Automatically shows loading, success, or error states based on the promise result.\n * Handles HTTP Response objects with non-2xx status codes as errors.\n *\n * @example\n * ```ts\n * // Basic promise toast\n * toast.promise(fetch('/api/data'), {\n * loading: 'Fetching data...',\n * success: 'Data loaded!',\n * error: 'Failed to load data',\n * });\n *\n * // With dynamic messages based on result\n * toast.promise(saveUser(data), {\n * loading: 'Saving user...',\n * success: (user) => `${user.name} saved!`,\n * error: (err) => `Error: ${err.message}`,\n * });\n *\n * // Access the underlying promise result\n * const result = toast.promise(fetchData(), { ... });\n * const data = await result.unwrap();\n * ```\n */\n promise = <ToastData>(\n promise: PromiseT<ToastData>,\n data?: PromiseData<ToastData>,\n ): { unwrap: () => Promise<ToastData> } | undefined => {\n if (!data) {\n return undefined;\n }\n\n let id: string | number | undefined;\n\n if (data.loading !== undefined) {\n id = this.create({\n ...data,\n type: \"loading\",\n message: data.loading,\n description:\n typeof data.description !== \"function\" ? data.description : undefined,\n });\n }\n\n const p = promise instanceof Function ? promise() : promise;\n\n let shouldDismiss = id !== undefined;\n let result: [\"resolve\", ToastData] | [\"reject\", unknown];\n\n const originalPromise = p\n .then(async (response) => {\n result = [\"resolve\", response];\n\n // Handle HTTP error responses\n if (isHttpResponse(response) && !response.ok) {\n shouldDismiss = false;\n\n const promiseData =\n typeof data.error === \"function\"\n ? await data.error(`HTTP error! status: ${response.status}`)\n : data.error;\n\n const description =\n typeof data.description === \"function\"\n ? await data.description(\n `HTTP error! status: ${response.status}` as unknown as ToastData,\n )\n : data.description;\n\n const isExtendedResult =\n typeof promiseData === \"object\" && promiseData !== null;\n\n const toastSettings: PromiseExtendedResult = isExtendedResult\n ? (promiseData as PromiseExtendedResult)\n : { message: promiseData as string };\n\n this.create({ id, type: \"error\", description, ...toastSettings });\n }\n // Handle Error instances\n else if (response instanceof Error) {\n shouldDismiss = false;\n\n const promiseData =\n typeof data.error === \"function\"\n ? await data.error(response)\n : data.error;\n\n const description =\n typeof data.description === \"function\"\n ? await data.description(response as unknown as ToastData)\n : data.description;\n\n const isExtendedResult =\n typeof promiseData === \"object\" && promiseData !== null;\n\n const toastSettings: PromiseExtendedResult = isExtendedResult\n ? (promiseData as PromiseExtendedResult)\n : { message: promiseData as string };\n\n this.create({ id, type: \"error\", description, ...toastSettings });\n }\n // Handle success\n else if (data.success !== undefined) {\n shouldDismiss = false;\n\n const promiseData =\n typeof data.success === \"function\"\n ? await data.success(response)\n : data.success;\n\n const description =\n typeof data.description === \"function\"\n ? await data.description(response)\n : data.description;\n\n const isExtendedResult =\n typeof promiseData === \"object\" && promiseData !== null;\n\n const toastSettings: PromiseExtendedResult = isExtendedResult\n ? (promiseData as PromiseExtendedResult)\n : { message: promiseData as string };\n\n this.create({ id, type: \"success\", description, ...toastSettings });\n }\n })\n .catch(async (error: unknown) => {\n result = [\"reject\", error];\n\n if (data.error !== undefined) {\n shouldDismiss = false;\n\n const promiseData =\n typeof data.error === \"function\"\n ? await data.error(error)\n : data.error;\n\n const description =\n typeof data.description === \"function\"\n ? await data.description(error as ToastData)\n : data.description;\n\n const isExtendedResult =\n typeof promiseData === \"object\" && promiseData !== null;\n\n const toastSettings: PromiseExtendedResult = isExtendedResult\n ? (promiseData as PromiseExtendedResult)\n : { message: promiseData as string };\n\n this.create({ id, type: \"error\", description, ...toastSettings });\n }\n })\n .finally(() => {\n if (shouldDismiss) {\n this.dismiss(id);\n id = undefined;\n }\n\n data.finally?.();\n });\n\n const unwrap = () =>\n new Promise<ToastData>((resolve, reject) =>\n originalPromise\n .then(() =>\n result[0] === \"reject\" ? reject(result[1]) : resolve(result[1]),\n )\n .catch(reject),\n );\n\n if (typeof id !== \"string\" && typeof id !== \"number\") {\n return { unwrap };\n }\n\n return Object.assign(id, { unwrap }) as unknown as {\n unwrap: () => Promise<ToastData>;\n };\n };\n\n /**\n * Get all active (non-dismissed) toasts\n */\n getActiveToasts = (): Toast[] => {\n return this.toasts.filter((toast) => !this.dismissedToasts.has(toast.id));\n };\n}\n\n/**\n * Global toast state singleton\n */\nexport const ToastState = new Observer();\n\n/**\n * Basic toast function - delegates to ToastState.message() for consistent behavior\n */\nconst toastFunction = (\n message: TitleT,\n data?: ExternalToast,\n): string | number => ToastState.message(message, data);\n\n/**\n * Get toast history (all toasts ever created, including dismissed)\n *\n * @example\n * ```ts\n * const history = toast.getHistory();\n * console.log(`Total toasts shown: ${history.length}`);\n * ```\n */\nconst getHistory = () => ToastState.toasts;\n\n/**\n * Get currently active (visible) toasts\n *\n * @example\n * ```ts\n * const active = toast.getToasts();\n * console.log(`Currently showing ${active.length} toasts`);\n * ```\n */\nconst getToasts = () => ToastState.getActiveToasts();\n\n/**\n * The main toast API - a function with methods attached\n *\n * @example\n * ```ts\n * // Basic usage\n * toast('Hello World');\n *\n * // With variants\n * toast.success('Operation completed');\n * toast.error('Something went wrong');\n * toast.warning('Be careful');\n * toast.info('Did you know?');\n * toast.loading('Processing...');\n *\n * // Promise toast\n * toast.promise(fetchData(), {\n * loading: 'Loading...',\n * success: 'Data loaded!',\n * error: 'Failed to load',\n * });\n *\n * // Dismiss\n * const id = toast('Hello');\n * toast.dismiss(id);\n * toast.dismiss(); // dismiss all\n * ```\n */\nexport const toast = Object.assign(\n toastFunction,\n {\n success: ToastState.success,\n info: ToastState.info,\n warning: ToastState.warning,\n error: ToastState.error,\n message: ToastState.message,\n promise: ToastState.promise,\n dismiss: ToastState.dismiss,\n loading: ToastState.loading,\n },\n { getHistory, getToasts },\n);\n","/**\n * Position utilities for toast rendering\n *\n * Handles conversion of position strings to box layout properties.\n */\n\nimport type { BoxOptions } from \"@opentui/core\";\nimport { DEFAULT_OFFSET } from \"../constants\";\nimport type { Position, ToasterOffset } from \"../types\";\n\n/**\n * Convert a Position and offset to BoxOptions for absolute positioning\n *\n * Handles all 6 position variants:\n * - top-left, top-center, top-right\n * - bottom-left, bottom-center, bottom-right\n *\n * @example\n * ```ts\n * getPositionStyles(\"top-right\", { top: 2, right: 3 })\n * // => { position: \"absolute\", top: 2, right: 3, alignItems: \"flex-end\" }\n *\n * getPositionStyles(\"bottom-center\", {})\n * // => { position: \"absolute\", bottom: 1, left: 0, width: \"100%\", alignItems: \"center\" }\n * ```\n */\nexport function getPositionStyles(\n position: Position,\n offset: ToasterOffset = {},\n): Partial<BoxOptions> {\n const [y, x] = position.split(\"-\");\n\n const styles: Partial<BoxOptions> = {\n position: \"absolute\",\n };\n\n // Vertical positioning\n if (y === \"top\") {\n styles.top = offset.top ?? DEFAULT_OFFSET.top;\n } else {\n styles.bottom = offset.bottom ?? DEFAULT_OFFSET.bottom;\n }\n\n // Horizontal positioning\n if (x === \"left\") {\n styles.left = offset.left ?? DEFAULT_OFFSET.left;\n styles.alignItems = \"flex-start\";\n } else if (x === \"center\") {\n // Center horizontally - make container full width and center children\n styles.left = 0;\n styles.width = \"100%\";\n styles.alignItems = \"center\";\n } else {\n styles.right = offset.right ?? DEFAULT_OFFSET.right;\n styles.alignItems = \"flex-end\";\n }\n\n return styles;\n}\n\n/**\n * Check if a position is horizontally centered\n */\nexport function isCenteredPosition(position: Position): boolean {\n return position.includes(\"center\");\n}\n\n/**\n * Check if a position is at the top of the screen\n */\nexport function isTopPosition(position: Position): boolean {\n return position.startsWith(\"top\");\n}\n","/**\n * Style utilities for toast rendering\n *\n * Handles style merging and padding resolution.\n */\n\nimport { DEFAULT_TOAST_OPTIONS } from \"../constants\";\nimport type { ToastOptions, ToastStyle, ToastType } from \"../types\";\n\n/**\n * Resolved padding values (all four sides)\n */\nexport interface ResolvedPadding {\n top: number;\n right: number;\n bottom: number;\n left: number;\n}\n\n/**\n * Resolve padding values with shorthand support\n *\n * Priority: specific > axis shorthand > uniform\n * e.g., paddingLeft > paddingX > padding\n *\n * @example\n * ```ts\n * resolvePadding({ padding: 1 })\n * // => { top: 1, right: 1, bottom: 1, left: 1 }\n *\n * resolvePadding({ paddingX: 2, paddingY: 1 })\n * // => { top: 1, right: 2, bottom: 1, left: 2 }\n *\n * resolvePadding({ padding: 1, paddingLeft: 3 })\n * // => { top: 1, right: 1, bottom: 1, left: 3 }\n * ```\n */\nexport function resolvePadding(style: ToastStyle): ResolvedPadding {\n const x = style.padding ?? style.paddingX ?? 0;\n const y = style.padding ?? style.paddingY ?? 0;\n\n return {\n top: style.paddingTop ?? y,\n right: style.paddingRight ?? x,\n bottom: style.paddingBottom ?? y,\n left: style.paddingLeft ?? x,\n };\n}\n\n/**\n * Merge multiple ToastStyle objects (later wins)\n *\n * Uses shallow Object.assign, so later styles completely\n * override earlier values for the same property.\n *\n * @example\n * ```ts\n * mergeStyles(\n * { borderColor: \"red\", padding: 1 },\n * { borderColor: \"blue\" }\n * )\n * // => { borderColor: \"blue\", padding: 1 }\n * ```\n */\nexport function mergeStyles(\n ...styles: (Partial<ToastStyle> | undefined)[]\n): ToastStyle {\n const result: ToastStyle = {};\n\n for (const style of styles) {\n if (!style) continue;\n Object.assign(result, style);\n }\n\n return result;\n}\n\n/**\n * Compute the final style for a toast by merging all style layers\n *\n * Merges styles in order of increasing specificity:\n * 1. DEFAULT_TOAST_OPTIONS.style (sensible defaults)\n * 2. DEFAULT_TOAST_OPTIONS[type].style (default type colors)\n * 3. toastOptions.style (user's global style)\n * 4. toastOptions[type].style (user's type-specific overrides)\n * 5. toastStyle (per-toast inline style from toast() call)\n *\n * @example\n * ```ts\n * computeToastStyle(\"success\", { style: { paddingX: 2 }, success: { style: { borderColor: \"green\" } } })\n * ```\n */\nexport function computeToastStyle(\n type: ToastType,\n toastOptions?: ToastOptions,\n toastStyle?: Partial<ToastStyle>,\n): ToastStyle {\n // Default styles\n const defaultBaseStyle = DEFAULT_TOAST_OPTIONS.style;\n const defaultTypeStyle = DEFAULT_TOAST_OPTIONS[type]?.style;\n\n // User-provided styles from toastOptions\n const userBaseStyle = toastOptions?.style;\n const userTypeStyle = toastOptions?.[type]?.style;\n\n const computedStyle = mergeStyles(\n defaultBaseStyle,\n defaultTypeStyle,\n userBaseStyle,\n userTypeStyle,\n toastStyle,\n );\n\n // If border is false, clear borderStyle and borderColor for cleaner config\n if (computedStyle.border === false) {\n delete computedStyle.borderStyle;\n delete computedStyle.borderColor;\n }\n\n return computedStyle;\n}\n\n/**\n * Compute the duration for a toast\n *\n * Priority: toast.duration > toastOptions[type].duration > toastOptions.duration > DEFAULT\n */\nexport function computeToastDuration(\n type: ToastType,\n toastOptions?: ToastOptions,\n toastDuration?: number,\n): number {\n // Per-toast duration takes highest priority\n if (toastDuration !== undefined) {\n return toastDuration;\n }\n\n // Type-specific duration from toastOptions\n const typeDuration = toastOptions?.[type]?.duration;\n if (typeDuration !== undefined) {\n return typeDuration;\n }\n\n // Global duration from toastOptions\n if (toastOptions?.duration !== undefined) {\n return toastOptions.duration;\n }\n\n // Default\n return DEFAULT_TOAST_OPTIONS.duration;\n}\n","/**\n * ToastRenderable - A single toast notification component\n *\n * Renders a toast with icon, title, description, action button, and close button.\n */\n\nimport {\n BoxRenderable,\n parseColor,\n type RenderContext,\n TextAttributes,\n TextRenderable,\n} from \"@opentui/core\";\n\nimport { TIME_BEFORE_UNMOUNT, TOAST_WIDTH } from \"../constants\";\nimport {\n DEFAULT_ICONS,\n getLoadingIcon,\n getSpinnerConfig,\n getTypeIcon,\n} from \"../icons\";\nimport type {\n SpinnerConfig,\n Toast,\n ToastIcons,\n ToastOptions,\n ToastStyle,\n} from \"../types\";\nimport { isAction } from \"../types\";\nimport {\n computeToastDuration,\n computeToastStyle,\n resolvePadding,\n} from \"../utils\";\n\n/**\n * Options for creating a ToastRenderable\n */\nexport interface ToastRenderableOptions {\n /** The toast data to render */\n toast: Toast;\n /** Custom icons (merged with defaults), or `false` to disable icons */\n icons?: Partial<ToastIcons> | false;\n /** Toast options (styles, duration, per-type overrides) */\n toastOptions?: ToastOptions;\n /** Global closeButton setting from toaster */\n closeButton?: boolean;\n /** Callback when toast should be removed from container */\n onRemove?: (toast: Toast) => void;\n}\n\n/**\n * ToastRenderable - A single toast notification\n *\n * Renders a toast with:\n * - Icon (based on type, with spinner animation for loading)\n * - Title (bold text)\n * - Description (optional, muted text)\n * - Action button (optional)\n * - Close button (optional)\n *\n * Supports:\n * - Auto-dismiss with configurable duration\n * - Pause/resume timer\n * - Style updates when toast type changes\n * - Spinner animation for loading toasts\n */\nexport class ToastRenderable extends BoxRenderable {\n private _toast: Toast;\n private _icons: ToastIcons | false;\n private _toastOptions?: ToastOptions;\n private _computedStyle: ToastStyle;\n private _closeButton?: boolean;\n private _onRemove?: (toast: Toast) => void;\n\n // Timer management\n private _remainingTime: number;\n private _closeTimerStartTime: number = 0;\n private _lastCloseTimerStartTime: number = 0;\n private _timerHandle: ReturnType<typeof setTimeout> | null = null;\n private _paused: boolean = false;\n private _dismissed: boolean = false;\n\n // Spinner animation for loading toasts\n private _spinnerHandle: ReturnType<typeof setInterval> | null = null;\n private _spinnerFrameIndex: number = 0;\n private _spinnerConfig: SpinnerConfig | null = null;\n\n // Child renderables\n private _iconText: TextRenderable | null = null;\n private _contentBox: BoxRenderable | null = null;\n private _titleText: TextRenderable | null = null;\n private _descriptionText: TextRenderable | null = null;\n private _actionsBox: BoxRenderable | null = null;\n\n constructor(ctx: RenderContext, options: ToastRenderableOptions) {\n // Compute the merged style for this toast\n const computedStyle = computeToastStyle(\n options.toast.type,\n options.toastOptions,\n options.toast.style,\n );\n\n // Compute duration for this toast\n const duration = computeToastDuration(\n options.toast.type,\n options.toastOptions,\n options.toast.duration,\n );\n\n // Resolve padding from computed style\n const padding = resolvePadding(computedStyle);\n\n super(ctx, {\n id: `toast-${options.toast.id}`,\n flexDirection: \"row\",\n gap: 1,\n border: computedStyle.border,\n borderStyle: computedStyle.borderStyle,\n borderColor: computedStyle.borderColor,\n customBorderChars: computedStyle.customBorderChars,\n backgroundColor: computedStyle.backgroundColor,\n minHeight: computedStyle.minHeight,\n maxWidth: computedStyle.maxWidth ?? TOAST_WIDTH,\n minWidth: computedStyle.minWidth,\n paddingTop: padding.top,\n paddingRight: padding.right,\n paddingBottom: padding.bottom,\n paddingLeft: padding.left,\n });\n\n this._toast = options.toast;\n this._icons =\n options.icons === false ? false : { ...DEFAULT_ICONS, ...options.icons };\n this._toastOptions = options.toastOptions;\n this._computedStyle = computedStyle;\n this._closeButton = options.closeButton;\n this._onRemove = options.onRemove;\n this._remainingTime = duration;\n\n this.setupContent();\n\n // Start timer if not infinite duration and not loading type\n if (this._remainingTime !== Infinity && this._toast.type !== \"loading\") {\n this.startTimer();\n }\n\n // Start spinner animation for loading toasts\n if (this._toast.type === \"loading\") {\n this.startSpinner();\n }\n }\n\n /**\n * Set up the toast content (icon, title, description, actions)\n */\n private setupContent(): void {\n const ctx = this.ctx;\n const toast = this._toast;\n const style = this._computedStyle;\n const icons = this._icons;\n\n // Determine icon color (iconColor > borderColor)\n const iconColor = style.iconColor ?? style.borderColor;\n\n // Icon - use spinner frames for loading type, otherwise use static icon\n // Skip icon rendering entirely if icons === false (unless toast has custom icon)\n const isLoading = toast.type === \"loading\";\n\n // Resolve the spinner config for loading toasts\n if (isLoading && icons !== false) {\n this._spinnerConfig = getSpinnerConfig(icons.loading);\n }\n\n const icon =\n toast.icon ??\n (icons === false\n ? undefined\n : isLoading\n ? getLoadingIcon(icons.loading)\n : getTypeIcon(toast.type, icons));\n if (icon) {\n this._iconText = new TextRenderable(ctx, {\n id: `${this.id}-icon`,\n content: icon,\n fg: iconColor,\n flexShrink: 0,\n paddingTop: 0,\n paddingBottom: 0,\n });\n this.add(this._iconText);\n }\n\n // Content container (title + description)\n this._contentBox = new BoxRenderable(ctx, {\n id: `${this.id}-content`,\n flexDirection: \"column\",\n flexGrow: 1,\n flexShrink: 1,\n gap: 0,\n });\n\n // Title\n const title =\n typeof toast.title === \"function\" ? toast.title() : toast.title;\n if (title) {\n this._titleText = new TextRenderable(ctx, {\n id: `${this.id}-title`,\n content: title,\n fg: style.foregroundColor,\n attributes: TextAttributes.BOLD,\n wrapMode: \"word\",\n });\n this._contentBox.add(this._titleText);\n }\n\n // Description\n const description =\n typeof toast.description === \"function\"\n ? toast.description()\n : toast.description;\n if (description) {\n this._descriptionText = new TextRenderable(ctx, {\n id: `${this.id}-description`,\n content: description,\n fg: style.mutedColor,\n wrapMode: \"word\",\n });\n this._contentBox.add(this._descriptionText);\n }\n\n this.add(this._contentBox);\n\n // Actions (action button)\n if (toast.action) {\n this._actionsBox = new BoxRenderable(ctx, {\n id: `${this.id}-actions`,\n flexDirection: \"row\",\n gap: 1,\n flexShrink: 0,\n alignItems: \"center\",\n });\n\n if (toast.action && isAction(toast.action)) {\n const actionText = new TextRenderable(ctx, {\n id: `${this.id}-action`,\n content: `[${toast.action.label}]`,\n fg: style.foregroundColor,\n onMouseUp: () => toast.action?.onClick?.(),\n });\n this._actionsBox.add(actionText);\n }\n\n this.add(this._actionsBox);\n }\n\n // Close button (if enabled globally or per-toast)\n const showCloseButton = toast.closeButton ?? this._closeButton;\n if (showCloseButton && toast.dismissible !== false) {\n const closeIcon = icons === false ? \"×\" : icons.close;\n const closeText = new TextRenderable(ctx, {\n id: `${this.id}-close`,\n content: closeIcon,\n fg: style.mutedColor,\n flexShrink: 0,\n onMouseUp: () => this.dismiss(),\n });\n this.add(closeText);\n }\n }\n\n /**\n * Start the auto-dismiss timer\n */\n private startTimer(): void {\n if (this._remainingTime === Infinity) return;\n\n this._closeTimerStartTime = Date.now();\n\n this._timerHandle = setTimeout(() => {\n this._toast.onAutoClose?.(this._toast);\n this.dismiss();\n }, this._remainingTime);\n }\n\n /**\n * Pause the auto-dismiss timer\n *\n * Call this when the user is interacting with the toast\n * (e.g., hovering over it in a mouse-enabled terminal)\n */\n public pause(): void {\n if (this._paused || this._remainingTime === Infinity) return;\n\n this._paused = true;\n\n if (this._timerHandle) {\n clearTimeout(this._timerHandle);\n this._timerHandle = null;\n }\n\n if (this._lastCloseTimerStartTime < this._closeTimerStartTime) {\n const elapsed = Date.now() - this._closeTimerStartTime;\n this._remainingTime = Math.max(0, this._remainingTime - elapsed);\n }\n\n this._lastCloseTimerStartTime = Date.now();\n }\n\n /**\n * Resume the auto-dismiss timer\n *\n * Call this when the user stops interacting with the toast\n */\n public resume(): void {\n if (!this._paused || this._remainingTime === Infinity) return;\n\n this._paused = false;\n this.startTimer();\n }\n\n /**\n * Start the spinner animation for loading toasts\n */\n private startSpinner(): void {\n if (this._spinnerHandle || !this._spinnerConfig) return;\n\n const { frames, interval } = this._spinnerConfig;\n\n this._spinnerHandle = setInterval(() => {\n this._spinnerFrameIndex = (this._spinnerFrameIndex + 1) % frames.length;\n const frame = frames[this._spinnerFrameIndex];\n if (this._iconText && frame) {\n this._iconText.content = frame;\n this.requestRender();\n }\n }, interval);\n }\n\n /**\n * Stop the spinner animation\n */\n private stopSpinner(): void {\n if (this._spinnerHandle) {\n clearInterval(this._spinnerHandle);\n this._spinnerHandle = null;\n }\n }\n\n /**\n * Dismiss this toast\n *\n * Triggers the onDismiss callback and schedules removal.\n */\n public dismiss(): void {\n if (this._dismissed) return;\n\n this._dismissed = true;\n\n if (this._timerHandle) {\n clearTimeout(this._timerHandle);\n this._timerHandle = null;\n }\n\n this.stopSpinner();\n\n this._toast.onDismiss?.(this._toast);\n\n // Wait a bit before removing (for potential exit effects)\n setTimeout(() => {\n this._onRemove?.(this._toast);\n }, TIME_BEFORE_UNMOUNT);\n }\n\n /**\n * Update the toast data\n *\n * Used for updating an existing toast (e.g., toast.success('done', { id: existingId }))\n */\n public updateToast(toast: Toast): void {\n this._toast = toast;\n\n // Recompute style for the new type\n const computedStyle = computeToastStyle(\n toast.type,\n this._toastOptions,\n toast.style,\n );\n this._computedStyle = computedStyle;\n\n // Update border color based on new type\n if (computedStyle.borderColor) {\n this.borderColor = computedStyle.borderColor;\n }\n\n // Update custom border chars if provided\n if (computedStyle.customBorderChars) {\n this.customBorderChars = computedStyle.customBorderChars;\n }\n\n // Determine icon color\n const iconColor = computedStyle.iconColor ?? computedStyle.borderColor;\n\n // Update icon\n if (this._iconText) {\n const icon =\n toast.icon ??\n (this._icons === false\n ? undefined\n : getTypeIcon(toast.type, this._icons));\n if (icon) {\n this._iconText.content = icon;\n }\n if (iconColor) {\n this._iconText.fg = parseColor(iconColor);\n }\n }\n\n // Update title\n if (this._titleText) {\n const title =\n typeof toast.title === \"function\" ? toast.title() : toast.title;\n if (title) {\n this._titleText.content = title;\n }\n }\n\n // Update description\n if (this._descriptionText) {\n const description =\n typeof toast.description === \"function\"\n ? toast.description()\n : toast.description;\n if (description) {\n this._descriptionText.content = description;\n }\n }\n\n // Handle spinner state changes\n const wasLoading = this._spinnerHandle !== null;\n const isLoading = toast.type === \"loading\";\n\n if (wasLoading && !isLoading) {\n // Transitioning from loading to non-loading: stop spinner\n this.stopSpinner();\n this._spinnerConfig = null;\n } else if (!wasLoading && isLoading) {\n // Transitioning to loading: set up spinner config and start\n if (this._icons !== false) {\n this._spinnerConfig = getSpinnerConfig(this._icons.loading);\n }\n this.startSpinner();\n }\n\n // Reset timer if duration changed and not loading\n if (toast.type !== \"loading\") {\n if (this._timerHandle) {\n clearTimeout(this._timerHandle);\n }\n this._remainingTime = computeToastDuration(\n toast.type,\n this._toastOptions,\n toast.duration,\n );\n if (this._remainingTime !== Infinity) {\n this.startTimer();\n }\n }\n\n this.requestRender();\n }\n\n /**\n * Get the toast data\n */\n public get toast(): Toast {\n return this._toast;\n }\n\n /**\n * Check if toast is dismissed\n */\n public get isDismissed(): boolean {\n return this._dismissed;\n }\n\n /**\n * Clean up on destroy\n */\n public override destroy(): void {\n if (this._timerHandle) {\n clearTimeout(this._timerHandle);\n this._timerHandle = null;\n }\n this.stopSpinner();\n super.destroy();\n }\n}\n","/**\n * ToasterRenderable - Container for toast notifications\n *\n * Manages the display of multiple toasts, subscribes to ToastState,\n * and handles positioning, stacking, and removal.\n */\n\nimport { BoxRenderable, type RenderContext } from \"@opentui/core\";\n\nimport { TOAST_WIDTH } from \"../constants\";\nimport { ToastState } from \"../state\";\nimport type { Toast, ToasterOptions, ToastToDismiss } from \"../types\";\nimport { getPositionStyles, isCenteredPosition, isTopPosition } from \"../utils\";\nimport { ToastRenderable } from \"./toast\";\n\n/**\n * ToasterRenderable - Container for toast notifications\n *\n * Features:\n * - Subscribes to ToastState for automatic toast management\n * - Supports 6 position variants (top/bottom + left/center/right)\n * - Single or stack mode for multiple toasts\n * - Configurable visible toast limit in stack mode\n * - Automatic oldest toast removal when limit exceeded\n *\n * @example\n * ```ts\n * import { ToasterRenderable, toast } from '@opentui-ui/toast';\n *\n * // Basic usage - add to your app once\n * const toaster = new ToasterRenderable(ctx);\n * ctx.root.add(toaster);\n *\n * // Then show toasts from anywhere\n * toast.success('Hello World!');\n * ```\n *\n * @example\n * ```ts\n * // With full configuration\n * const toaster = new ToasterRenderable(ctx, {\n * position: 'top-right',\n * stackingMode: 'stack',\n * visibleToasts: 5,\n * closeButton: true,\n * gap: 1,\n * toastOptions: {\n * style: { backgroundColor: '#1a1a1a' },\n * duration: 5000,\n * success: { style: { borderColor: '#22c55e' } },\n * error: { style: { borderColor: '#ef4444' } },\n * },\n * });\n * ```\n *\n * @example\n * ```ts\n * // With a theme preset\n * import { minimal } from '@opentui-ui/toast/themes';\n *\n * const toaster = new ToasterRenderable(ctx, {\n * ...minimal,\n * position: 'bottom-center',\n * });\n * ```\n */\nexport class ToasterRenderable extends BoxRenderable {\n private _options: ToasterOptions;\n private _toastRenderables: Map<string | number, ToastRenderable> = new Map();\n private _unsubscribe: (() => void) | null = null;\n\n constructor(ctx: RenderContext, options: ToasterOptions = {}) {\n const position = options.position ?? \"bottom-right\";\n const offset = options.offset ?? {};\n const positionStyles = getPositionStyles(position, offset);\n\n // For center positioning, don't constrain the container width\n // The individual toasts will have their own maxWidth\n const isCentered = isCenteredPosition(position);\n\n super(ctx, {\n id: \"toaster\",\n flexDirection: \"column\",\n gap: options.gap ?? 1,\n zIndex: 9999,\n ...(isCentered ? {} : { maxWidth: options.maxWidth ?? TOAST_WIDTH }),\n ...positionStyles,\n });\n\n this._options = options;\n this.subscribe();\n }\n\n /**\n * Subscribe to toast state changes\n */\n private subscribe(): void {\n this._unsubscribe = ToastState.subscribe((toast) => {\n if (\"dismiss\" in toast && (toast as ToastToDismiss).dismiss) {\n this.removeToast((toast as ToastToDismiss).id);\n } else {\n this.addOrUpdateToast(toast as Toast);\n }\n });\n }\n\n /**\n * Add a new toast or update an existing one\n */\n private addOrUpdateToast(toast: Toast): void {\n const existing = this._toastRenderables.get(toast.id);\n\n if (existing) {\n // Update existing toast\n existing.updateToast(toast);\n return;\n }\n\n // Handle stacking mode\n const stackingMode = this._options.stackingMode ?? \"single\";\n if (stackingMode === \"single\") {\n // Dismiss all existing toasts\n for (const [id] of this._toastRenderables) {\n this.removeToast(id);\n }\n } else {\n // Stack mode - limit visible toasts\n const maxVisible = this._options.visibleToasts ?? 3;\n const currentCount = this._toastRenderables.size;\n\n if (currentCount >= maxVisible) {\n // Remove oldest toast(s)\n const toRemove = currentCount - maxVisible + 1;\n const ids = Array.from(this._toastRenderables.keys());\n for (let i = 0; i < toRemove; i++) {\n const id = ids[i];\n if (id !== undefined) {\n this.removeToast(id);\n }\n }\n }\n }\n\n // Create new toast renderable with cascading styles\n const toastRenderable = new ToastRenderable(this.ctx, {\n toast,\n icons: this._options.icons,\n toastOptions: this._options.toastOptions,\n closeButton: this._options.closeButton,\n onRemove: (t) => this.handleToastRemoved(t),\n });\n\n this._toastRenderables.set(toast.id, toastRenderable);\n\n // Add to container (position based on y-axis)\n const position = this._options.position ?? \"bottom-right\";\n if (isTopPosition(position)) {\n // New toasts at the end (bottom of stack)\n this.add(toastRenderable);\n } else {\n // New toasts at the beginning (top of stack, visually at bottom)\n this.add(toastRenderable, 0);\n }\n\n this.requestRender();\n }\n\n /**\n * Remove a toast by ID\n */\n private removeToast(id: string | number): void {\n const toast = this._toastRenderables.get(id);\n if (toast) {\n toast.dismiss();\n }\n }\n\n /**\n * Handle when a toast is fully removed\n */\n private handleToastRemoved(toast: Toast): void {\n const renderable = this._toastRenderables.get(toast.id);\n if (renderable) {\n this._toastRenderables.delete(toast.id);\n this.remove(renderable.id);\n renderable.destroy();\n this.requestRender();\n }\n }\n\n /**\n * Get the current number of visible toasts\n *\n * @example\n * ```ts\n * if (toaster.toastCount > 0) {\n * console.log(`Showing ${toaster.toastCount} notifications`);\n * }\n * ```\n */\n public get toastCount(): number {\n return this._toastRenderables.size;\n }\n\n /**\n * Dismiss all toasts managed by this toaster\n *\n * @example\n * ```ts\n * // Clear all notifications\n * toaster.dismissAll();\n * ```\n */\n public dismissAll(): void {\n for (const [id] of this._toastRenderables) {\n this.removeToast(id);\n }\n }\n\n /**\n * Clean up on destroy\n */\n public override destroy(): void {\n this._unsubscribe?.();\n\n for (const [, renderable] of this._toastRenderables) {\n renderable.destroy();\n }\n this._toastRenderables.clear();\n\n super.destroy();\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
import { BorderCharacters, BorderSides, BorderStyle } from "@opentui/core";
|
|
2
|
+
|
|
3
|
+
//#region src/types.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Toast notification types
|
|
7
|
+
*/
|
|
8
|
+
type ToastType = "default" | "success" | "error" | "warning" | "info" | "loading";
|
|
9
|
+
/**
|
|
10
|
+
* Position options for the toaster
|
|
11
|
+
*/
|
|
12
|
+
type Position = "top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center" | "bottom-right";
|
|
13
|
+
/**
|
|
14
|
+
* Stacking mode for multiple toasts
|
|
15
|
+
*/
|
|
16
|
+
type StackingMode = "single" | "stack";
|
|
17
|
+
/**
|
|
18
|
+
* Action button configuration
|
|
19
|
+
*/
|
|
20
|
+
interface Action {
|
|
21
|
+
label: string;
|
|
22
|
+
onClick: () => void;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Terminal-specific toast styling options
|
|
26
|
+
*
|
|
27
|
+
* These map to terminal box rendering properties and provide
|
|
28
|
+
* intuitive shorthands for common patterns.
|
|
29
|
+
*/
|
|
30
|
+
interface ToastStyle {
|
|
31
|
+
/**
|
|
32
|
+
* Border configuration
|
|
33
|
+
* - `true` = full border (all sides)
|
|
34
|
+
* - `false` = no border
|
|
35
|
+
* - `["left", "right"]` = specific sides only
|
|
36
|
+
*/
|
|
37
|
+
border?: boolean | BorderSides[];
|
|
38
|
+
/**
|
|
39
|
+
* Border color (hex, rgb, or named color)
|
|
40
|
+
*/
|
|
41
|
+
borderColor?: string;
|
|
42
|
+
/**
|
|
43
|
+
* Border style for terminal rendering
|
|
44
|
+
* @default 'single'
|
|
45
|
+
*/
|
|
46
|
+
borderStyle?: BorderStyle;
|
|
47
|
+
/**
|
|
48
|
+
* Custom border characters for full control over border rendering
|
|
49
|
+
*
|
|
50
|
+
* When provided, this overrides the borderStyle setting.
|
|
51
|
+
* Use this to create unique border styles with custom characters.
|
|
52
|
+
*/
|
|
53
|
+
customBorderChars?: BorderCharacters;
|
|
54
|
+
/**
|
|
55
|
+
* Minimum height in terminal rows
|
|
56
|
+
*/
|
|
57
|
+
minHeight?: number;
|
|
58
|
+
/**
|
|
59
|
+
* Maximum width in terminal columns
|
|
60
|
+
*/
|
|
61
|
+
maxWidth?: number;
|
|
62
|
+
/**
|
|
63
|
+
* Minimum width in terminal columns
|
|
64
|
+
*/
|
|
65
|
+
minWidth?: number;
|
|
66
|
+
/**
|
|
67
|
+
* Uniform padding (all sides)
|
|
68
|
+
*/
|
|
69
|
+
padding?: number;
|
|
70
|
+
/**
|
|
71
|
+
* Horizontal padding (left + right)
|
|
72
|
+
*/
|
|
73
|
+
paddingX?: number;
|
|
74
|
+
/**
|
|
75
|
+
* Vertical padding (top + bottom)
|
|
76
|
+
*/
|
|
77
|
+
paddingY?: number;
|
|
78
|
+
/**
|
|
79
|
+
* Top padding
|
|
80
|
+
*/
|
|
81
|
+
paddingTop?: number;
|
|
82
|
+
/**
|
|
83
|
+
* Bottom padding
|
|
84
|
+
*/
|
|
85
|
+
paddingBottom?: number;
|
|
86
|
+
/**
|
|
87
|
+
* Left padding
|
|
88
|
+
*/
|
|
89
|
+
paddingLeft?: number;
|
|
90
|
+
/**
|
|
91
|
+
* Right padding
|
|
92
|
+
*/
|
|
93
|
+
paddingRight?: number;
|
|
94
|
+
/**
|
|
95
|
+
* Background color (hex, rgb, or named color)
|
|
96
|
+
*/
|
|
97
|
+
backgroundColor?: string;
|
|
98
|
+
/**
|
|
99
|
+
* Text/foreground color (hex, rgb, or named color)
|
|
100
|
+
*/
|
|
101
|
+
foregroundColor?: string;
|
|
102
|
+
/**
|
|
103
|
+
* Muted text color (for descriptions)
|
|
104
|
+
*/
|
|
105
|
+
mutedColor?: string;
|
|
106
|
+
/**
|
|
107
|
+
* Icon color override (defaults to borderColor)
|
|
108
|
+
*/
|
|
109
|
+
iconColor?: string;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Per-type toast options
|
|
113
|
+
*
|
|
114
|
+
* Each type can override style and duration.
|
|
115
|
+
*/
|
|
116
|
+
interface TypeToastOptions {
|
|
117
|
+
/** Style overrides for this toast type */
|
|
118
|
+
style?: Partial<ToastStyle>;
|
|
119
|
+
/** Duration override for this toast type */
|
|
120
|
+
duration?: number;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Default options for all toasts, with per-type overrides
|
|
124
|
+
*
|
|
125
|
+
* Similar to react-hot-toast's toastOptions API:
|
|
126
|
+
* @example
|
|
127
|
+
* ```ts
|
|
128
|
+
* toastOptions={{
|
|
129
|
+
* style: { backgroundColor: '#1a1a1a' },
|
|
130
|
+
* duration: 5000,
|
|
131
|
+
* success: {
|
|
132
|
+
* style: { borderColor: '#22c55e' },
|
|
133
|
+
* duration: 3000,
|
|
134
|
+
* },
|
|
135
|
+
* error: {
|
|
136
|
+
* style: { borderColor: '#ef4444' },
|
|
137
|
+
* },
|
|
138
|
+
* }}
|
|
139
|
+
* ```
|
|
140
|
+
*/
|
|
141
|
+
interface ToastOptions {
|
|
142
|
+
/** Base styles applied to all toasts */
|
|
143
|
+
style?: ToastStyle;
|
|
144
|
+
/** Default duration for all toasts (ms) */
|
|
145
|
+
duration?: number;
|
|
146
|
+
/** Options for default toasts */
|
|
147
|
+
default?: TypeToastOptions;
|
|
148
|
+
/** Options for success toasts */
|
|
149
|
+
success?: TypeToastOptions;
|
|
150
|
+
/** Options for error toasts */
|
|
151
|
+
error?: TypeToastOptions;
|
|
152
|
+
/** Options for warning toasts */
|
|
153
|
+
warning?: TypeToastOptions;
|
|
154
|
+
/** Options for info toasts */
|
|
155
|
+
info?: TypeToastOptions;
|
|
156
|
+
/** Options for loading toasts */
|
|
157
|
+
loading?: TypeToastOptions;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Internal toast representation
|
|
161
|
+
*/
|
|
162
|
+
interface Toast {
|
|
163
|
+
id: string | number;
|
|
164
|
+
type: ToastType;
|
|
165
|
+
title?: string | (() => string);
|
|
166
|
+
description?: string | (() => string);
|
|
167
|
+
duration?: number;
|
|
168
|
+
dismissible?: boolean;
|
|
169
|
+
icon?: string;
|
|
170
|
+
action?: Action;
|
|
171
|
+
onDismiss?: (toast: Toast) => void;
|
|
172
|
+
onAutoClose?: (toast: Toast) => void;
|
|
173
|
+
closeButton?: boolean;
|
|
174
|
+
/**
|
|
175
|
+
* Per-toast style overrides (highest priority)
|
|
176
|
+
*/
|
|
177
|
+
style?: Partial<ToastStyle>;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* External toast options (user-facing API)
|
|
181
|
+
*
|
|
182
|
+
* This is the options object passed to `toast()`, `toast.success()`, etc.
|
|
183
|
+
*/
|
|
184
|
+
interface ExternalToast extends Partial<Omit<Toast, "id" | "type" | "title" | "promise">> {
|
|
185
|
+
id?: string | number;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Promise type for toast.promise()
|
|
189
|
+
*/
|
|
190
|
+
type PromiseT<Data = unknown> = Promise<Data> | (() => Promise<Data>);
|
|
191
|
+
/**
|
|
192
|
+
* Extended result type for promise toasts
|
|
193
|
+
*/
|
|
194
|
+
interface PromiseExtendedResult extends ExternalToast {
|
|
195
|
+
message: string;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Promise result - can be a string or extended object
|
|
199
|
+
*/
|
|
200
|
+
type PromiseTResult<Data = unknown> = string | ((data: Data) => string | Promise<string>);
|
|
201
|
+
/**
|
|
202
|
+
* Promise toast configuration
|
|
203
|
+
*/
|
|
204
|
+
interface PromiseData<ToastData = unknown> {
|
|
205
|
+
loading?: string;
|
|
206
|
+
success?: PromiseTResult<ToastData> | PromiseExtendedResult | ((data: ToastData) => PromiseExtendedResult | Promise<PromiseExtendedResult>);
|
|
207
|
+
error?: PromiseTResult | PromiseExtendedResult | ((error: unknown) => PromiseExtendedResult | Promise<PromiseExtendedResult>);
|
|
208
|
+
description?: PromiseTResult<ToastData>;
|
|
209
|
+
finally?: () => void | Promise<void>;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Spinner configuration for animated loading icons
|
|
213
|
+
*
|
|
214
|
+
* @example
|
|
215
|
+
* ```ts
|
|
216
|
+
* // Dots spinner
|
|
217
|
+
* { frames: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"], interval: 80 }
|
|
218
|
+
*
|
|
219
|
+
* // Simple spinner
|
|
220
|
+
* { frames: ["-", "\\", "|", "/"], interval: 100 }
|
|
221
|
+
* ```
|
|
222
|
+
*/
|
|
223
|
+
interface SpinnerConfig {
|
|
224
|
+
/** Array of frames to cycle through */
|
|
225
|
+
frames: string[];
|
|
226
|
+
/** Interval between frames in milliseconds */
|
|
227
|
+
interval: number;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Check if a value is a SpinnerConfig object
|
|
231
|
+
*/
|
|
232
|
+
declare function isSpinnerConfig(value: string | SpinnerConfig): value is SpinnerConfig;
|
|
233
|
+
/**
|
|
234
|
+
* Icon configuration
|
|
235
|
+
*/
|
|
236
|
+
interface ToastIcons {
|
|
237
|
+
success: string;
|
|
238
|
+
error: string;
|
|
239
|
+
warning: string;
|
|
240
|
+
info: string;
|
|
241
|
+
/**
|
|
242
|
+
* Loading icon - can be a static string or an animated spinner config
|
|
243
|
+
*
|
|
244
|
+
* @example
|
|
245
|
+
* ```ts
|
|
246
|
+
* // Static icon
|
|
247
|
+
* loading: "◌"
|
|
248
|
+
*
|
|
249
|
+
* // Animated spinner
|
|
250
|
+
* loading: {
|
|
251
|
+
* frames: ["◜", "◠", "◝", "◞", "◡", "◟"],
|
|
252
|
+
* interval: 100,
|
|
253
|
+
* }
|
|
254
|
+
* ```
|
|
255
|
+
*/
|
|
256
|
+
loading: string | SpinnerConfig;
|
|
257
|
+
close: string;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Offset configuration for toaster positioning
|
|
261
|
+
*/
|
|
262
|
+
interface ToasterOffset {
|
|
263
|
+
top?: number;
|
|
264
|
+
right?: number;
|
|
265
|
+
bottom?: number;
|
|
266
|
+
left?: number;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Toaster component options
|
|
270
|
+
*/
|
|
271
|
+
interface ToasterOptions {
|
|
272
|
+
/**
|
|
273
|
+
* Position of the toaster on screen
|
|
274
|
+
* @default 'bottom-right'
|
|
275
|
+
*/
|
|
276
|
+
position?: Position;
|
|
277
|
+
/**
|
|
278
|
+
* Gap between toasts in terminal rows
|
|
279
|
+
* @default 1
|
|
280
|
+
*/
|
|
281
|
+
gap?: number;
|
|
282
|
+
/**
|
|
283
|
+
* Maximum number of visible toasts in stack mode
|
|
284
|
+
* @default 3
|
|
285
|
+
*/
|
|
286
|
+
visibleToasts?: number;
|
|
287
|
+
/**
|
|
288
|
+
* Show close button on toasts
|
|
289
|
+
* @default false
|
|
290
|
+
*/
|
|
291
|
+
closeButton?: boolean;
|
|
292
|
+
/**
|
|
293
|
+
* Offset from screen edges
|
|
294
|
+
*/
|
|
295
|
+
offset?: ToasterOffset;
|
|
296
|
+
/**
|
|
297
|
+
* Custom icons, or `false` to disable icons entirely
|
|
298
|
+
*
|
|
299
|
+
* @example
|
|
300
|
+
* ```ts
|
|
301
|
+
* // Custom icons
|
|
302
|
+
* icons: { success: "✓", error: "✗" }
|
|
303
|
+
*
|
|
304
|
+
* // Disable icons
|
|
305
|
+
* icons: false
|
|
306
|
+
* ```
|
|
307
|
+
*/
|
|
308
|
+
icons?: Partial<ToastIcons> | false;
|
|
309
|
+
/**
|
|
310
|
+
* How to handle multiple toasts
|
|
311
|
+
* @default 'single'
|
|
312
|
+
*/
|
|
313
|
+
stackingMode?: StackingMode;
|
|
314
|
+
/**
|
|
315
|
+
* Maximum width for toasts
|
|
316
|
+
* @default 60
|
|
317
|
+
*/
|
|
318
|
+
maxWidth?: number;
|
|
319
|
+
/**
|
|
320
|
+
* Default options for toasts (styles, duration, per-type overrides)
|
|
321
|
+
*
|
|
322
|
+
* @example
|
|
323
|
+
* ```ts
|
|
324
|
+
* toastOptions: {
|
|
325
|
+
* style: { backgroundColor: '#1a1a1a' },
|
|
326
|
+
* duration: 5000,
|
|
327
|
+
* success: {
|
|
328
|
+
* style: { borderColor: '#22c55e' },
|
|
329
|
+
* duration: 3000,
|
|
330
|
+
* },
|
|
331
|
+
* }
|
|
332
|
+
* ```
|
|
333
|
+
*/
|
|
334
|
+
toastOptions?: ToastOptions;
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Check if an action object is a valid Action
|
|
338
|
+
*/
|
|
339
|
+
declare function isAction(action: Action | unknown): action is Action;
|
|
340
|
+
//#endregion
|
|
341
|
+
export { isSpinnerConfig as _, PromiseT as a, Toast as c, ToastStyle as d, ToastType as f, isAction as g, TypeToastOptions as h, PromiseData as i, ToastIcons as l, ToasterOptions as m, ExternalToast as n, SpinnerConfig as o, ToasterOffset as p, Position as r, StackingMode as s, Action as t, ToastOptions as u };
|
|
342
|
+
//# sourceMappingURL=types-BnTc6iEw.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types-BnTc6iEw.d.mts","names":[],"sources":["../src/types.ts"],"sourcesContent":[],"mappings":";;;;;;AAKA;AAWY,KAXA,SAAA,GAWQ,SAAA,GAAA,SAAA,GAAA,OAAA,GAAA,SAAA,GAAA,MAAA,GAAA,SAAA;AAWpB;AAKA;AAWA;AAOqB,KAlCT,QAAA,GAkCS,UAAA,GAAA,YAAA,GAAA,WAAA,GAAA,aAAA,GAAA,eAAA,GAAA,cAAA;;;;AAiGJ,KAxHL,YAAA,GAwHqB,QAEf,GAAA,OAAA;AAwBlB;;;AAQY,UArJK,MAAA,CAqJL;EAEF,KAAA,EAAA,MAAA;EAEE,OAAA,EAAA,GAAA,GAAA,IAAA;;;;AAUZ;;;;AAUwB,UAlKP,UAAA,CAkKO;EAKN;;;AAgBlB;;;EACU,MAAA,CAAA,EAAA,OAAA,GAjLW,WAiLX,EAAA;EAAO;AAOjB;;EAAuC,WAAA,CAAA,EAAA,MAAA;EAA+B;;;AAKtE;EAOY,WAAA,CAAA,EAzLI,WAyLU;EAOT;;;;;;EAO0B,iBAAA,CAAA,EA/LrB,gBA+LqB;EAAR;;;EAMxB,SAAA,CAAA,EAAA,MAAA;EAAgC;;;EAC3B,QAAA,CAAA,EAAA,MAAA;EACS;;AAezB;EAUgB,QAAA,CAAA,EAAA,MAAA;EAeC;AA2BjB;AAUA;EAKa,OAAA,CAAA,EAAA,MAAA;EAuBF;;;EAoBM,QAAA,CAAA,EAAA,MAAA;EAuBA;;AAMjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAnRiB,gBAAA;;UAEP,QAAQ;;;;;;;;;;;;;;;;;;;;;;;UAwBD,YAAA;;UAEP;;;;YAIE;;YAEA;;UAEF;;YAEE;;SAEH;;YAEG;;;;;UAMK,KAAA;;QAET;;;;;;WAMG;sBACW;wBACE;;;;;UAKd,QAAQ;;;;;;;UAgBD,aAAA,SACP,QAAQ,KAAK;;;;;;KAOX,2BAA2B,QAAQ,eAAe,QAAQ;;;;UAKrD,qBAAA,SAA8B;;;;;;KAOnC,kDAEA,kBAAkB;;;;UAKb;;YAGX,eAAe,aACf,gCAEQ,cACH,wBAAwB,QAAQ;UAErC,iBACA,6CAGK,wBAAwB,QAAQ;gBAC3B,eAAe;yBACN;;;;;;;;;;;;;;UAeR,aAAA;;;;;;;;;iBAUD,eAAA,iBACE,yBACN;;;;UAaK,UAAA;;;;;;;;;;;;;;;;;;;;oBAoBG;;;;;;UAOH,aAAA;;;;;;;;;UAUA,cAAA;;;;;aAKJ;;;;;;;;;;;;;;;;;;;WAuBF;;;;;;;;;;;;;UAcD,QAAQ;;;;;iBAMD;;;;;;;;;;;;;;;;;;;;;iBAuBA;;;;;iBAMD,QAAA,SAAiB,6BAA6B"}
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@opentui-ui/toast",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "A beautiful toast library for terminal UIs built on OpenTUI",
|
|
5
|
+
"author": "Matt Simpson",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/msmps/opentui-ui.git",
|
|
11
|
+
"directory": "packages/toast"
|
|
12
|
+
},
|
|
13
|
+
"type": "module",
|
|
14
|
+
"module": "./dist/index.mjs",
|
|
15
|
+
"types": "./dist/index.d.mts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.mts",
|
|
19
|
+
"import": "./dist/index.mjs"
|
|
20
|
+
},
|
|
21
|
+
"./themes": {
|
|
22
|
+
"types": "./dist/themes.d.mts",
|
|
23
|
+
"import": "./dist/themes.mjs"
|
|
24
|
+
},
|
|
25
|
+
"./icons": {
|
|
26
|
+
"types": "./dist/icons.d.mts",
|
|
27
|
+
"import": "./dist/icons.mjs"
|
|
28
|
+
},
|
|
29
|
+
"./react": {
|
|
30
|
+
"types": "./dist/react.d.mts",
|
|
31
|
+
"import": "./dist/react.mjs"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"bugs": {
|
|
35
|
+
"url": "https://github.com/msmps/opentui-ui/issues"
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"dist"
|
|
39
|
+
],
|
|
40
|
+
"homepage": "https://github.com/msmps/opentui-ui#readme",
|
|
41
|
+
"keywords": [
|
|
42
|
+
"toast",
|
|
43
|
+
"notification",
|
|
44
|
+
"tui",
|
|
45
|
+
"terminal",
|
|
46
|
+
"cli",
|
|
47
|
+
"opentui",
|
|
48
|
+
"sonner"
|
|
49
|
+
],
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build": "tsdown",
|
|
52
|
+
"clean": "rm -rf dist"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@opentui/core": "^0.1.63",
|
|
56
|
+
"@opentui/react": "^0.1.63",
|
|
57
|
+
"@types/bun": "latest",
|
|
58
|
+
"tsdown": "^0.18.2",
|
|
59
|
+
"typescript": "^5"
|
|
60
|
+
},
|
|
61
|
+
"peerDependencies": {
|
|
62
|
+
"@opentui/core": "^0.1.63",
|
|
63
|
+
"@opentui/react": "^0.1.63"
|
|
64
|
+
},
|
|
65
|
+
"peerDependenciesMeta": {
|
|
66
|
+
"@opentui/react": {
|
|
67
|
+
"optional": true
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|