#
tokens: 47379/50000 8/1152 files (page 26/52)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 26 of 52. Use http://codebase.md/alibaba/formily?lines=true&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/next/docs/components/FormDrawer.md:
--------------------------------------------------------------------------------

```markdown
  1 | # FormDrawer
  2 | 
  3 | > Drawer form, mainly used in simple event to open form scene
  4 | 
  5 | ## Markup Schema example
  6 | 
  7 | ```tsx
  8 | import React from 'react'
  9 | import {
 10 |   FormDrawer,
 11 |   FormItem,
 12 |   Input,
 13 |   Submit,
 14 |   Reset,
 15 |   FormButtonGroup,
 16 |   FormLayout,
 17 | } from '@formily/next'
 18 | import { createSchemaField } from '@formily/react'
 19 | import { Button } from '@alifd/next'
 20 | 
 21 | const SchemaField = createSchemaField({
 22 |   components: {
 23 |     FormItem,
 24 |     Input,
 25 |   },
 26 | })
 27 | 
 28 | export default () => {
 29 |   return (
 30 |     <Button
 31 |       onClick={() => {
 32 |         FormDrawer('Drawer Form', () => {
 33 |           return (
 34 |             <FormLayout labelCol={6} wrapperCol={14}>
 35 |               <SchemaField>
 36 |                 <SchemaField.String
 37 |                   name="aaa"
 38 |                   required
 39 |                   title="input box 1"
 40 |                   x-decorator="FormItem"
 41 |                   x-component="Input"
 42 |                 />
 43 |                 <SchemaField.String
 44 |                   name="bbb"
 45 |                   required
 46 |                   title="input box 2"
 47 |                   x-decorator="FormItem"
 48 |                   x-component="Input"
 49 |                 />
 50 |                 <SchemaField.String
 51 |                   name="ccc"
 52 |                   required
 53 |                   title="input box 3"
 54 |                   x-decorator="FormItem"
 55 |                   x-component="Input"
 56 |                 />
 57 |                 <SchemaField.String
 58 |                   name="ddd"
 59 |                   required
 60 |                   title="input box 4"
 61 |                   x-decorator="FormItem"
 62 |                   x-component="Input"
 63 |                 />
 64 |               </SchemaField>
 65 |               <FormDrawer.Footer>
 66 |                 <FormButtonGroup align="right">
 67 |                   <Submit
 68 |                     onSubmit={() => {
 69 |                       return new Promise((resolve) => {
 70 |                         setTimeout(resolve, 1000)
 71 |                       })
 72 |                     }}
 73 |                   >
 74 |                     Submit
 75 |                   </Submit>
 76 |                   <Reset>Reset</Reset>
 77 |                 </FormButtonGroup>
 78 |               </FormDrawer.Footer>
 79 |             </FormLayout>
 80 |           )
 81 |         })
 82 |           .open({
 83 |             initialValues: {
 84 |               aaa: '123',
 85 |             },
 86 |           })
 87 |           .then(console.log)
 88 |       }}
 89 |     >
 90 |       Click me to open the form
 91 |     </Button>
 92 |   )
 93 | }
 94 | ```
 95 | 
 96 | ## JSON Schema case
 97 | 
 98 | ```tsx
 99 | import React from 'react'
100 | import {
101 |   FormDrawer,
102 |   FormItem,
103 |   Input,
104 |   Submit,
105 |   Reset,
106 |   FormButtonGroup,
107 |   FormLayout,
108 | } from '@formily/next'
109 | import { createSchemaField } from '@formily/react'
110 | import { Button } from '@alifd/next'
111 | 
112 | const SchemaField = createSchemaField({
113 |   components: {
114 |     FormItem,
115 |     Input,
116 |   },
117 | })
118 | 
119 | const schema = {
120 |   type: 'object',
121 |   properties: {
122 |     aaa: {
123 |       type: 'string',
124 |       title: 'input box 1',
125 |       required: true,
126 |       'x-decorator': 'FormItem',
127 |       'x-component': 'Input',
128 |     },
129 |     bbb: {
130 |       type: 'string',
131 |       title: 'input box 2',
132 |       required: true,
133 |       'x-decorator': 'FormItem',
134 |       'x-component': 'Input',
135 |     },
136 |     ccc: {
137 |       type: 'string',
138 |       title: 'input box 3',
139 |       required: true,
140 |       'x-decorator': 'FormItem',
141 |       'x-component': 'Input',
142 |     },
143 |     ddd: {
144 |       type: 'string',
145 |       title: 'input box 4',
146 |       required: true,
147 |       'x-decorator': 'FormItem',
148 |       'x-component': 'Input',
149 |     },
150 |   },
151 | }
152 | 
153 | export default () => {
154 |   return (
155 |     <Button
156 |       onClick={() => {
157 |         FormDrawer('Pop-up form', () => {
158 |           return (
159 |             <FormLayout labelCol={6} wrapperCol={14}>
160 |               <SchemaField schema={schema} />
161 |               <FormDrawer.Footer>
162 |                 <FormButtonGroup align="right">
163 |                   <Submit
164 |                     onSubmit={() => {
165 |                       return new Promise((resolve) => {
166 |                         setTimeout(resolve, 1000)
167 |                       })
168 |                     }}
169 |                   >
170 |                     Submit
171 |                   </Submit>
172 |                   <Reset>Reset</Reset>
173 |                 </FormButtonGroup>
174 |               </FormDrawer.Footer>
175 |             </FormLayout>
176 |           )
177 |         })
178 |           .open({
179 |             initialValues: {
180 |               aaa: '123',
181 |             },
182 |           })
183 |           .then(console.log)
184 |       }}
185 |     >
186 |       Click me to open the form
187 |     </Button>
188 |   )
189 | }
190 | ```
191 | 
192 | ## Pure JSX case
193 | 
194 | ```tsx
195 | import React from 'react'
196 | import {
197 |   FormDrawer,
198 |   FormItem,
199 |   Input,
200 |   Submit,
201 |   Reset,
202 |   FormButtonGroup,
203 |   FormLayout,
204 | } from '@formily/next'
205 | import { Field } from '@formily/react'
206 | import { Button } from '@alifd/next'
207 | 
208 | export default () => {
209 |   return (
210 |     <Button
211 |       onClick={() => {
212 |         FormDrawer('Pop-up form', () => {
213 |           return (
214 |             <FormLayout labelCol={6} wrapperCol={14}>
215 |               <Field
216 |                 name="aaa"
217 |                 required
218 |                 title="input box 1"
219 |                 decorator={[FormItem]}
220 |                 component={[Input]}
221 |               />
222 |               <Field
223 |                 name="bbb"
224 |                 required
225 |                 title="input box 2"
226 |                 decorator={[FormItem]}
227 |                 component={[Input]}
228 |               />
229 |               <Field
230 |                 name="ccc"
231 |                 required
232 |                 title="input box 3"
233 |                 decorator={[FormItem]}
234 |                 component={[Input]}
235 |               />
236 |               <Field
237 |                 name="ddd"
238 |                 required
239 |                 title="input box 4"
240 |                 decorator={[FormItem]}
241 |                 component={[Input]}
242 |               />
243 |               <FormDrawer.Footer>
244 |                 <FormButtonGroup align="right">
245 |                   <Submit
246 |                     onSubmit={() => {
247 |                       return new Promise((resolve) => {
248 |                         setTimeout(resolve, 1000)
249 |                       })
250 |                     }}
251 |                   >
252 |                     Submit
253 |                   </Submit>
254 |                   <Reset>Reset</Reset>
255 |                 </FormButtonGroup>
256 |               </FormDrawer.Footer>
257 |             </FormLayout>
258 |           )
259 |         })
260 |           .open({
261 |             initialValues: {
262 |               aaa: '123',
263 |             },
264 |           })
265 |           .then(console.log)
266 |       }}
267 |     >
268 |       Click me to open the form
269 |     </Button>
270 |   )
271 | }
272 | ```
273 | 
274 | ## Use Fusion Context
275 | 
276 | ```tsx
277 | import React from 'react'
278 | import {
279 |   FormDrawer,
280 |   FormItem,
281 |   Input,
282 |   Submit,
283 |   Reset,
284 |   FormButtonGroup,
285 |   FormLayout,
286 | } from '@formily/next'
287 | import { Field } from '@formily/react'
288 | import { Button, ConfigProvider } from '@alifd/next'
289 | 
290 | export default () => {
291 |   return (
292 |     <ConfigProvider
293 |       defaultPropsConfig={{
294 |         Drawer: {},
295 |       }}
296 |     >
297 |       <Button
298 |         onClick={() => {
299 |           FormDrawer('Pop-up form', () => {
300 |             return (
301 |               <FormLayout labelCol={6} wrapperCol={14}>
302 |                 <Field
303 |                   name="aaa"
304 |                   required
305 |                   title="input box 1"
306 |                   decorator={[FormItem]}
307 |                   component={[Input]}
308 |                 />
309 |                 <Field
310 |                   name="bbb"
311 |                   required
312 |                   title="input box 2"
313 |                   decorator={[FormItem]}
314 |                   component={[Input]}
315 |                 />
316 |                 <Field
317 |                   name="ccc"
318 |                   required
319 |                   title="input box 3"
320 |                   decorator={[FormItem]}
321 |                   component={[Input]}
322 |                 />
323 |                 <Field
324 |                   name="ddd"
325 |                   required
326 |                   title="input box 4"
327 |                   decorator={[FormItem]}
328 |                   component={[Input]}
329 |                 />
330 |                 <FormDrawer.Footer>
331 |                   <FormButtonGroup align="right">
332 |                     <Submit
333 |                       onSubmit={() => {
334 |                         return new Promise((resolve) => {
335 |                           setTimeout(resolve, 1000)
336 |                         })
337 |                       }}
338 |                     >
339 |                       Submit
340 |                     </Submit>
341 |                     <Reset>Reset</Reset>
342 |                   </FormButtonGroup>
343 |                 </FormDrawer.Footer>
344 |               </FormLayout>
345 |             )
346 |           })
347 |             .open({
348 |               initialValues: {
349 |                 aaa: '123',
350 |               },
351 |             })
352 |             .then(console.log)
353 |         }}
354 |       >
355 |         Click me to open the form
356 |       </Button>
357 |     </ConfigProvider>
358 |   )
359 | }
360 | ```
361 | 
362 | ## API
363 | 
364 | ### FormDrawer
365 | 
366 | ```ts pure
367 | import { IFormProps, Form } from '@formily/core'
368 | 
369 | type FormDrawerRenderer =
370 |   | React.ReactElement
371 |   | ((form: Form) => React.ReactElement)
372 | 
373 | interface IFormDrawer {
374 |   forOpen(
375 |     middleware: (
376 |       props: IFormProps,
377 |       next: (props?: IFormProps) => Promise<any>
378 |     ) => any
379 |   ): any //Middleware interceptor, can intercept Drawer to open
380 |   //Open the pop-up window to receive form attributes, you can pass in initialValues/values/effects etc.
381 |   open(props: IFormProps): Promise<any> //return form data
382 |   //Close the pop-up window
383 |   close(): void
384 | }
385 | 
386 | interface IDrawerProps extends DrawerProps {
387 |   onClose?: (reason: string, e: React.MouseEvent) => void | boolean // return false can prevent onClose
388 |   loadingText?: React.ReactNode
389 | }
390 | 
391 | interface FormDrawer {
392 |   (title: IDrawerProps, id: string, renderer: FormDrawerRenderer): IFormDrawer
393 |   (title: IDrawerProps, renderer: FormDrawerRenderer): IFormDrawer
394 |   (title: ModalTitle, id: string, renderer: FormDrawerRenderer): IFormDrawer
395 |   (title: ModalTitle, renderer: FormDrawerRenderer): IFormDrawer
396 | }
397 | ```
398 | 
399 | `DrawerProps` type definition reference ant design [Drawer API](https://fusion.design/pc/component/drawer?themeid=2#API)
400 | 
401 | ### FormDrawer.Footer
402 | 
403 | No attributes, only child nodes are received
404 | 
405 | ### FormDrawer.Portal
406 | 
407 | Receive an optional id attribute, the default value is `form-drawer`, if there are multiple prefixCls in an application, and the prefixCls in the pop-up window of different regions are different, then it is recommended to specify the id as the region-level id
408 | 
```

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

