#
tokens: 49931/50000 32/1152 files (page 10/35)
lines: off (toggle) GitHub
raw markdown copy
This is page 10 of 35. Use http://codebase.md/alibaba/formily?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/vue/src/types/index.ts:
--------------------------------------------------------------------------------

```typescript
import { Component } from 'vue'
import * as VueDemi from 'vue-demi'
import {
  Form,
  IFieldFactoryProps,
  IVoidFieldFactoryProps,
  GeneralField,
  Field,
  ObjectField,
  FormPatternTypes,
  FieldDisplayTypes,
  FieldValidator,
} from '@formily/core'
import type { FormPathPattern } from '@formily/shared'
import type { ISchema, Schema, SchemaKey } from '@formily/json-schema'

class Helper<Props> {
  Return = VueDemi.defineComponent({} as { props: Record<keyof Props, any> })
}

export type DefineComponent<Props> = Helper<Props>['Return']

export type VueComponent = Component

export type VueComponentOptionsWithProps = {
  props: unknown
}

export type VueComponentProps<T extends VueComponent> =
  T extends VueComponentOptionsWithProps ? T['props'] : T

export interface IProviderProps {
  form: Form
}

export type IFieldProps<
  D extends VueComponent = VueComponent,
  C extends VueComponent = VueComponent
> = IFieldFactoryProps<D, C>

export type IVoidFieldProps<
  D extends VueComponent = VueComponent,
  C extends VueComponent = VueComponent
> = IVoidFieldFactoryProps<D, C>

export type IArrayFieldProps = IFieldProps
export type IObjectFieldProps = IFieldProps

export interface IReactiveFieldProps {
  fieldType: 'Field' | 'ArrayField' | 'ObjectField' | 'VoidField'
  fieldProps: IFieldProps | IVoidFieldProps
}

export interface IComponentMapper<T extends VueComponent = any> {
  (target: T): VueComponent
}

export type IStateMapper<Props> =
  | {
      [key in keyof Field]?: keyof Props | boolean
    }
  | ((props: Props, field: GeneralField) => Props)

export type SchemaVueComponents = Record<string, VueComponent>

export interface ISchemaFieldVueFactoryOptions<
  Components extends SchemaVueComponents = any
> {
  components?: Components
  scope?: any
}

export interface ISchemaFieldProps
  extends Omit<IRecursionFieldProps, 'name' | 'schema'> {
  schema?: ISchema
  components?: {
    [key: string]: VueComponent
  }
  scope?: any
  name?: SchemaKey
}

export interface ISchemaMapper {
  (schema: Schema, name: SchemaKey): Schema
}

export interface ISchemaFilter {
  (schema: Schema, name: SchemaKey): boolean
}

export interface IRecursionFieldProps {
  schema: Schema
  name?: SchemaKey
  basePath?: FormPathPattern
  onlyRenderProperties?: boolean
  onlyRenderSelf?: boolean
  mapProperties?: ISchemaMapper
  filterProperties?: ISchemaFilter
}

export type ObjectKey = string | number | boolean | symbol

export type KeyOfComponents<T> = keyof T

export type ComponentPath<
  T,
  Key extends KeyOfComponents<T> = KeyOfComponents<T>
> = Key extends string ? Key : never

export type ComponentPropsByPathValue<
  T extends SchemaVueComponents,
  P extends ComponentPath<T>
> = P extends keyof T ? VueComponentProps<T[P]> : never

export type ISchemaMarkupFieldProps<
  Components extends SchemaVueComponents = SchemaVueComponents,
  Decorator extends ComponentPath<Components> = ComponentPath<Components>,
  Component extends ComponentPath<Components> = ComponentPath<Components>
> = ISchema<
  Decorator,
  Component,
  ComponentPropsByPathValue<Components, Decorator>,
  ComponentPropsByPathValue<Components, Component>,
  FormPatternTypes,
  FieldDisplayTypes,
  FieldValidator,
  string,
  GeneralField
>

export type ISchemaTypeFieldProps<
  Components extends SchemaVueComponents = SchemaVueComponents,
  Decorator extends ComponentPath<Components> = ComponentPath<Components>,
  Component extends ComponentPath<Components> = ComponentPath<Components>
> = Omit<ISchemaMarkupFieldProps<Components, Decorator, Component>, 'type'>

export type IExpressionScopeProps = {
  value: any
}

```

--------------------------------------------------------------------------------
/packages/core/docs/guide/mvvm.md:
--------------------------------------------------------------------------------

```markdown
# MVVM

## OOP architecture

**MVVM** (**Model–view–viewmodel**) is an OOP software architecture model. Its core is to separate the logic and view of our application to improve code maintainability and application robustness. We can use a picture to describe:

![](//img.alicdn.com/imgextra/i3/O1CN01jiB7h723ZFf0lBCTo_!!6000000007269-55-tps-1244-432.svg)

To explain, the View (view layer) is responsible for maintaining the UI structure and style, and is responsible for data binding with the ViewModel (view model). The data binding relationship here is two-way, that is, the ViewModel (view model) data occurs. Changes will trigger the update of the View (view layer), and at the same time changes in the data of the view layer will trigger the changes of the ViewModel (view model). Model is more biased towards the actual business data processing model. Both ViewModel and Model are congested models, and both are injected with business logic from different fields. For example, the business logic of ViewModel is more biased towards the domain logic of the view interaction layer, while the business logic of Model is more biased towards the processing logic of business data.

So, what should the Formily solution be positioned in MVVM?

Obviously, Formily provides two tiers of View and ViewModel capabilities. View is @formily/react @formily/vue, which is specifically used to bridge communication with @formily/core. Therefore, @formily/core is positioned at the ViewModel layer. ,

Where is the Model layer?

Of course it is our actual business code layer, this layer formily will not manage, so at this layer, whether users maintain a Model in OOP mode or maintain a series of business logic function sets in FP mode, formily Don't care.

Therefore, this also makes formily's intrusion into the business very low, because formily's goal is to reduce the cost of users designing ViewModels, allowing users to focus more on the realization of business logic.

## FP architecture

Remember before the React team used the simplest expression **UI = fn(State)** to express the entire React system? Such a functional UI is very simple and clear. Will it conflict with the MVVM model?

There is no conflict, because in the MVVM mode, the relationship between View and ViewModel is actually approximately equal to **UI = fn(State)**, because ViewModel is a congestion model injected with logic, which is related to **fn(State) ** can achieve the same goal, but it is a more OOP expression, but **fn(State)** is a more functional expression, the state exists as an anemia model, through one function after another, Immutable updates to the anemia model are finally reflected in the UI.

Therefore, from the perspective of separation of logic and data, functional expression is clearer, but functional expression requires all data to be Immutable. Therefore, in scenarios with high performance requirements, the benefits of using a functional model will not be too great, of course, this is only the case in the js language. On the contrary, the MVVM model requires more data for Reactive data, that is, a responsive data model that can manipulate data by reference, so that data changes can be accurately monitored, and finally reflected on the UI.

Therefore, in the form scenario, the performance advantage of the MVVM mode will be better. The most important thing is that most of the GUI products that have survived for decades almost all use MVVM coincidentally. It seems that in the front-end field, the function The type system will be more academic. In terms of the actual benefits to the business, MVVM is still the first choice.

```

--------------------------------------------------------------------------------
/packages/react/docs/api/components/ArrayField.md:
--------------------------------------------------------------------------------

```markdown
---
order: 1
---

# ArrayField

## Description

As @formily/core's [createArrayField](https://core.formilyjs.org/api/models/form#createarrayfield) React implementation, it is a bridge component specifically used to bind ViewModel and input controls, ArrayField component Property reference [IFieldFactoryProps](https://core.formilyjs.org/api/models/form#ifieldfactoryprops)

<Alert>
When we use the ArrayField component, we must remember to pass the name attribute. At the same time, use render props to organize sub-components
</Alert>

## Signature

```ts
type ArrayField = React.FC<React.PropsWithChildren<IFieldFactoryProps>>
```

## Custom component use case

```tsx
import React from 'react'
import { createForm, ArrayField as ArrayFieldType } from '@formily/core'
import {
  FormProvider,
  Field,
  ArrayField,
  useField,
  observer,
} from '@formily/react'
import { Input, Button, Space } from 'antd'

const form = createForm()

const ArrayComponent = observer(() => {
  const field = useField<ArrayFieldType>()
  return (
    <>
      <div>
        {field.value?.map((item, index) => (
          <div key={index} style={{ display: 'flex-block', marginBottom: 10 }}>
            <Space>
              <Field name={index} component={[Input]} />
              <Button
                onClick={() => {
                  field.remove(index)
                }}
              >
                Remove
              </Button>
              <Button
                onClick={() => {
                  field.moveUp(index)
                }}
              >
                Move Up
              </Button>
              <Button
                onClick={() => {
                  field.moveDown(index)
                }}
              >
                Move Down
              </Button>
            </Space>
          </div>
        ))}
      </div>
      <Button
        onClick={() => {
          field.push('')
        }}
      >
        Add
      </Button>
    </>
  )
})

export default () => (
  <FormProvider form={form}>
    <ArrayField name="array" component={[ArrayComponent]} />
  </FormProvider>
)
```

## RenderProps use cases

```tsx
import React from 'react'
import { createForm } from '@formily/core'
import { FormProvider, Field, ArrayField } from '@formily/react'
import { Input, Button, Space } from 'antd'

const form = createForm()

export default () => (
  <FormProvider form={form}>
    <ArrayField name="array">
      {(field) => {
        return (
          <>
            <div>
              {field.value?.map((item, index) => (
                <div
                  key={index}
                  style={{ display: 'flex-block', marginBottom: 10 }}
                >
                  <Space>
                    <Field name={index} component={[Input]} />
                    <Button
                      onClick={() => {
                        field.remove(index)
                      }}
                    >
                      Remove
                    </Button>
                    <Button
                      onClick={() => {
                        field.moveUp(index)
                      }}
                    >
                      Move Up
                    </Button>
                    <Button
                      onClick={() => {
                        field.moveDown(index)
                      }}
                    >
                      Move Down
                    </Button>
                  </Space>
                </div>
              ))}
            </div>
            <Button onClick={() => field.push('')}>Add</Button>
          </>
        )
      }}
    </ArrayField>
  </FormProvider>
)
```

```

--------------------------------------------------------------------------------
/packages/next/src/form-button-group/index.tsx:
--------------------------------------------------------------------------------

```typescript
/**
 * 1. FormItem网格布局
 * 2. 居中,居右,居左布局
 * 3. 行内布局
 * 4. 吸底布局
 */
import React, { useRef, useLayoutEffect, useState } from 'react'
import StickyBox from 'react-sticky-box'
import { ReactFC } from '@formily/react'
import { Space, ISpaceProps } from '../space'
import { BaseItem, IFormItemProps } from '../form-item'
import { usePrefixCls } from '../__builtins__'
import cls from 'classnames'
interface IStickyProps extends React.ComponentProps<typeof StickyBox> {
  align?: React.CSSProperties['textAlign']
}

type IFormButtonGroupProps = Omit<ISpaceProps, 'align' | 'size'> & {
  align?: React.CSSProperties['textAlign']
  gutter?: number
}

type ComposedButtonGroup = ReactFC<IFormButtonGroupProps> & {
  Sticky: ReactFC<IStickyProps>
  FormItem: ReactFC<
    IFormItemProps & {
      gutter?: number
    }
  >
}

function getInheritedBackgroundColor(el: HTMLElement) {
  // get default style for current browser
  let defaultStyle = getDefaultBackground() // typically "rgba(0, 0, 0, 0)"

  // get computed color for el
  let backgroundColor = window.getComputedStyle(el).backgroundColor

  // if we got a real value, return it
  if (backgroundColor != defaultStyle) return backgroundColor

  // if we've reached the top parent el without getting an explicit color, return default
  if (!el.parentElement) return defaultStyle

  // otherwise, recurse and try again on parent element
  return getInheritedBackgroundColor(el.parentElement)
}

function getDefaultBackground() {
  // have to add to the document in order to use getComputedStyle
  let div = document.createElement('div')
  document.head.appendChild(div)
  let bg = window.getComputedStyle(div).backgroundColor
  document.head.removeChild(div)
  return bg
}

export const FormButtonGroup: ComposedButtonGroup = ({
  align = 'left',
  gutter,
  ...props
}) => {
  const prefixCls = usePrefixCls('formily-button-group')
  return (
    <Space
      {...props}
      size={gutter}
      className={cls(prefixCls, props.className)}
      style={{
        ...props.style,
        justifyContent:
          align === 'left'
            ? 'flex-start'
            : align === 'right'
            ? 'flex-end'
            : 'center',
        display: 'flex',
      }}
    >
      {props.children}
    </Space>
  )
}

FormButtonGroup.FormItem = ({ gutter, ...props }) => {
  return (
    <BaseItem
      {...props}
      label=" "
      style={{
        margin: 0,
        padding: 0,
        ...props.style,
        width: '100%',
      }}
      colon={false}
    >
      {props.children?.['length'] ? (
        <Space size={gutter}>{props.children}</Space>
      ) : (
        props.children
      )}
    </BaseItem>
  )
}

FormButtonGroup.Sticky = ({ align = 'left', ...props }) => {
  const ref = useRef()
  const [color, setColor] = useState('transparent')
  const prefixCls = usePrefixCls('formily-button-group')

  useLayoutEffect(() => {
    if (ref.current) {
      const computed = getInheritedBackgroundColor(ref.current)
      if (computed !== color) {
        setColor(computed)
      }
    }
  })
  return (
    <StickyBox
      {...props}
      className={cls(`${prefixCls}-sticky`, props.className)}
      style={{
        backgroundColor: color,
        ...props.style,
      }}
      bottom
    >
      <div
        ref={ref}
        className={`${prefixCls}-sticky-inner`}
        style={{
          ...props.style,
          justifyContent:
            align === 'left'
              ? 'flex-start'
              : align === 'right'
              ? 'flex-end'
              : 'center',
        }}
      >
        {props.children}
      </div>
    </StickyBox>
  )
}

export default FormButtonGroup

```

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

```markdown
# Form

> The combination of FormProvider + FormLayout + form tags can help us quickly implement forms that are submitted with carriage return and can be laid out in batches

## Use Cases

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

const form = createForm()

export default () => (
  <Form
    form={form}
    layout="vertical"
    feedbackLayout="terse"
    onAutoSubmit={console.log}
    onAutoSubmitFailed={console.log}
  >
    <FormGrid maxColumns={4}>
      <Field
        name="aa"
        title="select box"
        decorator={[FormItem]}
        component={[Select]}
        dataSource={[
          {
            label: 'Option 1',
            value: 1,
          },
          {
            label: 'Option 2',
            value: 2,
          },
        ]}
      />
      <Field
        name="bb"
        title="input box"
        required
        decorator={[FormItem]}
        component={[Input]}
      />
      <Field
        name="cc"
        title="input box"
        decorator={[FormItem]}
        component={[Input]}
      />
      <Field
        name="dd"
        title="input box"
        decorator={[FormItem]}
        component={[Input]}
      />
      <Field
        name="ee"
        title="input box"
        decorator={[FormItem]}
        component={[Input]}
      />
      <FormButtonGroup.FormItem>
        <Submit>Query</Submit>
      </FormButtonGroup.FormItem>
    </FormGrid>
  </Form>
)
```

