#
tokens: 49730/50000 14/1152 files (page 17/35)
lines: off (toggle) GitHub
raw markdown copy
This is page 17 of 35. Use http://codebase.md/alibaba/formily?lines=false&page={x} to view the full context.

# Directory Structure

```
├── .all-contributorsrc
├── .codecov.yml
├── .editorconfig
├── .eslintignore
├── .eslintrc
├── .github
│   ├── CONTRIBUTING.md
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE
│   │   └── config.yml
│   ├── PULL_REQUEST_TEMPLATE.md
│   └── workflows
│       ├── check-pr-title.yml
│       ├── ci.yml
│       ├── commitlint.yml
│       ├── issue-open-check.yml
│       ├── package-size.yml
│       └── pr-welcome.yml
├── .gitignore
├── .prettierrc.js
├── .umirc.js
├── .vscode
│   └── cspell.json
├── .yarnrc
├── CHANGELOG.md
├── commitlint.config.js
├── devtools
│   ├── .eslintrc
│   └── chrome-extension
│       ├── .npmignore
│       ├── assets
│       │   └── img
│       │       ├── loading.svg
│       │       └── logo
│       │           ├── 128x128.png
│       │           ├── 16x16.png
│       │           ├── 38x38.png
│       │           ├── 48x48.png
│       │           ├── error.png
│       │           ├── gray.png
│       │           └── scalable.png
│       ├── config
│       │   ├── webpack.base.ts
│       │   ├── webpack.dev.ts
│       │   └── webpack.prod.ts
│       ├── LICENSE.md
│       ├── package.json
│       ├── src
│       │   ├── app
│       │   │   ├── components
│       │   │   │   ├── FieldTree.tsx
│       │   │   │   ├── filter.ts
│       │   │   │   ├── LeftPanel.tsx
│       │   │   │   ├── RightPanel.tsx
│       │   │   │   ├── SearchBox.tsx
│       │   │   │   └── Tabs.tsx
│       │   │   ├── demo.tsx
│       │   │   └── index.tsx
│       │   └── extension
│       │       ├── backend.ts
│       │       ├── background.ts
│       │       ├── content.ts
│       │       ├── devpanel.tsx
│       │       ├── devtools.tsx
│       │       ├── inject.ts
│       │       ├── manifest.json
│       │       ├── popup.tsx
│       │       └── views
│       │           ├── devpanel.ejs
│       │           ├── devtools.ejs
│       │           └── popup.ejs
│       ├── tsconfig.build.json
│       └── tsconfig.json
├── docs
│   ├── functions
│   │   ├── contributors.ts
│   │   └── npm-search.ts
│   ├── guide
│   │   ├── advanced
│   │   │   ├── async.md
│   │   │   ├── async.zh-CN.md
│   │   │   ├── build.md
│   │   │   ├── build.zh-CN.md
│   │   │   ├── business-logic.md
│   │   │   ├── business-logic.zh-CN.md
│   │   │   ├── calculator.md
│   │   │   ├── calculator.zh-CN.md
│   │   │   ├── controlled.md
│   │   │   ├── controlled.zh-CN.md
│   │   │   ├── custom.md
│   │   │   ├── custom.zh-CN.md
│   │   │   ├── destructor.md
│   │   │   ├── destructor.zh-CN.md
│   │   │   ├── input.less
│   │   │   ├── layout.md
│   │   │   ├── layout.zh-CN.md
│   │   │   ├── linkages.md
│   │   │   ├── linkages.zh-CN.md
│   │   │   ├── validate.md
│   │   │   └── validate.zh-CN.md
│   │   ├── contribution.md
│   │   ├── contribution.zh-CN.md
│   │   ├── form-builder.md
│   │   ├── form-builder.zh-CN.md
│   │   ├── index.md
│   │   ├── index.zh-CN.md
│   │   ├── issue-helper.md
│   │   ├── issue-helper.zh-CN.md
│   │   ├── learn-formily.md
│   │   ├── learn-formily.zh-CN.md
│   │   ├── quick-start.md
│   │   ├── quick-start.zh-CN.md
│   │   ├── scenes
│   │   │   ├── dialog-drawer.md
│   │   │   ├── dialog-drawer.zh-CN.md
│   │   │   ├── edit-detail.md
│   │   │   ├── edit-detail.zh-CN.md
│   │   │   ├── index.less
│   │   │   ├── login-register.md
│   │   │   ├── login-register.zh-CN.md
│   │   │   ├── more.md
│   │   │   ├── more.zh-CN.md
│   │   │   ├── query-list.md
│   │   │   ├── query-list.zh-CN.md
│   │   │   ├── step-form.md
│   │   │   ├── step-form.zh-CN.md
│   │   │   ├── tab-form.md
│   │   │   ├── tab-form.zh-CN.md
│   │   │   └── VerifyCode.tsx
│   │   ├── upgrade.md
│   │   └── upgrade.zh-CN.md
│   ├── index.md
│   ├── index.zh-CN.md
│   └── site
│       ├── Contributors.less
│       ├── Contributors.tsx
│       ├── QrCode.less
│       ├── QrCode.tsx
│       ├── Section.less
│       ├── Section.tsx
│       └── styles.less
├── global.config.ts
├── jest.config.js
├── lerna.json
├── LICENSE.md
├── package.json
├── packages
│   ├── .eslintrc
│   ├── antd
│   │   ├── __tests__
│   │   │   ├── moment.spec.ts
│   │   │   └── sideEffects.spec.ts
│   │   ├── .npmignore
│   │   ├── .umirc.js
│   │   ├── build-style.ts
│   │   ├── create-style.ts
│   │   ├── docs
│   │   │   ├── components
│   │   │   │   ├── ArrayCards.md
│   │   │   │   ├── ArrayCards.zh-CN.md
│   │   │   │   ├── ArrayCollapse.md
│   │   │   │   ├── ArrayCollapse.zh-CN.md
│   │   │   │   ├── ArrayItems.md
│   │   │   │   ├── ArrayItems.zh-CN.md
│   │   │   │   ├── ArrayTable.md
│   │   │   │   ├── ArrayTable.zh-CN.md
│   │   │   │   ├── ArrayTabs.md
│   │   │   │   ├── ArrayTabs.zh-CN.md
│   │   │   │   ├── Cascader.md
│   │   │   │   ├── Cascader.zh-CN.md
│   │   │   │   ├── Checkbox.md
│   │   │   │   ├── Checkbox.zh-CN.md
│   │   │   │   ├── DatePicker.md
│   │   │   │   ├── DatePicker.zh-CN.md
│   │   │   │   ├── Editable.md
│   │   │   │   ├── Editable.zh-CN.md
│   │   │   │   ├── Form.md
│   │   │   │   ├── Form.zh-CN.md
│   │   │   │   ├── FormButtonGroup.md
│   │   │   │   ├── FormButtonGroup.zh-CN.md
│   │   │   │   ├── FormCollapse.md
│   │   │   │   ├── FormCollapse.zh-CN.md
│   │   │   │   ├── FormDialog.md
│   │   │   │   ├── FormDialog.zh-CN.md
│   │   │   │   ├── FormDrawer.md
│   │   │   │   ├── FormDrawer.zh-CN.md
│   │   │   │   ├── FormGrid.md
│   │   │   │   ├── FormGrid.zh-CN.md
│   │   │   │   ├── FormItem.md
│   │   │   │   ├── FormItem.zh-CN.md
│   │   │   │   ├── FormLayout.md
│   │   │   │   ├── FormLayout.zh-CN.md
│   │   │   │   ├── FormStep.md
│   │   │   │   ├── FormStep.zh-CN.md
│   │   │   │   ├── FormTab.md
│   │   │   │   ├── FormTab.zh-CN.md
│   │   │   │   ├── index.md
│   │   │   │   ├── index.zh-CN.md
│   │   │   │   ├── Input.md
│   │   │   │   ├── Input.zh-CN.md
│   │   │   │   ├── NumberPicker.md
│   │   │   │   ├── NumberPicker.zh-CN.md
│   │   │   │   ├── Password.md
│   │   │   │   ├── Password.zh-CN.md
│   │   │   │   ├── PreviewText.md
│   │   │   │   ├── PreviewText.zh-CN.md
│   │   │   │   ├── Radio.md
│   │   │   │   ├── Radio.zh-CN.md
│   │   │   │   ├── Reset.md
│   │   │   │   ├── Reset.zh-CN.md
│   │   │   │   ├── Select.md
│   │   │   │   ├── Select.zh-CN.md
│   │   │   │   ├── SelectTable.md
│   │   │   │   ├── SelectTable.zh-CN.md
│   │   │   │   ├── Space.md
│   │   │   │   ├── Space.zh-CN.md
│   │   │   │   ├── Submit.md
│   │   │   │   ├── Submit.zh-CN.md
│   │   │   │   ├── Switch.md
│   │   │   │   ├── Switch.zh-CN.md
│   │   │   │   ├── TimePicker.md
│   │   │   │   ├── TimePicker.zh-CN.md
│   │   │   │   ├── Transfer.md
│   │   │   │   ├── Transfer.zh-CN.md
│   │   │   │   ├── TreeSelect.md
│   │   │   │   ├── TreeSelect.zh-CN.md
│   │   │   │   ├── Upload.md
│   │   │   │   └── Upload.zh-CN.md
│   │   │   ├── index.md
│   │   │   └── index.zh-CN.md
│   │   ├── LICENSE.md
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── rollup.config.js
│   │   ├── src
│   │   │   ├── __builtins__
│   │   │   │   ├── hooks
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── useClickAway.ts
│   │   │   │   │   └── usePrefixCls.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── loading.ts
│   │   │   │   ├── moment.ts
│   │   │   │   ├── pickDataProps.ts
│   │   │   │   ├── portal.tsx
│   │   │   │   ├── render.ts
│   │   │   │   └── sort.tsx
│   │   │   ├── array-base
│   │   │   │   ├── index.tsx
│   │   │   │   ├── style.less
│   │   │   │   └── style.ts
│   │   │   ├── array-cards
│   │   │   │   ├── index.tsx
│   │   │   │   ├── style.less
│   │   │   │   └── style.ts
│   │   │   ├── array-collapse
│   │   │   │   ├── index.tsx
│   │   │   │   ├── style.less
│   │   │   │   └── style.ts
│   │   │   ├── array-items
│   │   │   │   ├── index.tsx
│   │   │   │   ├── style.less
│   │   │   │   └── style.ts
│   │   │   ├── array-table
│   │   │   │   ├── index.tsx
│   │   │   │   ├── style.less
│   │   │   │   └── style.ts
│   │   │   ├── array-tabs
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── cascader
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── checkbox
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── date-picker
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── editable
│   │   │   │   ├── index.tsx
│   │   │   │   ├── style.less
│   │   │   │   └── style.ts
│   │   │   ├── form
│   │   │   │   ├── index.tsx
│   │   │   │   ├── style.less
│   │   │   │   └── style.ts
│   │   │   ├── form-button-group
│   │   │   │   ├── index.tsx
│   │   │   │   ├── style.less
│   │   │   │   └── style.ts
│   │   │   ├── form-collapse
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── form-dialog
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── form-drawer
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── form-grid
│   │   │   │   ├── index.tsx
│   │   │   │   ├── style.less
│   │   │   │   └── style.ts
│   │   │   ├── form-item
│   │   │   │   ├── animation.less
│   │   │   │   ├── grid.less
│   │   │   │   ├── index.tsx
│   │   │   │   ├── style.less
│   │   │   │   └── style.ts
│   │   │   ├── form-layout
│   │   │   │   ├── index.tsx
│   │   │   │   ├── style.less
│   │   │   │   ├── style.ts
│   │   │   │   └── useResponsiveFormLayout.ts
│   │   │   ├── form-step
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── form-tab
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── index.ts
│   │   │   ├── input
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── number-picker
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── password
│   │   │   │   ├── index.tsx
│   │   │   │   ├── PasswordStrength.tsx
│   │   │   │   └── style.ts
│   │   │   ├── preview-text
│   │   │   │   ├── index.tsx
│   │   │   │   ├── style.less
│   │   │   │   └── style.ts
│   │   │   ├── radio
│   │   │   │   ├── index.tsx
│   │   │   │   ├── style.less
│   │   │   │   └── style.ts
│   │   │   ├── reset
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── select
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── select-table
│   │   │   │   ├── index.tsx
│   │   │   │   ├── style.less
│   │   │   │   ├── style.ts
│   │   │   │   ├── useCheckSlackly.tsx
│   │   │   │   ├── useFilterOptions.tsx
│   │   │   │   ├── useFlatOptions.tsx
│   │   │   │   ├── useSize.tsx
│   │   │   │   ├── useTitleAddon.tsx
│   │   │   │   └── utils.ts
│   │   │   ├── space
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── style.less
│   │   │   ├── style.ts
│   │   │   ├── submit
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── switch
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── time-picker
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── transfer
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── tree-select
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   └── upload
│   │   │       ├── index.tsx
│   │   │       ├── placeholder.ts
│   │   │       └── style.ts
│   │   ├── tsconfig.build.json
│   │   └── tsconfig.json
│   ├── benchmark
│   │   ├── .npmignore
│   │   ├── .umirc.js
│   │   ├── LICENSE.md
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── src
│   │   │   └── index.tsx
│   │   ├── template.ejs
│   │   ├── tsconfig.build.json
│   │   ├── tsconfig.json
│   │   ├── webpack.base.ts
│   │   ├── webpack.dev.ts
│   │   └── webpack.prod.ts
│   ├── core
│   │   ├── .npmignore
│   │   ├── .umirc.js
│   │   ├── docs
│   │   │   ├── api
│   │   │   │   ├── entry
│   │   │   │   │   ├── ActionResponse.less
│   │   │   │   │   ├── ActionResponse.tsx
│   │   │   │   │   ├── createForm.md
│   │   │   │   │   ├── createForm.zh-CN.md
│   │   │   │   │   ├── FieldEffectHooks.md
│   │   │   │   │   ├── FieldEffectHooks.zh-CN.md
│   │   │   │   │   ├── FormChecker.md
│   │   │   │   │   ├── FormChecker.zh-CN.md
│   │   │   │   │   ├── FormEffectHooks.md
│   │   │   │   │   ├── FormEffectHooks.zh-CN.md
│   │   │   │   │   ├── FormHooksAPI.md
│   │   │   │   │   ├── FormHooksAPI.zh-CN.md
│   │   │   │   │   ├── FormPath.md
│   │   │   │   │   ├── FormPath.zh-CN.md
│   │   │   │   │   ├── FormValidatorRegistry.md
│   │   │   │   │   └── FormValidatorRegistry.zh-CN.md
│   │   │   │   └── models
│   │   │   │       ├── ArrayField.md
│   │   │   │       ├── ArrayField.zh-CN.md
│   │   │   │       ├── Field.md
│   │   │   │       ├── Field.zh-CN.md
│   │   │   │       ├── Form.md
│   │   │   │       ├── Form.zh-CN.md
│   │   │   │       ├── ObjectField.md
│   │   │   │       ├── ObjectField.zh-CN.md
│   │   │   │       ├── Query.md
│   │   │   │       ├── Query.zh-CN.md
│   │   │   │       ├── VoidField.md
│   │   │   │       └── VoidField.zh-CN.md
│   │   │   ├── guide
│   │   │   │   ├── architecture.md
│   │   │   │   ├── architecture.zh-CN.md
│   │   │   │   ├── field.md
│   │   │   │   ├── field.zh-CN.md
│   │   │   │   ├── form.md
│   │   │   │   ├── form.zh-CN.md
│   │   │   │   ├── index.md
│   │   │   │   ├── index.zh-CN.md
│   │   │   │   ├── mvvm.md
│   │   │   │   ├── mvvm.zh-CN.md
│   │   │   │   ├── values.md
│   │   │   │   └── values.zh-CN.md
│   │   │   ├── index.md
│   │   │   └── index.zh-CN.md
│   │   ├── LICENSE.md
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── rollup.config.js
│   │   ├── src
│   │   │   ├── __tests__
│   │   │   │   ├── array.spec.ts
│   │   │   │   ├── effects.spec.ts
│   │   │   │   ├── externals.spec.ts
│   │   │   │   ├── field.spec.ts
│   │   │   │   ├── form.spec.ts
│   │   │   │   ├── graph.spec.ts
│   │   │   │   ├── heart.spec.ts
│   │   │   │   ├── internals.spec.ts
│   │   │   │   ├── lifecycle.spec.ts
│   │   │   │   ├── object.spec.ts
│   │   │   │   ├── shared.ts
│   │   │   │   └── void.spec.ts
│   │   │   ├── effects
│   │   │   │   ├── index.ts
│   │   │   │   ├── onFieldEffects.ts
│   │   │   │   └── onFormEffects.ts
│   │   │   ├── global.d.ts
│   │   │   ├── index.ts
│   │   │   ├── models
│   │   │   │   ├── ArrayField.ts
│   │   │   │   ├── BaseField.ts
│   │   │   │   ├── Field.ts
│   │   │   │   ├── Form.ts
│   │   │   │   ├── Graph.ts
│   │   │   │   ├── Heart.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── LifeCycle.ts
│   │   │   │   ├── ObjectField.ts
│   │   │   │   ├── Query.ts
│   │   │   │   ├── types.ts
│   │   │   │   └── VoidField.ts
│   │   │   ├── shared
│   │   │   │   ├── checkers.ts
│   │   │   │   ├── constants.ts
│   │   │   │   ├── effective.ts
│   │   │   │   ├── externals.ts
│   │   │   │   └── internals.ts
│   │   │   └── types.ts
│   │   ├── tsconfig.build.json
│   │   └── tsconfig.json
│   ├── element
│   │   ├── .npmignore
│   │   ├── build-style.ts
│   │   ├── create-style.ts
│   │   ├── docs
│   │   │   ├── .vuepress
│   │   │   │   ├── components
│   │   │   │   │   ├── createCodeSandBox.js
│   │   │   │   │   ├── dumi-previewer.vue
│   │   │   │   │   └── highlight.js
│   │   │   │   ├── config.js
│   │   │   │   ├── enhanceApp.js
│   │   │   │   ├── styles
│   │   │   │   │   └── index.styl
│   │   │   │   └── util.js
│   │   │   ├── demos
│   │   │   │   ├── guide
│   │   │   │   │   ├── array-cards
│   │   │   │   │   │   ├── effects-json-schema.vue
│   │   │   │   │   │   ├── effects-markup-schema.vue
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   └── markup-schema.vue
│   │   │   │   │   ├── array-collapse
│   │   │   │   │   │   ├── effects-json-schema.vue
│   │   │   │   │   │   ├── effects-markup-schema.vue
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   └── markup-schema.vue
│   │   │   │   │   ├── array-items
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   └── markup-schema.vue
│   │   │   │   │   ├── array-table
│   │   │   │   │   │   ├── effects-json-schema.vue
│   │   │   │   │   │   ├── effects-markup-schema.vue
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   └── markup-schema.vue
│   │   │   │   │   ├── array-tabs
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   └── markup-schema.vue
│   │   │   │   │   ├── cascader
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   ├── markup-schema.vue
│   │   │   │   │   │   └── template.vue
│   │   │   │   │   ├── checkbox
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   ├── markup-schema.vue
│   │   │   │   │   │   └── template.vue
│   │   │   │   │   ├── date-picker
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   ├── markup-schema.vue
│   │   │   │   │   │   └── template.vue
│   │   │   │   │   ├── editable
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   ├── markup-schema.vue
│   │   │   │   │   │   └── template.vue
│   │   │   │   │   ├── form-button-group.vue
│   │   │   │   │   ├── form-collapse
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   └── markup-schema.vue
│   │   │   │   │   ├── form-dialog
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   ├── markup-schema.vue
│   │   │   │   │   │   └── template.vue
│   │   │   │   │   ├── form-drawer
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   ├── markup-schema.vue
│   │   │   │   │   │   └── template.vue
│   │   │   │   │   ├── form-grid
│   │   │   │   │   │   ├── form.vue
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   ├── markup-schema.vue
│   │   │   │   │   │   └── native.vue
│   │   │   │   │   ├── form-item
│   │   │   │   │   │   ├── bordered-none.vue
│   │   │   │   │   │   ├── common.vue
│   │   │   │   │   │   ├── feedback.vue
│   │   │   │   │   │   ├── inset.vue
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   ├── markup-schema.vue
│   │   │   │   │   │   ├── size.vue
│   │   │   │   │   │   └── template.vue
│   │   │   │   │   ├── form-layout
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   ├── markup-schema.vue
│   │   │   │   │   │   └── template.vue
│   │   │   │   │   ├── form-step
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   └── markup-schema.vue
│   │   │   │   │   ├── form-tab
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   └── markup-schema.vue
│   │   │   │   │   ├── form.vue
│   │   │   │   │   ├── input
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   ├── markup-schema.vue
│   │   │   │   │   │   └── template.vue
│   │   │   │   │   ├── input-number
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   ├── markup-schema.vue
│   │   │   │   │   │   └── template.vue
│   │   │   │   │   ├── password
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   ├── markup-schema.vue
│   │   │   │   │   │   └── template.vue
│   │   │   │   │   ├── preview-text
│   │   │   │   │   │   ├── base.vue
│   │   │   │   │   │   └── extend.vue
│   │   │   │   │   ├── radio
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   ├── markup-schema.vue
│   │   │   │   │   │   └── template.vue
│   │   │   │   │   ├── reset
│   │   │   │   │   │   ├── base.vue
│   │   │   │   │   │   ├── force.vue
│   │   │   │   │   │   └── validate.vue
│   │   │   │   │   ├── select
│   │   │   │   │   │   ├── json-schema-async.vue
│   │   │   │   │   │   ├── json-schema-sync.vue
│   │   │   │   │   │   ├── markup-schema-async-search.vue
│   │   │   │   │   │   ├── markup-schema-async.vue
│   │   │   │   │   │   ├── markup-schema-sync.vue
│   │   │   │   │   │   ├── template-async.vue
│   │   │   │   │   │   └── template-sync.vue
│   │   │   │   │   ├── space
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   ├── markup-schema.vue
│   │   │   │   │   │   └── template.vue
│   │   │   │   │   ├── submit
│   │   │   │   │   │   ├── base.vue
│   │   │   │   │   │   └── loading.vue
│   │   │   │   │   ├── switch
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   ├── markup-schema.vue
│   │   │   │   │   │   └── template.vue
│   │   │   │   │   ├── time-picker
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   ├── markup-schema.vue
│   │   │   │   │   │   └── template.vue
│   │   │   │   │   ├── transfer
│   │   │   │   │   │   ├── json-schema.vue
│   │   │   │   │   │   ├── markup-schema.vue
│   │   │   │   │   │   └── template.vue
│   │   │   │   │   └── upload
│   │   │   │   │       ├── json-schema.vue
│   │   │   │   │       ├── markup-schema.vue
│   │   │   │   │       └── template.vue
│   │   │   │   └── index.vue
│   │   │   ├── guide
│   │   │   │   ├── array-cards.md
│   │   │   │   ├── array-collapse.md
│   │   │   │   ├── array-items.md
│   │   │   │   ├── array-table.md
│   │   │   │   ├── array-tabs.md
│   │   │   │   ├── cascader.md
│   │   │   │   ├── checkbox.md
│   │   │   │   ├── date-picker.md
│   │   │   │   ├── editable.md
│   │   │   │   ├── form-button-group.md
│   │   │   │   ├── form-collapse.md
│   │   │   │   ├── form-dialog.md
│   │   │   │   ├── form-drawer.md
│   │   │   │   ├── form-grid.md
│   │   │   │   ├── form-item.md
│   │   │   │   ├── form-layout.md
│   │   │   │   ├── form-step.md
│   │   │   │   ├── form-tab.md
│   │   │   │   ├── form.md
│   │   │   │   ├── index.md
│   │   │   │   ├── input-number.md
│   │   │   │   ├── input.md
│   │   │   │   ├── password.md
│   │   │   │   ├── preview-text.md
│   │   │   │   ├── radio.md
│   │   │   │   ├── reset.md
│   │   │   │   ├── select.md
│   │   │   │   ├── space.md
│   │   │   │   ├── submit.md
│   │   │   │   ├── switch.md
│   │   │   │   ├── time-picker.md
│   │   │   │   ├── transfer.md
│   │   │   │   └── upload.md
│   │   │   └── README.md
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── rollup.config.js
│   │   ├── src
│   │   │   ├── __builtins__
│   │   │   │   ├── configs
│   │   │   │   │   └── index.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── shared
│   │   │   │   │   ├── create-context.ts
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── loading.ts
│   │   │   │   │   ├── portal.ts
│   │   │   │   │   ├── resolve-component.ts
│   │   │   │   │   ├── transform-component.ts
│   │   │   │   │   ├── types.ts
│   │   │   │   │   └── utils.ts
│   │   │   │   └── styles
│   │   │   │       └── common.scss
│   │   │   ├── array-base
│   │   │   │   ├── index.ts
│   │   │   │   ├── style.scss
│   │   │   │   └── style.ts
│   │   │   ├── array-cards
│   │   │   │   ├── index.ts
│   │   │   │   ├── style.scss
│   │   │   │   └── style.ts
│   │   │   ├── array-collapse
│   │   │   │   ├── index.ts
│   │   │   │   ├── style.scss
│   │   │   │   └── style.ts
│   │   │   ├── array-items
│   │   │   │   ├── index.ts
│   │   │   │   ├── style.scss
│   │   │   │   └── style.ts
│   │   │   ├── array-table
│   │   │   │   ├── index.ts
│   │   │   │   ├── style.scss
│   │   │   │   └── style.ts
│   │   │   ├── array-tabs
│   │   │   │   ├── index.ts
│   │   │   │   ├── style.scss
│   │   │   │   └── style.ts
│   │   │   ├── cascader
│   │   │   │   ├── index.ts
│   │   │   │   └── style.ts
│   │   │   ├── checkbox
│   │   │   │   ├── index.ts
│   │   │   │   └── style.ts
│   │   │   ├── date-picker
│   │   │   │   ├── index.ts
│   │   │   │   └── style.ts
│   │   │   ├── editable
│   │   │   │   ├── index.ts
│   │   │   │   ├── style.scss
│   │   │   │   └── style.ts
│   │   │   ├── el-form
│   │   │   │   ├── index.ts
│   │   │   │   └── style.ts
│   │   │   ├── el-form-item
│   │   │   │   ├── index.ts
│   │   │   │   └── style.ts
│   │   │   ├── form
│   │   │   │   ├── index.ts
│   │   │   │   ├── style.scss
│   │   │   │   └── style.ts
│   │   │   ├── form-button-group
│   │   │   │   ├── index.ts
│   │   │   │   ├── style.scss
│   │   │   │   └── style.ts
│   │   │   ├── form-collapse
│   │   │   │   ├── index.ts
│   │   │   │   ├── style.scss
│   │   │   │   └── style.ts
│   │   │   ├── form-dialog
│   │   │   │   ├── index.ts
│   │   │   │   └── style.ts
│   │   │   ├── form-drawer
│   │   │   │   ├── index.ts
│   │   │   │   ├── style.scss
│   │   │   │   └── style.ts
│   │   │   ├── form-grid
│   │   │   │   ├── index.ts
│   │   │   │   ├── style.scss
│   │   │   │   └── style.ts
│   │   │   ├── form-item
│   │   │   │   ├── animation.scss
│   │   │   │   ├── grid.scss
│   │   │   │   ├── index.ts
│   │   │   │   ├── style.scss
│   │   │   │   ├── style.ts
│   │   │   │   └── var.scss
│   │   │   ├── form-layout
│   │   │   │   ├── index.ts
│   │   │   │   ├── style.scss
│   │   │   │   ├── style.ts
│   │   │   │   └── useResponsiveFormLayout.ts
│   │   │   ├── form-step
│   │   │   │   ├── index.ts
│   │   │   │   └── style.ts
│   │   │   ├── form-tab
│   │   │   │   ├── index.ts
│   │   │   │   ├── style.scss
│   │   │   │   └── style.ts
│   │   │   ├── index.ts
│   │   │   ├── input
│   │   │   │   ├── index.ts
│   │   │   │   └── style.ts
│   │   │   ├── input-number
│   │   │   │   ├── index.ts
│   │   │   │   └── style.ts
│   │   │   ├── password
│   │   │   │   ├── index.ts
│   │   │   │   └── style.ts
│   │   │   ├── preview-text
│   │   │   │   ├── index.ts
│   │   │   │   └── style.ts
│   │   │   ├── radio
│   │   │   │   ├── index.ts
│   │   │   │   └── style.ts
│   │   │   ├── reset
│   │   │   │   ├── index.ts
│   │   │   │   └── style.ts
│   │   │   ├── select
│   │   │   │   ├── index.ts
│   │   │   │   └── style.ts
│   │   │   ├── space
│   │   │   │   ├── index.ts
│   │   │   │   ├── style.scss
│   │   │   │   └── style.ts
│   │   │   ├── style.ts
│   │   │   ├── submit
│   │   │   │   ├── index.ts
│   │   │   │   └── style.ts
│   │   │   ├── switch
│   │   │   │   ├── index.ts
│   │   │   │   └── style.ts
│   │   │   ├── time-picker
│   │   │   │   ├── index.ts
│   │   │   │   └── style.ts
│   │   │   ├── transfer
│   │   │   │   ├── index.ts
│   │   │   │   └── style.ts
│   │   │   └── upload
│   │   │       ├── index.ts
│   │   │       └── style.ts
│   │   ├── transformer.ts
│   │   ├── tsconfig.build.json
│   │   └── tsconfig.json
│   ├── grid
│   │   ├── .npmignore
│   │   ├── LICENSE.md
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── rollup.config.js
│   │   ├── src
│   │   │   ├── index.ts
│   │   │   └── observer.ts
│   │   ├── tsconfig.build.json
│   │   └── tsconfig.json
│   ├── json-schema
│   │   ├── .npmignore
│   │   ├── LICENSE.md
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── rollup.config.js
│   │   ├── src
│   │   │   ├── __tests__
│   │   │   │   ├── __snapshots__
│   │   │   │   │   └── schema.spec.ts.snap
│   │   │   │   ├── compiler.spec.ts
│   │   │   │   ├── patches.spec.ts
│   │   │   │   ├── schema.spec.ts
│   │   │   │   ├── server-validate.spec.ts
│   │   │   │   ├── shared.spec.ts
│   │   │   │   ├── transformer.spec.ts
│   │   │   │   └── traverse.spec.ts
│   │   │   ├── compiler.ts
│   │   │   ├── global.d.ts
│   │   │   ├── index.ts
│   │   │   ├── patches.ts
│   │   │   ├── polyfills
│   │   │   │   ├── index.ts
│   │   │   │   └── SPECIFICATION_1_0.ts
│   │   │   ├── schema.ts
│   │   │   ├── shared.ts
│   │   │   ├── transformer.ts
│   │   │   └── types.ts
│   │   ├── tsconfig.build.json
│   │   └── tsconfig.json
│   ├── next
│   │   ├── __tests__
│   │   │   ├── moment.spec.ts
│   │   │   └── sideEffects.spec.ts
│   │   ├── .npmignore
│   │   ├── .umirc.js
│   │   ├── build-style.ts
│   │   ├── create-style.ts
│   │   ├── docs
│   │   │   ├── components
│   │   │   │   ├── ArrayCards.md
│   │   │   │   ├── ArrayCards.zh-CN.md
│   │   │   │   ├── ArrayCollapse.md
│   │   │   │   ├── ArrayCollapse.zh-CN.md
│   │   │   │   ├── ArrayItems.md
│   │   │   │   ├── ArrayItems.zh-CN.md
│   │   │   │   ├── ArrayTable.md
│   │   │   │   ├── ArrayTable.zh-CN.md
│   │   │   │   ├── Cascader.md
│   │   │   │   ├── Cascader.zh-CN.md
│   │   │   │   ├── Checkbox.md
│   │   │   │   ├── Checkbox.zh-CN.md
│   │   │   │   ├── DatePicker.md
│   │   │   │   ├── DatePicker.zh-CN.md
│   │   │   │   ├── DatePicker2.md
│   │   │   │   ├── DatePicker2.zh-CN.md
│   │   │   │   ├── Editable.md
│   │   │   │   ├── Editable.zh-CN.md
│   │   │   │   ├── Form.md
│   │   │   │   ├── Form.zh-CN.md
│   │   │   │   ├── FormButtonGroup.md
│   │   │   │   ├── FormButtonGroup.zh-CN.md
│   │   │   │   ├── FormCollapse.md
│   │   │   │   ├── FormCollapse.zh-CN.md
│   │   │   │   ├── FormDialog.md
│   │   │   │   ├── FormDialog.zh-CN.md
│   │   │   │   ├── FormDrawer.md
│   │   │   │   ├── FormDrawer.zh-CN.md
│   │   │   │   ├── FormGrid.md
│   │   │   │   ├── FormGrid.zh-CN.md
│   │   │   │   ├── FormItem.md
│   │   │   │   ├── FormItem.zh-CN.md
│   │   │   │   ├── FormLayout.md
│   │   │   │   ├── FormLayout.zh-CN.md
│   │   │   │   ├── FormStep.md
│   │   │   │   ├── FormStep.zh-CN.md
│   │   │   │   ├── FormTab.md
│   │   │   │   ├── FormTab.zh-CN.md
│   │   │   │   ├── index.md
│   │   │   │   ├── index.zh-CN.md
│   │   │   │   ├── Input.md
│   │   │   │   ├── Input.zh-CN.md
│   │   │   │   ├── NumberPicker.md
│   │   │   │   ├── NumberPicker.zh-CN.md
│   │   │   │   ├── Password.md
│   │   │   │   ├── Password.zh-CN.md
│   │   │   │   ├── PreviewText.md
│   │   │   │   ├── PreviewText.zh-CN.md
│   │   │   │   ├── Radio.md
│   │   │   │   ├── Radio.zh-CN.md
│   │   │   │   ├── Reset.md
│   │   │   │   ├── Reset.zh-CN.md
│   │   │   │   ├── Select.md
│   │   │   │   ├── Select.zh-CN.md
│   │   │   │   ├── SelectTable.md
│   │   │   │   ├── SelectTable.zh-CN.md
│   │   │   │   ├── Space.md
│   │   │   │   ├── Space.zh-CN.md
│   │   │   │   ├── Submit.md
│   │   │   │   ├── Submit.zh-CN.md
│   │   │   │   ├── Switch.md
│   │   │   │   ├── Switch.zh-CN.md
│   │   │   │   ├── TimePicker.md
│   │   │   │   ├── TimePicker.zh-CN.md
│   │   │   │   ├── TimePicker2.md
│   │   │   │   ├── TimePicker2.zh-CN.md
│   │   │   │   ├── Transfer.md
│   │   │   │   ├── Transfer.zh-CN.md
│   │   │   │   ├── TreeSelect.md
│   │   │   │   ├── TreeSelect.zh-CN.md
│   │   │   │   ├── Upload.md
│   │   │   │   └── Upload.zh-CN.md
│   │   │   ├── index.md
│   │   │   └── index.zh-CN.md
│   │   ├── LESENCE.md
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── rollup.config.js
│   │   ├── src
│   │   │   ├── __builtins__
│   │   │   │   ├── empty.tsx
│   │   │   │   ├── hooks
│   │   │   │   │   ├── index.ts
│   │   │   │   │   ├── useClickAway.ts
│   │   │   │   │   └── usePrefixCls.ts
│   │   │   │   ├── icons.tsx
│   │   │   │   ├── index.ts
│   │   │   │   ├── loading.ts
│   │   │   │   ├── mapSize.ts
│   │   │   │   ├── mapStatus.ts
│   │   │   │   ├── moment.ts
│   │   │   │   ├── pickDataProps.ts
│   │   │   │   ├── portal.tsx
│   │   │   │   ├── render.ts
│   │   │   │   └── toArray.ts
│   │   │   ├── array-base
│   │   │   │   ├── index.tsx
│   │   │   │   ├── main.scss
│   │   │   │   └── style.ts
│   │   │   ├── array-cards
│   │   │   │   ├── index.tsx
│   │   │   │   ├── main.scss
│   │   │   │   └── style.ts
│   │   │   ├── array-collapse
│   │   │   │   ├── index.tsx
│   │   │   │   ├── main.scss
│   │   │   │   └── style.ts
│   │   │   ├── array-items
│   │   │   │   ├── index.tsx
│   │   │   │   ├── main.scss
│   │   │   │   └── style.ts
│   │   │   ├── array-table
│   │   │   │   ├── index.tsx
│   │   │   │   ├── main.scss
│   │   │   │   └── style.ts
│   │   │   ├── cascader
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── checkbox
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── date-picker
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── date-picker2
│   │   │   │   ├── index.tsx
│   │   │   │   ├── main.scss
│   │   │   │   └── style.ts
│   │   │   ├── editable
│   │   │   │   ├── index.tsx
│   │   │   │   ├── main.scss
│   │   │   │   └── style.ts
│   │   │   ├── form
│   │   │   │   ├── index.tsx
│   │   │   │   ├── main.scss
│   │   │   │   └── style.ts
│   │   │   ├── form-button-group
│   │   │   │   ├── index.tsx
│   │   │   │   ├── main.scss
│   │   │   │   └── style.ts
│   │   │   ├── form-collapse
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── form-dialog
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── form-drawer
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── form-grid
│   │   │   │   ├── index.tsx
│   │   │   │   ├── main.scss
│   │   │   │   └── style.ts
│   │   │   ├── form-item
│   │   │   │   ├── animation.scss
│   │   │   │   ├── grid.scss
│   │   │   │   ├── index.tsx
│   │   │   │   ├── main.scss
│   │   │   │   ├── scss
│   │   │   │   │   └── variable.scss
│   │   │   │   └── style.ts
│   │   │   ├── form-layout
│   │   │   │   ├── index.tsx
│   │   │   │   ├── main.scss
│   │   │   │   ├── style.ts
│   │   │   │   └── useResponsiveFormLayout.ts
│   │   │   ├── form-step
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── form-tab
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── index.ts
│   │   │   ├── input
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── main.scss
│   │   │   ├── number-picker
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── password
│   │   │   │   ├── index.tsx
│   │   │   │   ├── PasswordStrength.tsx
│   │   │   │   └── style.ts
│   │   │   ├── preview-text
│   │   │   │   ├── index.tsx
│   │   │   │   ├── main.scss
│   │   │   │   └── style.ts
│   │   │   ├── radio
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── reset
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── select
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── select-table
│   │   │   │   ├── index.tsx
│   │   │   │   ├── main.scss
│   │   │   │   ├── style.ts
│   │   │   │   ├── useCheckSlackly.tsx
│   │   │   │   ├── useFilterOptions.tsx
│   │   │   │   ├── useFlatOptions.tsx
│   │   │   │   ├── useSize.tsx
│   │   │   │   ├── useTitleAddon.tsx
│   │   │   │   └── utils.ts
│   │   │   ├── space
│   │   │   │   ├── index.tsx
│   │   │   │   ├── main.scss
│   │   │   │   └── style.ts
│   │   │   ├── style.ts
│   │   │   ├── submit
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── switch
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── time-picker
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── time-picker2
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── transfer
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   ├── tree-select
│   │   │   │   ├── index.tsx
│   │   │   │   └── style.ts
│   │   │   └── upload
│   │   │       ├── index.tsx
│   │   │       ├── main.scss
│   │   │       ├── placeholder.ts
│   │   │       └── style.ts
│   │   ├── tsconfig.build.json
│   │   └── tsconfig.json
│   ├── path
│   │   ├── .npmignore
│   │   ├── benchmark.ts
│   │   ├── LICENSE.md
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── rollup.config.js
│   │   ├── src
│   │   │   ├── __tests__
│   │   │   │   ├── accessor.spec.ts
│   │   │   │   ├── basic.spec.ts
│   │   │   │   ├── match.spec.ts
│   │   │   │   ├── parser.spec.ts
│   │   │   │   └── share.spec.ts
│   │   │   ├── contexts.ts
│   │   │   ├── destructor.ts
│   │   │   ├── index.ts
│   │   │   ├── matcher.ts
│   │   │   ├── parser.ts
│   │   │   ├── shared.ts
│   │   │   ├── tokenizer.ts
│   │   │   ├── tokens.ts
│   │   │   └── types.ts
│   │   ├── tsconfig.build.json
│   │   └── tsconfig.json
│   ├── react
│   │   ├── .npmignore
│   │   ├── .umirc.js
│   │   ├── docs
│   │   │   ├── api
│   │   │   │   ├── components
│   │   │   │   │   ├── ArrayField.md
│   │   │   │   │   ├── ArrayField.zh-CN.md
│   │   │   │   │   ├── ExpressionScope.md
│   │   │   │   │   ├── ExpressionScope.zh-CN.md
│   │   │   │   │   ├── Field.md
│   │   │   │   │   ├── Field.zh-CN.md
│   │   │   │   │   ├── FormConsumer.md
│   │   │   │   │   ├── FormConsumer.zh-CN.md
│   │   │   │   │   ├── FormProvider.md
│   │   │   │   │   ├── FormProvider.zh-CN.md
│   │   │   │   │   ├── ObjectField.md
│   │   │   │   │   ├── ObjectField.zh-CN.md
│   │   │   │   │   ├── RecordScope.md
│   │   │   │   │   ├── RecordScope.zh-CN.md
│   │   │   │   │   ├── RecordsScope.md
│   │   │   │   │   ├── RecordsScope.zh-CN.md
│   │   │   │   │   ├── RecursionField.md
│   │   │   │   │   ├── RecursionField.zh-CN.md
│   │   │   │   │   ├── SchemaField.md
│   │   │   │   │   ├── SchemaField.zh-CN.md
│   │   │   │   │   ├── VoidField.md
│   │   │   │   │   └── VoidField.zh-CN.md
│   │   │   │   ├── hooks
│   │   │   │   │   ├── useExpressionScope.md
│   │   │   │   │   ├── useExpressionScope.zh-CN.md
│   │   │   │   │   ├── useField.md
│   │   │   │   │   ├── useField.zh-CN.md
│   │   │   │   │   ├── useFieldSchema.md
│   │   │   │   │   ├── useFieldSchema.zh-CN.md
│   │   │   │   │   ├── useForm.md
│   │   │   │   │   ├── useForm.zh-CN.md
│   │   │   │   │   ├── useFormEffects.md
│   │   │   │   │   ├── useFormEffects.zh-CN.md
│   │   │   │   │   ├── useParentForm.md
│   │   │   │   │   └── useParentForm.zh-CN.md
│   │   │   │   └── shared
│   │   │   │       ├── connect.md
│   │   │   │       ├── connect.zh-CN.md
│   │   │   │       ├── context.md
│   │   │   │       ├── context.zh-CN.md
│   │   │   │       ├── mapProps.md
│   │   │   │       ├── mapProps.zh-CN.md
│   │   │   │       ├── mapReadPretty.md
│   │   │   │       ├── mapReadPretty.zh-CN.md
│   │   │   │       ├── observer.md
│   │   │   │       ├── observer.zh-CN.md
│   │   │   │       ├── Schema.md
│   │   │   │       └── Schema.zh-CN.md
│   │   │   ├── guide
│   │   │   │   ├── architecture.md
│   │   │   │   ├── architecture.zh-CN.md
│   │   │   │   ├── concept.md
│   │   │   │   ├── concept.zh-CN.md
│   │   │   │   ├── index.md
│   │   │   │   └── index.zh-CN.md
│   │   │   ├── index.md
│   │   │   └── index.zh-CN.md
│   │   ├── LICENSE.md
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── rollup.config.js
│   │   ├── src
│   │   │   ├── __tests__
│   │   │   │   ├── expression.spec.tsx
│   │   │   │   ├── field.spec.tsx
│   │   │   │   ├── form.spec.tsx
│   │   │   │   ├── schema.json.spec.tsx
│   │   │   │   ├── schema.markup.spec.tsx
│   │   │   │   └── shared.tsx
│   │   │   ├── components
│   │   │   │   ├── ArrayField.tsx
│   │   │   │   ├── ExpressionScope.tsx
│   │   │   │   ├── Field.tsx
│   │   │   │   ├── FormConsumer.tsx
│   │   │   │   ├── FormProvider.tsx
│   │   │   │   ├── index.ts
│   │   │   │   ├── ObjectField.tsx
│   │   │   │   ├── ReactiveField.tsx
│   │   │   │   ├── RecordScope.tsx
│   │   │   │   ├── RecordsScope.tsx
│   │   │   │   ├── RecursionField.tsx
│   │   │   │   ├── SchemaField.tsx
│   │   │   │   └── VoidField.tsx
│   │   │   ├── global.d.ts
│   │   │   ├── hooks
│   │   │   │   ├── index.ts
│   │   │   │   ├── useAttach.ts
│   │   │   │   ├── useExpressionScope.ts
│   │   │   │   ├── useField.ts
│   │   │   │   ├── useFieldSchema.ts
│   │   │   │   ├── useForm.ts
│   │   │   │   ├── useFormEffects.ts
│   │   │   │   └── useParentForm.ts
│   │   │   ├── index.ts
│   │   │   ├── shared
│   │   │   │   ├── connect.ts
│   │   │   │   ├── context.ts
│   │   │   │   ├── index.ts
│   │   │   │   └── render.ts
│   │   │   └── types.ts
│   │   ├── tsconfig.build.json
│   │   └── tsconfig.json
│   ├── reactive
│   │   ├── .npmignore
│   │   ├── .umirc.js
│   │   ├── benchmark.ts
│   │   ├── docs
│   │   │   ├── api
│   │   │   │   ├── action.md
│   │   │   │   ├── action.zh-CN.md
│   │   │   │   ├── autorun.md
│   │   │   │   ├── autorun.zh-CN.md
│   │   │   │   ├── batch.md
│   │   │   │   ├── batch.zh-CN.md
│   │   │   │   ├── define.md
│   │   │   │   ├── define.zh-CN.md
│   │   │   │   ├── hasCollected.md
│   │   │   │   ├── hasCollected.zh-CN.md
│   │   │   │   ├── markObservable.md
│   │   │   │   ├── markObservable.zh-CN.md
│   │   │   │   ├── markRaw.md
│   │   │   │   ├── markRaw.zh-CN.md
│   │   │   │   ├── model.md
│   │   │   │   ├── model.zh-CN.md
│   │   │   │   ├── observable.md
│   │   │   │   ├── observable.zh-CN.md
│   │   │   │   ├── observe.md
│   │   │   │   ├── observe.zh-CN.md
│   │   │   │   ├── raw.md
│   │   │   │   ├── raw.zh-CN.md
│   │   │   │   ├── react
│   │   │   │   │   ├── observer.md
│   │   │   │   │   └── observer.zh-CN.md
│   │   │   │   ├── reaction.md
│   │   │   │   ├── reaction.zh-CN.md
│   │   │   │   ├── toJS.md
│   │   │   │   ├── toJS.zh-CN.md
│   │   │   │   ├── tracker.md
│   │   │   │   ├── tracker.zh-CN.md
│   │   │   │   ├── typeChecker.md
│   │   │   │   ├── typeChecker.zh-CN.md
│   │   │   │   ├── untracked.md
│   │   │   │   ├── untracked.zh-CN.md
│   │   │   │   └── vue
│   │   │   │       ├── observer.md
│   │   │   │       └── observer.zh-CN.md
│   │   │   ├── guide
│   │   │   │   ├── best-practice.md
│   │   │   │   ├── best-practice.zh-CN.md
│   │   │   │   ├── concept.md
│   │   │   │   ├── concept.zh-CN.md
│   │   │   │   ├── index.md
│   │   │   │   └── index.zh-CN.md
│   │   │   ├── index.md
│   │   │   └── index.zh-CN.md
│   │   ├── LICENSE.md
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── rollup.config.js
│   │   ├── src
│   │   │   ├── __tests__
│   │   │   │   ├── action.spec.ts
│   │   │   │   ├── annotations.spec.ts
│   │   │   │   ├── array.spec.ts
│   │   │   │   ├── autorun.spec.ts
│   │   │   │   ├── batch.spec.ts
│   │   │   │   ├── collections-map.spec.ts
│   │   │   │   ├── collections-set.spec.ts
│   │   │   │   ├── collections-weakmap.spec.ts
│   │   │   │   ├── collections-weakset.spec.ts
│   │   │   │   ├── define.spec.ts
│   │   │   │   ├── externals.spec.ts
│   │   │   │   ├── hasCollected.spec.ts
│   │   │   │   ├── observable.spec.ts
│   │   │   │   ├── observe.spec.ts
│   │   │   │   ├── tracker.spec.ts
│   │   │   │   └── untracked.spec.ts
│   │   │   ├── action.ts
│   │   │   ├── annotations
│   │   │   │   ├── box.ts
│   │   │   │   ├── computed.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── observable.ts
│   │   │   │   ├── ref.ts
│   │   │   │   └── shallow.ts
│   │   │   ├── array.ts
│   │   │   ├── autorun.ts
│   │   │   ├── batch.ts
│   │   │   ├── checkers.ts
│   │   │   ├── environment.ts
│   │   │   ├── externals.ts
│   │   │   ├── global.d.ts
│   │   │   ├── handlers.ts
│   │   │   ├── index.ts
│   │   │   ├── internals.ts
│   │   │   ├── model.ts
│   │   │   ├── observable.ts
│   │   │   ├── observe.ts
│   │   │   ├── reaction.ts
│   │   │   ├── tracker.ts
│   │   │   ├── tree.ts
│   │   │   ├── types.ts
│   │   │   └── untracked.ts
│   │   ├── tsconfig.build.json
│   │   └── tsconfig.json
│   ├── reactive-react
│   │   ├── .npmignore
│   │   ├── .umirc.js
│   │   ├── LICENSE.md
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── rollup.config.js
│   │   ├── src
│   │   │   ├── hooks
│   │   │   │   ├── index.ts
│   │   │   │   ├── useCompatEffect.ts
│   │   │   │   ├── useCompatFactory.ts
│   │   │   │   ├── useDidUpdate.ts
│   │   │   │   ├── useForceUpdate.ts
│   │   │   │   ├── useLayoutEffect.ts
│   │   │   │   └── useObserver.ts
│   │   │   ├── index.ts
│   │   │   ├── observer.ts
│   │   │   ├── shared
│   │   │   │   ├── gc.ts
│   │   │   │   ├── global.ts
│   │   │   │   ├── immediate.ts
│   │   │   │   └── index.ts
│   │   │   └── types.ts
│   │   ├── tsconfig.build.json
│   │   └── tsconfig.json
│   ├── reactive-test-cases-for-react18
│   │   ├── .npmignore
│   │   ├── .umirc.js
│   │   ├── LICENSE.md
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── src
│   │   │   ├── index.js
│   │   │   └── MySlowList.js
│   │   ├── template.ejs
│   │   ├── tsconfig.build.json
│   │   ├── tsconfig.json
│   │   ├── webpack.base.ts
│   │   ├── webpack.dev.ts
│   │   └── webpack.prod.ts
│   ├── reactive-vue
│   │   ├── .npmignore
│   │   ├── LICENSE.md
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── rollup.config.js
│   │   ├── src
│   │   │   ├── __tests__
│   │   │   │   └── observer.spec.ts
│   │   │   ├── hooks
│   │   │   │   ├── index.ts
│   │   │   │   └── useObserver.ts
│   │   │   ├── index.ts
│   │   │   ├── observer
│   │   │   │   ├── collectData.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── observerInVue2.ts
│   │   │   │   └── observerInVue3.ts
│   │   │   └── types.ts
│   │   ├── tsconfig.build.json
│   │   └── tsconfig.json
│   ├── shared
│   │   ├── .npmignore
│   │   ├── LICENSE.md
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── rollup.config.js
│   │   ├── src
│   │   │   ├── __tests__
│   │   │   │   └── index.spec.ts
│   │   │   ├── array.ts
│   │   │   ├── case.ts
│   │   │   ├── checkers.ts
│   │   │   ├── clone.ts
│   │   │   ├── compare.ts
│   │   │   ├── defaults.ts
│   │   │   ├── deprecate.ts
│   │   │   ├── global.ts
│   │   │   ├── index.ts
│   │   │   ├── instanceof.ts
│   │   │   ├── isEmpty.ts
│   │   │   ├── merge.ts
│   │   │   ├── middleware.ts
│   │   │   ├── path.ts
│   │   │   ├── string.ts
│   │   │   ├── subscribable.ts
│   │   │   └── uid.ts
│   │   ├── tsconfig.build.json
│   │   └── tsconfig.json
│   ├── validator
│   │   ├── .npmignore
│   │   ├── LICENSE.md
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── rollup.config.js
│   │   ├── src
│   │   │   ├── __tests__
│   │   │   │   ├── parser.spec.ts
│   │   │   │   ├── registry.spec.ts
│   │   │   │   └── validator.spec.ts
│   │   │   ├── formats.ts
│   │   │   ├── index.ts
│   │   │   ├── locale.ts
│   │   │   ├── parser.ts
│   │   │   ├── registry.ts
│   │   │   ├── rules.ts
│   │   │   ├── template.ts
│   │   │   ├── types.ts
│   │   │   └── validator.ts
│   │   ├── tsconfig.build.json
│   │   └── tsconfig.json
│   └── vue
│       ├── .npmignore
│       ├── bin
│       │   ├── formily-vue-fix.js
│       │   └── formily-vue-switch.js
│       ├── docs
│       │   ├── .vuepress
│       │   │   ├── components
│       │   │   │   ├── createCodeSandBox.js
│       │   │   │   ├── dumi-previewer.vue
│       │   │   │   └── highlight.js
│       │   │   ├── config.js
│       │   │   ├── enhanceApp.js
│       │   │   └── styles
│       │   │       └── index.styl
│       │   ├── api
│       │   │   ├── components
│       │   │   │   ├── array-field.md
│       │   │   │   ├── expression-scope.md
│       │   │   │   ├── field.md
│       │   │   │   ├── form-consumer.md
│       │   │   │   ├── form-provider.md
│       │   │   │   ├── object-field.md
│       │   │   │   ├── recursion-field-with-component.md
│       │   │   │   ├── recursion-field.md
│       │   │   │   ├── schema-field-with-schema.md
│       │   │   │   ├── schema-field.md
│       │   │   │   └── void-field.md
│       │   │   ├── hooks
│       │   │   │   ├── use-field-schema.md
│       │   │   │   ├── use-field.md
│       │   │   │   ├── use-form-effects.md
│       │   │   │   ├── use-form.md
│       │   │   │   └── use-parent-form.md
│       │   │   └── shared
│       │   │       ├── connect.md
│       │   │       ├── injections.md
│       │   │       ├── map-props.md
│       │   │       ├── map-read-pretty.md
│       │   │       ├── observer.md
│       │   │       └── schema.md
│       │   ├── demos
│       │   │   ├── api
│       │   │   │   ├── components
│       │   │   │   │   ├── array-field.vue
│       │   │   │   │   ├── expression-scope.vue
│       │   │   │   │   ├── field.vue
│       │   │   │   │   ├── form-consumer.vue
│       │   │   │   │   ├── form-provider.vue
│       │   │   │   │   ├── object-field.vue
│       │   │   │   │   ├── recursion-field-with-component.vue
│       │   │   │   │   ├── recursion-field.vue
│       │   │   │   │   ├── schema-field-with-schema.vue
│       │   │   │   │   ├── schema-field.vue
│       │   │   │   │   └── void-field.vue
│       │   │   │   ├── hooks
│       │   │   │   │   ├── use-field-schema.vue
│       │   │   │   │   ├── use-field.vue
│       │   │   │   │   ├── use-form-effects.vue
│       │   │   │   │   ├── use-form.vue
│       │   │   │   │   └── use-parent-form.vue
│       │   │   │   └── shared
│       │   │   │       ├── connect.vue
│       │   │   │       ├── map-props.vue
│       │   │   │       ├── map-read-pretty.vue
│       │   │   │       └── observer.vue
│       │   │   ├── index.vue
│       │   │   └── questions
│       │   │       ├── default-slot.vue
│       │   │       ├── events.vue
│       │   │       ├── named-slot.vue
│       │   │       └── scoped-slot.vue
│       │   ├── guide
│       │   │   ├── architecture.md
│       │   │   ├── concept.md
│       │   │   └── README.md
│       │   ├── questions
│       │   │   └── README.md
│       │   └── README.md
│       ├── package.json
│       ├── README.md
│       ├── rollup.config.js
│       ├── scripts
│       │   ├── postinstall.js
│       │   ├── switch-cli.js
│       │   └── utils.js
│       ├── src
│       │   ├── __tests__
│       │   │   ├── expression.scope.spec.ts
│       │   │   ├── field.spec.ts
│       │   │   ├── form.spec.ts
│       │   │   ├── schema.json.spec.ts
│       │   │   ├── schema.markup.spec.ts
│       │   │   ├── shared.spec.ts
│       │   │   └── utils.spec.ts
│       │   ├── components
│       │   │   ├── ArrayField.ts
│       │   │   ├── ExpressionScope.ts
│       │   │   ├── Field.ts
│       │   │   ├── FormConsumer.ts
│       │   │   ├── FormProvider.ts
│       │   │   ├── index.ts
│       │   │   ├── ObjectField.ts
│       │   │   ├── ReactiveField.ts
│       │   │   ├── RecursionField.ts
│       │   │   ├── SchemaField.ts
│       │   │   └── VoidField.ts
│       │   ├── global.d.ts
│       │   ├── hooks
│       │   │   ├── index.ts
│       │   │   ├── useAttach.ts
│       │   │   ├── useField.ts
│       │   │   ├── useFieldSchema.ts
│       │   │   ├── useForm.ts
│       │   │   ├── useFormEffects.ts
│       │   │   ├── useInjectionCleaner.ts
│       │   │   └── useParentForm.ts
│       │   ├── index.ts
│       │   ├── shared
│       │   │   ├── connect.ts
│       │   │   ├── context.ts
│       │   │   ├── createForm.ts
│       │   │   ├── fragment.ts
│       │   │   ├── h.ts
│       │   │   └── index.ts
│       │   ├── types
│       │   │   └── index.ts
│       │   ├── utils
│       │   │   ├── formatVNodeData.ts
│       │   │   ├── getFieldProps.ts
│       │   │   ├── getRawComponent.ts
│       │   │   └── resolveSchemaProps.ts
│       │   └── vue2-components.ts
│       ├── tsconfig.build.json
│       ├── tsconfig.json
│       └── tsconfig.types.json
├── README.md
├── README.zh-cn.md
├── scripts
│   ├── build-style
│   │   ├── buildAllStyles.ts
│   │   ├── copy.ts
│   │   ├── helper.ts
│   │   └── index.ts
│   └── rollup.base.js
├── tsconfig.build.json
├── tsconfig.jest.json
├── tsconfig.json
└── yarn.lock
```

