import React, { useRef, Fragment } from 'react' import { createControllerBox, ISchemaVirtualFieldComponentProps, createEffectHook, useFormEffects, useFieldState, IVirtualBoxProps } from '@xformily/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' } 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 items = 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 }) } useFormEffects(($, { setFieldState }) => { items.forEach(({ name }, index) => { setFieldState(name, (state: any) => { state.display = index === current }) }) $(StateMap.ON_FORM_STEP_CURRENT_CHANGE).subscribe( ({ value, name, path }: any = {}) => { matchUpdate(name, path, () => { form.hostUpdate(() => { items.forEach(({ name }, index) => { if (!name) throw new Error( 'FormStep dataSource must include `name` property' ) setFieldState(name, (state: any) => { state.display = index === value }) }) }) }) } ) $(StateMap.ON_FORM_STEP_NEXT).subscribe(({ name, path }: any = {}) => { matchUpdate(name, path, () => { form.validate().then(({ errors }) => { if (errors.length === 0) { update( ref.current + 1 > items.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 > items.length)) { update(value) } }) } ) }) ref.current = current return ( {items.map((props, key) => { return })} {' '} {children} ) } ) as any Object.assign(FormStep, StateMap, EffectHooks) export default FormStep