```typescript
  1 | import { observable, action, autorun } from '..'
  2 | import { reaction } from '../autorun'
  3 | import { batch } from '../batch'
  4 | import { define } from '../model'
  5 | 
  6 | describe('normal action', () => {
  7 |   test('no action', () => {
  8 |     const obs = observable({
  9 |       aa: {
 10 |         bb: 123,
 11 |       },
 12 |     })
 13 |     const handler = jest.fn()
 14 |     autorun(() => {
 15 |       handler(obs.aa.bb)
 16 |     })
 17 |     obs.aa.bb = 111
 18 |     obs.aa.bb = 222
 19 |     expect(handler).toBeCalledTimes(3)
 20 | 
 21 |     obs.aa.bb = 333
 22 |     obs.aa.bb = 444
 23 | 
 24 |     expect(handler).toBeCalledTimes(5)
 25 |   })
 26 | 
 27 |   test('action', () => {
 28 |     const obs = observable({
 29 |       aa: {
 30 |         bb: 123,
 31 |       },
 32 |     })
 33 |     const handler = jest.fn()
 34 |     autorun(() => {
 35 |       handler(obs.aa.bb)
 36 |     })
 37 |     obs.aa.bb = 111
 38 |     obs.aa.bb = 222
 39 |     expect(handler).toBeCalledTimes(3)
 40 |     action(() => {
 41 |       obs.aa.bb = 333
 42 |       obs.aa.bb = 444
 43 |     })
 44 |     action(() => {})
 45 |     action()
 46 |     expect(handler).toBeCalledTimes(4)
 47 |   })
 48 | 
 49 |   test('action track', () => {
 50 |     const obs = observable({
 51 |       aa: {
 52 |         bb: 123,
 53 |       },
 54 |       cc: 1,
 55 |     })
 56 |     const handler = jest.fn()
 57 |     autorun(() => {
 58 |       action(() => {
 59 |         if (obs.cc > 0) {
 60 |           handler(obs.aa.bb)
 61 |           obs.cc = obs.cc + 20
 62 |         }
 63 |       })
 64 |     })
 65 |     expect(handler).toBeCalledTimes(1)
 66 |     expect(obs.cc).toEqual(21)
 67 |     obs.aa.bb = 321
 68 |     expect(handler).toBeCalledTimes(1)
 69 |     expect(obs.cc).toEqual(21)
 70 |   })
 71 | 
 72 |   test('action.bound', () => {
 73 |     const obs = observable({
 74 |       aa: {
 75 |         bb: 123,
 76 |       },
 77 |     })
 78 |     const handler = jest.fn()
 79 |     const setData = action.bound(() => {
 80 |       obs.aa.bb = 333
 81 |       obs.aa.bb = 444
 82 |     })
 83 |     autorun(() => {
 84 |       handler(obs.aa.bb)
 85 |     })
 86 |     obs.aa.bb = 111
 87 |     obs.aa.bb = 222
 88 |     expect(handler).toBeCalledTimes(3)
 89 |     setData()
 90 |     action.bound(() => {})
 91 |     expect(handler).toBeCalledTimes(4)
 92 |   })
 93 | 
 94 |   test('action.bound track', () => {
 95 |     const obs = observable({
 96 |       aa: {
 97 |         bb: 123,
 98 |       },
 99 |       cc: 1,
100 |     })
101 |     const handler = jest.fn()
102 |     autorun(() => {
103 |       action.bound(() => {
104 |         if (obs.cc > 0) {
105 |           handler(obs.aa.bb)
106 |           obs.cc = obs.cc + 20
107 |         }
108 |       })()
109 |     })
110 |     expect(handler).toBeCalledTimes(1)
111 |     expect(obs.cc).toEqual(21)
112 |     obs.aa.bb = 321
113 |     expect(handler).toBeCalledTimes(1)
114 |     expect(obs.cc).toEqual(21)
115 |   })
116 | 
117 |   test('action.scope xxx', () => {
118 |     const obs = observable<any>({})
119 | 
120 |     const handler = jest.fn()
121 | 
122 |     autorun(() => {
123 |       handler(obs.aa, obs.bb, obs.cc, obs.dd)
124 |     })
125 | 
126 |     action(() => {
127 |       action.scope(() => {
128 |         obs.aa = 123
129 |       })
130 |       action.scope(() => {
131 |         obs.cc = 'ccccc'
132 |       })
133 |       obs.bb = 321
134 |       obs.dd = 'ddddd'
135 |     })
136 | 
137 |     expect(handler).toBeCalledTimes(4)
138 |   })
139 | 
140 |   test('action.scope bound', () => {
141 |     const obs = observable<any>({})
142 | 
143 |     const handler = jest.fn()
144 | 
145 |     autorun(() => {
146 |       handler(obs.aa, obs.bb, obs.cc, obs.dd)
147 |     })
148 | 
149 |     const scope1 = action.scope.bound(() => {
150 |       obs.aa = 123
151 |     })
152 |     action(() => {
153 |       scope1()
154 |       action.scope.bound(() => {
155 |         obs.cc = 'ccccc'
156 |       })()
157 |       obs.bb = 321
158 |       obs.dd = 'ddddd'
159 |     })
160 | 
161 |     expect(handler).toBeCalledTimes(4)
162 |   })
163 | 
164 |   test('action.scope track', () => {
165 |     const obs = observable({
166 |       aa: {
167 |         bb: 123,
168 |       },
169 |       cc: 1,
170 |     })
171 |     const handler = jest.fn()
172 |     autorun(() => {
173 |       action.scope(() => {
174 |         if (obs.cc > 0) {
175 |           handler(obs.aa.bb)
176 |           obs.cc = obs.cc + 20
177 |         }
178 |       })
179 |     })
180 |     expect(handler).toBeCalledTimes(1)
181 |     expect(obs.cc).toEqual(21)
182 |     obs.aa.bb = 321
183 |     expect(handler).toBeCalledTimes(1)
184 |     expect(obs.cc).toEqual(21)
185 |   })
186 | 
187 |   test('action.scope bound track', () => {
188 |     const obs = observable({
189 |       aa: {
190 |         bb: 123,
191 |       },
192 |       cc: 1,
193 |     })
194 |     const handler = jest.fn()
195 |     autorun(() => {
196 |       action.scope.bound(() => {
197 |         if (obs.cc > 0) {
198 |           handler(obs.aa.bb)
199 |           obs.cc = obs.cc + 20
200 |         }
201 |       })()
202 |     })
203 |     expect(handler).toBeCalledTimes(1)
204 |     expect(obs.cc).toEqual(21)
205 |     obs.aa.bb = 321
206 |     expect(handler).toBeCalledTimes(1)
207 |     expect(obs.cc).toEqual(21)
208 |   })
209 | })
210 | 
211 | describe('annotation action', () => {
212 |   test('action', () => {
213 |     const obs = define(
214 |       {
215 |         aa: {
216 |           bb: 123,
217 |         },
218 |         setData() {
219 |           this.aa.bb = 333
220 |           this.aa.bb = 444
221 |         },
222 |       },
223 |       {
224 |         aa: observable,
225 |         setData: action,
226 |       }
227 |     )
228 |     const handler = jest.fn()
229 |     autorun(() => {
230 |       handler(obs.aa.bb)
231 |     })
232 |     obs.aa.bb = 111
233 |     obs.aa.bb = 222
234 |     expect(handler).toBeCalledTimes(3)
235 |     obs.setData()
236 |     expect(handler).toBeCalledTimes(4)
237 |   })
238 | 
239 |   test('action track', () => {
240 |     const obs = define(
241 |       {
242 |         aa: {
243 |           bb: 123,
244 |         },
245 |         cc: 1,
246 |         setData() {
247 |           if (obs.cc > 0) {
248 |             handler(obs.aa.bb)
249 |             obs.cc = obs.cc + 20
250 |           }
251 |         },
252 |       },
253 |       {
254 |         aa: observable,
255 |         setData: action,
256 |       }
257 |     )
258 |     const handler = jest.fn()
259 |     autorun(() => {
260 |       obs.setData()
261 |     })
262 |     expect(handler).toBeCalledTimes(1)
263 |     expect(obs.cc).toEqual(21)
264 |     obs.aa.bb = 321
265 |     expect(handler).toBeCalledTimes(1)
266 |     expect(obs.cc).toEqual(21)
267 |   })
268 | 
269 |   test('action.bound', () => {
270 |     const obs = define(
271 |       {
272 |         aa: {
273 |           bb: 123,
274 |         },
275 |         setData() {
276 |           this.aa.bb = 333
277 |           this.aa.bb = 444
278 |         },
279 |       },
280 |       {
281 |         aa: observable,
282 |         setData: action.bound,
283 |       }
284 |     )
285 |     const handler = jest.fn()
286 |     autorun(() => {
287 |       handler(obs.aa.bb)
288 |     })
289 |     obs.aa.bb = 111
290 |     obs.aa.bb = 222
291 |     expect(handler).toBeCalledTimes(3)
292 |     obs.setData()
293 |     expect(handler).toBeCalledTimes(4)
294 |   })
295 | 
296 |   test('action.bound track', () => {
297 |     const obs = define(
298 |       {
299 |         aa: {
300 |           bb: 123,
301 |         },
302 |         cc: 1,
303 |         setData() {
304 |           if (obs.cc > 0) {
305 |             handler(obs.aa.bb)
306 |             obs.cc = obs.cc + 20
307 |           }
308 |         },
309 |       },
310 |       {
311 |         aa: observable,
312 |         setData: action.bound,
313 |       }
314 |     )
315 |     const handler = jest.fn()
316 |     autorun(() => {
317 |       obs.setData()
318 |     })
319 |     expect(handler).toBeCalledTimes(1)
320 |     expect(obs.cc).toEqual(21)
321 |     obs.aa.bb = 321
322 |     expect(handler).toBeCalledTimes(1)
323 |     expect(obs.cc).toEqual(21)
324 |   })
325 | 
326 |   test('action.scope', () => {
327 |     const obs = define(
328 |       {
329 |         aa: null,
330 |         bb: null,
331 |         cc: null,
332 |         dd: null,
333 |         scope1() {
334 |           this.aa = 123
335 |         },
336 |         scope2() {
337 |           this.cc = 'ccccc'
338 |         },
339 |       },
340 |       {
341 |         aa: observable,
342 |         bb: observable,
343 |         cc: observable,
344 |         dd: observable,
345 |         scope1: action.scope,
346 |         scope2: action.scope,
347 |       }
348 |     )
349 | 
350 |     const handler = jest.fn()
351 | 
352 |     autorun(() => {
353 |       handler(obs.aa, obs.bb, obs.cc, obs.dd)
354 |     })
355 | 
356 |     action(() => {
357 |       obs.scope1()
358 |       obs.scope2()
359 |       obs.bb = 321
360 |       obs.dd = 'ddddd'
361 |     })
362 | 
363 |     expect(handler).toBeCalledTimes(4)
364 |   })
365 | 
366 |   test('action.scope bound', () => {
367 |     const obs = define(
368 |       {
369 |         aa: null,
370 |         bb: null,
371 |         cc: null,
372 |         dd: null,
373 |         scope1() {
374 |           this.aa = 123
375 |         },
376 |         scope2() {
377 |           this.cc = 'ccccc'
378 |         },
379 |       },
380 |       {
381 |         aa: observable,
382 |         bb: observable,
383 |         cc: observable,
384 |         dd: observable,
385 |         scope1: action.scope.bound,
386 |         scope2: action.scope.bound,
387 |       }
388 |     )
389 | 
390 |     const handler = jest.fn()
391 | 
392 |     autorun(() => {
393 |       handler(obs.aa, obs.bb, obs.cc, obs.dd)
394 |     })
395 | 
396 |     action(() => {
397 |       obs.scope1()
398 |       obs.scope2()
399 |       obs.bb = 321
400 |       obs.dd = 'ddddd'
401 |     })
402 | 
403 |     expect(handler).toBeCalledTimes(4)
404 |   })
405 | 
406 |   test('action.scope track', () => {
407 |     const obs = define(
408 |       {
409 |         aa: {
410 |           bb: 123,
411 |         },
412 |         cc: 1,
413 |         scope() {
414 |           if (this.cc > 0) {
415 |             handler(this.aa.bb)
416 |             this.cc = this.cc + 20
417 |           }
418 |         },
419 |       },
420 |       {
421 |         aa: observable,
422 |         cc: observable,
423 |         scope: action.scope,
424 |       }
425 |     )
426 |     const handler = jest.fn()
427 |     autorun(() => {
428 |       obs.scope()
429 |     })
430 |     expect(handler).toBeCalledTimes(1)
431 |     expect(obs.cc).toEqual(21)
432 |     obs.aa.bb = 321
433 |     expect(handler).toBeCalledTimes(1)
434 |     expect(obs.cc).toEqual(21)
435 |   })
436 | 
437 |   test('action.scope bound track', () => {
438 |     const obs = define(
439 |       {
440 |         aa: {
441 |           bb: 123,
442 |         },
443 |         cc: 1,
444 |         scope() {
445 |           if (this.cc > 0) {
446 |             handler(this.aa.bb)
447 |             this.cc = this.cc + 20
448 |           }
449 |         },
450 |       },
451 |       {
452 |         aa: observable,
453 |         cc: observable,
454 |         scope: action.scope.bound,
455 |       }
456 |     )
457 |     const handler = jest.fn()
458 |     autorun(() => {
459 |       obs.scope()
460 |     })
461 |     expect(handler).toBeCalledTimes(1)
462 |     expect(obs.cc).toEqual(21)
463 |     obs.aa.bb = 321
464 |     expect(handler).toBeCalledTimes(1)
465 |     expect(obs.cc).toEqual(21)
466 |   })
467 | })
468 | 
469 | test('nested action to reaction', () => {
470 |   const obs = observable({
471 |     aa: 0,
472 |   })
473 |   const handler = jest.fn()
474 |   reaction(
475 |     () => obs.aa,
476 |     (v) => handler(v)
477 |   )
478 |   action(() => {
479 |     obs.aa = 1
480 |     action(() => {
481 |       obs.aa = 2
482 |     })
483 |   })
484 |   action(() => {
485 |     obs.aa = 3
486 |     action(() => {
487 |       obs.aa = 4
488 |     })
489 |   })
490 |   expect(handler).nthCalledWith(1, 2)
491 |   expect(handler).nthCalledWith(2, 4)
492 |   expect(handler).toBeCalledTimes(2)
493 | })
494 | 
495 | test('nested action/batch to reaction', () => {
496 |   const obs = define(
497 |     {
498 |       bb: 0,
499 |       get aa() {
500 |         return this.bb
501 |       },
502 |       set aa(v) {
503 |         this.bb = v
504 |       },
505 |     },
506 |     {
507 |       aa: observable.computed,
508 |       bb: observable,
509 |     }
510 |   )
511 |   const handler = jest.fn()
512 |   reaction(
513 |     () => obs.aa,
514 |     (v) => handler(v)
515 |   )
516 |   action(() => {
517 |     obs.aa = 1
518 |     batch(() => {
519 |       obs.aa = 2
520 |     })
521 |   })
522 |   action(() => {
523 |     obs.aa = 3
524 |     batch(() => {
525 |       obs.aa = 4
526 |     })
527 |   })
528 |   expect(handler).nthCalledWith(1, 2)
529 |   expect(handler).nthCalledWith(2, 4)
530 |   expect(handler).toBeCalledTimes(2)
531 | })
532 | 
```