# Files

--------------------------------------------------------------------------------
/packages/core/src/__tests__/void.spec.ts:
--------------------------------------------------------------------------------

```typescript
import { createForm } from '../'
import { attach } from './shared'

test('create void field', () => {
  const form = attach(createForm())
  const field = attach(
    form.createVoidField({
      name: 'void',
    })
  )
  field.destroy()
})

test('create void field props', () => {
  const form = attach(createForm())
  const field1 = attach(
    form.createVoidField({
      name: 'field1',
      title: 'Field 1',
      description: 'This is Field 1',
    })
  )
  expect(field1.title).toEqual('Field 1')
  expect(field1.description).toEqual('This is Field 1')
  const field2 = attach(
    form.createVoidField({
      name: 'field2',
      disabled: true,
      hidden: true,
    })
  )
  expect(field2.pattern).toEqual('disabled')
  expect(field2.disabled).toBeTruthy()
  expect(field2.display).toEqual('hidden')
  expect(field2.hidden).toBeTruthy()
  const field3 = attach(
    form.createVoidField({
      name: 'field3',
      readOnly: true,
      visible: false,
    })
  )
  expect(field3.pattern).toEqual('readOnly')
  expect(field3.readOnly).toBeTruthy()
  expect(field3.display).toEqual('none')
  expect(field3.visible).toBeFalsy()
})

test('setComponent/setComponentProps', () => {
  const form = attach(createForm())
  const field = attach(
    form.createVoidField({
      name: 'aa',
    })
  )
  const component = () => null
  field.setComponent(component, { props: 123 })
  expect(field.component[0]).toEqual(component)
  expect(field.component[1]).toEqual({ props: 123 })
  field.setComponentProps({
    hello: 'world',
  })
  expect(field.component[1]).toEqual({ props: 123, hello: 'world' })
})

test('setTitle/setDescription', () => {
  const form = attach(createForm())
  const aa = attach(
    form.createVoidField({
      name: 'aa',
    })
  )
  aa.setTitle('AAA')
  aa.setDescription('This is AAA')
  expect(aa.title).toEqual('AAA')
  expect(aa.description).toEqual('This is AAA')
})

test('setComponent/setComponentProps', () => {
  const component = () => null
  const form = attach(createForm())
  const field = attach(
    form.createVoidField({
      name: 'aa',
    })
  )

  field.setComponent(undefined, { props: 123 })
  field.setComponent(component)
  expect(field.component[0]).toEqual(component)
  expect(field.component[1]).toEqual({ props: 123 })
  field.setComponentProps({
    hello: 'world',
  })
  expect(field.component[1]).toEqual({ props: 123, hello: 'world' })
})

test('setDecorator/setDecoratorProps', () => {
  const component = () => null
  const form = attach(createForm())
  const field = attach(
    form.createVoidField({
      name: 'aa',
    })
  )
  field.setDecorator(undefined, { props: 123 })
  field.setDecorator(component)
  expect(field.decorator[0]).toEqual(component)
  expect(field.decorator[1]).toEqual({ props: 123 })
  field.setDecoratorProps({
    hello: 'world',
  })
  expect(field.decorator[1]).toEqual({ props: 123, hello: 'world' })
})

test('setState/getState', () => {
  const form = attach(createForm())
  const aa = attach(
    form.createVoidField({
      name: 'aa',
    })
  )
  const state = aa.getState()
  aa.setState((state) => {
    state.title = 'AAA'
  })
  expect(aa.title).toEqual('AAA')
  aa.setState(state)
  expect(aa.title).toBeUndefined()
  aa.setState((state) => {
    state.hidden = false
  })
  expect(aa.display).toEqual('visible')
  aa.setState((state) => {
    state.visible = true
  })
  expect(aa.display).toEqual('visible')
  aa.setState((state) => {
    state.readOnly = false
  })
  expect(aa.pattern).toEqual('editable')
  aa.setState((state) => {
    state.disabled = false
  })
  expect(aa.pattern).toEqual('editable')
  aa.setState((state) => {
    state.editable = true
  })
  expect(aa.pattern).toEqual('editable')
  aa.setState((state) => {
    state.editable = false
  })
  expect(aa.pattern).toEqual('readPretty')
  aa.setState((state) => {
    state.readPretty = true
  })
  expect(aa.pattern).toEqual('readPretty')
  aa.setState((state) => {
    state.readPretty = false
  })
  expect(aa.pattern).toEqual('editable')
  expect(aa.parent).toBeUndefined()
})

test('nested display/pattern', () => {
  const form = attach(createForm())
  attach(
    form.createObjectField({
      name: 'object',
    })
  )
  const void_ = attach(
    form.createVoidField({
      name: 'void',
      basePath: 'object',
    })
  )
  const void2_ = attach(
    form.createVoidField({
      name: 'void',
      basePath: 'object.void.0',
    })
  )
  const aaa = attach(
    form.createField({
      name: 'aaa',
      basePath: 'object.void',
    })
  )
  const bbb = attach(
    form.createField({
      name: 'bbb',
      basePath: 'object.void',
    })
  )
  void_.setPattern('readPretty')
  expect(void_.pattern).toEqual('readPretty')
  expect(aaa.pattern).toEqual('readPretty')
  expect(bbb.pattern).toEqual('readPretty')
  void_.setPattern('readOnly')
  expect(void_.pattern).toEqual('readOnly')
  expect(aaa.pattern).toEqual('readOnly')
  expect(bbb.pattern).toEqual('readOnly')
  void_.setPattern('disabled')
  expect(void_.pattern).toEqual('disabled')
  expect(aaa.pattern).toEqual('disabled')
  expect(bbb.pattern).toEqual('disabled')
  void_.setPattern()
  expect(void_.pattern).toEqual('editable')
  expect(aaa.pattern).toEqual('editable')
  expect(bbb.pattern).toEqual('editable')

  void_.setDisplay('hidden')
  expect(void_.display).toEqual('hidden')
  expect(aaa.display).toEqual('hidden')
  expect(bbb.display).toEqual('hidden')
  void_.setDisplay('none')
  expect(void_.display).toEqual('none')
  expect(aaa.display).toEqual('none')
  expect(bbb.display).toEqual('none')
  void_.setDisplay()
  expect(void_.display).toEqual('visible')
  expect(aaa.display).toEqual('visible')
  expect(bbb.display).toEqual('visible')
  void_.onUnmount()
  expect(void2_.parent === void_).toBeTruthy()
})

test('reactions', async () => {
  const form = attach(createForm())
  const aa = attach(
    form.createField({
      name: 'aa',
    })
  )
  const bb = attach(
    form.createVoidField({
      name: 'bb',
      reactions: [
        (field) => {
          const aa = field.query('aa')
          if (aa.get('value') === '123') {
            field.visible = false
          } else {
            field.visible = true
          }
          if (aa.get('inputValue') === '333') {
            field.editable = false
          } else if (aa.get('inputValue') === '444') {
            field.editable = true
          }
          if (aa.get('initialValue') === '555') {
            field.readOnly = true
          } else if (aa.get('initialValue') === '666') {
            field.readOnly = false
          }
        },
        null,
      ],
    })
  )
  expect(bb.visible).toBeTruthy()
  aa.setValue('123')
  expect(bb.visible).toBeFalsy()
  await aa.onInput('333')
  expect(bb.editable).toBeFalsy()
  await aa.onInput('444')
  expect(bb.editable).toBeTruthy()
  aa.setInitialValue('555')
  expect(bb.readOnly).toBeTruthy()
  aa.setInitialValue('666')
  expect(bb.readOnly).toBeFalsy()
  form.onUnmount()
})

test('fault tolerance', () => {
  const form = attach(createForm())
  form.setDisplay(null)
  form.setPattern(null)
  const field = attach(
    form.createVoidField({
      name: 'xxx',
    })
  )
  expect(field.display).toEqual('visible')
  expect(field.pattern).toEqual('editable')
})

test('child field reactions', () => {
  const form = attach(createForm())
  const voidField = attach(form.createVoidField({ name: 'void' }))
  const field1 = attach(
    form.createField({
      name: 'field1',
      basePath: voidField.address,
      reactions: [
        (field) => {
          field.value = field.query('field3').getIn('value')
        },
      ],
    })
  )
  const field2 = attach(
    form.createField({
      name: 'field2',
      basePath: voidField.address,
      reactions: [
        (field) => {
          field.value = field.query('.field3').getIn('value')
        },
      ],
    })
  )
  expect(field1.value).toBeUndefined()
  expect(field2.value).toBeUndefined()
  const field3 = attach(
    form.createField({
      name: 'field3',
      basePath: voidField.address,
      value: 1,
    })
  )
  expect(field1.value).toBe(1)
  expect(field2.value).toBe(1)
  field3.value = 2
  expect(field1.value).toBe(2)
  expect(field2.value).toBe(2)
})

```

