import { Store, UIEventSource } from "../../Logic/UIEventSource" import Combine from "../Base/Combine" import BaseUIElement from "../BaseUIElement" import { SubtleButton } from "../Base/SubtleButton" import Svg from "../../Svg" import Translations from "../i18n/Translations" import { VariableUiElement } from "../Base/VariableUIElement" import Toggle from "../Input/Toggle" import { UIElement } from "../UIElement" import { FixedUiElement } from "../Base/FixedUiElement" export interface FlowStep extends BaseUIElement { readonly IsValid: Store readonly Value: Store } export class FlowPanelFactory { private _initial: FlowStep private _steps: ((x: any) => FlowStep)[] private _stepNames: (string | BaseUIElement)[] private constructor( initial: FlowStep, steps: ((x: any) => FlowStep)[], stepNames: (string | BaseUIElement)[] ) { this._initial = initial this._steps = steps this._stepNames = stepNames } public static start( name: { title: BaseUIElement }, step: FlowStep ): FlowPanelFactory { return new FlowPanelFactory(step, [], [name.title]) } public then( name: string | { title: BaseUIElement }, construct: (t: T) => FlowStep ): FlowPanelFactory { return new FlowPanelFactory( this._initial, this._steps.concat([construct]), this._stepNames.concat([name["title"] ?? name]) ) } public finish( name: string | BaseUIElement, construct: (t: T, backButton?: BaseUIElement) => BaseUIElement ): { flow: BaseUIElement furthestStep: UIEventSource titles: (string | BaseUIElement)[] } { const furthestStep = new UIEventSource(0) // Construct all the flowpanels step by step (in reverse order) const nextConstr: ((t: any, back?: UIElement) => BaseUIElement)[] = this._steps.map( (_) => undefined ) nextConstr.push(construct) for (let i = this._steps.length - 1; i >= 0; i--) { const createFlowStep: (value) => FlowStep = this._steps[i] const isConfirm = i == this._steps.length - 1 nextConstr[i] = (value, backButton) => { const flowStep = createFlowStep(value) furthestStep.setData(i + 1) const panel = new FlowPanel(flowStep, nextConstr[i + 1], backButton, isConfirm) panel.isActive.addCallbackAndRun((active) => { if (active) { furthestStep.setData(i + 1) } }) return panel } } const flow = new FlowPanel(this._initial, nextConstr[0]) flow.isActive.addCallbackAndRun((active) => { if (active) { furthestStep.setData(0) } }) return { flow, furthestStep, titles: this._stepNames, } } } export class FlowPanel extends Toggle { public isActive: UIEventSource constructor( initial: FlowStep, constructNextstep: (input: T, backButton: BaseUIElement) => BaseUIElement, backbutton?: BaseUIElement, isConfirm = false ) { const t = Translations.t.general const currentStepActive = new UIEventSource(true) let nextStep: UIEventSource = new UIEventSource(undefined) const backButtonForNextStep = new SubtleButton(Svg.back_svg(), t.back).onClick(() => { currentStepActive.setData(true) }) let elements: (BaseUIElement | string)[] = [] const isError = new UIEventSource(false) if (initial !== undefined) { // Startup the flow elements = [ initial, new Combine([ backbutton, new Toggle( new SubtleButton( isConfirm ? Svg.checkmark_svg() : Svg.back_svg().SetStyle("transform: rotate(180deg);"), isConfirm ? t.confirm : t.next ).onClick(() => { try { const v = initial.Value.data nextStep.setData(constructNextstep(v, backButtonForNextStep)) currentStepActive.setData(false) } catch (e) { console.error(e) isError.setData(true) } }), new SubtleButton(Svg.invalid_svg(), t.notValid), initial.IsValid ), new Toggle(t.error.SetClass("alert"), undefined, isError), ]).SetClass("flex w-full justify-end space-x-2"), ] } super( new Combine(elements).SetClass("h-full flex flex-col justify-between"), new VariableUiElement(nextStep), currentStepActive ) this.isActive = currentStepActive } }