--------------------------------------------------------------------------------
/packages/reactive/src/__tests__/collections-map.spec.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { observable, autorun, raw } from '..'
  2 | 
  3 | describe('Map', () => {
  4 |   test('should be a proper JS Map', () => {
  5 |     const map = observable(new Map())
  6 |     expect(map).toBeInstanceOf(Map)
  7 |     expect(raw(map)).toBeInstanceOf(Map)
  8 |   })
  9 | 
 10 |   test('should autorun mutations', () => {
 11 |     const handler = jest.fn()
 12 |     const map = observable(new Map())
 13 |     autorun(() => handler(map.get('key')))
 14 | 
 15 |     expect(handler).toBeCalledTimes(1)
 16 |     expect(handler).lastCalledWith(undefined)
 17 |     map.set('key', 'value')
 18 |     expect(handler).toBeCalledTimes(2)
 19 |     expect(handler).lastCalledWith('value')
 20 |     map.set('key', 'value2')
 21 |     expect(handler).toBeCalledTimes(3)
 22 |     expect(handler).lastCalledWith('value2')
 23 |     map.delete('key')
 24 |     expect(handler).toBeCalledTimes(4)
 25 |     expect(handler).lastCalledWith(undefined)
 26 |   })
 27 | 
 28 |   test('should autorun size mutations', () => {
 29 |     const handler = jest.fn()
 30 |     const map = observable(new Map())
 31 |     autorun(() => handler(map.size))
 32 | 
 33 |     expect(handler).toBeCalledTimes(1)
 34 |     expect(handler).lastCalledWith(0)
 35 |     map.set('key1', 'value')
 36 |     map.set('key2', 'value2')
 37 |     expect(handler).toBeCalledTimes(3)
 38 |     expect(handler).lastCalledWith(2)
 39 |     map.delete('key1')
 40 |     expect(handler).toBeCalledTimes(4)
 41 |     expect(handler).lastCalledWith(1)
 42 |     map.clear()
 43 |     expect(handler).toBeCalledTimes(5)
 44 |     expect(handler).lastCalledWith(0)
 45 |   })
 46 | 
 47 |   test('should autorun for of iteration', () => {
 48 |     const handler = jest.fn()
 49 |     const map = observable(new Map())
 50 |     autorun(() => {
 51 |       let sum = 0
 52 |       // eslint-disable-next-line no-unused-vars
 53 |       for (let [, num] of map) {
 54 |         sum += num
 55 |       }
 56 |       handler(sum)
 57 |     })
 58 | 
 59 |     expect(handler).toBeCalledTimes(1)
 60 |     expect(handler).lastCalledWith(0)
 61 |     map.set('key0', 3)
 62 |     expect(handler).toBeCalledTimes(2)
 63 |     expect(handler).lastCalledWith(3)
 64 |     map.set('key1', 2)
 65 |     expect(handler).toBeCalledTimes(3)
 66 |     expect(handler).lastCalledWith(5)
 67 |     map.delete('key0')
 68 |     expect(handler).toBeCalledTimes(4)
 69 |     expect(handler).lastCalledWith(2)
 70 |     map.clear()
 71 |     expect(handler).toBeCalledTimes(5)
 72 |     expect(handler).lastCalledWith(0)
 73 |   })
 74 | 
 75 |   test('should autorun forEach iteration', () => {
 76 |     const handler = jest.fn()
 77 |     const map = observable(new Map())
 78 |     autorun(() => {
 79 |       let sum = 0
 80 |       map.forEach((num) => (sum += num))
 81 |       handler(sum)
 82 |     })
 83 | 
 84 |     expect(handler).toBeCalledTimes(1)
 85 |     expect(handler).lastCalledWith(0)
 86 |     map.set('key0', 3)
 87 |     expect(handler).toBeCalledTimes(2)
 88 |     expect(handler).lastCalledWith(3)
 89 |     map.set('key1', 2)
 90 |     expect(handler).toBeCalledTimes(3)
 91 |     expect(handler).lastCalledWith(5)
 92 |     map.delete('key0')
 93 |     expect(handler).toBeCalledTimes(4)
 94 |     expect(handler).lastCalledWith(2)
 95 |     map.clear()
 96 |     expect(handler).toBeCalledTimes(5)
 97 |     expect(handler).lastCalledWith(0)
 98 |   })
 99 | 
100 |   test('should autorun keys iteration', () => {
101 |     const handler = jest.fn()
102 |     const map = observable(new Map())
103 |     autorun(() => {
104 |       let sum = 0
105 |       for (let key of map.keys()) {
106 |         sum += key
107 |       }
108 |       handler(sum)
109 |     })
110 | 
111 |     expect(handler).toBeCalledTimes(1)
112 |     expect(handler).lastCalledWith(0)
113 |     map.set(3, 3)
114 |     expect(handler).toBeCalledTimes(2)
115 |     expect(handler).lastCalledWith(3)
116 |     map.set(2, 2)
117 |     expect(handler).toBeCalledTimes(3)
118 |     expect(handler).lastCalledWith(5)
119 |     map.delete(3)
120 |     expect(handler).toBeCalledTimes(4)
121 |     expect(handler).lastCalledWith(2)
122 |     map.clear()
123 |     expect(handler).toBeCalledTimes(5)
124 |     expect(handler).lastCalledWith(0)
125 |   })
126 | 
127 |   test('should autorun values iteration', () => {
128 |     const handler = jest.fn()
129 |     const map = observable(new Map())
130 |     autorun(() => {
131 |       let sum = 0
132 |       for (let num of map.values()) {
133 |         sum += num
134 |       }
135 |       handler(sum)
136 |     })
137 | 
138 |     expect(handler).toBeCalledTimes(1)
139 |     expect(handler).lastCalledWith(0)
140 |     map.set('key0', 3)
141 |     expect(handler).toBeCalledTimes(2)
142 |     expect(handler).lastCalledWith(3)
143 |     map.set('key1', 2)
144 |     expect(handler).toBeCalledTimes(3)
145 |     expect(handler).lastCalledWith(5)
146 |     map.delete('key0')
147 |     expect(handler).toBeCalledTimes(4)
148 |     expect(handler).lastCalledWith(2)
149 |     map.clear()
150 |     expect(handler).toBeCalledTimes(5)
151 |     expect(handler).lastCalledWith(0)
152 |   })
153 | 
154 |   test('should autorun entries iteration', () => {
155 |     const handler = jest.fn()
156 |     const map = observable(new Map())
157 |     autorun(() => {
158 |       let sum = 0
159 |       // eslint-disable-next-line no-unused-vars
160 |       for (let [, num] of map.entries()) {
161 |         sum += num
162 |       }
163 |       handler(sum)
164 |     })
165 | 
166 |     expect(handler).toBeCalledTimes(1)
167 |     expect(handler).lastCalledWith(0)
168 |     map.set('key0', 3)
169 |     expect(handler).toBeCalledTimes(2)
170 |     expect(handler).lastCalledWith(3)
171 |     map.set('key1', 2)
172 |     expect(handler).toBeCalledTimes(3)
173 |     expect(handler).lastCalledWith(5)
174 |     map.delete('key0')
175 |     expect(handler).toBeCalledTimes(4)
176 |     expect(handler).lastCalledWith(2)
177 |     map.clear()
178 |     expect(handler).toBeCalledTimes(5)
179 |     expect(handler).lastCalledWith(0)
180 |   })
181 | 
182 |   test('should be triggered by clearing', () => {
183 |     const handler = jest.fn()
184 |     const map = observable(new Map())
185 |     autorun(() => handler(map.get('key')))
186 | 
187 |     expect(handler).toBeCalledTimes(1)
188 |     expect(handler).lastCalledWith(undefined)
189 |     map.set('key', 3)
190 |     expect(handler).toBeCalledTimes(2)
191 |     expect(handler).lastCalledWith(3)
192 |     map.clear()
193 |     expect(handler).toBeCalledTimes(3)
194 |     expect(handler).lastCalledWith(undefined)
195 |   })
196 | 
197 |   test('should not autorun custom property mutations', () => {
198 |     const handler = jest.fn()
199 |     const map = observable(new Map())
200 |     autorun(() => handler(map['customProp']))
201 | 
202 |     expect(handler).toBeCalledTimes(1)
203 |     expect(handler).lastCalledWith(undefined)
204 |     map['customProp'] = 'Hello World'
205 |     expect(handler).toBeCalledTimes(1)
206 |   })
207 | 
208 |   test('should not autorun non value changing mutations', () => {
209 |     const handler = jest.fn()
210 |     const map = observable(new Map())
211 |     autorun(() => handler(map.get('key')))
212 | 
213 |     expect(handler).toBeCalledTimes(1)
214 |     expect(handler).lastCalledWith(undefined)
215 |     map.set('key', 'value')
216 |     expect(handler).toBeCalledTimes(2)
217 |     expect(handler).lastCalledWith('value')
218 |     map.set('key', 'value')
219 |     expect(handler).toBeCalledTimes(2)
220 |     map.delete('key')
221 |     expect(handler).toBeCalledTimes(3)
222 |     expect(handler).lastCalledWith(undefined)
223 |     map.delete('key')
224 |     expect(handler).toBeCalledTimes(3)
225 |     map.clear()
226 |     expect(handler).toBeCalledTimes(3)
227 |   })
228 | 
229 |   test('should not autorun raw data', () => {
230 |     const handler = jest.fn()
231 |     const map = observable(new Map())
232 |     autorun(() => handler(raw(map).get('key')))
233 | 
234 |     expect(handler).toBeCalledTimes(1)
235 |     expect(handler).lastCalledWith(undefined)
236 |     map.set('key', 'Hello')
237 |     expect(handler).toBeCalledTimes(1)
238 |     map.delete('key')
239 |     expect(handler).toBeCalledTimes(1)
240 |   })
241 | 
242 |   test('should not autorun raw iterations', () => {
243 |     const handler = jest.fn()
244 |     const map = observable(new Map())
245 |     autorun(() => {
246 |       let sum = 0
247 |       // eslint-disable-next-line no-unused-vars
248 |       for (let [, num] of raw(map).entries()) {
249 |         sum += num
250 |       }
251 |       for (let key of raw(map).keys()) {
252 |         sum += raw(map).get(key)
253 |       }
254 |       for (let num of raw(map).values()) {
255 |         sum += num
256 |       }
257 |       raw(map).forEach((num) => {
258 |         sum += num
259 |       })
260 |       // eslint-disable-next-line no-unused-vars
261 |       for (let [, num] of raw(map)) {
262 |         sum += num
263 |       }
264 |       handler(sum)
265 |     })
266 | 
267 |     expect(handler).toBeCalledTimes(1)
268 |     expect(handler).lastCalledWith(0)
269 |     map.set('key1', 2)
270 |     map.set('key2', 3)
271 |     expect(handler).toBeCalledTimes(1)
272 |     map.delete('key1')
273 |     expect(handler).toBeCalledTimes(1)
274 |   })
275 | 
276 |   test('should not be triggered by raw mutations', () => {
277 |     const handler = jest.fn()
278 |     const map = observable(new Map())
279 |     autorun(() => handler(map.get('key')))
280 | 
281 |     expect(handler).toBeCalledTimes(1)
282 |     expect(handler).lastCalledWith(undefined)
283 |     raw(map).set('key', 'Hello')
284 |     expect(handler).toBeCalledTimes(1)
285 |     raw(map).delete('key')
286 |     expect(handler).toBeCalledTimes(1)
287 |     raw(map).clear()
288 |     expect(handler).toBeCalledTimes(1)
289 |   })
290 | 
291 |   test('should not autorun raw size mutations', () => {
292 |     const handler = jest.fn()
293 |     const map = observable(new Map())
294 |     autorun(() => handler(raw(map).size))
295 | 
296 |     expect(handler).toBeCalledTimes(1)
297 |     expect(handler).lastCalledWith(0)
298 |     map.set('key', 'value')
299 |     expect(handler).toBeCalledTimes(1)
300 |   })
301 | 
302 |   test('should not be triggered by raw size mutations', () => {
303 |     const handler = jest.fn()
304 |     const map = observable(new Map())
305 |     autorun(() => handler(map.size))
306 | 
307 |     expect(handler).toBeCalledTimes(1)
308 |     expect(handler).lastCalledWith(0)
309 |     raw(map).set('key', 'value')
310 |     expect(handler).toBeCalledTimes(1)
311 |   })
312 | 
313 |   test('should support objects as key', () => {
314 |     const handler = jest.fn()
315 |     const key = {}
316 |     const map = observable(new Map())
317 |     autorun(() => handler(map.get(key)))
318 | 
319 |     expect(handler).toBeCalledTimes(1)
320 |     expect(handler).lastCalledWith(undefined)
321 | 
322 |     map.set(key, 1)
323 |     expect(handler).toBeCalledTimes(2)
324 |     expect(handler).lastCalledWith(1)
325 | 
326 |     map.set({}, 2)
327 |     expect(handler).toBeCalledTimes(2)
328 |     expect(handler).lastCalledWith(1)
329 |   })
330 | 
331 |   test('observer object', () => {
332 |     const handler = jest.fn()
333 |     const map = observable(new Map<string, Record<string, any>>([]))
334 |     map.set('key', {})
335 |     map.set('key2', observable({}))
336 |     autorun(() => {
337 |       const [obs1, obs2] = [...map.values()]
338 | 
339 |       handler(obs1.aa, obs2.aa)
340 |     })
341 | 
342 |     expect(handler).toBeCalledTimes(1)
343 |     const obs1 = map.get('key')
344 |     const obs2 = map.get('key2')
345 |     obs1.aa = '123'
346 |     obs2.aa = '234'
347 |     expect(handler).toBeCalledTimes(3)
348 |   })
349 | 
350 |   test('shallow', () => {
351 |     const handler = jest.fn()
352 |     const map = observable.shallow(new Map<string, Record<string, any>>([]))
353 |     map.set('key', {})
354 |     autorun(() => {
355 |       const [obs] = [...map.values()]
356 | 
357 |       handler(obs.aa)
358 |     })
359 | 
360 |     expect(handler).toBeCalledTimes(1)
361 |     const obs = map.get('key')
362 |     obs.aa = '123'
363 |     expect(handler).toBeCalledTimes(1)
364 |   })
365 | })
366 | 
```

--------------------------------------------------------------------------------
/packages/path/src/__tests__/match.spec.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import expect from 'expect'
  2 | import { Path } from '../'
  3 | import { Matcher } from '../matcher'
  4 | 
  5 | const match = (obj) => {
  6 |   for (let name in obj) {
  7 |     test('test match ' + name, () => {
  8 |       const path = new Path(name)
  9 |       if (Array.isArray(obj[name]) && Array.isArray(obj[name][0])) {
 10 |         obj[name].forEach((_path) => {
 11 |           expect(path.match(_path)).toBeTruthy()
 12 |         })
 13 |       } else {
 14 |         expect(path.match(obj[name])).toBeTruthy()
 15 |       }
 16 |     })
 17 |   }
 18 | }
 19 | 
 20 | const unmatch = (obj) => {
 21 |   for (let name in obj) {
 22 |     test('test unmatch ' + name, () => {
 23 |       const path = new Path(name)
 24 |       if (Array.isArray(obj[name]) && Array.isArray(obj[name][0])) {
 25 |         obj[name].forEach((_path) => {
 26 |           expect(path.match(_path)).toBeFalsy()
 27 |         })
 28 |       } else {
 29 |         expect(path.match(obj[name])).toBeFalsy()
 30 |       }
 31 |     })
 32 |   }
 33 | }
 34 | 
 35 | test('basic match', () => {
 36 |   expect(Path.parse('xxx').match('')).toBeFalsy()
 37 |   expect(Path.parse('xxx').match('aaa')).toBeFalsy()
 38 |   expect(Path.parse('xxx.eee').match('xxx')).toBeFalsy()
 39 |   expect(Path.parse('*(xxx.eee~)').match('xxx')).toBeFalsy()
 40 |   expect(Path.parse('xxx.eee~').match('xxx.eee')).toBeTruthy()
 41 |   expect(Path.parse('*(!xxx.eee,yyy)').match('xxx')).toBeFalsy()
 42 |   expect(Path.parse('*(!xxx.eee,yyy)').match('xxx.ooo.ppp')).toBeTruthy()
 43 |   expect(Path.parse('*(!xxx.eee,yyy)').match('xxx.eee')).toBeFalsy()
 44 |   expect(Path.parse('*(!xxx.eee~,yyy)').match('xxx.eee')).toBeFalsy()
 45 |   expect(Path.parse('~.aa').match('xxx.aa')).toBeTruthy()
 46 | })
 47 | 
 48 | test('not expect match not', () => {
 49 |   expect(new Matcher({}).match(['']).matched).toBeFalsy()
 50 | })
 51 | 
 52 | test('test matchGroup', () => {
 53 |   const pattern = new Path('*(aa,bb,cc)')
 54 |   expect(pattern.matchAliasGroup('aa', 'bb')).toEqual(true)
 55 |   const excludePattern = new Path('aa.bb.*(11,22,33).*(!aa,bb,cc)')
 56 |   expect(
 57 |     excludePattern.matchAliasGroup('aa.bb.11.mm', 'aa.cc.dd.bb.11.mm')
 58 |   ).toEqual(true)
 59 |   expect(excludePattern.matchAliasGroup('aa.cc', 'aa.kk.cc')).toEqual(false)
 60 |   expect(new Path('aa.*(!bb)').matchAliasGroup('kk.mm.aa.bb', 'aa.bb')).toEqual(
 61 |     false
 62 |   )
 63 |   expect(
 64 |     new Path('aa.*(!bb)').matchAliasGroup('kk.mm.aa.bb.cc', 'kk.mm.aa')
 65 |   ).toEqual(false)
 66 |   expect(new Path('aa.*(!bb,oo)').matchAliasGroup('kk.mm', 'aa')).toEqual(false)
 67 |   expect(new Path('aa.*(!bb.*)').matchAliasGroup('kk.mm', 'aa')).toEqual(false)
 68 |   expect(new Path('aa.*(!bb)').matchAliasGroup('kk.mm.aa.cc', 'aa.cc')).toEqual(
 69 |     true
 70 |   )
 71 |   const patttern2 = Path.parse('*(array)')
 72 |   expect(patttern2.matchAliasGroup(['array', 0], ['array', 0])).toEqual(false)
 73 | })
 74 | 
 75 | test('exclude match', () => {
 76 |   //路径长度相等
 77 |   expect(Path.parse('*(!aaa)').match('ggg')).toBeTruthy()
 78 |   expect(Path.parse('*(!aaa)').match('aaa')).toBeFalsy()
 79 |   expect(Path.parse('*(!aaa.bbb)').match('ggg.ddd')).toBeTruthy()
 80 |   expect(Path.parse('*(!aaa.ccc)').match('aaa.ccc')).toBeFalsy()
 81 |   //长路径匹配短路径
 82 |   expect(Path.parse('*(!aaa.bbb)').match('ggg')).toBeTruthy()
 83 |   expect(Path.parse('*(!aaa.bbb)').match('aaa')).toBeFalsy()
 84 |   //短路径匹配长路径
 85 |   expect(Path.parse('*(!aaa)').match('aaa.bbb')).toBeTruthy()
 86 |   expect(Path.parse('*(!aaa)').match('aaa.ccc')).toBeTruthy()
 87 |   expect(Path.parse('*(!aaa)').match('bbb.ccc')).toBeTruthy()
 88 | 
 89 |   expect(Path.parse('*(!aaa,bbb)').match('bbb')).toBeFalsy()
 90 |   expect(Path.parse('*(!aaa.bbb)').match('aaa.ccc')).toBeTruthy()
 91 |   expect(Path.parse('*(!basic.name,versionTag)').match('basic.id')).toBeTruthy()
 92 |   expect(Path.parse('*(!basic.name,versionTag)').match('basic')).toBeFalsy()
 93 |   expect(
 94 |     Path.parse('*(!basic.name,versionTag)').match('isExecutable')
 95 |   ).toBeTruthy()
 96 |   expect(
 97 |     Path.parse('*(!basic.name,versionTag)').match('versionTag')
 98 |   ).toBeFalsy()
 99 |   expect(
100 |     Path.parse('*(!basic.name,basic.name.*,versionTag)').match('basic.name')
101 |   ).toBeFalsy()
102 |   expect(
103 |     Path.parse('*(!basic.name,basic.name.*,versionTag)').match('basic.name.kkk')
104 |   ).toBeFalsy()
105 |   expect(Path.parse('aa.*(!bb)').match('kk.mm.aa.bb.cc')).toBeFalsy()
106 |   expect(Path.parse('aa.*(!bb)').match('aa')).toBeFalsy()
107 |   expect(Path.parse('aa.*(!bb.*)').match('aa')).toBeFalsy()
108 |   expect(Path.parse('aa.*(!bb,cc)').match('aa')).toBeFalsy()
109 |   expect(Path.parse('aa.*(!bb,cc)').match('aa.dd')).toBeTruthy()
110 |   expect(Path.parse('aa.*(!bb,cc)').match('aa.kk')).toBeTruthy()
111 | })
112 | 
113 | test('match regexp', () => {
114 |   expect(Path.parse(/^\d+$/).match('212')).toBeTruthy()
115 |   expect(Path.parse(/^\d+$/).match('212dd')).toBeFalsy()
116 | })
117 | 
118 | test('test zero', () => {
119 |   expect(Path.parse('t.0.value~').match(['t', 0, 'value_list'])).toEqual(true)
120 | })
121 | 
122 | test('test optional wild match', () => {
123 |   expect(Path.parse('aa.**').match(['aa'])).toEqual(true)
124 |   expect(Path.parse('aa.**').match(['aa', 'bb', 'cc'])).toEqual(true)
125 |   expect(Path.parse('aa.*').match(['aa'])).toEqual(false)
126 |   expect(Path.parse('aa.\\*\\*\\.aa').match(['aa', '**.aa'])).toEqual(true)
127 |   expect(Path.parse('aa.[[**.aa]]').match(['aa', '**.aa'])).toEqual(true)
128 |   expect(() => Path.parse('aa.**.aa').match(['aa'])).toThrowError()
129 |   expect(() => Path.parse('aa.**(bb)').match(['aa'])).toThrowError()
130 |   expect(Path.parse('*(aa.**)').match(['aa'])).toEqual(true)
131 |   expect(Path.parse('*(aa.**,bb.**)').match(['aa'])).toEqual(true)
132 |   expect(Path.parse('*(aa.**,bb.**)').match(['aa', 'bb', 'cc'])).toEqual(true)
133 |   expect(Path.parse('*(aa.**,bb.**)').match(['bb'])).toEqual(true)
134 |   expect(Path.parse('*(aa.**,bb.**)').match(['bb', 'cc', 'dd'])).toEqual(true)
135 |   expect(Path.parse('*(aa.**,bb.**)').match(['cc'])).toEqual(false)
136 |   expect(Path.parse('*(aa.**,bb.**).bb').match(['aa', 'oo'])).toEqual(true)
137 |   expect(Path.parse('*(aa.**,bb.**).bb').match(['bb', 'oo'])).toEqual(true)
138 |   expect(Path.parse('*(aa.**,bb.**).bb').match(['aa', 'oo', 'bb'])).toEqual(
139 |     true
140 |   )
141 |   expect(Path.parse('*(aa.**,bb.**).bb').match(['bb', 'oo', 'bb'])).toEqual(
142 |     true
143 |   )
144 |   expect(
145 |     Path.parse('*(aa.**,bb.**).bb').match(['aa', 'oo', 'kk', 'dd', 'bb'])
146 |   ).toEqual(true)
147 |   expect(
148 |     Path.parse('*(aa.**,bb.**).bb').match(['cc', 'oo', 'kk', 'dd', 'bb'])
149 |   ).toEqual(false)
150 |   expect(
151 |     Path.parse('*(aa.**,bb.**).bb').match(['bb', 'oo', 'kk', 'dd', 'bb'])
152 |   ).toEqual(true)
153 |   expect(
154 |     Path.parse('*(aa.**,bb.**).bb').match(['kk', 'oo', 'kk', 'dd', 'bb'])
155 |   ).toEqual(false)
156 | })
157 | 
158 | test('test expand', () => {
159 |   expect(
160 |     Path.parse('t.0.value~').match(['t', 0, 'value_list', 'hello'])
161 |   ).toEqual(false)
162 | })
163 | 
164 | test('test multi expand', () => {
165 |   expect(Path.parse('*(aa~,bb~).*').match(['aa12323', 'asdasd'])).toEqual(true)
166 | })
167 | 
168 | test('test group', () => {
169 |   const node = Path.parse('*(phases.*.type,phases.*.steps.*.type)')
170 |   expect(node.match('phases.0.steps.1.type')).toBeTruthy()
171 | })
172 | 
173 | test('test segments', () => {
174 |   const node = Path.parse('a.0.b')
175 |   expect(node.match(['a', 0, 'b'])).toEqual(true)
176 | })
177 | 
178 | test('nested group match', () => {
179 |   expect(
180 |     Path.parse('aa.*.*(bb,cc).dd.*(kk,oo).ee').match('aa.0.cc.dd.kk.ee')
181 |   ).toEqual(true)
182 | })
183 | 
184 | test('group match with destructor', () => {
185 |   expect(Path.parse('*([startDate,endDate],date,weak)').match('date')).toEqual(
186 |     true
187 |   )
188 |   expect(Path.parse('*({startDate,endDate},date,weak)').match('date')).toEqual(
189 |     true
190 |   )
191 |   expect(Path.parse('*([startDate,endDate],date,weak)').match('xxx')).toEqual(
192 |     false
193 |   )
194 |   expect(Path.parse('*({startDate,endDate},date,weak)').match('xxx')).toEqual(
195 |     false
196 |   )
197 |   expect(
198 |     Path.parse('*([startDate,endDate],date,weak)').match('[startDate,endDate]')
199 |   ).toEqual(true)
200 |   expect(
201 |     Path.parse('*({startDate,endDate},date,weak)').match('{startDate,endDate}')
202 |   ).toEqual(true)
203 | })
204 | 
205 | test('all range match', () => {
206 |   expect(
207 |     Path.parse('array.*[:].*[:].*[:].bb').match('array.0.0.0.aa')
208 |   ).toBeFalsy()
209 | })
210 | 
211 | match({
212 |   '*': [[], ['aa'], ['aa', 'bb', 'cc'], ['aa', 'dd', 'gg']],
213 |   '*.a.b': [
214 |     ['c', 'a', 'b'],
215 |     ['k', 'a', 'b'],
216 |     ['m', 'a', 'b'],
217 |   ],
218 |   'a.*.k': [
219 |     ['a', 'b', 'k'],
220 |     ['a', 'd', 'k'],
221 |     ['a', 'c', 'k'],
222 |   ],
223 |   'a.*(b,d,m).k': [
224 |     ['a', 'b', 'k'],
225 |     ['a', 'd', 'k'],
226 |     ['a', 'm', 'k'],
227 |   ],
228 |   'a.*(!b,d,m).*(!a,b)': [
229 |     ['a', 'o', 'k'],
230 |     ['a', 'q', 'k'],
231 |     ['a', 'c', 'k'],
232 |   ],
233 |   'a.*(b.c.d,d,m).k': [
234 |     ['a', 'b', 'c', 'd', 'k'],
235 |     ['a', 'd', 'k'],
236 |     ['a', 'm', 'k'],
237 |   ],
238 |   'a.*(b.*(c,k).d,d,m).k': [
239 |     ['a', 'b', 'c', 'd', 'k'],
240 |     ['a', 'b', 'k', 'd', 'k'],
241 |     ['a', 'd', 'k'],
242 |     ['a', 'm', 'k'],
243 |   ],
244 |   'a.b.*': [
245 |     ['a', 'b', 'c', 'd'],
246 |     ['a', 'b', 'c'],
247 |     ['a', 'b', 2, 'aaa', 3, 'bbb'],
248 |   ],
249 |   '*(step1,step2).*': [
250 |     ['step1', 'aa', 'bb'],
251 |     ['step1', 'aa', 'bb', 'ccc', 'ddd'],
252 |   ],
253 |   'dyanmic.*(!dynamic-1)': [
254 |     ['dyanmic', 'dynamic-2'],
255 |     ['dyanmic', 'dynamic-3'],
256 |   ],
257 |   't.0.value~': [['t', '0', 'value']],
258 |   'a.*[10:50].*(!a,b)': [
259 |     ['a', 49, 's'],
260 |     ['a', 10, 's'],
261 |     ['a', 50, 's'],
262 |   ],
263 |   'a.*[10:].*(!a,b)': [
264 |     ['a', 49, 's'],
265 |     ['a', 10, 's'],
266 |     ['a', 50, 's'],
267 |   ],
268 |   'a.*[].*(!a,b)': [
269 |     ['a', 49, 's'],
270 |     ['a', 10, 's'],
271 |     ['a', 50, 's'],
272 |   ],
273 |   'a.*[:50].*(!a,b)': [
274 |     ['a', 49, 's'],
275 |     ['a', 10, 's'],
276 |     ['a', 50, 's'],
277 |   ],
278 |   'a.*([[a.b.c]],[[c.b.d~]])': [
279 |     ['a', '[[a.b.c]]'],
280 |     ['a', 'c.b.d~'],
281 |   ],
282 |   'a.*(!k,d,m).k': [
283 |     ['a', 'u', 'k'],
284 |     ['a', 'o', 'k'],
285 |     ['a', 'p', 'k'],
286 |   ],
287 |   'a\\.\\*\\[1\\]': [['a.*[1]']],
288 |   '[[\\[aa,bb\\]]]': [['[aa,bb]']],
289 |   '[[\\[aa,bb\\]   ]]': [['[aa,bb]   ']],
290 |   '[[   \\[aa,bb~\\]   ]]': [['   [aa,bb~]   ']],
291 |   'aa.bb.*': [['aa', 'bb', 'ccc']],
292 |   'a.*': [
293 |     ['a', 'b'],
294 |     ['a', 'b', 'c'],
295 |   ],
296 |   'aa.*.*(bb,cc).dd': [['aa', '0', 'cc', 'dd']],
297 |   'aaa.products.0.*': [['aaa', 'products', '0', 'aaa']],
298 |   'aa~.ccc': [
299 |     ['aa', 'ccc'],
300 |     ['aa12', 'ccc'],
301 |   ],
302 |   '*(aa~,bb~).*': [
303 |     ['aa12323', 'asdasd'],
304 |     ['bb12222', 'asd'],
305 |   ],
306 |   '*(aa,bb,bb.aa)': [['bb', 'aa']],
307 |   '*(!aa,bb,bb.aa)': [['xx'], ['yyy']],
308 |   '*(!aaa)': [['bbb']],
309 |   '*(!aaa,bbb)': [['ccc'], ['ggg']],
310 |   '*([startDate,endDate],date,weak)': [['date']],
311 | })
312 | 
313 | unmatch({
314 |   'a.*': [['a'], ['b']],
315 |   '*(array)': [['array', '0']],
316 |   'aa.bb.*': [['aa', 'bb']],
317 |   'a.*.b': [['a', 'k', 'b', 'd']],
318 |   '*(!aaa)': [['aaa']],
319 |   'dyanmic.*(!dynamic-1)': [['dyanmic', 'dynamic-1']],
320 |   'dyanmic.*(!dynamic-1.*)': [['dyanmic', 'dynamic-1', 'ccc']],
321 |   a: [['c', 'b']],
322 |   'aa~.ccc': [['a', 'ccc'], ['aa'], ['aaasdd']],
323 |   bb: [['bb', 'cc']],
324 |   'aa.*(cc,bb).*.aa': [['aa', 'cc', '0', 'bb']],
325 | })
326 | 
```

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