--------------------------------------------------------------------------------
/packages/core/docs/api/entry/FormChecker.md:
--------------------------------------------------------------------------------

```markdown
---
order: 4
---

# Form Checkers

> The type checker is mainly used to determine the specific type of an object

## isForm

#### Description

Determine whether an object is a [Form](/api/models/form) object

#### Signature

```ts
interface isForm {
  (target: any): target is Form
}
```

#### Example

```ts
import { createForm, isForm } from '@formily/core'

const form = createForm()

console.log(isForm(form)) //true
```

## isField

#### Description

Determine whether an object is a [Field](/api/models/field) object

#### Signature

```ts
interface isField {
  (target: any): target is Field
}
```

#### Example

```ts
import { createForm, isField } from '@formily/core'

const form = createForm()

const field = form.createField({ name: 'target' })

console.log(isField(field)) //true
```

## isArrayField

#### Description

Determine whether an object is [ArrayField](/api/models/array-field) object

#### Signature

```ts
interface isArrayField {
  (target: any): target is ArrayField
}
```

#### Example

```ts
import { createForm, isArrayField } from '@formily/core'

const form = createForm()

const field = form.createArrayField({ name: 'target' })

console.log(isArrayField(field)) //true
```

## isObjectField

#### Description

Determine whether an object is a [ObjectField](/api/models/object-field) object

#### Signature

```ts
interface isObjectField {
  (target: any): target is ObjectField
}
```

#### Example

```ts
import { createForm, isObjectField } from '@formily/core'

