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