react-use-wizard
Version:
React wizard (stepper) builder without the hassle, powered by hooks.
127 lines (108 loc) • 3.75 kB
JavaScript
import { createContext, memo, useState, useRef, Children, useMemo, isValidElement, createElement, Fragment, useContext } from 'react';
/**
* Log messages in the console with a corresponding urgency
*
* @param level The urgency of the message
* @param message The message to log in the console
*/
const log = (level, message) => {
if (process.env.NODE_ENV !== "production") {
const packageName = '[react-use-wizard]';
switch (level) {
case 'warn':
console.warn(packageName + " " + message);
break;
case 'error':
console.error(packageName + " " + message);
break;
default:
console.log(packageName + " " + message);
}
}
};
const WizardContext = /*#__PURE__*/createContext(null);
WizardContext.displayName = 'WizardContext';
const Wizard = /*#__PURE__*/memo(({
header,
footer,
children,
startIndex = 0
}) => {
const [activeStep, setActiveStep] = useState(startIndex);
const [isLoading, setIsLoading] = useState(false);
const hasNextStep = useRef(true);
const hasPreviousStep = useRef(false);
const nextStepHandler = useRef(() => {});
hasNextStep.current = activeStep < Children.toArray(children).length - 1;
hasPreviousStep.current = activeStep > 0;
const goToNextStep = useRef(stepIndex => {
if (hasNextStep.current) {
setActiveStep(activeStep => stepIndex != null ? stepIndex : activeStep + 1);
}
});
const goToPreviousStep = useRef(step => {
if (hasPreviousStep.current) {
setActiveStep(activeStep => step != null ? step : activeStep - 1);
}
}); // Callback to attach the step handler
const handleStep = useRef(handler => {
nextStepHandler.current = handler;
});
const doNextStep = useRef(async stepIndex => {
if (hasNextStep.current && nextStepHandler.current) {
try {
setIsLoading(true);
await nextStepHandler.current();
setIsLoading(false);
nextStepHandler.current = null;
goToNextStep.current(stepIndex);
} catch (error) {
setIsLoading(false);
throw error;
}
} else {
goToNextStep.current(stepIndex);
}
});
const wizardValue = useMemo(() => ({
nextStep: doNextStep.current,
previousStep: goToPreviousStep.current,
handleStep: handleStep.current,
isLoading,
activeStep,
isFirstStep: !hasPreviousStep.current,
isLastStep: !hasNextStep.current
}), [activeStep, isLoading]);
const activeStepContent = useMemo(() => {
const reactChildren = Children.toArray(children);
if (process.env.NODE_ENV !== "production") {
// No steps passed
if (reactChildren.length === 0) {
log('warn', 'Make sure to pass your steps as children in your <Wizard>');
} // The passed start index is invalid
if (activeStep > reactChildren.length) {
log('warn', 'An invalid startIndex is passed to <Wizard>');
} // Invalid header element
if (header && !isValidElement(header)) {
log('error', 'Invalid header passed to <Wizard>');
} // Invalid footer element
if (footer && !isValidElement(footer)) {
log('error', 'Invalid footer passed to <Wizard>');
}
}
return reactChildren[activeStep];
}, [activeStep, children, header, footer]);
return createElement(WizardContext.Provider, {
value: wizardValue
}, createElement(Fragment, null, header, activeStepContent, footer));
});
const useWizard = () => {
const context = useContext(WizardContext);
if (!context && process.env.NODE_ENV !== "production") {
throw Error('Wrap your step with `Wizard`');
} else {
return context;
}
};
export { Wizard, useWizard };
//# sourceMappingURL=react-use-wizard.esm.js.map