"use client"; import type { ToastManagerAddOptions } from "@base-ui/react/toast";import type { ComponentPropsWithoutRef, ReactNode } from "react"; import type { ThreadToastData } from "./toast"; export type StackedThreadToastOptions = { type: "error" | "warning" | "success" | "info" | "loading"; title: ReactNode; description?: ReactNode; timeout?: number; priority?: "low" | "high"; actionProps?: ComponentPropsWithoutRef<"button">; /** Merged into `data`; `actionLayout` is always forced to `"stacked-end"` by the helper. */ actionVariant?: ThreadToastData["actionVariant"]; data?: Omit<ThreadToastData, "actionLayout">;}; /** * Defined `actionProps` that hide a previous toast CTA on `toastManager.update`. * Passing `actionProps: undefined` is a no-op because updates omit undefined keys. */export const hiddenToastActionProps = { children: null,} as const satisfies Pick<ComponentPropsWithoutRef<"button">, "children">; /** * Thread toast using the stacked body + bottom action row (copy for errors, CTA on its own row). */export function stackedThreadToast( options: StackedThreadToastOptions,): ToastManagerAddOptions<ThreadToastData> { const { type, title, description, timeout, priority, actionProps, actionVariant, data } = options; // Helper-owned `actionLayout` must win over any caller-provided `data`, so spread // the caller's data first and apply `actionLayout: "stacked-end"` last. const mergedData: ThreadToastData = { ...(data !== undefined ? data : {}), actionLayout: "stacked-end", }; if (actionVariant !== undefined) { mergedData.actionVariant = actionVariant; } const payload: ToastManagerAddOptions<ThreadToastData> = { type, title, data: mergedData, }; if (description !== undefined) { payload.description = description; } if (timeout !== undefined) { payload.timeout = timeout; } if (priority !== undefined) { payload.priority = priority; } if (actionProps !== undefined) { payload.actionProps = actionProps; } return payload;}
"use client";
import type { ToastManagerAddOptions } from "@base-ui/react/toast";
import type { ComponentPropsWithoutRef, ReactNode } from "react";
import type { ThreadToastData } from "./toast";
export type StackedThreadToastOptions = {
type: "error" | "warning" | "success" | "info" | "loading";
title: ReactNode;
description?: ReactNode;
timeout?: number;
priority?: "low" | "high";
actionProps?: ComponentPropsWithoutRef<"button">;
/** Merged into `data`; `actionLayout` is always forced to `"stacked-end"` by the helper. */
actionVariant?: ThreadToastData["actionVariant"];
data?: Omit<ThreadToastData, "actionLayout">;
};
/**
* Defined `actionProps` that hide a previous toast CTA on `toastManager.update`.
* Passing `actionProps: undefined` is a no-op because updates omit undefined keys.
*/
export const hiddenToastActionProps = {
children: null,
} as const satisfies Pick<ComponentPropsWithoutRef<"button">, "children">;
/**
* Thread toast using the stacked body + bottom action row (copy for errors, CTA on its own row).
*/
export function stackedThreadToast(
options: StackedThreadToastOptions,
): ToastManagerAddOptions<ThreadToastData> {
const { type, title, description, timeout, priority, actionProps, actionVariant, data } = options;
// Helper-owned `actionLayout` must win over any caller-provided `data`, so spread
// the caller's data first and apply `actionLayout: "stacked-end"` last.
const mergedData: ThreadToastData = {
...(data !== undefined ? data : {}),
actionLayout: "stacked-end",
};
if (actionVariant !== undefined) {
mergedData.actionVariant = actionVariant;
}
const payload: ToastManagerAddOptions<ThreadToastData> = {
type,
title,
data: mergedData,
};
if (description !== undefined) {
payload.description = description;
}
if (timeout !== undefined) {
payload.timeout = timeout;
}
if (priority !== undefined) {
payload.priority = priority;
}
if (actionProps !== undefined) {
payload.actionProps = actionProps;
}
return payload;
}