const form = createForm()

const field = form.createObjectField({ name: 'target' })

console.log(isObjectField(field)) //true
```

## isVoidField

#### Description

Determine whether an object is a [VoidField](/api/models/void-field) object

#### Signature

```ts
interface isVoidField {
  (target: any): target is VoidField
}
```

#### Example

```ts
import { createForm, isVoidField } from '@formily/core'

const form = createForm()

const field = form.createVoidField({ name: 'target' })

console.log(isVoidField(field)) //true
```

## isGeneralField

#### Description

Determine whether an object is a Field/ArrayField/ObjectField/VoidField object

#### Signature

```ts
interface isGeneralField {
  (target: any): target is Field | ArrayField | ObjectField | VoidField
}
```

#### Example

```ts
import { createForm, isGeneralField } from '@formily/core'

const form = createForm()

const field = form.createField({ name: 'target' })
const arr = form.createArrayField({ name: 'array' })
const obj = form.createObjectField({ name: 'object' })
const vod = form.createVoidField({ name: 'void' })

console.log(isGeneralField(field)) //true
console.log(isGeneralField(arr)) //true
console.log(isGeneralField(obj)) //true
console.log(isGeneralField(vod)) //true
console.log(isGeneralField({})) //false
```

## isDataField

#### Description

Determine whether an object is a Field/ArrayField/ObjectField object

#### Signature

```ts
interface isDataField {
  (target: any): target is Field | ArrayField | ObjectField
}
```

#### Example

```ts
import { createForm, isDataField } from '@formily/core'

const form = createForm()

const field = form.createField({ name: 'target' })
const arr = form.createArrayField({ name: 'array' })
const obj = form.createObjectField({ name: 'object' })
const vod = form.createVoidField({ name: 'void' })

console.log(isDataField(field)) //true
console.log(isDataField(arr)) //true
console.log(isDataField(obj)) //true
console.log(isDataField(vod)) //false
console.log(isDataField({})) //false
```

## isFormState

#### Description

Determine whether an object is [IFormState](/api/models/form#iformstate) object

#### Signature

```ts
interface isFormState {
  (target: any): target is IFormState
}
```

#### Example

```ts
import { createForm, isFormState } from '@formily/core'

const form = createForm()

console.log(isFormState(form)) //false
console.log(isFormState(form.getState())) //true
```

## isFieldState

#### Description

Determine whether an object is [IFieldState](/api/models/field#ifieldstate) object

#### Signature

```ts
interface isFieldState {
  (target: any): target is IFieldState
}
```

#### Example

```ts
import { createForm, isFieldState } from '@formily/core'

const form = createForm()
const field = form.createField({
  name: 'target',
})

console.log(isFieldState(field)) //false
console.log(isFieldState(field.getState())) //true
```

## isArrayFieldState

#### Description

Determine whether an object is [IArrayFieldState](/api/models/array-field#iarrayfieldstate) object

#### Signature

```ts
interface isArrayFieldState {
  (target: any): target is IArrayFieldState
}
```

#### Example

```ts
import { createForm, isArrayFieldState } from '@formily/core'

const form = createForm()
const field = form.createArrayField({
  name: 'target',
})

console.log(isArrayFieldState(field)) //false
console.log(isArrayFieldState(field.getState())) //true
```

## isObjectFieldState

#### Description

Determine whether an object is [IObjectFieldState](/api/models/object-field#iobjectfieldstate) object

#### Signature

```ts
interface isObjectFieldState {
  (target: any): target is IObjectFieldState
}
```

#### Example

```ts
import { createForm, isObjectFieldState } from '@formily/core'

const form = createForm()
const field = form.createObjectField({
  name: 'target',
})

console.log(isObjectFieldState(field)) //false
console.log(isObjectFieldState(field.getState())) //true
```

## isVoidFieldState

#### Description

Determine whether an object is [IVoidFieldState](/api/models/void-field#ivoidfieldstate) object

#### Signature

```ts
interface isVoidFieldState {
  (target: any): target is IVoidFieldState
}
```

#### Example

```ts
import { createForm, isVoidFieldState } from '@formily/core'

const form = createForm()
const field = form.createVoidField({
  name: 'target',
})

console.log(isVoidFieldState(field)) //false
console.log(isVoidFieldState(field.getState())) //true
```

## isGeneralFieldState

#### Description

Determine whether an object is an IFieldState/IArrayFieldState/IObjectFieldState/IVoidFieldState object

#### Signature

```ts
interface isGeneralFieldState {
  (target: any): target is
    | IFieldState
    | IArrayFieldState
    | IObjectFieldState
    | IVoidFieldState
}
```

#### Example

```ts
import { createForm, isGeneralFieldState } from '@formily/core'

const form = createForm()

const field = form.createField({ name: 'target' })
const arr = form.createArrayField({ name: 'array' })
const obj = form.createObjectField({ name: 'object' })
const vod = form.createVoidField({ name: 'void' })

console.log(isGeneralFieldState(field)) //false
console.log(isGeneralFieldState(arr)) //false
console.log(isGeneralFieldState(obj)) //false
console.log(isGeneralFieldState(vod)) //false
console.log(isGeneralFieldState(field.getState())) //true
console.log(isGeneralFieldState(arr.getState())) //true
console.log(isGeneralFieldState(obj.getState())) //true
console.log(isGeneralFieldState(vod.getState())) //true
console.log(isGeneralFieldState({})) //false
```

## isDataFieldState

#### Description

Determine whether an object is an IFieldState/IArrayFieldState/IObjectFieldState object

#### Signature

```ts
interface isDataFieldState {
  (target: any): target is IFieldState | IArrayFieldState | IObjectFieldState
}
```

#### Example

```ts
import { createForm, isDataFieldState } from '@formily/core'

const form = createForm()

const field = form.createField({ name: 'target' })
const arr = form.createArrayField({ name: 'array' })
const obj = form.createObjectField({ name: 'object' })
const vod = form.createVoidField({ name: 'void' })

console.log(isDataFieldState(field)) //false
console.log(isDataFieldState(arr)) //false
console.log(isDataFieldState(obj)) //false
console.log(isDataFieldState(vod)) //false
console.log(isDataFieldState(field.getState())) //true
console.log(isDataFieldState(arr.getState())) //true
console.log(isDataFieldState(obj.getState())) //true
console.log(isDataFieldState(vod.getState())) //false
console.log(isDataFieldState({})) //false
```

## isQuery

#### Description

Determine whether an object is a Query object

#### Signature

```ts
interface isQuery {
  (target: any): target is Query
}
```

#### Example

```ts
import { createForm, isQuery } from '@formily/core'

const form = createForm()
console.log(isQuery(form.query('target'))) //true
```

```

--------------------------------------------------------------------------------
/packages/antd/docs/components/FormLayout.md:
--------------------------------------------------------------------------------

```markdown
# FormLayout

> Block-level layout batch control component, with the help of this component, we can easily control the layout mode of all FormItem components enclosed by FormLayout

## Markup Schema example

```tsx
import React from 'react'
import { Input, Select, FormItem, FormLayout } from '@formily/antd'
import { createForm } from '@formily/core'
import { FormProvider, createSchemaField } from '@formily/react'

const SchemaField = createSchemaField({
  components: {
    Input,
    Select,
    FormItem,
    FormLayout,
  },
})

const form = createForm()

export default () => (
  <FormProvider form={form}>
    <SchemaField>
      <SchemaField.Void
        x-component="FormLayout"
        x-component-props={{
          labelCol: 6,
          wrapperCol: 10,
        }}
      >
        <SchemaField.String
          name="input"
          title="input box"
          x-decorator="FormItem"
          x-decorator-props={{
            tooltip: <div>123</div>,
          }}
          x-component="Input"
          required
        />
        <SchemaField.String
          name="select"
          title="select box"
          x-decorator="FormItem"
          x-component="Select"
          required
        />
      </SchemaField.Void>
    </SchemaField>
  </FormProvider>
)
```

## JSON Schema case

```tsx
import React from 'react'
import { Input, Select, FormItem, FormLayout } from '@formily/antd'
import { createForm } from '@formily/core'
import { FormProvider, createSchemaField } from '@formily/react'

const SchemaField = createSchemaField({
  components: {
    Input,
    Select,
    FormItem,
    FormLayout,
  },
})

const schema = {
  type: 'object',
  properties: {
    layout: {
      type: 'void',
      'x-component': 'FormLayout',
      'x-component-props': {
        labelCol: 6,
        wrapperCol: 10,
        layout: 'vertical',
      },
      properties: {
        input: {
          type: 'string',
          title: 'input box',
          required: true,
          'x-decorator': 'FormItem',
          'x-decorator-props': {
            tooltip: <div>123</div>,
          },
          'x-component': 'Input',
        },
        select: {
          type: 'string',
          title: 'Select box',
          required: true,
          'x-decorator': 'FormItem',
          'x-component': 'Select',
        },
      },
    },
  },
}

const form = createForm()

export default () => (
  <FormProvider form={form}>
    <SchemaField schema={schema} />
  </FormProvider>
)
```

## Pure JSX case

```tsx
import React from 'react'
import {
  Input,
  Select,
  FormItem,
  FormButtonGroup,
  Submit,
  FormLayout,
} from '@formily/antd'
import { createForm } from '@formily/core'
import { FormProvider, Field } from '@formily/react'

const form = createForm()

export default () => (
  <FormProvider form={form}>
    <FormLayout labelCol={6} wrapperCol={10}>
      <Field
        name="input"
        required
        title="input box"
        decorator={[FormItem]}
        component={[Input]}
      />
      <Field
        name="select"
        required
        title="select box"
        decorator={[FormItem]}
        component={[Select]}
      />
      <FormButtonGroup.FormItem>
        <Submit onSubmit={console.log}>Submit</Submit>
      </FormButtonGroup.FormItem>
    </FormLayout>
  </FormProvider>
)
```

## API

| Property name  | Type                                                                                   | Description                                                 | Default value |
| -------------- | -------------------------------------------------------------------------------------- | ----------------------------------------------------------- | ------------- |
| style          | CSSProperties                                                                          | Style                                                       | -             |
| className      | string                                                                                 | class name                                                  | -             |
| colon          | boolean                                                                                | Is there a colon                                            | true          |
| requiredMark   | boolean \| `"optional"`                                                                | Required mark style. Can use required mark or optional mark | true          |
| labelAlign     | `'right' \| 'left' \| ('right' \| 'left')[]`                                           | Label content alignment                                     | -             |
| wrapperAlign   | `'right' \| 'left' \| ('right' \| 'left')[]`                                           | Component container content alignment                       | -             |
| labelWrap      | boolean                                                                                | Wrap label content                                          | false         |
| labelWidth     | number                                                                                 | Label width (px)                                            | -             |
| wrapperWidth   | number                                                                                 | Component container width (px)                              | -             |
| wrapperWrap    | boolean                                                                                | Component container wrap                                    | false         |
| labelCol       | `number \| number[]`                                                                   | Label width (24 column)                                     | -             |
| wrapperCol     | `number \| number[]`                                                                   | Component container width (24 column)                       | -             |
| fullness       | boolean                                                                                | Component container width 100%                              | false         |
| size           | `'small' \|'default' \|'large'`                                                        | component size                                              | default       |
| layout         | `'vertical' \| 'horizontal' \| 'inline' \| ('vertical' \| 'horizontal' \| 'inline')[]` | layout mode                                                 | horizontal    |
| direction      | `'rtl' \|'ltr'`                                                                        | direction (not supported yet)                               | ltr           |
| inset          | boolean                                                                                | Inline layout                                               | false         |
| shallow        | boolean                                                                                | shallow context transfer                                    | true          |
| feedbackLayout | `'loose' \|'terse' \|'popover' \|'none'`                                               | feedback layout                                             | true          |
| tooltipLayout  | `"icon" \| "text"`                                                                     | Ask the prompt layout                                       | `"icon"`      |
| tooltipIcon    | ReactNode                                                                              | Ask the prompt icon                                         | -             |
| bordered       | boolean                                                                                | Is there a border                                           | true          |
| breakpoints    | number[]                                                                               | Container size breakpoints                                  | -             |
| gridColumnGap  | number                                                                                 | Grid Column Gap                                             | 8             |
| gridRowGap     | number                                                                                 | Grid Row Gap                                                | 4             |
| spaceGap       | number                                                                                 | Space Gap                                                   | 8             |

```

--------------------------------------------------------------------------------
/docs/guide/upgrade.md:
--------------------------------------------------------------------------------

```markdown
# V2 Upgrade Guide

It is important to mention here that Formily2 is very different from Formily1.x, and there are a lot of Break Changes.

Therefore, for old users, they basically need to learn again, and V1 and V2 cannot be upgraded smoothly.

But the original intention of the Formily2 project is to reduce everyone's learning costs, because the old users themselves have a certain understanding of Formily's core ideas. In order to help old users learn Formily2 more quickly, this article will list the core differences between V1 and V2. , and will not list the new capabilities.

## Kernel Difference

> This mainly refers to the difference between @formily/core

Because Formily1.x users mainly use setFieldState/setFormState and getFieldState/getFormState when using the core APIs, these APIs are retained in V2, but the internal model properties are semantically different. The differences are as follows:

**modified**

- V1: Represent whether the field has been changed, in fact, it is of no use, because the initialization of the field means that it has been changed.
- V2: Indicates whether the field is manually modified, that is, it will be set to true when the component triggers the onChange event.

**inputed**

- V1: Represent Whether the field has been manually modified
- V2: Remove, use modified uniformly

**pristine**

- V1:Represent whether the field value is equal to initialValue
- V2: Remove, user manual judgment, this attribute will cause a lot of dirty checks

**display**

- V1: Represent whether the field is displayed, if it is false, the field value will not be removed
- V2: Represent the field display mode, the value is `"none" | "visible" | "hidden"`

**touched**

- V1: Redundant field
- V2: Remove

**validating**

- V1: Whether the representative field is being verified
- V2: Remove, use validateStatus uniformly

**effectErrors/effectWarnings**

- V1: Errors and warnings that represent the manual operation of the user
- V2: Remove, use feedbacks uniformly

**ruleErrors/ruleWarnings**

- V1: Errors and warnings representing the verification operation of the validator
- V2: Remove, use feedbacks uniformly

**values**

- V1: Represent all the parameters returned by the onChange event
- V2: Remove, use inputValues uniformly

**rules**

- V1: Represent verification rules
- V2: Remove, use validator uniformly, because rules literally means rules, but the meaning of rules is very big, not limited to verification rules

**props**

- V1: Represent the extended attributes of the component, and the positioning is very unclear. In the pure JSX scenario, it represents the collection of component attributes and FormItem attributes. In the Schema scenario, it represents the attributes of the Schema field.
- V2: Remove, use decorator and component uniformly

**VirtualField**

- V1: Represents a virtual field
- V2: Renamed and use [VoidField](https://core.formilyjs.org/api/models/void-field) uniformly

## Bridge layer differences

> This mainly refers to the difference between @formily/react and @formily/react-schema-renderer.

**createFormActions/createAsyncFormActions**

- V1 Create a Form operator, you can call the setFieldState/setFormState method.
- V2 is removed, and the operation status of the Form instance created by [createForm](https://core.formilyjs.org/api/entry/create-form) in @formily/core is used uniformly.

**Form**

- V1 will create a Form instance inside, which can control the transfer of values/initialValues attributes, etc.
- V2 removed, unified use of [FormProvider](https://react.formilyjs.org/api/components/form-provider)

**SchemaForm**

- V1 will parse the json-schema protocol internally, create a Form instance, support controlled mode, and render it.
- V2 is removed, the SchemaField component created by [createSchemaField](https://react.formilyjs.org/api/components/schema-field) is used uniformly, and the controlled mode is not supported.

**Field**

- V1 supports controlled mode, which requires the use of render props for component state mapping.
- V2 does not support controlled mode, you can quickly implement state mapping by passing in the decorator/component property.

**VirtualField**

- V1 supports controlled mode, which requires the use of render props for component state mapping.
- V2 does not support controlled mode, renamed [VoidField](https://react.formilyjs.org/api/components/void-field), and passed in the decorator/component property to quickly implement state mapping.

**FieldList**

- V1 Represent auto-incremented field control component
- V2 Renamed to [ArrayField](https://react.formilyjs.org/api/components/array-field)

**FormSpy**

- V1 Monitor all life cycle triggers and re-render
- V2 Remove and use [FormConsumer](https://react.formilyjs.org/api/components/form-consumer)

**SchemaMarkupField**

- V1 Stands for Schema description label component
- V2 Remove, unified use the description label component created by the [createSchemaField](https://react.formilyjs.org/api/components/schema-field)

**useFormQuery**

- V1 Fast Hook for realizing form query, supporting middleware mechanism
- V2 Temporarily remove

**useForm**

- V1 Represents the creation of a Form instance
- V2 Represents the Form instance in the consumption context, if you want to create it, please use [createForm](https://react.formilyjs.org/api/entry/create-form)

**useField**

- V1 Represents the creation of a Field instance
- V2 Represents the Field instance in the consumption context, if you want to create it, please call [form.createField](https://core.formilyjs.org/api/models/form#createfield)

**useVirtualField**

- V1 Represents the creation of a VirtualField instance
- V2 Remove, if you want to create, please call [form.createVoidField](https://core.formilyjs.org/api/models/form#createvoidfield)

**useFormState**

- V1 Form state in consumption context
- V2 Remove, use [useForm](https://react.formilyjs.org/api/hooks/use-form) uniformly

**useFieldState**

- V1 consume Field status in context
- V2 Remove, use [useField](https://react.formilyjs.org/api/hooks/use-field)

**useFormSpy**

- V1 Create a lifecycle listener and trigger a re-render
- V2 Remove

**useSchemaProps**

- V1Cconsume rops of SchemaField in context
- V2 Remove, use [useFieldSchema](https://react.formilyjs.org/api/hooks/use-field-schema) uniformly

**connect**

- V1 Standard HOC
- V2 The higher-order function is changed to 1st order, and the properties have changed dramatically. See the [connect document](https://react.formilyjs.org/api/shared/connect) for details

**registerFormField/registerVirtaulBox/registerFormComponent/registerFormItemComponent**

- V1 Globally registered components
- V2 Remove, global registration is no longer supported

**FormEffectHooks**

- V1 RxJS lifecycle hook
- V2 Remove, export from @formily/core uniformly, and will not return RxJS Observable object

**effects**

- V1 Support callback function`$` selector
- V2 Remove`$`selector

## Protocol layer differences

> This mainly refers to the difference in the JSON Schema protocol

**editable**

- V1 is directly in the Schema description, indicating whether the field can be edited
- V2 Renamed x-editable

**visible**

- V1 Indicates whether the field is displayed
- V2 Renamed x-visible

**display**

- V1 Represent whether the field is displayed or not, if it is false, it represents the hidden behavior without deleting the value
- V2 Renamed x-display, which represents the field display mode, and the value is`"none" | "visible" | "hidden"`

**triggerType**

- V1 Represent the field verification timing
- V2 Remove, please use`x-validator:[{triggerType:"onBlur",validator:()=>...}]`

**x-props**

- V1 Represents the FormItem property
- V2 Remove, please use x-decorator-props

**x-rules**

- V1 Represent field verification rules
- V2 Renamed x-validator

**x-linkages**

- V1 Represent field linkage
- V2 Remove, use x-reactions uniformly

**x-mega-props**

- V1 Represent the sub-component properties of the MegaLayout component
- V2 Remove

## Component library differences

In Formily 1.x, we mainly use @formily/antd and @formily/antd-components, or @formily/next and @formily/next-components.

In V2, we have the following changes:

- @formily/antd and @formily/antd-components were merged into @formily/antd, and the directory structure was changed to that of a pure component library.

- The internal API of @formily/react @formily/core will no longer be exported.
- Almost all components have been rewritten and cannot be smoothly upgraded.
- Remove styled-components.

```

