#
tokens: 33746/50000 1/1152 files (page 34/35)
lines: off (toggle) GitHub
raw markdown copy
This is page 34 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/core/src/__tests__/field.spec.ts:
--------------------------------------------------------------------------------

```typescript
import { autorun, batch, observable } from '@formily/reactive'
import { createForm, onFieldReact, isField } from '../'
import { DataField } from '../types'
import { attach, sleep } from './shared'

test('create field', () => {
  const form = attach(createForm())
  const field = attach(
    form.createField({
      name: 'normal',
    })
  )
  expect(field).not.toBeUndefined()
})

test('create field props', () => {
  const form = attach(createForm())
  const field1 = attach(
    form.createField({
      name: 'field1',
      title: 'Field 1',
      description: 'This is Field 1',
      required: true,
    })
  )
  expect(field1.title).toEqual('Field 1')
  expect(field1.description).toEqual('This is Field 1')
  expect(field1.required).toBeTruthy()
  expect(field1.validator).not.toBeUndefined()
  const field2 = attach(
    form.createField({
      name: 'field2',
      disabled: true,
      hidden: true,
    })
  )
  expect(field2.pattern).toEqual('disabled')
  expect(field2.disabled).toBeTruthy()
  expect(field2.display).toEqual('hidden')
  expect(field2.hidden).toBeTruthy()
  const field3 = attach(
    form.createField({
      name: 'field3',
      readOnly: true,
      visible: false,
    })
  )
  expect(field3.pattern).toEqual('readOnly')
  expect(field3.readOnly).toBeTruthy()
  expect(field3.display).toEqual('none')
  expect(field3.visible).toBeFalsy()
  const field4 = attach(
    form.createField({
      name: 'field4',
      value: 123,
    })
  )
  expect(field4.value).toEqual(123)
  expect(field4.initialValue).toBeUndefined()
  const field5 = attach(
    form.createField({
      name: 'field5',
      initialValue: 123,
    })
  )
  expect(field5.value).toEqual(123)
  expect(field5.initialValue).toEqual(123)
})

test('field display and value', () => {
  const form = attach(createForm())
  const objectField = attach(
    form.createObjectField({
      name: 'object',
    })
  )
  const arrayField = attach(
    form.createArrayField({
      name: 'array',
    })
  )
  const valueField = attach(
    form.createField({
      name: 'value',
    })
  )
  expect(objectField.value).toEqual({})
  expect(arrayField.value).toEqual([])
  expect(valueField.value).toBeUndefined()

  objectField.hidden = true
  arrayField.hidden = true
  valueField.hidden = true
  expect(objectField.value).toEqual({})
  expect(arrayField.value).toEqual([])
  expect(valueField.value).toBeUndefined()

  objectField.hidden = false
  arrayField.hidden = false
  valueField.hidden = false
  expect(objectField.value).toEqual({})
  expect(arrayField.value).toEqual([])
  expect(valueField.value).toBeUndefined()

  objectField.visible = false
  arrayField.visible = false
  valueField.visible = false
  expect(objectField.value).toBeUndefined()
  expect(arrayField.value).toBeUndefined()
  expect(valueField.value).toBeUndefined()

  objectField.visible = true
  arrayField.visible = true
  valueField.visible = true
  expect(objectField.value).toEqual({})
  expect(arrayField.value).toEqual([])
  expect(valueField.value).toBeUndefined()

  objectField.value = { value: '123' }
  arrayField.value = ['123']
  valueField.value = '123'
  expect(objectField.value).toEqual({ value: '123' })
  expect(arrayField.value).toEqual(['123'])
  expect(valueField.value).toEqual('123')

  objectField.hidden = true
  arrayField.hidden = true
  valueField.hidden = true
  expect(objectField.value).toEqual({ value: '123' })
  expect(arrayField.value).toEqual(['123'])
  expect(valueField.value).toEqual('123')

  objectField.hidden = false
  arrayField.hidden = false
  valueField.hidden = false
  expect(objectField.value).toEqual({ value: '123' })
  expect(arrayField.value).toEqual(['123'])
  expect(valueField.value).toEqual('123')

  objectField.visible = false
  arrayField.visible = false
  valueField.visible = false
  expect(objectField.value).toBeUndefined()
  expect(arrayField.value).toBeUndefined()
  expect(valueField.value).toBeUndefined()

  objectField.visible = true
  arrayField.visible = true
  valueField.visible = true
  expect(objectField.value).toEqual({ value: '123' })
  expect(arrayField.value).toEqual(['123'])
  expect(valueField.value).toEqual('123')
})

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

  object_.setDisplay('hidden')
  expect(void_.display).toEqual('hidden')
  expect(aaa.display).toEqual('hidden')
  expect(bbb.display).toEqual('hidden')
  object_.setDisplay('none')
  expect(void_.display).toEqual('none')
  expect(aaa.display).toEqual('none')
  expect(bbb.display).toEqual('none')
  object_.setDisplay()
  expect(void_.display).toEqual('visible')
  expect(aaa.display).toEqual('visible')
  expect(bbb.display).toEqual('visible')

  aaa.setValue('123')
  expect(aaa.value).toEqual('123')
  aaa.setDisplay('none')
  expect(aaa.value).toBeUndefined()
  aaa.setDisplay('visible')
  expect(aaa.value).toEqual('123')
  aaa.setValue('123')
  object_.setDisplay('none')
  expect(aaa.value).toBeUndefined()
  object_.setDisplay('visible')
  expect(aaa.value).toEqual('123')
})

test('setValue/setInitialValue', () => {
  const form = attach(createForm())
  const aaa = attach(
    form.createField({
      name: 'aaa',
    })
  )
  const bbb = attach(
    form.createField({
      name: 'bbb',
    })
  )
  aaa.setValue('123')
  expect(aaa.value).toEqual('123')
  expect(form.values.aaa).toEqual('123')
  bbb.setValue('123')
  expect(bbb.value).toEqual('123')
  expect(form.values.bbb).toEqual('123')
  const ccc = attach(
    form.createField({
      name: 'ccc',
    })
  )
  const ddd = attach(
    form.createField({
      name: 'ddd',
    })
  )
  ccc.setInitialValue('123')
  expect(ccc.value).toEqual('123')
  expect(ccc.initialValue).toEqual('123')
  expect(form.values.ccc).toEqual('123')
  ddd.setInitialValue('123')
  expect(ddd.value).toEqual('123')
  expect(ddd.initialValue).toEqual('123')
  expect(form.values.ddd).toEqual('123')
  ccc.setInitialValue('222')
  expect(ccc.value).toEqual('222')
  expect(ccc.initialValue).toEqual('222')
  expect(form.values.ccc).toEqual('222')
  ddd.setInitialValue('222')
  expect(ddd.value).toEqual('222')
  expect(ddd.initialValue).toEqual('222')
  expect(form.values.ddd).toEqual('222')
})

test('setLoading/setValidating', async () => {
  const form = attach(createForm())
  const field = attach(
    form.createField({
      name: 'aa',
    })
  )
  field.setLoading(true)
  expect(field.loading).toBeFalsy()
  await sleep()
  expect(field.loading).toBeTruthy()
  field.setLoading(false)
  field.setLoading(false)
  expect(field.loading).toBeFalsy()
  field.setValidating(true)
  expect(field.validating).toBeFalsy()
  await sleep()
  expect(field.validating).toBeTruthy()
  field.setValidating(false)
  expect(field.validating).toBeFalsy()
})

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

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

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

test('reaction initialValue', () => {
  const form = attach(
    createForm({
      values: {
        aa: 123,
      },
    })
  )
  const aa = attach(
    form.createField({
      name: 'aa',
      reactions(field) {
        field.initialValue = 321
      },
    })
  )
  const bb = attach(
    form.createField({
      name: 'bb',
      value: 123,
      reactions(field) {
        field.initialValue = 321
      },
    })
  )
  expect(aa.value).toEqual(123)
  expect(bb.value).toEqual(123)
})

test('selfValidate/errors/warnings/successes/valid/invalid/validateStatus/queryFeedbacks', async () => {
  const form = attach(createForm())
  const field = attach(
    form.createField({
      name: 'aa',
      required: true,
      validateFirst: true,
      validator: [
        (value) => {
          if (value == '123') {
            return {
              type: 'success',
              message: 'success',
            }
          } else if (value == '321') {
            return {
              type: 'warning',
              message: 'warning',
            }
          } else if (value == '111') {
            return 'error'
          }
        },
        {
          triggerType: 'onBlur',
          format: 'url',
        },
        {
          triggerType: 'onFocus',
          format: 'date',
        },
      ],
    })
  )
  const field2 = attach(
    form.createField({
      name: 'bb',
      required: true,
      value: '111',
      validator: [
        (value) => {
          if (value == '123') {
            return {
              type: 'success',
              message: 'success',
            }
          } else if (value == '321') {
            return {
              type: 'warning',
              message: 'warning',
            }
          } else if (value == '111') {
            return 'error'
          }
        },
        {
          triggerType: 'onBlur',
          format: 'url',
        },
        {
          triggerType: 'onFocus',
          format: 'date',
        },
      ],
    })
  )
  const field3 = attach(
    form.createField({
      name: 'xxx',
    })
  )
  const field4 = attach(
    form.createField({
      name: 'ppp',
      required: true,
    })
  )
  try {
    await field.validate()
  } catch {}
  try {
    await field2.validate()
  } catch {}
  expect(field.invalid).toBeTruthy()
  expect(field.selfErrors.length).toEqual(1)
  expect(field2.invalid).toBeTruthy()
  expect(field2.selfErrors.length).toEqual(3)
  await field.onInput('123')
  expect(field.selfSuccesses).toEqual(['success'])
  await field.onInput('321')
  expect(field.selfWarnings).toEqual(['warning'])
  await field.onInput('111')
  expect(field.selfErrors).toEqual(['error'])
  await field.onBlur()
  expect(field.selfErrors).toEqual([
    'error',
    'The field value is a invalid url',
  ])
  await field.onFocus()
  expect(field.selfErrors).toEqual([
    'error',
    'The field value is a invalid url',
    'The field value is not a valid date format',
  ])
  field.setFeedback()
  expect(field.selfErrors).toEqual([
    'error',
    'The field value is a invalid url',
    'The field value is not a valid date format',
  ])
  expect(field3.feedbacks).toEqual([])
  field3.setFeedback()
  field3.setFeedback({ messages: null })
  field3.setFeedback({ messages: ['error'], code: 'EffectError' })
  field3.setFeedback({ messages: ['error2'], code: 'EffectError' })
  expect(field3.feedbacks).toEqual([
    { code: 'EffectError', messages: ['error2'] },
  ])
  expect(
    field3.queryFeedbacks({
      address: 'xxx',
    })
  ).toEqual([{ code: 'EffectError', messages: ['error2'] }])
  expect(
    field3.queryFeedbacks({
      address: 'yyy',
    })
  ).toEqual([])
  expect(
    field3.queryFeedbacks({
      path: 'yyy',
    })
  ).toEqual([])
  field3.setFeedback({ messages: null, code: 'EffectError' })
  field3.setFeedback({ messages: [], code: 'EffectError' })
  field4.setDisplay('none')
  await field4.validate()
  expect(field4.selfErrors).toEqual([])
})

test('setValidateRule', () => {
  const form = attach(createForm())
  const field1 = attach(
    form.createField({
      name: 'aa',
      validator: [{ required: true }],
    })
  )
  const field2 = attach(
    form.createField({
      name: 'bb',
      validator: 'phone',
    })
  )
  const field3 = attach(
    form.createField({
      name: 'cc',
      validator: 'phone',
    })
  )
  const field4 = attach(
    form.createField({
      name: 'dd',
      validator: { format: 'phone' },
    })
  )
  const field5 = attach(
    form.createField({
      name: 'ee',
      validator: [{ format: 'phone' }],
    })
  )
  const field6 = attach(
    form.createField({
      name: 'ff',
    })
  )
  field1.setValidatorRule('format', 'phone')
  field2.setValidatorRule('max', 3)
  field3.setValidatorRule('format', 'url')
  field4.setValidatorRule('min', 3)
  field5.setValidatorRule('min', 3)
  field6.setValidatorRule('min', 3)
  expect(field1.validator).toEqual([{ required: true }, { format: 'phone' }])
  expect(field2.validator).toEqual([{ format: 'phone' }, { max: 3 }])
  expect(field3.validator).toEqual([{ format: 'url' }])
  expect(field4.validator).toEqual([{ format: 'phone' }, { min: 3 }])
  expect(field5.validator).toEqual([{ format: 'phone' }, { min: 3 }])
  expect(field6.validator).toEqual([{ min: 3 }])
})

test('query', () => {
  const form = attach(createForm())
  const object_ = attach(
    form.createObjectField({
      name: 'object',
    })
  )
  const void_ = attach(
    form.createVoidField({
      name: 'void',
      basePath: 'object',
    })
  )
  const aaa = attach(
    form.createField({
      name: 'aaa',
      basePath: 'object.void',
    })
  )
  const bbb = attach(
    form.createField({
      name: 'bbb',
      basePath: 'object',
    })
  )
  expect(object_.query('object.void').take()).not.toBeUndefined()
  expect(object_.query('object.void.aaa').take()).not.toBeUndefined()
  expect(void_.query('.')).not.toBeUndefined()
  expect(void_.query('.bbb').take()).not.toBeUndefined()
  expect(aaa.query('.ccc').take()).toBeUndefined()
  expect(aaa.query('..').take()).not.toBeUndefined()
  expect(aaa.query('..bbb').take()).not.toBeUndefined()
  expect(bbb.query('.void').take()).not.toBeUndefined()
  expect(bbb.query('.void.aaa').take()).not.toBeUndefined()
  expect(bbb.query('.void.ccc').take()).toBeUndefined()
})

test('empty initialValue', () => {
  const form = attach(createForm())
  const aa = attach(
    form.createField({
      name: 'aa',
      initialValue: '',
    })
  )
  const bb = attach(
    form.createField({
      name: 'bb',
    })
  )
  expect(aa.value).toEqual('')
  expect(form.values.aa).toEqual('')
  expect(bb.value).toEqual(undefined)
  expect(form.values.bb).toEqual(undefined)
})

test('objectFieldWithInitialValue', async () => {
  const form = attach(
    createForm({
      initialValues: {
        obj: {
          a: 'a',
        },
      },
    })
  )
  attach(
    form.createObjectField({
      name: 'obj',
    })
  )
  const fieldObjA = attach(
    form.createField({
      name: 'obj.a',
    })
  )

  expect(fieldObjA.initialValue).toEqual('a')
  fieldObjA.value = 'aa'
  expect(fieldObjA.value).toEqual('aa')
  expect(fieldObjA.initialValue).toEqual('a')
})

test('initialValueWithArray', () => {
  const form = attach(createForm())
  const field = attach(
    form.createArrayField({
      name: 'aaa',
      initialValue: [1, 2],
    })
  )
  expect(field.initialValue).toEqual([1, 2])
  expect(field.value).toEqual([1, 2])
  expect(form.initialValues.aaa).toEqual([1, 2])
  expect(form.values.aaa).toEqual([1, 2])
})

test('resetObjectFieldWithInitialValue', async () => {
  const form = attach(createForm())
  attach(
    form.createObjectField({
      name: 'obj',
    })
  )
  const fieldObjA = attach(
    form.createField({
      name: 'obj.a',
      initialValue: 'a',
    })
  )

  fieldObjA.value = 'aa'
  expect(fieldObjA.value).toEqual('aa')
  await form.reset()
  expect(fieldObjA.value).toEqual('a')

  fieldObjA.value = 'aa'
  expect(fieldObjA.value).toEqual('aa')
  await form.reset()
  expect(fieldObjA.initialValue).toEqual('a')
  expect(fieldObjA.value).toEqual('a')
})

test('reset', async () => {
  const form = attach(
    createForm<any>({
      values: {
        bb: 123,
      },
      initialValues: {
        aa: 123,
        cc: null,
      },
    })
  )
  const aa = attach(
    form.createField({
      name: 'aa',
      required: true,
    })
  )
  const bb = attach(
    form.createField({
      name: 'bb',
      required: true,
    })
  )
  const cc = attach(
    form.createField({
      name: 'cc',
      required: true,
    })
  )
  const dd = attach(
    form.createField({
      name: 'dd',
      required: true,
    })
  )
  expect(aa.value).toEqual(123)
  expect(bb.value).toEqual(123)
  expect(cc.value).toEqual(null)
  expect(form.values.aa).toEqual(123)
  expect(form.values.bb).toEqual(123)
  expect(form.values.cc).toEqual(null)
  aa.onInput('xxxxx')
  expect(form.values.aa).toEqual('xxxxx')
  dd.onInput(null)
  expect(form.values.dd).toEqual(null)
  aa.reset()
  expect(aa.value).toEqual(123)
  expect(form.values.aa).toEqual(123)
  bb.onInput('xxxxx')
  expect(form.values.bb).toEqual('xxxxx')
  bb.reset()
  expect(bb.value).toBeUndefined()
  expect(form.values.bb).toBeUndefined()

  cc.onInput('xxxxx')
  expect(form.values.cc).toEqual('xxxxx')
  cc.reset()
  expect(cc.value).toBeNull()
  expect(form.values.cc).toBeNull()
  dd.reset()
  expect(dd.value).toBeUndefined()
  expect(form.values.dd).toBeUndefined()

  aa.reset({
    forceClear: true,
  })
  expect(aa.value).toBeUndefined()
  expect(form.values.aa).toBeUndefined()
  cc.reset({
    forceClear: true,
  })
  expect(cc.value).toBeUndefined()
  expect(form.values.cc).toBeUndefined()

  expect(aa.valid).toBeTruthy()
  await aa.reset({
    forceClear: true,
    validate: true,
  })
  expect(aa.valid).toBeFalsy()

  expect(cc.valid).toBeTruthy()
  await cc.reset({
    forceClear: true,
    validate: true,
  })
  expect(cc.valid).toBeFalsy()
})

test('match', () => {
  const form = attach(
    createForm<any>({
      values: {
        bb: 123,
      },
      initialValues: {
        aa: 123,
      },
    })
  )
  const aa = attach(
    form.createField({
      name: 'aa',
      required: true,
    })
  )
  expect(aa.match('aa')).toBeTruthy()
  expect(aa.match('*')).toBeTruthy()
  expect(aa.match('a~')).toBeTruthy()
  expect(aa.match('*(aa,bb)')).toBeTruthy()
})

test('setState/getState', () => {
  const form = attach(createForm())
  const aa = attach(
    form.createField({
      name: 'aa',
      required: true,
    })
  )
  const state = aa.getState()
  aa.setState((state) => {
    state.value = '123'
    state.title = 'AAA'
  })
  expect(aa.value).toEqual('123')
  expect(aa.title).toEqual('AAA')
  state['setState'] = () => {}
  aa.setState(state)
  expect(aa.value).toBeUndefined()
  expect(aa.title).toBeUndefined()
  aa.setState((state) => {
    state.hidden = false
  })
  expect(aa.display).toEqual('visible')
  aa.setState((state) => {
    state.visible = true
  })
  expect(aa.display).toEqual('visible')
  aa.setState((state) => {
    state.readOnly = false
  })
  expect(aa.pattern).toEqual('editable')
  aa.setState((state) => {
    state.disabled = false
  })
  expect(aa.pattern).toEqual('editable')
  aa.setState((state) => {
    state.editable = true
  })
  expect(aa.pattern).toEqual('editable')
  aa.setState((state) => {
    state.editable = false
  })
  expect(aa.pattern).toEqual('readPretty')
  aa.setState((state) => {
    state.readPretty = true
  })
  expect(aa.pattern).toEqual('readPretty')
  aa.setState((state) => {
    state.readPretty = false
  })
  expect(aa.pattern).toEqual('editable')
  form.setFieldState('bb', (state) => {
    state.value = 'bbb'
  })
  form.setFieldState('bb', (state) => {
    state.visible = false
  })
  const bb = attach(
    form.createField({
      name: 'bb',
    })
  )
  expect(bb.value).toEqual(undefined)
  expect(bb.visible).toBeFalsy()
  form.setFieldState('*', (state) => {
    state.value = '123'
  })
  const cc = attach(
    form.createField({
      name: 'cc',
    })
  )
  expect(aa.value).toEqual('123')
  expect(bb.value).toBeUndefined()
  expect(cc.value).toEqual('123')
  form.setFieldState(form.query('cc'), (state) => {
    state.value = 'ccc'
  })
  expect(cc.value).toEqual('ccc')
  form.setFieldState(cc, (state) => {
    state.value = '123'
  })
  expect(cc.value).toEqual('123')
  expect(form.getFieldState(aa)).not.toBeUndefined()
  expect(form.getFieldState(form.query('aa'))).not.toBeUndefined()
})

test('setDataSource', () => {
  const form = attach(createForm())
  const aa = attach(
    form.createField({
      name: 'aa',
      required: true,
    })
  )
  aa.setDataSource([
    { label: 's1', value: 's1' },
    { label: 's2', value: 's2' },
  ])
  expect(aa.dataSource).toEqual([
    { label: 's1', value: 's1' },
    { label: 's2', value: 's2' },
  ])
})

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

test('required/setRequired', () => {
  const form = attach(createForm())
  const aa = attach(
    form.createField({
      name: 'aa',
    })
  )
  aa.setRequired(true)
  expect(aa.required).toBeTruthy()
  aa.setRequired(false)
  expect(aa.required).toBeFalsy()
  const bb = attach(
    form.createField({
      name: 'bb',
      validator: {
        max: 3,
        required: true,
      },
    })
  )
  expect(bb.required).toBeTruthy()
  bb.setRequired(false)
  expect(bb.required).toBeFalsy()
  const cc = attach(
    form.createField({
      name: 'cc',
      validator: [
        'date',
        {
          max: 3,
        },
        {
          required: true,
        },
      ],
    })
  )
  expect(cc.required).toBeTruthy()
  cc.setRequired(false)
  expect(cc.required).toBeFalsy()
  const dd = attach(
    form.createField({
      name: 'dd',
      validator: {
        max: 3,
      },
    })
  )
  expect(dd.required).toBeFalsy()
  dd.setRequired(true)
  expect(dd.required).toBeTruthy()
})

test('setData/setContent', () => {
  const form = attach(createForm())
  const aa = attach(
    form.createField({
      name: 'aa',
      required: true,
    })
  )
  aa.setData('This is data')
  aa.setContent('This is Content')
  expect(aa.data).toEqual('This is data')
  expect(aa.content).toEqual('This is Content')
})

test('setData/setContent in void field', () => {
  const form = attach(createForm())
  const voidFeild = attach(
    form.createVoidField({
      name: 'voidFeild',
    })
  )
  voidFeild.setData('This is data')
  voidFeild.setContent('This is Content')
  expect(voidFeild.data).toEqual('This is data')
  expect(voidFeild.content).toEqual('This is Content')
})

test('setErrors/setWarnings/setSuccesses/setValidator', async () => {
  const form = attach(createForm())
  const aa = attach(
    form.createField({
      name: 'aa',
    })
  )
  const bb = attach(
    form.createField({
      name: 'bb',
    })
  )
  const cc = attach(
    form.createField({
      name: 'cc',
    })
  )
  const dd = attach(
    form.createField({
      name: 'dd',
      validator() {
        return new Promise(() => {})
      },
    })
  )
  aa.setSelfErrors(['error'])
  aa.setSelfWarnings(['warning'])
  aa.setSelfSuccesses(['success'])
  bb.setSelfSuccesses(['success'])
  cc.setSelfWarnings(['warning'])
  expect(aa.selfErrors).toEqual(['error'])
  expect(aa.valid).toBeFalsy()
  expect(aa.selfWarnings).toEqual(['warning'])
  expect(aa.selfSuccesses).toEqual(['success'])
  expect(bb.validateStatus).toEqual('success')
  expect(cc.validateStatus).toEqual('warning')
  aa.setValidator('date')
  await aa.onInput('123')
  expect(aa.selfErrors.length).toEqual(2)
  dd.onInput('123')
  await sleep()
  expect(dd.validateStatus).toEqual('validating')
})

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

test('fault tolerance', () => {
  const form = attach(createForm())
  const field = attach(
    form.createField({
      name: 'aa',
      value: 123,
    })
  )
  field.setDisplay('none')
  expect(field.value).toBeUndefined()
  field.setDisplay('visible')
  expect(field.value).toEqual(123)
  field.setDisplay('none')
  expect(field.value).toBeUndefined()
  field.setValue(321)
  expect(field.value).toBeUndefined()
  field.setDisplay('visible')
  expect(field.value).toEqual(321)
  form.setDisplay(null)
  form.setPattern(null)
  const field2 = attach(
    form.createField({
      name: 'xxx',
    })
  )
  expect(field2.display).toEqual('visible')
  expect(field2.pattern).toEqual('editable')
})

test('initialValue', () => {
  const form = attach(createForm())
  const field = attach(
    form.createField({
      name: 'aaa',
      initialValue: 123,
    })
  )
  expect(form.values.aaa).toEqual(123)
  expect(form.initialValues.aaa).toEqual(123)
  expect(field.value).toEqual(123)
  expect(field.initialValue).toEqual(123)
})

test('array path calculation with none index', async () => {
  const form = attach(createForm())
  const array = attach(
    form.createArrayField({
      name: 'array',
    })
  )
  await array.push({})
  const input = attach(
    form.createField({
      name: '0.input',
      basePath: 'array',
    })
  )
  expect(input.path.toString()).toEqual('array.0.input')
})

test('array path calculation with none index and void nested', async () => {
  const form = attach(createForm())
  const array = attach(
    form.createArrayField({
      name: 'array',
    })
  )
  await array.push({})
  attach(
    form.createVoidField({
      name: '0.column',
      basePath: 'array',
    })
  )
  const input = attach(
    form.createField({
      name: 'input',
      basePath: 'array.0.column',
    })
  )
  expect(input.path.toString()).toEqual('array.0.input')
})

test('array path calculation with object index', async () => {
  const form = attach(createForm())
  const array = attach(
    form.createArrayField({
      name: 'array',
    })
  )
  await array.push({})
  attach(
    form.createObjectField({
      name: '0',
      basePath: 'array',
    })
  )
  const input = attach(
    form.createField({
      name: 'input',
      basePath: 'array.0',
    })
  )
  expect(input.path.toString()).toEqual('array.0.input')
})

test('array path calculation with void index', async () => {
  const form = attach(createForm())
  const array = attach(
    form.createArrayField({
      name: 'array',
    })
  )
  await array.push('')
  attach(
    form.createVoidField({
      name: '0',
      basePath: 'array',
    })
  )
  const input = attach(
    form.createField({
      name: 'input',
      basePath: 'array.0',
    })
  )
  expect(input.path.toString()).toEqual('array.0')
})

test('array path calculation with void index and void wrapper', async () => {
  const form = attach(createForm())
  attach(
    form.createVoidField({
      name: 'layout',
    })
  )
  const array_in_layout = attach(
    form.createArrayField({
      name: 'array_in_layout',
      basePath: 'layout',
    })
  )
  await array_in_layout.push('')
  attach(
    form.createVoidField({
      name: '0',
      basePath: 'layout.array_in_layout',
    })
  )
  const input = attach(
    form.createField({
      name: 'input',
      basePath: 'layout.array_in_layout.0',
    })
  )
  expect(input.path.toString()).toEqual('array_in_layout.0')
})

test('reaction in reaction', () => {
  const form = attach(createForm())
  const void_ = attach(
    form.createVoidField({
      name: 'void',
    })
  )
  attach(
    form.createField({
      name: 'field1',
      basePath: 'void',
      initialValue: 123,
    })
  )
  const field2 = attach(
    form.createField({
      name: 'field2',
      basePath: 'void',
      initialValue: 456,
      reactions: (field) => {
        const f1 = field.query('field1')
        if (f1.get('value') === 123) {
          field.display = 'visible'
        } else {
          field.display = 'none'
        }
      },
    })
  )
  void_.setDisplay('none')
  expect(field2.value).toEqual(undefined)
  expect(field2.display).toEqual('none')
})

test('nested fields hidden and selfValidate', async () => {
  const form = attach(createForm())
  const parent = attach(
    form.createVoidField({
      name: 'parent',
    })
  )
  attach(
    form.createField({
      name: 'aa',
      basePath: 'parent',
      required: true,
    })
  )
  attach(
    form.createField({
      name: 'bb',
      basePath: 'parent',
      required: true,
    })
  )
  try {
    await form.validate()
  } catch {}
  expect(form.invalid).toBeTruthy()
  parent.display = 'hidden'
  await form.validate()
  expect(form.invalid).toBeFalsy()
})

test('deep nested fields hidden and selfValidate', async () => {
  const form = attach(createForm())
  const parent1 = attach(
    form.createVoidField({
      name: 'parent1',
    })
  )
  const parent2 = attach(
    form.createVoidField({
      name: 'parent2',
      basePath: 'parent1',
    })
  )
  const aa = attach(
    form.createField({
      name: 'aa',
      basePath: 'parent1.parent2',
      required: true,
    })
  )
  const bb = attach(
    form.createField({
      name: 'bb',
      basePath: 'parent1.parent2',
      required: true,
    })
  )
  try {
    await form.validate()
  } catch {}
  expect(form.invalid).toBeTruthy()
  parent2.display = 'visible'
  parent1.display = 'hidden'
  expect(parent2.display).toEqual('hidden')
  expect(aa.display).toEqual('hidden')
  expect(bb.display).toEqual('hidden')
  await form.validate()
  expect(form.invalid).toBeFalsy()
})

test('deep nested fields hidden and selfValidate with middle hidden', async () => {
  const form = attach(createForm())
  const parent1 = attach(
    form.createVoidField({
      name: 'parent1',
    })
  )
  const parent2 = attach(
    form.createVoidField({
      name: 'parent2',
      basePath: 'parent1',
    })
  )
  const aa = attach(
    form.createField({
      name: 'aa',
      basePath: 'parent1.parent2',
      required: true,
    })
  )
  const bb = attach(
    form.createField({
      name: 'bb',
      basePath: 'parent1.parent2',
      required: true,
    })
  )
  try {
    await form.validate()
  } catch {}
  expect(form.invalid).toBeTruthy()
  parent2.display = 'hidden'
  parent1.display = 'none'
  expect(parent2.display).toEqual('hidden')
  expect(aa.display).toEqual('hidden')
  expect(bb.display).toEqual('hidden')
  await form.validate()
  expect(form.invalid).toBeFalsy()
})

test('fields unmount and selfValidate', async () => {
  const form = attach(createForm())
  const field = attach(
    form.createField({
      name: 'parent',
      required: true,
    })
  )
  try {
    await form.validate()
  } catch {}
  expect(form.invalid).toBeTruthy()
  field.onUnmount()
  try {
    await form.validate()
  } catch {}
  expect(form.invalid).toBeTruthy()
  form.clearFormGraph('parent')
  await form.validate()
  expect(form.invalid).toBeFalsy()
})

test('auto clean with ArrayField', () => {
  const form = attach(createForm())
  attach(
    form.createArrayField({
      name: 'array',
      initialValue: [{}, {}],
    })
  )
  attach(
    form.createField({
      name: '0.aa',
      basePath: 'array',
    })
  )
  attach(
    form.createField({
      name: '1.aa',
      basePath: 'array',
    })
  )
  const array1 = attach(
    form.createArrayField({
      name: 'array1',
      initialValue: [{}, {}],
    })
  )
  attach(
    form.createField({
      name: '0.aa',
      basePath: 'array1',
    })
  )
  attach(
    form.createField({
      name: '1.aa',
      basePath: 'array1',
    })
  )
  const array2 = attach(
    form.createArrayField({
      name: 'array2',
      initialValue: [{}, {}],
    })
  )
  attach(
    form.createField({
      name: '0.aa',
      basePath: 'array2',
    })
  )
  attach(
    form.createField({
      name: '1.aa',
      basePath: 'array2',
    })
  )
  expect(form.fields['array.1.aa']).not.toBeUndefined()
  expect(form.values.array).toEqual([{}, {}])
  form.setValues(
    {
      array: [{}],
    },
    'shallowMerge'
  )
  expect(form.values.array).toEqual([{}])
  expect(form.fields['array.1.aa']).toBeUndefined()
  expect(form.fields['array1.0.aa']).not.toBeUndefined()
  expect(form.fields['array1.1.aa']).not.toBeUndefined()
  expect(form.values.array1).toEqual([{}, {}])
  array1.setValue([])
  expect(form.fields['array1.0.aa']).toBeUndefined()
  expect(form.fields['array1.1.aa']).toBeUndefined()
  expect(form.fields['array2.0.aa']).not.toBeUndefined()
  expect(form.fields['array2.1.aa']).not.toBeUndefined()
  array2.setValue([])
  expect(form.fields['array2.0.aa']).toBeUndefined()
  expect(form.fields['array2.1.aa']).toBeUndefined()
})

test('auto clean with ObjectField', () => {
  const form = attach(createForm())
  attach(
    form.createObjectField({
      name: 'obj',
      initialValue: {
        aa: 'aa',
        bb: 'bb',
      },
    })
  )
  attach(
    form.createField({
      name: 'aa',
      basePath: 'obj',
    })
  )
  attach(
    form.createField({
      name: 'bb',
      basePath: 'obj',
    })
  )
  const obj1 = attach(
    form.createObjectField({
      name: 'obj1',
      initialValue: {
        aa: 'aa',
        bb: 'bb',
      },
    })
  )
  attach(
    form.createField({
      name: 'aa',
      basePath: 'obj1',
    })
  )
  attach(
    form.createField({
      name: 'bb',
      basePath: 'obj1',
    })
  )
  const obj2 = attach(
    form.createObjectField({
      name: 'obj2',
      initialValue: {
        aa: 'aa',
        bb: 'bb',
      },
    })
  )
  attach(
    form.createField({
      name: 'aa',
      basePath: 'obj2',
    })
  )
  attach(
    form.createField({
      name: 'bb',
      basePath: 'obj2',
    })
  )
  expect(form.fields['obj.aa']).not.toBeUndefined()
  expect(form.fields['obj.bb']).not.toBeUndefined()
  expect(form.values.obj).toEqual({ aa: 'aa', bb: 'bb' })
  form.setValues(
    {
      obj: {
        aa: '123',
      },
    },
    'shallowMerge'
  )
  expect(form.values.obj).toEqual({ aa: '123' })
  expect(form.fields['obj.aa']).not.toBeUndefined()
  expect(form.fields['obj.bb']).not.toBeUndefined()
  expect(form.fields['obj1.aa']).not.toBeUndefined()
  expect(form.fields['obj1.bb']).not.toBeUndefined()
  expect(form.values.obj1).toEqual({ aa: 'aa', bb: 'bb' })
  obj1.setValue({})
  expect(form.values.obj1).toEqual({})
  expect(form.fields['obj1.aa']).not.toBeUndefined()
  expect(form.fields['obj1.bb']).not.toBeUndefined()
  expect(form.fields['obj2.aa']).not.toBeUndefined()
  expect(form.fields['obj2.bb']).not.toBeUndefined()
  expect(form.values.obj2).toEqual({ aa: 'aa', bb: 'bb' })
  obj2.setValue({ aa: 'aa', bb: 'bb', cc: 'cc' })
  expect(form.fields['obj2.aa']).not.toBeUndefined()
  expect(form.fields['obj2.bb']).not.toBeUndefined()
  expect(form.fields['obj2.cc']).toBeUndefined()
  obj2.addProperty('cc', '123')
  attach(
    form.createField({
      name: 'cc',
      basePath: 'obj2',
    })
  )
  expect(form.fields['obj2.cc']).not.toBeUndefined()
  obj2.removeProperty('cc')
  expect(form.fields['obj2.cc']).toBeUndefined()
})

test('initial value with empty', () => {
  const form = attach(createForm())
  const array = attach(form.createField({ name: 'array', initialValue: '' }))
  expect(array.value).toEqual('')

  const beNull = attach(form.createField({ name: 'null', initialValue: null }))
  expect(beNull.value).toEqual(null)
})

test('field submit', async () => {
  const form = attach(
    createForm({
      initialValues: {
        aa: {
          cc: 'cc',
        },
        bb: 'bb',
      },
    })
  )
  const childForm = attach(
    form.createObjectField({
      name: 'aa',
    })
  )
  attach(
    form.createField({
      name: 'bb',
    })
  )
  attach(
    form.createField({
      name: 'cc',
      basePath: 'aa',
    })
  )
  const onSubmit = jest.fn()
  await childForm.submit(onSubmit)
  expect(onSubmit).toBeCalledWith({
    cc: 'cc',
  })
})

test('field submit with error', async () => {
  const form = attach(createForm())
  const childForm = attach(
    form.createObjectField({
      name: 'aa',
    })
  )
  attach(
    form.createField({
      name: 'bb',
      required: true,
    })
  )
  attach(
    form.createField({
      name: 'cc',
      basePath: 'aa',
      required: true,
    })
  )
  const onSubmit = jest.fn()
  try {
    await childForm.submit(onSubmit)
  } catch (e) {
    expect(e).not.toBeUndefined()
  }
  expect(onSubmit).toBeCalledTimes(0)
})

test('initial display with value', () => {
  const form = attach(createForm())
  const aa = attach(
    form.createField({
      name: 'aa',
      value: 123,
      visible: false,
    })
  )
  const bb = attach(
    form.createField({
      name: 'bb',
      value: 123,
      visible: true,
    })
  )
  const cc = attach(
    form.createField({
      name: 'cc',
      value: 123,
      hidden: true,
    })
  )
  expect(aa.value).toBeUndefined()
  expect(aa.visible).toBeFalsy()
  expect(bb.value).toEqual(123)
  expect(bb.visible).toBeTruthy()
  expect(cc.value).toEqual(123)
  expect(cc.hidden).toBeTruthy()
})

test('state depend field visible value', async () => {
  const form = attach(createForm())
  const aa = attach(
    form.createField({
      name: 'aa',
    })
  )
  const bb = attach(
    form.createField({
      name: 'bb',
      reactions(field) {
        field.visible = aa.value === '123'
      },
    })
  )
  const cc = attach(
    form.createField({
      name: 'cc',
      reactions(field) {
        field.visible = aa.value === '123'
        field.disabled = !bb.value
      },
    })
  )
  expect(bb.visible).toBeFalsy()
  expect(cc.visible).toBeFalsy()
  expect(cc.disabled).toBeTruthy()
  aa.value = '123'
  await sleep(10)
  expect(bb.visible).toBeTruthy()
  expect(cc.visible).toBeTruthy()
  expect(cc.disabled).toBeTruthy()
  bb.value = '321'
  await sleep(10)
  expect(bb.visible).toBeTruthy()
  expect(cc.visible).toBeTruthy()
  expect(cc.disabled).toBeFalsy()
  aa.value = ''
  await sleep(10)
  expect(bb.visible).toBeFalsy()
  expect(cc.visible).toBeFalsy()
  expect(cc.disabled).toBeTruthy()
  aa.value = '123'
  await sleep(10)
  expect(bb.visible).toBeTruthy()
  expect(cc.visible).toBeTruthy()
  expect(cc.disabled).toBeFalsy()
})

test('reactions initialValue and value', () => {
  const form = attach(
    createForm({
      values: {
        aa: {
          input: '111',
        },
      },
    })
  )
  attach(
    form.createObjectField({
      name: 'aa',
      reactions: [
        (field) => {
          field.initialValue = {}
          field.initialValue.input = 123
        },
      ],
    })
  )
  attach(
    form.createField({
      name: 'input',
      basePath: 'aa',
    })
  )
  expect(form.values.aa.input).toEqual('111')
})

test('field name is length in initialize', () => {
  const form = attach(createForm())
  const field = attach(
    form.createField({
      name: 'length',
      initialValue: 123,
    })
  )
  expect(field.value).toEqual(123)
})

test('field name is length in dynamic assign', () => {
  const form = attach(createForm())
  const field = attach(
    form.createField({
      name: 'length',
    })
  )
  field.initialValue = 123
  expect(field.value).toEqual(123)
})

test('nested field modified', async () => {
  const form = attach(createForm())
  const obj = attach(
    form.createObjectField({
      name: 'object',
    })
  )
  const child = attach(
    form.createField({
      name: 'child',
      basePath: 'object',
    })
  )
  await child.onInput()
  expect(child.modified).toBeTruthy()
  expect(child.selfModified).toBeTruthy()
  expect(obj.modified).toBeTruthy()
  expect(obj.selfModified).toBeFalsy()
  expect(form.modified).toBeTruthy()
  await obj.reset()
  expect(child.modified).toBeFalsy()
  expect(child.selfModified).toBeFalsy()
  expect(obj.modified).toBeFalsy()
  expect(obj.selfModified).toBeFalsy()
  expect(form.modified).toBeTruthy()
  await form.reset()
  expect(form.modified).toBeFalsy()
})

test('field setValidator repeat call', async () => {
  const form = attach(createForm())
  const field = attach(
    form.createField({
      name: 'normal',
    })
  )

  const validator1 = jest.fn(() => '')
  const validator2 = jest.fn(() => '')
  const validator3 = jest.fn(() => '')

  field.setValidator([validator1, validator2, validator3])

  await form.validate()
  expect(validator1).toBeCalledTimes(1)
})

test('custom validator to get ctx.field', async () => {
  const form = attach(createForm())
  let ctxField = null
  let ctxForm = null
  attach(
    form.createField({
      name: 'aaa',
      validator(value, rule, ctx) {
        ctxField = ctx.field
        ctxForm = ctx.form
        return ''
      },
    })
  )
  await form.submit()
  expect(!!ctxField).toBeTruthy()
  expect(!!ctxForm).toBeTruthy()
})

test('single direction linkage effect', async () => {
  const form = attach(createForm())

  const input1 = form.createField({
    name: 'input1',
    reactions: (field: DataField) => {
      if (!field.selfModified) {
        return
      }
      input2.value = field.value
    },
  })

  const input2 = form.createField({
    name: 'input2',
  })

  await input1.onInput('123')
  expect(input2.value).toBe('123')
  await input2.onInput('321')
  expect(input2.value).toBe('321')
})

test('path change will update computed value', () => {
  const form = attach(createForm())

  const input = form.createField({
    name: 'input',
  })

  const value = jest.fn()

  autorun(() => {
    value(input.value)
  })
  batch(() => {
    input.locate('select')
    input.value = '123'
  })
  expect(value).nthCalledWith(2, '123')
})

test('object field reset', async () => {
  const form = attach(createForm())

  attach(
    form.createObjectField({
      name: 'obj',
    })
  )

  const input = attach(
    form.createField({
      name: 'input',
      basePath: 'obj',
    })
  )

  await form.reset()
  form.setValues({
    obj: {
      input: '123',
    },
  })
  expect(input.value).toBe('123')
})

test('field visible default value should work', () => {
  const form = attach(
    createForm({
      effects(form) {
        onFieldReact('obj.input1', (field) => {
          field.pattern = 'disabled'
        })
        onFieldReact('obj', (field) => {
          field.visible = form.values.select !== 'none'
        })
        onFieldReact('obj.input1', (field) => {
          if (isField(field)) {
            field.initialValue = '123'
          }
        })
        onFieldReact('obj.input2', (field) => {
          if (isField(field)) {
            field.value = form.values.select
          }
        })
      },
    })
  )

  const select = attach(
    form.createField({
      name: 'select',
    })
  )

  attach(
    form.createObjectField({
      name: 'obj',
    })
  )

  attach(
    form.createField({
      name: 'input1',
      basePath: 'obj',
    })
  )

  attach(
    form.createField({
      name: 'input2',
      basePath: 'obj',
    })
  )

  select.value = 'none'
  expect(form.values.obj?.input1).toBeUndefined()
  select.value = 'visible'
  expect(form.values.obj.input1).toBe('123')
})

test('query value with sibling path syntax', () => {
  const form = attach(createForm())
  const fn = jest.fn()
  attach(
    form.createVoidField({
      name: 'void',
    })
  )
  attach(
    form.createObjectField({
      name: 'obj',
      basePath: 'void',
    })
  )
  attach(
    form.createField({
      name: 'input',
      basePath: 'void.obj',
      reactions: [
        (field) => {
          fn(
            field.query('.textarea').value(),
            field.query('.textarea').initialValue()
          )
        },
      ],
    })
  )
  const textarea = attach(
    form.createField({
      name: 'textarea',
      basePath: 'void.obj',
      initialValue: 'aaa',
    })
  )
  textarea.value = '123'
  expect(fn).toBeCalledWith('123', 'aaa')
})

test('relative query with void field', () => {
  const form = attach(createForm())
  attach(
    form.createVoidField({
      name: 'void',
    })
  )
  const aa = attach(
    form.createField({
      name: 'aa',
      basePath: 'void',
    })
  )
  attach(
    form.createVoidField({
      name: 'mm',
    })
  )
  const bb = attach(
    form.createField({
      name: 'bb',
      basePath: 'mm',
    })
  )

  expect(bb.query('.aa').take()).toBe(aa)
})

test('empty string or number or null value need rewrite default value', () => {
  const form = attach(
    createForm<any>({
      values: {
        aa: '',
        bb: 0,
        ee: null,
      },
    })
  )
  attach(
    form.createField({
      name: 'aa',
      initialValue: 'test',
    })
  )
  attach(
    form.createField({
      name: 'bb',
      initialValue: 123,
    })
  )
  attach(
    form.createField({
      name: 'cc',
      initialValue: 'test',
    })
  )
  attach(
    form.createField({
      name: 'dd',
      initialValue: 123,
    })
  )
  attach(
    form.createField({
      name: 'ee',
      initialValue: 'test',
    })
  )
  expect(form.values.aa).toEqual('')
  expect(form.values.bb).toEqual(0)
  expect(form.values.cc).toEqual('test')
  expect(form.values.dd).toEqual(123)
  expect(form.values.ee).toEqual(null)
})

test('destroy field need auto remove initialValues', () => {
  const form = attach(createForm<any>())
  const aa = attach(
    form.createField({
      name: 'aa',
      initialValue: 'test',
    })
  )
  expect(form.initialValues.aa).toEqual('test')
  expect(form.values.aa).toEqual('test')
  aa.destroy()
  expect(form.initialValues.aa).toBeUndefined()
  expect(form.values.aa).toBeUndefined()
})

test('validateFirst', async () => {
  const form = attach(
    createForm<any>({
      validateFirst: false,
    })
  )
  const aaValidate = jest.fn(() => 'aaError')
  const aa = attach(
    form.createField({
      name: 'aa',
      validateFirst: true,
      validator: [aaValidate, aaValidate],
    })
  )
  await aa.onInput('aa')
  const bbValidate = jest.fn(() => 'bbError')
  const bb = attach(
    form.createField({
      name: 'bb',
      validator: [bbValidate, bbValidate],
      validateFirst: false,
    })
  )
  await bb.onInput('bb')
  const ccValidate = jest.fn(() => 'ccError')
  const cc = attach(
    form.createField({
      name: 'cc',
      validator: [ccValidate, ccValidate],
    })
  )
  await cc.onInput('cc')

  expect(aaValidate).toBeCalledTimes(1)
  expect(bbValidate).toBeCalledTimes(2)
  expect(ccValidate).toBeCalledTimes(2)
})

test('reactions should not be triggered when field destroyed', () => {
  const form = attach(createForm<any>())
  const handler = jest.fn()
  const obs = observable({ bb: 123 })
  const aa = attach(
    form.createField({
      name: 'aa',
      initialValue: 'test',
      reactions() {
        handler(obs.bb)
      },
    })
  )
  obs.bb = 321
  aa.destroy()
  obs.bb = 111
  expect(handler).toBeCalledTimes(2)
})

test('parent readPretty will overwrite self disabled or readOnly', () => {
  const form = attach(
    createForm<any>({
      readPretty: true,
    })
  )
  const aa = attach(
    form.createField({
      name: 'aa',
      initialValue: 'test',
      disabled: true,
    })
  )
  const bb = attach(
    form.createField({
      name: 'bb',
      initialValue: 'test',
      editable: true,
    })
  )
  expect(aa.pattern).toBe('readPretty')
  expect(bb.pattern).toBe('editable')
})

test('conflict name for errors filter', async () => {
  const form = attach(createForm<any>())
  const aa = attach(
    form.createField({
      name: 'aa',
      required: true,
    })
  )
  const aa1 = attach(
    form.createField({
      name: 'aa1',
      required: true,
    })
  )

  await aa1.onInput('')
  expect(aa.invalid).toBe(false)
})

test('field destroyed can not be assign value', () => {
  const form = attach(createForm<any>())
  const aa = attach(
    form.createField({
      name: 'aa',
    })
  )
  aa.destroy()
  aa.initialValue = 222
  aa.value = 111
  expect(form.values).toEqual({})
  expect(form.initialValues).toEqual({})
})

test('onInput could pass value with target', async () => {
  const form = attach(createForm<any>())
  const aa = attach(
    form.createField({
      name: 'aa',
    })
  )
  await aa.onInput({
    target: '123',
  })
  expect(aa.value).toEqual({ target: '123' })
})

test('field destroyed or display none should not be assign value from patch initialValues', () => {
  const form = attach(createForm())
  const aa = attach(
    form.createField({
      name: 'aa',
      display: 'none',
    })
  )

  aa.initialValue = '123'

  expect(form.values).toEqual({})

  aa.display = 'visible'

  expect(aa.value).toBe('123')
  expect(form.values).toEqual({ aa: '123' })
})

test('onFieldReact with field destroyed', () => {
  const fn = jest.fn()
  const obs = observable<any>({ value: 123 })
  const form = attach(
    createForm({
      effects() {
        onFieldReact('aa', () => {
          fn(obs.value)
        })
      },
    })
  )
  const aa = attach(
    form.createField({
      name: 'aa',
    })
  )
  obs.value = '321'
  expect(fn).toBeCalledTimes(2)
  aa.destroy()
  obs.value = '111'
  expect(fn).toBeCalledTimes(2)
})

test('field actions', () => {
  const form = attach(createForm())
  const aa = attach(
    form.createField({
      name: 'aa',
    })
  )
  expect(aa.actions).toEqual({})
  aa.inject({
    test: () => 123,
  })
  expect(aa.invoke('test')).toEqual(123)
  aa.inject({
    test: () => 321,
  })
  expect(aa.invoke('test')).toEqual(321)
})

test('field hidden value', () => {
  const form = attach(createForm())
  const aa = attach(
    form.createField({
      name: 'aa',
      hidden: true,
      initialValue: '123',
    })
  )
  expect(form.values).toEqual({ aa: '123' })

  const objectField = attach(
    form.createObjectField({
      name: 'object',
      hidden: true,
    })
  )
  const arrayField = attach(
    form.createArrayField({
      name: 'array',
      hidden: true,
    })
  )

  aa.setDisplay('none')
  objectField.setDisplay('none')
  arrayField.setDisplay('none')
  expect(aa.value).toBeUndefined()
  expect(objectField.value).toBeUndefined()
  expect(arrayField.value).toBeUndefined()

  aa.setDisplay('hidden')
  objectField.setDisplay('hidden')
  arrayField.setDisplay('hidden')
  expect(aa.value).toEqual('123')
  expect(objectField.value).toEqual({})
  expect(arrayField.value).toEqual([])
})

test('field destructor path with display none', () => {
  const form = attach(createForm())
  const aa = attach(
    form.createArrayField({
      name: '[aa,bb]',
    })
  )
  aa.setDisplay('none')
  expect(form.values).toEqual({})
  expect(aa.value).toEqual([])
})

test('onInput should ignore HTMLInputEvent propagation', async () => {
  const form = attach(createForm<any>())
  const mockHTMLInput = { value: '321' }
  const mockDomEvent = { target: mockHTMLInput, currentTarget: mockHTMLInput }
  const aa = attach(
    form.createField({
      name: 'aa',
    })
  )
  await aa.onInput(mockDomEvent)
  expect(aa.value).toEqual('321')

  await aa.onInput({ target: { value: '2' }, currentTarget: { value: '4' } })
  expect(aa.value).toEqual('321')

  // currentTarget is undefined, skip ignore
  await aa.onInput({ target: { value: '123' } })
  expect(aa.value).toEqual('123')
})

test('onFocus and onBlur with invalid target value', async () => {
  const form = attach(createForm<any>())
  const field = attach(
    form.createField({
      name: 'aa',
      validateFirst: true,
      value: '111',
      validator: [
        {
          triggerType: 'onFocus',
          format: 'date',
        },
        {
          triggerType: 'onBlur',
          format: 'url',
        },
      ],
    })
  )

  await field.onFocus({ target: {} })
  expect(field.selfErrors).toEqual([])
  await field.onBlur({ target: {} })
  expect(field.selfErrors).toEqual([])

  await field.onFocus()
  expect(field.selfErrors).toEqual([
    'The field value is not a valid date format',
  ])
  await field.onBlur()
  expect(field.selfErrors).toEqual([
    'The field value is not a valid date format',
    'The field value is a invalid url',
  ])
})

test('validatePattern and validateDisplay', async () => {
  const form = attach(
    createForm<any>({
      validatePattern: ['editable'],
      validateDisplay: ['visible'],
    })
  )
  const field1 = attach(
    form.createField({
      name: 'a',
      required: true,
    })
  )
  const field2 = attach(
    form.createField({
      name: 'b',
      required: true,
      validatePattern: ['readOnly'],
      validateDisplay: ['hidden'],
    })
  )
  const field3 = attach(
    form.createField({
      name: 'c',
      required: true,
      validatePattern: ['readOnly', 'editable'],
      validateDisplay: ['hidden', 'visible'],
    })
  )

  try {
    await form.validate()
  } catch {}
  expect(field1.selfErrors.length).toBe(1)
  expect(field2.selfErrors.length).toBe(0)
  expect(field3.selfErrors.length).toBe(1)

  form.setPattern('readOnly')
  form.setDisplay('hidden')
  try {
    await form.validate()
  } catch {}
  expect(field1.selfErrors.length).toBe(0)
  expect(field2.selfErrors.length).toBe(1)
  expect(field3.selfErrors.length).toBe(1)
})

```
Page 34/35FirstPrevNextLast