forked from MapComplete/MapComplete
		
	
		
			
				
	
	
		
			151 lines
		
	
	
	
		
			5.2 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			151 lines
		
	
	
	
		
			5.2 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
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"
 | 
						|
 | 
						|
export interface FlowStep<T> extends BaseUIElement {
 | 
						|
    readonly IsValid: Store<boolean>
 | 
						|
    readonly Value: Store<T>
 | 
						|
}
 | 
						|
 | 
						|
export class FlowPanelFactory<T> {
 | 
						|
    private _initial: FlowStep<any>
 | 
						|
    private _steps: ((x: any) => FlowStep<any>)[]
 | 
						|
    private _stepNames: (string | BaseUIElement)[]
 | 
						|
 | 
						|
    private constructor(
 | 
						|
        initial: FlowStep<any>,
 | 
						|
        steps: ((x: any) => FlowStep<any>)[],
 | 
						|
        stepNames: (string | BaseUIElement)[]
 | 
						|
    ) {
 | 
						|
        this._initial = initial
 | 
						|
        this._steps = steps
 | 
						|
        this._stepNames = stepNames
 | 
						|
    }
 | 
						|
 | 
						|
    public static start<TOut>(
 | 
						|
        name: { title: BaseUIElement },
 | 
						|
        step: FlowStep<TOut>
 | 
						|
    ): FlowPanelFactory<TOut> {
 | 
						|
        return new FlowPanelFactory(step, [], [name.title])
 | 
						|
    }
 | 
						|
 | 
						|
    public then<TOut>(
 | 
						|
        name: string | { title: BaseUIElement },
 | 
						|
        construct: (t: T) => FlowStep<TOut>
 | 
						|
    ): FlowPanelFactory<TOut> {
 | 
						|
        return new FlowPanelFactory<TOut>(
 | 
						|
            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<number>
 | 
						|
        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<any> = 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<T> extends Toggle {
 | 
						|
    public isActive: UIEventSource<boolean>
 | 
						|
 | 
						|
    constructor(
 | 
						|
        initial: FlowStep<T>,
 | 
						|
        constructNextstep: (input: T, backButton: BaseUIElement) => BaseUIElement,
 | 
						|
        backbutton?: BaseUIElement,
 | 
						|
        isConfirm = false
 | 
						|
    ) {
 | 
						|
        const t = Translations.t.general
 | 
						|
 | 
						|
        const currentStepActive = new UIEventSource(true)
 | 
						|
 | 
						|
        let nextStep: UIEventSource<BaseUIElement> = new UIEventSource<BaseUIElement>(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
 | 
						|
    }
 | 
						|
}
 |