--------------------------------------------------------------------------------
/packages/next/docs/components/Space.md:
--------------------------------------------------------------------------------

```markdown
# Space

> Super convenient Flex layout component, can help users quickly realize the layout of any element side by side next to each other

## Markup Schema example

```tsx
import React from 'react'
import {
  Input,
  FormItem,
  FormLayout,
  FormButtonGroup,
  Submit,
  Space,
} from '@formily/next'
import { createForm } from '@formily/core'
import { FormProvider, createSchemaField } from '@formily/react'

const SchemaField = createSchemaField({
  components: {
    Input,
    FormItem,
    Space,
  },
})

const form = createForm()

export default () => (
  <FormProvider form={form}>
    <FormLayout labelCol={6} wrapperCol={16}>
      <SchemaField>
        <SchemaField.Void
          title="name"
          x-decorator="FormItem"
          x-decorator-props={{
            asterisk: true,
            feedbackLayout: 'none',
          }}
          x-component="Space"
        >
          <SchemaField.String
            name="firstName"
            x-decorator="FormItem"
            x-component="Input"
            required
          />
          <SchemaField.String
            name="lastName"
            x-decorator="FormItem"
            x-component="Input"
            x-visible="{{$values.firstName === '123'}}"
            required
          />
          <SchemaField.String
            name="kk"
            x-decorator="FormItem"
            x-component="Input"
            x-decorator-props={{
              addonAfter: 'Unit',
            }}
            required
          />
        </SchemaField.Void>
        <SchemaField.Void
          title="Text concatenation"
          x-decorator="FormItem"
          x-decorator-props={{
            asterisk: true,
            feedbackLayout: 'none',
          }}
          x-component="Space"
        >
          <SchemaField.String
            name="aa"
            x-decorator="FormItem"
            x-component="Input"
            x-decorator-props={{
              addonAfter: 'Unit',
            }}
            required
          />
          <SchemaField.String
            name="bb"
            x-decorator="FormItem"
            x-component="Input"
            x-decorator-props={{
              addonAfter: 'Unit',
            }}
            required
          />
          <SchemaField.String
            name="cc"
            x-decorator="FormItem"
            x-component="Input"
            x-decorator-props={{
              addonAfter: 'Unit',
            }}
            required
          />
        </SchemaField.Void>
        <SchemaField.String
          name="textarea"
          title="text box"
          x-decorator="FormItem"
          required
          x-component="Input.TextArea"
          x-component-props={{
            style: {
              width: 400,
            },
          }}
        />
      </SchemaField>
      <FormButtonGroup.FormItem>
        <Submit onSubmit={console.log}>Submit</Submit>
      </FormButtonGroup.FormItem>
    </FormLayout>
  </FormProvider>
)
```

## JSON Schema case

```tsx
import React from 'react'
import {
  Input,
  FormItem,
  FormLayout,
  FormButtonGroup,
  Submit,
  Space,
} from '@formily/next'
import { createForm } from '@formily/core'
import { FormProvider, createSchemaField } from '@formily/react'

const SchemaField = createSchemaField({
  components: {
    Input,
    FormItem,
    Space,
  },
})

const form = createForm()

const schema = {
  type: 'object',
  properties: {
    name: {
      type: 'void',
      title: 'Name',
      'x-decorator': 'FormItem',
      'x-decorator-props': {
        asterisk: true,
        feedbackLayout: 'none',
      },
      'x-component': 'Space',
      properties: {
        firstName: {
          type: 'string',
          'x-decorator': 'FormItem',
          'x-component': 'Input',
          required: true,
        },
        lastName: {
          type: 'string',
          'x-decorator': 'FormItem',
          'x-component': 'Input',
          required: true,
        },
      },
    },
    texts: {
      type: 'void',
      title: 'Text concatenation',
      'x-decorator': 'FormItem',
      'x-decorator-props': {
        asterisk: true,
        feedbackLayout: 'none',
      },
      'x-component': 'Space',
      properties: {
        aa: {
          type: 'string',
          'x-decorator': 'FormItem',
          'x-decorator-props': {
            addonAfter: 'Unit',
          },
          'x-component': 'Input',
          required: true,
        },
        bb: {
          type: 'string',
          'x-decorator': 'FormItem',
          'x-decorator-props': {
            addonAfter: 'Unit',
          },
          'x-component': 'Input',
          required: true,
        },
        cc: {
          type: 'string',
          'x-decorator': 'FormItem',
          'x-decorator-props': {
            addonAfter: 'Unit',
          },
          'x-component': 'Input',
          required: true,
        },
      },
    },

    textarea: {
      type: 'string',
      title: 'Text box',
      'x-decorator': 'FormItem',
      'x-component': 'Input.TextArea',
      'x-component-props': {
        style: {
          width: 400,
        },
      },
      required: true,
    },
  },
}

export default () => (
  <FormProvider form={form}>
    <FormLayout labelCol={6} wrapperCol={16}>
      <SchemaField schema={schema} />
      <FormButtonGroup.FormItem>
        <Submit onSubmit={console.log}>Submit</Submit>
      </FormButtonGroup.FormItem>
    </FormLayout>
  </FormProvider>
)
```

## Pure JSX case

```tsx
import React from 'react'
import {
  Input,
  FormItem,
  FormLayout,
  FormButtonGroup,
  Submit,
  Space,
} from '@formily/next'
import { createForm } from '@formily/core'
import { FormProvider, Field, VoidField } from '@formily/react'

const form = createForm()

export default () => (
  <FormProvider form={form}>
    <FormLayout labelCol={6} wrapperCol={16}>
      <VoidField
        name="name"
        title="name"
        decorator={[
          FormItem,
          {
            asterisk: true,
            feedbackLayout: 'none',
          },
        ]}
        component={[Space]}
      >
        <Field
          name="firstName"
          decorator={[FormItem]}
          component={[Input]}
          required
        />
        <Field
          name="lastName"
          decorator={[FormItem]}
          component={[Input]}
          required
        />
      </VoidField>
      <VoidField
        name="texts"
        title="Text concatenation"
        decorator={[
          FormItem,
          {
            asterisk: true,
            feedbackLayout: 'none',
          },
        ]}
        component={[Space]}
      >
        <Field
          name="aa"
          decorator={[
            FormItem,
            {
              addonAfter: 'Unit',
            },
          ]}
          component={[Input]}
          required
        />
        <Field
          name="bb"
          decorator={[
            FormItem,
            {
              addonAfter: 'Unit',
            },
          ]}
          component={[Input]}
          required
        />
        <Field
          name="cc"
          decorator={[
            FormItem,
            {
              addonAfter: 'Unit',
            },
          ]}
          component={[Input]}
          required
        />
      </VoidField>
      <Field
        name="textarea"
        title="text box"
        decorator={[FormItem]}
        component={[
          Input.TextArea,
          {
            style: {
              width: 400,
            },
          },
        ]}
        required
      />
      <FormButtonGroup.FormItem>
        <Submit onSubmit={console.log}>Submit</Submit>
      </FormButtonGroup.FormItem>
    </FormLayout>
  </FormProvider>
)
```

## API

| Property name | Type                                      | Description     | Default value |
| ------------- | ----------------------------------------- | --------------- | ------------- |
| style         | CSSProperties                             | Style           | -             |
| className     | string                                    | class name      | -             |
| prefix        | string                                    | style prefix    | true          |
| size          | `number \|'small' \|'large' \|'middle'`   | interval size   | 8px           |
| direction     | `'horizontal' \|'vertical'`               | direction       | -             |
| align         | `'start' \|'end' \|'center' \|'baseline'` | align           | `'start'`     |
| wrap          | boolean                                   | Whether to wrap | false         |

```

--------------------------------------------------------------------------------
/packages/next/src/form-item/main.scss:
--------------------------------------------------------------------------------

```scss
@import '~@alifd/next/lib/core/index-noreset.scss';
@import './scss/variable.scss';
@import './grid.scss';
@import './animation.scss';

.#{$form-item-cls} {
  display: flex;
  margin-bottom: $form-item-m-margin-b + 2;
  position: relative;
  line-height: $form-element-medium-height;
  font-size: $form-element-medium-font-size;

  &-layout-vertical {
    display: block;
  }

  &-label-content {
    min-height: $form-element-medium-height;
  }

  &-control-content-component {
    line-height: $form-element-medium-height;
  }

  &-inset {
    padding: 0 8px;
    border: 1px solid $input-border-color;
    border-radius: $form-element-medium-corner;
    width: 100%;

    .#{$css-prefix}input {
      border: none !important;
      box-shadow: none !important;
      outline: none;
    }
  }

  &-bordered-none:not(.#{$form-item-cls}-inset) {
    .#{$css-prefix}input {
      border: none !important;
      box-shadow: none !important;
      outline: none;
    }
  }
}

.#{$form-item-cls}-label {
  position: relative;
  display: flex;
  &-content {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  &-tooltip {
    cursor: help;

    .#{$form-item-cls}-colon {
      display: flex;
    }

    * {
      cursor: help;
    }

    &-colon {
      cursor: help;
    }

    label {
      border-bottom: 1px dashed currentColor;
    }
  }
}

.#{$form-item-cls}-label label {
  cursor: text;
  color: rgba(0, 0, 0, 0.85);
}

.#{$form-item-cls}-label-align-left {
  > .#{$form-item-cls}-label {
    justify-content: flex-start;
  }
}

.#{$form-item-cls}-label-align-right {
  > .#{$form-item-cls}-label {
    justify-content: flex-end;
  }
}

.#{$form-item-cls}-label-wrap {
  .#{$form-item-cls}-label {
    label {
      white-space: pre-line;
      word-break: break-all;
    }
  }
}

.#{$form-item-cls}-feedback-layout-terse {
  margin-bottom: 8px;

  &.#{$form-item-cls}-feedback-has-text:not(.#{$form-item-cls}-inset) {
    margin-bottom: 0;
  }
}

.#{$form-item-cls}-feedback-layout-loose {
  margin-bottom: $form-item-m-margin-b + 4;

  &.#{$form-item-cls}-feedback-has-text:not(.#{$form-item-cls}-inset) {
    margin-bottom: 0;
  }
}

.#{$form-item-cls}-feedback-layout-none {
  margin-bottom: 0px;

  &.#{$form-item-cls}-feedback-has-text:not(.#{$form-item-cls}-inset) {
    margin-bottom: 0;
  }
}

.#{$form-item-cls}-control {
  flex: 1;
  max-width: 100%;

  .#{$form-item-cls}-control-content {
    display: flex;

    .#{$form-item-cls}-control-content-component {
      width: 100%;
      min-height: $form-element-medium-height;
      line-height: $form-element-medium-height;

      &-has-feedback-icon {
        flex: 1;
        position: relative;
        display: flex;
        align-items: center;

        .#{$css-prefix}input {
          height: 100%;
          border: none !important;
          box-shadow: none !important;
          outline: none;

          .#{$css-prefix}input-control {
            margin-right: 0;
            padding-right: 0;

            .#{$css-prefix}icon {
              display: none;
            }

            .#{$css-prefix}input-hint-wrap {
              .#{$css-prefix}icon {
                display: block;
              }
            }
          }
        }
      }

      .#{$css-prefix}range {
        height: 100%;
        padding-left: 2px;
      }

      .#{$css-prefix}transfer {
        display: flex;
        align-items: center;
      }
    }

    .#{$form-item-cls}-addon-before {
      margin-right: 8px;
      display: inline-flex;
      align-items: center;
      min-height: $form-element-medium-height;
      flex-shrink: 0;
    }

    .#{$form-item-cls}-addon-after {
      margin-left: 8px;
      display: inline-flex;
      align-items: center;
      min-height: $form-element-medium-height;
      flex-shrink: 0;
    }
  }
}

.#{$form-item-cls}-size-small {
  font-size: $form-element-small-font-size;
  line-height: $form-element-small-height;

  .#{$form-item-cls}-label-content {
    min-height: $form-element-small-height;
  }

  .#{$form-item-cls}-control-content {
    .#{$form-item-cls}-control-content-component {
      line-height: $form-element-small-height;
      min-height: $form-element-small-height;
    }

    .#{$form-item-cls}-addon-before {
      min-height: $form-element-small-height;
    }

    .#{$form-item-cls}-addon-after {
      min-height: $form-element-small-height;
    }
  }

  &-inset {
    border-radius: $form-element-small-corner;
  }

  &.#{$form-item-cls}-feedback-layout-terse {
    margin-bottom: 8px;

    &.#{$form-item-cls}-feedback-has-text:not(.#{$form-item-cls}-inset) {
      margin-bottom: 0;
    }
  }

  &.#{$form-item-cls}-feedback-layout-loose {
    margin-bottom: $form-item-s-margin-b + 8;

    &.#{$form-item-cls}-feedback-has-text:not(.#{$form-item-cls}-inset) {
      margin-bottom: 0;
    }
  }

  .#{$form-item-cls}-help,
  .#{$form-item-cls}-extra {
    min-height: $form-element-small-font-size + 2;
  }

  .#{$form-item-cls}-control-content {
    min-height: $form-element-small-height;
  }

  .#{$form-item-cls}-label > label {
    height: $form-element-small-height;
  }
}

.#{$form-item-cls}-size-large {
  font-size: $form-element-large-font-size;
  line-height: $form-element-large-height;

  .#{$form-item-cls}-label-content {
    min-height: $form-element-large-height;
  }

  .#{$form-item-cls}-control-content {
    .#{$form-item-cls}-control-content-component {
      line-height: $form-element-large-height;
      min-height: $form-element-large-height;
    }

    .#{$form-item-cls}-addon-before {
      min-height: $form-element-large-height;
    }

    .#{$form-item-cls}-addon-after {
      min-height: $form-element-large-height;
    }
  }

  &-inset {
    border-radius: $form-element-large-corner;
  }

  .#{$form-item-cls}-help,
  .#{$form-item-cls}-extra {
    min-height: $form-element-large-font-size + 2;
  }

  &.#{$form-item-cls}-feedback-layout-loose {
    margin-bottom: $form-item-l-margin-b;

    &.#{$form-item-cls}-feedback-has-text:not(.#{$form-item-cls}-inset) {
      margin-bottom: 0;
    }
  }

  .#{$form-item-cls}-control-content {
    min-height: $form-element-large-height;
  }

  .#{$form-item-cls}-label > label {
    height: $form-element-large-height;
  }
}

.#{$form-item-cls}-feedback-layout-popover {
  margin-bottom: 8px;
}

.#{$form-item-cls}-label-tooltip-icon {
  margin-left: 4px;
  color: #00000073;
  cursor: pointer;
  max-height: $form-element-medium-height;
}

.#{$form-item-cls}-control-align-left {
  .#{$form-item-cls}-control-content {
    justify-content: flex-start;
  }
}

.#{$form-item-cls}-control-align-right {
  .#{$form-item-cls}-control-content {
    justify-content: flex-end;
  }
}

.#{$form-item-cls}-control-wrap {
  .#{$form-item-cls}-control {
    white-space: pre-line;
  }
}

.#{$form-item-cls}-asterisk {
  color: $form-error-color;
  margin-right: 4px;
  display: inline-block;
  font-family: SimSun, sans-serif;
}

.#{$form-item-cls}-colon {
  margin-left: 2px;
  margin-right: 8px;
}

.#{$form-item-cls}-help,
.#{$form-item-cls}-extra {
  clear: both;
  line-height: $form-item-m-margin-b + 4;
  min-height: $form-item-m-margin-b;
  color: rgba(0, 0, 0, 0.45);
  transition: color 0.3s cubic-bezier(0.215, 0.61, 0.355, 1);
  padding-top: 0px;
}

.#{$form-item-cls}-fullness {
  > .#{$form-item-cls}-control {
    > .#{$form-item-cls}-control-content {
      > .#{$form-item-cls}-control-content-component {
        > *:first-child:not(.#{$css-prefix}switch):not(.#{$css-prefix}icon):not(.#{$css-prefix}formily-icon):not(.anticon):not(.#{$css-prefix}btn-text) {
          width: 100%;
        }
      }
    }
  }
}

.#{$form-item-cls}-control-content-component-has-feedback-icon {
  border-radius: 2px;
  border: 1px solid $input-border-color;
  transition: all 0.3s;
  touch-action: manipulation;
  outline: none;
  padding-right: 8px;

  .#{$css-prefix}input {
    border: none !important;
  }

  .#{$css-prefix}range-picker-trigger {
    border: none !important;
  }

  .#{$form-item-cls}-feedback-icon {
    display: flex;
    justify-content: center;
    align-items: center;
    margin-right: -2px;
    padding-left: 2px;

    .anticon {
      display: inline-block;
      color: inherit;
      font-style: normal;
      font-size: $form-element-medium-font-size;
      line-height: 0;
      text-align: center;
      text-transform: none;
      vertical-align: -0.125em;
      text-rendering: optimizeLegibility;
    }
  }
}

.#{$form-item-cls}-error-help {
  color: $form-error-color;
}

.#{$form-item-cls}-warning-help {
  color: $form-warning-color;
}

.#{$form-item-cls}-success-help {
  color: $form-success-color;
}

```

--------------------------------------------------------------------------------
/packages/reactive/src/__tests__/annotations.spec.ts:
--------------------------------------------------------------------------------