```typescript
  1 | import React, {
  2 |   Fragment,
  3 |   useState,
  4 |   useRef,
  5 |   useEffect,
  6 |   createContext,
  7 |   useContext,
  8 | } from 'react'
  9 | import { Table, Pagination, Select, Badge } from '@alifd/next'
 10 | import { PaginationProps } from '@alifd/next/lib/pagination'
 11 | import { TableProps, ColumnProps } from '@alifd/next/lib/table'
 12 | import { SelectProps } from '@alifd/next/lib/select'
 13 | import cls from 'classnames'
 14 | import { GeneralField, FieldDisplayTypes, ArrayField } from '@formily/core'
 15 | import {
 16 |   useField,
 17 |   observer,
 18 |   useFieldSchema,
 19 |   RecursionField,
 20 |   ReactFC,
 21 | } from '@formily/react'
 22 | import { isArr, isBool, isFn } from '@formily/shared'
 23 | import { Schema } from '@formily/json-schema'
 24 | import { usePrefixCls } from '../__builtins__'
 25 | import { ArrayBase, ArrayBaseMixins, IArrayBaseProps } from '../array-base'
 26 | 
 27 | interface ObservableColumnSource {
 28 |   field: GeneralField
 29 |   columnProps: ColumnProps
 30 |   schema: Schema
 31 |   display: FieldDisplayTypes
 32 |   name: string
 33 | }
 34 | 
 35 | interface IArrayTablePaginationProps extends Omit<PaginationProps, 'children'> {
 36 |   dataSource?: any[]
 37 |   children?: (
 38 |     dataSource: any[],
 39 |     pagination: React.ReactNode
 40 |   ) => React.ReactElement
 41 | }
 42 | 
 43 | interface IStatusSelectProps extends SelectProps {
 44 |   pageSize?: number
 45 | }
 46 | 
 47 | export type ExtendTableProps = {
 48 |   pagination?: PaginationProps
 49 | } & IArrayBaseProps &
 50 |   TableProps
 51 | 
 52 | type ComposedArrayTable = ReactFC<ExtendTableProps> &
 53 |   ArrayBaseMixins & {
 54 |     Column?: ReactFC<ColumnProps>
 55 |   }
 56 | 
 57 | interface PaginationAction {
 58 |   totalPage?: number
 59 |   pageSize?: number
 60 |   changePage?: (page: number) => void
 61 | }
 62 | 
 63 | const isColumnComponent = (schema: Schema) => {
 64 |   return schema['x-component']?.indexOf('Column') > -1
 65 | }
 66 | 
 67 | const isOperationsComponent = (schema: Schema) => {
 68 |   return schema['x-component']?.indexOf('Operations') > -1
 69 | }
 70 | 
 71 | const isAdditionComponent = (schema: Schema) => {
 72 |   return schema['x-component']?.indexOf('Addition') > -1
 73 | }
 74 | 
 75 | const useArrayTableSources = () => {
 76 |   const arrayField = useField()
 77 |   const schema = useFieldSchema()
 78 |   const parseSources = (schema: Schema): ObservableColumnSource[] => {
 79 |     if (
 80 |       isColumnComponent(schema) ||
 81 |       isOperationsComponent(schema) ||
 82 |       isAdditionComponent(schema)
 83 |     ) {
 84 |       if (!schema['x-component-props']?.['dataIndex'] && !schema['name'])
 85 |         return []
 86 |       const name = schema['x-component-props']?.['dataIndex'] || schema['name']
 87 |       const field = arrayField.query(arrayField.address.concat(name)).take()
 88 |       const columnProps =
 89 |         field?.component?.[1] || schema['x-component-props'] || {}
 90 |       const display = field?.display || schema['x-display'] || 'visible'
 91 |       return [
 92 |         {
 93 |           name,
 94 |           display,
 95 |           field,
 96 |           schema,
 97 |           columnProps,
 98 |         },
 99 |       ]
100 |     } else if (schema.properties) {
101 |       return schema.reduceProperties((buf, schema) => {
102 |         return buf.concat(parseSources(schema))
103 |       }, [])
104 |     }
105 |   }
106 | 
107 |   const parseArrayItems = (schema: Schema['items']) => {
108 |     if (!schema) return []
109 |     const sources: ObservableColumnSource[] = []
110 |     const items = isArr(schema) ? schema : [schema]
111 |     return items.reduce((columns, schema) => {
112 |       const item = parseSources(schema)
113 |       if (item) {
114 |         return columns.concat(item)
115 |       }
116 |       return columns
117 |     }, sources)
118 |   }
119 | 
120 |   return parseArrayItems(schema.items)
121 | }
122 | 
123 | const useArrayTableColumns = (
124 |   dataSource: any[],
125 |   field: ArrayField,
126 |   sources: ObservableColumnSource[]
127 | ): TableProps['columns'] => {
128 |   return sources.reduce((buf, { name, columnProps, schema, display }, key) => {
129 |     if (display !== 'visible') return buf
130 |     if (!isColumnComponent(schema)) return buf
131 |     return buf.concat({
132 |       ...columnProps,
133 |       key,
134 |       dataIndex: name,
135 |       cell: (value: any, _: number, record: any) => {
136 |         const index = dataSource?.indexOf(record)
137 |         const children = (
138 |           <ArrayBase.Item
139 |             key={index}
140 |             index={index}
141 |             record={() => field.value?.[index]}
142 |           >
143 |             <RecursionField schema={schema} name={index} onlyRenderProperties />
144 |           </ArrayBase.Item>
145 |         )
146 |         return children
147 |       },
148 |     })
149 |   }, [])
150 | }
151 | 
152 | const useAddition = () => {
153 |   const schema = useFieldSchema()
154 |   return schema.reduceProperties((addition, schema, key) => {
155 |     if (isAdditionComponent(schema)) {
156 |       return <RecursionField schema={schema} name={key} />
157 |     }
158 |     return addition
159 |   }, null)
160 | }
161 | 
162 | const schedulerRequest = {
163 |   request: null,
164 | }
165 | 
166 | const StatusSelect: ReactFC<IStatusSelectProps> = observer(
167 |   ({ pageSize, ...props }) => {
168 |     const field = useField<ArrayField>()
169 |     const prefixCls = usePrefixCls('formily-array-table')
170 |     const errors = field.errors
171 |     const parseIndex = (address: string) => {
172 |       return Number(
173 |         address
174 |           .slice(address.indexOf(field.address.toString()) + 1)
175 |           .match(/(\d+)/)?.[1]
176 |       )
177 |     }
178 |     const options = props.dataSource?.map(({ label, value }) => {
179 |       const hasError = errors.some(({ address }) => {
180 |         const currentIndex = parseIndex(address)
181 |         const startIndex = (value - 1) * pageSize
182 |         const endIndex = value * pageSize
183 |         return currentIndex >= startIndex && currentIndex <= endIndex
184 |       })
185 |       return {
186 |         label: hasError ? <Badge dot>{label}</Badge> : label,
187 |         value,
188 |       }
189 |     })
190 | 
191 |     return (
192 |       <Select
193 |         {...props}
194 |         value={props.value}
195 |         onChange={props.onChange}
196 |         dataSource={options}
197 |         useVirtual
198 |         className={cls(`${prefixCls}-status-select`, {
199 |           'has-error': errors?.length,
200 |         })}
201 |       />
202 |     )
203 |   },
204 |   {
205 |     scheduler: (update) => {
206 |       clearTimeout(schedulerRequest.request)
207 |       schedulerRequest.request = setTimeout(() => {
208 |         update()
209 |       }, 100)
210 |     },
211 |   }
212 | )
213 | 
214 | const PaginationContext = createContext<PaginationAction>({})
215 | const usePagination = () => {
216 |   return useContext(PaginationContext)
217 | }
218 | 
219 | const ArrayTablePagination: ReactFC<IArrayTablePaginationProps> = ({
220 |   children,
221 |   dataSource,
222 |   ...props
223 | }) => {
224 |   const [current, setCurrent] = useState(1)
225 |   const prefixCls = usePrefixCls('formily-array-table')
226 |   const pageSize = props.pageSize || 10
227 |   const size = props.size || 'medium'
228 |   const sources = dataSource || []
229 |   const startIndex = (current - 1) * pageSize
230 |   const endIndex = startIndex + pageSize - 1
231 |   const total = sources?.length || 0
232 |   const totalPage = Math.ceil(total / pageSize)
233 |   const pages = Array.from(new Array(totalPage)).map((_, index) => {
234 |     const page = index + 1
235 |     return {
236 |       label: page,
237 |       value: page,
238 |     }
239 |   })
240 |   const handleChange = (current: number) => {
241 |     setCurrent(current)
242 |   }
243 | 
244 |   useEffect(() => {
245 |     if (totalPage > 0 && totalPage < current) {
246 |       handleChange(totalPage)
247 |     }
248 |   }, [totalPage, current])
249 | 
250 |   const renderPagination = () => {
251 |     if (totalPage <= 1) return
252 |     return (
253 |       <div className={`${prefixCls}-pagination`}>
254 |         <StatusSelect
255 |           value={current}
256 |           pageSize={pageSize}
257 |           onChange={handleChange}
258 |           dataSource={pages}
259 |           notFoundContent={false}
260 |         />
261 |         <Pagination
262 |           {...props}
263 |           pageSize={pageSize}
264 |           current={current}
265 |           total={dataSource.length}
266 |           size={size}
267 |           pageSizeSelector={false}
268 |           onChange={handleChange}
269 |         />
270 |       </div>
271 |     )
272 |   }
273 | 
274 |   return (
275 |     <Fragment>
276 |       <PaginationContext.Provider
277 |         value={{ totalPage, pageSize, changePage: handleChange }}
278 |       >
279 |         {children?.(
280 |           dataSource?.slice(startIndex, endIndex + 1),
281 |           renderPagination()
282 |         )}
283 |       </PaginationContext.Provider>
284 |     </Fragment>
285 |   )
286 | }
287 | 
288 | const omit = (props: any, keys?: string[]) => {
289 |   return Object.keys(props)
290 |     .filter((key) => !keys?.includes(key))
291 |     .reduce((buf, key) => {
292 |       buf[key] = props[key]
293 |       return buf
294 |     }, {})
295 | }
296 | 
297 | export const ArrayTable: ComposedArrayTable = observer(
298 |   (props: ExtendTableProps) => {
299 |     const ref = useRef<HTMLDivElement>()
300 |     const field = useField<ArrayField>()
301 |     const prefixCls = usePrefixCls('formily-array-table')
302 |     const dataSource = Array.isArray(field.value) ? field.value.slice() : []
303 |     const sources = useArrayTableSources()
304 |     const columns = useArrayTableColumns(dataSource, field, sources)
305 |     const pagination = isBool(props.pagination) ? {} : props.pagination
306 |     const { onAdd, onCopy, onRemove, onMoveDown, onMoveUp } = props
307 |     const addition = useAddition()
308 | 
309 |     return (
310 |       <ArrayTablePagination {...pagination} dataSource={dataSource}>
311 |         {(dataSource, pager) => (
312 |           <div ref={ref} className={prefixCls}>
313 |             <ArrayBase
314 |               onAdd={onAdd}
315 |               onCopy={onCopy}
316 |               onRemove={onRemove}
317 |               onMoveUp={onMoveUp}
318 |               onMoveDown={onMoveDown}
319 |             >
320 |               <Table
321 |                 size="small"
322 |                 {...omit(props, ['value', 'onChange', 'pagination'])}
323 |                 columns={columns}
324 |                 dataSource={dataSource}
325 |               />
326 |               <div style={{ marginTop: 5, marginBottom: 5 }}>{pager}</div>
327 |               {sources.map((column, key) => {
328 |                 //专门用来承接对Column的状态管理
329 |                 if (!isColumnComponent(column.schema)) return
330 |                 return React.createElement(RecursionField, {
331 |                   name: column.name,
332 |                   schema: column.schema,
333 |                   onlyRenderSelf: true,
334 |                   key,
335 |                 })
336 |               })}
337 |               {addition}
338 |             </ArrayBase>
339 |           </div>
340 |         )}
341 |       </ArrayTablePagination>
342 |     )
343 |   }
344 | )
345 | 
346 | ArrayTable.displayName = 'ArrayTable'
347 | 
348 | ArrayTable.Column = () => {
349 |   return <Fragment />
350 | }
351 | 
352 | ArrayBase.mixin(ArrayTable)
353 | 
354 | const Addition: ArrayBaseMixins['Addition'] = (props) => {
355 |   const array = ArrayBase.useArray()
356 |   const { totalPage = 0, pageSize = 10, changePage } = usePagination()
357 |   return (
358 |     <ArrayBase.Addition
359 |       {...props}
360 |       onClick={(e) => {
361 |         // 如果添加数据后将超过当前页,则自动切换到下一页
362 |         const total = array?.field?.value.length || 0
363 |         if (total === totalPage * pageSize + 1 && isFn(changePage)) {
364 |           changePage(totalPage + 1)
365 |         }
366 |         props.onClick?.(e)
367 |       }}
368 |     />
369 |   )
370 | }
371 | ArrayTable.Addition = Addition
372 | 
373 | export default ArrayTable
374 | 
```

