import type { JSX } from "@solidjs/web"; import { type Accessor, createContext, createSignal, useContext, } from "solid-js"; import { SNAPSHOT_KEYS, type Snapshottable, snapshotKeys, } from "../internals/observability/snapshot"; import type { ImageLoadingStatus } from "./avatar-root-context"; export type { ImageLoadingStatus }; /** * Public api returned by {@link createAvatar}. Exposes the live * image-loading status and the setter that `` calls from its * `load` / `error` handlers. */ export interface AvatarApi { /** Reactive accessor for the image-loading status. */ imageLoadingStatus: Accessor; /** Setter — called from image load/error handlers. */ setImageLoadingStatus: (next: ImageLoadingStatus) => void; /** Whether the fallback should render (`status !== "loaded"`). */ showFallback: Accessor; /** Whether the image is fully loaded. */ isLoaded: Accessor; /** Context provider — pair with {@link useAvatar}. */ Provider: (props: { children: JSX.Element }) => JSX.Element; } const AvatarContext = createContext(null); /** * Read the nearest {@link AvatarApi} provided by `c.Provider`. Throws if * called outside a Provider. */ export function useAvatar(): AvatarApi { const ctx = useContext(AvatarContext); if (ctx == null) { throw new Error( "Base UI: useAvatar() must be called inside a .", ); } return ctx; } /** * Solid-native headless avatar primitive. Owns the `imageLoadingStatus` * signal that consumers (or ``) write into from their * `load`/`error` handlers; exposes derived `showFallback` / `isLoaded` * accessors for the typical avatar UI flow. */ export function createAvatar(): AvatarApi { // Status writes happen from DOM event handlers (`` load/error), // outside any tracking scope — a plain createSignal is correct here. const [imageLoadingStatus, setImageLoadingStatus] = createSignal("idle"); const showFallback: Accessor = () => imageLoadingStatus() !== "loaded"; const isLoaded: Accessor = () => imageLoadingStatus() === "loaded"; const api: AvatarApi = { imageLoadingStatus, setImageLoadingStatus, showFallback, isLoaded, Provider: (props) => ( {props.children} ), }; (api as AvatarApi & Snapshottable)[SNAPSHOT_KEYS] = AVATAR_SNAPSHOT_KEYS; return api; } const AVATAR_SNAPSHOT_KEYS = snapshotKeys( "imageLoadingStatus", "showFallback", "isLoaded", );