```typescript
import { observable, action, model, define } from '../'
import { autorun, reaction } from '../autorun'
import { observe } from '../observe'
import { isObservable } from '../externals'
import { untracked } from '../untracked'
import { getObservableMaker } from '../internals'

test('observable annotation', () => {
  const obs = observable<any>({
    aa: 111,
  })
  const handler = jest.fn()
  const handler1 = jest.fn()
  observe(obs, handler1)
  reaction(() => {
    handler(obs.aa)
  })
  obs.aa = { bb: { cc: 123 } }
  obs.aa.bb = 333
  expect(handler).toBeCalledTimes(2)
  expect(handler1).toBeCalledTimes(2)

  const handler2 = jest.fn()
  const handler3 = jest.fn()
  const obsAnno = getObservableMaker(observable)({ value: obs })

  observe(obsAnno, handler2)
  reaction(() => {
    handler3(obsAnno.aa)
  })
  obsAnno.aa = { bb: { cc: 123 } }
  obsAnno.aa.bb = 333
  expect(handler2).toBeCalledTimes(2)
  expect(handler3).toBeCalledTimes(2)
})

test('shallow annotation', () => {
  const obs = observable.shallow<any>({
    aa: 111,
  })
  const handler = jest.fn()
  const handler1 = jest.fn()
  observe(obs, handler1)
  reaction(() => {
    handler(obs.aa)
  })
  obs.aa = { bb: { cc: 123 } }
  expect(isObservable(obs)).toBe(true)
  expect(isObservable(obs.aa)).toBe(false)
  expect(isObservable(obs.aa.bb)).toBe(false)
  obs.aa.bb = 333
  obs.cc = 444
  expect(handler).toBeCalledTimes(2)
  expect(handler1).toBeCalledTimes(2)
})

test('box annotation', () => {
  const obs = observable.box(123)
  const handler = jest.fn()
  const handler1 = jest.fn()
  observe(obs, handler1)
  reaction(() => {
    handler(obs.get())
  })
  const boxValue = 333
  obs.set(boxValue)
  expect(handler1).toBeCalledTimes(1)
  expect(handler1.mock.calls[0][0]).toMatchObject({
    value: boxValue,
  })
  expect(handler).toBeCalledTimes(2)
  expect(handler.mock.calls[0][0]).toBe(123)
  expect(handler.mock.calls[1][0]).toBe(boxValue)
})

test('ref annotation', () => {
  const obs = observable.ref(123)
  const handler = jest.fn()
  const handler1 = jest.fn()
  observe(obs, handler1)
  reaction(() => {
    handler(obs.value)
  })
  obs.value = 333
  expect(handler).nthCalledWith(1, 123)
  expect(handler).nthCalledWith(2, 333)
  expect(handler1).toBeCalledTimes(1)
})

test('action annotation', () => {
  const obs = observable<any>({})
  const setData = action.bound(() => {
    obs.aa = 123
    obs.bb = 321
  })
  const handler = jest.fn()
  reaction(() => {
    return [obs.aa, obs.bb]
  }, handler)
  setData()
  expect(handler).toBeCalledTimes(1)
  expect(handler).toBeCalledWith([123, 321], [undefined, undefined])
})

test('no action annotation', () => {
  const obs = observable<any>({})
  const setData = () => {
    obs.aa = 123
    obs.bb = 321
  }
  const handler = jest.fn()
  reaction(() => {
    return [obs.aa, obs.bb]
  }, handler)
  setData()
  expect(handler).toBeCalledTimes(2)
  expect(handler).nthCalledWith(1, [123, undefined], [undefined, undefined])
  expect(handler).nthCalledWith(2, [123, 321], [123, undefined])
})

test('computed annotation', () => {
  const obs = observable({
    aa: 11,
    bb: 22,
  })
  const handler = jest.fn(() => obs.aa + obs.bb)
  const runner1 = jest.fn()
  const runner2 = jest.fn()
  const runner3 = jest.fn()
  const compu = observable.computed(handler)
  expect(compu.value).toEqual(33)
  expect(handler).toBeCalledTimes(1)
  obs.aa = 22
  expect(handler).toBeCalledTimes(1)
  const dispose = autorun(() => {
    compu.value
    runner1()
  })
  const dispose2 = autorun(() => {
    compu.value
    runner2()
  })
  expect(compu.value).toEqual(44)
  expect(handler).toBeCalledTimes(2)
  obs.bb = 33
  expect(runner1).toBeCalledTimes(2)
  expect(runner2).toBeCalledTimes(2)
  expect(handler).toBeCalledTimes(3)
  expect(compu.value).toEqual(55)
  expect(handler).toBeCalledTimes(3)
  obs.aa = 11
  expect(runner1).toBeCalledTimes(3)
  expect(runner2).toBeCalledTimes(3)
  expect(handler).toBeCalledTimes(4)
  expect(compu.value).toEqual(44)
  expect(handler).toBeCalledTimes(4)
  dispose()
  obs.aa = 22
  expect(runner1).toBeCalledTimes(3)
  expect(runner2).toBeCalledTimes(4)
  expect(handler).toBeCalledTimes(5)
  expect(compu.value).toEqual(55)
  expect(handler).toBeCalledTimes(5)
  dispose2()
  obs.aa = 33
  expect(runner1).toBeCalledTimes(3)
  expect(runner2).toBeCalledTimes(4)
  expect(handler).toBeCalledTimes(5)
  expect(compu.value).toEqual(66)
  expect(handler).toBeCalledTimes(6)
  expect(compu.value).toEqual(66)
  expect(handler).toBeCalledTimes(6)
  autorun(() => {
    compu.value
    runner3()
  })
  expect(compu.value).toEqual(66)
  expect(handler).toBeCalledTimes(6)
  expect(compu.value).toEqual(66)
  expect(handler).toBeCalledTimes(6)
  obs.aa = 11
  expect(handler).toBeCalledTimes(7)
  expect(compu.value).toEqual(44)
  expect(handler).toBeCalledTimes(7)
})

test('computed chain annotation', () => {
  const obs = observable({
    aa: 11,
    bb: 22,
  })
  const handler = jest.fn(() => obs.aa + obs.bb)
  const compu1 = observable.computed(handler)
  const handler1 = jest.fn(() => compu1.value + 33)
  const compu2 = observable.computed(handler1)
  const dispose = autorun(() => {
    compu2.value
  })
  expect(handler).toBeCalledTimes(1)
  expect(handler1).toBeCalledTimes(1)
  expect(compu2.value).toEqual(66)
  expect(handler).toBeCalledTimes(1)
  expect(handler1).toBeCalledTimes(1)
  obs.aa = 22
  expect(handler).toBeCalledTimes(2)
  expect(handler1).toBeCalledTimes(2)
  expect(compu2.value).toEqual(77)
  expect(handler).toBeCalledTimes(2)
  expect(handler1).toBeCalledTimes(2)
  dispose()
  obs.aa = 11
  expect(handler).toBeCalledTimes(2)
  expect(handler1).toBeCalledTimes(2)
  expect(compu2.value).toEqual(66)
  expect(handler).toBeCalledTimes(3)
  expect(handler1).toBeCalledTimes(3)
})

test('computed with array length', () => {
  const obs = model({
    arr: [],
    get isEmpty() {
      return this.arr.length === 0
    },
    get isNotEmpty() {
      return !this.isEmpty
    },
  })
  const handler = jest.fn()
  autorun(() => {
    handler(obs.isEmpty)
    handler(obs.isNotEmpty)
  })
  expect(handler).toBeCalledTimes(2)
  obs.arr = ['1']
  obs.arr = []
  expect(handler).toBeCalledTimes(6)
})

test('computed with computed array length', () => {
  const obs = model({
    arr: [],
    get arr2() {
      return this.arr.map((item: number) => item + 1)
    },
    get isEmpty() {
      return this.arr2.length === 0
    },
    get isNotEmpty() {
      return !this.isEmpty
    },
  })
  const handler = jest.fn()
  const handler2 = jest.fn()
  autorun(() => {
    handler(obs.isNotEmpty)
    handler2(obs.arr2)
  })
  expect(handler).toBeCalledTimes(1)
  expect(handler).lastCalledWith(false)
  expect(handler2).toBeCalledTimes(1)
  expect(handler2.mock.calls[0][0]).toEqual([])
  obs.arr.push(1)
  expect(handler).lastCalledWith(true)
  expect(handler2.mock.calls[1][0]).toEqual([2])
  obs.arr = []
  expect(handler).lastCalledWith(false)
  expect(handler2.mock.calls[2][0]).toEqual([])
})

test('computed recollect dependencies', () => {
  const computed = jest.fn()
  const obs = model({
    aa: 'aaa',
    bb: 'bbb',
    cc: 'ccc',
    get compute() {
      computed()
      if (this.aa === 'aaa') {
        return this.bb
      }
      return this.cc
    },
  })
  const handler = jest.fn()
  autorun(() => {
    handler(obs.compute)
  })
  obs.aa = '111'
  obs.bb = '222'
  expect(computed).toBeCalledTimes(2)
})

test('computed no params', () => {
  observable.computed(null)
})

test('computed object params', () => {
  observable.computed({ get: () => {} })
})

test('computed no track get', () => {
  const obs = observable({ aa: 123 })
  const compu = observable.computed({ get: () => obs.aa })
  untracked(() => {
    expect(compu.value).toBe(123)
  })
})

test('computed cache descriptor', () => {
  class A {
    _value = 0
    constructor() {
      define(this, {
        _value: observable.ref,
        value: observable.computed,
      })
    }

    get value() {
      return this._value
    }
  }
  const obs1 = new A()
  const obs2 = new A()
  const handler1 = jest.fn()
  const handler2 = jest.fn()
  autorun(() => {
    handler1(obs1.value)
  })
  autorun(() => {
    handler2(obs2.value)
  })
  expect(handler1).toBeCalledTimes(1)
  expect(handler2).toBeCalledTimes(1)
  obs1._value = 123
  obs2._value = 123
  expect(handler1).toBeCalledTimes(2)
  expect(handler2).toBeCalledTimes(2)
})

test('computed normal object', () => {
  const obs = define(
    {
      _value: 0,
      get value() {
        return this._value
      },
    },
    {
      _value: observable.ref,
      value: observable.computed,
    }
  )
  const handler = jest.fn()
  autorun(() => {
    handler(obs.value)
  })
  expect(handler).toBeCalledTimes(1)
  obs._value = 123
  expect(handler).toBeCalledTimes(2)
})

```

--------------------------------------------------------------------------------
/packages/react/src/__tests__/field.spec.tsx:
--------------------------------------------------------------------------------

```typescript
import React from 'react'
import { act } from 'react-dom/test-utils'
import { render, fireEvent, waitFor } from '@testing-library/react'
import { createForm, onFieldUnmount, isArrayField } from '@formily/core'
import {
  isField,
  Field as FieldType,
  isVoidField,
  onFieldChange,
} from '@formily/core'
import {
  FormProvider,
  ArrayField,
  ObjectField,
  VoidField,
  Field,
  useField,
  useFormEffects,
  observer,
  connect,
  mapProps,
  mapReadPretty,
} from '..'
import { ReactiveField } from '../components/ReactiveField'
import { expectThrowError } from './shared'

type InputProps = {
  value?: string
  onChange?: (...args: any) => void
}

type CustomProps = {
  list?: string[]
}

const Decorator = (props) => <div>{props.children}</div>
const Input: React.FC<React.PropsWithChildren<InputProps>> = (props) => (
  <input
    {...props}
    value={props.value || ''}
    data-testid={useField().path.toString()}
  />
)

const Normal = () => <div></div>

test('render field', async () => {
  const form = createForm()
  const onChange = jest.fn()
  const { getByTestId, queryByTestId, unmount } = render(
    <FormProvider form={form}>
      <Field
        name="aa"
        decorator={[Decorator]}
        component={[Input, { onChange }]}
      />
      <ArrayField name="bb" decorator={[Decorator]}>
        <div data-testid="bb-children"></div>
      </ArrayField>
      <ObjectField name="cc" decorator={[Decorator]}>
        <Field name="mm" decorator={[Decorator]} component={[Input]} />
        <ObjectField name="pp" decorator={[Decorator]} />
        <ArrayField name="tt" decorator={[Decorator]} />
        <VoidField name="ww" />
      </ObjectField>
      <VoidField name="dd" decorator={[Decorator]}>
        {() => (
          <div data-testid="dd-children">
            <Field name="oo" decorator={[Decorator]} component={[Input]} />
          </div>
        )}
      </VoidField>
      <VoidField name="xx" decorator={[Decorator]} component={[Normal]} />
      <Field
        name="ee"
        visible={false}
        decorator={[Decorator]}
        component={[Input]}
      />
      <Field name="ff" decorator={[]} component={[]} />
      <Field name="gg" decorator={null} component={null} />
      <Field name="hh" decorator={[null]} component={[null, null]} />
      <Field
        name="kk"
        decorator={[Decorator]}
        component={[Input, { onChange: null }]}
      />
    </FormProvider>
  )
  expect(form.mounted).toBeTruthy()
  expect(form.query('aa').take().mounted).toBeTruthy()
  expect(form.query('bb').take().mounted).toBeTruthy()
  expect(form.query('cc').take().mounted).toBeTruthy()
  expect(form.query('dd').take().mounted).toBeTruthy()
  fireEvent.change(getByTestId('aa'), {
    target: {
      value: '123',
    },
  })
  fireEvent.change(getByTestId('kk'), {
    target: {
      value: '123',
    },
  })
  expect(onChange).toBeCalledTimes(1)
  expect(getByTestId('bb-children')).not.toBeUndefined()
  expect(getByTestId('dd-children')).not.toBeUndefined()
  expect(queryByTestId('ee')).toBeNull()
  expect(form.query('aa').get('value')).toEqual('123')
  expect(form.query('kk').get('value')).toEqual('123')
  unmount()
})

test('render field no context', () => {
  expectThrowError(() => {
    return (
      <>
        <Field name="aa">{() => <div></div>}</Field>
        <ArrayField name="bb">
          <div></div>
        </ArrayField>
        <ObjectField name="cc" />
        <VoidField name="dd" />
      </>
    )
  })
})

test('ReactiveField', () => {
  render(<ReactiveField field={null} />)
  render(<ReactiveField field={null}>{() => <div></div>}</ReactiveField>)
})

test('useAttach basic', async () => {
  const form = createForm()
  const MyComponent = (props: any) => {
    return (
      <FormProvider form={form}>
        <Field name={props.name} decorator={[Decorator]} component={[Input]} />
      </FormProvider>
    )
  }
  const { rerender } = render(<MyComponent name="aa" />)
  expect(form.query('aa').take().mounted).toBeTruthy()
  rerender(<MyComponent name="bb" />)
  await waitFor(() => {
    expect(form.query('aa').take().mounted).toBeFalsy()
    expect(form.query('bb').take().mounted).toBeTruthy()
  })
})

test('useAttach with array field', async () => {
  const form = createForm()
  const MyComponent = () => {
    return (
      <FormProvider form={form}>
        <ArrayField
          name="array"
          initialValue={[{ input: '11' }, { input: '22' }]}
        >
          {(field) => {
            return field.value.map((val, index) => {
              return (
                <Field
                  key={index}
                  name={index + '.input'}
                  decorator={[Decorator]}
                  component={[Input]}
                />
              )
            })
          }}
        </ArrayField>
      </FormProvider>
    )
  }
  render(<MyComponent />)
  await waitFor(() => {
    expect(form.query('array.0.input').take().mounted).toBeTruthy()
    expect(form.query('array.1.input').take().mounted).toBeTruthy()
  })
  form.query('array').take((field) => {
    if (isArrayField(field)) {
      field.moveDown(0)
    }
  })
  await waitFor(() => {
    expect(form.query('array.0.input').take().mounted).toBeTruthy()
    expect(form.query('array.1.input').take().mounted).toBeTruthy()
  })
})

test('useFormEffects', async () => {
  const form = createForm()
  const CustomField = observer(() => {
    const field = useField<FieldType>()
    useFormEffects(() => {
      onFieldChange('aa', ['value'], (target) => {
        if (isVoidField(target)) return
        field.setValue(target.value)
      })
    })
    return <div data-testid="custom-value">{field.value}</div>
  })
  act(async () => {
    const { queryByTestId, rerender } = render(
      <FormProvider form={form}>
        <Field name="aa" decorator={[Decorator]} component={[Input]} />
        <Field name="bb" component={[CustomField, { tag: 'xxx' }]} />
      </FormProvider>
    )

    expect(queryByTestId('custom-value')?.textContent).toEqual('')
    form.query('aa').take((aa) => {
      if (isField(aa)) {
        aa.setValue('123')
      }
    })
    await waitFor(() => {
      expect(queryByTestId('custom-value')?.textContent).toEqual('123')
    })
    rerender(
      <FormProvider form={form}>
        <Field name="aa" decorator={[Decorator]} component={[Input]} />
        <Field name="bb" component={[CustomField, { tag: 'yyy' }]} />
      </FormProvider>
    )
  })
})

test('connect', async () => {
  const CustomField = connect(
    (props: CustomProps) => {
      return <div>{props.list}</div>
    },
    mapProps({ value: 'list', loading: true }, (props, field) => {
      return {
        ...props,
        mounted: field.mounted ? 1 : 2,
      }
    }),
    mapReadPretty(() => <div>read pretty</div>)
  )
  const BaseComponent = (props: any) => {
    return <div>{props.value}</div>
  }
  BaseComponent.displayName = 'BaseComponent'
  const CustomField2 = connect(
    BaseComponent,
    mapProps({ value: true, loading: true }),
    mapReadPretty(() => <div>read pretty</div>)
  )
  const form = createForm()
  const MyComponent = () => {
    return (
      <FormProvider form={form}>
        <Field name="aa" decorator={[Decorator]} component={[CustomField]} />
        <Field name="bb" decorator={[Decorator]} component={[CustomField2]} />
      </FormProvider>
    )
  }
  const { queryByText } = render(<MyComponent />)
  form.query('aa').take((field) => {
    field.setState((state) => {
      state.value = '123'
    })
  })
  await waitFor(() => {
    expect(queryByText('123')).toBeVisible()
  })

  form.query('aa').take((field) => {
    if (!isField(field)) return
    field.readPretty = true
  })
  await waitFor(() => {
    expect(queryByText('123')).toBeNull()
    expect(queryByText('read pretty')).toBeVisible()
  })
})

test('fields unmount and validate', async () => {
  const fn = jest.fn()
  const form = createForm({
    initialValues: {
      parent: {
        type: 'mounted',
      },
    },
    effects: () => {
      onFieldUnmount('parent.child', () => {
        fn()
      })
    },
  })
  const Parent = observer(() => {
    const field = useField<FieldType>()
    if (field.value.type === 'mounted') {
      return (
        <Field
          name="child"
          component={[Input]}
          validator={{ required: true }}
        />
      )
    }
    return <div data-testid="unmounted"></div>
  })

  const MyComponent = () => {
    return (
      <FormProvider form={form}>
        <Field name="parent" component={[Parent]} />
      </FormProvider>
    )
  }
  render(<MyComponent />)

  try {
    await form.validate()
  } catch {}

  expect(form.invalid).toBeTruthy()

  form.query('parent').take((field) => {
    field.setState((state) => {
      state.value.type = 'unmounted'
    })
  })

  await waitFor(() => {
    expect(fn.mock.calls.length).toBe(1)
  })

  try {
    await form.validate()
  } catch {}
  expect(form.invalid).toBeTruthy()
})

```

--------------------------------------------------------------------------------
/packages/next/docs/components/FormDialog.md:
--------------------------------------------------------------------------------

```markdown
# FormDialog

> Pop-up form, mainly used in simple event to open the form scene

## Markup Schema example

```tsx
import React from 'react'
import { FormDialog, FormItem, Input, FormLayout } from '@formily/next'
import { createSchemaField } from '@formily/react'
import { Button } from '@alifd/next'

const SchemaField = createSchemaField({
  components: {
    FormItem,
    Input,
  },
})

export default () => {
  return (
    <Button
      onClick={() => {
        FormDialog('Pop-up form', () => {
          return (
            <FormLayout labelCol={6} wrapperCol={14}>
              <SchemaField>
                <SchemaField.String
                  name="aaa"
                  required
                  title="input box 1"
                  x-decorator="FormItem"
                  x-component="Input"
                />
                <SchemaField.String
                  name="bbb"
                  required
                  title="input box 2"
                  x-decorator="FormItem"
                  x-component="Input"
                />
                <SchemaField.String
                  name="ccc"
                  required
                  title="input box 3"
                  x-decorator="FormItem"
                  x-component="Input"
                />
                <SchemaField.String
                  name="ddd"
                  required
                  title="input box 4"
                  x-decorator="FormItem"
                  x-component="Input"
                />
              </SchemaField>
              <FormDialog.Footer>
                <span style={{ marginLeft: 4 }}>Extended copywriting</span>
              </FormDialog.Footer>
            </FormLayout>
          )
        })
          .open({
            initialValues: {
              aaa: '123',
            },
          })
          .then(console.log)
      }}
    >
      Click me to open the form
    </Button>
  )
}
```

## JSON Schema case

```tsx
import React from 'react'
import { FormDialog, FormItem, Input, FormLayout } from '@formily/next'
import { createSchemaField } from '@formily/react'
import { Button } from '@alifd/next'

const SchemaField = createSchemaField({
  components: {
    FormItem,
    Input,
  },
})

const schema = {
  type: 'object',
  properties: {
    aaa: {
      type: 'string',
      title: 'input box 1',
      required: true,
      'x-decorator': 'FormItem',
      'x-component': 'Input',
    },
    bbb: {
      type: 'string',
      title: 'input box 2',
      required: true,
      'x-decorator': 'FormItem',
      'x-component': 'Input',
    },
    ccc: {
      type: 'string',
      title: 'input box 3',
      required: true,
      'x-decorator': 'FormItem',
      'x-component': 'Input',
    },
    ddd: {
      type: 'string',
      title: 'input box 4',
      required: true,
      'x-decorator': 'FormItem',
      'x-component': 'Input',
    },
  },
}

export default () => {
  return (
    <Button
      onClick={() => {
        FormDialog('Pop-up form', () => {
          return (
            <FormLayout labelCol={6} wrapperCol={14}>
              <SchemaField schema={schema} />
              <FormDialog.Footer>
                <span style={{ marginLeft: 4 }}>Extended copywriting</span>
              </FormDialog.Footer>
            </FormLayout>
          )
        })
          .open({
            initialValues: {
              aaa: '123',
            },
          })
          .then(console.log)
      }}
    >
      Click me to open the form
    </Button>
  )
}
```

## Pure JSX case

```tsx
import React from 'react'
import { FormDialog, FormItem, Input, FormLayout } from '@formily/next'
import { Field } from '@formily/react'
import { Button } from '@alifd/next'

export default () => {
  return (
    <Button
      onClick={() => {
        FormDialog('Pop-up form', () => {
          return (
            <FormLayout labelCol={6} wrapperCol={14}>
              <Field
                name="aaa"
                required
                title="input box 1"
                decorator={[FormItem]}
                component={[Input]}
              />
              <Field
                name="bbb"
                required
                title="input box 2"
                decorator={[FormItem]}
                component={[Input]}
              />
              <Field
                name="ccc"
                required
                title="input box 3"
                decorator={[FormItem]}
                component={[Input]}
              />
              <Field
                name="ddd"
                required
                title="input box 4"
                decorator={[FormItem]}
                component={[Input]}
              />
              <FormDialog.Footer>
                <span style={{ marginLeft: 4 }}>Extended copywriting</span>
              </FormDialog.Footer>
            </FormLayout>
          )
        })
          .open({
            initialValues: {
              aaa: '123',
            },
          })
          .then(console.log)
      }}
    >
      Click me to open the form
    </Button>
  )
}
```

## Use Fusion Context

```tsx
import React from 'react'
import { FormDialog, FormItem, Input, FormLayout } from '@formily/next'
import { Field } from '@formily/react'
import { Button, ConfigProvider } from '@alifd/next'

export default () => {
  return (
    <ConfigProvider
      locale={{
        Dialog: {
          ok: 'OK',
          cancel: 'Cancel',
        },
      }}
      defaultPropsConfig={{
        Dialog: {
          isFullScreen: true,
          footerActions: ['cancel', 'ok'],
        },
      }}
    >
      <Button
        onClick={() => {
          FormDialog('Pop-up form', () => {
            return (
              <FormLayout labelCol={6} wrapperCol={14}>
                <Field
                  name="aaa"
                  required
                  title="input box 1"
                  decorator={[FormItem]}
                  component={[Input]}
                />
                <Field
                  name="bbb"
                  required
                  title="input box 2"
                  decorator={[FormItem]}
                  component={[Input]}
                />
                <Field
                  name="ccc"
                  required
                  title="input box 3"
                  decorator={[FormItem]}
                  component={[Input]}
                />
                <Field
                  name="ddd"
                  required
                  title="input box 4"
                  decorator={[FormItem]}
                  component={[Input]}
                />
                <FormDialog.Footer>
                  <span style={{ marginLeft: 4 }}>Extended copywriting</span>
                </FormDialog.Footer>
              </FormLayout>
            )
          })
            .open({
              initialValues: {
                aaa: '123',
              },
            })
            .then(console.log)
        }}
      >
        Click me to open the form
      </Button>
    </ConfigProvider>
  )
}
```

## API

### FormDialog

```ts pure
import { IFormProps, Form } from '@formily/core'

type FormDialogRenderer =
  | React.ReactElement
  | ((form: Form) => React.ReactElement)

interface IFormDialog {
  forOpen(
    middleware: (
      props: IFormProps,
      next: (props?: IFormProps) => Promise<any>
    ) => any
  ): any //Middleware interceptor, can intercept Dialog to open
  forConfirm(
    middleware: (props: Form, next: (props?: Form) => Promise<any>) => any
  ): any //Middleware interceptor, which can intercept Dialog confirmation
  forCancel(
    middleware: (props: Form, next: (props?: Form) => Promise<any>) => any
  ): any //Middleware interceptor, can intercept Dialog to cancel
  //Open the pop-up window to receive form attributes, you can pass in initialValues/values/effects etc.
  open(props: IFormProps): Promise<any> //return form data
  //Close the pop-up window
  close(): void
}

interface IDialogProps extends DialogProps {
  onOk?: (event: React.MouseEvent) => void | boolean // return false can prevent onOk
  onCancel?: (event: React.MouseEvent) => void | boolean // return false can prevent onCancel
  loadingText?: React.ReactText
}