--------------------------------------------------------------------------------
/packages/core/src/types.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import {
  2 |   IValidatorRules,
  3 |   Validator,
  4 |   ValidatorTriggerType,
  5 | } from '@formily/validator'
  6 | import { FormPath } from '@formily/shared'
  7 | import {
  8 |   Form,
  9 |   Field,
 10 |   LifeCycle,
 11 |   ArrayField,
 12 |   VoidField,
 13 |   ObjectField,
 14 |   Query,
 15 | } from './models'
 16 | 
 17 | export type NonFunctionPropertyNames<T> = {
 18 |   [K in keyof T]: T[K] extends (...args: any) => any ? never : K
 19 | }[keyof T]
 20 | 
 21 | export type NonFunctionProperties<T> = Pick<T, NonFunctionPropertyNames<T>>
 22 | 
 23 | export type AnyFunction = (...args: any[]) => any
 24 | 
 25 | export type JSXComponent = any
 26 | 
 27 | export type LifeCycleHandler<T> = (payload: T, context: any) => void
 28 | 
 29 | export type LifeCyclePayload<T> = (
 30 |   params: {
 31 |     type: string
 32 |     payload: T
 33 |   },
 34 |   context: any
 35 | ) => void
 36 | 
 37 | export enum LifeCycleTypes {
 38 |   /**
 39 |    * Form LifeCycle
 40 |    **/
 41 | 
 42 |   ON_FORM_INIT = 'onFormInit',
 43 |   ON_FORM_MOUNT = 'onFormMount',
 44 |   ON_FORM_UNMOUNT = 'onFormUnmount',
 45 | 
 46 |   ON_FORM_INPUT_CHANGE = 'onFormInputChange',
 47 |   ON_FORM_VALUES_CHANGE = 'onFormValuesChange',
 48 |   ON_FORM_INITIAL_VALUES_CHANGE = 'onFormInitialValuesChange',
 49 | 
 50 |   ON_FORM_SUBMIT = 'onFormSubmit',
 51 |   ON_FORM_RESET = 'onFormReset',
 52 |   ON_FORM_SUBMIT_START = 'onFormSubmitStart',
 53 |   ON_FORM_SUBMITTING = 'onFormSubmitting',
 54 |   ON_FORM_SUBMIT_END = 'onFormSubmitEnd',
 55 |   ON_FORM_SUBMIT_VALIDATE_START = 'onFormSubmitValidateStart',
 56 |   ON_FORM_SUBMIT_VALIDATE_SUCCESS = 'onFormSubmitValidateSuccess',
 57 |   ON_FORM_SUBMIT_VALIDATE_FAILED = 'onFormSubmitValidateFailed',
 58 |   ON_FORM_SUBMIT_VALIDATE_END = 'onFormSubmitValidateEnd',
 59 |   ON_FORM_SUBMIT_SUCCESS = 'onFormSubmitSuccess',
 60 |   ON_FORM_SUBMIT_FAILED = 'onFormSubmitFailed',
 61 |   ON_FORM_VALIDATE_START = 'onFormValidateStart',
 62 |   ON_FORM_VALIDATING = 'onFormValidating',
 63 |   ON_FORM_VALIDATE_SUCCESS = 'onFormValidateSuccess',
 64 |   ON_FORM_VALIDATE_FAILED = 'onFormValidateFailed',
 65 |   ON_FORM_VALIDATE_END = 'onFormValidateEnd',
 66 | 
 67 |   ON_FORM_GRAPH_CHANGE = 'onFormGraphChange',
 68 |   ON_FORM_LOADING = 'onFormLoading',
 69 | 
 70 |   /**
 71 |    * Field LifeCycle
 72 |    **/
 73 | 
 74 |   ON_FIELD_INIT = 'onFieldInit',
 75 |   ON_FIELD_INPUT_VALUE_CHANGE = 'onFieldInputValueChange',
 76 |   ON_FIELD_VALUE_CHANGE = 'onFieldValueChange',
 77 |   ON_FIELD_INITIAL_VALUE_CHANGE = 'onFieldInitialValueChange',
 78 | 
 79 |   ON_FIELD_SUBMIT = 'onFieldSubmit',
 80 |   ON_FIELD_SUBMIT_START = 'onFieldSubmitStart',
 81 |   ON_FIELD_SUBMITTING = 'onFieldSubmitting',
 82 |   ON_FIELD_SUBMIT_END = 'onFieldSubmitEnd',
 83 |   ON_FIELD_SUBMIT_VALIDATE_START = 'onFieldSubmitValidateStart',
 84 |   ON_FIELD_SUBMIT_VALIDATE_SUCCESS = 'onFieldSubmitValidateSuccess',
 85 |   ON_FIELD_SUBMIT_VALIDATE_FAILED = 'onFieldSubmitValidateFailed',
 86 |   ON_FIELD_SUBMIT_VALIDATE_END = 'onFieldSubmitValidateEnd',
 87 |   ON_FIELD_SUBMIT_SUCCESS = 'onFieldSubmitSuccess',
 88 |   ON_FIELD_SUBMIT_FAILED = 'onFieldSubmitFailed',
 89 |   ON_FIELD_VALIDATE_START = 'onFieldValidateStart',
 90 |   ON_FIELD_VALIDATING = 'onFieldValidating',
 91 |   ON_FIELD_VALIDATE_SUCCESS = 'onFieldValidateSuccess',
 92 |   ON_FIELD_VALIDATE_FAILED = 'onFieldValidateFailed',
 93 |   ON_FIELD_VALIDATE_END = 'onFieldValidateEnd',
 94 | 
 95 |   ON_FIELD_LOADING = 'onFieldLoading',
 96 |   ON_FIELD_RESET = 'onFieldReset',
 97 |   ON_FIELD_MOUNT = 'onFieldMount',
 98 |   ON_FIELD_UNMOUNT = 'onFieldUnmount',
 99 | }
