152 lines
5.3 KiB
TypeScript
152 lines
5.3 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"
|
|
import { FixedUiElement } from "../Base/FixedUiElement"
|
|
|
|
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
|
|
}
|
|
}
|