import { ReadonlySignal, Signal } from "@preact/signals-core"; import { useSignal } from "@preact/signals-react"; import { useSignals } from "@preact/signals-react/runtime"; import { Fragment, createElement, useMemo, ReactNode, useLayoutEffect, useEffect, } from "react"; interface ShowProps { when: Signal | ReadonlySignal | (() => T); fallback?: ReactNode; children: ReactNode | ((value: NonNullable) => ReactNode); } const Item = (props: any) => { useSignals(); return typeof props.children === "function" ? props.children(props.v, props.i) : props.children; }; Item.displayName = "Item"; export function Show(props: ShowProps): JSX.Element | null { useSignals(); const value = typeof props.when === "function" ? props.when() : props.when.value; if (!value) return (props.fallback as JSX.Element) || null; return ; } Show.displayName = "Show"; interface ForProps { each: | Signal> | ReadonlySignal> | (() => Array | Signal> | ReadonlySignal>); fallback?: ReactNode; getKey?: (item: T, index: number) => string | number; children: (value: T, index: number) => ReactNode; } export function For(props: ForProps): JSX.Element | null { useSignals(); const cache = useMemo(() => new Map(), []); const list = (typeof props.each === "function" ? props.each() : props.each) as | Signal> | Array; const listValue = list instanceof Signal ? list.value : list; if (!listValue.length) return (props.fallback as JSX.Element) || null; const removed = new Set(cache.keys()); const items = listValue.map((value, index) => { removed.delete(value); if (!cache.has(value)) { const key = props.getKey ? props.getKey(value, index) : index; const result = ( ); cache.set(value, result); return result; } return cache.get(value); }); removed.forEach(value => { cache.delete(value); }); return createElement(Fragment, { children: items }); } For.displayName = "For"; const useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect; export function useLiveSignal(value: T): Signal { const s = useSignal(value); useIsomorphicLayoutEffect(() => { if (s.peek() !== value) s.value = value; }, [value]); return s; } export function useSignalRef(value: T) { const ref = useSignal(value) as Signal & { current: T }; if (!("current" in ref)) Object.defineProperty(ref, "current", refSignalProto); return ref; } const refSignalProto = { configurable: true, get(this: Signal) { return this.value; }, set(this: Signal, v: any) { this.value = v; }, };