Skip to content

Commit 4a55dfb

Browse files
committed
feat: Implement an asynchronous beforeNext guard mechanism for step transitions.
1 parent 3d22446 commit 4a55dfb

1 file changed

Lines changed: 36 additions & 10 deletions

File tree

src/lib-ts/index.tsx

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,65 @@
11
import * as React from "react";
22

3+
type BeforeNextGuard = () => boolean | Promise<boolean>;
4+
35
interface IStepsContext {
46
current: number;
57
setCurrent: React.Dispatch<React.SetStateAction<number>>;
68
size: number;
79
setSize: React.Dispatch<React.SetStateAction<number>>;
10+
beforeNextRef: React.MutableRefObject<BeforeNextGuard | undefined>;
811
isLast: boolean;
912
isFirst: boolean;
1013
hasPrev: boolean;
1114
hasNext: boolean;
1215
progress: number;
13-
next: () => void;
16+
next: () => Promise<boolean>;
1417
prev: () => void;
1518
jump: (step: number) => void;
1619
reset: () => void;
1720
}
1821

22+
const noop = () => {};
23+
const noopRef = { current: undefined } as React.MutableRefObject<
24+
BeforeNextGuard | undefined
25+
>;
26+
1927
const StepsContext = React.createContext<IStepsContext>({
2028
current: 1,
21-
setCurrent: () => {},
29+
setCurrent: noop,
2230
size: 0,
23-
setSize: () => {},
31+
setSize: noop,
32+
beforeNextRef: noopRef,
2433
isLast: false,
2534
isFirst: false,
2635
hasPrev: false,
2736
hasNext: false,
2837
progress: 0,
29-
next: () => {},
30-
prev: () => {},
31-
jump: () => {},
32-
reset: () => {},
38+
next: () => Promise.resolve(false),
39+
prev: noop,
40+
jump: noop,
41+
reset: noop,
3342
});
3443

3544
export const StepsProvider: React.FC<React.PropsWithChildren> = ({
3645
children,
3746
}) => {
3847
const [current, setCurrent] = React.useState(1);
3948
const [size, setSize] = React.useState(0);
49+
const beforeNextRef = React.useRef<BeforeNextGuard | undefined>(undefined);
4050

41-
const next = () => {
51+
const next = async () => {
52+
const guard = beforeNextRef.current;
53+
if (guard) {
54+
const allowed = await guard();
55+
if (!allowed) return false;
56+
}
4257
const nextStep = current + 1;
43-
nextStep <= size && setCurrent(nextStep);
58+
if (nextStep <= size) {
59+
setCurrent(nextStep);
60+
return true;
61+
}
62+
return false;
4463
};
4564

4665
const prev = () => {
@@ -68,6 +87,7 @@ export const StepsProvider: React.FC<React.PropsWithChildren> = ({
6887
setCurrent,
6988
size,
7089
setSize,
90+
beforeNextRef,
7191
isLast,
7292
isFirst,
7393
hasPrev,
@@ -94,15 +114,21 @@ export interface StepChangeContext {
94114
export interface StepsProps {
95115
children: React.ReactNode;
96116
onStepChange?: (context?: StepChangeContext) => void;
117+
beforeNext?: BeforeNextGuard;
97118
startsFrom?: number;
98119
}
99120

100121
export const Steps: React.FC<StepsProps> = (props) => {
101122
const stepsContext = React.useContext(StepsContext);
102-
const { current, setCurrent, setSize } = stepsContext;
123+
const { current, setCurrent, setSize, beforeNextRef } = stepsContext;
103124
const [isInitialRender, setIsInitialRender] = React.useState(true);
104125
const prevStepRef = React.useRef(current);
105126

127+
// Register the beforeNext guard into context
128+
React.useEffect(() => {
129+
beforeNextRef.current = props.beforeNext;
130+
}, [props.beforeNext]);
131+
106132
React.useEffect(() => {
107133
setIsInitialRender(false);
108134
const { startsFrom = 1 } = props;

0 commit comments

Comments
 (0)