1"use client";
2
3import type { ToastManagerAddOptions } from "@base-ui/react/toast";
4import type { ComponentPropsWithoutRef, ReactNode } from "react";
5
6import type { ThreadToastData } from "./toast";
7
8export type StackedThreadToastOptions = {
9 type: "error" | "warning" | "success" | "info" | "loading";
10 title: ReactNode;
11 description?: ReactNode;
12 timeout?: number;
13 priority?: "low" | "high";
14 actionProps?: ComponentPropsWithoutRef<"button">;
15 /** Merged into `data`; `actionLayout` is always forced to `"stacked-end"` by the helper. */
16 actionVariant?: ThreadToastData["actionVariant"];
17 data?: Omit<ThreadToastData, "actionLayout">;
18};
19
20/**
21 * Defined `actionProps` that hide a previous toast CTA on `toastManager.update`.
22 * Passing `actionProps: undefined` is a no-op because updates omit undefined keys.
23 */
24export const hiddenToastActionProps = {
25 children: null,
26} as const satisfies Pick<ComponentPropsWithoutRef<"button">, "children">;
27
28/**
29 * Thread toast using the stacked body + bottom action row (copy for errors, CTA on its own row).
30 */
31export function stackedThreadToast(
32 options: StackedThreadToastOptions,
33): ToastManagerAddOptions<ThreadToastData> {
34 const { type, title, description, timeout, priority, actionProps, actionVariant, data } = options;
35
36 // Helper-owned `actionLayout` must win over any caller-provided `data`, so spread
37 // the caller's data first and apply `actionLayout: "stacked-end"` last.
38 const mergedData: ThreadToastData = {
39 ...(data !== undefined ? data : {}),
40 actionLayout: "stacked-end",
41 };
42 if (actionVariant !== undefined) {
43 mergedData.actionVariant = actionVariant;
44 }
45
46 const payload: ToastManagerAddOptions<ThreadToastData> = {
47 type,
48 title,
49 data: mergedData,
50 };
51
52 if (description !== undefined) {
53 payload.description = description;
54 }
55 if (timeout !== undefined) {
56 payload.timeout = timeout;
57 }
58 if (priority !== undefined) {
59 payload.priority = priority;
60 }
61 if (actionProps !== undefined) {
62 payload.actionProps = actionProps;
63 }
64
65 return payload;
66}