import type { JSX, ValidComponent } from "@solidjs/web";
import { dynamic } from "@solidjs/web";
import { createMemo, omit, onCleanup, Show, untrack } from "solid-js";
import { useBaseUiId } from "../../internals/base-ui-id";
import { mergeRootProps } from "../../internals/merge-root-props";
import { resolveClassStyle } from "../../internals/resolve-class-style";
import type { BaseUIComponentProps } from "../../internals/types";
import { useIsoLayoutEffect } from "../../internals/use-iso-layout-effect";
import { useToastRootContext } from "../root/toast-root-context";
export interface ToastDescriptionState {
/** The toast's `type` slot (forwarded from the toast object). */
type: string | undefined;
}
export interface ToastDescriptionProps
extends BaseUIComponentProps<"p", ToastDescriptionState> {
/** Tag/component to render. Defaults to `"p"`. */
as?: ValidComponent;
}
/**
* The toast's description. Renders a `
` by default and registers its
* id with `` so the root emits `aria-describedby`.
*/
export function ToastDescription(props: ToastDescriptionProps): JSX.Element {
const ctx = useToastRootContext();
const id = useBaseUiId(
typeof untrack(() => props.id) === "string"
? (untrack(() => props.id) as string)
: undefined,
);
const content = createMemo((): JSX.Element | string | undefined => {
if (props.children != null) return props.children as JSX.Element;
return ctx.toast().description as JSX.Element | string | undefined;
});
const shouldRender = createMemo(() => {
const value = content();
return value != null && value !== "";
});
useIsoLayoutEffect(
() => shouldRender(),
(show) => {
if (show) ctx.setDescriptionId(id);
else if (untrack(ctx.descriptionId) === id)
ctx.setDescriptionId(undefined);
},
);
onCleanup(() => {
if (untrack(ctx.descriptionId) === id) ctx.setDescriptionId(undefined);
});
const userClassStyle = resolveClassStyle(props, () => ({
type: ctx.toast().type,
}));
const userHtmlProps = omit(
props,
"as",
"class",
"style",
"attrs",
"children",
) as Record;
// Plain object — NOT a memo. The bag's getters keep `data-type` / children
// reactive across the spread, so the merge itself reads nothing reactive
// and a memo would add no dependency; worse, reading that memo back in the
// untracked component body trips `[STRICT_READ_UNTRACKED]`. Spread a
// pre-bound variable, not an inline call (`{...mergeRootProps(...)}`), so
// the SSR compiler doesn't take its lazy-thunk path (attribute drop +
// hydration-key shift). See `collapsible-root.tsx`.
const descriptionProps = mergeRootProps(
mergeRootProps(
{
id,
get "data-type"() {
return ctx.toast().type;
},
get children() {
return content();
},
},
userClassStyle,
),
userHtmlProps,
);
const Tag = dynamic(() => props.as ?? "p");
return (
);
}