interface FormDialog {
  (title: IDialogProps, id: string, renderer: FormDialogRenderer): IFormDialog
  (title: IDialogProps, renderer: FormDialogRenderer): IFormDialog
  (title: ModalTitle, id: string, renderer: FormDialogRenderer): IFormDialog
  (title: ModalTitle, renderer: FormDialogRenderer): IFormDialog
}
```

`DialogProps` type definition reference fusion [Dialog API](https://fusion.design/pc/component/dialog?themeid=2#API)

### FormDialog.Footer

No attributes, only child nodes are received

### FormDialog.Portal

Receive the optional id attribute, the default value is `form-dialog`, if there are multiple prefixCls in an application, and the prefixCls in the pop-up window of different regions are different, then it is recommended to specify the id as the region-level id

```

--------------------------------------------------------------------------------
/packages/core/docs/api/models/VoidField.md:
--------------------------------------------------------------------------------

```markdown
---
order: 4
---

# VoidField

Call the VoidField model returned by [createVoidField](/api/models/form#createvoidfield).

All model attributes are listed below. If the attribute is writable, then we can directly refer to it to modify the attribute, and @formily/reactive will respond to trigger the UI update.

## Attributes

| Property    | Description                               | Type                                    | Read-only or not | Default value |
| ----------- | ----------------------------------------- | --------------------------------------- | ---------------- | ------------- |
| initialized | Whether the field has been initialized    | Boolean                                 | No               | `false`       |
| mounted     | Is the field mounted                      | Boolean                                 | No               | `false`       |
| unmounted   | Is the field unmounted                    | Boolean                                 | No               | `false`       |
| address     | Field node path                           | [FormPath](/api/entry/form-path)        | Yes              |               |
| path        | Field data path                           | [FormPath](/api/entry/form-path)        | Yes              |               |
| title       | Field Title                               | [FieldMessage](#fieldmessage)           | No               | `""`          |
| description | Field description                         | [FieldMessage](#fieldmessage)           | No               | `""`          |
| decorator   | field decorator                           | Any[]                                   | No               | `null`        |
| component   | Field component                           | Any[]                                   | No               | `null`        |
| parent      | Parent field                              | [GeneralField](#generalfield)           | yes              | `null`        |
| display     | Field display status                      | [FieldDisplayTypes](#fielddisplaytypes) | No               | `"visible"`   |
| pattern     | Field interaction mode                    | [FieldPatternTypes](#fieldpatterntypes) | No               | `"editable"`  |
| hidden      | Whether the field is hidden               | Boolean                                 | No               | `false`       |
| visible     | Whether the field is displayed            | Boolean                                 | No               | `true`        |
| disabled    | Whether the field is disabled             | Boolean                                 | No               | `false`       |
| readOnly    | Is the field read-only                    | Boolean                                 | No               | `false`       |
| readPretty  | Whether the field is in the reading state | Boolean                                 | No               | `false`       |
| editable    | Field is editable                         | Boolean                                 | No               | `true`        |

#### explain in detail

**hidden**

When true, display is hidden, when false, display is visible

**visible**

When true, display is visible, when false, display is none

## Method

### setTitle

#### Description

Set field title

#### Signature

```ts
interface setTitle {
  (title?: FieldMessage): void
}
```

FieldMessage Reference [FieldMessage](#fieldmessage)

### setDescription

#### Description

Set field description information

#### Signature

```ts
interface setDescription {
  (title?: FieldMessage): void
}
```

FieldMessage Reference [FieldMessage](#fieldmessage)

### setDisplay

#### Description

Set field display status

#### Signature

```ts
interface setDisplay {
  (display?: FieldDisplayTypes): void
}
```

FieldDisplayTypes Reference [FieldDisplayTypes](#fielddisplaytypes)

### setPattern

#### Description

Set field interaction mode

#### Signature

```ts
interface setPattern {
  (pattern?: FieldPatternTypes): void
}
```

FieldPatternTypes Reference [FieldPatternTypes](#fieldpatterntypes)

### setComponent

#### Description

Set field component

#### Signature

```ts
interface setComponent {
  (component?: FieldComponent, props?: any): void
}
```

FieldComponent Reference [FieldComponent](#fieldcomponent)

### setComponentProps

#### Description

Set field component properties

#### Signature

```ts
interface setComponentProps {
  (props?: any): void
}
```

### setDecorator

#### Description

Set field decorator

#### Signature

```ts
interface setDecorator {
  (decorator?: FieldDecorator, props?: any): void
}
```

FieldDecorator Reference [FieldDecorator](#fielddecorator)

### setDecoratorProps

#### Description

Set field decorator properties

#### Signature

```ts
interface setDecoratorProps {
  (props?: any): void
}
```

### setState

#### Description

Set field status

#### Signature

```ts
interface setState {
  (state: IVoidFieldState): void
  (callback: (state: IVoidFieldState) => void): void
}
```

IVoidFieldState Reference [IVoidFieldState](#ifieldstate)

### getState

#### Description

Get field status

#### Signature

```ts
interface getState<T> {
  (): IVoidFieldState
  (callback: (state: IVoidFieldState) => T): T
}
```

IVoidFieldState Reference [IVoidFieldState](#ifieldstate)

### setData

#### Description

set field data

#### Signature

```ts
interface setData {
  (data: any): void
}
```

### setContent

#### Description

set field content

#### Signature

```ts
interface setContent {
  (content: any): void
}
```

### onInit

#### Description

Trigger field initialization, no need to call manually

#### Signature

```ts
interface onInit {
  (): void
}
```

### onMount

#### Description

Trigger field mount

#### Signature

```ts
interface onMount {
  (): void
}
```

### onUnmount

#### Description

Trigger field unloading

#### Signature

```ts
interface onUnmount {
  (): void
}
```

### query

#### Description

Query field, you can query adjacent fields based on the current field

#### Signature

```ts
interface query {
  (pattern: FormPathPattern): Query
}
```

FormPathPattern API Reference [FormPath](/api/entry/form-path#formpathpattern)

Query object API reference [Query](/api/models/query)

### dispose

#### Description

Release observer, no need to release manually by default

#### Signature

```ts
interface dispose {
  (): void
}
```

### destroy

#### Description

Release observer, and remove current field model

#### Signature

```ts
interface destroy {
  (): void
}
```

### match

#### Description

Match fields based on path

#### Signature

```ts
interface match {
  (pattern: FormPathPattern): boolean
}
```

FormPathPattern API Reference [FormPath](/api/entry/form-path#formpathpattern)

### inject

#### Description

Inject executable methods into field models

#### Signature

```ts
interface inject {
  (actions: Record<string, (...args: any[]) => any>): void
}
```

### invoke

#### Description

Invoke an executable method injected by the field model via inject

#### Signature

```ts
interface invoke {
  (name: string, ...args: any[]): any
}
```

## Types of

<Alert>
Note: If you want to manually consume the type, just export it directly from the package module
</Alert>

### FieldMessage

```ts
type FieldMessage = string | JSXElement
```

If under the UI framework that supports JSX, we can directly pass the Node of JSX, otherwise, we can only pass the string

### FieldComponent

```ts
type FieldComponent = string | JSXComponentConstructor
```

Field component, if we use it in a framework that supports JSX, FieldComponent recommends to store the JSX component reference directly, otherwise it can store a component identification string and distribute it during actual rendering.

### FieldDecorator

```ts
type FieldDecorator = string | JSXComponentConstructor
```

Field decorator, if we use it in a framework that supports JSX, FieldDecorator recommends to store the JSX component reference directly, otherwise it can store a component identification string and distribute it during actual rendering.

### FieldReaction

```ts
type FieldReaction = (field: GeneralField) => void
```

### FieldDisplayTypes

```ts
type FieldDisplayTypes = 'none' | 'hidden' | 'visible'
```

### FieldPatternTypes

```ts
type FieldPatternTypes = 'editable' | 'disabled' | 'readOnly' | 'readPretty'
```

### GeneralField

```ts
type GeneralField = Field | VoidField | ArrayField | ObjectField
```

Field Reference [Field](/api/models/field)

ArrayField Reference [ArrayField](/api/models/array-field)

ObjectField Reference [ObjectField](/api/models/object-field)

### IVoidFieldState

```ts
interface IVoidFieldState {
  hidden?: boolean
  visible?: boolean
  editable?: boolean
  readOnly?: boolean
  disabled?: boolean
  readPretty?: boolean
  title?: any
  description?: any
  modified?: boolean
  active?: boolean
  visited?: boolean
  initialized?: boolean
  mounted?: boolean
  unmounted?: boolean
  decorator?: FieldDecorator
  component?: FieldComponent
  readonly parent?: GeneralField
  display?: FieldDisplayTypes
  pattern?: FieldPatternTypes
}
```

### IGeneralFieldState

```ts
type IGeneralFieldState = IVoidFieldState & IFieldState
```

IFieldState Reference [IFieldState](/api/models/field#ifieldstate)

```

--------------------------------------------------------------------------------
/packages/json-schema/src/__tests__/transformer.spec.ts:
--------------------------------------------------------------------------------

```typescript
import { Schema } from '../schema'
import { createForm } from '@formily/core'
import { isObservable } from '@formily/reactive'
import { ISchema, ISchemaTransformerOptions } from '../types'

const attach = <T extends { onMount: () => void }>(target: T): T => {
  target.onMount()
  return target
}

const getFormAndFields = (
  field1SchemaProps: Omit<ISchema, 'name'> = {},
  field2SchemaProps: Omit<ISchema, 'name'> = {},
  options: ISchemaTransformerOptions = {}
) => {
  const filed1Schema = new Schema({
    name: 'field1',
    ...field1SchemaProps,
  }).toFieldProps(options)

  const filed2Schema = new Schema({
    name: 'field2',
    ...field2SchemaProps,
  }).toFieldProps(options)

  const form = createForm()
  const field1 = form.createField(filed1Schema)
  const field2 = form.createField(filed2Schema)

  return {
    form,
    field1,
    field2,
  }
}

test('baseReaction', () => {
  const { field1, field2 } = getFormAndFields(
    {
      title: 'field1Title',
    },
    {
      title: 'field2Title',
    }
  )

  expect(field1.title).toBe('field1Title')
  expect(field2.title).toBe('field2Title')
})

test('baseReaction with scopes', () => {
  const scopeTitle = 'fieldTitle'
  const scopeDescription = 'fieldDescription'

  const { field1, field2 } = getFormAndFields(
    {
      title: '{{scopeTitle}}',
    },
    {
      description: '{{scopeDescription}}',
    },
    {
      scope: {
        scopeTitle,
        scopeDescription,
      },
    }
  )

  expect(field1.title).toBe(scopeTitle)
  expect(field2.description).toBe(scopeDescription)
})

test('userReactions with target(state)', () => {
  const field2Title = 'field2Title'
  const { field2 } = getFormAndFields({
    'x-reactions': {
      target: 'field2',
      fulfill: {
        state: {
          title: field2Title,
        },
      },
    },
  })

  expect(field2.title).toBe(field2Title)
})

test('userReactions with target(schema)', () => {
  const field2Data = 'fieldData'
  const { field2 } = getFormAndFields({
    'x-reactions': {
      target: 'field2',
      fulfill: {
        schema: {
          'x-data': field2Data,
        },
      },
    },
  })

  expect(field2.data).toBe(field2Data)
})

test('userReactions with target(runner)', () => {
  const mockFn = jest.fn()
  const field2Title = 'field2Title'
  const { field2 } = getFormAndFields(
    {
      'x-reactions': {
        target: 'field2',
        fulfill: {
          run: `$target.title='${field2Title}';fn()`,
        },
      },
    },
    {},
    {
      scope: {
        fn: mockFn,
      },
    }
  )

  expect(mockFn).toBeCalledTimes(1)
  expect(field2.title).toBe(field2Title)
})

test('userReactions without target(state)', () => {
  const field1Title = 'field1Title'
  const { field1 } = getFormAndFields({
    'x-reactions': {
      fulfill: {
        state: {
          title: field1Title,
        },
      },
    },
  })

  expect(field1.title).toBe(field1Title)
})

test('userReactions without target(schema)', () => {
  const field1Data = 'fieldData'
  const { field1 } = getFormAndFields({
    'x-reactions': {
      fulfill: {
        schema: {
          'x-data': field1Data,
        },
      },
    },
  })

  expect(field1.data).toBe(field1Data)
})

test('userReactions without target(runner)', () => {
  const mockFn = jest.fn()
  const { field1 } = getFormAndFields(
    {
      'x-reactions': {
        fulfill: {
          run: `$self.__target__=$target;fn()`,
        },
      },
    },
    {},
    {
      scope: {
        fn: mockFn,
      },
    }
  )

  expect(mockFn).toBeCalledTimes(1)
  expect((field1 as any).__target__).toBe(null)
})

test('userReactions with condition', () => {
  const mockFn = jest.fn()
  const { field1 } = getFormAndFields(
    {
      'x-value': true,
      'x-reactions': {
        when: '$self.value===true',
        fulfill: {
          run: 'mockFn($self.value)',
        },
        otherwise: {
          run: 'mockFn($self.value)',
        },
      },
    },
    {},
    {
      scope: {
        mockFn,
      },
    }
  )

  expect(mockFn).nthCalledWith(1, true)

  field1.value = false

  expect(mockFn).nthCalledWith(2, false)
})

test('userReactions with condition(wrong type)', () => {
  const field1Value = 'field1Value'
  const mockFn = jest.fn()
  getFormAndFields(
    {
      'x-value': field1Value,
      'x-reactions': {
        dependencies: 'value',
        fulfill: {
          run: 'mockFn($deps, $dependencies)',
        },
      },
    },
    {},
    {
      scope: {
        mockFn,
      },
    }
  )

  expect(mockFn).nthCalledWith(1, [], [])
})

test('userReactions with condition(array)', () => {
  const field1Value = 'field1Value'
  const field2Value = 'field2Value'
  const field1Title = 'field1Title'
  const field1Description = 'field1Description'
  const mockFn = jest.fn()

  getFormAndFields(
    {
      title: field1Title,
      description: field1Description,
      'x-value': field1Value,
    },
    {
      'x-value': field2Value,
      'x-reactions': {
        dependencies: [
          'field2',
          {
            name: 1,
            source: 'field1',
          },
          {
            name: 2,
            source: 'field1#title',
          },
          {
            name: 3,
            source: 'field1',
            property: 'description',
          },
        ],
        fulfill: {
          run: `mockFn($deps)`,
        },
      },
    },
    {
      scope: {
        mockFn,
      },
    }
  )

  expect(mockFn).nthCalledWith(1, [
    field2Value,
    field1Value,
    field1Title,
    field1Description,
  ])
})

test('userReactions with condition(object)', () => {
  const field2Value = 'field2Value'
  const field1Title = 'field1Title'
  const mockFn = jest.fn()

  getFormAndFields(
    {
      title: field1Title,
    },
    {
      'x-value': field2Value,
      'x-reactions': {
        dependencies: {
          key1: 'field1#title',
          key2: 'field2',
        },
        fulfill: {
          run: `mockFn($deps)`,
        },
      },
    },
    {
      scope: {
        mockFn,
      },
    }
  )

  expect(mockFn).nthCalledWith(1, {
    key1: field1Title,
    key2: field2Value,
  })
})

test('userReactions with user-defined effects', () => {
  const field2Value = 'field2Value'
  const field1Title = 'field1Title'
  const mockFn = jest.fn()

  const { field2 } = getFormAndFields(
    {
      title: field1Title,
      'x-reactions': {
        target: 'field2',
        fulfill: {
          run: `mockFn($target.value)`,
        },
        effects: ['onFieldInit'],
      },
    },
    {
      'x-value': field2Value,
    },
    {
      scope: {
        mockFn,
      },
    }
  )

  expect(mockFn).toBeCalledTimes(1)
  expect(mockFn).nthCalledWith(1, field2Value)

  field2.value = field1Title
  expect(mockFn).toBeCalledTimes(1)
})

test('userReactions with function type', () => {
  const componentProps = {
    prop: 1,
  }
  let observable: any = {}
  const { field1 } = getFormAndFields({
    'x-reactions': (field, baseScope) => {
      baseScope.$props(componentProps)
      observable = baseScope.$observable({})
    },
  })

  expect(field1.componentProps).toMatchObject(componentProps)
  expect(isObservable(observable)).toBe(true)
})

test('userReactions with $lookup $record $records $index', () => {
  const initialValues = {
    array: [
      { a: 1, b: 2 },
      { a: 3, b: 4 },
    ],
  }
  const form = attach(
    createForm({
      initialValues,
    })
  )

  form.createArrayField({
    name: 'array',
  })
  form.createObjectField({
    name: '0',
    basePath: 'array',
  })
  form.createObjectField({
    name: '1',
    basePath: 'array',
  })

  const field0aSchema = new Schema({
    name: 'array.0.a',
    'x-reactions': `{{$self.title = $record.b}}`,
  }).toFieldProps({})

  const field0bSchema = new Schema({
    name: 'array.0.b',
    'x-reactions': '{{$self.title = $lookup.array[0].a}}',
  }).toFieldProps({})

  const field1aSchema = new Schema({
    name: 'array.1.a',
    'x-reactions': '{{$self.title = $records[$index].b}}',
  }).toFieldProps({})

  const field1bSchema = new Schema({
    name: 'array.1.b',
    'x-reactions': `{{$self.title = $record.$lookup.array[$record.$index].a}}`,
  }).toFieldProps({})

  const field0a = attach(form.createField(field0aSchema))
  const field0b = attach(form.createField(field0bSchema))
  const field1a = attach(form.createField(field1aSchema))
  const field1b = attach(form.createField(field1bSchema))

  expect(field0a.title).toEqual(2)
  expect(field0b.title).toEqual(1)
  expect(field1a.title).toEqual(4)
  expect(field1b.title).toEqual(3)
})

test('userReactions with primary type record', () => {
  const initialValues = {
    array: [1, 2, 3],
  }

  const form = attach(
    createForm({
      initialValues,
    })
  )

  const field0Schema = new Schema({
    name: 'array.0',
    'x-reactions': `{{$self.title = $record}}`,
  }).toFieldProps({})

  const field1Schema = new Schema({
    name: 'array.1',
    'x-reactions': '{{$self.title = $record}}',
  }).toFieldProps({})

  form.createArrayField({
    name: 'array',
  })
  const field0 = attach(form.createField(field0Schema))
  const field1 = attach(form.createField(field1Schema))
  expect(field0.title).toEqual(1)
  expect(field1.title).toEqual(2)
})

```

--------------------------------------------------------------------------------
/packages/next/docs/components/Select.zh-CN.md:
--------------------------------------------------------------------------------

```markdown
# Select

> 下拉框组件

## Markup Schema 同步数据源案例

```tsx
import React from 'react'
import { Select, FormItem, FormButtonGroup, Submit } from '@formily/next'
import { createForm } from '@formily/core'
import { FormProvider, createSchemaField } from '@formily/react'

const SchemaField = createSchemaField({
  components: {
    Select,
    FormItem,
  },
})

const form = createForm()

export default () => (
  <FormProvider form={form}>
    <SchemaField>
      <SchemaField.Number
        name="select"
        title="选择框"
        x-decorator="FormItem"
        x-component="Select"
        enum={[
          { label: '选项1', value: 1 },
          { label: '选项2', value: 2 },
        ]}
        x-component-props={{
          style: {
            width: 120,
          },
        }}
      />
    </SchemaField>
    <FormButtonGroup>
      <Submit onSubmit={console.log}>提交</Submit>
    </FormButtonGroup>
  </FormProvider>
)
```

## Markup Schema 异步联动数据源案例

```tsx
import React from 'react'
import { Select, FormItem, FormButtonGroup, Submit } from '@formily/next'
import { createForm, onFieldReact, FormPathPattern, Field } from '@formily/core'
import { FormProvider, createSchemaField } from '@formily/react'
import { action } from '@formily/reactive'

const SchemaField = createSchemaField({
  components: {
    Select,
    FormItem,
  },
})

const useAsyncDataSource = (
  pattern: FormPathPattern,
  service: (field: Field) => Promise<{ label: string; value: any }[]>
) => {
  onFieldReact(pattern, (field) => {
    field.loading = true
    service(field).then(
      action.bound((data) => {
        field.dataSource = data
        field.loading = false
      })
    )
  })
}

const form = createForm({
  effects: () => {
    useAsyncDataSource('select', async (field) => {
      const linkage = field.query('linkage').get('value')
      if (!linkage) return []
      return new Promise((resolve) => {
        setTimeout(() => {
          if (linkage === 1) {
            resolve([
              {
                label: 'AAA',
                value: 'aaa',
              },
              {
                label: 'BBB',
                value: 'ccc',
              },
            ])
          } else if (linkage === 2) {
            resolve([
              {
                label: 'CCC',
                value: 'ccc',
              },
              {
                label: 'DDD',
                value: 'ddd',
              },
            ])
          }
        }, 1500)
      })
    })
  },
})

export default () => (
  <FormProvider form={form}>
    <SchemaField>
      <SchemaField.Number
        name="linkage"
        title="联动选择框"
        x-decorator="FormItem"
        x-component="Select"
        enum={[
          { label: '发请求1', value: 1 },
          { label: '发请求2', value: 2 },
        ]}
        x-component-props={{
          style: {
            width: 120,
          },
        }}
      />
      <SchemaField.String
        name="select"
        title="异步选择框"
        x-decorator="FormItem"
        x-component="Select"
        x-component-props={{
          style: {
            width: 120,
          },
        }}
      />
    </SchemaField>
    <FormButtonGroup>
      <Submit onSubmit={console.log}>提交</Submit>
    </FormButtonGroup>
  </FormProvider>
)
```

## JSON Schema 同步数据源案例

```tsx
import React from 'react'
import { Select, FormItem, FormButtonGroup, Submit } from '@formily/next'
import { createForm } from '@formily/core'
import { FormProvider, createSchemaField } from '@formily/react'

const SchemaField = createSchemaField({
  components: {
    Select,
    FormItem,
  },
})

const form = createForm()

