import type { Fn , NumOrString } from "@thi.ng/api" ; import { assert } from "@thi.ng/errors/assert" ; import type { ISubscribable } from "@thi.ng/rstream" ; import type { IComponent , IMountWithState , NumOrElement } from "./api.js" ; import { $compile } from "./compile.js" ; import { Component } from "./component.js" ; import { $sub } from "./sub.js" ; import { $wrapText } from "./wrap.js" ; remarks Subscribes to `src`, then calls `keyFn` for each received value and uses
 * result to call one of the given `ctors` async component factories. The value
 * returned from the chosen factory will be passsed to  $compile and then
 * mounted in place of this `$switch` wrapper. If an uncaught error occurs in
 * the selected component factory, the `error` component factory will be used
 * instead (which is expected to succeed).
 *
 * When a new value is received from `src`, the currently active inner component
 * (if any) will always be fist `unmount`ed and if the optional `loader` is
 * given, it will be temporarily mounted whilst the actual `ctor` component
 * factory executes. This is can be used to show a pre-loaders.
 *
 * **IMPORTANT:** When a `null` or `undefined` value is received from `src`, the
 * currently active inner component will be unmounted and the optional loader
 * shown. However, no other inner component constructor will be called in this
 * case (until the next valid/non-null value is received).
 *
 * All component factories are async functions to facilitate dynamic `import()`
 * / code splitting and other async initializations (WASM etc.)
 *
 *  example param src param keyFn param ctors param error param loader export const $switch = < T > ( src , keyFn , ctors , error ? , loader ? ) => $sub < T > ( src , new Switch < T > ( keyFn , ctors , error , loader ) ) ; /**
 * Syntax sugar for  $switch for cases when there's only a single
 * component which should transition through its entire lifecycle for
 * each reactive value change.
 *
 *  remarks In other words, for each new value received from `src`. The wrapped
 * component will first be unmounted, an optional pre-`loader` shown
 * whilst the async `ctor` component factory executes, its result
 * `$compile`d and then getting re-mounted. See  $switch for
 * further details.
 *
 *  example param src param ctor param error param loader export const $refresh = < T > ( src , ctor , error ? , loader ? ) => $switch ( src , ( ) => 0 , { 0 : ctor } , error , loader ) ; export class Switch < T > extends Component implements IMountWithState < T > { protected val ? : T ; protected parent ? : Element ; protected inner ? : IComponent < T > ; protected index ? : NumOrElement ; constructor ( protected keyFn , protected ctors , protected error = async ( e ) => $wrapText ( "span" , { } , e ) , protected loader = async ( ) => $wrapText ( "span" , { hidden : true , } ) ) { super ( ) ; } async mount ( parent , index , val ) { this . parent = parent ; this . index = index ; await this . update ( val ) ; return this . inner ! . el ! ; } async unmount ( ) { this . inner && ( await this . inner ! . unmount ( ) ) ; this . val = undefined ; this . parent = undefined ; this . inner = undefined ; } async update ( val ) { this . inner && ( await this . inner . unmount ( ) ) ; this . inner = undefined ; if ( val != null ) { this . val = val ; let loader ; if ( this . loader ) { loader = $compile ( await this . loader ( val ) ) ; await loader . mount ( this . parent ! , this . index ! ) ; } try { const key = this . keyFn ( val ) ; const next = this . ctors [ key ] ; assert ( ! ! next , `missing component for key: ${ key }` ) ; this . inner = $compile ( await next ( val ) ) ; loader && ( await loader . unmount ( ) ) ; } catch ( e ) { if ( this . error ) { this . inner = $compile ( await this . error ( < Error > e ) ) ; loader && ( await loader . unmount ( ) ) ; } } } else { this . loader && ( this . inner = $compile ( await this . loader ( val ) ) ) ; } this . inner && ( await this . inner . mount ( this . parent ! , this . index ! ) ) ; } }