import React, { useRef, Fragment, useEffect } from 'react' import { createControllerBox, ISchemaVirtualFieldComponentProps, createEffectHook, useFormEffects, useFieldState, IVirtualBoxProps } from '@formily/react-schema-renderer' import { toArr } from '@formily/shared' import { Steps } from 'antd' import { createMatchUpdate } from '../shared' import { IFormStep } from '../types' enum StateMap { ON_FORM_STEP_NEXT = 'onFormStepNext', ON_FORM_STEP_PREVIOUS = 'onFormStepPrevious', ON_FORM_STEP_GO_TO = 'onFormStepGoto', ON_FORM_STEP_CURRENT_CHANGE = 'onFormStepCurrentChange', ON_FORM_STEP_DATA_SOURCE_CHANGED = 'onFormStepDataSourceChanged' } const EffectHooks = { onStepNext$: createEffectHook(StateMap.ON_FORM_STEP_NEXT), onStepPrevious$: createEffectHook(StateMap.ON_FORM_STEP_PREVIOUS), onStepGoto$: createEffectHook(StateMap.ON_FORM_STEP_GO_TO), onStepCurrentChange$: createEffectHook<{ value: number preValue: number }>(StateMap.ON_FORM_STEP_CURRENT_CHANGE) } type ExtendsProps = StateMap & typeof EffectHooks export const FormStep: React.FC> & ExtendsProps = createControllerBox( 'step', ({ form, schema, path, name, children }: ISchemaVirtualFieldComponentProps) => { const { dataSource, ...stepProps } = schema.getExtendsComponentProps() const [{ current }, setFieldState] = useFieldState({ current: stepProps.current || 0 }) const ref = useRef(current) const itemsRef = useRef([]) itemsRef.current = toArr(dataSource) const matchUpdate = createMatchUpdate(name, path) const update = (cur: number) => { form.notify(StateMap.ON_FORM_STEP_CURRENT_CHANGE, { path, name, value: cur, preValue: current }) setFieldState({ current: cur }) } useEffect(() => { form.notify(StateMap.ON_FORM_STEP_DATA_SOURCE_CHANGED, { path, name, value: itemsRef.current }) }, [itemsRef.current.length]) useFormEffects(($, { setFieldState }) => { const updateFields = () => { itemsRef.current.forEach(({ name }, index) => { setFieldState(name, (state: any) => { state.display = index === current }) }) } updateFields() $(StateMap.ON_FORM_STEP_DATA_SOURCE_CHANGED).subscribe( ({ name, path }) => { matchUpdate(name, path, () => { updateFields() }) } ) $(StateMap.ON_FORM_STEP_CURRENT_CHANGE).subscribe( ({ value, name, path }: any = {}) => { matchUpdate(name, path, () => { form.hostUpdate(() => { itemsRef.current.forEach(({ name }, index) => { if (!name) throw new Error( 'FormStep dataSource must include `name` property' ) setFieldState(name, (state: any) => { state.display = index === value }) }) }, true) }) } ) $(StateMap.ON_FORM_STEP_NEXT).subscribe(({ name, path }: any = {}) => { matchUpdate(name, path, () => { form.validate().then(({ errors }) => { if (errors.length === 0) { update( ref.current + 1 > itemsRef.current.length - 1 ? ref.current : ref.current + 1 ) } }) }) }) $(StateMap.ON_FORM_STEP_PREVIOUS).subscribe( ({ name, path }: any = {}) => { matchUpdate(name, path, () => { update(ref.current - 1 < 0 ? ref.current : ref.current - 1) }) } ) $(StateMap.ON_FORM_STEP_GO_TO).subscribe( ({ name, path, value }: any = {}) => { matchUpdate(name, path, () => { if (!(value < 0 || value > itemsRef.current.length)) { update(value) } }) } ) }) ref.current = current return ( {itemsRef.current.map((props, key) => { return })} {' '} {children} ) } ) as any Object.assign(FormStep, StateMap, EffectHooks) export default FormStep