export type Step<FormOpts, StepId extends string> = {
id: StepId;
dependencies?: StepId[];
shouldRender: (state: FormState<FormOpts>) => boolean;
render: (props: { form: Form<FormOpts> }) => React.ReactNode;
};
function shouldRenderStep<FormOpts, StepId extends string>(
step: Step<FormOpts, StepId>,
state: FormState<FormOpts>,
allSteps: Step<FormOpts, StepId>[]
): boolean {
if (!step.shouldRender(state)) {
return false;
}
const dependencies = step.dependencies ?? [];
for (const depId of dependencies) {
const dependentStep = allSteps.find((s) => s.id === depId);
if (!dependentStep) {
throw new Error(`Dependency step with id "${depId}" not found.`);
}
if (!dependentStep.shouldRender(state)) {
return false;
}
}
return true;
}
export function StepperForm<FormOpts, StepId extends string>({
form,
steps,
}: {
form: Form<FormOpts>;
steps: Step<FormOpts, StepId>[];
}) {
return (
<>
{steps.map((step) => (
<form.Subscribe
selector={(state) => shouldRenderStep(step, state, steps)}
key={step.id}
>
{(shouldRender) => shouldRender && <step.render form={form} />}
</form.Subscribe>
))}
</>
);
}
export type Step<FormOpts, StepId extends string> = {
id: StepId;
dependencies?: StepId[];
shouldRender: (state: FormState<FormOpts>) => boolean;
render: (props: { form: Form<FormOpts> }) => React.ReactNode;
};
function shouldRenderStep<FormOpts, StepId extends string>(
step: Step<FormOpts, StepId>,
state: FormState<FormOpts>,
allSteps: Step<FormOpts, StepId>[]
): boolean {
if (!step.shouldRender(state)) {
return false;
}
const dependencies = step.dependencies ?? [];
for (const depId of dependencies) {
const dependentStep = allSteps.find((s) => s.id === depId);
if (!dependentStep) {
throw new Error(`Dependency step with id "${depId}" not found.`);
}
if (!dependentStep.shouldRender(state)) {
return false;
}
}
return true;
}
export function StepperForm<FormOpts, StepId extends string>({
form,
steps,
}: {
form: Form<FormOpts>;
steps: Step<FormOpts, StepId>[];
}) {
return (
<>
{steps.map((step) => (
<form.Subscribe
selector={(state) => shouldRenderStep(step, state, steps)}
key={step.id}
>
{(shouldRender) => shouldRender && <step.render form={form} />}
</form.Subscribe>
))}
</>
);
}