addons/components/multi_stepper/MultiStepperContext.tsxblame
View source
b69ab311/**
b69ab312 * Copyright (c) Meta Platforms, Inc. and affiliates.
b69ab313 *
b69ab314 * This source code is licensed under the MIT license found in the
b69ab315 * LICENSE file in the root directory of this source tree.
b69ab316 */
b69ab317
b69ab318import type {ReactNode} from 'react';
b69ab319
b69ab3110import {useMemo, useState} from 'react';
b69ab3111
b69ab3112export type StepConfig<TKey> = Readonly<{
b69ab3113 /**
b69ab3114 * Key to identify this step.
b69ab3115 */
b69ab3116 key: TKey;
b69ab3117
b69ab3118 /**
b69ab3119 * Label to display (in the stepper, header, etc.)
b69ab3120 */
b69ab3121 label: ReactNode;
b69ab3122}>;
b69ab3123
b69ab3124export type MultiStepperContext<TKey> = Readonly<{
b69ab3125 /* Getters */
b69ab3126
b69ab3127 /**
b69ab3128 * Gets the current step config.
b69ab3129 */
b69ab3130 getCurrentStep: () => StepConfig<TKey>;
b69ab3131
b69ab3132 /**
b69ab3133 * Gets the step config for the given key.
b69ab3134 */
b69ab3135 getStep: (step: TKey) => StepConfig<TKey> | undefined;
b69ab3136
b69ab3137 /**
b69ab3138 * Gets the index of the current step.
b69ab3139 */
b69ab3140 getStepIndex: () => number;
b69ab3141
b69ab3142 /**
b69ab3143 * Gets the total number of steps.
b69ab3144 */
b69ab3145 getStepCount: () => number;
b69ab3146
b69ab3147 /**
b69ab3148 * Get all step configs in order.
b69ab3149 */
b69ab3150 getAllSteps: () => Array<StepConfig<TKey>>;
b69ab3151
b69ab3152 /* Navigation Methods */
b69ab3153
b69ab3154 /**
b69ab3155 * Go to the step at the given index.
b69ab3156 */
b69ab3157 goToStep: (index: number) => void;
b69ab3158
b69ab3159 /**
b69ab3160 * Go to the step with the given key.
b69ab3161 */
b69ab3162 goToStepByKey: (key: TKey) => void;
b69ab3163
b69ab3164 /**
b69ab3165 * Go to the next step.
b69ab3166 */
b69ab3167 goToNextStep: () => void;
b69ab3168
b69ab3169 /**
b69ab3170 * Go to the previous step.
b69ab3171 */
b69ab3172 goToPreviousStep: () => void;
b69ab3173
b69ab3174 /**
b69ab3175 * Go to the first step.
b69ab3176 */
b69ab3177 goToFirstStep: () => void;
b69ab3178
b69ab3179 /**
b69ab3180 * Go to the last step.
b69ab3181 */
b69ab3182 goToLastStep: () => void;
b69ab3183}>;
b69ab3184
b69ab3185/**
b69ab3186 * Hook to access the current step and navigate to other steps.
b69ab3187 */
b69ab3188export function useMultiStepperContext<TKey>(
b69ab3189 pages: Array<StepConfig<TKey>>,
b69ab3190): MultiStepperContext<TKey> {
b69ab3191 const [currentStep, setCurrentStep] = useState<number>(0);
b69ab3192
b69ab3193 const stepByKey = useMemo(
b69ab3194 () => new Map<TKey, number>(pages.map((page, index) => [page.key, index])),
b69ab3195 [pages],
b69ab3196 );
b69ab3197
b69ab3198 const value = useMemo(
b69ab3199 () => ({
b69ab31100 _steps: pages,
b69ab31101 _currentStep: currentStep,
b69ab31102 _setCurrentStep: setCurrentStep,
b69ab31103
b69ab31104 getCurrentStep: () => pages[currentStep],
b69ab31105
b69ab31106 getStep: (step: TKey) => pages.at(stepByKey.get(step) ?? -1),
b69ab31107
b69ab31108 getStepIndex: () => currentStep,
b69ab31109
b69ab31110 getStepCount: () => pages.length,
b69ab31111
b69ab31112 getAllSteps: () => pages,
b69ab31113
b69ab31114 goToStep: (index: number) => setCurrentStep(index),
b69ab31115
b69ab31116 goToStepByKey: (key: TKey) => {
b69ab31117 const index = stepByKey.get(key);
b69ab31118 if (index != null) {
b69ab31119 setCurrentStep(index);
b69ab31120 }
b69ab31121 },
b69ab31122
b69ab31123 goToNextStep: () => {
b69ab31124 if (currentStep < pages.length - 1) {
b69ab31125 setCurrentStep(currentStep + 1);
b69ab31126 }
b69ab31127 },
b69ab31128
b69ab31129 goToPreviousStep: () => {
b69ab31130 if (currentStep > 0) {
b69ab31131 setCurrentStep(currentStep - 1);
b69ab31132 }
b69ab31133 },
b69ab31134
b69ab31135 goToFirstStep: () => setCurrentStep(0),
b69ab31136
b69ab31137 goToLastStep: () => setCurrentStep(pages.length - 1),
b69ab31138 }),
b69ab31139 [currentStep, pages, stepByKey],
b69ab31140 );
b69ab31141
b69ab31142 return value;
b69ab31143}