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