#
tokens: 46758/50000 5/1152 files (page 32/52)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 32 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/reactive/src/__tests__/autorun.spec.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { observable, reaction, autorun } from '../'
  2 | import { batch } from '../batch'
  3 | import { define } from '../model'
  4 | 
  5 | const sleep = (duration = 100) =>
  6 |   new Promise((resolve) => setTimeout(resolve, duration))
  7 | 
  8 | test('autorun', () => {
  9 |   const obs = observable({
 10 |     aa: {
 11 |       bb: 123,
 12 |     },
 13 |   })
 14 |   const handler = jest.fn()
 15 |   const dispose = autorun(() => {
 16 |     handler(obs.aa.bb)
 17 |   })
 18 |   obs.aa.bb = 123
 19 |   expect(handler).toBeCalledTimes(1)
 20 |   obs.aa.bb = 111
 21 |   expect(handler).toBeCalledTimes(2)
 22 |   dispose()
 23 |   obs.aa.bb = 222
 24 |   expect(handler).toBeCalledTimes(2)
 25 | })
 26 | 
 27 | test('reaction', () => {
 28 |   const obs = observable({
 29 |     aa: {
 30 |       bb: 123,
 31 |     },
 32 |   })
 33 |   const handler = jest.fn()
 34 |   const dispose = reaction(() => {
 35 |     return obs.aa.bb
 36 |   }, handler)
 37 |   obs.aa.bb = 123
 38 |   expect(handler).toBeCalledTimes(0)
 39 |   obs.aa.bb = 111
 40 |   expect(handler).toBeCalledTimes(1)
 41 |   dispose()
 42 |   obs.aa.bb = 222
 43 |   expect(handler).toBeCalledTimes(1)
 44 | })
 45 | 
 46 | test('reaction fireImmediately', () => {
 47 |   const obs = observable({
 48 |     aa: {
 49 |       bb: 123,
 50 |     },
 51 |   })
 52 |   const handler = jest.fn()
 53 |   const dispose = reaction(
 54 |     () => {
 55 |       return obs.aa.bb
 56 |     },
 57 |     handler,
 58 |     {
 59 |       fireImmediately: true,
 60 |     }
 61 |   )
 62 |   expect(handler).toBeCalledTimes(1)
 63 |   obs.aa.bb = 123
 64 |   expect(handler).toBeCalledTimes(1)
 65 |   obs.aa.bb = 111
 66 |   expect(handler).toBeCalledTimes(2)
 67 |   dispose()
 68 |   obs.aa.bb = 222
 69 |   expect(handler).toBeCalledTimes(2)
 70 | })
 71 | 
 72 | test('reaction untrack handler', () => {
 73 |   const obs = observable({
 74 |     aa: {
 75 |       bb: 123,
 76 |       cc: 123,
 77 |     },
 78 |   })
 79 |   const handler = jest.fn()
 80 |   const dispose = reaction(
 81 |     () => {
 82 |       return obs.aa.bb
 83 |     },
 84 |     () => {
 85 |       handler(obs.aa.cc)
 86 |     }
 87 |   )
 88 |   obs.aa.bb = 222
 89 |   obs.aa.cc = 222
 90 |   expect(handler).toBeCalledTimes(1)
 91 |   dispose()
 92 | })
 93 | 
 94 | test('reaction dirty check', () => {
 95 |   const obs: any = {
 96 |     aa: 123,
 97 |   }
 98 |   define(obs, {
 99 |     aa: observable.ref,
100 |   })
101 |   const handler = jest.fn()
102 |   reaction(() => {
103 |     return obs.aa
104 |   }, handler)
105 |   batch(() => {
106 |     obs.aa = 123
107 |     obs.aa = 123
108 |   })
109 | 
110 |   expect(handler).toBeCalledTimes(0)
111 | })
112 | 
113 | test('reaction with shallow equals', () => {
114 |   const obs: any = {
115 |     aa: { bb: 123 },
116 |   }
117 |   define(obs, {
118 |     aa: observable.ref,
119 |   })
120 |   const handler = jest.fn()
121 |   reaction(() => {
122 |     return obs.aa
123 |   }, handler)
124 |   obs.aa = { bb: 123 }
125 |   expect(handler).toBeCalledTimes(1)
126 |   expect(handler.mock.calls[0][0]).toEqual({ bb: 123 })
127 | })
128 | 
129 | test('reaction with deep equals', () => {
130 |   const obs: any = {
131 |     aa: { bb: 123 },
132 |   }
133 |   define(obs, {
134 |     aa: observable.ref,
135 |   })
136 |   const handler = jest.fn()
137 |   reaction(
138 |     () => {
139 |       return obs.aa
140 |     },
141 |     handler,
142 |     {
143 |       equals: (a, b) => JSON.stringify(a) === JSON.stringify(b),
144 |     }
145 |   )
146 |   obs.aa = { bb: 123 }
147 |   expect(handler).toBeCalledTimes(0)
148 | })
149 | 
150 | test('autorun direct recursive react', () => {
151 |   const obs = observable<any>({ value: 1 })
152 |   autorun(() => {
153 |     obs.value++
154 |   })
155 |   expect(obs.value).toEqual(2)
156 | })
157 | 
158 | test('autorun direct recursive react with if', () => {
159 |   const obs1 = observable<any>({})
160 |   const obs2 = observable<any>({})
161 |   const fn = jest.fn()
162 |   autorun(() => {
163 |     if (!obs1.value) {
164 |       obs1.value = '111'
165 |       return
166 |     }
167 |     fn(obs1.value, obs2.value)
168 |   })
169 |   obs2.value = '222'
170 |   expect(fn).toBeCalledTimes(0)
171 | })
172 | 
173 | test('autorun indirect recursive react', () => {
174 |   const obs1 = observable<any>({})
175 |   const obs2 = observable<any>({})
176 |   const obs3 = observable<any>({})
177 |   autorun(() => {
178 |     obs1.value = obs2.value + 1
179 |   })
180 |   autorun(() => {
181 |     obs2.value = obs3.value + 1
182 |   })
183 |   autorun(() => {
184 |     if (obs1.value) {
185 |       obs3.value = obs1.value + 1
186 |     } else {
187 |       obs3.value = 0
188 |     }
189 |   })
190 |   expect(obs2.value).toEqual(1)
191 |   expect(obs1.value).toEqual(2)
192 |   obs3.value = 1
193 |   expect(obs2.value).toEqual(2)
194 |   expect(obs1.value).toEqual(3)
195 | })
196 | 
197 | test('autorun indirect alive recursive react', () => {
198 |   const aa = observable<any>({})
199 |   const bb = observable<any>({})
200 |   const cc = observable<any>({})
201 | 
202 |   batch(() => {
203 |     autorun(() => {
204 |       if (aa.value) {
205 |         bb.value = aa.value + 1
206 |       }
207 |     })
208 |     autorun(() => {
209 |       if (aa.value && bb.value) {
210 |         cc.value = aa.value + bb.value
211 |       }
212 |     })
213 |     batch(() => {
214 |       aa.value = 1
215 |     })
216 |   })
217 |   expect(aa.value).toEqual(1)
218 |   expect(bb.value).toEqual(2)
219 |   expect(cc.value).toEqual(3)
220 | })
221 | 
222 | test('autorun direct recursive react with head track', () => {
223 |   const obs1 = observable<any>({})
224 |   const obs2 = observable<any>({})
225 |   const fn = jest.fn()
226 |   autorun(() => {
227 |     const obs2Value = obs2.value
228 |     if (!obs1.value) {
229 |       obs1.value = '111'
230 |       return
231 |     }
232 |     fn(obs1.value, obs2Value)
233 |   })
234 |   obs2.value = '222'
235 |   expect(fn).toBeCalledTimes(1)
236 |   expect(fn).lastCalledWith('111', '222')
237 | })
238 | 
239 | test('autorun.memo', () => {
240 |   const obs = observable<any>({
241 |     bb: 0,
242 |   })
243 |   const fn = jest.fn()
244 |   autorun(() => {
245 |     const value = autorun.memo(() => ({
246 |       aa: 0,
247 |     }))
248 |     fn(obs.bb, value.aa++)
249 |   })
250 |   obs.bb++
251 |   obs.bb++
252 |   obs.bb++
253 |   obs.bb++
254 |   expect(fn).toBeCalledTimes(5)
255 |   expect(fn).nthCalledWith(1, 0, 0)
256 |   expect(fn).nthCalledWith(2, 1, 1)
257 |   expect(fn).nthCalledWith(3, 2, 2)
258 |   expect(fn).nthCalledWith(4, 3, 3)
259 |   expect(fn).nthCalledWith(5, 4, 4)
260 | })
261 | 
262 | test('autorun.memo with observable', () => {
263 |   const obs1 = observable({
264 |     aa: 0,
265 |   })
266 |   const fn = jest.fn()
267 |   const dispose = autorun(() => {
268 |     const obs2 = autorun.memo(() =>
269 |       observable({
270 |         bb: 0,
271 |       })
272 |     )
273 |     fn(obs1.aa, obs2.bb++)
274 |   })
275 |   obs1.aa++
276 |   obs1.aa++
277 |   obs1.aa++
278 |   expect(fn).toBeCalledTimes(4)
279 |   expect(fn).nthCalledWith(1, 0, 0)
280 |   expect(fn).nthCalledWith(2, 1, 1)
281 |   expect(fn).nthCalledWith(3, 2, 2)
282 |   expect(fn).nthCalledWith(4, 3, 3)
283 |   dispose()
284 |   obs1.aa++
285 |   expect(fn).toBeCalledTimes(4)
286 | })
287 | 
288 | test('autorun.memo with observable and effect', async () => {
289 |   const obs1 = observable({
290 |     aa: 0,
291 |   })
292 |   const fn = jest.fn()
293 |   const dispose = autorun(() => {
294 |     const obs2 = autorun.memo(() =>
295 |       observable({
296 |         bb: 0,
297 |       })
298 |     )
299 |     fn(obs1.aa, obs2.bb++)
300 |     autorun.effect(() => {
301 |       obs2.bb++
302 |     }, [])
303 |   })
304 |   obs1.aa++
305 |   obs1.aa++
306 |   obs1.aa++
307 |   await sleep(0)
308 |   expect(fn).toBeCalledTimes(5)
309 |   expect(fn).nthCalledWith(1, 0, 0)
310 |   expect(fn).nthCalledWith(2, 1, 1)
311 |   expect(fn).nthCalledWith(3, 2, 2)
312 |   expect(fn).nthCalledWith(4, 3, 3)
313 |   expect(fn).nthCalledWith(5, 3, 5)
314 |   dispose()
315 |   obs1.aa++
316 |   expect(fn).toBeCalledTimes(5)
317 | })
318 | 
319 | test('autorun.memo with deps', () => {
320 |   const obs = observable<any>({
321 |     bb: 0,
322 |     cc: 0,
323 |   })
324 |   const fn = jest.fn()
325 |   autorun(() => {
326 |     const value = autorun.memo(
327 |       () => ({
328 |         aa: 0,
329 |       }),
330 |       [obs.cc]
331 |     )
332 |     fn(obs.bb, value.aa++)
333 |   })
334 |   obs.bb++
335 |   obs.bb++
336 |   obs.bb++
337 |   obs.bb++
338 |   expect(fn).toBeCalledTimes(5)
339 |   expect(fn).nthCalledWith(1, 0, 0)
340 |   expect(fn).nthCalledWith(2, 1, 1)
341 |   expect(fn).nthCalledWith(3, 2, 2)
342 |   expect(fn).nthCalledWith(4, 3, 3)
343 |   expect(fn).nthCalledWith(5, 4, 4)
344 |   obs.cc++
345 |   expect(fn).toBeCalledTimes(6)
346 |   expect(fn).nthCalledWith(6, 4, 0)
347 | })
348 | 
349 | test('autorun.memo with deps and dispose', () => {
350 |   const obs = observable<any>({
351 |     bb: 0,
352 |     cc: 0,
353 |   })
354 |   const fn = jest.fn()
355 |   const dispose = autorun(() => {
356 |     const value = autorun.memo(
357 |       () => ({
358 |         aa: 0,
359 |       }),
360 |       [obs.cc]
361 |     )
362 |     fn(obs.bb, value.aa++)
363 |   })
364 |   obs.bb++
365 |   obs.bb++
366 |   obs.bb++
367 |   obs.bb++
368 |   expect(fn).toBeCalledTimes(5)
369 |   expect(fn).lastCalledWith(4, 4)
370 |   obs.cc++
371 |   expect(fn).toBeCalledTimes(6)
372 |   expect(fn).lastCalledWith(4, 0)
373 |   dispose()
374 |   obs.bb++
375 |   obs.cc++
376 |   expect(fn).toBeCalledTimes(6)
377 | })
378 | 
379 | test('autorun.memo with invalid params', () => {
380 |   const obs = observable<any>({
381 |     bb: 0,
382 |   })
383 |   const fn = jest.fn()
384 |   autorun(() => {
385 |     const value = autorun.memo({ aa: 0 } as any)
386 |     fn(obs.bb, value)
387 |   })
388 |   obs.bb++
389 |   obs.bb++
390 |   obs.bb++
391 |   obs.bb++
392 |   expect(fn).toBeCalledTimes(5)
393 |   expect(fn).lastCalledWith(4, undefined)
394 | })
395 | 
396 | test('autorun.memo not in autorun', () => {
397 |   expect(() => autorun.memo(() => ({ aa: 0 }))).toThrow()
398 | })
399 | 
400 | test('autorun no memo', () => {
401 |   const obs = observable<any>({
402 |     bb: 0,
403 |   })
404 |   const fn = jest.fn()
405 |   autorun(() => {
406 |     const value = {
407 |       aa: 0,
408 |     }
409 |     fn(obs.bb, value.aa++)
410 |   })
411 |   obs.bb++
412 |   obs.bb++
413 |   obs.bb++
414 |   obs.bb++
415 |   expect(fn).toBeCalledTimes(5)
416 |   expect(fn).nthCalledWith(1, 0, 0)
417 |   expect(fn).nthCalledWith(2, 1, 0)
418 |   expect(fn).nthCalledWith(3, 2, 0)
419 |   expect(fn).nthCalledWith(4, 3, 0)
420 |   expect(fn).nthCalledWith(5, 4, 0)
421 | })
422 | 
423 | test('autorun.effect', async () => {
424 |   const obs = observable<any>({
425 |     bb: 0,
426 |   })
427 |   const fn = jest.fn()
428 |   const effect = jest.fn()
429 |   const disposer = jest.fn()
430 |   const dispose = autorun(() => {
431 |     autorun.effect(() => {
432 |       effect()
433 |       return disposer
434 |     }, [])
435 |     fn(obs.bb)
436 |   })
437 |   obs.bb++
438 |   obs.bb++
439 |   obs.bb++
440 |   obs.bb++
441 | 
442 |   await sleep(0)
443 |   expect(fn).toBeCalledTimes(5)
444 |   expect(fn).lastCalledWith(4)
445 |   expect(effect).toBeCalledTimes(1)
446 |   expect(disposer).toBeCalledTimes(0)
447 | 
448 |   dispose()
449 |   await sleep(0)
450 |   expect(effect).toBeCalledTimes(1)
451 |   expect(disposer).toBeCalledTimes(1)
452 | })
453 | 
454 | test('autorun.effect dispose when autorun dispose', async () => {
455 |   const obs = observable<any>({
456 |     bb: 0,
457 |   })
458 |   const fn = jest.fn()
459 |   const effect = jest.fn()
460 |   const disposer = jest.fn()
461 |   const dispose = autorun(() => {
462 |     autorun.effect(() => {
463 |       effect()
464 |       return disposer
465 |     }, [])
466 |     fn(obs.bb)
467 |   })
468 |   obs.bb++
469 |   obs.bb++
470 |   obs.bb++
471 |   obs.bb++
472 | 
473 |   dispose()
474 |   await sleep(0)
475 |   expect(fn).toBeCalledTimes(5)
476 |   expect(fn).lastCalledWith(4)
477 |   expect(effect).toBeCalledTimes(0)
478 |   expect(disposer).toBeCalledTimes(0)
479 | })
480 | 
481 | test('autorun.effect with deps', async () => {
482 |   const obs = observable<any>({
483 |     bb: 0,
484 |     cc: 0,
485 |   })
486 |   const fn = jest.fn()
487 |   const effect = jest.fn()
488 |   const dispose = autorun(() => {
489 |     autorun.effect(() => {
490 |       effect()
491 |     }, [obs.cc])
492 |     fn(obs.bb)
493 |   })
494 |   obs.bb++
495 |   obs.bb++
496 |   obs.bb++
497 |   obs.bb++
498 |   expect(effect).toBeCalledTimes(0)
499 |   await sleep(0)
500 |   expect(fn).toBeCalledTimes(5)
501 |   expect(fn).lastCalledWith(4)
502 |   expect(effect).toBeCalledTimes(1)
503 |   obs.cc++
504 |   expect(effect).toBeCalledTimes(1)
505 |   await sleep(0)
506 |   expect(fn).toBeCalledTimes(6)
507 |   expect(fn).lastCalledWith(4)
508 |   expect(effect).toBeCalledTimes(2)
509 |   dispose()
510 |   await sleep(0)
511 |   expect(effect).toBeCalledTimes(2)
512 | })
513 | 
514 | test('autorun.effect with default deps', async () => {
515 |   const obs = observable<any>({
516 |     bb: 0,
517 |   })
518 |   const fn = jest.fn()
519 |   const effect = jest.fn()
520 |   const dispose = autorun(() => {
521 |     autorun.effect(() => {
522 |       effect()
523 |     })
524 |     fn(obs.bb)
525 |   })
526 |   obs.bb++
527 |   obs.bb++
528 |   obs.bb++
529 |   obs.bb++
530 |   expect(effect).toBeCalledTimes(0)
531 |   await sleep(0)
532 |   expect(fn).toBeCalledTimes(5)
533 |   expect(fn).lastCalledWith(4)
534 |   expect(effect).toBeCalledTimes(5)
535 |   dispose()
536 |   await sleep(0)
537 |   expect(effect).toBeCalledTimes(5)
538 | })
539 | 
540 | test('autorun.effect not in autorun', () => {
541 |   expect(() => autorun.effect(() => {})).toThrow()
542 | })
543 | 
544 | test('autorun.effect with invalid params', () => {
545 |   autorun.effect({} as any)
546 | })
547 | 
548 | test('autorun dispose in batch', () => {
549 |   const obs = observable({
550 |     value: 123,
551 |   })
552 |   const handler = jest.fn()
553 |   const dispose = autorun(() => {
554 |     handler(obs.value)
555 |   })
556 | 
557 |   batch(() => {
558 |     obs.value = 321
559 |     dispose()
560 |   })
561 |   expect(handler).toBeCalledTimes(1)
562 | })
563 | 
564 | test('set value by computed depend', () => {
565 |   const obs = observable<any>({})
566 |   const comp1 = observable.computed(() => {
567 |     return obs.aa?.bb
568 |   })
569 |   const comp2 = observable.computed(() => {
570 |     return obs.aa?.cc
571 |   })
572 |   const handler = jest.fn()
573 |   autorun(() => {
574 |     handler(comp1.value, comp2.value)
575 |   })
576 |   obs.aa = {
577 |     bb: 123,
578 |     cc: 321,
579 |   }
580 |   expect(handler).toBeCalledTimes(2)
581 |   expect(handler).nthCalledWith(1, undefined, undefined)
582 |   expect(handler).nthCalledWith(2, 123, 321)
583 | })
584 | 
585 | test('delete value by computed depend', () => {
586 |   const handler = jest.fn()
587 |   const obs = observable({
588 |     a: {
589 |       b: 1,
590 |       c: 2,
591 |     },
592 |   })
593 |   const comp1 = observable.computed(() => {
594 |     return obs.a?.b
595 |   })
596 |   const comp2 = observable.computed(() => {
597 |     return obs.a?.c
598 |   })
599 |   autorun(() => {
600 |     handler(comp1.value, comp2.value)
601 |   })
602 |   delete obs.a
603 |   expect(handler).toBeCalledTimes(2)
604 |   expect(handler).nthCalledWith(1, 1, 2)
605 |   expect(handler).nthCalledWith(2, undefined, undefined)
606 | })
607 | 
608 | test('set Set value by computed depend', () => {
609 |   const handler = jest.fn()
610 |   const obs = observable({
611 |     set: new Set(),
612 |   })
613 |   const comp1 = observable.computed(() => {
614 |     return obs.set.has(1)
615 |   })
616 |   const comp2 = observable.computed(() => {
617 |     return obs.set.size
618 |   })
619 |   autorun(() => {
620 |     handler(comp1.value, comp2.value)
621 |   })
622 |   obs.set.add(1)
623 |   expect(handler).toBeCalledTimes(2)
624 |   expect(handler).nthCalledWith(1, false, 0)
625 |   expect(handler).nthCalledWith(2, true, 1)
626 | })
627 | 
628 | test('delete Set by computed depend', () => {
629 |   const handler = jest.fn()
630 |   const obs = observable({
631 |     set: new Set([1]),
632 |   })
633 |   const comp1 = observable.computed(() => {
634 |     return obs.set.has(1)
635 |   })
636 |   const comp2 = observable.computed(() => {
637 |     return obs.set.size
638 |   })
639 |   autorun(() => {
640 |     handler(comp1.value, comp2.value)
641 |   })
642 |   obs.set.delete(1)
643 |   expect(handler).toBeCalledTimes(2)
644 |   expect(handler).nthCalledWith(1, true, 1)
645 |   expect(handler).nthCalledWith(2, false, 0)
646 | })
647 | 
648 | test('set Map value by computed depend', () => {
649 |   const handler = jest.fn()
650 |   const obs = observable({
651 |     map: new Map(),
652 |   })
653 |   const comp1 = observable.computed(() => {
654 |     return obs.map.has(1)
655 |   })
656 |   const comp2 = observable.computed(() => {
657 |     return obs.map.size
658 |   })
659 |   autorun(() => {
660 |     handler(comp1.value, comp2.value)
661 |   })
662 |   obs.map.set(1, 1)
663 |   expect(handler).toBeCalledTimes(2)
664 |   expect(handler).nthCalledWith(1, false, 0)
665 |   expect(handler).nthCalledWith(2, true, 1)
666 | })
667 | 
668 | test('delete Map by computed depend', () => {
669 |   const handler = jest.fn()
670 |   const obs = observable({
671 |     map: new Map([[1, 1]]),
672 |   })
673 |   const comp1 = observable.computed(() => {
674 |     return obs.map.has(1)
675 |   })
676 |   const comp2 = observable.computed(() => {
677 |     return obs.map.size
678 |   })
679 |   autorun(() => {
680 |     handler(comp1.value, comp2.value)
681 |   })
682 |   obs.map.delete(1)
683 |   expect(handler).toBeCalledTimes(2)
684 |   expect(handler).nthCalledWith(1, true, 1)
685 |   expect(handler).nthCalledWith(2, false, 0)
686 | })
687 | 
688 | test('autorun recollect dependencies', () => {
689 |   const obs = observable<any>({
690 |     aa: 'aaa',
691 |     bb: 'bbb',
692 |     cc: 'ccc',
693 |   })
694 |   const fn = jest.fn()
695 |   autorun(() => {
696 |     fn()
697 |     if (obs.aa === 'aaa') {
698 |       return obs.bb
699 |     }
700 |     return obs.cc
701 |   })
702 |   obs.aa = '111'
703 |   obs.bb = '222'
704 |   expect(fn).toBeCalledTimes(2)
705 | })
706 | 
707 | test('reaction recollect dependencies', () => {
708 |   const obs = observable<any>({
709 |     aa: 'aaa',
710 |     bb: 'bbb',
711 |     cc: 'ccc',
712 |   })
713 |   const fn1 = jest.fn()
714 |   const fn2 = jest.fn()
715 |   const trigger1 = jest.fn()
716 |   const trigger2 = jest.fn()
717 |   reaction(() => {
718 |     fn1()
719 |     if (obs.aa === 'aaa') {
720 |       return obs.bb
721 |     }
722 |     return obs.cc
723 |   }, trigger1)
724 |   reaction(
725 |     () => {
726 |       fn2()
727 |       if (obs.aa === 'aaa') {
728 |         return obs.bb
729 |       }
730 |       return obs.cc
731 |     },
732 |     trigger2,
733 |     {
734 |       fireImmediately: true,
735 |     }
736 |   )
737 |   obs.aa = '111'
738 |   obs.bb = '222'
739 |   expect(fn1).toBeCalledTimes(2)
740 |   expect(trigger1).toBeCalledTimes(1)
741 |   expect(fn2).toBeCalledTimes(2)
742 |   expect(trigger2).toBeCalledTimes(2)
743 | })
744 | 
```

--------------------------------------------------------------------------------
/packages/core/src/models/Form.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { define, observable, batch, action, observe } from '@formily/reactive'
  2 | import {
  3 |   FormPath,
  4 |   FormPathPattern,
  5 |   isValid,
  6 |   uid,
  7 |   globalThisPolyfill,
  8 |   merge,
  9 |   isPlainObj,
 10 |   isArr,
 11 |   isObj,
 12 | } from '@formily/shared'
 13 | import { Heart } from './Heart'
 14 | import { Field } from './Field'
 15 | import {
 16 |   JSXComponent,
 17 |   LifeCycleTypes,
 18 |   HeartSubscriber,
 19 |   FormPatternTypes,
 20 |   IFormRequests,
 21 |   IFormFeedback,
 22 |   ISearchFeedback,
 23 |   IFormGraph,
 24 |   IFormProps,
 25 |   IFieldResetOptions,
 26 |   IFormFields,
 27 |   IFieldFactoryProps,
 28 |   IVoidFieldFactoryProps,
 29 |   IFormState,
 30 |   IModelGetter,
 31 |   IModelSetter,
 32 |   IFieldStateGetter,
 33 |   IFieldStateSetter,
 34 |   FormDisplayTypes,
 35 |   IFormMergeStrategy,
 36 | } from '../types'
 37 | import {
 38 |   createStateGetter,
 39 |   createStateSetter,
 40 |   createBatchStateSetter,
 41 |   createBatchStateGetter,
 42 |   triggerFormInitialValuesChange,
 43 |   triggerFormValuesChange,
 44 |   batchValidate,
 45 |   batchReset,
 46 |   batchSubmit,
 47 |   setValidating,
 48 |   setSubmitting,
 49 |   setLoading,
 50 |   getValidFormValues,
 51 | } from '../shared/internals'
 52 | import { isVoidField } from '../shared/checkers'
 53 | import { runEffects } from '../shared/effective'
 54 | import { ArrayField } from './ArrayField'
 55 | import { ObjectField } from './ObjectField'
 56 | import { VoidField } from './VoidField'
 57 | import { Query } from './Query'
 58 | import { Graph } from './Graph'
 59 | 
 60 | const DEV_TOOLS_HOOK = '__FORMILY_DEV_TOOLS_HOOK__'
 61 | 
 62 | export class Form<ValueType extends object = any> {
 63 |   displayName = 'Form'
 64 |   id: string
 65 |   initialized: boolean
 66 |   validating: boolean
 67 |   submitting: boolean
 68 |   loading: boolean
 69 |   modified: boolean
 70 |   pattern: FormPatternTypes
 71 |   display: FormDisplayTypes
 72 |   values: ValueType
 73 |   initialValues: ValueType
 74 |   mounted: boolean
 75 |   unmounted: boolean
 76 |   props: IFormProps<ValueType>
 77 |   heart: Heart
 78 |   graph: Graph
 79 |   fields: IFormFields = {}
 80 |   requests: IFormRequests = {}
 81 |   indexes: Record<string, string> = {}
 82 |   disposers: (() => void)[] = []
 83 | 
 84 |   constructor(props: IFormProps<ValueType>) {
 85 |     this.initialize(props)
 86 |     this.makeObservable()
 87 |     this.makeReactive()
 88 |     this.makeValues()
 89 |     this.onInit()
 90 |   }
 91 | 
 92 |   protected initialize(props: IFormProps<ValueType>) {
 93 |     this.id = uid()
 94 |     this.props = { ...props }
 95 |     this.initialized = false
 96 |     this.submitting = false
 97 |     this.validating = false
 98 |     this.loading = false
 99 |     this.modified = false
100 |     this.mounted = false
101 |     this.unmounted = false
102 |     this.display = this.props.display || 'visible'
103 |     this.pattern = this.props.pattern || 'editable'
104 |     this.editable = this.props.editable
105 |     this.disabled = this.props.disabled
106 |     this.readOnly = this.props.readOnly
107 |     this.readPretty = this.props.readPretty
108 |     this.visible = this.props.visible
109 |     this.hidden = this.props.hidden
110 |     this.graph = new Graph(this)
111 |     this.heart = new Heart({
112 |       lifecycles: this.lifecycles,
113 |       context: this,
114 |     })
115 |   }
116 | 
117 |   protected makeValues() {
118 |     this.values = getValidFormValues(this.props.values)
119 |     this.initialValues = getValidFormValues(this.props.initialValues)
120 |   }
121 | 
122 |   protected makeObservable() {
123 |     define(this, {
124 |       fields: observable.shallow,
125 |       indexes: observable.shallow,
126 |       initialized: observable.ref,
127 |       validating: observable.ref,
128 |       submitting: observable.ref,
129 |       loading: observable.ref,
130 |       modified: observable.ref,
131 |       pattern: observable.ref,
132 |       display: observable.ref,
133 |       mounted: observable.ref,
134 |       unmounted: observable.ref,
135 |       values: observable,
136 |       initialValues: observable,
137 |       valid: observable.computed,
138 |       invalid: observable.computed,
139 |       errors: observable.computed,
140 |       warnings: observable.computed,
141 |       successes: observable.computed,
142 |       hidden: observable.computed,
143 |       visible: observable.computed,
144 |       editable: observable.computed,
145 |       readOnly: observable.computed,
146 |       readPretty: observable.computed,
147 |       disabled: observable.computed,
148 |       setValues: action,
149 |       setValuesIn: action,
150 |       setInitialValues: action,
151 |       setInitialValuesIn: action,
152 |       setPattern: action,
153 |       setDisplay: action,
154 |       setState: action,
155 |       deleteInitialValuesIn: action,
156 |       deleteValuesIn: action,
157 |       setSubmitting: action,
158 |       setValidating: action,
159 |       reset: action,
160 |       submit: action,
161 |       validate: action,
162 |       onMount: batch,
163 |       onUnmount: batch,
164 |       onInit: batch,
165 |     })
166 |   }
167 | 
168 |   protected makeReactive() {
169 |     this.disposers.push(
170 |       observe(
171 |         this,
172 |         (change) => {
173 |           triggerFormInitialValuesChange(this, change)
174 |           triggerFormValuesChange(this, change)
175 |         },
176 |         true
177 |       )
178 |     )
179 |   }
180 | 
181 |   get valid() {
182 |     return !this.invalid
183 |   }
184 | 
185 |   get invalid() {
186 |     return this.errors.length > 0
187 |   }
188 | 
189 |   get errors() {
190 |     return this.queryFeedbacks({
191 |       type: 'error',
192 |     })
193 |   }
194 | 
195 |   get warnings() {
196 |     return this.queryFeedbacks({
197 |       type: 'warning',
198 |     })
199 |   }
200 | 
201 |   get successes() {
202 |     return this.queryFeedbacks({
203 |       type: 'success',
204 |     })
205 |   }
206 | 
207 |   get lifecycles() {
208 |     return runEffects(this, this.props.effects)
209 |   }
210 | 
211 |   get hidden() {
212 |     return this.display === 'hidden'
213 |   }
214 | 
215 |   get visible() {
216 |     return this.display === 'visible'
217 |   }
218 | 
219 |   set hidden(hidden: boolean) {
220 |     if (!isValid(hidden)) return
221 |     if (hidden) {
222 |       this.display = 'hidden'
223 |     } else {
224 |       this.display = 'visible'
225 |     }
226 |   }
227 | 
228 |   set visible(visible: boolean) {
229 |     if (!isValid(visible)) return
230 |     if (visible) {
231 |       this.display = 'visible'
232 |     } else {
233 |       this.display = 'none'
234 |     }
235 |   }
236 | 
237 |   get editable() {
238 |     return this.pattern === 'editable'
239 |   }
240 | 
241 |   set editable(editable) {
242 |     if (!isValid(editable)) return
243 |     if (editable) {
244 |       this.pattern = 'editable'
245 |     } else {
246 |       this.pattern = 'readPretty'
247 |     }
248 |   }
249 | 
250 |   get readOnly() {
251 |     return this.pattern === 'readOnly'
252 |   }
253 | 
254 |   set readOnly(readOnly) {
255 |     if (!isValid(readOnly)) return
256 |     if (readOnly) {
257 |       this.pattern = 'readOnly'
258 |     } else {
259 |       this.pattern = 'editable'
260 |     }
261 |   }
262 | 
263 |   get disabled() {
264 |     return this.pattern === 'disabled'
265 |   }
266 | 
267 |   set disabled(disabled) {
268 |     if (!isValid(disabled)) return
269 |     if (disabled) {
270 |       this.pattern = 'disabled'
271 |     } else {
272 |       this.pattern = 'editable'
273 |     }
274 |   }
275 | 
276 |   get readPretty() {
277 |     return this.pattern === 'readPretty'
278 |   }
279 | 
280 |   set readPretty(readPretty) {
281 |     if (!isValid(readPretty)) return
282 |     if (readPretty) {
283 |       this.pattern = 'readPretty'
284 |     } else {
285 |       this.pattern = 'editable'
286 |     }
287 |   }
288 | 
289 |   /** 创建字段 **/
290 | 
291 |   createField = <
292 |     Decorator extends JSXComponent,
293 |     Component extends JSXComponent
294 |   >(
295 |     props: IFieldFactoryProps<Decorator, Component>
296 |   ): Field<Decorator, Component> => {
297 |     const address = FormPath.parse(props.basePath).concat(props.name)
298 |     const identifier = address.toString()
299 |     if (!identifier) return
300 |     if (!this.fields[identifier] || this.props.designable) {
301 |       batch(() => {
302 |         new Field(address, props, this, this.props.designable)
303 |       })
304 |       this.notify(LifeCycleTypes.ON_FORM_GRAPH_CHANGE)
305 |     }
306 |     return this.fields[identifier] as any
307 |   }
308 | 
309 |   createArrayField = <
310 |     Decorator extends JSXComponent,
311 |     Component extends JSXComponent
312 |   >(
313 |     props: IFieldFactoryProps<Decorator, Component>
314 |   ): ArrayField<Decorator, Component> => {
315 |     const address = FormPath.parse(props.basePath).concat(props.name)
316 |     const identifier = address.toString()
317 |     if (!identifier) return
318 |     if (!this.fields[identifier] || this.props.designable) {
319 |       batch(() => {
320 |         new ArrayField(
321 |           address,
322 |           {
323 |             ...props,
324 |             value: isArr(props.value) ? props.value : [],
325 |           },
326 |           this,
327 |           this.props.designable
328 |         )
329 |       })
330 |       this.notify(LifeCycleTypes.ON_FORM_GRAPH_CHANGE)
331 |     }
332 |     return this.fields[identifier] as any
333 |   }
334 | 
335 |   createObjectField = <
336 |     Decorator extends JSXComponent,
337 |     Component extends JSXComponent
338 |   >(
339 |     props: IFieldFactoryProps<Decorator, Component>
340 |   ): ObjectField<Decorator, Component> => {
341 |     const address = FormPath.parse(props.basePath).concat(props.name)
342 |     const identifier = address.toString()
343 |     if (!identifier) return
344 |     if (!this.fields[identifier] || this.props.designable) {
345 |       batch(() => {
346 |         new ObjectField(
347 |           address,
348 |           {
349 |             ...props,
350 |             value: isObj(props.value) ? props.value : {},
351 |           },
352 |           this,
353 |           this.props.designable
354 |         )
355 |       })
356 |       this.notify(LifeCycleTypes.ON_FORM_GRAPH_CHANGE)
357 |     }
358 |     return this.fields[identifier] as any
359 |   }
360 | 
361 |   createVoidField = <
362 |     Decorator extends JSXComponent,
363 |     Component extends JSXComponent
364 |   >(
365 |     props: IVoidFieldFactoryProps<Decorator, Component>
366 |   ): VoidField<Decorator, Component> => {
367 |     const address = FormPath.parse(props.basePath).concat(props.name)
368 |     const identifier = address.toString()
369 |     if (!identifier) return
370 |     if (!this.fields[identifier] || this.props.designable) {
371 |       batch(() => {
372 |         new VoidField(address, props, this, this.props.designable)
373 |       })
374 |       this.notify(LifeCycleTypes.ON_FORM_GRAPH_CHANGE)
375 |     }
376 |     return this.fields[identifier] as any
377 |   }
378 | 
379 |   /** 状态操作模型 **/
380 | 
381 |   setValues = (values: any, strategy: IFormMergeStrategy = 'merge') => {
382 |     if (!isPlainObj(values)) return
383 |     if (strategy === 'merge' || strategy === 'deepMerge') {
384 |       merge(this.values, values, {
385 |         // never reach
386 |         arrayMerge: (target, source) => source,
387 |         assign: true,
388 |       })
389 |     } else if (strategy === 'shallowMerge') {
390 |       Object.assign(this.values, values)
391 |     } else {
392 |       this.values = values as any
393 |     }
394 |   }
395 | 
396 |   setInitialValues = (
397 |     initialValues: any,
398 |     strategy: IFormMergeStrategy = 'merge'
399 |   ) => {
400 |     if (!isPlainObj(initialValues)) return
401 |     if (strategy === 'merge' || strategy === 'deepMerge') {
402 |       merge(this.initialValues, initialValues, {
403 |         // never reach
404 |         arrayMerge: (target, source) => source,
405 |         assign: true,
406 |       })
407 |     } else if (strategy === 'shallowMerge') {
408 |       Object.assign(this.initialValues, initialValues)
409 |     } else {
410 |       this.initialValues = initialValues as any
411 |     }
412 |   }
413 | 
414 |   setValuesIn = (pattern: FormPathPattern, value: any) => {
415 |     FormPath.setIn(this.values, pattern, value)
416 |   }
417 | 
418 |   deleteValuesIn = (pattern: FormPathPattern) => {
419 |     FormPath.deleteIn(this.values, pattern)
420 |   }
421 | 
422 |   existValuesIn = (pattern: FormPathPattern) => {
423 |     return FormPath.existIn(this.values, pattern)
424 |   }
425 | 
426 |   getValuesIn = (pattern: FormPathPattern) => {
427 |     return FormPath.getIn(this.values, pattern)
428 |   }
429 | 
430 |   setInitialValuesIn = (pattern: FormPathPattern, initialValue: any) => {
431 |     FormPath.setIn(this.initialValues, pattern, initialValue)
432 |   }
433 | 
434 |   deleteInitialValuesIn = (pattern: FormPathPattern) => {
435 |     FormPath.deleteIn(this.initialValues, pattern)
436 |   }
437 | 
438 |   existInitialValuesIn = (pattern: FormPathPattern) => {
439 |     return FormPath.existIn(this.initialValues, pattern)
440 |   }
441 | 
442 |   getInitialValuesIn = (pattern: FormPathPattern) => {
443 |     return FormPath.getIn(this.initialValues, pattern)
444 |   }
445 | 
446 |   setLoading = (loading: boolean) => {
447 |     setLoading(this, loading)
448 |   }
449 | 
450 |   setSubmitting = (submitting: boolean) => {
451 |     setSubmitting(this, submitting)
452 |   }
453 | 
454 |   setValidating = (validating: boolean) => {
455 |     setValidating(this, validating)
456 |   }
457 | 
458 |   setDisplay = (display: FormDisplayTypes) => {
459 |     this.display = display
460 |   }
461 | 
462 |   setPattern = (pattern: FormPatternTypes) => {
463 |     this.pattern = pattern
464 |   }
465 | 
466 |   addEffects = (id: any, effects: IFormProps['effects']) => {
467 |     if (!this.heart.hasLifeCycles(id)) {
468 |       this.heart.addLifeCycles(id, runEffects(this, effects))
469 |     }
470 |   }
471 | 
472 |   removeEffects = (id: any) => {
473 |     this.heart.removeLifeCycles(id)
474 |   }
475 | 
476 |   setEffects = (effects: IFormProps['effects']) => {
477 |     this.heart.setLifeCycles(runEffects(this, effects))
478 |   }
479 | 
480 |   clearErrors = (pattern: FormPathPattern = '*') => {
481 |     this.query(pattern).forEach((field) => {
482 |       if (!isVoidField(field)) {
483 |         field.setFeedback({
484 |           type: 'error',
485 |           messages: [],
486 |         })
487 |       }
488 |     })
489 |   }
490 | 
491 |   clearWarnings = (pattern: FormPathPattern = '*') => {
492 |     this.query(pattern).forEach((field) => {
493 |       if (!isVoidField(field)) {
494 |         field.setFeedback({
495 |           type: 'warning',
496 |           messages: [],
497 |         })
498 |       }
499 |     })
500 |   }
501 | 
502 |   clearSuccesses = (pattern: FormPathPattern = '*') => {
503 |     this.query(pattern).forEach((field) => {
504 |       if (!isVoidField(field)) {
505 |         field.setFeedback({
506 |           type: 'success',
507 |           messages: [],
508 |         })
509 |       }
510 |     })
511 |   }
512 | 
513 |   query = (pattern: FormPathPattern): Query => {
514 |     return new Query({
515 |       pattern,
516 |       base: '',
517 |       form: this,
518 |     })
519 |   }
520 | 
521 |   queryFeedbacks = (search: ISearchFeedback): IFormFeedback[] => {
522 |     return this.query(search.address || search.path || '*').reduce(
523 |       (messages, field) => {
524 |         if (isVoidField(field)) return messages
525 |         return messages.concat(
526 |           field
527 |             .queryFeedbacks(search)
528 |             .map((feedback) => ({
529 |               ...feedback,
530 |               address: field.address.toString(),
531 |               path: field.path.toString(),
532 |             }))
533 |             .filter((feedback) => feedback.messages.length > 0)
534 |         )
535 |       },
536 |       []
537 |     )
538 |   }
539 | 
540 |   notify = (type: string, payload?: any) => {
541 |     this.heart.publish(type, payload ?? this)
542 |   }
543 | 
544 |   subscribe = (subscriber?: HeartSubscriber) => {
545 |     return this.heart.subscribe(subscriber)
546 |   }
547 | 
548 |   unsubscribe = (id: number) => {
549 |     this.heart.unsubscribe(id)
550 |   }
551 | 
552 |   /**事件钩子**/
553 | 
554 |   onInit = () => {
555 |     this.initialized = true
556 |     this.notify(LifeCycleTypes.ON_FORM_INIT)
557 |   }
558 | 
559 |   onMount = () => {
560 |     this.mounted = true
561 |     this.notify(LifeCycleTypes.ON_FORM_MOUNT)
562 |     if (globalThisPolyfill[DEV_TOOLS_HOOK] && !this.props.designable) {
563 |       globalThisPolyfill[DEV_TOOLS_HOOK].inject(this.id, this)
564 |     }
565 |   }
566 | 
567 |   onUnmount = () => {
568 |     this.notify(LifeCycleTypes.ON_FORM_UNMOUNT)
569 |     this.query('*').forEach((field) => field.destroy(false))
570 |     this.disposers.forEach((dispose) => dispose())
571 |     this.unmounted = true
572 |     this.indexes = {}
573 |     this.heart.clear()
574 |     if (globalThisPolyfill[DEV_TOOLS_HOOK] && !this.props.designable) {
575 |       globalThisPolyfill[DEV_TOOLS_HOOK].unmount(this.id)
576 |     }
577 |   }
578 | 
579 |   setState: IModelSetter<IFormState<ValueType>> = createStateSetter(this)
580 | 
581 |   getState: IModelGetter<IFormState<ValueType>> = createStateGetter(this)
582 | 
583 |   setFormState: IModelSetter<IFormState<ValueType>> = createStateSetter(this)
584 | 
585 |   getFormState: IModelGetter<IFormState<ValueType>> = createStateGetter(this)
586 | 
587 |   setFieldState: IFieldStateSetter = createBatchStateSetter(this)
588 | 
589 |   getFieldState: IFieldStateGetter = createBatchStateGetter(this)
590 | 
591 |   getFormGraph = () => {
592 |     return this.graph.getGraph()
593 |   }
594 | 
595 |   setFormGraph = (graph: IFormGraph) => {
596 |     this.graph.setGraph(graph)
597 |   }
598 | 
599 |   clearFormGraph = (pattern: FormPathPattern = '*', forceClear = true) => {
600 |     this.query(pattern).forEach((field) => {
601 |       field.destroy(forceClear)
602 |     })
603 |   }
604 | 
605 |   validate = (pattern: FormPathPattern = '*') => {
606 |     return batchValidate(this, pattern)
607 |   }
608 | 
609 |   submit = <T>(
610 |     onSubmit?: (values: ValueType) => Promise<T> | void
611 |   ): Promise<T> => {
612 |     return batchSubmit(this, onSubmit)
613 |   }
614 | 
615 |   reset = (pattern: FormPathPattern = '*', options?: IFieldResetOptions) => {
616 |     return batchReset(this, pattern, options)
617 |   }
618 | }
619 | 
```

