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 ( ); }