-
Notifications
You must be signed in to change notification settings - Fork 96
Expand file tree
/
Copy pathwizard.tsx
More file actions
136 lines (115 loc) · 4.45 KB
/
wizard.tsx
File metadata and controls
136 lines (115 loc) · 4.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import React, { useReducer, useEffect, KeyboardEventHandler, KeyboardEvent } from 'react';
import { useFormApi, WizardContext, AnyObject, Field } from '@data-driven-forms/react-form-renderer';
import get from 'lodash/get';
import set from 'lodash/set';
import flattenDeep from 'lodash/flattenDeep';
import handleEnter from './enter-handler';
import reducer, { DYNAMIC_WIZARD_TYPES, findCurrentStep, WizardState } from './reducer';
import selectNext from './select-next';
import { CONDITIONAL_SUBMIT_FLAG } from './consts';
export interface WizardProps extends AnyObject {
fields: Field[];
isDynamic?: boolean;
crossroads?: string[];
Wizard?: React.ComponentType<any>;
component?: any;
initialState?: Partial<WizardState>;
conditionalSubmitFlag?: string;
}
const WizardComponent: React.FC<WizardProps> = ({
fields,
isDynamic,
crossroads,
Wizard: WizardRenderer,
component,
initialState,
conditionalSubmitFlag = CONDITIONAL_SUBMIT_FLAG,
...props
}) => {
const formOptions = useFormApi();
const [state, dispatch] = useReducer(reducer, {
activeStep: fields[0]?.name || '',
prevSteps: [],
activeStepIndex: 0,
maxStepIndex: 0,
...initialState,
isDynamic: isDynamic || fields.some(({ nextStep }) => DYNAMIC_WIZARD_TYPES.includes(typeof nextStep)),
loading: true,
});
useEffect(() => {
dispatch({ type: 'finishLoading', payload: { formOptions, fields } });
}, [fields]); // eslint-disable-line react-hooks/exhaustive-deps
if (state.loading) {
return null;
}
const prepareValues = (values: AnyObject, visitedSteps: string[], getRegisteredFields: () => string[]): AnyObject => {
// Add the final step fields to history
const finalRegisteredFieldsHistory = {
...state.registeredFieldsHistory,
[state.activeStep]: getRegisteredFields(),
};
const finalObject: AnyObject = {};
// Find only visited fields
flattenDeep(
Object.values([...visitedSteps, state.activeStep].reduce((obj: AnyObject, key: string) => ({ ...obj, [key]: finalRegisteredFieldsHistory[key] }), {}))
).forEach((key: string) => set(finalObject, key, get(values, key)));
return finalObject;
};
const onCancel = () => formOptions.onCancel?.(state);
const handleSubmit = (): Promise<Record<string, any>> => {
const result = formOptions.onSubmit?.(
prepareValues(formOptions.getState().values, [...state.prevSteps, state.activeStep], formOptions.getRegisteredFields),
formOptions,
state
);
// Ensure we always return a Promise of an object
if (result === undefined || result === null) {
return Promise.resolve({});
}
// Check if result is a Promise
if (typeof result === 'object' && result && 'then' in result) {
return (result as Promise<any>).then((res: any) => res || {});
}
// Handle non-promise results - cast to any first to avoid TypeScript error
const safeResult = result as any;
return Promise.resolve(typeof safeResult === 'object' && safeResult ? safeResult : {});
};
const jumpToStep = (index: number, valid?: boolean) =>
dispatch({ type: 'jumpToStep', payload: { index, valid, fields, crossroads, formOptions } });
const handlePrev = () => jumpToStep(state.activeStepIndex - 1);
const handleNext = (nextStep: string) => dispatch({ type: 'handleNext', payload: { nextStep, formOptions, fields } });
const setPrevSteps = () => dispatch({ type: 'setPrevSteps', payload: { formOptions, fields } });
const findCurrentStepWrapped = (step: string) => findCurrentStep(step, fields);
const onKeyDown = (e: KeyboardEvent) => handleEnter(e, formOptions, state.activeStep, findCurrentStepWrapped, handleNext, handleSubmit);
if (!WizardRenderer) {
return null;
}
return (
<WizardContext.Provider
value={{
handleNext,
onKeyDown,
setPrevSteps,
currentStep: findCurrentStep(state.activeStep, fields),
jumpToStep,
handlePrev,
formOptions: {
...formOptions,
onCancel,
handleSubmit,
},
navSchema: state.navSchema,
activeStepIndex: state.activeStepIndex,
maxStepIndex: state.maxStepIndex,
isDynamic: state.isDynamic,
crossroads: crossroads,
prevSteps: state.prevSteps,
selectNext,
}}
>
<WizardRenderer conditionalSubmitFlag={conditionalSubmitFlag} {...props} />
</WizardContext.Provider>
);
};
export const wizardProps: AnyObject = {};
export default WizardComponent;