--------------------------------------------------------------------------------
/packages/path/src/index.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { Parser } from './parser'
  2 | import { isStr, isArr, isFn, isEqual, isObj, isNum, isRegExp } from './shared'
  3 | import {
  4 |   getDestructor,
  5 |   getInByDestructor,
  6 |   setInByDestructor,
  7 |   deleteInByDestructor,
  8 |   existInByDestructor,
  9 | } from './destructor'
 10 | import { Segments, Node, Pattern } from './types'
 11 | import { Matcher } from './matcher'
 12 | 
 13 | const pathCache = new Map()
 14 | 
 15 | const isMatcher = Symbol('PATH_MATCHER')
 16 | 
 17 | const isValid = (val: any) => val !== undefined && val !== null
 18 | 
 19 | const isSimplePath = (val: string) =>
 20 |   val.indexOf('*') === -1 &&
 21 |   val.indexOf('~') === -1 &&
 22 |   val.indexOf('[') === -1 &&
 23 |   val.indexOf(']') === -1 &&
 24 |   val.indexOf(',') === -1 &&
 25 |   val.indexOf(':') === -1 &&
 26 |   val.indexOf(' ') === -1 &&
 27 |   val[0] !== '.'
 28 | 
 29 | const isAssignable = (val: any) =>
 30 |   typeof val === 'object' || typeof val === 'function'
 31 | 
 32 | const isNumberIndex = (val: any) =>
 33 |   isStr(val) ? /^\d+$/.test(val) : isNum(val)
 34 | 
 35 | const getIn = (segments: Segments, source: any) => {
 36 |   for (let i = 0; i < segments.length; i++) {
 37 |     const index = segments[i]
 38 |     const rules = getDestructor(index as string)
 39 |     if (!rules) {
 40 |       if (!isValid(source)) return
 41 |       source = source[index]
 42 |     } else {
 43 |       source = getInByDestructor(source, rules, { setIn, getIn })
 44 |       break
 45 |     }
 46 |   }
 47 |   return source
 48 | }
 49 | 
 50 | const setIn = (segments: Segments, source: any, value: any) => {
 51 |   for (let i = 0; i < segments.length; i++) {
 52 |     const index = segments[i]
 53 |     const rules = getDestructor(index as string)
 54 |     if (!rules) {
 55 |       if (!isValid(source) || !isAssignable(source)) return
 56 |       if (isArr(source) && !isNumberIndex(index)) {
 57 |         return
 58 |       }
 59 |       if (!isValid(source[index])) {
 60 |         if (value === undefined) {
 61 |           if (source[index] === null) source[index] = value
 62 |           return
 63 |         }
 64 |         if (i < segments.length - 1) {
 65 |           source[index] = isNum(segments[i + 1]) ? [] : {}
 66 |         }
 67 |       }
 68 |       if (i === segments.length - 1) {
 69 |         source[index] = value
 70 |       }
 71 |       source = source[index]
 72 |     } else {
 73 |       setInByDestructor(source, rules, value, { setIn, getIn })
 74 |       break
 75 |     }
 76 |   }
 77 | }
 78 | 
 79 | const deleteIn = (segments: Segments, source: any) => {
 80 |   for (let i = 0; i < segments.length; i++) {
 81 |     const index = segments[i]
 82 |     const rules = getDestructor(index as string)
 83 |     if (!rules) {
 84 |       if (i === segments.length - 1 && isValid(source)) {
 85 |         delete source[index]
 86 |         return
 87 |       }
 88 | 
 89 |       if (!isValid(source) || !isAssignable(source)) return
 90 |       source = source[index]
 91 |       if (!isObj(source)) {
 92 |         return
 93 |       }
 94 |     } else {
 95 |       deleteInByDestructor(source, rules, {
 96 |         setIn,
 97 |         getIn,
 98 |         deleteIn,
 99 |       })
100 |       break
101 |     }
102 |   }
103 | }
104 | 
105 | const hasOwnProperty = Object.prototype.hasOwnProperty
106 | 
107 | const existIn = (segments: Segments, source: any, start: number | Path) => {
108 |   if (start instanceof Path) {
109 |     start = start.length
110 |   }
111 |   for (let i = start; i < segments.length; i++) {
112 |     const index = segments[i]
113 |     const rules = getDestructor(index as string)
114 |     if (!rules) {
115 |       if (i === segments.length - 1) {
116 |         return hasOwnProperty.call(source, index)
117 |       }
118 | 
119 |       if (!isValid(source) || !isAssignable(source)) return false
120 |       source = source[index]
121 | 
122 |       if (!isObj(source)) {
123 |         return false
124 |       }
125 |     } else {
126 |       return existInByDestructor(source, rules, start, {
127 |         setIn,
128 |         getIn,
129 |         deleteIn,
130 |         existIn,
131 |       })
132 |     }
133 |   }
134 | }
135 | 
136 | const parse = (pattern: Pattern, base?: Pattern) => {
137 |   if (pattern instanceof Path) {
138 |     return {
139 |       entire: pattern.entire,
140 |       segments: pattern.segments.slice(),
141 |       isRegExp: false,
142 |       haveRelativePattern: pattern.haveRelativePattern,
143 |       isWildMatchPattern: pattern.isWildMatchPattern,
144 |       isMatchPattern: pattern.isMatchPattern,
145 |       haveExcludePattern: pattern.haveExcludePattern,
146 |       tree: pattern.tree,
147 |     }
148 |   } else if (isStr(pattern)) {
149 |     if (!pattern) {
150 |       return {
151 |         entire: '',
152 |         segments: [],
153 |         isRegExp: false,
154 |         isWildMatchPattern: false,
155 |         haveExcludePattern: false,
156 |         isMatchPattern: false,
157 |       }
158 |     }
159 |     if (isSimplePath(pattern)) {
160 |       return {
161 |         entire: pattern,
162 |         segments: pattern.split('.'),
163 |         isRegExp: false,
164 |         isWildMatchPattern: false,
165 |         haveExcludePattern: false,
166 |         isMatchPattern: false,
167 |       }
168 |     }
169 |     const parser = new Parser(pattern, Path.parse(base))
170 |     const tree = parser.parse()
171 |     if (!parser.isMatchPattern) {
172 |       const segments = parser.data.segments
173 |       return {
174 |         entire: segments.join('.'),
175 |         segments,
176 |         tree,
177 |         isRegExp: false,
178 |         haveRelativePattern: parser.haveRelativePattern,
179 |         isWildMatchPattern: false,
180 |         haveExcludePattern: false,
181 |         isMatchPattern: false,
182 |       }
183 |     } else {
184 |       return {
185 |         entire: pattern,
186 |         segments: [],
187 |         isRegExp: false,
188 |         haveRelativePattern: false,
189 |         isWildMatchPattern: parser.isWildMatchPattern,
190 |         haveExcludePattern: parser.haveExcludePattern,
191 |         isMatchPattern: true,
192 |         tree,
193 |       }
194 |     }
195 |   } else if (isFn(pattern) && pattern[isMatcher]) {
196 |     return parse(pattern['path'])
197 |   } else if (isArr(pattern)) {
198 |     return {
199 |       entire: pattern.join('.'),
200 |       segments: pattern.reduce((buf, key) => {
201 |         return buf.concat(parseString(key))
202 |       }, []),
203 |       isRegExp: false,
204 |       haveRelativePattern: false,
205 |       isWildMatchPattern: false,
206 |       haveExcludePattern: false,
207 |       isMatchPattern: false,
208 |     }
209 |   } else if (isRegExp(pattern)) {
210 |     return {
211 |       entire: pattern,
212 |       segments: [],
213 |       isRegExp: true,
214 |       haveRelativePattern: false,
215 |       isWildMatchPattern: false,
216 |       haveExcludePattern: false,
217 |       isMatchPattern: true,
218 |     }
219 |   } else {
220 |     return {
221 |       entire: '',
222 |       isRegExp: false,
223 |       segments: pattern !== undefined ? [pattern] : [],
224 |       haveRelativePattern: false,
225 |       isWildMatchPattern: false,
226 |       haveExcludePattern: false,
227 |       isMatchPattern: false,
228 |     }
229 |   }
230 | }
231 | 
232 | const parseString = (source: any) => {
233 |   if (isStr(source)) {
234 |     source = source.replace(/\s*/g, '')
235 |     try {
236 |       const { segments, isMatchPattern } = parse(source)
237 |       return !isMatchPattern ? segments : source
238 |     } catch (e) {
239 |       return source
240 |     }
241 |   } else if (source instanceof Path) {
242 |     return source.segments
243 |   }
244 |   return source
245 | }
246 | 
247 | export class Path {
248 |   public entire: string | RegExp
249 |   public segments: Segments
250 |   public isMatchPattern: boolean
251 |   public isWildMatchPattern: boolean
252 |   public isRegExp: boolean
253 |   public haveRelativePattern: boolean
254 |   public haveExcludePattern: boolean
255 |   public matchScore: number
256 |   public tree: Node
257 |   private matchCache: any
258 |   private includesCache: any
259 | 
260 |   constructor(input: Pattern, base?: Pattern) {
261 |     const {
262 |       tree,
263 |       segments,
264 |       entire,
265 |       isRegExp,
266 |       isMatchPattern,
267 |       isWildMatchPattern,
268 |       haveRelativePattern,
269 |       haveExcludePattern,
270 |     } = parse(input, base)
271 |     this.entire = entire
272 |     this.segments = segments
273 |     this.isMatchPattern = isMatchPattern
274 |     this.isWildMatchPattern = isWildMatchPattern
275 |     this.haveRelativePattern = haveRelativePattern
276 |     this.isRegExp = isRegExp
277 |     this.haveExcludePattern = haveExcludePattern
278 |     this.tree = tree as Node
279 |     this.matchCache = new Map()
280 |     this.includesCache = new Map()
281 |   }
282 | 
283 |   toString() {
284 |     return this.entire?.toString()
285 |   }
286 | 
287 |   toArr() {
288 |     return this.segments?.slice()
289 |   }
290 | 
291 |   get length() {
292 |     return this.segments.length
293 |   }
294 | 
295 |   concat = (...args: Pattern[]) => {
296 |     if (this.isMatchPattern || this.isRegExp) {
297 |       throw new Error(`${this.entire} cannot be concat`)
298 |     }
299 |     const path = new Path('')
300 |     path.segments = this.segments.concat(...args.map((s) => parseString(s)))
301 |     path.entire = path.segments.join('.')
302 |     return path
303 |   }
304 | 
305 |   slice = (start?: number, end?: number) => {
306 |     if (this.isMatchPattern || this.isRegExp) {
307 |       throw new Error(`${this.entire} cannot be slice`)
308 |     }
309 |     const path = new Path('')
310 |     path.segments = this.segments.slice(start, end)
311 |     path.entire = path.segments.join('.')
312 |     return path
313 |   }
314 | 
315 |   push = (...items: Pattern[]) => {
316 |     return this.concat(...items)
317 |   }
318 | 
319 |   pop = () => {
320 |     if (this.isMatchPattern || this.isRegExp) {
321 |       throw new Error(`${this.entire} cannot be pop`)
322 |     }
323 |     return new Path(this.segments.slice(0, this.segments.length - 1))
324 |   }
325 | 
326 |   splice = (
327 |     start: number,
328 |     deleteCount?: number,
329 |     ...items: Array<string | number>
330 |   ) => {
331 |     if (this.isMatchPattern || this.isRegExp) {
332 |       throw new Error(`${this.entire} cannot be splice`)
333 |     }
334 |     items = items.reduce((buf, item) => buf.concat(parseString(item)), [])
335 |     const segments_ = this.segments.slice()
336 |     segments_.splice(start, deleteCount, ...items)
337 |     return new Path(segments_)
338 |   }
339 | 
340 |   forEach = (callback: (key: string | number) => any) => {
341 |     if (this.isMatchPattern || this.isRegExp) {
342 |       throw new Error(`${this.entire} cannot be each`)
343 |     }
344 |     this.segments.forEach(callback)
345 |   }
346 | 
347 |   map = (callback: (key: string | number) => any) => {
348 |     if (this.isMatchPattern || this.isRegExp) {
349 |       throw new Error(`${this.entire} cannot be map`)
350 |     }
351 |     return this.segments.map(callback)
352 |   }
353 | 
354 |   reduce = <T>(
355 |     callback: (buf: T, item: string | number, index: number) => T,
356 |     initial: T
357 |   ): T => {
358 |     if (this.isMatchPattern || this.isRegExp) {
359 |       throw new Error(`${this.entire} cannot be reduce`)
360 |     }
361 |     return this.segments.reduce(callback, initial)
362 |   }
363 | 
364 |   parent = () => {
365 |     return this.slice(0, this.length - 1)
366 |   }
367 | 
368 |   includes = (pattern: Pattern) => {
369 |     const { entire, segments, isMatchPattern } = Path.parse(pattern)
370 |     const cache = this.includesCache.get(entire)
371 |     if (cache !== undefined) return cache
372 |     const cacheWith = (value: boolean): boolean => {
373 |       this.includesCache.set(entire, value)
374 |       return value
375 |     }
376 |     if (this.isMatchPattern) {
377 |       if (!isMatchPattern) {
378 |         return cacheWith(this.match(segments))
379 |       } else {
380 |         throw new Error(`${this.entire} cannot be used to match ${entire}`)
381 |       }
382 |     }
383 |     if (isMatchPattern) {
384 |       throw new Error(`${this.entire} cannot be used to match ${entire}`)
385 |     }
386 |     if (segments.length > this.segments.length) return cacheWith(false)
387 |     for (let i = 0; i < segments.length; i++) {
388 |       if (!isEqual(String(segments[i]), String(this.segments[i]))) {
389 |         return cacheWith(false)
390 |       }
391 |     }
392 |     return cacheWith(true)
393 |   }
394 | 
395 |   transform = <T>(
396 |     regexp: string | RegExp,
397 |     callback: (...args: string[]) => T
398 |   ): T | string => {
399 |     if (!isFn(callback)) return ''
400 |     if (this.isMatchPattern) {
401 |       throw new Error(`${this.entire} cannot be transformed`)
402 |     }
403 |     const reg = new RegExp(regexp)
404 |     const args = this.segments.filter((key) =>
405 |       reg.test(key as string)
406 |     ) as string[]
407 |     return callback(...args)
408 |   }
409 | 
410 |   match = (pattern: Pattern): boolean => {
411 |     const path = Path.parse(pattern)
412 |     const cache = this.matchCache.get(path.entire)
413 |     if (cache !== undefined) {
414 |       if (cache.record && cache.record.score !== undefined) {
415 |         this.matchScore = cache.record.score
416 |       }
417 |       return cache.matched
418 |     }
419 |     const cacheWith = (value: any) => {
420 |       this.matchCache.set(path.entire, value)
421 |       return value
422 |     }
423 |     if (path.isMatchPattern) {
424 |       if (this.isMatchPattern) {
425 |         throw new Error(`${path.entire} cannot match ${this.entire}`)
426 |       } else {
427 |         this.matchScore = 0
428 |         return cacheWith(path.match(this.segments))
429 |       }
430 |     } else {
431 |       if (this.isMatchPattern) {
432 |         if (this.isRegExp) {
433 |           try {
434 |             return this['entire']?.['test']?.(path.entire)
435 |           } finally {
436 |             ;(this.entire as RegExp).lastIndex = 0
437 |           }
438 |         }
439 |         const record = {
440 |           score: 0,
441 |         }
442 |         const result = cacheWith(
443 |           new Matcher(this.tree, record).match(path.segments)
444 |         )
445 |         this.matchScore = record.score
446 |         return result.matched
447 |       } else {
448 |         const record = {
449 |           score: 0,
450 |         }
451 |         const result = cacheWith(
452 |           Matcher.matchSegments(this.segments, path.segments, record)
453 |         )
454 |         this.matchScore = record.score
455 |         return result.matched
456 |       }
457 |     }
458 |   }
459 | 
460 |   //别名组匹配
461 |   matchAliasGroup = (name: Pattern, alias: Pattern) => {
462 |     const namePath = Path.parse(name)
463 |     const aliasPath = Path.parse(alias)
464 |     const nameMatched = this.match(namePath)
465 |     const nameMatchedScore = this.matchScore
466 |     const aliasMatched = this.match(aliasPath)
467 |     const aliasMatchedScore = this.matchScore
468 |     if (this.haveExcludePattern) {
469 |       if (nameMatchedScore >= aliasMatchedScore) {
470 |         return nameMatched
471 |       } else {
472 |         return aliasMatched
473 |       }
474 |     } else {
475 |       return nameMatched || aliasMatched
476 |     }
477 |   }
478 | 
479 |   existIn = (source?: any, start: number | Path = 0) => {
480 |     return existIn(this.segments, source, start)
481 |   }
482 | 
483 |   getIn = (source?: any) => {
484 |     return getIn(this.segments, source)
485 |   }
486 | 
487 |   setIn = (source?: any, value?: any) => {
488 |     setIn(this.segments, source, value)
489 |     return source
490 |   }
491 | 
492 |   deleteIn = (source?: any) => {
493 |     deleteIn(this.segments, source)
494 |     return source
495 |   }
496 | 
497 |   ensureIn = (source?: any, defaults?: any) => {
498 |     const results = this.getIn(source)
499 |     if (results === undefined) {
500 |       this.setIn(source, defaults)
501 |       return this.getIn(source)
502 |     }
503 |     return results
504 |   }
505 | 
506 |   static match(pattern: Pattern) {
507 |     const path = Path.parse(pattern)
508 |     const matcher = (target) => {
509 |       return path.match(target)
510 |     }
511 |     matcher[isMatcher] = true
512 |     matcher.path = path
513 |     return matcher
514 |   }
515 | 
516 |   static isPathPattern(target: any): target is Pattern {
517 |     return !!(
518 |       isStr(target) ||
519 |       isArr(target) ||
520 |       isRegExp(target) ||
521 |       (isFn(target) && target[isMatcher])
522 |     )
523 |   }
524 | 
525 |   static transform<T>(
526 |     pattern: Pattern,
527 |     regexp: string | RegExp,
528 |     callback: (...args: string[]) => T
529 |   ): any {
530 |     return Path.parse(pattern).transform(regexp, callback)
531 |   }
532 | 
533 |   static parse(path: Pattern = '', base?: Pattern): Path {
534 |     if (path instanceof Path) {
535 |       const found = pathCache.get(path.entire)
536 |       if (found) {
537 |         return found
538 |       } else {
539 |         pathCache.set(path.entire, path)
540 |         return path
541 |       }
542 |     } else if (path && path[isMatcher]) {
543 |       return Path.parse(path['path'])
544 |     } else {
545 |       const key_ = base ? Path.parse(base) : ''
546 |       const key = `${path}:${key_}`
547 |       const found = pathCache.get(key)
548 |       if (found) {
549 |         return found
550 |       } else {
551 |         path = new Path(path, base)
552 |         pathCache.set(key, path)
553 |         return path
554 |       }
555 |     }
556 |   }
557 | 
558 |   static getIn = (source: any, pattern: Pattern) => {
559 |     const path = Path.parse(pattern)
560 |     return path.getIn(source)
561 |   }
562 | 
563 |   static setIn = (source: any, pattern: Pattern, value: any) => {
564 |     const path = Path.parse(pattern)
565 |     return path.setIn(source, value)
566 |   }
567 | 
568 |   static deleteIn = (source: any, pattern: Pattern) => {
569 |     const path = Path.parse(pattern)
570 |     return path.deleteIn(source)
571 |   }
572 | 
573 |   static existIn = (source: any, pattern: Pattern, start?: number | Path) => {
574 |     const path = Path.parse(pattern)
575 |     return path.existIn(source, start)
576 |   }
577 | 
578 |   static ensureIn = (source: any, pattern: Pattern, defaultValue?: any) => {
579 |     const path = Path.parse(pattern)
580 |     return path.ensureIn(source, defaultValue)
581 |   }
582 | }
583 | 
584 | export { Pattern }
585 | 
```

--------------------------------------------------------------------------------
/packages/antd/docs/components/ArrayCollapse.zh-CN.md:
--------------------------------------------------------------------------------

```markdown
  1 | # ArrayCollapse
  2 | 
  3 | > 折叠面板,对于每行字段数量较多,联动较多的场景比较适合使用 ArrayCollapse
  4 | >
  5 | > 注意:该组件只适用于 Schema 场景
  6 | 
  7 | ## Markup Schema 案例
  8 | 
  9 | ```tsx
 10 | import React from 'react'
 11 | import {
 12 |   FormItem,
 13 |   Input,
 14 |   ArrayCollapse,
 15 |   FormButtonGroup,
 16 |   Submit,
 17 | } from '@formily/antd'
 18 | import { createForm } from '@formily/core'
 19 | import { FormProvider, createSchemaField } from '@formily/react'
 20 | import { Button } from 'antd'
 21 | 
 22 | const SchemaField = createSchemaField({
 23 |   components: {
 24 |     FormItem,
 25 |     Input,
 26 |     ArrayCollapse,
 27 |   },
 28 | })
 29 | 
 30 | const form = createForm()
 31 | 
 32 | export default () => {
 33 |   return (
 34 |     <FormProvider form={form}>
 35 |       <SchemaField>
 36 |         <SchemaField.Array
 37 |           name="string_array"
 38 |           maxItems={3}
 39 |           x-decorator="FormItem"
 40 |           x-component="ArrayCollapse"
 41 |           x-component-props={{
 42 |             accordion: true,
 43 |             defaultOpenPanelCount: 3,
 44 |           }}
 45 |         >
 46 |           <SchemaField.Void
 47 |             x-component="ArrayCollapse.CollapsePanel"
 48 |             x-component-props={{
 49 |               header: '字符串数组',
 50 |             }}
 51 |           >
 52 |             <SchemaField.Void x-component="ArrayCollapse.Index" />
 53 |             <SchemaField.String
 54 |               name="input"
 55 |               x-decorator="FormItem"
 56 |               title="Input"
 57 |               required
 58 |               x-component="Input"
 59 |             />
 60 |             <SchemaField.Void x-component="ArrayCollapse.Remove" />
 61 |             <SchemaField.Void x-component="ArrayCollapse.MoveUp" />
 62 |             <SchemaField.Void x-component="ArrayCollapse.MoveDown" />
 63 |           </SchemaField.Void>
 64 |           <SchemaField.Void
 65 |             x-component="ArrayCollapse.Addition"
 66 |             title="添加条目"
 67 |           />
 68 |         </SchemaField.Array>
 69 |         <SchemaField.Array
 70 |           name="array"
 71 |           maxItems={3}
 72 |           x-decorator="FormItem"
 73 |           x-component="ArrayCollapse"
 74 |         >
 75 |           <SchemaField.Object
 76 |             x-component="ArrayCollapse.CollapsePanel"
 77 |             x-component-props={{
 78 |               header: '对象数组',
 79 |             }}
 80 |           >
 81 |             <SchemaField.Void x-component="ArrayCollapse.Index" />
 82 |             <SchemaField.String
 83 |               name="input"
 84 |               x-decorator="FormItem"
 85 |               title="Input"
 86 |               required
 87 |               x-component="Input"
 88 |             />
 89 |             <SchemaField.Void x-component="ArrayCollapse.Remove" />
 90 |             <SchemaField.Void x-component="ArrayCollapse.MoveUp" />
 91 |             <SchemaField.Void x-component="ArrayCollapse.MoveDown" />
 92 |           </SchemaField.Object>
 93 |           <SchemaField.Void
 94 |             x-component="ArrayCollapse.Addition"
 95 |             title="添加条目"
 96 |           />
 97 |         </SchemaField.Array>
 98 |         <SchemaField.Array
 99 |           name="string_array_unshift"
100 |           maxItems={3}
101 |           x-decorator="FormItem"
102 |           x-component="ArrayCollapse"
103 |           x-component-props={{
104 |             defaultOpenPanelCount: 8,
105 |           }}
106 |         >
107 |           <SchemaField.Void
108 |             x-component="ArrayCollapse.CollapsePanel"
109 |             x-component-props={{
110 |               header: '字符串数组',
111 |             }}
112 |           >
113 |             <SchemaField.Void x-component="ArrayCollapse.Index" />
114 |             <SchemaField.String
115 |               name="input"
116 |               x-decorator="FormItem"
117 |               title="Input"
118 |               required
119 |               x-component="Input"
120 |             />
121 |             <SchemaField.Void x-component="ArrayCollapse.Remove" />
122 |             <SchemaField.Void x-component="ArrayCollapse.MoveUp" />
123 |             <SchemaField.Void x-component="ArrayCollapse.MoveDown" />
124 |           </SchemaField.Void>
125 |           <SchemaField.Void
126 |             x-component="ArrayCollapse.Addition"
127 |             title="添加条目(unshift)"
128 |             x-component-props={{
129 |               method: 'unshift',
130 |             }}
131 |           />
132 |         </SchemaField.Array>
133 |       </SchemaField>
134 |       <FormButtonGroup>
135 |         <Button
136 |           onClick={() => {
137 |             form.setInitialValues({
138 |               array: Array.from({ length: 10 }).map(() => ({
139 |                 input: 'default value',
140 |               })),
141 |               string_array: Array.from({ length: 10 }).map(
142 |                 () => 'default value'
143 |               ),
144 |               string_array_unshift: Array.from({ length: 10 }).map(
145 |                 () => 'default value'
146 |               ),
147 |             })
148 |           }}
149 |         >
150 |           加载默认数据
151 |         </Button>
152 |         <Submit onSubmit={console.log}>提交</Submit>
153 |       </FormButtonGroup>
154 |     </FormProvider>
155 |   )
156 | }
157 | ```
158 | 
159 | ## JSON Schema 案例
160 | 
161 | ```tsx
162 | import React from 'react'
163 | import {
164 |   FormItem,
165 |   Input,
166 |   ArrayCollapse,
167 |   FormButtonGroup,
168 |   Submit,
169 | } from '@formily/antd'
170 | import { createForm } from '@formily/core'
171 | import { FormProvider, createSchemaField } from '@formily/react'
172 | 
173 | const SchemaField = createSchemaField({
174 |   components: {
175 |     FormItem,
176 |     Input,
177 |     ArrayCollapse,
178 |   },
179 | })
180 | 
181 | const form = createForm()
182 | 
183 | const schema = {
184 |   type: 'object',
185 |   properties: {
186 |     string_array: {
187 |       type: 'array',
188 |       'x-component': 'ArrayCollapse',
189 |       'x-component-props': {
190 |         onAdd: (index: number) => {
191 |           console.log('Adding ' + index + ' item')
192 |         },
193 |       },
194 |       maxItems: 3,
195 |       'x-decorator': 'FormItem',
196 |       items: {
197 |         type: 'void',
198 |         'x-component': 'ArrayCollapse.CollapsePanel',
199 |         'x-component-props': {
200 |           header: '字符串数组',
201 |         },
202 |         properties: {
203 |           index: {
204 |             type: 'void',
205 |             'x-component': 'ArrayCollapse.Index',
206 |           },
207 |           input: {
208 |             type: 'string',
209 |             'x-decorator': 'FormItem',
210 |             title: 'Input',
211 |             required: true,
212 |             'x-component': 'Input',
213 |           },
214 |           remove: {
215 |             type: 'void',
216 |             'x-component': 'ArrayCollapse.Remove',
217 |           },
218 |           moveUp: {
219 |             type: 'void',
220 |             'x-component': 'ArrayCollapse.MoveUp',
221 |           },
222 |           moveDown: {
223 |             type: 'void',
224 |             'x-component': 'ArrayCollapse.MoveDown',
225 |           },
226 |         },
227 |       },
228 |       properties: {
229 |         addition: {
230 |           type: 'void',
231 |           title: '添加条目',
232 |           'x-component': 'ArrayCollapse.Addition',
233 |         },
234 |       },
235 |     },
236 |     array: {
237 |       type: 'array',
238 |       'x-component': 'ArrayCollapse',
239 |       maxItems: 3,
240 |       'x-decorator': 'FormItem',
241 |       items: {
242 |         type: 'object',
243 |         'x-component': 'ArrayCollapse.CollapsePanel',
244 |         'x-component-props': {
245 |           header: '对象数组',
246 |         },
247 |         properties: {
248 |           index: {
249 |             type: 'void',
250 |             'x-component': 'ArrayCollapse.Index',
251 |           },
252 |           input: {
253 |             type: 'string',
254 |             'x-decorator': 'FormItem',
255 |             title: 'Input',
256 |             required: true,
257 |             'x-component': 'Input',
258 |           },
259 |           remove: {
260 |             type: 'void',
261 |             'x-component': 'ArrayCollapse.Remove',
262 |           },
263 |           moveUp: {
264 |             type: 'void',
265 |             'x-component': 'ArrayCollapse.MoveUp',
266 |           },
267 |           moveDown: {
268 |             type: 'void',
269 |             'x-component': 'ArrayCollapse.MoveDown',
270 |           },
271 |         },
272 |       },
273 |       properties: {
274 |         addition: {
275 |           type: 'void',
276 |           title: '添加条目',
277 |           'x-component': 'ArrayCollapse.Addition',
278 |         },
279 |       },
280 |     },
281 |     array_unshift: {
282 |       type: 'array',
283 |       'x-component': 'ArrayCollapse',
284 |       maxItems: 3,
285 |       'x-decorator': 'FormItem',
286 |       items: {
287 |         type: 'object',
288 |         'x-component': 'ArrayCollapse.CollapsePanel',
289 |         'x-component-props': {
290 |           header: '对象数组',
291 |         },
292 |         properties: {
293 |           index: {
294 |             type: 'void',
295 |             'x-component': 'ArrayCollapse.Index',
296 |           },
297 |           input: {
298 |             type: 'string',
299 |             'x-decorator': 'FormItem',
300 |             title: 'Input',
301 |             required: true,
302 |             'x-component': 'Input',
303 |           },
304 |           remove: {
305 |             type: 'void',
306 |             'x-component': 'ArrayCollapse.Remove',
307 |           },
308 |           moveUp: {
309 |             type: 'void',
310 |             'x-component': 'ArrayCollapse.MoveUp',
311 |           },
312 |           moveDown: {
313 |             type: 'void',
314 |             'x-component': 'ArrayCollapse.MoveDown',
315 |           },
316 |         },
317 |       },
318 |       properties: {
319 |         addition: {
320 |           type: 'void',
321 |           title: '添加条目(unshift)',
322 |           'x-component': 'ArrayCollapse.Addition',
323 |           'x-component-props': {
324 |             method: 'unshift',
325 |           },
326 |         },
327 |       },
328 |     },
329 |   },
330 | }
331 | 
332 | export default () => {
333 |   return (
334 |     <FormProvider form={form}>
335 |       <SchemaField schema={schema} />
336 |       <FormButtonGroup>
337 |         <Submit onSubmit={console.log}>提交</Submit>
338 |       </FormButtonGroup>
339 |     </FormProvider>
340 |   )
341 | }
342 | ```
343 | 
344 | ## Effects 联动案例
345 | 
346 | ```tsx
347 | import React from 'react'
348 | import {
349 |   FormItem,
350 |   Input,
351 |   ArrayCollapse,
352 |   FormButtonGroup,
353 |   Submit,
354 | } from '@formily/antd'
355 | import { createForm, onFieldChange, onFieldReact } from '@formily/core'
356 | import { FormProvider, createSchemaField } from '@formily/react'
357 | 
358 | const SchemaField = createSchemaField({
359 |   components: {
360 |     FormItem,
361 |     Input,
362 |     ArrayCollapse,
363 |   },
364 | })
365 | 
366 | const form = createForm({
367 |   effects: () => {
368 |     //主动联动模式
369 |     onFieldChange('array.*.aa', ['value'], (field, form) => {
370 |       form.setFieldState(field.query('.bb'), (state) => {
371 |         state.visible = field.value != '123'
372 |       })
373 |     })
374 |     //被动联动模式
375 |     onFieldReact('array.*.dd', (field) => {
376 |       field.visible = field.query('.cc').get('value') != '123'
377 |     })
378 |   },
379 | })
380 | 
381 | export default () => {
382 |   return (
383 |     <FormProvider form={form}>
384 |       <SchemaField>
385 |         <SchemaField.Array
386 |           name="array"
387 |           maxItems={3}
388 |           x-component="ArrayCollapse"
389 |           x-decorator="FormItem"
390 |           x-component-props={{
391 |             title: '对象数组',
392 |           }}
393 |         >
394 |           <SchemaField.Object
395 |             x-component="ArrayCollapse.CollapsePanel"
396 |             x-component-props={{
397 |               header: '对象数组',
398 |             }}
399 |           >
400 |             <SchemaField.Void x-component="ArrayCollapse.Index" />
401 |             <SchemaField.String
402 |               name="aa"
403 |               x-decorator="FormItem"
404 |               title="AA"
405 |               required
406 |               description="AA输入123时隐藏BB"
407 |               x-component="Input"
408 |             />
409 |             <SchemaField.String
410 |               name="bb"
411 |               x-decorator="FormItem"
412 |               title="BB"
413 |               required
414 |               x-component="Input"
415 |             />
416 |             <SchemaField.String
417 |               name="cc"
418 |               x-decorator="FormItem"
419 |               title="CC"
420 |               required
421 |               description="CC输入123时隐藏DD"
422 |               x-component="Input"
423 |             />
424 |             <SchemaField.String
425 |               name="dd"
426 |               x-decorator="FormItem"
427 |               title="DD"
428 |               required
429 |               x-component="Input"
430 |             />
431 |             <SchemaField.Void x-component="ArrayCollapse.Remove" />
432 |             <SchemaField.Void x-component="ArrayCollapse.MoveUp" />
433 |             <SchemaField.Void x-component="ArrayCollapse.MoveDown" />
434 |           </SchemaField.Object>
435 |           <SchemaField.Void
436 |             x-component="ArrayCollapse.Addition"
437 |             title="添加条目"
438 |           />
439 |         </SchemaField.Array>
440 |       </SchemaField>
441 |       <FormButtonGroup>
442 |         <Submit onSubmit={console.log}>提交</Submit>
443 |       </FormButtonGroup>
444 |     </FormProvider>
445 |   )
446 | }
447 | ```
448 | 
449 | ## JSON Schema 联动案例
450 | 
451 | ```tsx
452 | import React from 'react'
453 | import {
454 |   FormItem,
455 |   Input,
456 |   ArrayCollapse,
457 |   FormButtonGroup,
458 |   Submit,
459 | } from '@formily/antd'
460 | import { createForm } from '@formily/core'
461 | import { FormProvider, createSchemaField } from '@formily/react'
462 | 
463 | const SchemaField = createSchemaField({
464 |   components: {
465 |     FormItem,
466 |     Input,
467 |     ArrayCollapse,
468 |   },
469 | })
470 | 
471 | const form = createForm()
472 | 
473 | const schema = {
474 |   type: 'object',
475 |   properties: {
476 |     array: {
477 |       type: 'array',
478 |       'x-component': 'ArrayCollapse',
479 |       maxItems: 3,
480 |       title: '对象数组',
481 |       items: {
482 |         type: 'object',
483 |         'x-component': 'ArrayCollapse.CollapsePanel',
484 |         'x-component-props': {
485 |           header: '对象数组',
486 |         },
487 |         properties: {
488 |           index: {
489 |             type: 'void',
490 |             'x-component': 'ArrayCollapse.Index',
491 |           },
492 |           aa: {
493 |             type: 'string',
494 |             'x-decorator': 'FormItem',
495 |             title: 'AA',
496 |             required: true,
497 |             'x-component': 'Input',
498 |             description: '输入123',
499 |           },
500 |           bb: {
501 |             type: 'string',
502 |             title: 'BB',
503 |             required: true,
504 |             'x-decorator': 'FormItem',
505 |             'x-component': 'Input',
506 |             'x-reactions': [
507 |               {
508 |                 dependencies: ['.aa'],
509 |                 when: "{{$deps[0] != '123'}}",
510 |                 fulfill: {
511 |                   schema: {
512 |                     title: 'BB',
513 |                     'x-disabled': true,
514 |                   },
515 |                 },
516 |                 otherwise: {
517 |                   schema: {
518 |                     title: 'Changed',
519 |                     'x-disabled': false,
520 |                   },
521 |                 },
522 |               },
523 |             ],
524 |           },
525 |           remove: {
526 |             type: 'void',
527 |             'x-component': 'ArrayCollapse.Remove',
528 |           },
529 |           moveUp: {
530 |             type: 'void',
531 |             'x-component': 'ArrayCollapse.MoveUp',
532 |           },
533 |           moveDown: {
534 |             type: 'void',
535 |             'x-component': 'ArrayCollapse.MoveDown',
536 |           },
537 |         },
538 |       },
539 |       properties: {
540 |         addition: {
541 |           type: 'void',
542 |           title: '添加条目',
543 |           'x-component': 'ArrayCollapse.Addition',
544 |         },
545 |       },
546 |     },
547 |   },
548 | }
549 | 
550 | export default () => {
551 |   return (
552 |     <FormProvider form={form}>
553 |       <SchemaField schema={schema} />
554 |       <FormButtonGroup>
555 |         <Submit onSubmit={console.log}>提交</Submit>
556 |       </FormButtonGroup>
557 |     </FormProvider>
558 |   )
559 | }
560 | ```
561 | 
562 | ## API
563 | 
564 | ### ArrayCollapse
565 | 
566 | 参考 https://ant.design/components/collapse-cn/
567 | 
568 | ### ArrayCollapse.CollapsePanel
569 | 
570 | 参考 https://ant.design/components/collapse-cn/
571 | 
572 | ### ArrayCollapse.Addition
573 | 
574 | > 添加按钮
575 | 
576 | 扩展属性
577 | 
578 | | 属性名       | 类型                  | 描述     | 默认值   |
579 | | ------------ | --------------------- | -------- | -------- |
580 | | title        | ReactText             | 文案     |          |
581 | | method       | `'push' \| 'unshift'` | 添加方式 | `'push'` |
582 | | defaultValue | `any`                 | 默认值   |          |
583 | 
584 | 其余参考 https://ant.design/components/button-cn/
585 | 
586 | 注意:title 属性可以接收 Field 模型中的 title 映射,也就是在 Field 上传 title 也是生效的
587 | 
588 | 注意:使用`onClick={e => e.preventDefault()}`可禁用默认行为。
589 | 
590 | ### ArrayCollapse.Remove
591 | 
592 | > 删除按钮
593 | 
594 | | 属性名 | 类型      | 描述 | 默认值 |
595 | | ------ | --------- | ---- | ------ |
596 | | title  | ReactText | 文案 |        |
597 | 
598 | 其余参考 https://ant.design/components/icon-cn/
599 | 
600 | 注意:title 属性可以接收 Field 模型中的 title 映射,也就是在 Field 上传 title 也是生效的
601 | 
602 | 注意:使用`onClick={e => e.preventDefault()}`可禁用默认行为。
603 | 
604 | ### ArrayCollapse.MoveDown
605 | 
606 | > 下移按钮
607 | 
608 | | 属性名 | 类型      | 描述 | 默认值 |
609 | | ------ | --------- | ---- | ------ |
610 | | title  | ReactText | 文案 |        |
611 | 
612 | 其余参考 https://ant.design/components/icon-cn/
613 | 
614 | 注意:title 属性可以接收 Field 模型中的 title 映射,也就是在 Field 上传 title 也是生效的
615 | 
616 | 注意:使用`onClick={e => e.preventDefault()}`可禁用默认行为。
617 | 
618 | ### ArrayCollapse.MoveUp
619 | 
620 | > 上移按钮
621 | 
622 | | 属性名 | 类型      | 描述 | 默认值 |
623 | | ------ | --------- | ---- | ------ |
624 | | title  | ReactText | 文案 |        |
625 | 
626 | 其余参考 https://ant.design/components/icon-cn/
627 | 
628 | 注意:title 属性可以接收 Field 模型中的 title 映射,也就是在 Field 上传 title 也是生效的
629 | 
630 | 注意:使用`onClick={e => e.preventDefault()}`可禁用默认行为。
631 | 
632 | ### ArrayCollapse.Index
633 | 
634 | > 索引渲染器
635 | 
636 | 无属性
637 | 
638 | ### ArrayCollapse.useIndex
639 | 
640 | > 读取当前渲染行索引的 React Hook
641 | 
642 | ### ArrayCollapse.useRecord
643 | 
644 | > 读取当前渲染记录的 React Hook
645 | 
```

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

```markdown
  1 | # ArrayCollapse
  2 | 
  3 | > 折叠面板,对于每行字段数量较多,联动较多的场景比较适合使用 ArrayCollapse
  4 | >
  5 | > 注意:该组件只适用于 Schema 场景
  6 | 
  7 | ## Markup Schema 案例
  8 | 
  9 | ```tsx
 10 | import React from 'react'
 11 | import {
 12 |   FormItem,
 13 |   Input,
 14 |   ArrayCollapse,
 15 |   FormButtonGroup,
 16 |   Radio,
 17 |   Submit,
 18 | } from '@formily/next'
 19 | import { createForm } from '@formily/core'
 20 | import { FormProvider, createSchemaField } from '@formily/react'
 21 | import { Button } from '@alifd/next'
 22 | 
 23 | const SchemaField = createSchemaField({
 24 |   components: {
 25 |     Radio,
 26 |     FormItem,
 27 |     Input,
 28 |     ArrayCollapse,
 29 |   },
 30 | })
 31 | 
 32 | const form = createForm()
 33 | 
 34 | export default () => {
 35 |   return (
 36 |     <FormProvider form={form}>
 37 |       <SchemaField>
 38 |         <SchemaField.Array
 39 |           name="string_array"
 40 |           maxItems={3}
 41 |           x-decorator="FormItem"
 42 |           x-component="ArrayCollapse"
 43 |           x-component-props={{
 44 |             accordion: true,
 45 |             defaultOpenPanelCount: 3,
 46 |           }}
 47 |         >
 48 |           <SchemaField.Void
 49 |             x-component="ArrayCollapse.CollapsePanel"
 50 |             x-component-props={{
 51 |               title: '字符串数组',
 52 |             }}
 53 |           >
 54 |             <SchemaField.Void x-component="ArrayCollapse.Index" />
 55 |             <SchemaField.String
 56 |               name="input"
 57 |               x-decorator="FormItem"
 58 |               title="Input"
 59 |               required
 60 |               x-component="Input"
 61 |             />
 62 |             <SchemaField.Void x-component="ArrayCollapse.Remove" />
 63 |             <SchemaField.Void x-component="ArrayCollapse.MoveUp" />
 64 |             <SchemaField.Void x-component="ArrayCollapse.MoveDown" />
 65 |           </SchemaField.Void>
 66 |           <SchemaField.Void
 67 |             x-component="ArrayCollapse.Addition"
 68 |             title="添加条目"
 69 |           />
 70 |         </SchemaField.Array>
 71 |         <SchemaField.Array
 72 |           name="array"
 73 |           maxItems={3}
 74 |           x-decorator="FormItem"
 75 |           x-component="ArrayCollapse"
 76 |         >
 77 |           <SchemaField.Object
 78 |             x-component="ArrayCollapse.CollapsePanel"
 79 |             x-component-props={{
 80 |               title: '对象数组',
 81 |             }}
 82 |           >
 83 |             <SchemaField.Void x-component="ArrayCollapse.Index" />
 84 |             <SchemaField.String
 85 |               name="input"
 86 |               x-decorator="FormItem"
 87 |               title="Input"
 88 |               required
 89 |               x-component="Input"
 90 |             />
 91 |             <SchemaField.String
 92 |               name="radio"
 93 |               x-decorator="FormItem"
 94 |               title="Radio"
 95 |               enum={[1, 2]}
 96 |               required
 97 |               x-component="Radio.Group"
 98 |             />
 99 |             <SchemaField.Void x-component="ArrayCollapse.Remove" />
100 |             <SchemaField.Void x-component="ArrayCollapse.MoveUp" />
101 |             <SchemaField.Void x-component="ArrayCollapse.MoveDown" />
102 |           </SchemaField.Object>
103 |           <SchemaField.Void
104 |             x-component="ArrayCollapse.Addition"
105 |             title="添加条目"
106 |           />
107 |         </SchemaField.Array>
108 |         <SchemaField.Array
109 |           name="string_array_unshift"
110 |           maxItems={3}
111 |           x-decorator="FormItem"
112 |           x-component="ArrayCollapse"
113 |           x-component-props={{
114 |             defaultOpenPanelCount: 8,
115 |           }}
116 |         >
117 |           <SchemaField.Void
118 |             x-component="ArrayCollapse.CollapsePanel"
119 |             x-component-props={{
120 |               title: '字符串数组',
121 |             }}
122 |           >
123 |             <SchemaField.Void x-component="ArrayCollapse.Index" />
124 |             <SchemaField.String
125 |               name="input"
126 |               x-decorator="FormItem"
127 |               title="Input"
128 |               required
129 |               x-component="Input"
130 |             />
131 |             <SchemaField.Void x-component="ArrayCollapse.Remove" />
132 |             <SchemaField.Void x-component="ArrayCollapse.MoveUp" />
133 |             <SchemaField.Void x-component="ArrayCollapse.MoveDown" />
134 |           </SchemaField.Void>
135 |           <SchemaField.Void
136 |             x-component="ArrayCollapse.Addition"
137 |             title="添加条目(unshift)"
138 |             x-component-props={{
139 |               method: 'unshift',
140 |             }}
141 |           />
142 |         </SchemaField.Array>
143 |       </SchemaField>
144 |       <FormButtonGroup>
145 |         <Button
146 |           onClick={() => {
147 |             form.setInitialValues({
148 |               array: Array.from({ length: 10 }).map(() => ({
149 |                 input: 'default value',
150 |               })),
151 |               string_array: Array.from({ length: 10 }).map(
152 |                 () => 'default value'
153 |               ),
154 |               string_array_unshift: Array.from({ length: 10 }).map(
155 |                 () => 'default value'
156 |               ),
157 |             })
158 |           }}
159 |         >
160 |           加载默认数据
161 |         </Button>
162 |         <Submit onSubmit={console.log}>提交</Submit>
163 |       </FormButtonGroup>
164 |     </FormProvider>
165 |   )
166 | }
167 | ```
168 | 
169 | ## JSON Schema 案例
170 | 
171 | ```tsx
172 | import React from 'react'
173 | import {
174 |   FormItem,
175 |   Input,
176 |   ArrayCollapse,
177 |   FormButtonGroup,
178 |   Submit,
179 | } from '@formily/next'
180 | import { createForm } from '@formily/core'
181 | import { FormProvider, createSchemaField } from '@formily/react'
182 | 
183 | const SchemaField = createSchemaField({
184 |   components: {
185 |     FormItem,
186 |     Input,
187 |     ArrayCollapse,
188 |   },
189 | })
190 | 
191 | const form = createForm()
192 | 
193 | const schema = {
194 |   type: 'object',
195 |   properties: {
196 |     string_array: {
197 |       type: 'array',
198 |       'x-component': 'ArrayCollapse',
199 |       maxItems: 3,
200 |       'x-decorator': 'FormItem',
201 |       items: {
202 |         type: 'void',
203 |         'x-component': 'ArrayCollapse.CollapsePanel',
204 |         'x-component-props': {
205 |           title: '字符串数组',
206 |         },
207 |         properties: {
208 |           index: {
209 |             type: 'void',
210 |             'x-component': 'ArrayCollapse.Index',
211 |           },
212 |           input: {
213 |             type: 'string',
214 |             'x-decorator': 'FormItem',
215 |             title: 'Input',
216 |             required: true,
217 |             'x-component': 'Input',
218 |           },
219 |           remove: {
220 |             type: 'void',
221 |             'x-component': 'ArrayCollapse.Remove',
222 |           },
223 |           moveUp: {
224 |             type: 'void',
225 |             'x-component': 'ArrayCollapse.MoveUp',
226 |           },
227 |           moveDown: {
228 |             type: 'void',
229 |             'x-component': 'ArrayCollapse.MoveDown',
230 |           },
231 |         },
232 |       },
233 |       properties: {
234 |         addition: {
235 |           type: 'void',
236 |           title: '添加条目',
237 |           'x-component': 'ArrayCollapse.Addition',
238 |         },
239 |       },
240 |     },
241 |     array: {
242 |       type: 'array',
243 |       'x-component': 'ArrayCollapse',
244 |       maxItems: 3,
245 |       'x-decorator': 'FormItem',
246 |       items: {
247 |         type: 'object',
248 |         'x-component': 'ArrayCollapse.CollapsePanel',
249 |         'x-component-props': {
250 |           title: '对象数组',
251 |         },
252 |         properties: {
253 |           index: {
254 |             type: 'void',
255 |             'x-component': 'ArrayCollapse.Index',
256 |           },
257 |           input: {
258 |             type: 'string',
259 |             'x-decorator': 'FormItem',
260 |             title: 'Input',
261 |             required: true,
262 |             'x-component': 'Input',
263 |           },
264 |           remove: {
265 |             type: 'void',
266 |             'x-component': 'ArrayCollapse.Remove',
267 |           },
268 |           moveUp: {
269 |             type: 'void',
270 |             'x-component': 'ArrayCollapse.MoveUp',
271 |           },
272 |           moveDown: {
273 |             type: 'void',
274 |             'x-component': 'ArrayCollapse.MoveDown',
275 |           },
276 |         },
277 |       },
278 |       properties: {
279 |         addition: {
280 |           type: 'void',
281 |           title: '添加条目',
282 |           'x-component': 'ArrayCollapse.Addition',
283 |         },
284 |       },
285 |     },
286 |     array_unshift: {
287 |       type: 'array',
288 |       'x-component': 'ArrayCollapse',
289 |       maxItems: 3,
290 |       'x-decorator': 'FormItem',
291 |       items: {
292 |         type: 'object',
293 |         'x-component': 'ArrayCollapse.CollapsePanel',
294 |         'x-component-props': {
295 |           title: '对象数组',
296 |         },
297 |         properties: {
298 |           index: {
299 |             type: 'void',
300 |             'x-component': 'ArrayCollapse.Index',
301 |           },
302 |           input: {
303 |             type: 'string',
304 |             'x-decorator': 'FormItem',
305 |             title: 'Input',
306 |             required: true,
307 |             'x-component': 'Input',
308 |           },
309 |           remove: {
310 |             type: 'void',
311 |             'x-component': 'ArrayCollapse.Remove',
312 |           },
313 |           moveUp: {
314 |             type: 'void',
315 |             'x-component': 'ArrayCollapse.MoveUp',
316 |           },
317 |           moveDown: {
318 |             type: 'void',
319 |             'x-component': 'ArrayCollapse.MoveDown',
320 |           },
321 |         },
322 |       },
323 |       properties: {
324 |         addition: {
325 |           type: 'void',
326 |           title: '添加条目(unshift)',
327 |           'x-component': 'ArrayCollapse.Addition',
328 |           'x-component-props': {
329 |             method: 'unshift',
330 |           },
331 |         },
332 |       },
333 |     },
334 |   },
335 | }
336 | 
337 | export default () => {
338 |   return (
339 |     <FormProvider form={form}>
340 |       <SchemaField schema={schema} />
341 |       <FormButtonGroup>
342 |         <Submit onSubmit={console.log}>提交</Submit>
343 |       </FormButtonGroup>
344 |     </FormProvider>
345 |   )
346 | }
347 | ```
348 | 
349 | ## Effects 联动案例
350 | 
351 | ```tsx
352 | import React from 'react'
353 | import {
354 |   FormItem,
355 |   Input,
356 |   ArrayCollapse,
357 |   FormButtonGroup,
358 |   Submit,
359 | } from '@formily/next'
360 | import { createForm, onFieldChange, onFieldReact } from '@formily/core'
361 | import { FormProvider, createSchemaField } from '@formily/react'
362 | 
363 | const SchemaField = createSchemaField({
364 |   components: {
365 |     FormItem,
366 |     Input,
367 |     ArrayCollapse,
368 |   },
369 | })
370 | 
371 | const form = createForm({
372 |   effects: () => {
373 |     //主动联动模式
374 |     onFieldChange('array.*.aa', ['value'], (field, form) => {
375 |       form.setFieldState(field.query('.bb'), (state) => {
376 |         state.visible = field.value != '123'
377 |       })
378 |     })
379 |     //被动联动模式
380 |     onFieldReact('array.*.dd', (field) => {
381 |       field.visible = field.query('.cc').get('value') != '123'
382 |     })
383 |   },
384 | })
385 | 
386 | export default () => {
387 |   return (
388 |     <FormProvider form={form}>
389 |       <SchemaField>
390 |         <SchemaField.Array
391 |           name="array"
392 |           maxItems={3}
393 |           x-component="ArrayCollapse"
394 |           x-decorator="FormItem"
395 |           x-component-props={{
396 |             title: '对象数组',
397 |           }}
398 |         >
399 |           <SchemaField.Object
400 |             x-component="ArrayCollapse.CollapsePanel"
401 |             x-component-props={{
402 |               title: '对象数组',
403 |             }}
404 |           >
405 |             <SchemaField.Void x-component="ArrayCollapse.Index" />
406 |             <SchemaField.String
407 |               name="aa"
408 |               x-decorator="FormItem"
409 |               title="AA"
410 |               required
411 |               description="AA输入123时隐藏BB"
412 |               x-component="Input"
413 |             />
414 |             <SchemaField.String
415 |               name="bb"
416 |               x-decorator="FormItem"
417 |               title="BB"
418 |               required
419 |               x-component="Input"
420 |             />
421 |             <SchemaField.String
422 |               name="cc"
423 |               x-decorator="FormItem"
424 |               title="CC"
425 |               required
426 |               description="CC输入123时隐藏DD"
427 |               x-component="Input"
428 |             />
429 |             <SchemaField.String
430 |               name="dd"
431 |               x-decorator="FormItem"
432 |               title="DD"
433 |               required
434 |               x-component="Input"
435 |             />
436 |             <SchemaField.Void x-component="ArrayCollapse.Remove" />
437 |             <SchemaField.Void x-component="ArrayCollapse.MoveUp" />
438 |             <SchemaField.Void x-component="ArrayCollapse.MoveDown" />
439 |           </SchemaField.Object>
440 |           <SchemaField.Void
441 |             x-component="ArrayCollapse.Addition"
442 |             title="添加条目"
443 |           />
444 |         </SchemaField.Array>
445 |       </SchemaField>
446 |       <FormButtonGroup>
447 |         <Submit onSubmit={console.log}>提交</Submit>
448 |       </FormButtonGroup>
449 |     </FormProvider>
450 |   )
451 | }
452 | ```
453 | 
454 | ## JSON Schema 联动案例
455 | 
456 | ```tsx
457 | import React from 'react'
458 | import {
459 |   FormItem,
460 |   Input,
461 |   ArrayCollapse,
462 |   FormButtonGroup,
463 |   Submit,
464 | } from '@formily/next'
465 | import { createForm } from '@formily/core'
466 | import { FormProvider, createSchemaField } from '@formily/react'
467 | 
468 | const SchemaField = createSchemaField({
469 |   components: {
470 |     FormItem,
471 |     Input,
472 |     ArrayCollapse,
473 |   },
474 | })
475 | 
476 | const form = createForm()
477 | 
478 | const schema = {
479 |   type: 'object',
480 |   properties: {
481 |     array: {
482 |       type: 'array',
483 |       'x-component': 'ArrayCollapse',
484 |       maxItems: 3,
485 |       title: '对象数组',
486 |       items: {
487 |         type: 'object',
488 |         'x-component': 'ArrayCollapse.CollapsePanel',
489 |         'x-component-props': {
490 |           title: '对象数组',
491 |         },
492 |         properties: {
493 |           index: {
494 |             type: 'void',
495 |             'x-component': 'ArrayCollapse.Index',
496 |           },
497 |           aa: {
498 |             type: 'string',
499 |             'x-decorator': 'FormItem',
500 |             title: 'AA',
501 |             required: true,
502 |             'x-component': 'Input',
503 |             description: '输入123',
504 |           },
505 |           bb: {
506 |             type: 'string',
507 |             title: 'BB',
508 |             required: true,
509 |             'x-decorator': 'FormItem',
510 |             'x-component': 'Input',
511 |             'x-reactions': [
512 |               {
513 |                 dependencies: ['.aa'],
514 |                 when: "{{$deps[0] != '123'}}",
515 |                 fulfill: {
516 |                   schema: {
517 |                     title: 'BB',
518 |                     'x-disabled': true,
519 |                   },
520 |                 },
521 |                 otherwise: {
522 |                   schema: {
523 |                     title: 'Changed',
524 |                     'x-disabled': false,
525 |                   },
526 |                 },
527 |               },
528 |             ],
529 |           },
530 |           remove: {
531 |             type: 'void',
532 |             'x-component': 'ArrayCollapse.Remove',
533 |           },
534 |           moveUp: {
535 |             type: 'void',
536 |             'x-component': 'ArrayCollapse.MoveUp',
537 |           },
538 |           moveDown: {
539 |             type: 'void',
540 |             'x-component': 'ArrayCollapse.MoveDown',
541 |           },
542 |         },
543 |       },
544 |       properties: {
545 |         addition: {
546 |           type: 'void',
547 |           title: '添加条目',
548 |           'x-component': 'ArrayCollapse.Addition',
549 |         },
550 |       },
551 |     },
552 |   },
553 | }
554 | 
555 | export default () => {
556 |   return (
557 |     <FormProvider form={form}>
558 |       <SchemaField schema={schema} />
559 |       <FormButtonGroup>
560 |         <Submit onSubmit={console.log}>提交</Submit>
561 |       </FormButtonGroup>
562 |     </FormProvider>
563 |   )
564 | }
565 | ```
566 | 
567 | ## API
568 | 
569 | ### ArrayCollapse
570 | 
571 | 参考 https://fusion.design/pc/component/collapse
572 | 
573 | 扩展属性
574 | 
575 | | 属性名                | 类型   | 描述                | 默认值 |
576 | | --------------------- | ------ | ------------------- | ------ |
577 | | defaultOpenPanelCount | number | 默认展开 Panel 数量 | 5      |
578 | 
579 | ### ArrayCollapse.CollapsePanel
580 | 
581 | 参考 https://fusion.design/pc/component/collapse
582 | 
583 | ### ArrayCollapse.Addition
584 | 
585 | > 添加按钮
586 | 
587 | 扩展属性
588 | 
589 | | 属性名       | 类型                  | 描述     | 默认值   |
590 | | ------------ | --------------------- | -------- | -------- |
591 | | title        | ReactText             | 文案     |          |
592 | | method       | `'push' \| 'unshift'` | 添加方式 | `'push'` |
593 | | defaultValue | `any`                 | 默认值   |          |
594 | 
595 | 其余参考 https://fusion.design/pc/component/basic/button
596 | 
597 | 注意:title 属性可以接收 Field 模型中的 title 映射,也就是在 Field 上传 title 也是生效的
598 | 
599 | ### ArrayCollapse.Remove
600 | 
601 | > 删除按钮
602 | 
603 | | 属性名 | 类型      | 描述 | 默认值 |
604 | | ------ | --------- | ---- | ------ |
605 | | title  | ReactText | 文案 |        |
606 | 
607 | 其余参考 https://ant.design/components/icon-cn/
608 | 
609 | 注意:title 属性可以接收 Field 模型中的 title 映射,也就是在 Field 上传 title 也是生效的
610 | 
611 | ### ArrayCollapse.MoveDown
612 | 
613 | > 下移按钮
614 | 
615 | | 属性名 | 类型      | 描述 | 默认值 |
616 | | ------ | --------- | ---- | ------ |
617 | | title  | ReactText | 文案 |        |
618 | 
619 | 其余参考 https://ant.design/components/icon-cn/
620 | 
621 | 注意:title 属性可以接收 Field 模型中的 title 映射,也就是在 Field 上传 title 也是生效的
622 | 
623 | ### ArrayCollapse.MoveUp
624 | 
625 | > 上移按钮
626 | 
627 | | 属性名 | 类型      | 描述 | 默认值 |
628 | | ------ | --------- | ---- | ------ |
629 | | title  | ReactText | 文案 |        |
630 | 
631 | 其余参考 https://ant.design/components/icon-cn/
632 | 
633 | 注意:title 属性可以接收 Field 模型中的 title 映射,也就是在 Field 上传 title 也是生效的
634 | 
635 | ### ArrayCollapse.Index
636 | 
637 | > 索引渲染器
638 | 
639 | 无属性
640 | 
641 | ### ArrayCollapse.useIndex
642 | 
643 | > 读取当前渲染行索引的 React Hook
644 | 
645 | ### ArrayCollapse.useRecord
646 | 
647 | > 读取当前渲染记录的 React Hook
648 | 
```
Page 32/52FirstPrevNextLast