+ Add basic app steps system and style. * Rename steps counter to avoid collision with app steps.
109 lines
2.5 KiB
TypeScript
109 lines
2.5 KiB
TypeScript
import React, {useEffect} from "react";
|
|
import {
|
|
GlobalStateProvider,
|
|
useGlobalStateReducers, useGlobalStateValue,
|
|
} from "../../GlobalState";
|
|
import {usePreviousValue} from "../../Utils";
|
|
import {StepKeyType, stepsGlobalState, useCurrentStepKey, useStepsNavigator} from "./StepsContext";
|
|
|
|
|
|
|
|
/**
|
|
* Main Steps component.
|
|
*/
|
|
export function Steps({children}: React.PropsWithChildren<{}>)
|
|
{
|
|
return (
|
|
<GlobalStateProvider globalState={stepsGlobalState}>
|
|
<div className={"steps"}>
|
|
<StepsNavigatorComponent />
|
|
|
|
<div>
|
|
{children}
|
|
</div>
|
|
</div>
|
|
</GlobalStateProvider>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Steps navigator component.
|
|
*/
|
|
export function StepsNavigatorComponent()
|
|
{
|
|
// Get the steps navigator functions.
|
|
const stepsNavigator = useStepsNavigator();
|
|
|
|
// Get the current steps state.
|
|
const stepsState = useGlobalStateValue(stepsGlobalState);
|
|
|
|
// Get the current step.
|
|
const currentStep = useCurrentStepKey();
|
|
|
|
return (
|
|
<nav className={"steps"}>
|
|
<ul>
|
|
{ // Showing a button for each step.
|
|
stepsState.steps.map((step, index) => (
|
|
// Rendering the current step button.
|
|
<li key={step.key} className={currentStep == step.key ? "active" : undefined}>
|
|
<button type={"button"} onClick={() => {
|
|
stepsNavigator.set(step.key);
|
|
}}>
|
|
{step.title ?? (index + 1)}
|
|
</button>
|
|
</li>
|
|
))
|
|
}
|
|
</ul>
|
|
</nav>
|
|
);
|
|
}
|
|
|
|
|
|
/**
|
|
* Component of a step.
|
|
*/
|
|
export function Step({stepKey, stepTitle, children}: React.PropsWithChildren<{
|
|
/**
|
|
* The current step unique key.
|
|
*/
|
|
stepKey: StepKeyType;
|
|
|
|
/**
|
|
* The step title, to show in the navigator.
|
|
*/
|
|
stepTitle?: React.ReactNode;
|
|
}>)
|
|
{
|
|
// Get the global state reducers class.
|
|
const stepsGlobalStateReducers = useGlobalStateReducers(stepsGlobalState);
|
|
|
|
// Get the previous step key.
|
|
const previousStepKey = usePreviousValue(stepKey);
|
|
|
|
useEffect(() => {
|
|
// Remove the previous step key, if there is one.
|
|
if (previousStepKey) stepsGlobalStateReducers.removeStep(previousStepKey);
|
|
|
|
// Register the current step key.
|
|
stepsGlobalStateReducers.registerStep(stepKey, stepTitle);
|
|
|
|
// Remove the step key when component is removed.
|
|
return () => stepsGlobalStateReducers.removeStep(stepKey);
|
|
}, [stepsGlobalStateReducers, previousStepKey, stepTitle]);
|
|
|
|
// Get the current step key.
|
|
const currentStep = useCurrentStepKey();
|
|
|
|
if (currentStep != stepKey)
|
|
// If this step is not the current one, rendering nothing.
|
|
return undefined;
|
|
|
|
// Rendering the current step.
|
|
return (
|
|
<section className={"step"}>
|
|
{children}
|
|
</section>
|
|
);
|
|
}
|