const schema = {
  type: 'object',
  properties: {
    select: {
      type: 'string',
      title: '选择框',
      'x-decorator': 'FormItem',
      'x-component': 'Select',
      enum: [
        { label: '选项1', value: 1 },
        { label: '选项2', value: 2 },
      ],
      'x-component-props': {
        style: {
          width: 120,
        },
      },
    },
  },
}

export default () => (
  <FormProvider form={form}>
    <SchemaField schema={schema} />
    <FormButtonGroup>
      <Submit onSubmit={console.log}>提交</Submit>
    </FormButtonGroup>
  </FormProvider>
)
```

## JSON Schema 异步联动数据源案例

```tsx
import React from 'react'
import { Select, FormItem, FormButtonGroup, Submit } from '@formily/next'
import { createForm } from '@formily/core'
import { FormProvider, createSchemaField } from '@formily/react'
import { action } from '@formily/reactive'

const SchemaField = createSchemaField({
  components: {
    Select,
    FormItem,
  },
})

const loadData = async (field) => {
  const linkage = field.query('linkage').get('value')
  if (!linkage) return []
  return new Promise((resolve) => {
    setTimeout(() => {
      if (linkage === 1) {
        resolve([
          {
            label: 'AAA',
            value: 'aaa',
          },
          {
            label: 'BBB',
            value: 'ccc',
          },
        ])
      } else if (linkage === 2) {
        resolve([
          {
            label: 'CCC',
            value: 'ccc',
          },
          {
            label: 'DDD',
            value: 'ddd',
          },
        ])
      }
    }, 1500)
  })
}

const useAsyncDataSource = (service) => (field) => {
  field.loading = true
  service(field).then(
    action.bound((data) => {
      field.dataSource = data
      field.loading = false
    })
  )
}

const form = createForm()

const schema = {
  type: 'object',
  properties: {
    linkage: {
      type: 'string',
      title: '联动选择框',
      enum: [
        { label: '发请求1', value: 1 },
        { label: '发请求2', value: 2 },
      ],
      'x-decorator': 'FormItem',
      'x-component': 'Select',
      'x-component-props': {
        style: {
          width: 120,
        },
      },
    },
    select: {
      type: 'string',
      title: '异步选择框',
      'x-decorator': 'FormItem',
      'x-component': 'Select',
      'x-component-props': {
        style: {
          width: 120,
        },
      },
      'x-reactions': ['{{useAsyncDataSource(loadData)}}'],
    },
  },
}

export default () => (
  <FormProvider form={form}>
    <SchemaField schema={schema} scope={{ useAsyncDataSource, loadData }} />
    <FormButtonGroup>
      <Submit onSubmit={console.log}>提交</Submit>
    </FormButtonGroup>
  </FormProvider>
)
```

## 纯 JSX 同步数据源案例

```tsx
import React from 'react'
import { Select, FormItem, FormButtonGroup, Submit } from '@formily/next'
import { createForm } from '@formily/core'
import { FormProvider, Field } from '@formily/react'

const form = createForm()

export default () => (
  <FormProvider form={form}>
    <Field
      name="select"
      title="选择框"
      dataSource={[
        { label: '选项1', value: 1 },
        { label: '选项2', value: 2 },
      ]}
      decorator={[FormItem]}
      component={[
        Select,
        {
          style: {
            width: 120,
          },
        },
      ]}
    />
    <FormButtonGroup>
      <Submit onSubmit={console.log}>提交</Submit>
    </FormButtonGroup>
  </FormProvider>
)
```

## 纯 JSX 异步联动数据源案例

```tsx
import React from 'react'
import { Select, FormItem, FormButtonGroup, Submit } from '@formily/next'
import {
  createForm,
  onFieldReact,
  FormPathPattern,
  FieldType,
} from '@formily/core'
import { FormProvider, Field } from '@formily/react'
import { action } from '@formily/reactive'

const useAsyncDataSource = (
  pattern: FormPathPattern,
  service: (field: FieldType) => Promise<{ label: string; value: any }[]>
) => {
  onFieldReact(pattern, (field) => {
    field.loading = true
    service(field).then(
      action.bound((data) => {
        field.dataSource = data
        field.loading = false
      })
    )
  })
}

const form = createForm({
  effects: () => {
    useAsyncDataSource('select', async (field) => {
      const linkage = field.query('linkage').get('value')
      if (!linkage) return []
      return new Promise((resolve) => {
        setTimeout(() => {
          if (linkage === 1) {
            resolve([
              {
                label: 'AAA',
                value: 'aaa',
              },
              {
                label: 'BBB',
                value: 'ccc',
              },
            ])
          } else if (linkage === 2) {
            resolve([
              {
                label: 'CCC',
                value: 'ccc',
              },
              {
                label: 'DDD',
                value: 'ddd',
              },
            ])
          }
        }, 1500)
      })
    })
  },
})

export default () => (
  <FormProvider form={form}>
    <Field
      name="linkage"
      title="联动选择框"
      dataSource={[
        { label: '发请求1', value: 1 },
        { label: '发请求2', value: 2 },
      ]}
      decorator={[FormItem]}
      component={[
        Select,
        {
          style: {
            width: 120,
          },
        },
      ]}
    />
    <Field
      name="select"
      title="异步选择框"
      decorator={[FormItem]}
      component={[
        Select,
        {
          style: {
            width: 120,
          },
        },
      ]}
    />
    <FormButtonGroup>
      <Submit onSubmit={console.log}>提交</Submit>
    </FormButtonGroup>
  </FormProvider>
)
```

## API

参考 https://fusion.design/pc/component/basic/select

```

--------------------------------------------------------------------------------
/packages/antd/docs/components/FormDialog.md:
--------------------------------------------------------------------------------

```markdown
# FormDialog

> Pop-up form, mainly used in simple event to open the form scene

## Markup Schema example

```tsx
import React from 'react'
import { FormDialog, FormItem, FormLayout, Input } from '@formily/antd'
import { createSchemaField } from '@formily/react'
import { Button } from 'antd'

const SchemaField = createSchemaField({
  components: {
    FormItem,
    Input,
  },
})

export default () => {
  return (
    <Button
      onClick={() => {
        const dialog = FormDialog('Pop-up form', () => {
          return (
            <FormLayout labelCol={6} wrapperCol={10}>
              <SchemaField>
                <SchemaField.String
                  name="aaa"
                  required
                  title="input box 1"
                  x-decorator="FormItem"
                  x-component="Input"
                />
                <SchemaField.String
                  name="bbb"
                  required
                  title="input box 2"
                  x-decorator="FormItem"
                  x-component="Input"
                />
                <SchemaField.String
                  name="ccc"
                  required
                  title="input box 3"
                  x-decorator="FormItem"
                  x-component="Input"
                />
                <SchemaField.String
                  name="ddd"
                  required
                  title="input box 4"
                  x-decorator="FormItem"
                  x-component="Input"
                />
              </SchemaField>
              <FormDialog.Footer>
                <span
                  onClick={() => {
                    dialog.close()
                  }}
                  style={{ marginLeft: 4 }}
                >
                  Extended copywriting(Click me to close the form)
                </span>
              </FormDialog.Footer>
            </FormLayout>
          )
        })
        dialog
          .forOpen((payload, next) => {
            setTimeout(() => {
              next({
                initialValues: {
                  aaa: '123',
                },
              })
            }, 1000)
          })
          .forConfirm((payload, next) => {
            setTimeout(() => {
              console.log(payload)
              next(payload)
            }, 1000)
          })
          .forCancel((payload, next) => {
            setTimeout(() => {
              console.log(payload)
              next(payload)
            }, 1000)
          })
          .open({
            initialValues: {
              aaa: '123',
            },
          })
          .then(console.log)
      }}
    >
      Click me to open the form
    </Button>
  )
}
```

## JSON Schema case

```tsx
import React from 'react'
import { FormDialog, FormItem, FormLayout, Input } from '@formily/antd'
import { createSchemaField } from '@formily/react'
import { Button } from 'antd'

const SchemaField = createSchemaField({
  components: {
    FormItem,
    Input,
  },
})

const schema = {
  type: 'object',
  properties: {
    aaa: {
      type: 'string',
      title: 'input box 1',
      required: true,
      'x-decorator': 'FormItem',
      'x-component': 'Input',
    },
    bbb: {
      type: 'string',
      title: 'input box 2',
      required: true,
      'x-decorator': 'FormItem',
      'x-component': 'Input',
    },
    ccc: {
      type: 'string',
      title: 'input box 3',
      required: true,
      'x-decorator': 'FormItem',
      'x-component': 'Input',
    },
    ddd: {
      type: 'string',
      title: 'input box 4',
      required: true,
      'x-decorator': 'FormItem',
      'x-component': 'Input',
    },
  },
}

export default () => {
  return (
    <Button
      onClick={() => {
        const dialog = FormDialog('Pop-up form', () => {
          return (
            <FormLayout labelCol={6} wrapperCol={10}>
              <SchemaField schema={schema} />
              <FormDialog.Footer>
                <span
                  onClick={() => {
                    dialog.close()
                  }}
                  style={{ marginLeft: 4 }}
                >
                  Extended copywriting(Click me to close the form)
                </span>
              </FormDialog.Footer>
            </FormLayout>
          )
        })
        dialog
          .forOpen((payload, next) => {
            setTimeout(() => {
              next({
                initialValues: {
                  aaa: '123',
                },
              })
            }, 1000)
          })
          .forConfirm((payload, next) => {
            setTimeout(() => {
              console.log(payload)
              next(payload)
            }, 1000)
          })
          .forCancel((payload, next) => {
            setTimeout(() => {
              console.log(payload)
              next(payload)
            }, 1000)
          })
          .open({
            initialValues: {
              aaa: '123',
            },
          })
          .then(console.log)
      }}
    >
      Click me to open the form
    </Button>
  )
}
```

## Pure JSX case

```tsx
import React from 'react'
import { FormDialog, FormItem, FormLayout, Input } from '@formily/antd'
import { Field } from '@formily/react'
import { Button } from 'antd'

export default () => {
  return (
    <Button
      onClick={() => {
        const dialog = FormDialog('Pop-up form', () => {
          return (
            <FormLayout labelCol={6} wrapperCol={10}>
              <Field
                name="aaa"
                required
                title="input box 1"
                decorator={[FormItem]}
                component={[Input]}
              />
              <Field
                name="bbb"
                required
                title="input box 2"
                decorator={[FormItem]}
                component={[Input]}
              />
              <Field
                name="ccc"
                required
                title="input box 3"
                decorator={[FormItem]}
                component={[Input]}
              />
              <Field
                name="ddd"
                required
                title="input box 4"
                decorator={[FormItem]}
                component={[Input]}
              />
              <FormDialog.Footer>
                <span
                  onClick={() => {
                    dialog.close()
                  }}
                  style={{ marginLeft: 4 }}
                >
                  Extended copywriting(Click me to close the form)
                </span>
              </FormDialog.Footer>
            </FormLayout>
          )
        })
        dialog
          .forOpen((payload, next) => {
            setTimeout(() => {
              next({
                initialValues: {
                  aaa: '123',
                },
              })
            }, 1000)
          })
          .forConfirm((payload, next) => {
            setTimeout(() => {
              console.log(payload)
              next(payload)
            }, 1000)
          })
          .forCancel((payload, next) => {
            setTimeout(() => {
              console.log(payload)
              next(payload)
            }, 1000)
          })
          .open({
            initialValues: {
              aaa: '123',
            },
          })
          .then(console.log)
      }}
    >
      Click me to open the form
    </Button>
  )
}
```

## API

### FormDialog

```ts pure
import { IFormProps, Form } from '@formily/core'

type FormDialogRenderer =
  | React.ReactElement
  | ((form: Form) => React.ReactElement)

type ModalTitle = string | number | React.ReactElement

interface IFormDialog {
  forOpen(
    middleware: (
      props: IFormProps,
      next: (props?: IFormProps) => Promise<any>
    ) => any
  ): any //Middleware interceptor, can intercept Dialog to open
  forConfirm(
    middleware: (props: Form, next: (props?: Form) => Promise<any>) => any
  ): any //Middleware interceptor, which can intercept Dialog confirmation
  forCancel(
    middleware: (props: Form, next: (props?: Form) => Promise<any>) => any
  ): any //Middleware interceptor, can intercept Dialog to cancel
  //Open the pop-up window to receive form attributes, you can pass in initialValues/values/effects etc.
  open(props: IFormProps): Promise<any> //return form data
  //Close the pop-up window
  close(): void
}

interface IModalProps extends ModalProps {
  onOk?: (event: React.MouseEvent<HTMLElement>) => void | boolean // return false can prevent onOk
  onCancel?: (event: React.MouseEvent<HTMLElement>) => void | boolean // return false can prevent onCancel
  loadingText?: React.ReactNode
}

interface FormDialog {
  (title: IModalProps, id: string, renderer: FormDialogRenderer): IFormDialog
  (title: IModalProps, renderer: FormDialogRenderer): IFormDialog
  (title: ModalTitle, id: string, renderer: FormDialogRenderer): IFormDialog
  (title: ModalTitle, renderer: FormDialogRenderer): IFormDialog
}
```

`ModalProps` type definition reference ant design [Modal API](https://ant.design/components/modal-cn/#API)

### FormDialog.Footer

No attributes, only child nodes are received

### FormDialog.Portal

Receive the optional id attribute, the default value is `form-dialog`, if there are multiple prefixCls in an application, and the prefixCls in the pop-up window of different regions are different, then it is recommended to specify the id as the region-level id

```

--------------------------------------------------------------------------------
/packages/next/docs/components/FormDialog.zh-CN.md:
--------------------------------------------------------------------------------

```markdown
# FormDialog

> 弹窗表单,主要用在简单的事件打开表单场景

## Markup Schema 案例

```tsx
import React, { createContext, useContext } from 'react'
import { FormDialog, FormItem, Input, FormLayout } from '@formily/next'
import { createSchemaField } from '@formily/react'
import { Button } from '@alifd/next'

const SchemaField = createSchemaField({
  components: {
    FormItem,
    Input,
  },
})

const Context = createContext()

const PortalId = '可以传,也可以不传的ID,默认是form-dialog'

export default () => {
  return (
    <Context.Provider value="自定义上下文可以直接传到弹窗内部,只需要ID一致即可">
      <FormDialog.Portal id={PortalId}>
        <Button
          onClick={() => {
            FormDialog('弹窗表单', PortalId, (form) => {
              console.log(useContext(Context))
              return (
                <FormLayout labelCol={6} wrapperCol={10}>
                  <SchemaField>
                    <SchemaField.String
                      name="aaa"
                      required
                      title="输入框1"
                      x-decorator="FormItem"
                      x-component="Input"
                    />
                    <SchemaField.String
                      name="bbb"
                      required
                      title="输入框2"
                      x-decorator="FormItem"
                      x-component="Input"
                    />
                    <SchemaField.String
                      name="ccc"
                      required
                      title="输入框3"
                      x-decorator="FormItem"
                      x-component="Input"
                    />
                    <SchemaField.String
                      name="ddd"
                      required
                      title="输入框4"
                      x-decorator="FormItem"
                      x-component="Input"
                    />
                  </SchemaField>
                  <FormDialog.Footer>
                    <span style={{ marginLeft: 4 }}>
                      扩展文案:{form.values.aaa}
                    </span>
                  </FormDialog.Footer>
                </FormLayout>
              )
            })
              .forOpen((payload, next) => {
                setTimeout(() => {
                  next({
                    initialValues: {
                      aaa: '123',
                    },
                  })
                }, 1000)
              })
              .forConfirm((payload, next) => {
                setTimeout(() => {
                  console.log(payload)
                  next(payload)
                }, 1000)
              })
              .forCancel((payload, next) => {
                setTimeout(() => {
                  console.log(payload)
                  next(payload)
                }, 1000)
              })
              .open()
              .then(console.log)
              .catch(console.error)
          }}
        >
          点我打开表单
        </Button>
      </FormDialog.Portal>
    </Context.Provider>
  )
}
```

## JSON Schema 案例

```tsx
import React from 'react'
import { FormDialog, FormItem, Input, FormLayout } from '@formily/next'
import { createSchemaField } from '@formily/react'
import { Button } from '@alifd/next'

const SchemaField = createSchemaField({
  components: {
    FormItem,
    Input,
  },
})

const schema = {
  type: 'object',
  properties: {
    aaa: {
      type: 'string',
      title: '输入框1',
      required: true,
      'x-decorator': 'FormItem',
      'x-component': 'Input',
    },
    bbb: {
      type: 'string',
      title: '输入框2',
      required: true,
      'x-decorator': 'FormItem',
      'x-component': 'Input',
    },
    ccc: {
      type: 'string',
      title: '输入框3',
      required: true,
      'x-decorator': 'FormItem',
      'x-component': 'Input',
    },
    ddd: {
      type: 'string',
      title: '输入框4',
      required: true,
      'x-decorator': 'FormItem',
      'x-component': 'Input',
    },
  },
}

export default () => {
  return (
    <Button
      onClick={() => {
        FormDialog('弹窗表单', () => {
          return (
            <FormLayout labelCol={6} wrapperCol={14}>
              <SchemaField schema={schema} />
              <FormDialog.Footer>
                <span style={{ marginLeft: 4 }}>扩展文案</span>
              </FormDialog.Footer>
            </FormLayout>
          )
        })
          .open({
            initialValues: {
              aaa: '123',
            },
          })
          .then(console.log)
      }}
    >
      点我打开表单
    </Button>
  )
}
```

## 纯 JSX 案例

```tsx
import React from 'react'
import { FormDialog, FormItem, Input, FormLayout } from '@formily/next'
import { Field } from '@formily/react'
import { Button } from '@alifd/next'

export default () => {
  return (
    <Button
      onClick={() => {
        FormDialog('弹窗表单', () => {
          return (
            <FormLayout labelCol={6} wrapperCol={14}>
              <Field
                name="aaa"
                required
                title="输入框1"
                decorator={[FormItem]}
                component={[Input]}
              />
              <Field
                name="bbb"
                required
                title="输入框2"
                decorator={[FormItem]}
                component={[Input]}
              />
              <Field
                name="ccc"
                required
                title="输入框3"
                decorator={[FormItem]}
                component={[Input]}
              />
              <Field
                name="ddd"
                required
                title="输入框4"
                decorator={[FormItem]}
                component={[Input]}
              />
              <FormDialog.Footer>
                <span style={{ marginLeft: 4 }}>扩展文案</span>
              </FormDialog.Footer>
            </FormLayout>
          )
        })
          .open({
            initialValues: {
              aaa: '123',
            },
          })
          .then(console.log)
      }}
    >
      点我打开表单
    </Button>
  )
}
```

## 使用 Fusion Context

```tsx
import React from 'react'
import { FormDialog, FormItem, Input, FormLayout } from '@formily/next'
import { Field } from '@formily/react'
import { Button, ConfigProvider } from '@alifd/next'

export default () => {
  return (
    <ConfigProvider
      locale={{
        Dialog: {
          ok: 'OK',
          cancel: 'Cancel',
        },
      }}
      defaultPropsConfig={{
        Dialog: {
          isFullScreen: true,
          footerActions: ['cancel', 'ok'],
        },
      }}
    >
      <Button
        onClick={() => {
          FormDialog('弹窗表单', () => {
            return (
              <FormLayout labelCol={6} wrapperCol={14}>
                <Field
                  name="aaa"
                  required
                  title="输入框1"
                  decorator={[FormItem]}
                  component={[Input]}
                />
                <Field
                  name="bbb"
                  required
                  title="输入框2"
                  decorator={[FormItem]}
                  component={[Input]}
                />
                <Field
                  name="ccc"
                  required
                  title="输入框3"
                  decorator={[FormItem]}
                  component={[Input]}
                />
                <Field
                  name="ddd"
                  required
                  title="输入框4"
                  decorator={[FormItem]}
                  component={[Input]}
                />
                <FormDialog.Footer>
                  <span style={{ marginLeft: 4 }}>扩展文案</span>
                </FormDialog.Footer>
              </FormLayout>
            )
          })
            .open({
              initialValues: {
                aaa: '123',
              },
            })
            .then(console.log)
        }}
      >
        点我打开表单
      </Button>
    </ConfigProvider>
  )
}
```

## API

### FormDialog

```ts pure
import { IFormProps, Form } from '@formily/core'

type FormDialogRenderer =
  | React.ReactElement
  | ((form: Form) => React.ReactElement)

interface IFormDialog {
  forOpen(
    middleware: (
      props: IFormProps,
      next: (props?: IFormProps) => Promise<any>
    ) => any
  ): any //中间件拦截器,可以拦截Dialog打开
  forConfirm(
    middleware: (props: Form, next: (props?: Form) => Promise<any>) => any
  ): any //中间件拦截器,可以拦截Dialog确认
  forCancel(
    middleware: (props: Form, next: (props?: Form) => Promise<any>) => any
  ): any //中间件拦截器,可以拦截Dialog取消
  //打开弹窗,接收表单属性,可以传入initialValues/values/effects etc.
  open(props: IFormProps): Promise<any> //返回表单数据
  //关闭弹窗
  close(): void
}

interface IDialogProps extends DialogProps {
  onOk?: (event: React.MouseEvent) => void | boolean // return false can prevent onOk
  onCancel?: (event: React.MouseEvent) => void | boolean // return false can prevent onCancel
  loadingText?: React.ReactText
}

interface FormDialog {
  (title: IDialogProps, id: string, renderer: FormDialogRenderer): IFormDialog
  (title: IDialogProps, renderer: FormDialogRenderer): IFormDialog
  (title: ModalTitle, id: string, renderer: FormDialogRenderer): IFormDialog
  (title: ModalTitle, renderer: FormDialogRenderer): IFormDialog
}
```

`DialogProps` 类型定义参考 fusion [Dialog API](https://fusion.design/pc/component/dialog?themeid=2#API)

### FormDialog.Footer

无属性,只接收子节点

### FormDialog.Portal

接收可选的 id 属性,默认值为`form-dialog`,如果一个应用存在多个 prefixCls,不同区域的弹窗内部 prefixCls 不一样,那推荐指定 id 为区域级 id

```
Page 17/35FirstPrevNextLast