100 | 
101 | export type HeartSubscriber = ({
102 |   type,
103 |   payload,
104 | }: {
105 |   type: string
106 |   payload: any
107 | }) => void
108 | 
109 | export interface INodePatch<T> {
110 |   type: 'remove' | 'update'
111 |   address: string
112 |   oldAddress?: string
113 |   payload?: T
114 | }
115 | 
116 | export interface IHeartProps<Context> {
117 |   lifecycles?: LifeCycle[]
118 |   context?: Context
119 | }
120 | 
121 | export interface IFieldFeedback {
122 |   triggerType?: FieldFeedbackTriggerTypes
123 |   type?: FieldFeedbackTypes
124 |   code?: FieldFeedbackCodeTypes
125 |   messages?: FeedbackMessage
126 | }
127 | 
128 | export type IFormFeedback = IFieldFeedback & {
129 |   path?: string
130 |   address?: string
131 | }
132 | 
133 | export interface ISearchFeedback {
134 |   triggerType?: FieldFeedbackTriggerTypes
135 |   type?: FieldFeedbackTypes
136 |   code?: FieldFeedbackCodeTypes
137 |   address?: FormPathPattern
138 |   path?: FormPathPattern
139 |   messages?: FeedbackMessage
140 | }
141 | 
142 | export type FeedbackMessage = any[]
143 | 
144 | export type IFieldUpdate = {
145 |   pattern: FormPath
146 |   callbacks: ((...args: any[]) => any)[]
147 | }
148 | 
149 | export interface IFormRequests {
150 |   validate?: number
151 |   submit?: number
152 |   loading?: number
153 |   updates?: IFieldUpdate[]
154 |   updateIndexes?: Record<string, number>
155 | }
156 | 
157 | export type IFormFields = Record<string, GeneralField>
158 | 
159 | export type FieldFeedbackTypes = 'error' | 'success' | 'warning'
160 | 
161 | export type FieldFeedbackTriggerTypes = ValidatorTriggerType
162 | 
163 | export type FieldFeedbackCodeTypes =
164 |   | 'ValidateError'
165 |   | 'ValidateSuccess'
166 |   | 'ValidateWarning'
167 |   | 'EffectError'
168 |   | 'EffectSuccess'
169 |   | 'EffectWarning'
170 |   | (string & {})
171 | 
172 | export type FormPatternTypes =
173 |   | 'editable'
174 |   | 'readOnly'
175 |   | 'disabled'
176 |   | 'readPretty'
177 |   | ({} & string)
178 | export type FormDisplayTypes = 'none' | 'hidden' | 'visible' | ({} & string)
179 | 
180 | export type FormPathPattern =
181 |   | string
182 |   | number
183 |   | Array<string | number>
184 |   | FormPath
185 |   | RegExp
186 |   | (((address: Array<string | number>) => boolean) & {
187 |       path: FormPath
188 |     })
189 | 
190 | type OmitState<P> = Omit<
191 |   P,
192 |   | 'selfDisplay'
193 |   | 'selfPattern'
194 |   | 'originValues'
195 |   | 'originInitialValues'
196 |   | 'id'
197 |   | 'address'
198 |   | 'path'
199 |   | 'lifecycles'
200 |   | 'disposers'
201 |   | 'requests'
202 |   | 'fields'
203 |   | 'graph'
204 |   | 'heart'
205 |   | 'indexes'
206 |   | 'props'
207 |   | 'displayName'
208 |   | 'setState'
209 |   | 'getState'
210 |   | 'getFormGraph'
211 |   | 'setFormGraph'
212 |   | 'setFormState'
213 |   | 'getFormState'
214 | >
215 | 
216 | export type IFieldState = Partial<
217 |   Pick<
218 |     Field,
219 |     NonFunctionPropertyNames<OmitState<Field<any, any, string, string>>>
220 |   >
221 | >
222 | 
223 | export type IVoidFieldState = Partial<
224 |   Pick<
225 |     VoidField,
226 |     NonFunctionPropertyNames<OmitState<VoidField<any, any, string>>>
227 |   >
228 | >
229 | 
230 | export type IFormState<T extends Record<any, any> = any> = Pick<
231 |   Form<T>,
232 |   NonFunctionPropertyNames<OmitState<Form<{ [key: string]: any }>>>
233 | >
234 | 
235 | export type IFormGraph = Record<string, IGeneralFieldState | IFormState>
236 | 
237 | export interface IFormProps<T extends object = any> {
238 |   values?: Partial<T>
239 |   initialValues?: Partial<T>
240 |   pattern?: FormPatternTypes
241 |   display?: FormDisplayTypes
242 |   hidden?: boolean
243 |   visible?: boolean
244 |   editable?: boolean
245 |   disabled?: boolean
246 |   readOnly?: boolean
247 |   readPretty?: boolean
248 |   effects?: (form: Form<T>) => void
249 |   validateFirst?: boolean
250 |   validatePattern?: FormPatternTypes[]
251 |   validateDisplay?: FormDisplayTypes[]
252 |   designable?: boolean
253 | }
254 | 
255 | export type IFormMergeStrategy =
256 |   | 'overwrite'
257 |   | 'merge'
258 |   | 'deepMerge'
259 |   | 'shallowMerge'
260 | 
261 | export interface IFieldFactoryProps<
262 |   Decorator extends JSXComponent,
263 |   Component extends JSXComponent,
264 |   TextType = any,
265 |   ValueType = any
266 | > extends IFieldProps<Decorator, Component, TextType, ValueType> {
267 |   name: FormPathPattern
268 |   basePath?: FormPathPattern
269 | }
270 | 
271 | export interface IVoidFieldFactoryProps<
272 |   Decorator extends JSXComponent,
273 |   Component extends JSXComponent,
274 |   TextType = any
275 | > extends IVoidFieldProps<Decorator, Component, TextType> {
276 |   name: FormPathPattern
277 |   basePath?: FormPathPattern
278 | }
279 | 
280 | export interface IFieldRequests {
281 |   validate?: number
282 |   submit?: number
283 |   loading?: number
284 |   batch?: () => void
285 | }
286 | 
287 | export interface IFieldCaches {
288 |   value?: any
289 |   initialValue?: any
290 |   inputting?: boolean
291 | }
292 | 
293 | export type FieldDisplayTypes = 'none' | 'hidden' | 'visible' | ({} & string)
294 | 
295 | export type FieldPatternTypes =
296 |   | 'editable'
297 |   | 'readOnly'
298 |   | 'disabled'
299 |   | 'readPretty'
300 |   | ({} & string)
301 | 
302 | export type FieldValidatorContext = IValidatorRules & {
303 |   field?: Field
304 |   form?: Form
305 |   value?: any
306 | }
307 | 
308 | export type FieldValidator = Validator<FieldValidatorContext>
309 | 
310 | export type FieldDataSource = {
311 |   label?: any
312 |   value?: any
313 |   title?: any
314 |   key?: any
315 |   text?: any
316 |   children?: FieldDataSource
317 |   [key: string]: any
318 | }[]
319 | 
320 | export type FieldComponent<
321 |   Component extends JSXComponent,
322 |   ComponentProps = any
323 | > = [Component] | [Component, ComponentProps] | boolean | any[]
324 | 
325 | export type FieldDecorator<
326 |   Decorator extends JSXComponent,
327 |   ComponentProps = any
328 | > = [Decorator] | [Decorator, ComponentProps] | boolean | any[]
329 | 
330 | export type FieldReaction = (field: Field) => void
331 | export interface IFieldProps<
332 |   Decorator extends JSXComponent = any,
333 |   Component extends JSXComponent = any,
334 |   TextType = any,
335 |   ValueType = any
336 | > {
337 |   name: FormPathPattern
338 |   basePath?: FormPathPattern
339 |   title?: TextType
340 |   description?: TextType
341 |   value?: ValueType
342 |   initialValue?: ValueType
343 |   required?: boolean
344 |   display?: FieldDisplayTypes
345 |   pattern?: FieldPatternTypes
346 |   hidden?: boolean
347 |   visible?: boolean
348 |   editable?: boolean
349 |   disabled?: boolean
350 |   readOnly?: boolean
351 |   readPretty?: boolean
352 |   dataSource?: FieldDataSource
353 |   validateFirst?: boolean
354 |   validatePattern?: FieldPatternTypes[]
355 |   validateDisplay?: FieldDisplayTypes[]
356 |   validator?: FieldValidator
357 |   decorator?: FieldDecorator<Decorator>
358 |   component?: FieldComponent<Component>
359 |   reactions?: FieldReaction[] | FieldReaction
360 |   content?: any
361 |   data?: any
362 | }
363 | 
364 | export interface IVoidFieldProps<
365 |   Decorator extends JSXComponent = any,
366 |   Component extends JSXComponent = any,
367 |   TextType = any
368 | > {
369 |   name: FormPathPattern
370 |   basePath?: FormPathPattern
371 |   title?: TextType
372 |   description?: TextType
373 |   display?: FieldDisplayTypes
374 |   pattern?: FieldPatternTypes
375 |   hidden?: boolean
376 |   visible?: boolean
377 |   editable?: boolean
378 |   disabled?: boolean
379 |   readOnly?: boolean
380 |   readPretty?: boolean
381 |   decorator?: FieldDecorator<Decorator>
382 |   component?: FieldComponent<Component>
383 |   reactions?: FieldReaction[] | FieldReaction
384 |   content?: any
385 |   data?: any
386 | }
387 | 
388 | export interface IFieldResetOptions {
389 |   forceClear?: boolean
390 |   validate?: boolean
391 | }
392 | 
393 | export type IGeneralFieldState = IFieldState & IVoidFieldState
394 | 
395 | export type GeneralField = Field | VoidField | ArrayField | ObjectField
396 | 
397 | export type DataField = Field | ArrayField | ObjectField
398 | export interface ISpliceArrayStateProps {
399 |   startIndex?: number
400 |   deleteCount?: number
401 |   insertCount?: number
402 | }
403 | 
404 | export interface IExchangeArrayStateProps {
405 |   fromIndex?: number
406 |   toIndex?: number
407 | }
408 | 
409 | export interface IQueryProps {
410 |   pattern: FormPathPattern
411 |   base: FormPathPattern
412 |   form: Form
413 | }
414 | 
415 | export interface IModelSetter<P = any> {
416 |   (setter: (state: P) => void): void
417 |   (setter: Partial<P>): void
418 |   (): void
419 | }
420 | 
421 | export interface IModelGetter<P = any> {
422 |   <Getter extends (state: P) => any>(getter: Getter): ReturnType<Getter>
423 |   (): P
424 | }
425 | 
426 | export type FieldMatchPattern = FormPathPattern | Query | GeneralField
427 | 
428 | export interface IFieldStateSetter {
429 |   (pattern: FieldMatchPattern, setter: (state: IFieldState) => void): void
430 |   (pattern: FieldMatchPattern, setter: Partial<IFieldState>): void
431 | }
432 | 
433 | export interface IFieldStateGetter {
434 |   <Getter extends (state: IGeneralFieldState) => any>(
435 |     pattern: FieldMatchPattern,
436 |     getter: Getter
437 |   ): ReturnType<Getter>
438 |   (pattern: FieldMatchPattern): IGeneralFieldState
439 | }
440 | 
441 | export interface IFieldActions {
442 |   [key: string]: (...args: any[]) => any
443 | }
444 | 
```

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

```markdown
  1 | # Form model
  2 | 
  3 | Earlier I talked about the overall architecture of the Formily kernel, and also talked about MVVM. You should also be able to roughly understand what Formily's form model is. Let's take a deeper look at the specific domain logic of the form model, which is mainly biased. Concluding content, if you don't understand it the first time, you can go directly to the API documentation, and come back after reading it, you can deepen your understanding of formily.
  4 | 
  5 | ## Combing
  6 | 
  7 | The entire form model is very large and complicated. In fact, the core of the decomposition is the following sub-models:
  8 | 
  9 | - Field management model
 10 | - Field model
 11 | - Data model
 12 | - Linkage model
 13 | - Path system
 14 | 
 15 | Let's talk about how the form model is managed in detail below.
 16 | 
 17 | ## Field Management Model
 18 | 
 19 | The field management model mainly includes:
 20 | 
 21 | - Field addition
 22 | - Field query
 23 | - Import field set
 24 | - Export field set
 25 | - Clear the field set
 26 | 
 27 | #### Field addition
 28 | 
 29 | The field is created mainly through the createField/createArrayField/createObjectField/createVoidField method. If the field already exists, it will not be created repeatedly
 30 | 
 31 | #### Field query
 32 | 
 33 | The query method is mainly used to query the field. The query method can pass in the path of the field or regular expression to match the field.
 34 | 
 35 | Because the detailed rules of the field path are still more complicated, they will be explained in detail in the following [Path System](/api/entry/form-path) article.
 36 | 
 37 | Then calling the query method will return a Query object. The Query object can have a forEach/map/reduce method that traverses all fields in batches, or a take method that takes only the first field that is queried, as well as direct reading of fields. The get method of properties, and the getIn method that can read field properties in depth, the difference between the two methods is that the former can have smart prompts, and the latter has no prompts, so it is recommended that users use the get method.
 38 | 
 39 | #### Import field set
 40 | 
 41 | The field set is imported mainly through setFormGraph. The input parameter format is a flat object format, the key is the absolute path of the field, and the value is the state of the field. Use this API to import the Immutable field state into the form in some scenarios that require time travel. In the model.
 42 | 
 43 | #### Export field set
 44 | 
 45 | The field set is mainly exported through getFormGraph. The export format is a flat object format, the key is the absolute path of the field, and the value is the state of the field, which is consistent with the imported field set input parameters. Because the returned data is an Immutable data, it is OK Completely persistent storage, convenient for time travel.
 46 | 
 47 | #### Clear the field set
 48 | 
 49 | The field set is cleared mainly through clearFormGraph.
 50 | 
 51 | ## Field Model
 52 | 
 53 | The field model mainly includes:
 54 | 
 55 | - Field model, which is mainly responsible for managing the state of non-incremental fields, such as Input/Select/NumberPicker/DatePicker components
 56 | - The ArrayField model is mainly responsible for managing the state of the auto-increment list field, and can add, delete, and move list items.
 57 | - ObjectField model, which is mainly responsible for managing the state of auto-incremented object fields, and can add or delete the key of the object.
 58 | - The VoidField model is mainly responsible for managing the state of the virtual field. The virtual field is a node that does not pollute the form data, but it can control the display and hiding of its child nodes, and the interactive mode.
 59 | 
 60 | Because the field model is very complicated, it will be explained in detail in the following [Field Model](/guide/field) article.
 61 | 
 62 | ## Data Model
 63 | 
 64 | For the form data model, the previous version of Formily will more or less have some boundary problems. After reorganizing a version in 2.x, it really broke through the previous legacy problems.
 65 | 
 66 | The data model mainly includes:
 67 | 
 68 | - Form values (values) management
 69 | - Form default value (initialValues) management
 70 | - Field value (value) management
 71 | - Field default value (initialValue) management
 72 | - Value and default value selection merge strategy
 73 | 
 74 | Form value management is actually the values attribute of an object structure, but it is an @formily/reactive observable attribute. At the same time, with the help of @formily/reactive's deep observer capability, it monitors any attribute changes, and if it changes, it will trigger The life cycle hook of onFormValuesChange.
 75 | 
 76 | In the same way, the default value management is actually the initialValues property of an object structure. It will also deeply monitor property changes and trigger the onFormInitialValues life cycle hook.
 77 | 
 78 | Field value management is reflected in the value attribute of each data type field. Formily will maintain a data path attribute called path for each field, and then read and write values are all read and write the values of the top-level form. This ensures that the value of the field and the value of the form are absolutely idempotent, and the default value of the field is the same.
 79 | 
 80 | To sum up, the management of **values is all managed on the top-level form, and the value of the field and the value of the form are absolutely idempotent through path. **
 81 | 
 82 | <Alert>
 83 | 
 84 | The difference between the value and the default value is actually whether the field will be reset to the default value state when the form is reset
 85 | 
 86 | </Alert>
 87 | 
 88 | #### Value and default value selection merge strategy
 89 | 
 90 | Usually, in the process of business development, there is always a need for data echo. This data is generally used as an asynchronous default value. If it is used as a detail page, it is okay, but as an editing page, there will be some problems. :
 91 | 
 92 | **There is a conflict**
 93 | 
 94 | For example, the form value is `{xx:123}`, and the default form value is `{xx:321}`. The strategy here is:
 95 | 
 96 | - If `xx` does not have a corresponding field model, it means it is just redundant data and cannot be modified by the user
 97 |   - If the form value is assigned first, and the default value is assigned later, then the default value directly overrides the form value. This scenario is suitable for asynchronous data echo scenarios. Different business states have different default data echoes, and the data is finally submitted.` {xx:321}`
 98 |   - If the default value is assigned first, and the form value is assigned later, the form value directly overrides the default value. This scenario is suitable for synchronizing the default value and finally submitting the data `{xx:123}`
 99 | - If `xx` has a field model
