import { createMemo, untrack, createSignal, onError, children, Accessor, Setter, onCleanup } from "../reactive/signal.js"; import { mapArray, indexArray } from "../reactive/array.js"; import { sharedConfig } from "./hydration.js"; import type { JSX } from "../jsx.js"; /** * creates a list elements from a list * * it receives a map function as its child that receives a list element and an accessor with the index and returns a JSX-Element; if the list is empty, an optional fallback is returned: * ```typescript * No items}> * {(item, index) =>
{item}
} *
* ``` * If you have a list with fixed indices and changing values, consider using `` instead. * * @description https://www.solidjs.com/docs/latest/api#%3Cfor%3E */ export function For(props: { each: readonly T[] | undefined | null | false; fallback?: JSX.Element; children: (item: T, index: Accessor) => U; }) { const fallback = "fallback" in props && { fallback: () => props.fallback }; return createMemo( mapArray(() => props.each, props.children, fallback ? fallback : undefined) ); } /** * Non-keyed iteration over a list creating elements from its items * * To be used if you have a list with fixed indices, but changing values. * ```typescript * No items}> * {(item, index) =>
{item()}
} *
* ``` * If you have a list with changing indices, better use ``. * * @description https://www.solidjs.com/docs/latest/api#%3Cindex%3E */ export function Index(props: { each: readonly T[] | undefined | null | false; fallback?: JSX.Element; children: (item: Accessor, index: number) => U; }) { const fallback = "fallback" in props && { fallback: () => props.fallback }; return createMemo( indexArray(() => props.each, props.children, fallback ? fallback : undefined) ); } /** * Conditionally render its children or an optional fallback component * @description https://www.solidjs.com/docs/latest/api#%3Cshow%3E */ export function Show(props: { when: T | undefined | null | false; keyed: true; fallback?: JSX.Element; children: JSX.Element | ((item: NonNullable) => JSX.Element); }): () => JSX.Element; export function Show(props: { when: T | undefined | null | false; keyed?: false; fallback?: JSX.Element; children: JSX.Element; }): () => JSX.Element; export function Show(props: { when: T | undefined | null | false; keyed?: boolean; fallback?: JSX.Element; children: | JSX.Element | ((item: NonNullable) => JSX.Element); }) { let strictEqual = false; const keyed = props.keyed; const condition = createMemo(() => props.when, undefined, { equals: (a, b) => (strictEqual ? a === b : !a === !b) }); return createMemo(() => { const c = condition(); if (c) { const child = props.children; const fn = typeof child === "function" && child.length > 0; strictEqual = keyed || fn; return fn ? untrack(() => (child as any)(c as T)) : child; } return props.fallback; }) as () => JSX.Element; } type EvalConditions = [number, unknown?, MatchProps?]; /** * switches between content based on mutually exclusive conditions * ```typescript * }> * * * * * * * * ``` * @description https://www.solidjs.com/docs/latest/api#%3Cswitch%3E%2F%3Cmatch%3E */ export function Switch(props: { fallback?: JSX.Element; children: JSX.Element; }): Accessor { let strictEqual = false; let keyed = false; const conditions = children(() => props.children) as unknown as () => MatchProps[], evalConditions = createMemo( (): EvalConditions => { let conds = conditions(); if (!Array.isArray(conds)) conds = [conds]; for (let i = 0; i < conds.length; i++) { const c = conds[i].when; if (c) { keyed = !!conds[i].keyed; return [i, c, conds[i]]; } } return [-1]; }, undefined, { equals: (a, b) => a[0] === b[0] && (strictEqual ? a[1] === b[1] : !a[1] === !b[1]) && a[2] === b[2] } ); return createMemo(() => { const [index, when, cond] = evalConditions(); if (index < 0) return props.fallback; const c = cond!.children; const fn = typeof c === "function" && c.length > 0; strictEqual = keyed || fn; return fn ? untrack(() => (c as any)(when)) : c; }); } export type MatchProps = { when: T | undefined | null | false; keyed?: boolean; children: | JSX.Element | ((item: NonNullable) => JSX.Element); }; /** * selects a content based on condition when inside a `` control flow * ```typescript * * * * ``` * @description https://www.solidjs.com/docs/latest/api#%3Cswitch%3E%2F%3Cmatch%3E */ export function Match(props: { when: T | undefined | null | false; keyed: true; children: JSX.Element | ((item: NonNullable) => JSX.Element); }): JSX.Element; export function Match(props: { when: T | undefined | null | false; keyed?: false; children: JSX.Element; }): JSX.Element; export function Match(props: MatchProps) { return props as unknown as JSX.Element; } let Errors: Set>; export function resetErrorBoundaries() { Errors && [...Errors].forEach(fn => fn()); } /** * catches uncaught errors inside components and renders a fallback content * * Also supports a callback form that passes the error and a reset function: * ```typescript *
Error: {err.toString()}
* }> * *
* ``` * Errors thrown from the fallback can be caught by a parent ErrorBoundary * * @description https://www.solidjs.com/docs/latest/api#%3Cerrorboundary%3E */ export function ErrorBoundary(props: { fallback: JSX.Element | ((err: any, reset: () => void) => JSX.Element); children: JSX.Element; }): Accessor { let err; let v; if ( sharedConfig!.context && sharedConfig!.load && (v = sharedConfig.load(sharedConfig.context.id + sharedConfig.context.count)) ) err = v[0]; const [errored, setErrored] = createSignal(err); Errors || (Errors = new Set()); Errors.add(setErrored); onCleanup(() => Errors.delete(setErrored)); return createMemo(() => { let e: any; if ((e = errored())) { const f = props.fallback; if ("_SOLID_DEV_" && (typeof f !== "function" || f.length == 0)) console.error(e); const res = typeof f === "function" && f.length ? untrack(() => f(e, () => setErrored())) : f; onError(setErrored); return res; } onError(setErrored); return props.children; }) as Accessor; }