<Alert style="margin-top:20px">
Note: To realize the carriage return submission, we cannot pass the onSubmit event to it when using the Submit component, otherwise the carriage return submission will become invalid. The purpose of this is to prevent users from writing onSubmit event listeners in multiple places at the same time, and processing logic If they are inconsistent, it is difficult to locate the problem when submitting.
</Alert>

## API

For layout-related API properties, we can refer to [FormLayout](./form-layout), and the rest are the unique API properties of the Form component

| Property name          | Type                                                                                             | Description                                                         | Default value |
| ---------------------- | ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------- | ------------- |
| form                   | [Form](https://core.formilyjs.org/api/models/form)                                               | Form example                                                        | -             |
| component              | string                                                                                           | Rendering component, can be specified as custom component rendering | `form`        |
| previewTextPlaceholder | ReactNode                                                                                        | Preview State Placeholder                                           | `N/A`         |
| onAutoSubmit           | `(values:any)=>any`                                                                              | Carriage return submit event callback                               | -             |
| onAutoSubmitFailed     | (feedbacks: [IFormFeedback](https://core.formilyjs.org/api/models/form#iformfeedback)[]) => void | Carriage return submission verification failure event callback      | -             |

```

--------------------------------------------------------------------------------
/packages/shared/src/compare.ts:
--------------------------------------------------------------------------------

```typescript
import { isArr } from './checkers'
import { instOf } from './instanceof'
const isArray = isArr
const keyList = Object.keys
const hasProp = Object.prototype.hasOwnProperty

/* eslint-disable */
function equal(a: any, b: any) {
  // fast-deep-equal index.js 2.0.1
  if (a === b) {
    return true
  }

  if (a && b && typeof a === 'object' && typeof b === 'object') {
    const arrA = isArray(a)
    const arrB = isArray(b)
    let i: number
    let length: number
    let key: string | number

    if (arrA && arrB) {
      length = a.length
      if (length !== b.length) {
        return false
      }
      for (i = length; i-- !== 0; ) {
        if (!equal(a[i], b[i])) {
          return false
        }
      }
      return true
    }

    if (arrA !== arrB) {
      return false
    }
    const momentA = a && a._isAMomentObject
    const momentB = b && b._isAMomentObject
    if (momentA !== momentB) return false
    if (momentA && momentB) return a.isSame(b)
    const immutableA = a && a.toJS
    const immutableB = b && b.toJS
    if (immutableA !== immutableB) return false
    if (immutableA) return a.is ? a.is(b) : a === b
    const dateA = instOf(a, 'Date')
    const dateB = instOf(b, 'Date')
    if (dateA !== dateB) {
      return false
    }
    if (dateA && dateB) {
      return a.getTime() === b.getTime()
    }
    const regexpA = instOf(a, 'RegExp')
    const regexpB = instOf(b, 'RegExp')
    if (regexpA !== regexpB) {
      return false
    }
    if (regexpA && regexpB) {
      return a.toString() === b.toString()
    }
    const urlA = instOf(a, 'URL')
    const urlB = instOf(b, 'URL')

    if (urlA !== urlB) {
      return false
    }

    if (urlA && urlB) {
      return a.href === b.href
    }

    const schemaA = a && a.toJSON
    const schemaB = b && b.toJSON
    if (schemaA !== schemaB) return false
    if (schemaA && schemaB) return equal(a.toJSON(), b.toJSON())

    const keys = keyList(a)
    length = keys.length

    if (length !== keyList(b).length) {
      return false
    }

    for (i = length; i-- !== 0; ) {
      if (!hasProp.call(b, keys[i])) {
        return false
      }
    }
    // end fast-deep-equal

    // Custom handling for React
    for (i = length; i-- !== 0; ) {
      key = keys[i]

      if (key === '_owner' && a.$$typeof) {
        // React-specific: avoid traversing React elements' _owner.
        //  _owner contains circular references
        // and is not needed when comparing the actual elements (and not their owners)
        // .$$typeof and ._store on just reasonable markers of a react element
        continue
      } else {
        // all other properties should be traversed as usual
        if (!equal(a[key], b[key])) {
          return false
        }
      }
    }

    // fast-deep-equal index.js 2.0.1
    return true
  }

  return a !== a && b !== b
}
// end fast-deep-equal

export const isEqual = function exportedEqual(a: any, b: any) {
  try {
    return equal(a, b)
  } catch (error) {
    /* istanbul ignore next */
    if (
      (error.message && error.message.match(/stack|recursion/i)) ||
      error.number === -2146828260
    ) {
      // warn on circular references, don't crash
      // browsers give this different errors name and messages:
      // chrome/safari: "RangeError", "Maximum call stack size exceeded"
      // firefox: "InternalError", too much recursion"
      // edge: "Error", "Out of stack space"
      console.warn(
        'Warning: react-fast-compare does not handle circular references.',
        error.name,
        error.message
      )
      return false
    }
    // some other error. we should definitely know about these
    /* istanbul ignore next */
    throw error
  }
}

```

--------------------------------------------------------------------------------
/packages/antd/src/form-button-group/index.tsx:
--------------------------------------------------------------------------------

```typescript
/**
 * 1. FormItem网格布局
 * 2. 居中,居右,居左布局
 * 3. 行内布局
 * 4. 吸底布局
 */
import React, { useRef, useLayoutEffect, useState } from 'react'
import { ReactFC } from '@formily/react'
import { Space } from 'antd'
import { SpaceProps } from 'antd/lib/space'
import { BaseItem, IFormItemProps } from '../form-item'
import { usePrefixCls } from '../__builtins__'
import StickyBox from 'react-sticky-box'
import cls from 'classnames'
interface IStickyProps extends React.ComponentProps<typeof StickyBox> {
  align?: React.CSSProperties['textAlign']
}

type IFormButtonGroupProps = Omit<SpaceProps, 'align' | 'size'> & {
  align?: React.CSSProperties['textAlign']
  gutter?: number
}

type ComposedButtonGroup = ReactFC<IFormButtonGroupProps> & {
  Sticky: ReactFC<React.PropsWithChildren<IStickyProps>>
  FormItem: ReactFC<
    IFormItemProps & {
      gutter?: number
    }
  >
}

function getInheritedBackgroundColor(el: HTMLElement) {
  // get default style for current browser
  const defaultStyle = getDefaultBackground() // typically "rgba(0, 0, 0, 0)"

  // get computed color for el
  const backgroundColor = window.getComputedStyle(el).backgroundColor

  // if we got a real value, return it
  if (backgroundColor != defaultStyle) return backgroundColor

  // if we've reached the top parent el without getting an explicit color, return default
  if (!el.parentElement) return defaultStyle

  // otherwise, recurse and try again on parent element
  return getInheritedBackgroundColor(el.parentElement)
}

function getDefaultBackground() {
  // have to add to the document in order to use getComputedStyle
  let div = document.createElement('div')
  document.head.appendChild(div)
  let bg = window.getComputedStyle(div).backgroundColor
  document.head.removeChild(div)
  return bg
}

export const FormButtonGroup: ComposedButtonGroup = ({
  align = 'left',
  gutter,
  ...props
}) => {
  const prefixCls = usePrefixCls('formily-button-group')
  return (
    <Space
      {...props}
      size={gutter}
      className={cls(prefixCls, props.className)}
      style={{
        ...props.style,
        justifyContent:
          align === 'left'
            ? 'flex-start'
            : align === 'right'
            ? 'flex-end'
            : 'center',
        display: 'flex',
      }}
    >
      {props.children}
    </Space>
  )
}

FormButtonGroup.FormItem = ({ gutter, ...props }) => {
  return (
    <BaseItem
      {...props}
      label=" "
      style={{
        margin: 0,
        padding: 0,
        ...props.style,
        width: '100%',
      }}
      colon={false}
    >
      {props.children?.['length'] ? (
        <Space size={gutter}>{props.children}</Space>
      ) : (
        props.children
      )}
    </BaseItem>
  )
}

FormButtonGroup.Sticky = ({ align = 'left', ...props }) => {
  const ref = useRef()
  const [color, setColor] = useState('transparent')
  const prefixCls = usePrefixCls('formily-button-group')

  useLayoutEffect(() => {
    if (ref.current) {
      const computed = getInheritedBackgroundColor(ref.current)
      if (computed !== color) {
        setColor(computed)
      }
    }
  })
  return (
    <StickyBox
      {...props}
      className={cls(`${prefixCls}-sticky`, props.className)}
      style={{
        backgroundColor: color,
        ...props.style,
      }}
      bottom
    >
      <div
        ref={ref}
        className={`${prefixCls}-sticky-inner`}
        style={{
          ...props.style,
          justifyContent:
            align === 'left'
              ? 'flex-start'
              : align === 'right'
              ? 'flex-end'
              : 'center',
        }}
      >
        {props.children}
      </div>
    </StickyBox>
  )
}

export default FormButtonGroup

```

--------------------------------------------------------------------------------
/packages/reactive/src/annotations/computed.ts:
--------------------------------------------------------------------------------

```typescript
import { ObModelSymbol, ReactionStack } from '../environment'
import { createAnnotation } from '../internals'
import { buildDataTree } from '../tree'
import { isFn } from '../checkers'
import {
  bindTargetKeyWithCurrentReaction,
  runReactionsFromTargetKey,
  bindComputedReactions,
  hasRunningReaction,
  isUntracking,
  batchStart,
  batchEnd,
  releaseBindingReactions,
} from '../reaction'

interface IValue<T = any> {
  value?: T
}
export interface IComputed {
  <T>(compute: () => T): IValue<T>
  <T>(compute: { get?: () => T; set?: (value: T) => void }): IValue<T>
}

const getDescriptor = Object.getOwnPropertyDescriptor

const getProto = Object.getPrototypeOf

const ClassDescriptorSymbol = Symbol('ClassDescriptorSymbol')

function getPropertyDescriptor(obj: any, key: PropertyKey) {
  if (!obj) return
  return getDescriptor(obj, key) || getPropertyDescriptor(getProto(obj), key)
}

function getPropertyDescriptorCache(obj: any, key: PropertyKey) {
  const constructor = obj.constructor
  if (constructor === Object || constructor === Array)
    return getPropertyDescriptor(obj, key)
  const cache = constructor[ClassDescriptorSymbol] || {}
  const descriptor = cache[key]
  if (descriptor) return descriptor
  const newDesc = getPropertyDescriptor(obj, key)
  constructor[ClassDescriptorSymbol] = cache
  cache[key] = newDesc
  return newDesc
}

function getPrototypeDescriptor(
  target: any,
  key: PropertyKey,
  value: any
): PropertyDescriptor {
  if (!target) {
    if (value) {
      if (isFn(value)) {
        return { get: value }
      } else {
        return value
      }
    }
    return {}
  }
  const descriptor = getPropertyDescriptorCache(target, key)
  if (descriptor) {
    return descriptor
  }
  return {}
}

export const computed: IComputed = createAnnotation(
  ({ target, key, value }) => {
    const store: IValue = {}

    const proxy = {}

    const context = target ? target : store
    const property = target ? key : 'value'
    const descriptor = getPrototypeDescriptor(target, property, value)

    function compute() {
      store.value = descriptor.get?.call(context)
    }
    function reaction() {
      if (ReactionStack.indexOf(reaction) === -1) {
        releaseBindingReactions(reaction)
        try {
          ReactionStack.push(reaction)
          compute()
        } finally {
          ReactionStack.pop()
        }
      }
    }
    reaction._name = 'ComputedReaction'
    reaction._scheduler = () => {
      reaction._dirty = true
      runReactionsFromTargetKey({
        target: context,
        key: property,
        value: store.value,
        type: 'set',
      })
    }
    reaction._isComputed = true
    reaction._dirty = true
    reaction._context = context
    reaction._property = property

    function get() {
      if (hasRunningReaction()) {
        bindComputedReactions(reaction)
      }
      if (!isUntracking()) {
        //如果允许untracked过程中收集依赖,那么永远不会存在绑定,因为_dirty已经设置为false
        if (reaction._dirty) {
          reaction()
          reaction._dirty = false
        }
      } else {
        compute()
      }
      bindTargetKeyWithCurrentReaction({
        target: context,
        key: property,
        type: 'get',
      })
      return store.value
    }

    function set(value: any) {
      try {
        batchStart()
        descriptor.set?.call(context, value)
      } finally {
        batchEnd()
      }
    }
    if (target) {
      Object.defineProperty(target, key, {
        get,
        set,
        enumerable: true,
      })
      return target
    } else {
      Object.defineProperty(proxy, 'value', {
        set,
        get,
      })
      buildDataTree(target, key, store)
      proxy[ObModelSymbol] = store
    }
    return proxy
  }
)

```

--------------------------------------------------------------------------------
/packages/react/docs/api/components/RecursionField.md:
--------------------------------------------------------------------------------

```markdown
---
order: 5
---

# RecursionField

## Description

The recursive rendering component is mainly based on [JSON-Schema](/api/shared/schema) for recursive rendering. It is the core rendering component inside the [SchemaField](/api/components/schema-field) component. Of course, it can It is used separately from SchemaField. When we use it, it is mainly used in custom components to implement custom components with recursive rendering capabilities.

## Signature

```ts
interface IRecursionFieldProps {
  schema: ISchema //Field schema
  name?: string //Path name
  basePath?: FormPathPattern //base path
  propsRecursion?: boolean //Whether to recursiveliy pass mapProperties and filterProperties
  onlyRenderProperties?: boolean //Whether to only render properties
  onlyRenderSelf?: boolean //Whether to only render itself without rendering properties
  mapProperties?: (schema: Schema, name: string) => Schema //schema properties mapper, mainly used to rewrite the schema
  filterProperties?: (schema: Schema, name: string) => boolean //schema properties filter, the filtered schema nodes will not be rendered
}

type RecursionField = React.FC<React.PropsWithChildren<IRecursionFieldProps>>
```

## Example

### Simple recursion

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

const form = createForm()

const Custom = (props) => {
  return <RecursionField schema={props.schema} onlyRenderProperties />
}

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

export default () => (
  <FormProvider form={form}>
    <SchemaField>
      <SchemaField.Object
        name="custom"
        x-component="Custom"
        x-component-props={{
          schema: {
            type: 'object',
            properties: {
              input: {
                type: 'string',
                'x-component': 'Input',
              },
            },
          },
        }}
      />
    </SchemaField>
  </FormProvider>
)
```

We can read independent schema objects from component properties and pass them to RecursionField for rendering

### Incremental list recursion

```tsx
import React from 'react'
import { createForm } from '@formily/core'
import {
  FormProvider,
  createSchemaField,
  RecursionField,
  useField,
  useFieldSchema,
  observer,
} from '@formily/react'
import { Input, Space, Button } from 'antd'

const form = createForm()

const ArrayItems = observer((props) => {
  const field = useField()
  const schema = useFieldSchema()
  return (
    <div>
      {props.value?.map((item, index) => {
        return (
          <div key={index} style={{ marginBottom: 10 }}>
            <Space>
              <RecursionField schema={schema.items} name={index} />
              <Button
                onClick={() => {
                  field.remove(index)
                }}
              >
                Remove
              </Button>
            </Space>
          </div>
        )
      })}
      <Button
        onClick={() => {
          field.push({})
        }}
      >
        Add
      </Button>
    </div>
  )
})

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

export default () => (
  <FormProvider form={form}>
    <SchemaField>
      <SchemaField.Array name="custom" x-component="ArrayItems">
        <SchemaField.Object>
          <SchemaField.String name="input" x-component="Input" />
        </SchemaField.Object>
      </SchemaField.Array>
    </SchemaField>
  </FormProvider>
)
```

Use [useField](/api/hooks/useField) and [useFieldSchema](/api/shared/use-field-schema) to get the field instance and field schema in the current field context

```

--------------------------------------------------------------------------------
/packages/react/docs/api/components/ObjectField.zh-CN.md:
--------------------------------------------------------------------------------

```markdown
---
order: 2
---

# ObjectField

## 描述

作为@formily/core 的 [createObjectField](https://core.formilyjs.org/zh-CN/api/models/form#createobjectfield) React 实现,它是专门用于将 ViewModel 与输入控件做绑定的桥接组件,ObjectField 组件属性参考[IFieldFactoryProps](https://core.formilyjs.org/zh-CN/api/models/form#ifieldfactoryprops)

<Alert>
我们在使用 ObjectField 组件的时候,一定要记得传name属性。同时要使用render props形式来组织子组件
</Alert>

## 签名

```ts
type ObjectField = React.FC<React.PropsWithChildren<IFieldFactoryProps>>
```

## 自定义组件用例

```tsx
import React from 'react'
import { createForm, ObjectField as ObjectFieldType } from '@formily/core'
import {
  FormProvider,
  Field,
  ObjectField,
  useField,
  observer,
} from '@formily/react'
import { Input, Button, Space } from 'antd'

const form = createForm()

const ObjectComponent = observer(() => {
  const field = useField<ObjectFieldType>()
  return (
    <>
      <div>
        {Object.keys(field.value || {}).map((key) => (
          <div key={key} style={{ display: 'flex-block', marginBottom: 10 }}>
            <Space>
              <Field name={key} component={[Input, { placeholder: key }]} />
              <Button
                onClick={() => {
                  field.removeProperty(key)
                }}
              >
                Remove
              </Button>
            </Space>
          </div>
        ))}
      </div>
      <Space>
        <Field
          name="propertyName"
          basePath={''}
          required
          component={[Input, { placeholder: 'Property Name' }]}
        />
        <Button
          onClick={() => {
            const name = form.values.propertyName
            if (name && !form.existValuesIn(`${field.path}.${name}`)) {
              field.addProperty(name, '')
              form.deleteValuesIn('propertyName')
            }
          }}
        >
          Add
        </Button>
      </Space>
    </>
  )
})

export default () => (
  <FormProvider form={form}>
    <ObjectField name="object" component={[ObjectComponent]} />
  </FormProvider>
)
```

## RenderProps 用例

```tsx
import React from 'react'
import { createForm } from '@formily/core'
import { FormProvider, Field, ObjectField } from '@formily/react'
import { Input, Button, Space } from 'antd'

const form = createForm()

export default () => (
  <FormProvider form={form}>
    <ObjectField name="object">
      {(field) => {
        return (
          <>
            <div>
              {Object.keys(field.value || {}).map((key) => (
                <div
                  key={key}
                  style={{ display: 'flex-block', marginBottom: 10 }}
                >
                  <Space>
                    <Field
                      name={key}
                      component={[Input, { placeholder: key }]}
                    />
                    <Button
                      onClick={() => {
                        field.removeProperty(key)
                      }}
                    >
                      Remove
                    </Button>
                  </Space>
                </div>
              ))}
            </div>
            <Space>
              <Field
                name="propertyName"
                basePath={''}
                required
                component={[Input, { placeholder: 'Property Name' }]}
              />
              <Button
                onClick={() => {
                  const name = form.values.propertyName
                  if (name && !form.existValuesIn(`${field.path}.${name}`)) {
                    field.addProperty(name, '')
                    form.deleteValuesIn('propertyName')
                  }
                }}
              >
                Add
              </Button>
            </Space>
          </>
        )
      }}
    </ObjectField>
  </FormProvider>
)
```

```

--------------------------------------------------------------------------------
/packages/antd/src/array-items/index.tsx:
--------------------------------------------------------------------------------

```typescript
import React, { useRef } from 'react'
import { ArrayField } from '@formily/core'
import {
  useField,
  observer,
  useFieldSchema,
  RecursionField,
} from '@formily/react'
import cls from 'classnames'
import { ISchema } from '@formily/json-schema'
import {
  usePrefixCls,
  SortableContainer,
  SortableElement,
} from '../__builtins__'
import { ArrayBase, ArrayBaseMixins, IArrayBaseProps } from '../array-base'

type ComposedArrayItems = React.FC<
  React.PropsWithChildren<
    React.HTMLAttributes<HTMLDivElement> & IArrayBaseProps
  >
> &
  ArrayBaseMixins & {
    Item?: React.FC<
      React.HTMLAttributes<HTMLDivElement> & {
        type?: 'card' | 'divide'
      }
    >
  }

const SortableItem = SortableElement(
  (props: React.PropsWithChildren<React.HTMLAttributes<HTMLDivElement>>) => {
    const prefixCls = usePrefixCls('formily-array-items')
    return (
      <div {...props} className={cls(`${prefixCls}-item`, props.className)}>
        {props.children}
      </div>
    )
  }
)

const SortableList = SortableContainer(
  (props: React.PropsWithChildren<React.HTMLAttributes<HTMLDivElement>>) => {
    const prefixCls = usePrefixCls('formily-array-items')
    return (
      <div {...props} className={cls(`${prefixCls}-list`, props.className)}>
        {props.children}
      </div>
    )
  }
)

const isAdditionComponent = (schema: ISchema) => {
  return schema['x-component']?.indexOf('Addition') > -1
}

const useAddition = () => {
  const schema = useFieldSchema()
  return schema.reduceProperties((addition, schema, key) => {
    if (isAdditionComponent(schema)) {
      return <RecursionField schema={schema} name={key} />
    }
    return addition
  }, null)
}

export const ArrayItems: ComposedArrayItems = observer((props) => {
  const field = useField<ArrayField>()
  const prefixCls = usePrefixCls('formily-array-items')
  const ref = useRef<HTMLDivElement>(null)
  const schema = useFieldSchema()
  const addition = useAddition()
  const dataSource = Array.isArray(field.value) ? field.value : []
  const { onAdd, onCopy, onRemove, onMoveDown, onMoveUp } = props
  if (!schema) throw new Error('can not found schema object')
  return (
    <ArrayBase
      onAdd={onAdd}
      onCopy={onCopy}
      onRemove={onRemove}
      onMoveUp={onMoveUp}
      onMoveDown={onMoveDown}
    >
      <div
        {...props}
        ref={ref}
        onChange={() => {}}
        className={cls(prefixCls, props.className)}
      >
        <SortableList
          list={dataSource.slice()}
          className={`${prefixCls}-sort-helper`}
          onSortEnd={({ oldIndex, newIndex }) => {
            field.move(oldIndex, newIndex)
          }}
        >
          {dataSource?.map((item, index) => {
            const items = Array.isArray(schema.items)
              ? schema.items[index] || schema.items[0]
              : schema.items
            return (
              <ArrayBase.Item
                key={index}
                index={index}
                record={() => field.value?.[index]}
              >
                <SortableItem key={`item-${index}`} lockAxis="y" index={index}>
                  <div className={`${prefixCls}-item-inner`}>
                    <RecursionField schema={items} name={index} />
                  </div>
                </SortableItem>
              </ArrayBase.Item>
            )
          })}
        </SortableList>
        {addition}
      </div>
    </ArrayBase>
  )
})

ArrayItems.displayName = 'ArrayItems'

ArrayItems.Item = (props) => {
  const prefixCls = usePrefixCls('formily-array-items')
  return (
    <div
      {...props}
      onChange={() => {}}
      className={cls(`${prefixCls}-${props.type || 'card'}`, props.className)}
    >
      {props.children}
    </div>
  )
}

ArrayBase.mixin(ArrayItems)

export default ArrayItems

```

--------------------------------------------------------------------------------
/scripts/rollup.base.js:
--------------------------------------------------------------------------------

```javascript
import path from 'path'
import typescript from 'rollup-plugin-typescript2'
import resolve from 'rollup-plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs'
import externalGlobals from 'rollup-plugin-external-globals'
import injectProcessEnv from 'rollup-plugin-inject-process-env'
import dts from 'rollup-plugin-dts'
import { terser } from 'rollup-plugin-terser'

const presets = () => {
  const externals = {
    antd: 'antd',
    vue: 'Vue',
    react: 'React',
    moment: 'moment',
    'react-is': 'ReactIs',
    '@alifd/next': 'Next',
    'mobx-react-lite': 'mobxReactLite',
    'react-dom': 'ReactDOM',
    'element-ui': 'Element',
    '@ant-design/icons': 'icons',
    '@vue/composition-api': 'VueCompositionAPI',
    '@formily/reactive-react': 'Formily.ReactiveReact',
    '@formily/reactive-vue': 'Formily.ReactiveVue',
    '@formily/reactive': 'Formily.Reactive',
    '@formily/path': 'Formily.Path',
    '@formily/shared': 'Formily.Shared',
    '@formily/validator': 'Formily.Validator',
    '@formily/core': 'Formily.Core',
    '@formily/json-schema': 'Formily.JSONSchema',
    '@formily/react': 'Formily.React',
    '@formily/vue': 'Formily.Vue',
    'vue-demi': 'VueDemi'
  }
  return [
    typescript({
      tsconfig: './tsconfig.build.json',
      tsconfigOverride: {
        compilerOptions: {
          module: 'ESNext',
          declaration: false,
        },
      },
    }),
    resolve(),
    commonjs(),
    externalGlobals(externals, {
      exclude: ['**/*.{less,sass,scss}'],
    }),
  ]
}

const createEnvPlugin = (env) => {
  return injectProcessEnv(
    {
      NODE_ENV: env,
    },
    {
      exclude: '**/*.{css,less,sass,scss}',
      verbose: false,
    }
  )
}

const inputFilePath = path.join(process.cwd(), 'src/index.ts')

const noUIDtsPackages = [
  'formily.core',
  'formily.validator',
  'formily.shared',
  'formily.path',
  'formily.json-schema',
  'formily.reactive',
]

export const removeImportStyleFromInputFilePlugin = () => ({
  name: 'remove-import-style-from-input-file',
  transform(code, id) {
    // 样式由 build:style 进行打包,所以要删除入口文件上的 `import './style'`
    if (inputFilePath === id) {
      return code.replace(`import './style';`, '')
    }

    return code
  },
})

export default (filename, targetName, ...plugins) => {
  const base = [
    {
      input: 'src/index.ts',
      output: {
        format: 'umd',
        file: `dist/${filename}.umd.development.js`,
        name: targetName,
        sourcemap: true,
        amd: {
          id: filename,
        },
        globals: {
          '@formily/json-schema': 'Formily.JSONSchema',
        },
      },
      external: ['react', 'react-dom', 'react-is', '@formily/json-schema'],
      plugins: [...presets(), ...plugins, createEnvPlugin('development')],
    },
    {
      input: 'src/index.ts',
      output: {
        format: 'umd',
        file: `dist/${filename}.umd.production.js`,
        name: targetName,
        sourcemap: true,
        amd: {
          id: filename,
        },
        globals: {
          '@formily/json-schema': 'Formily.JSONSchema',
        },
      },
      external: ['react', 'react-dom', 'react-is', '@formily/json-schema'],
      plugins: [
        ...presets(),
        terser(),
        ...plugins,
        createEnvPlugin('production'),
      ],
    },
  ]

  if (noUIDtsPackages.includes(filename)) {
    base.push({
      input: 'esm/index.d.ts',
      output: {
        format: 'es',
        file: `dist/${filename}.d.ts`,
      },
      plugins: [dts(), ...plugins],
    })
    base.push({
      input: 'esm/index.d.ts',
      output: {
        format: 'es',
        file: `dist/${filename}.all.d.ts`,
      },
      plugins: [
        dts({
          respectExternal: true,
        }),
        ...plugins,
      ],
    })
  }

  return base
}

```

--------------------------------------------------------------------------------
/packages/reactive/src/autorun.ts:
--------------------------------------------------------------------------------

```typescript
import {
  batchEnd,
  batchStart,
  disposeBindingReactions,
  releaseBindingReactions,
  disposeEffects,
  hasDepsChange,
} from './reaction'
import { isFn } from './checkers'
import { ReactionStack } from './environment'
import { Reaction, IReactionOptions, Dispose } from './types'
import { toArray } from './array'

interface IValue {
  currentValue?: any
  oldValue?: any
}

export const autorun = (tracker: Reaction, name = 'AutoRun') => {
  const reaction: Reaction = () => {
    if (!isFn(tracker)) return
    if (reaction._boundary > 0) return
    if (ReactionStack.indexOf(reaction) === -1) {
      releaseBindingReactions(reaction)
      try {
        batchStart()
        ReactionStack.push(reaction)
        tracker()
      } finally {
        ReactionStack.pop()
        reaction._boundary++
        batchEnd()
        reaction._boundary = 0
        reaction._memos.cursor = 0
        reaction._effects.cursor = 0
      }
    }
  }
  const cleanRefs = () => {
    reaction._memos = {
      queue: [],
      cursor: 0,
    }
    reaction._effects = {
      queue: [],
      cursor: 0,
    }
  }
  reaction._boundary = 0
  reaction._name = name
  cleanRefs()
  reaction()
  return () => {
    disposeBindingReactions(reaction)
    disposeEffects(reaction)
    cleanRefs()
  }
}

autorun.memo = <T>(callback: () => T, dependencies?: any[]): T => {
  if (!isFn(callback)) return
  const current = ReactionStack[ReactionStack.length - 1]
  if (!current || !current._memos)
    throw new Error('autorun.memo must used in autorun function body.')
  const deps = toArray(dependencies || [])
  const id = current._memos.cursor++
  const old = current._memos.queue[id]
  if (!old || hasDepsChange(deps, old.deps)) {
    const value = callback()
    current._memos.queue[id] = {
      value,
      deps,
    }
    return value
  }
  return old.value
}

autorun.effect = (callback: () => void | Dispose, dependencies?: any[]) => {
  if (!isFn(callback)) return
  const current = ReactionStack[ReactionStack.length - 1]
  if (!current || !current._effects)
    throw new Error('autorun.effect must used in autorun function body.')
  const effects = current._effects
  const deps = toArray(dependencies || [{}])
  const id = effects.cursor++
  const old = effects.queue[id]
  if (!old || hasDepsChange(deps, old.deps)) {
    Promise.resolve(0).then(() => {
      if (current._disposed) return
      const dispose = callback()
      if (isFn(dispose)) {
        effects.queue[id].dispose = dispose
      }
    })
    effects.queue[id] = {
      deps,
    }
  }
}

export const reaction = <T>(
  tracker: () => T,
  subscriber?: (value: T, oldValue: T) => void,
  options?: IReactionOptions<T>
) => {
  const realOptions = {
    name: 'Reaction',
    ...options,
  }
  const value: IValue = {}
  const dirtyCheck = () => {
    if (isFn(realOptions.equals))
      return !realOptions.equals(value.oldValue, value.currentValue)
    return value.oldValue !== value.currentValue
  }

  const fireAction = () => {
    try {
      //如果untrack的话,会导致用户如果在scheduler里同步调用setState影响下次React渲染的依赖收集
      batchStart()
      if (isFn(subscriber)) subscriber(value.currentValue, value.oldValue)
    } finally {
      batchEnd()
    }
  }

  const reaction: Reaction = () => {
    if (ReactionStack.indexOf(reaction) === -1) {
      releaseBindingReactions(reaction)
      try {
        ReactionStack.push(reaction)
        value.currentValue = tracker()
      } finally {
        ReactionStack.pop()
      }
    }
  }
  reaction._scheduler = (looping) => {
    looping()
    if (dirtyCheck()) fireAction()
    value.oldValue = value.currentValue
  }
  reaction._name = realOptions.name
  reaction()
  value.oldValue = value.currentValue
  if (realOptions.fireImmediately) {
    fireAction()
  }
  return () => {
    disposeBindingReactions(reaction)
  }
}

```

--------------------------------------------------------------------------------
/packages/antd/src/password/PasswordStrength.tsx:
--------------------------------------------------------------------------------

```typescript
import React, { Fragment } from 'react'
import { ReactFC } from '@formily/react'
import { isFn } from '@formily/shared'

type ReactRenderPropsChildren<T = any> =
  | React.ReactNode
  | ((props: T) => React.ReactElement)

interface IPasswordStrengthProps {
  value?: React.ReactText
  children?: ReactRenderPropsChildren<number>
}

const isNum = function (c) {
  return c >= 48 && c <= 57
}
const isLower = function (c) {
  return c >= 97 && c <= 122
}
const isUpper = function (c) {
  return c >= 65 && c <= 90
}
const isSymbol = function (c) {
  return !(isLower(c) || isUpper(c) || isNum(c))
}
const isLetter = function (c) {
  return isLower(c) || isUpper(c)
}

const getStrength = (val) => {
  if (!val) return 0
  let num = 0
  let lower = 0
  let upper = 0
  let symbol = 0
  let MNS = 0
  let rep = 0
  let repC = 0
  let consecutive = 0
  let sequential = 0
  const len = () => num + lower + upper + symbol
  const callme = () => {
    let re = num > 0 ? 1 : 0
    re += lower > 0 ? 1 : 0
    re += upper > 0 ? 1 : 0
    re += symbol > 0 ? 1 : 0
    if (re > 2 && len() >= 8) {
      return re + 1
    } else {
      return 0
    }
  }
  for (let i = 0; i < val.length; i++) {
    const c = val.charCodeAt(i)
    if (isNum(c)) {
      num++
      if (i !== 0 && i !== val.length - 1) {
        MNS++
      }
      if (i > 0 && isNum(val.charCodeAt(i - 1))) {
        consecutive++
      }
    } else if (isLower(c)) {
      lower++
      if (i > 0 && isLower(val.charCodeAt(i - 1))) {
        consecutive++
      }
    } else if (isUpper(c)) {
      upper++
      if (i > 0 && isUpper(val.charCodeAt(i - 1))) {
        consecutive++
      }
    } else {
      symbol++
      if (i !== 0 && i !== val.length - 1) {
        MNS++
      }
    }
    let exists = false
    for (let j = 0; j < val.length; j++) {
      if (val[i] === val[j] && i !== j) {
        exists = true
        repC += Math.abs(val.length / (j - i))
      }
    }
    if (exists) {
      rep++
      const unique = val.length - rep
      repC = unique ? Math.ceil(repC / unique) : Math.ceil(repC)
    }
    if (i > 1) {
      const last1 = val.charCodeAt(i - 1)
      const last2 = val.charCodeAt(i - 2)
      if (isLetter(c)) {
        if (isLetter(last1) && isLetter(last2)) {
          const v = val.toLowerCase()
          const vi = v.charCodeAt(i)
          const vi1 = v.charCodeAt(i - 1)
          const vi2 = v.charCodeAt(i - 2)
          if (vi - vi1 === vi1 - vi2 && Math.abs(vi - vi1) === 1) {
            sequential++
          }
        }
      } else if (isNum(c)) {
        if (isNum(last1) && isNum(last2)) {
          if (c - last1 === last1 - last2 && Math.abs(c - last1) === 1) {
            sequential++
          }
        }
      } else {
        if (isSymbol(last1) && isSymbol(last2)) {
          if (c - last1 === last1 - last2 && Math.abs(c - last1) === 1) {
            sequential++
          }
        }
      }
    }
  }
  let sum = 0
  const length = len()
  sum += 4 * length
  if (lower > 0) {
    sum += 2 * (length - lower)
  }
  if (upper > 0) {
    sum += 2 * (length - upper)
  }
  if (num !== length) {
    sum += 4 * num
  }
  sum += 6 * symbol
  sum += 2 * MNS
  sum += 2 * callme()
  if (length === lower + upper) {
    sum -= length
  }
  if (length === num) {
    sum -= num
  }
  sum -= repC
  sum -= 2 * consecutive
  sum -= 3 * sequential
  sum = sum < 0 ? 0 : sum
  sum = sum > 100 ? 100 : sum

  if (sum >= 80) {
    return 100
  } else if (sum >= 60) {
    return 80
  } else if (sum >= 40) {
    return 60
  } else if (sum >= 20) {
    return 40
  } else {
    return 20
  }
}

export const PasswordStrength: ReactFC<IPasswordStrengthProps> = (props) => {
  if (isFn(props.children)) {
    return props.children(getStrength(String(props.value)))
  } else {
    return <Fragment>{props.children}</Fragment>
  }
}

```

--------------------------------------------------------------------------------
/packages/next/src/password/PasswordStrength.tsx:
--------------------------------------------------------------------------------

```typescript
import React, { Fragment } from 'react'
import { ReactFC } from '@formily/react'
import { isFn } from '@formily/shared'

type ReactRenderPropsChildren<T = any> =
  | React.ReactNode
  | ((props: T) => React.ReactElement)

interface IPasswordStrengthProps {
  value?: React.ReactText
  children?: ReactRenderPropsChildren<number>
}

const isNum = function (c) {
  return c >= 48 && c <= 57
}
const isLower = function (c) {
  return c >= 97 && c <= 122
}
const isUpper = function (c) {
  return c >= 65 && c <= 90
}
const isSymbol = function (c) {
  return !(isLower(c) || isUpper(c) || isNum(c))
}
const isLetter = function (c) {
  return isLower(c) || isUpper(c)
}

const getStrength = (val) => {
  if (!val) return 0
  let num = 0
  let lower = 0
  let upper = 0
  let symbol = 0
  let MNS = 0
  let rep = 0
  let repC = 0
  let consecutive = 0
  let sequential = 0
  const len = () => num + lower + upper + symbol
  const callme = () => {
    let re = num > 0 ? 1 : 0
    re += lower > 0 ? 1 : 0
    re += upper > 0 ? 1 : 0
    re += symbol > 0 ? 1 : 0
    if (re > 2 && len() >= 8) {
      return re + 1
    } else {
      return 0
    }
  }
  for (let i = 0; i < val.length; i++) {
    const c = val.charCodeAt(i)
    if (isNum(c)) {
      num++
      if (i !== 0 && i !== val.length - 1) {
        MNS++
      }
      if (i > 0 && isNum(val.charCodeAt(i - 1))) {
        consecutive++
      }
    } else if (isLower(c)) {
      lower++
      if (i > 0 && isLower(val.charCodeAt(i - 1))) {
        consecutive++
      }
    } else if (isUpper(c)) {
      upper++
      if (i > 0 && isUpper(val.charCodeAt(i - 1))) {
        consecutive++
      }
    } else {
      symbol++
      if (i !== 0 && i !== val.length - 1) {
        MNS++
      }
    }
    let exists = false
    for (let j = 0; j < val.length; j++) {
      if (val[i] === val[j] && i !== j) {
        exists = true
        repC += Math.abs(val.length / (j - i))
      }
    }
    if (exists) {
      rep++
      const unique = val.length - rep
      repC = unique ? Math.ceil(repC / unique) : Math.ceil(repC)
    }
    if (i > 1) {
      const last1 = val.charCodeAt(i - 1)
      const last2 = val.charCodeAt(i - 2)
      if (isLetter(c)) {
        if (isLetter(last1) && isLetter(last2)) {
          const v = val.toLowerCase()
          const vi = v.charCodeAt(i)
          const vi1 = v.charCodeAt(i - 1)
          const vi2 = v.charCodeAt(i - 2)
          if (vi - vi1 === vi1 - vi2 && Math.abs(vi - vi1) === 1) {
            sequential++
          }
        }
      } else if (isNum(c)) {
        if (isNum(last1) && isNum(last2)) {
          if (c - last1 === last1 - last2 && Math.abs(c - last1) === 1) {
            sequential++
          }
        }
      } else {
        if (isSymbol(last1) && isSymbol(last2)) {
          if (c - last1 === last1 - last2 && Math.abs(c - last1) === 1) {
            sequential++
          }
        }
      }
    }
  }
  let sum = 0
  const length = len()
  sum += 4 * length
  if (lower > 0) {
    sum += 2 * (length - lower)
  }
  if (upper > 0) {
    sum += 2 * (length - upper)
  }
  if (num !== length) {
    sum += 4 * num
  }
  sum += 6 * symbol
  sum += 2 * MNS
  sum += 2 * callme()
  if (length === lower + upper) {
    sum -= length
  }
  if (length === num) {
    sum -= num
  }
  sum -= repC
  sum -= 2 * consecutive
  sum -= 3 * sequential
  sum = sum < 0 ? 0 : sum
  sum = sum > 100 ? 100 : sum

  if (sum >= 80) {
    return 100
  } else if (sum >= 60) {
    return 80
  } else if (sum >= 40) {
    return 60
  } else if (sum >= 20) {
    return 40
  } else {
    return 20
  }
}

export const PasswordStrength: ReactFC<IPasswordStrengthProps> = (props) => {
  if (isFn(props.children)) {
    return props.children(getStrength(String(props.value)))
  } else {
    return <Fragment>{props.children}</Fragment>
  }
}

```

--------------------------------------------------------------------------------
/packages/react/docs/index.md:
--------------------------------------------------------------------------------

```markdown
---
title: Formily-Alibaba unified front-end form solution
order: 10
hero:
  title: React Library
  desc: Alibaba Unified Form Solution
  actions:
    - text: Home Site
      link: //formilyjs.org
    - text: Development Guide
      link: /guide
features:
  - icon: https://img.alicdn.com/imgextra/i1/O1CN01bHdrZJ1rEOESvXEi5_!!6000000005599-55-tps-800-800.svg
    title: Ultra High Performance
    desc: Dependency tracking, efficient update, on-demand rendering
  - icon: https://img.alicdn.com/imgextra/i2/O1CN016i72sH1c5wh1kyy9U_!!6000000003550-55-tps-800-800.svg
    title: Out Of The Box
    desc: The component status is automatically bound, and the access cost is extremely low
  - icon: https://img.alicdn.com/imgextra/i3/O1CN01JHzg8U1FZV5Mvt012_!!6000000000501-55-tps-800-800.svg
    title: JSON Schema Driver
    desc: Standard JSON-Schema
  - icon: https://img.alicdn.com/imgextra/i3/O1CN0194OqFF1ui6mMT4g7O_!!6000000006070-55-tps-800-800.svg
    title: Scene Reuse
    desc: Based on protocol-driven, abstract scene components
  - icon: https://img.alicdn.com/imgextra/i4/O1CN018vDmpl2186xdLu6KI_!!6000000006939-55-tps-800-800.svg
    title: Debugging Friendly
    desc: Natural docking with Formily DevTools
  - icon: https://img.alicdn.com/imgextra/i4/O1CN01u6jHgs1ZMwXpjAYnh_!!6000000003181-55-tps-800-800.svg
    title: Smart Tips
    desc: Embrace Typescript
footer: Open-source MIT Licensed | Copyright © 2019-present<br />Powered by self
---

## Installation

```bash
$ npm install --save @formily/core @formily/react

```

## Quick start

```tsx
/**
 * defaultShowCode: true
 */
import React, { useMemo } from 'react'
import { createForm, setValidateLanguage } from '@formily/core'
import {
  FormProvider,
  FormConsumer,
  Field,
  useField,
  observer,
} from '@formily/react'
import { Input, Form } from 'antd'

// FormItem UI component
const FormItem = observer(({ children }) => {
  const field = useField()
  return (
    <Form.Item
      label={field.title}
      help={field.selfErrors?.length ? field.selfErrors : undefined}
      extra={field.description}
      validateStatus={field.validateStatus}
    >
      {children}
    </Form.Item>
  )
})

/*
 * The above logic has been implemented in @formily/antd, and there is no need to rewrite it in actual use
 */

//Switch the built-in check internationalization copy to English
setValidateLanguage('en')

export default () => {
  const form = useMemo(() => createForm({ validateFirst: true }))

  const createPasswordEqualValidate = (equalName) => (field) => {
    if (
      form.values.confirm_password &&
      field.value &&
      form.values[equalName] !== field.value
    ) {
      field.selfErrors = ['Password does not match Confirm Password.']
    } else {
      field.selfErrors = []
    }
  }

  return (
    <FormProvider form={form}>
      <Form layout="vertical">
        <Field
          name="name"
          title="Name"
          required
          decorator={[FormItem]}
          component={[Input, { placeholder: 'Please Input' }]}
        />
        <Field
          name="password"
          title="Password"
          required
          decorator={[FormItem]}
          component={[Input, { type: 'password', placeholder: 'Please Input' }]}
          reactions={createPasswordEqualValidate('confirm_password')}
        />
        <Field
          name="confirm_password"
          title="Confirm Password"
          required
          decorator={[FormItem]}
          component={[Input, { type: 'password', placeholder: 'Please Input' }]}
          reactions={createPasswordEqualValidate('password')}
        />
        <code>
          <pre>
            <FormConsumer>
              {(form) => JSON.stringify(form.values, null, 2)}
            </FormConsumer>
          </pre>
        </code>
      </Form>
    </FormProvider>
  )
}
```

```

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

```markdown
# Ant Design

## Introduction

@formily/antd is a professional component library for form scenarios based on Ant Design encapsulation. It has the following characteristics:

- Only Formily 2.x is supported
  - Most components are not backward compatible
  - Unfortunately, many components of 1.x have inherent flaws in the API design. This is also because the form scheme has been explored, so there will be version breaks.
- Richer component system
  - Layout components
    - FormLayout
    - FormItem
    - FormGrid
    - FormButtonGroup
    - Space
    - Submit
    - Reset
  - Input controls
    - Input
    - Password
    - Select
    - TreeSelect
    - DatePicker
    - TimePicker
    - NumberPicker
    - Transfer
    - Cascader
    - Radio
    - Checkbox
    - Upload
    - Switch
  - Scene components
    - ArrayCards
    - ArrayItems
    - ArrayTable
    - ArrayTabs
    - FormCollapse
    - FormStep
    - FormTab
    - FormDialog
    - FormDrawer
    - Editable
  - Reading state component
    - PreviewText
- Theme customization ability
  - Completely abandon the 1.x styled-components solution, follow the style system of the component library, it is more convenient to customize the theme
- Support secondary packaging
  - All components can be repackaged, and the 1.x component system cannot be repackaged, so providing this capability makes it more convenient for users to do business customization
- Support reading mode
  - Although 1.x also supports reading mode, 2.x provides a separate PreviewText component, users can make reading mode encapsulation based on it, which is more flexible
- Type is more friendly
  - Each component has an extremely complete type definition, and users can feel an unprecedented intelligent reminder experience during the actual development process
- More complete layout control capabilities
  - 1.x's layout capabilities have basically converged to FormMegaLayout. This time, we directly removed Mega. Mega is a standard component and is completely internalized into FormLayout and FormItem components. At the same time, MegaLayout's grid layout capabilities are placed in FormGrid components. In, it also provides smarter layout capabilities.
- More elegant and easy-to-use APIs, such as:
  - FormStep in the past has many problems. First, the type is not friendly. Second, the API is too hidden. To control the forward and backwards, you need to understand a bunch of private events. In the new version of FormStep, users only need to pay attention to the FormStep Reactive Model. You can create a Reactive Model through createFormStep and pass it to the FormStep component to quickly communicate. Similarly, FormTab/FormCollapse is the same communication mode.
  - Pop-up forms, drawer forms, presumably in the past, users had to write a lot of code on these two scenarios almost every time. This time, an extremely simple API is directly provided for users to use, which maximizes development efficiency.

## Installation

```bash
$ npm install --save antd moment
$ npm install --save @formily/core @formily/react @formily/antd

```

## Q/A

Q: I want to package a set of component libraries by myself, what should I do?

Answer: If it is an open source component library, you can directly participate in the project co-construction and provide PR. If it is a private component library in the enterprise, you can refer to the source code. The source code does not have too much complicated logic.

Question: Why do components such as ArrayCards/ArrayTable/FormStep only support Schema mode and not pure JSX mode?

Answer: This is the core advantage of Schema mode. With the help of protocols, we can do scene-based abstraction. On the contrary, pure JSX mode is limited by the unparseability of JSX. It is difficult for us to achieve UI-level scene-based abstraction. It's just an abstract hook.

```

--------------------------------------------------------------------------------
/packages/element/docs/demos/guide/array-table/json-schema.vue:
--------------------------------------------------------------------------------

```vue
<template>
  <FormProvider :form="form">
    <SchemaField :schema="schema" />
    <Submit @submit="log">提交</Submit>
  </FormProvider>
</template>

<script>
import { createForm } from '@formily/core'
import { FormProvider, createSchemaField } from '@formily/vue'
import { Submit, FormItem, ArrayTable, Input, Editable } from '@formily/element'

const fields = createSchemaField({
  components: {
    FormItem,
    ArrayTable,
    Input,
    Editable,
  },
})

export default {
  components: { FormProvider, Submit, ...fields },
  data() {
    const form = createForm()
    const schema = {
      type: 'object',
      properties: {
        array: {
          type: 'array',
          'x-decorator': 'FormItem',
          'x-component': 'ArrayTable',
          items: {
            type: 'object',
            properties: {
              column1: {
                type: 'void',
                'x-component': 'ArrayTable.Column',
                'x-component-props': {
                  width: 80,
                  title: 'Index',
                  align: 'center',
                },
                properties: {
                  index: {
                    type: 'void',
                    'x-component': 'ArrayTable.Index',
                  },
                },
              },
              column2: {
                type: 'void',
                'x-component': 'ArrayTable.Column',
                'x-component-props': { width: 200, title: 'A1' },
                properties: {
                  a1: {
                    type: 'string',
                    'x-decorator': 'Editable',
                    'x-component': 'Input',
                  },
                },
              },
              column3: {
                type: 'void',
                'x-component': 'ArrayTable.Column',
                'x-component-props': { width: 200, title: 'A2' },
                properties: {
                  a2: {
                    type: 'string',
                    'x-decorator': 'FormItem',
                    'x-component': 'Input',
                  },
                },
              },
              column4: {
                type: 'void',
                'x-component': 'ArrayTable.Column',
                'x-component-props': { title: 'A3' },
                properties: {
                  a3: {
                    type: 'string',
                    'x-decorator': 'FormItem',
                    'x-component': 'Input',
                  },
                },
              },
              column5: {
                type: 'void',
                'x-component': 'ArrayTable.Column',
                'x-component-props': {
                  title: 'Operations',
                  prop: 'operations',
                  width: 200,
                  fixed: 'right',
                },
                properties: {
                  item: {
                    type: 'void',
                    'x-component': 'FormItem',
                    properties: {
                      remove: {
                        type: 'void',
                        'x-component': 'ArrayTable.Remove',
                      },
                      moveDown: {
                        type: 'void',
                        'x-component': 'ArrayTable.MoveDown',
                      },
                      moveUp: {
                        type: 'void',
                        'x-component': 'ArrayTable.MoveUp',
                      },
                    },
                  },
                },
              },
            },
          },
          properties: {
            add: {
              type: 'void',
              'x-component': 'ArrayTable.Addition',
              title: '添加条目',
            },
          },
        },
      },
    }
    return {
      form,
      schema,
    }
  },
  methods: {
    log(...v) {
      console.log(...v)
    },
  },
}
</script>

```

--------------------------------------------------------------------------------
/docs/guide/learn-formily.md:
--------------------------------------------------------------------------------

```markdown
# How to learn Formily

## Study Suggestion

To describe Formily in one sentence, it is an MVVM form solution that abstracts the form domain model. Therefore, if you want to use Formily in depth, you must learn and understand what Formily's domain model is like and what problems does it solve. After understanding the domain model, it is actually how to consume the view layer of this domain model. This layer only needs to look at the documentation of the specific components.

## About the documentation

Because Formily’s learning costs are still relatively high, if you want to quickly understand the full picture of Formily, the most important thing is to read the documentation. It's just how to look at the document and where it will be more important. Below we give different document learning routes for different users.

### Entry-level user

- Introduction, because you need to understand Formily's core ideas and whether it is suitable for your business scenario.
- Quick start, learn how to use Formily in practice from the simplest example.
- Component documentation/core library documentation, because Formily has already encapsulated most of the out-of-the-box components for you. If you encounter component-related problems, you can just check the component documentation just like looking up a dictionary.
- Scenario case, starting from the specific scenario, see what is the best practice in this scenario.

### Advanced users

- Digest the core concepts carefully and have a deeper understanding of Formily.
- Advanced guide, mainly to learn more advanced usage methods, such as custom components, from simple custom components to super complex custom components.
- Read component documents/core library documents at any time to deepen memory
- For the details and best practices of custom component development, it is recommended to look directly at the source code of @formily/antd or @formily/next, because this is the boilerplate code and is closely related to the actual business scenario.

### Source code co-builder

- Contribution guide, understand the most basic contribution posture.
- Read the document, if you find that the document is defective, you can submit a PR to fix it.
- Read the unit test to understand the implementation details corresponding to each test case. If you find that there are missing test cases, you can submit a PR.
- Read the source code, if you find a bug in the source code, you can raise a PR.

<Alert type="error">
Pay attention to modify the source code, you must bring unit tests
</Alert>

## About the question

If you encounter problems during the development process, it is recommended to use the search function at the top of the document to quickly search for the content of the document and solve it quickly. If you can’t find it, I recommend you to ask questions in the [forum](https://github.com/alibaba/formily/discussions). It is convenient to record. If you encounter a very urgent problem, you can help solve it in the Dingding group @白玄. **It is not recommended to ask various basic questions directly without reading the document, which is very inefficient**

## About the bug

If you find behaviors that do not meet expectations during the development process and can be reproduced in the smallest case, you can submit an [issue](https://github.com/alibaba/formily/issues) to Formily
It is strongly not recommended to record the problem in the issue, which will disrupt the information flow of Issue. At the same time, **be sure to bring the smallest reproducible link address when mentioning Issue**, so that developers can quickly locate the problem and fix it quickly, instead of Find bugs in a bunch of codes.

## About Feature Request

If during the development process you find that some of Formily's designs are not good, or can be improved better, you can submit your own ideas in the [forum](https://github.com/alibaba/formily/discussions)

```

--------------------------------------------------------------------------------
/packages/validator/src/parser.ts:
--------------------------------------------------------------------------------

```typescript
import { isArr, isBool, isFn, isStr } from '@formily/shared'
import {
  ValidatorDescription,
  ValidatorFunction,
  ValidatorParsedFunction,
  Validator,
  IValidatorRules,
  isValidateResult,
  IValidatorOptions,
} from './types'
import { getValidateRules, getValidateLocale } from './registry'
import { render } from './template'

const getRuleMessage = (rule: IValidatorRules, type: string) => {
  if (rule.format) {
    return rule.message || getValidateLocale(rule.format)
  }
  return rule.message || getValidateLocale(type)
}

export const parseValidatorDescription = (
  description: ValidatorDescription
): IValidatorRules => {
  if (!description) return {}
  let rules: IValidatorRules = {}
  if (isStr(description)) {
    rules.format = description
  } else if (isFn(description)) {
    rules.validator = description
  } else {
    rules = Object.assign(rules, description)
  }
  return rules
}

export const parseValidatorDescriptions = <Context = any>(
  validator: Validator<Context>
): IValidatorRules[] => {
  if (!validator) return []
  const array = isArr(validator) ? validator : [validator]
  return array.map((description) => {
    return parseValidatorDescription(description)
  })
}

export const parseValidatorRules = (
  rules: IValidatorRules = {}
): ValidatorParsedFunction[] => {
  const getRulesKeys = (): string[] => {
    const keys = []
    if ('required' in rules) {
      keys.push('required')
    }
    for (let key in rules) {
      if (key === 'required' || key === 'validator') continue
      keys.push(key)
    }
    if ('validator' in rules) {
      keys.push('validator')
    }
    return keys
  }
  const getContext = (context: any, value: any) => {
    return {
      ...rules,
      ...context,
      value,
    }
  }
  const createValidate =
    (callback: ValidatorFunction, message: string) =>
    async (value: any, context: any) => {
      const context_ = getContext(context, value)
      try {
        const results = await callback(
          value,
          { ...rules, message },
          context_,
          (message: string, scope: any) => {
            return render(
              {
                type: 'error',
                message,
              },
              Object.assign(context_, scope)
            )?.message
          }
        )
        if (isBool(results)) {
          if (!results) {
            return render(
              {
                type: 'error',
                message,
              },
              context_
            )
          }
          return {
            type: 'error',
            message: undefined,
          }
        } else if (results) {
          if (isValidateResult(results)) {
            return render(results, context_)
          }
          return render(
            {
              type: 'error',
              message: results,
            },
            context_
          )
        }

        return {
          type: 'error',
          message: undefined,
        }
      } catch (e) {
        return {
          type: 'error',
          message: e?.message || e,
        }
      }
    }
  return getRulesKeys().reduce((buf, key) => {
    const callback = getValidateRules(key)
    if (callback) {
      const validator = createValidate(callback, getRuleMessage(rules, key))
      return buf.concat(validator)
    }
    return buf
  }, [])
}

export const parseValidator = <Context = any>(
  validator: Validator<Context>,
  options: IValidatorOptions = {}
) => {
  if (!validator) return []
  const array = isArr(validator) ? validator : [validator]
  return array.reduce<ValidatorParsedFunction<Context>[]>(
    (buf, description) => {
      const rules = parseValidatorDescription(description)
      const triggerType = rules.triggerType ?? 'onInput'
      if (options?.triggerType && options.triggerType !== triggerType)
        return buf
      return rules ? buf.concat(parseValidatorRules(rules)) : buf
    },
    []
  )
}

```

--------------------------------------------------------------------------------
/docs/guide/contribution.md:
--------------------------------------------------------------------------------

```markdown
# Contribution Guide

## Why become a contributor?

Welcome to our community!**Formily** It is the only official open-source form framework announced by Alibaba. Its functions and quality are guaranteed. It has a large number of community users. Participating in contributions can make **Formily** stronger and allow more developers to enjoy a better experience of developing forms. we are very grateful to any people who initiated **Pull Request** for this project.

## What can I contribute?

- Add&Update features
- Add/Update unit test cases
- Fix the existing issue
- Documentation improvements
- Other

## How to contribute?

#### Pull Repository

- Original repository: https://github.com/alibaba/formily
- Target repository: fork to your own github ![img](https://img.alicdn.com/tfs/TB1NLrjxXY7gK0jSZKzXXaikpXa-2206-490.png)

#### Pull Branch

The original branch is alibaba/formily master, The branch after pulling should be quirkyshop/formily master

> Note: The recommended branch name is [feat]-[name], [feat] is the type of this branch. Featdoc[other] is optional, and [name] is the name, just customize it. eg. unittest-core (meaning: add single test to the core)

#### Submit Code

The code style follows 2 spaces and no semicolons. Please do not include any console-related methods and debuggers in the code unless it is explained. After the development is completed, submit a pull request to the repository you forked.![img](https://img.alicdn.com/tfs/TB1HSvqxkT2gK0jSZFkXXcIQFXa-2050-898.png)![img](https://img.alicdn.com/tfs/TB1O.6mxbr1gK0jSZR0XXbP8XXa-1696-254.png)

> Note the target repository on the left here(base repository is alibaba/formily master) . And then the doc-wiki of the current branch own repository on the right.

#### PR Specification

Reference documents: https://github.com/alibaba/formily/blob/master/.github/GIT_COMMIT_SPECIFIC.md

- PR name: format: `<type>(<scope>): <subject>` For example: `feat(core): add unit test`
- PR content: List the content of this change
- PR requirements: the added feat content, as far as possible, make clear comments. And the corresponding single test coverage should be covered as much 关注梁帅抽大奖 possible.
- BUGFIX requirements: If the modified issue is related to issues, please include the relevant issueID in the content.

#### Review&Merge

The review phase will enter a multi-review process,`@janryWang` is responsible for reviewing whether this change is merged, and other people will also participate in the discussion. The discussion will be stored in the PR of github, and the DingTalk group will also receive corresponding notifications.

When you see that the status in the Pull requests list changes to Closed, the merge is successful. ![img](https://img.alicdn.com/tfs/TB1HUnjxXY7gK0jSZKzXXaikpXa-964-104.png)

#### Synchronize source repository changes to repository after fork

```
# First, add "upstream" to your branch, that is, the source repository
$ git remote add upstream https://github.com/alibaba/formily.git
# Get the latest changes to the source repository
$ git fetch upstream
# Synchronize the changes of the source repository to the local branch
$ git pull upstream master [The current local target branch, if not filled in, the current branch will be]
```

#### Project Development

```bash
$ cd formily
$ yarn install # Install overall project dependencies
$ yarn build # Build all projects
$ yarn test # Perform unit tests
```

#### Development Document

Main project document

```bash
$ yarn start
```

Core project documentation

```bash
$ yarn workspace @formily/core start
```

React project documentation

```bash
$ yarn workspace @formily/react start
```

Vue project documentation

```bash
$ yarn workspace @formily/vue start
```

Antd project documentation

```bash
$ yarn workspace @formily/antd start
```

Fusion project documentation

```bash
$ yarn workspace @formily/next start
```

Reactive project documentation

```bash
$ yarn workspace @formily/reactive start
```

```

--------------------------------------------------------------------------------
/packages/next/src/array-items/index.tsx:
--------------------------------------------------------------------------------

```typescript
import React from 'react'
import { ArrayField } from '@formily/core'
import {
  useField,
  observer,
  useFieldSchema,
  RecursionField,
} from '@formily/react'
import cls from 'classnames'
import {
  SortableContainer,
  SortableElement,
  SortableContainerProps,
  SortableElementProps,
} from 'react-sortable-hoc'
import { ISchema } from '@formily/json-schema'
import { usePrefixCls } from '../__builtins__'
import { ArrayBase, ArrayBaseMixins, IArrayBaseProps } from '../array-base'

type ComposedArrayItems = React.FC<
  React.PropsWithChildren<
    React.HTMLAttributes<HTMLDivElement> & IArrayBaseProps
  >
> &
  ArrayBaseMixins & {
    Item?: React.FC<
      React.HTMLAttributes<HTMLDivElement> & {
        type?: 'card' | 'divide'
      }
    >
  }

const SortableItem: React.FC<
  React.PropsWithChildren<React.HTMLAttributes<HTMLDivElement>> &
    SortableElementProps
> = SortableElement(
  (props: React.PropsWithChildren<React.HTMLAttributes<HTMLDivElement>>) => {
    const prefixCls = usePrefixCls('formily-array-items')
    return (
      <div {...props} className={cls(`${prefixCls}-item`, props.className)}>
        {props.children}
      </div>
    )
  }
) as any

const SortableList: React.FC<
  React.PropsWithChildren<React.HTMLAttributes<HTMLDivElement>> &
    SortableContainerProps
> = SortableContainer(
  (props: React.PropsWithChildren<React.HTMLAttributes<HTMLDivElement>>) => {
    const prefixCls = usePrefixCls('formily-array-items')
    return (
      <div {...props} className={cls(`${prefixCls}-list`, props.className)}>
        {props.children}
      </div>
    )
  }
) as any

const isAdditionComponent = (schema: ISchema) => {
  return schema['x-component']?.indexOf('Addition') > -1
}

const useAddition = () => {
  const schema = useFieldSchema()
  return schema.reduceProperties((addition, schema, key) => {
    if (isAdditionComponent(schema)) {
      return <RecursionField schema={schema} name={key} />
    }
    return addition
  }, null)
}

export const ArrayItems: ComposedArrayItems = observer((props) => {
  const field = useField<ArrayField>()
  const prefixCls = usePrefixCls('formily-array-items')
  const schema = useFieldSchema()
  const addition = useAddition()
  const { onAdd, onCopy, onRemove, onMoveDown, onMoveUp } = props
  const dataSource = Array.isArray(field.value) ? field.value : []
  return (
    <ArrayBase
      onAdd={onAdd}
      onCopy={onCopy}
      onRemove={onRemove}
      onMoveUp={onMoveUp}
      onMoveDown={onMoveDown}
    >
      <div
        {...props}
        onChange={() => {}}
        className={cls(prefixCls, props.className)}
      >
        <SortableList
          useDragHandle
          lockAxis="y"
          helperClass={`${prefixCls}-sort-helper`}
          onSortEnd={({ oldIndex, newIndex }) => {
            field.move(oldIndex, newIndex)
          }}
        >
          {dataSource?.map((item, index) => {
            const items = Array.isArray(schema.items)
              ? schema.items[index] || schema.items[0]
              : schema.items
            return (
              <ArrayBase.Item
                key={index}
                index={index}
                record={() => field.value?.[index]}
              >
                <SortableItem key={`item-${index}`} index={index}>
                  <div className={`${prefixCls}-item-inner`}>
                    <RecursionField schema={items} name={index} />
                  </div>
                </SortableItem>
              </ArrayBase.Item>
            )
          })}
        </SortableList>
        {addition}
      </div>
    </ArrayBase>
  )
})

ArrayItems.displayName = 'ArrayItems'

ArrayItems.Item = (props) => {
  const prefixCls = usePrefixCls('formily-array-items')
  return (
    <div
      {...props}
      onChange={() => {}}
      className={cls(`${prefixCls}-${props.type || 'card'}`, props.className)}
    >
      {props.children}
    </div>
  )
}

ArrayBase.mixin(ArrayItems)

export default ArrayItems

```

--------------------------------------------------------------------------------
/packages/react/docs/api/components/ObjectField.md:
--------------------------------------------------------------------------------

```markdown
---
order: 2
---

# ObjectField

## Description

As @formily/core's [createObjectField](https://core.formilyjs.org/api/models/form#createobjectfield) React implementation, it is a bridge component specifically used to bind ViewModel and input controls, ObjectField component Property reference [IFieldFactoryProps](https://core.formilyjs.org/api/models/form#ifieldfactoryprops)

<Alert>
When we use the ObjectField component, we must remember to pass the name attribute. At the same time, use render props to organize sub-components
</Alert>

## Signature

```ts
type ObjectField = React.FC<React.PropsWithChildren<IFieldFactoryProps>>
```

## Custom component use case

```tsx
import React from 'react'
import { createForm, ObjectField as ObjectFieldType } from '@formily/core'
import {
  FormProvider,
  Field,
  ObjectField,
  useField,
  observer,
} from '@formily/react'
import { Input, Button, Space } from 'antd'

const form = createForm()

const ObjectComponent = observer(() => {
  const field = useField<ObjectFieldType>()
  return (
    <>
      <div>
        {Object.keys(field.value || {}).map((key) => (
          <div key={key} style={{ display: 'flex-block', marginBottom: 10 }}>
            <Space>
              <Field name={key} component={[Input, { placeholder: key }]} />
              <Button
                onClick={() => {
                  field.removeProperty(key)
                }}
              >
                Remove
              </Button>
            </Space>
          </div>
        ))}
      </div>
      <Space>
        <Field
          name="propertyName"
          basePath={''}
          required
          component={[Input, { placeholder: 'Property Name' }]}
        />
        <Button
          onClick={() => {
            const name = form.values.propertyName
            if (name && !form.existValuesIn(`${field.path}.${name}`)) {
              field.addProperty(name, '')
              form.deleteValuesIn('propertyName')
            }
          }}
        >
          Add
        </Button>
      </Space>
    </>
  )
})

export default () => (
  <FormProvider form={form}>
    <ObjectField name="object" component={[ObjectComponent]} />
  </FormProvider>
)
```

## RenderProps use cases

```tsx
import React from 'react'
import { createForm } from '@formily/core'
import { FormProvider, Field, ObjectField } from '@formily/react'
import { Input, Button, Space } from 'antd'

const form = createForm()

export default () => (
  <FormProvider form={form}>
    <ObjectField name="object">
      {(field) => {
        return (
          <>
            <div>
              {Object.keys(field.value || {}).map((key) => (
                <div
                  key={key}
                  style={{ display: 'flex-block', marginBottom: 10 }}
                >
                  <Space>
                    <Field
                      name={key}
                      component={[Input, { placeholder: key }]}
                    />
                    <Button
                      onClick={() => {
                        field.removeProperty(key)
                      }}
                    >
                      Remove
                    </Button>
                  </Space>
                </div>
              ))}
            </div>
            <Space>
              <Field
                name="propertyName"
                basePath={''}
                required
                component={[Input, { placeholder: 'Property Name' }]}
              />
              <Button
                onClick={() => {
                  const name = form.values.propertyName
                  if (name && !form.existValuesIn(`${field.path}.${name}`)) {
                    field.addProperty(name, '')
                    form.deleteValuesIn('propertyName')
                  }
                }}
              >
                Add
              </Button>
            </Space>
          </>
        )
      }}
    </ObjectField>
  </FormProvider>
)
```

```

--------------------------------------------------------------------------------
/packages/vue/src/__tests__/form.spec.ts:
--------------------------------------------------------------------------------

```typescript
import Vue from 'vue'
import { render, fireEvent } from '@testing-library/vue'
import { mount } from '@vue/test-utils'
import { createForm } from '@formily/core'
import {
  FormProvider,
  FormConsumer,
  Field,
  ObjectField,
  VoidField,
} from '../vue2-components'
import { defineComponent } from 'vue-demi'
import { useParentForm, useField } from '../hooks'
import { h } from 'vue-demi'

Vue.component('FormProvider', FormProvider)
Vue.component('FormConsumer', FormConsumer)
Vue.component('ObjectField', ObjectField)
Vue.component('VoidField', VoidField)
Vue.component('Field', Field)

const Input = defineComponent({
  props: ['value'],
  setup(props, { attrs, listeners }) {
    const fieldRef = useField()
    return () => {
      const field = fieldRef.value
      return h('input', {
        class: 'test-input',
        attrs: {
          ...attrs,
          value: props.value,
          'data-testid': field.path.toString(),
        },
        on: {
          ...listeners,
          input: listeners.change,
        },
      })
    }
  },
})

test('render form', () => {
  const form = createForm()
  render({
    data() {
      return { form }
    },
    template: `<FormProvider :form="form">
      <FormConsumer>
        <template #default="{ form }">
          {{ form.mounted }}
        </template>
      </FormConsumer>
      <FormConsumer />
    </FormProvider>`,
  })

  expect(form.mounted).toBeTruthy()
})

const DisplayParentForm = defineComponent({
  setup() {
    const form = useParentForm()

    return () => h('div', [form.value.displayName])
  },
})

test('useParentForm', () => {
  const { queryByTestId } = render({
    components: {
      DisplayParentForm,
    },
    data() {
      const form = createForm()
      return { form }
    },
    template: `<FormProvider :form="form">
    <ObjectField name="aa">
      <Field name="bb">
        <DisplayParentForm data-testid="111" />
      </Field>
    </ObjectField>
    <VoidField name="cc">
      <Field name="dd">
        <DisplayParentForm data-testid="222" />
      </Field>
    </VoidField>
    <DisplayParentForm data-testid="333" />
  </FormProvider>`,
  })
  expect(queryByTestId('111').textContent).toBe('ObjectField')
  expect(queryByTestId('222').textContent).toBe('Form')
  expect(queryByTestId('333').textContent).toBe('Form')
})

test('useInjectionCleaner', async () => {
  const form = createForm()

  const { getByTestId } = render({
    name: 'TestComponent',
    setup() {
      return {
        form,
        Input,
      }
    },
    template: `<FormProvider :form="form">
      <Field name="parent">
        <FormProvider :form="form">
          <Field name="inner" :component="[Input]" />
        </FormProvider>
        <Field name="outer" :component="[Input]" />
      </Field>
    </FormProvider>`,
  })
  expect(form.mounted).toBeTruthy()
  expect(form.query('inner').take().mounted).toBeTruthy()
  expect(form.query('parent.outer').take().mounted).toBeTruthy()
  await fireEvent.update(getByTestId('parent.outer'), '123')
  expect(form.getValuesIn('parent.outer')).toBe('123')
  await fireEvent.update(getByTestId('inner'), '123')
  expect(form.getValuesIn('inner')).toBe('123')
})

test('FormConsumer', async () => {
  const form = createForm({
    values: {
      a: 'abc',
    },
  })
  const wrapper = mount({
    data() {
      return { form, Input }
    },
    template: `<FormProvider :form="form">
      <Field name="a" :component="[Input]" />
      <FormConsumer ref="consumer">
        <template #default="{ form }">
          <div class="consumer">{{JSON.stringify(form.values)}}</div>
        </template>
      </FormConsumer>
    </FormProvider>`,
  })
  expect(form.getValuesIn('a')).toBe('abc')
  expect(wrapper.find('.consumer').text()).toBe('{"a":"abc"}')
  form.setDisplay('none')
  expect(form.getValuesIn('a')).toBeUndefined()
  const $consumer = wrapper.vm.$refs.consumer as Vue
  $consumer.$forceUpdate()
  expect(wrapper.find('.consumer').text()).toBe('{}')
})

```

--------------------------------------------------------------------------------
/packages/element/src/form-grid/index.ts:
--------------------------------------------------------------------------------

```typescript
import { Grid, IGridOptions } from '@formily/grid'
import { markRaw } from '@formily/reactive'
import { observer } from '@formily/reactive-vue'
import { h } from '@formily/vue'
import {
  computed,
  defineComponent,
  inject,
  InjectionKey,
  onMounted,
  PropType,
  provide,
  ref,
  Ref,
  watchEffect,
} from 'vue-demi'
import { useFormLayout } from '../form-layout'
import { stylePrefix } from '../__builtins__/configs'
import { composeExport } from '../__builtins__/shared'

export interface IFormGridProps extends IGridOptions {
  grid?: Grid<HTMLElement>
  prefixCls?: string
  className?: string
  style?: React.CSSProperties
}

const FormGridSymbol: InjectionKey<Ref<Grid<HTMLElement>>> =
  Symbol('FormGridContext')

interface GridColumnProps {
  gridSpan: number
}

export const createFormGrid = (props: IFormGridProps): Grid<HTMLElement> => {
  return markRaw(new Grid(props))
}

export const useFormGrid = (): Ref<Grid<HTMLElement>> => inject(FormGridSymbol)

/**
 * @deprecated
 */
const useGridSpan = (gridSpan: number) => {
  return gridSpan
}

/**
 * @deprecated
 */
export const useGridColumn = (gridSpan = 1) => {
  return gridSpan
}

const FormGridInner = observer(
  defineComponent({
    name: 'FFormGrid',
    props: {
      columnGap: {
        type: Number,
      },
      rowGap: {
        type: Number,
      },
      minColumns: {
        type: [Number, Array],
      },
      minWidth: {
        type: [Number, Array],
      },
      maxColumns: {
        type: [Number, Array],
      },
      maxWidth: {
        type: [Number, Array],
      },
      breakpoints: {
        type: Array,
      },
      colWrap: {
        type: Boolean,
        default: true,
      },
      strictAutoFit: {
        type: Boolean,
        default: false,
      },
      shouldVisible: {
        type: Function as PropType<IGridOptions['shouldVisible']>,
        default() {
          return () => true
        },
      },
      grid: {
        type: Object as PropType<Grid<HTMLElement>>,
      },
    },
    setup(props: IFormGridProps) {
      const layout = useFormLayout()

      const gridInstance = computed(() => {
        const newProps: IFormGridProps = {}
        Object.keys(props).forEach((key) => {
          if (typeof props[key] !== 'undefined') {
            newProps[key] = props[key]
          }
        })
        const options = {
          columnGap: layout.value?.gridColumnGap ?? 8,
          rowGap: layout.value?.gridRowGap ?? 4,
          ...newProps,
        }
        return markRaw(options?.grid ? options.grid : new Grid(options))
      })
      const prefixCls = `${stylePrefix}-form-grid`
      const root = ref(null)

      provide(FormGridSymbol, gridInstance)

      onMounted(() => {
        watchEffect((onInvalidate) => {
          const dispose = gridInstance.value.connect(root.value)
          onInvalidate(() => {
            dispose()
          })
        })
      })

      return {
        prefixCls,
        root,
        gridInstance,
      }
    },
    render() {
      const { prefixCls, gridInstance } = this
      return h(
        'div',
        {
          attrs: {
            class: `${prefixCls}`,
          },
          style: {
            gridTemplateColumns: gridInstance.templateColumns,
            gap: gridInstance.gap,
          },
          ref: 'root',
        },
        {
          default: () => this.$slots.default,
        }
      )
    },
  })
) as any

const FormGridColumn = observer(
  defineComponent({
    name: 'FFormGridColumn',
    props: {
      gridSpan: {
        type: Number,
        default: 1,
      },
    },
    setup(props: GridColumnProps, { slots }) {
      return () => {
        return h(
          'div',
          {
            attrs: {
              'data-grid-span': props.gridSpan,
            },
          },
          slots
        )
      }
    },
  })
)

export const FormGrid = composeExport(FormGridInner, {
  GridColumn: FormGridColumn,
  useGridSpan,
  useFormGrid,
  createFormGrid,
})

export default FormGrid

```

--------------------------------------------------------------------------------
/docs/guide/advanced/business-logic.md:
--------------------------------------------------------------------------------

```markdown
# Manage Business Logic

In the previous document, we can actually find that Formily has provided the ability to describe the logic locally, that is, the x-reactions/reactions property of the field component. And in Schema, x-reactions can pass both functions and a structured object. Of course, there are also effects inherited from Formily 1.x, So to summarize, the ways to describe logic in Formily 2.x are:

- Effects or reactions property in pure JSX mode
- Effects or structured x-reactions property in Schema mode
- Effects or functional x-reactions property in Schema mode

With so many ways of describing logic, how should we choose? What scenarios are best practices? First, we need to understand the positioning of effects and reactions.

First of all, reactions are responders used on specific field properties. They will be executed repeatedly based on the data changes that the function depends on. Its biggest advantage is that it is simple, straightforward and easy to understand, such as:

```tsx pure
/* eslint-disable */
<Field
  name="A"
  reactions={(field) => {
    /**specific logic implementation**/
  }}
/>
```

Then, effects are used to implement the side-effect isolation logic management model. Its biggest advantage is that it can make the view code easier to maintain in a scenario with a large number of fields. At the same time, it also has the ability to process fields in batches. For example, we declare x-reactions in the field properties of A, B, C. If the x-reactions logic of these three fields are exactly the same, then we only need to write this in effects:

```ts
onFieldReact('*(A,B,C)', (field) => {
  //...logic
})
```

Another advantage of using effects is that a series of reusable logic plug-ins can be implemented, which can be very convenient logic pluggable, and at the same time can do some things like global monitoring.

In this way, do we not need to define the logic locally?

No, the premise of the above writing is that for a large number of fields, if the view layer is full of reactions, it looks uncomfortable, so it is a better strategy to consider extracting logic from unified maintenance.
On the contrary, if the number of fields is small and the logic is relatively simple, it is also good to write reactions directly on the field attributes, which is clear.

At the same time, because JSON Schema can be consumed by the configuration system, we need to logically configure a specific field on the configuration interface. So we still need to support local definition logic capabilities, and also need to support structured description logic, such as:

```json
{
  "x-reactions": {
    "dependencies": ["aa"],
    "fulfill": {
      "state": {
        "visible": "{{$deps[0] == '123'}}"
      }
    }
  }
}
```

This can well solve the linkage requirements of most configuration scenarios. However, there is another scenario, that is, our linkage process is asynchronous, the logic is very complicated, or there is a large amount of data processing, then we can only consider open up the ability to describe functional states, such as:

```json
{
  "x-reactions": "{{(field)=>{/**specific logic implementation**/}}}"
}
```

This is very similar to a low-code configuration. Of course, we can also register a series of general logic functions in the context scope:

```json
{
  "x-reactions": "{{customFunction}}"
}
```

In conclusion, the way we manage business logic has the following priorities:

- Pure source mode
  - The number of fields is huge and the logic is complex, and the logic defined in effects is preferred.
  - The number of fields is small, the logic is simple, and the logic defined in reactions is preferred
- Schema mode
  - There is no asynchronous logic, structured reactions are preferred to define logic.
  - There is asynchronous logic, or a large number of calculations, the functional state reactions are preferred to define logic.

For how to play with effects in effects, we mainly look at the [@formily/core](https://core.formilyjs.org) document.

```

--------------------------------------------------------------------------------
/packages/element/docs/demos/guide/form-grid/native.vue:
--------------------------------------------------------------------------------

```vue
<template>
  <div>
    <p>maxColumns 3 + minColumns 2</p>
    <FormGrid :maxColumns="3" :minColumns="2" :columnGap="4">
      <FormGridColumn :gridSpan="4">
        <Cell>1</Cell>
      </FormGridColumn>
      <FormGridColumn>
        <Cell>2</Cell>
      </FormGridColumn>
      <FormGridColumn>
        <Cell>3</Cell>
      </FormGridColumn>
      <FormGridColumn>
        <Cell>4</Cell>
      </FormGridColumn>
      <FormGridColumn>
        <Cell>5</Cell>
      </FormGridColumn>
      <FormGridColumn>
        <Cell>6</Cell>
      </FormGridColumn>
    </FormGrid>
    <p>maxColumns 3</p>
    <FormGrid :maxColumns="3" :columnGap="4">
      <FormGridColumn :gridSpan="2">
        <Cell>1</Cell>
      </FormGridColumn>
      <FormGridColumn>
        <Cell>2</Cell>
      </FormGridColumn>
      <FormGridColumn>
        <Cell>3</Cell>
      </FormGridColumn>
      <FormGridColumn>
        <Cell>4</Cell>
      </FormGridColumn>
      <FormGridColumn>
        <Cell>5</Cell>
      </FormGridColumn>
      <FormGridColumn>
        <Cell>6</Cell>
      </FormGridColumn>
    </FormGrid>
    <p>minColumns 2</p>
    <FormGrid :minColumns="2" :columnGap="4">
      <FormGridColumn :gridSpan="2">
        <Cell>1</Cell>
      </FormGridColumn>
      <FormGridColumn>
        <Cell>2</Cell>
      </FormGridColumn>
      <FormGridColumn>
        <Cell>3</Cell>
      </FormGridColumn>
      <FormGridColumn>
        <Cell>4</Cell>
      </FormGridColumn>
      <FormGridColumn>
        <Cell>5</Cell>
      </FormGridColumn>
      <FormGridColumn>
        <Cell>6</Cell>
      </FormGridColumn>
    </FormGrid>
    <p>Null</p>
    <FormGrid :columnGap="4">
      <FormGridColumn :gridSpan="2">
        <Cell>1</Cell>
      </FormGridColumn>
      <FormGridColumn>
        <Cell>2</Cell>
      </FormGridColumn>
      <FormGridColumn>
        <Cell>3</Cell>
      </FormGridColumn>
      <FormGridColumn>
        <Cell>4</Cell>
      </FormGridColumn>
      <FormGridColumn>
        <Cell>5</Cell>
      </FormGridColumn>
      <FormGridColumn>
        <Cell>6</Cell>
      </FormGridColumn>
    </FormGrid>
    <p>minWidth 150 +maxColumns 3</p>
    <FormGrid :minWidth="150" :maxColumns="3" :columnGap="4">
      <FormGridColumn :gridSpan="2">
        <Cell>1</Cell>
      </FormGridColumn>
      <FormGridColumn>
        <Cell>2</Cell>
      </FormGridColumn>
      <FormGridColumn>
        <Cell>3</Cell>
      </FormGridColumn>
      <FormGridColumn>
        <Cell>4</Cell>
      </FormGridColumn>
      <FormGridColumn>
        <Cell>5</Cell>
      </FormGridColumn>
      <FormGridColumn>
        <Cell>6</Cell>
      </FormGridColumn>
    </FormGrid>
    <p>maxWidth 120+minColumns 2</p>
    <FormGrid :maxWidth="120" :minColumns="2" :columnGap="4">
      <FormGridColumn :gridSpan="2">
        <Cell>1</Cell>
      </FormGridColumn>
      <FormGridColumn>
        <Cell>2</Cell>
      </FormGridColumn>
      <FormGridColumn>
        <Cell>3</Cell>
      </FormGridColumn>
      <FormGridColumn>
        <Cell>4</Cell>
      </FormGridColumn>
      <FormGridColumn>
        <Cell>5</Cell>
      </FormGridColumn>
      <FormGridColumn>
        <Cell>6</Cell>
      </FormGridColumn>
    </FormGrid>
    <p>maxWidth 120 + gridSpan -1</p>
    <FormGrid :maxWidth="120" :columnGap="4">
      <FormGridColumn :gridSpan="2">
        <Cell>1</Cell>
      </FormGridColumn>
      <FormGridColumn>
        <Cell>2</Cell>
      </FormGridColumn>
      <FormGridColumn :gridSpan="-1">
        <Cell>3</Cell>
      </FormGridColumn>
    </FormGrid>
  </div>
</template>

<script>
import { FormGrid } from '@formily/element'

const Cell = {
  functional: true,
  render(h, context) {
    return h(
      'div',
      {
        style: {
          backgroundColor: '#AAA',
          color: '#FFF',
          height: '30px',
          display: 'flex',
          alignItems: 'center',
          padding: '0 10px',
        },
      },
      context.children
    )
  },
}

export default {
  components: { FormGrid, FormGridColumn: FormGrid.GridColumn, Cell },
}
</script>

```

--------------------------------------------------------------------------------
/packages/benchmark/src/index.tsx:
--------------------------------------------------------------------------------

```typescript
import React, { useMemo, useState } from 'react'
import ReactDOM from 'react-dom'
import { createForm } from '@formily/core'
import { Field, createSchemaField } from '@formily/react'
import { Input, Form, FormItem } from '@formily/antd'
import { Form as AntdForm, Input as AntdInput } from 'antd'
const SchemaField = createSchemaField({
  components: {
    Input,
    FormItem,
  },
})

const PureAntdInput = () => {
  return (
    <>
      {Array.from({ length: 2000 }).map((_, i) => {
        return <AntdInput key={i} />
      })}
    </>
  )
}

const PureAntd = () => {
  return (
    <AntdForm>
      <h1>Please pay attention to the performance of the form input</h1>
      {Array.from({ length: 2000 }).map((_, i) => {
        return (
          <AntdForm.Item
            key={i}
            name={`name_${i}`}
            required
            label={`name ${i + 1}`}
          >
            <AntdInput />
          </AntdForm.Item>
        )
      })}
    </AntdForm>
  )
}

const PureJSX = () => {
  const form = useMemo(() => createForm(), [])
  return (
    <Form form={form}>
      <h1>Please pay attention to the performance of the form input</h1>
      {Array.from({ length: 2000 }).map((_, i) => {
        return (
          <Field
            key={i}
            name={`name_${i}`}
            title={`name ${i + 1}`}
            required
            decorator={[FormItem]}
            component={[Input, { placeholder: 'Please Input' }]}
          />
        )
      })}
    </Form>
  )
}

const PureJSONSchema = () => {
  const form = useMemo(() => createForm(), [])
  const schema = {
    type: 'object',
    properties: {},
  }
  Array.from({ length: 2000 }).forEach((_, i) => {
    schema.properties[`name_${i}`] = {
      type: 'string',
      title: `name ${i + 1}`,
      'x-decorator': 'FormItem',
      'x-component': 'Input',
      'x-component-props': {
        placeholder: 'Please Input',
      },
    }
  })
  return (
    <Form form={form}>
      <h1>Please pay attention to the performance of the form input</h1>
      <SchemaField schema={schema} />
    </Form>
  )
}

const PureMarkupSchema = () => {
  const form = useMemo(() => createForm(), [])
  return (
    <Form form={form}>
      <h1>Please pay attention to the performance of the form input</h1>
      <SchemaField>
        {Array.from({ length: 2000 }).map((_, i) => {
          return (
            <SchemaField.String
              key={i}
              name={`name_${i}`}
              title={`name ${i + 1}`}
              required
              x-decorator="FormItem"
              x-component="Input"
              x-component-props={{
                placeholder: 'Please Input',
              }}
            />
          )
        })}
      </SchemaField>
    </Form>
  )
}

const App = () => {
  const [visibleAntd, setVisibleAntd] = useState(false)
  const [visibleJSX, setVisibleJSX] = useState(false)
  const [visibleMarkupSchema, setVisibleMarkupSchema] = useState(false)
  const [visibleJSONSchema, setVisibleJSONSchema] = useState(false)
  const [visibleAntdInput, setVisibleAntdInput] = useState(false)
  return (
    <div>
      <button
        onClick={() => {
          setVisibleJSX(!visibleJSX)
        }}
      >
        Show JSX
      </button>
      <button
        onClick={() => {
          setVisibleMarkupSchema(!visibleMarkupSchema)
        }}
      >
        Show Markup Schema
      </button>
      <button
        onClick={() => {
          setVisibleJSONSchema(!visibleJSONSchema)
        }}
      >
        Show JSON Schema
      </button>
      <button
        onClick={() => {
          setVisibleAntd(!visibleAntd)
        }}
      >
        Show Antd
      </button>
      <button
        onClick={() => {
          setVisibleAntdInput(!visibleAntdInput)
        }}
      >
        Show Antd Input
      </button>
      {visibleJSX && <PureJSX />}
      {visibleMarkupSchema && <PureMarkupSchema />}
      {visibleJSONSchema && <PureJSONSchema />}
      {visibleAntd && <PureAntd />}
      {visibleAntdInput && <PureAntdInput />}
    </div>
  )
}
ReactDOM.render(<App />, document.getElementById('root'))

```

--------------------------------------------------------------------------------
/packages/element/src/form-layout/index.ts:
--------------------------------------------------------------------------------

```typescript
import { h } from '@formily/vue'
import {
  defineComponent,
  inject,
  InjectionKey,
  provide,
  Ref,
  ref,
  watch,
} from 'vue-demi'
import { stylePrefix } from '../__builtins__/configs'
import { useCompatRef } from '../__builtins__/shared'
import { useResponsiveFormLayout } from './useResponsiveFormLayout'

export type FormLayoutProps = {
  className?: string
  colon?: boolean
  labelAlign?: 'right' | 'left' | ('right' | 'left')[]
  wrapperAlign?: 'right' | 'left' | ('right' | 'left')[]
  labelWrap?: boolean
  labelWidth?: number
  wrapperWidth?: number
  wrapperWrap?: boolean
  labelCol?: number | number[]
  wrapperCol?: number | number[]
  fullness?: boolean
  size?: 'small' | 'default' | 'large'
  layout?:
    | 'vertical'
    | 'horizontal'
    | 'inline'
    | ('vertical' | 'horizontal' | 'inline')[]
  direction?: 'rtl' | 'ltr'
  shallow?: boolean
  feedbackLayout?: 'loose' | 'terse' | 'popover'
  tooltipLayout?: 'icon' | 'text'
  bordered?: boolean
  breakpoints?: number[]
  inset?: boolean
  spaceGap?: number
  gridColumnGap?: number
  gridRowGap?: number
}

export const FormLayoutDeepContext: InjectionKey<Ref<FormLayoutProps>> = Symbol(
  'FormLayoutDeepContext'
)

export const FormLayoutShallowContext: InjectionKey<Ref<FormLayoutProps>> =
  Symbol('FormLayoutShallowContext')

export const useFormDeepLayout = (): Ref<FormLayoutProps> =>
  inject(FormLayoutDeepContext, ref({}))

export const useFormShallowLayout = (): Ref<FormLayoutProps> =>
  inject(FormLayoutShallowContext, ref({}))

export const useFormLayout = (): Ref<FormLayoutProps> => {
  const shallowLayout = useFormShallowLayout()
  const deepLayout = useFormDeepLayout()
  const formLayout = ref({
    ...deepLayout.value,
    ...shallowLayout.value,
  })

  watch(
    [shallowLayout, deepLayout],
    () => {
      formLayout.value = {
        ...deepLayout.value,
        ...shallowLayout.value,
      }
    },
    {
      deep: true,
    }
  )
  return formLayout
}

export const FormLayout = defineComponent<FormLayoutProps>({
  name: 'FFormLayout',
  props: {
    className: {},
    colon: { default: true },
    labelAlign: {},
    wrapperAlign: {},
    labelWrap: { default: false },
    labelWidth: {},
    wrapperWidth: {},
    wrapperWrap: { default: false },
    labelCol: {},
    wrapperCol: {},
    fullness: { default: false },
    size: { default: 'default' },
    layout: { default: 'horizontal' },
    direction: { default: 'ltr' },
    shallow: { default: true },
    feedbackLayout: {},
    tooltipLayout: {},
    bordered: { default: true },
    inset: { default: false },
    breakpoints: {},
    spaceGap: {},
    gridColumnGap: {},
    gridRowGap: {},
  },
  setup(customProps, { slots, refs }) {
    const { elRef: root, elRefBinder } = useCompatRef(refs)
    const { props } = useResponsiveFormLayout(customProps, root)

    const deepLayout = useFormDeepLayout()
    const newDeepLayout = ref({
      ...deepLayout,
    })
    const shallowProps = ref({})

    watch(
      [props, deepLayout],
      () => {
        shallowProps.value = props.value.shallow ? props.value : undefined
        if (!props.value.shallow) {
          Object.assign(newDeepLayout.value, props.value)
        } else {
          if (props.value.size) {
            newDeepLayout.value.size = props.value.size
          }
          if (props.value.colon) {
            newDeepLayout.value.colon = props.value.colon
          }
        }
      },
      { deep: true, immediate: true }
    )

    provide(FormLayoutDeepContext, newDeepLayout)
    provide(FormLayoutShallowContext, shallowProps)

    const formPrefixCls = `${stylePrefix}-form`
    return () => {
      const classNames = {
        [`${formPrefixCls}-${props.value.layout}`]: true,
        [`${formPrefixCls}-rtl`]: props.value.direction === 'rtl',
        [`${formPrefixCls}-${props.value.size}`]:
          props.value.size !== undefined,
        [`${props.value.className}`]: props.value.className !== undefined,
      }
      return h(
        'div',
        {
          ref: elRefBinder,
          class: classNames,
        },
        slots
      )
    }
  },
})

export default FormLayout

```

--------------------------------------------------------------------------------
/packages/element/src/array-tabs/index.ts:
--------------------------------------------------------------------------------

```typescript
import { ArrayField } from '@formily/core'
import { observer } from '@formily/reactive-vue'
import { h, RecursionField, useField, useFieldSchema } from '@formily/vue'
import { Badge, TabPane, Tabs } from 'element-ui'
import { defineComponent, ref } from 'vue-demi'
import { stylePrefix } from '../__builtins__/configs'

import type { Tabs as TabsProps } from 'element-ui'

export const ArrayTabs = observer(
  defineComponent<TabsProps>({
    name: 'ArrayTabs',
    props: [],
    setup(props, { attrs, listeners }) {
      const fieldRef = useField<ArrayField>()
      const schemaRef = useFieldSchema()

      const prefixCls = `${stylePrefix}-array-tabs`
      const activeKey = ref('tab-0')

      return () => {
        const field = fieldRef.value
        const schema = schemaRef.value
        const value = Array.isArray(field.value) ? field.value : []
        const dataSource = value?.length ? value : [{}]

        const onEdit = (targetKey: any, type: 'add' | 'remove') => {
          if (type == 'add') {
            const id = dataSource.length
            if (field?.value?.length) {
              field.push(null)
            } else {
              field.push(null, null)
            }
            activeKey.value = `tab-${id}`
          } else if (type == 'remove') {
            const index = targetKey.match(/-(\d+)/)?.[1]
            field.remove(Number(index))
            if (activeKey.value === targetKey) {
              activeKey.value = `tab-${index - 1}`
            }
          }
        }

        const badgedTab = (index: number) => {
          const tab = `${field.title || 'Untitled'} ${index + 1}`
          const path = field.address.concat(index)
          const errors = field.form.queryFeedbacks({
            type: 'error',
            address: `${path}.**`,
          })
          if (errors.length) {
            return h(
              'span',
              {},
              {
                default: () => [
                  h(
                    Badge,
                    {
                      class: [`${prefixCls}-errors-badge`],
                      props: {
                        value: errors.length,
                      },
                    },
                    {
                      default: () => [tab],
                    }
                  ),
                ],
              }
            )
          }
          return h(
            'span',
            {},
            {
              default: () => [tab],
            }
          )
        }

        const renderItems = () =>
          dataSource?.map((item, index) => {
            const items = Array.isArray(schema.items)
              ? schema.items[index]
              : schema.items
            const key = `tab-${index}`

            return h(
              TabPane,
              {
                key,
                attrs: {
                  closable: index !== 0,
                  name: key,
                },
              },
              {
                default: () =>
                  h(
                    RecursionField,
                    {
                      props: {
                        schema: items,
                        name: index,
                      },
                    },
                    {}
                  ),

                label: () => [badgedTab(index)],
              }
            )
          })
        return h(
          Tabs,
          {
            class: [prefixCls],
            attrs: {
              ...attrs,
              type: 'card',
              value: activeKey.value,
              addable: true,
            },
            on: {
              ...listeners,
              input: (key) => {
                activeKey.value = key
              },
              'tab-remove': (target) => {
                onEdit(target, 'remove')
                listeners?.['tab-remove']?.(target)
              },
              'tab-add': () => {
                onEdit(null, 'add')
                listeners?.['tab-add']?.()
              },
            },
          },
          {
            default: () => [renderItems()],
          }
        )
      }
    },
  })
)

export default ArrayTabs

```

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

```markdown
# Alibaba Fusion

## Introduction

@formily/next is a professional component library for form scenarios based on Fusion Design encapsulation. It has the following characteristics:

- Only Formily 2.x is supported
  - Most components are not backward compatible
  - Unfortunately, many components of 1.x have inherent flaws in the API design. This is also because the form scheme has been explored, so there will be version breaks.
- Richer component system
  - Layout components
    - FormLayout
    - FormItem
    - FormGrid
    - FormButtonGroup
    - Space
    - Submit
    - Reset
  - Input controls
    - Input
    - Password
    - Select
    - TreeSelect
    - DatePicker
    - TimePicker
    - NumberPicker
    - Transfer
    - Cascader
    - Radio
    - Checkbox
    - Upload
    - Switch
  - Scene components
    - ArrayCards
    - ArrayItems
    - ArrayTable
    - FormCollapse
    - FormStep
    - FormTab
    - FormDialog
    - FormDrawer
    - Editable
    - LogicDiagram
  - Reading state component
    - PreviewText
- Theme customization ability
  - Completely abandon the 1.x styled-components solution, follow the style system of the component library, it is more convenient to customize the theme
- Support secondary packaging
  - All components can be repackaged, and the 1.x component system cannot be repackaged, so providing this capability makes it more convenient for users to do business customization
- Support reading mode
  - Although 1.x also supports reading mode, 2.x provides a separate PreviewText component, users can make reading mode encapsulation based on it, which is more flexible
- Type is more friendly
  - Each component has an extremely complete type definition, and users can feel an unprecedented intelligent reminder experience during the actual development process
- More complete layout control capabilities
  - 1.x's layout capabilities have basically converged to FormMegaLayout. This time, we directly removed Mega. Mega is a standard component and is completely internalized into FormLayout and FormItem components. At the same time, MegaLayout's grid layout capabilities are placed in FormGrid components. In, it also provides smarter layout capabilities.
- More elegant and easy-to-use APIs, such as:
  - FormStep in the past has many problems. First, the type is not friendly. Second, the API is too hidden. To control the forward and backwards, you need to understand a bunch of private events. In the new version of FormStep, users only need to pay attention to the FormStep Reactive Model. You can create a Reactive Model through createFormStep and pass it to the FormStep component to quickly communicate. Similarly, FormTab/FormCollapse is the same communication mode.
  - Pop-up forms, drawer forms, presumably in the past, users had to write a lot of code on these two scenarios almost every time. This time, an extremely simple API is directly provided for users to use, which maximizes development efficiency.

## Note

Because Fusion is built on Sass, if you use Webpack configuration, please use the following two Sass tools

```
"sass": "^1.32.11",
"sass-loader": "^8.0.2"
```

## Installation

```bash
$ npm install --save @alifd/next moment
$ npm install --save @formily/next @formily/react

```

## Q/A

Q: I want to package a set of component libraries by myself, what should I do?

Answer: If it is an open source component library, you can directly participate in the project co-construction and provide PR. If it is a private component library in the enterprise, you can refer to the source code. The source code does not have too much complicated logic.

Question: Why do components such as ArrayCards/ArrayTable/FormStep only support Schema mode and not pure JSX mode?

Answer: This is the core advantage of Schema mode. With the help of protocols, we can do scene-based abstraction. On the contrary, pure JSX mode is limited by the unparseability of JSX. It is difficult for us to achieve UI-level scene-based abstraction. It's just an abstract hook.

Q: Why is there no ArrayTabs component?

Answer: Because Fusion's Tab component does not support the ability to add Tabs, the ArrayTabs component is temporarily not supported.

```

--------------------------------------------------------------------------------
/packages/antd/src/form-step/index.tsx:
--------------------------------------------------------------------------------

```typescript
import React, { Fragment } from 'react'
import { define, observable, action, markRaw, model } from '@formily/reactive'
import { Steps } from 'antd'
import cls from 'classnames'
import { StepsProps, StepProps } from 'antd/lib/steps'
import { Form, VoidField } from '@formily/core'
import {
  connect,
  useField,
  observer,
  useFieldSchema,
  RecursionField,
} from '@formily/react'
import { Schema, SchemaKey } from '@formily/json-schema'
import { usePrefixCls } from '../__builtins__'

export interface IFormStep {
  connect: (steps: SchemaStep[], field: VoidField) => void
  current: number
  allowNext: boolean
  allowBack: boolean
  setCurrent(key: number): void
  submit: Form['submit']
  next(): void
  back(): void
}

export interface IFormStepProps extends StepsProps {
  formStep?: IFormStep
}

type ComposedFormStep = React.FC<React.PropsWithChildren<IFormStepProps>> & {
  StepPane: React.FC<React.PropsWithChildren<StepProps>>
  createFormStep: (defaultCurrent?: number) => IFormStep
}

type SchemaStep = {
  name: SchemaKey
  props: any
  schema: Schema
}

type FormStepEnv = {
  form: Form
  field: VoidField
  steps: SchemaStep[]
}

const parseSteps = (schema: Schema) => {
  const steps: SchemaStep[] = []
  schema.mapProperties((schema, name) => {
    if (schema['x-component']?.indexOf('StepPane') > -1) {
      steps.push({
        name,
        props: schema['x-component-props'],
        schema,
      })
    }
  })
  return steps
}

const createFormStep = (defaultCurrent = 0): IFormStep => {
  const env: FormStepEnv = define(
    {
      form: null,
      field: null,
      steps: [],
    },
    {
      form: observable.ref,
      field: observable.ref,
      steps: observable.shallow,
    }
  )

  const setDisplay = action.bound((target: number) => {
    const currentStep = env.steps[target]
    env.steps.forEach(({ name }) => {
      env.form.query(`${env.field.address}.${name}`).take((field) => {
        if (name === currentStep.name) {
          field.setDisplay('visible')
        } else {
          field.setDisplay('hidden')
        }
      })
    })
  })

  const next = action.bound(() => {
    if (formStep.allowNext) {
      formStep.setCurrent(formStep.current + 1)
    }
  })

  const back = action.bound(() => {
    if (formStep.allowBack) {
      formStep.setCurrent(formStep.current - 1)
    }
  })

  const formStep: IFormStep = model({
    connect(steps, field) {
      env.steps = steps
      env.form = field?.form
      env.field = field
    },
    current: defaultCurrent,
    setCurrent(key: number) {
      setDisplay(key)
      formStep.current = key
    },
    get allowNext() {
      return formStep.current < env.steps.length - 1
    },
    get allowBack() {
      return formStep.current > 0
    },
    async next() {
      try {
        await env.form.validate()
        if (env.form.valid) {
          next()
        }
      } catch {}
    },
    async back() {
      back()
    },
    async submit(onSubmit) {
      return env.form?.submit?.(onSubmit)
    },
  })
  return markRaw(formStep)
}

export const FormStep = connect(
  observer(({ formStep, className, ...props }: IFormStepProps) => {
    const field = useField<VoidField>()
    const prefixCls = usePrefixCls('formily-step', props)
    const schema = useFieldSchema()
    const steps = parseSteps(schema)
    const current = props.current || formStep?.current || 0
    formStep?.connect?.(steps, field)
    return (
      <div className={cls(prefixCls, className)}>
        <Steps
          {...props}
          style={{ marginBottom: 10, ...props.style }}
          current={current}
        >
          {steps.map(({ props }, key) => {
            return <Steps.Step {...props} key={key} />
          })}
        </Steps>
        {steps.map(({ name, schema }, key) => {
          if (key !== current) return
          return <RecursionField key={key} name={name} schema={schema} />
        })}
      </div>
    )
  })
) as unknown as ComposedFormStep

const StepPane: React.FC<React.PropsWithChildren<StepProps>> = ({
  children,
}) => {
  return <Fragment>{children}</Fragment>
}

FormStep.StepPane = StepPane
FormStep.createFormStep = createFormStep

export default FormStep

```
Page 10/35FirstPrevNextLast