100 |   - If the form value is assigned first, the default value is assigned later
101 |     - If the current field has been modified by the user (modified is true), then the default value cannot overwrite the form value, and finally submit the data `{xx:123}`
102 |     - If the current field has not been modified by the user (modified is false), then the default value will directly override the field value. This scenario is suitable for asynchronous data echo scenarios. Different business states have different default data echoes, and the data is finally submitted `{xx:321}`
103 |   - If the default value is assigned first, and the form value is assigned later, the form value directly overrides the default value. This scenario is suitable for synchronizing the default value and finally submitting the data `{xx:123}`
104 | 
105 | **No conflicts**
106 | 
107 | For example, the form value is `{xx:123}`, and the default form value is `{yy:321}`. The strategy here is to merge directly.
108 | 
109 | To sum up, the selection and merging strategy of the value and the default value, the core is to see whether the field has been modified by the user, everything is subject to the user, if it has not been modified by the user, the order of assignment shall prevail.\*\*
110 | 
111 | <Alert>
112 | 
113 | The default value mentioned here can be assigned repeatedly, and it is also the question of whether to discard the value in the process of repeated assignment.
114 | 
115 | </Alert>
116 | 
117 | ## Validation model
118 | 
119 | The core of the form verification model is to verify the validity of the data, and then manage the verification results, so the verification model mainly includes:
120 | 
121 | - Validation rule management
122 | - Calibration result management
123 | 
124 | Because the verification model belongs to the field model, it will be explained in detail in the following [Field Model](/guide/field#Verification Rules)
125 | 
126 | ## Linkage model
127 | 
128 | The core of the linkage model in formily1.x is the active linkage model, which is roughly expressed in one sentence:
129 | 
130 | ```ts
131 | setFieldState(Subscribe(FormLifeCycle, Selector(Path)), TargetState)
132 | ```
133 | 
134 | The explanation is that any linkage is based on a certain life cycle hook of the form to trigger the state of the field under the specified path. Such a model can solve many problems, but it also has an obvious problem, which is the many-to-one linkage. In the scenario where you need to monitor changes in multiple fields at the same time to control the state of a field, the implementation cost is still relatively high for users, especially to achieve some calculator linkage requirements, and the amount of code increases sharply. Of course, for one-to-many scenarios, this model is the most efficient.
135 | 
136 | Therefore, in formily 2.x, a passive linkage model is added to the active linkage model, which is also an expression:
137 | 
138 | ```ts
139 | subscribe(Dependencies, Reactions)
140 | ```
141 | 
142 | Simplified a lot, the core is to respond to dependent data changes. The dependent data can be form model attributes or attributes of any field model. The response action can be to change the attributes of any field model or do other asynchronous actions. . Such a model is also a complete linkage model, but in a one-to-many scenario, the implementation cost will be higher than the active model.
143 | 
144 | Therefore, the two linkage models require users to choose according to their own needs.
145 | 
146 | ## Path system
147 | 
148 | The path system is very important. The path system is used everywhere in almost the entire form model. It mainly provides the following capabilities for the form model:
149 | 
150 | - It can be used to find any field from the field set, and it also supports batch search according to the rules
151 | - It can be used to express the model of the relationship between the fields. With the help of the path system, we can find the father of a certain field, can find the father, and can also realize the data inheritance ability of the tree level. Similarly, we can also find the data of a certain field. Adjacent node
152 | - It can be used to read and write field data, read and write data with deconstruction
153 | 
154 | The entire path system is actually implemented based on the path DSL of @formily/path. If you want to know more about the path system, you can take a look at [FormPath API](/api/entry/form-path) in detail
155 | 
```

--------------------------------------------------------------------------------
/packages/react/docs/guide/concept.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Core idea
  2 | 
  3 | The architecture of @formily/react itself is not complicated, because it only provides a series of components and Hooks for users to use, but we still need to understand the following concepts:
  4 | 
  5 | - Form context
  6 | - Field context
  7 | - Protocol context
  8 | - Model binding
  9 | - Protocol driven
 10 | - Three development modes
 11 | 
 12 | ## Form context
 13 | 
 14 | From the [architecture diagram](/guide/architecture) we can see that FormProvider exists as a unified context for forms, and its position is very important. It is mainly used to create [Form](//core. formilyjs.org/api/models/form) instances are distributed to all sub-components, whether in built-in components or user-extended components, can be read through [useForm](/api/hooks/use-form) [ Form](//core.formilyjs.org/api/models/form) instance
 15 | 
 16 | ## Field context
 17 | 
 18 | From the [architecture diagram](/guide/architecture) we can see that whether it is Field/ArrayField/ObjectField/VoidField, a FieldContext will be issued to the subtree. We can read the current field model in the custom component, mainly Use [useField](/api/hooks/use-field) to read, which is very convenient for model mapping
 19 | 
 20 | ## Protocol context
 21 | 
 22 | From the [architecture diagram](/guide/architecture) we can see that [RecursionField](/api/components/recursion-field) will send a FieldSchemaContext to the subtree, and we can read the current field in the custom component The Schema description is mainly read using [useFieldSchema](/api/hooks/useFieldSchema). Note that this Hook can only be used in the [SchemaField](/api/components/SchemaField) and [RecursionField](/api/components/recursion-field) subtrees
 23 | 
 24 | ## Model binding
 25 | 
 26 | To understand model binding, you need to understand what [MVVM](//core.formilyjs.org/guide/mvvm) is. After understanding, let’s take a look at this picture:
 27 | 
 28 | ![](https://img.alicdn.com/imgextra/i1/O1CN01A03C191KwT1raxnDg_!!6000000001228-55-tps-2200-869.svg)
 29 | 
 30 | In Formily, @formily/core is ViewModel, Component and Decorator are View, @formily/react is the glue layer that binds ViewModel and View, and the binding of ViewModel and View is called model binding, which implements model binding. The main methods are [useField](/api/hooks/use-field), and [connect](/api/shared/connect) and [mapProps](/api/shared/map-props) can also be used. Note that Component only needs to support the value/onChange property to automatically realize the two-way binding of the data layer.
 31 | 
 32 | ## JSON Schema Driver
 33 | 
 34 | Protocol-driven rendering is the most expensive part of @formily/react, but after learning it, the benefits it brings to the business are also very high. A total of 4 core concepts need to be understood:
 35 | 
 36 | - Schema
 37 | - Recursive rendering
 38 | - Protocol binding
 39 | - Three development modes
 40 | 
 41 | ### Schema
 42 | 
 43 | Formily’s protocol driver is mainly based on the standard JSON Schema to drive rendering. At the same time, we have extended some `x-*` attributes to express the UI on top of the standard, so that the entire protocol can fully describe a complex form. Schema protocol, refer to [Schema](/api/shared/schema) API document
 44 | 
 45 | ### Recursive rendering
 46 | 
 47 | What is recursive rendering? Recursive rendering means that component A will continue to use component A to render content under certain conditions. Take a look at the following pseudo code:
 48 | 
 49 | ```json
 50 | {<---- RecursionField (condition: object; rendering right: RecursionField)
 51 |   "type":"object",
 52 |   "properties":{
 53 |     "username":{ <---- RecursionField (condition: string; rendering right: RecursionField)
 54 |       "type":"string",
 55 |       "x-component":"Input"
 56 |     },
 57 |     "phone":{ <---- RecursionField (condition: string; rendering right: RecursionField)
 58 |       "type":"string",
 59 |       "x-component":"Input",
 60 |       "x-validator":"phone"
 61 |     },
 62 |     "email":{ <---- RecursionField (condition: string; rendering right: RecursionField)
 63 |       "type":"string",
 64 |       "x-component":"Input",
 65 |       "x-validator":"email"
 66 |     },
 67 |     "contacts":{ <---- RecursionField (condition: array; rendering right: RecursionField)
 68 |       "type":"array",
 69 |       "x-component":"ArrayTable",
 70 |       "items":{ <---- RecursionField (condition: object; rendering rights: ArrayTable component)
 71 |         "type":"object",
 72 |         "properties":{
 73 |           "username":{ <---- RecursionField (condition: string; rendering right: RecursionField)
 74 |             "type":"string",
 75 |             "x-component":"Input"
 76 |           },
 77 |           "phone":{ <---- RecursionField (condition: string; rendering right: RecursionField)
 78 |             "type":"string",
 79 |             "x-component":"Input",
 80 |             "x-validator":"phone"
 81 |           },
 82 |           "email":{ <---- RecursionField (condition: string; rendering right: RecursionField)
 83 |             "type":"string",
 84 |             "x-component":"Input",
 85 |             "x-validator":"email"
 86 |           },
 87 |         }
 88 |       }
 89 |     }
 90 |   }
 91 | }
 92 | ```
 93 | 
 94 | @formily/react The entry point for recursive rendering is [SchemaField](/api/components/schema-field), but it actually uses [RecursionField](/api/components/recursion-field) to render internally, because of JSON-Schema It is a recursive structure, so [RecursionField](/api/components/recursion-field) will be parsed from the top-level Schema node when rendering. If it is a non-object and array type, it will directly render the specific component. If it is an object, it will traverse. properties Continue to use [RecursionField](/api/components/recursion-field) to render child Schema nodes.
 95 | 
 96 | A special case here is the rendering of the array type auto-increment list, which requires the user to use [RecursionField](/api/components/recursion-field) in the custom component for recursive rendering, because the UI of the auto-increment list is very customized High, so the recursive rendering rights are handed over to the user to render, so the design can also make protocol-driven rendering more flexible.
 97 | 
 98 | What is the difference between SchemaField and RecursionField? There are two main points:
 99 | 
100 | - SchemaField supports Markup grammar, it will parse Markup grammar in advance to generate [JSON Schema](/api/shared/schema) and transfer it to RecursionField for rendering, so RecursionField can only be rendered based on [JSON Schema](/api/shared/schema)
101 | - SchemaField renders the overall Schema protocol, while RecursionField renders the partial Schema protocol
102 | 
103 | ### Protocol binding
104 | 
105 | I talked about model binding, and protocol binding is the process of converting Schema protocol into model binding, because JSON-Schema protocol is a JSON string and can be stored offline, while model binding is a binding between memory The relationship is at the Runtime layer. For example, `x-component` is the string identifier of the component in the Schema, but the component in the model requires component reference, so the JSON string and the Runtime layer need to be converted. Then we can continue to improve the above model binding diagram:
106 | 
107 | ![](https://img.alicdn.com/imgextra/i3/O1CN01jLCRxH1aa3V0x6nw4_!!6000000003345-55-tps-2200-1147.svg)
108 | 
109 | To sum up, in @formily/react, there are mainly two layers of binding relationships, Schema binding model, model binding component, the glue layer that realizes the binding is @formily/react, it should be noted that Schema binds the field model After that, the Schema is not perceptible in the field model. For example, if you want to modify the `enum`, you need to modify the `dataSource` attribute in the field model. In short, if you want to update the field model, refer to [Field](//core.formilyjs. org/api/models/field), you can refer to [Schema](/api/shared/schema) document if you want to understand the mapping relationship between Schema and field model
110 | 
111 | ## Three development models
112 | 
113 | From the [architecture diagram](/guide/architecture), we have actually seen that the entire @formily/react has three development modes, corresponding to different users:
114 | 
115 | - JSX development model
116 | - JSON Schema development mode
117 | - Markup Schema development mode
118 | 
119 | We can look at specific examples
120 | 
121 | #### JSX development model
122 | 
123 | This mode mainly uses Field/ArrayField/ObjectField/VoidField components
124 | 
125 | ```tsx
126 | import React from 'react'
127 | import { createForm } from '@formily/core'
128 | import { FormProvider, Field } from '@formily/react'
129 | import { Input } from 'antd'
130 | 
131 | const form = createForm()
132 | 
133 | export default () => (
134 |   <FormProvider form={form}>
135 |     <Field name="input" component={[Input, { placeholder: 'Please enter' }]} />
136 |   </FormProvider>
137 | )
138 | ```
139 | 
140 | #### JSON Schema Development Mode
141 | 
142 | This mode is to pass JSON Schema to the schema attribute of SchemaField
143 | 
144 | ```tsx
145 | import React from 'react'
146 | import { createForm } from '@formily/core'
147 | import { FormProvider, createSchemaField } from '@formily/react'
148 | import { Input } from 'antd'
149 | 
150 | const form = createForm()
151 | 
152 | const SchemaField = createSchemaField({
153 |   components: {
154 |     Input,
155 |   },
156 | })
157 | 
158 | export default () => (
159 |   <FormProvider form={form}>
160 |     <SchemaField
161 |       schema={{
162 |         type: 'object',
163 |         properties: {
164 |           input: {
165 |             type: 'string',
166 |             'x-component': 'Input',
167 |             'x-component-props': {
168 |               placeholder: 'Please enter',
169 |             },
170 |           },
171 |         },
172 |       }}
173 |     />
174 |   </FormProvider>
175 | )
176 | ```
177 | 
178 | #### Markup Schema Development Mode
179 | 
180 | This mode can be regarded as a Schema development mode that is more friendly to source code development, and it also uses the SchemaField component.
181 | 
182 | Because it is difficult to get the best smart prompt experience in the JSX environment with JSON Schema, and it is inconvenient to maintain, the maintainability in the form of tags will be better, and the smart prompt is also very strong.
183 | 
184 | Markup Schema mode mainly has the following characteristics:
185 | 
186 | - Mainly rely on description tags such as SchemaField.String/SchemaField.Array/SchemaField.Object... to express Schema
187 | - Each description tag represents a Schema node, which is equivalent to JSON-Schema
188 | - SchemaField child nodes cannot insert UI elements at will, because SchemaField will only parse all the Schema description tags of the child nodes, and then convert them into JSON Schema, and finally give it to [RecursionField](/api/components/recursion-field) for rendering, if you want Insert UI elements, you can upload the `x-content` attribute in VoidDield to insert UI elements
189 | 
190 | ```tsx
191 | import React from 'react'
192 | import { createForm } from '@formily/core'
193 | import { FormProvider, createSchemaField } from '@formily/react'
194 | import { Input } from 'antd'
195 | 
196 | const form = createForm()
197 | 
198 | const SchemaField = createSchemaField({
199 |   components: {
200 |     Input,
201 |   },
202 | })
203 | 
204 | export default () => (
205 |   <FormProvider form={form}>
206 |     <SchemaField>
207 |       <SchemaField.String
208 |         x-component="Input"
209 |         x-component-props={{ placeholder: 'Please enter' }}
210 |       />
211 |       <div>I will not be rendered</div>
212 |       <SchemaField.Void x-content={<div>I will be rendered</div>} />
213 |     </SchemaField>
214 |   </FormProvider>
215 | )
216 | ```
217 | 
```
Page 26/52FirstPrevNextLast