This is page 51 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/core/src/__tests__/field.spec.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { autorun, batch, observable } from '@formily/reactive' 2 | import { createForm, onFieldReact, isField } from '../' 3 | import { DataField } from '../types' 4 | import { attach, sleep } from './shared' 5 | 6 | test('create field', () => { 7 | const form = attach(createForm()) 8 | const field = attach( 9 | form.createField({ 10 | name: 'normal', 11 | }) 12 | ) 13 | expect(field).not.toBeUndefined() 14 | }) 15 | 16 | test('create field props', () => { 17 | const form = attach(createForm()) 18 | const field1 = attach( 19 | form.createField({ 20 | name: 'field1', 21 | title: 'Field 1', 22 | description: 'This is Field 1', 23 | required: true, 24 | }) 25 | ) 26 | expect(field1.title).toEqual('Field 1') 27 | expect(field1.description).toEqual('This is Field 1') 28 | expect(field1.required).toBeTruthy() 29 | expect(field1.validator).not.toBeUndefined() 30 | const field2 = attach( 31 | form.createField({ 32 | name: 'field2', 33 | disabled: true, 34 | hidden: true, 35 | }) 36 | ) 37 | expect(field2.pattern).toEqual('disabled') 38 | expect(field2.disabled).toBeTruthy() 39 | expect(field2.display).toEqual('hidden') 40 | expect(field2.hidden).toBeTruthy() 41 | const field3 = attach( 42 | form.createField({ 43 | name: 'field3', 44 | readOnly: true, 45 | visible: false, 46 | }) 47 | ) 48 | expect(field3.pattern).toEqual('readOnly') 49 | expect(field3.readOnly).toBeTruthy() 50 | expect(field3.display).toEqual('none') 51 | expect(field3.visible).toBeFalsy() 52 | const field4 = attach( 53 | form.createField({ 54 | name: 'field4', 55 | value: 123, 56 | }) 57 | ) 58 | expect(field4.value).toEqual(123) 59 | expect(field4.initialValue).toBeUndefined() 60 | const field5 = attach( 61 | form.createField({ 62 | name: 'field5', 63 | initialValue: 123, 64 | }) 65 | ) 66 | expect(field5.value).toEqual(123) 67 | expect(field5.initialValue).toEqual(123) 68 | }) 69 | 70 | test('field display and value', () => { 71 | const form = attach(createForm()) 72 | const objectField = attach( 73 | form.createObjectField({ 74 | name: 'object', 75 | }) 76 | ) 77 | const arrayField = attach( 78 | form.createArrayField({ 79 | name: 'array', 80 | }) 81 | ) 82 | const valueField = attach( 83 | form.createField({ 84 | name: 'value', 85 | }) 86 | ) 87 | expect(objectField.value).toEqual({}) 88 | expect(arrayField.value).toEqual([]) 89 | expect(valueField.value).toBeUndefined() 90 | 91 | objectField.hidden = true 92 | arrayField.hidden = true 93 | valueField.hidden = true 94 | expect(objectField.value).toEqual({}) 95 | expect(arrayField.value).toEqual([]) 96 | expect(valueField.value).toBeUndefined() 97 | 98 | objectField.hidden = false 99 | arrayField.hidden = false 100 | valueField.hidden = false 101 | expect(objectField.value).toEqual({}) 102 | expect(arrayField.value).toEqual([]) 103 | expect(valueField.value).toBeUndefined() 104 | 105 | objectField.visible = false 106 | arrayField.visible = false 107 | valueField.visible = false 108 | expect(objectField.value).toBeUndefined() 109 | expect(arrayField.value).toBeUndefined() 110 | expect(valueField.value).toBeUndefined() 111 | 112 | objectField.visible = true 113 | arrayField.visible = true 114 | valueField.visible = true 115 | expect(objectField.value).toEqual({}) 116 | expect(arrayField.value).toEqual([]) 117 | expect(valueField.value).toBeUndefined() 118 | 119 | objectField.value = { value: '123' } 120 | arrayField.value = ['123'] 121 | valueField.value = '123' 122 | expect(objectField.value).toEqual({ value: '123' }) 123 | expect(arrayField.value).toEqual(['123']) 124 | expect(valueField.value).toEqual('123') 125 | 126 | objectField.hidden = true 127 | arrayField.hidden = true 128 | valueField.hidden = true 129 | expect(objectField.value).toEqual({ value: '123' }) 130 | expect(arrayField.value).toEqual(['123']) 131 | expect(valueField.value).toEqual('123') 132 | 133 | objectField.hidden = false 134 | arrayField.hidden = false 135 | valueField.hidden = false 136 | expect(objectField.value).toEqual({ value: '123' }) 137 | expect(arrayField.value).toEqual(['123']) 138 | expect(valueField.value).toEqual('123') 139 | 140 | objectField.visible = false 141 | arrayField.visible = false 142 | valueField.visible = false 143 | expect(objectField.value).toBeUndefined() 144 | expect(arrayField.value).toBeUndefined() 145 | expect(valueField.value).toBeUndefined() 146 | 147 | objectField.visible = true 148 | arrayField.visible = true 149 | valueField.visible = true 150 | expect(objectField.value).toEqual({ value: '123' }) 151 | expect(arrayField.value).toEqual(['123']) 152 | expect(valueField.value).toEqual('123') 153 | }) 154 | 155 | test('nested display/pattern', () => { 156 | const form = attach(createForm()) 157 | const object_ = attach( 158 | form.createObjectField({ 159 | name: 'object', 160 | }) 161 | ) 162 | const void_ = attach( 163 | form.createVoidField({ 164 | name: 'void', 165 | basePath: 'object', 166 | }) 167 | ) 168 | const aaa = attach( 169 | form.createField({ 170 | name: 'aaa', 171 | basePath: 'object.void', 172 | }) 173 | ) 174 | const bbb = attach( 175 | form.createField({ 176 | name: 'bbb', 177 | basePath: 'object', 178 | }) 179 | ) 180 | const ddd = attach( 181 | form.createField({ 182 | name: 'ddd', 183 | }) 184 | ) 185 | expect(ddd.visible).toBeTruthy() 186 | expect(ddd.editable).toBeTruthy() 187 | object_.setPattern('readPretty') 188 | expect(void_.pattern).toEqual('readPretty') 189 | expect(aaa.pattern).toEqual('readPretty') 190 | expect(bbb.pattern).toEqual('readPretty') 191 | object_.setPattern('readOnly') 192 | expect(void_.pattern).toEqual('readOnly') 193 | expect(aaa.pattern).toEqual('readOnly') 194 | expect(bbb.pattern).toEqual('readOnly') 195 | object_.setPattern('disabled') 196 | expect(void_.pattern).toEqual('disabled') 197 | expect(aaa.pattern).toEqual('disabled') 198 | expect(bbb.pattern).toEqual('disabled') 199 | object_.setPattern() 200 | expect(void_.pattern).toEqual('editable') 201 | expect(aaa.pattern).toEqual('editable') 202 | expect(bbb.pattern).toEqual('editable') 203 | 204 | object_.setDisplay('hidden') 205 | expect(void_.display).toEqual('hidden') 206 | expect(aaa.display).toEqual('hidden') 207 | expect(bbb.display).toEqual('hidden') 208 | object_.setDisplay('none') 209 | expect(void_.display).toEqual('none') 210 | expect(aaa.display).toEqual('none') 211 | expect(bbb.display).toEqual('none') 212 | object_.setDisplay() 213 | expect(void_.display).toEqual('visible') 214 | expect(aaa.display).toEqual('visible') 215 | expect(bbb.display).toEqual('visible') 216 | 217 | aaa.setValue('123') 218 | expect(aaa.value).toEqual('123') 219 | aaa.setDisplay('none') 220 | expect(aaa.value).toBeUndefined() 221 | aaa.setDisplay('visible') 222 | expect(aaa.value).toEqual('123') 223 | aaa.setValue('123') 224 | object_.setDisplay('none') 225 | expect(aaa.value).toBeUndefined() 226 | object_.setDisplay('visible') 227 | expect(aaa.value).toEqual('123') 228 | }) 229 | 230 | test('setValue/setInitialValue', () => { 231 | const form = attach(createForm()) 232 | const aaa = attach( 233 | form.createField({ 234 | name: 'aaa', 235 | }) 236 | ) 237 | const bbb = attach( 238 | form.createField({ 239 | name: 'bbb', 240 | }) 241 | ) 242 | aaa.setValue('123') 243 | expect(aaa.value).toEqual('123') 244 | expect(form.values.aaa).toEqual('123') 245 | bbb.setValue('123') 246 | expect(bbb.value).toEqual('123') 247 | expect(form.values.bbb).toEqual('123') 248 | const ccc = attach( 249 | form.createField({ 250 | name: 'ccc', 251 | }) 252 | ) 253 | const ddd = attach( 254 | form.createField({ 255 | name: 'ddd', 256 | }) 257 | ) 258 | ccc.setInitialValue('123') 259 | expect(ccc.value).toEqual('123') 260 | expect(ccc.initialValue).toEqual('123') 261 | expect(form.values.ccc).toEqual('123') 262 | ddd.setInitialValue('123') 263 | expect(ddd.value).toEqual('123') 264 | expect(ddd.initialValue).toEqual('123') 265 | expect(form.values.ddd).toEqual('123') 266 | ccc.setInitialValue('222') 267 | expect(ccc.value).toEqual('222') 268 | expect(ccc.initialValue).toEqual('222') 269 | expect(form.values.ccc).toEqual('222') 270 | ddd.setInitialValue('222') 271 | expect(ddd.value).toEqual('222') 272 | expect(ddd.initialValue).toEqual('222') 273 | expect(form.values.ddd).toEqual('222') 274 | }) 275 | 276 | test('setLoading/setValidating', async () => { 277 | const form = attach(createForm()) 278 | const field = attach( 279 | form.createField({ 280 | name: 'aa', 281 | }) 282 | ) 283 | field.setLoading(true) 284 | expect(field.loading).toBeFalsy() 285 | await sleep() 286 | expect(field.loading).toBeTruthy() 287 | field.setLoading(false) 288 | field.setLoading(false) 289 | expect(field.loading).toBeFalsy() 290 | field.setValidating(true) 291 | expect(field.validating).toBeFalsy() 292 | await sleep() 293 | expect(field.validating).toBeTruthy() 294 | field.setValidating(false) 295 | expect(field.validating).toBeFalsy() 296 | }) 297 | 298 | test('setComponent/setComponentProps', () => { 299 | const component = () => null 300 | const form = attach(createForm()) 301 | const field = attach( 302 | form.createField({ 303 | name: 'aa', 304 | }) 305 | ) 306 | 307 | field.setComponent(undefined, { props: 123 }) 308 | field.setComponent(component) 309 | expect(field.component[0]).toEqual(component) 310 | expect(field.component[1]).toEqual({ props: 123 }) 311 | field.setComponentProps({ 312 | hello: 'world', 313 | }) 314 | expect(field.component[1]).toEqual({ props: 123, hello: 'world' }) 315 | }) 316 | 317 | test('setDecorator/setDecoratorProps', () => { 318 | const component = () => null 319 | const form = attach(createForm()) 320 | const field = attach( 321 | form.createField({ 322 | name: 'aa', 323 | }) 324 | ) 325 | field.setDecorator(undefined, { props: 123 }) 326 | field.setDecorator(component) 327 | expect(field.decorator[0]).toEqual(component) 328 | expect(field.decorator[1]).toEqual({ props: 123 }) 329 | field.setDecoratorProps({ 330 | hello: 'world', 331 | }) 332 | expect(field.decorator[1]).toEqual({ props: 123, hello: 'world' }) 333 | }) 334 | 335 | test('reaction initialValue', () => { 336 | const form = attach( 337 | createForm({ 338 | values: { 339 | aa: 123, 340 | }, 341 | }) 342 | ) 343 | const aa = attach( 344 | form.createField({ 345 | name: 'aa', 346 | reactions(field) { 347 | field.initialValue = 321 348 | }, 349 | }) 350 | ) 351 | const bb = attach( 352 | form.createField({ 353 | name: 'bb', 354 | value: 123, 355 | reactions(field) { 356 | field.initialValue = 321 357 | }, 358 | }) 359 | ) 360 | expect(aa.value).toEqual(123) 361 | expect(bb.value).toEqual(123) 362 | }) 363 | 364 | test('selfValidate/errors/warnings/successes/valid/invalid/validateStatus/queryFeedbacks', async () => { 365 | const form = attach(createForm()) 366 | const field = attach( 367 | form.createField({ 368 | name: 'aa', 369 | required: true, 370 | validateFirst: true, 371 | validator: [ 372 | (value) => { 373 | if (value == '123') { 374 | return { 375 | type: 'success', 376 | message: 'success', 377 | } 378 | } else if (value == '321') { 379 | return { 380 | type: 'warning', 381 | message: 'warning', 382 | } 383 | } else if (value == '111') { 384 | return 'error' 385 | } 386 | }, 387 | { 388 | triggerType: 'onBlur', 389 | format: 'url', 390 | }, 391 | { 392 | triggerType: 'onFocus', 393 | format: 'date', 394 | }, 395 | ], 396 | }) 397 | ) 398 | const field2 = attach( 399 | form.createField({ 400 | name: 'bb', 401 | required: true, 402 | value: '111', 403 | validator: [ 404 | (value) => { 405 | if (value == '123') { 406 | return { 407 | type: 'success', 408 | message: 'success', 409 | } 410 | } else if (value == '321') { 411 | return { 412 | type: 'warning', 413 | message: 'warning', 414 | } 415 | } else if (value == '111') { 416 | return 'error' 417 | } 418 | }, 419 | { 420 | triggerType: 'onBlur', 421 | format: 'url', 422 | }, 423 | { 424 | triggerType: 'onFocus', 425 | format: 'date', 426 | }, 427 | ], 428 | }) 429 | ) 430 | const field3 = attach( 431 | form.createField({ 432 | name: 'xxx', 433 | }) 434 | ) 435 | const field4 = attach( 436 | form.createField({ 437 | name: 'ppp', 438 | required: true, 439 | }) 440 | ) 441 | try { 442 | await field.validate() 443 | } catch {} 444 | try { 445 | await field2.validate() 446 | } catch {} 447 | expect(field.invalid).toBeTruthy() 448 | expect(field.selfErrors.length).toEqual(1) 449 | expect(field2.invalid).toBeTruthy() 450 | expect(field2.selfErrors.length).toEqual(3) 451 | await field.onInput('123') 452 | expect(field.selfSuccesses).toEqual(['success']) 453 | await field.onInput('321') 454 | expect(field.selfWarnings).toEqual(['warning']) 455 | await field.onInput('111') 456 | expect(field.selfErrors).toEqual(['error']) 457 | await field.onBlur() 458 | expect(field.selfErrors).toEqual([ 459 | 'error', 460 | 'The field value is a invalid url', 461 | ]) 462 | await field.onFocus() 463 | expect(field.selfErrors).toEqual([ 464 | 'error', 465 | 'The field value is a invalid url', 466 | 'The field value is not a valid date format', 467 | ]) 468 | field.setFeedback() 469 | expect(field.selfErrors).toEqual([ 470 | 'error', 471 | 'The field value is a invalid url', 472 | 'The field value is not a valid date format', 473 | ]) 474 | expect(field3.feedbacks).toEqual([]) 475 | field3.setFeedback() 476 | field3.setFeedback({ messages: null }) 477 | field3.setFeedback({ messages: ['error'], code: 'EffectError' }) 478 | field3.setFeedback({ messages: ['error2'], code: 'EffectError' }) 479 | expect(field3.feedbacks).toEqual([ 480 | { code: 'EffectError', messages: ['error2'] }, 481 | ]) 482 | expect( 483 | field3.queryFeedbacks({ 484 | address: 'xxx', 485 | }) 486 | ).toEqual([{ code: 'EffectError', messages: ['error2'] }]) 487 | expect( 488 | field3.queryFeedbacks({ 489 | address: 'yyy', 490 | }) 491 | ).toEqual([]) 492 | expect( 493 | field3.queryFeedbacks({ 494 | path: 'yyy', 495 | }) 496 | ).toEqual([]) 497 | field3.setFeedback({ messages: null, code: 'EffectError' }) 498 | field3.setFeedback({ messages: [], code: 'EffectError' }) 499 | field4.setDisplay('none') 500 | await field4.validate() 501 | expect(field4.selfErrors).toEqual([]) 502 | }) 503 | 504 | test('setValidateRule', () => { 505 | const form = attach(createForm()) 506 | const field1 = attach( 507 | form.createField({ 508 | name: 'aa', 509 | validator: [{ required: true }], 510 | }) 511 | ) 512 | const field2 = attach( 513 | form.createField({ 514 | name: 'bb', 515 | validator: 'phone', 516 | }) 517 | ) 518 | const field3 = attach( 519 | form.createField({ 520 | name: 'cc', 521 | validator: 'phone', 522 | }) 523 | ) 524 | const field4 = attach( 525 | form.createField({ 526 | name: 'dd', 527 | validator: { format: 'phone' }, 528 | }) 529 | ) 530 | const field5 = attach( 531 | form.createField({ 532 | name: 'ee', 533 | validator: [{ format: 'phone' }], 534 | }) 535 | ) 536 | const field6 = attach( 537 | form.createField({ 538 | name: 'ff', 539 | }) 540 | ) 541 | field1.setValidatorRule('format', 'phone') 542 | field2.setValidatorRule('max', 3) 543 | field3.setValidatorRule('format', 'url') 544 | field4.setValidatorRule('min', 3) 545 | field5.setValidatorRule('min', 3) 546 | field6.setValidatorRule('min', 3) 547 | expect(field1.validator).toEqual([{ required: true }, { format: 'phone' }]) 548 | expect(field2.validator).toEqual([{ format: 'phone' }, { max: 3 }]) 549 | expect(field3.validator).toEqual([{ format: 'url' }]) 550 | expect(field4.validator).toEqual([{ format: 'phone' }, { min: 3 }]) 551 | expect(field5.validator).toEqual([{ format: 'phone' }, { min: 3 }]) 552 | expect(field6.validator).toEqual([{ min: 3 }]) 553 | }) 554 | 555 | test('query', () => { 556 | const form = attach(createForm()) 557 | const object_ = attach( 558 | form.createObjectField({ 559 | name: 'object', 560 | }) 561 | ) 562 | const void_ = attach( 563 | form.createVoidField({ 564 | name: 'void', 565 | basePath: 'object', 566 | }) 567 | ) 568 | const aaa = attach( 569 | form.createField({ 570 | name: 'aaa', 571 | basePath: 'object.void', 572 | }) 573 | ) 574 | const bbb = attach( 575 | form.createField({ 576 | name: 'bbb', 577 | basePath: 'object', 578 | }) 579 | ) 580 | expect(object_.query('object.void').take()).not.toBeUndefined() 581 | expect(object_.query('object.void.aaa').take()).not.toBeUndefined() 582 | expect(void_.query('.')).not.toBeUndefined() 583 | expect(void_.query('.bbb').take()).not.toBeUndefined() 584 | expect(aaa.query('.ccc').take()).toBeUndefined() 585 | expect(aaa.query('..').take()).not.toBeUndefined() 586 | expect(aaa.query('..bbb').take()).not.toBeUndefined() 587 | expect(bbb.query('.void').take()).not.toBeUndefined() 588 | expect(bbb.query('.void.aaa').take()).not.toBeUndefined() 589 | expect(bbb.query('.void.ccc').take()).toBeUndefined() 590 | }) 591 | 592 | test('empty initialValue', () => { 593 | const form = attach(createForm()) 594 | const aa = attach( 595 | form.createField({ 596 | name: 'aa', 597 | initialValue: '', 598 | }) 599 | ) 600 | const bb = attach( 601 | form.createField({ 602 | name: 'bb', 603 | }) 604 | ) 605 | expect(aa.value).toEqual('') 606 | expect(form.values.aa).toEqual('') 607 | expect(bb.value).toEqual(undefined) 608 | expect(form.values.bb).toEqual(undefined) 609 | }) 610 | 611 | test('objectFieldWithInitialValue', async () => { 612 | const form = attach( 613 | createForm({ 614 | initialValues: { 615 | obj: { 616 | a: 'a', 617 | }, 618 | }, 619 | }) 620 | ) 621 | attach( 622 | form.createObjectField({ 623 | name: 'obj', 624 | }) 625 | ) 626 | const fieldObjA = attach( 627 | form.createField({ 628 | name: 'obj.a', 629 | }) 630 | ) 631 | 632 | expect(fieldObjA.initialValue).toEqual('a') 633 | fieldObjA.value = 'aa' 634 | expect(fieldObjA.value).toEqual('aa') 635 | expect(fieldObjA.initialValue).toEqual('a') 636 | }) 637 | 638 | test('initialValueWithArray', () => { 639 | const form = attach(createForm()) 640 | const field = attach( 641 | form.createArrayField({ 642 | name: 'aaa', 643 | initialValue: [1, 2], 644 | }) 645 | ) 646 | expect(field.initialValue).toEqual([1, 2]) 647 | expect(field.value).toEqual([1, 2]) 648 | expect(form.initialValues.aaa).toEqual([1, 2]) 649 | expect(form.values.aaa).toEqual([1, 2]) 650 | }) 651 | 652 | test('resetObjectFieldWithInitialValue', async () => { 653 | const form = attach(createForm()) 654 | attach( 655 | form.createObjectField({ 656 | name: 'obj', 657 | }) 658 | ) 659 | const fieldObjA = attach( 660 | form.createField({ 661 | name: 'obj.a', 662 | initialValue: 'a', 663 | }) 664 | ) 665 | 666 | fieldObjA.value = 'aa' 667 | expect(fieldObjA.value).toEqual('aa') 668 | await form.reset() 669 | expect(fieldObjA.value).toEqual('a') 670 | 671 | fieldObjA.value = 'aa' 672 | expect(fieldObjA.value).toEqual('aa') 673 | await form.reset() 674 | expect(fieldObjA.initialValue).toEqual('a') 675 | expect(fieldObjA.value).toEqual('a') 676 | }) 677 | 678 | test('reset', async () => { 679 | const form = attach( 680 | createForm<any>({ 681 | values: { 682 | bb: 123, 683 | }, 684 | initialValues: { 685 | aa: 123, 686 | cc: null, 687 | }, 688 | }) 689 | ) 690 | const aa = attach( 691 | form.createField({ 692 | name: 'aa', 693 | required: true, 694 | }) 695 | ) 696 | const bb = attach( 697 | form.createField({ 698 | name: 'bb', 699 | required: true, 700 | }) 701 | ) 702 | const cc = attach( 703 | form.createField({ 704 | name: 'cc', 705 | required: true, 706 | }) 707 | ) 708 | const dd = attach( 709 | form.createField({ 710 | name: 'dd', 711 | required: true, 712 | }) 713 | ) 714 | expect(aa.value).toEqual(123) 715 | expect(bb.value).toEqual(123) 716 | expect(cc.value).toEqual(null) 717 | expect(form.values.aa).toEqual(123) 718 | expect(form.values.bb).toEqual(123) 719 | expect(form.values.cc).toEqual(null) 720 | aa.onInput('xxxxx') 721 | expect(form.values.aa).toEqual('xxxxx') 722 | dd.onInput(null) 723 | expect(form.values.dd).toEqual(null) 724 | aa.reset() 725 | expect(aa.value).toEqual(123) 726 | expect(form.values.aa).toEqual(123) 727 | bb.onInput('xxxxx') 728 | expect(form.values.bb).toEqual('xxxxx') 729 | bb.reset() 730 | expect(bb.value).toBeUndefined() 731 | expect(form.values.bb).toBeUndefined() 732 | 733 | cc.onInput('xxxxx') 734 | expect(form.values.cc).toEqual('xxxxx') 735 | cc.reset() 736 | expect(cc.value).toBeNull() 737 | expect(form.values.cc).toBeNull() 738 | dd.reset() 739 | expect(dd.value).toBeUndefined() 740 | expect(form.values.dd).toBeUndefined() 741 | 742 | aa.reset({ 743 | forceClear: true, 744 | }) 745 | expect(aa.value).toBeUndefined() 746 | expect(form.values.aa).toBeUndefined() 747 | cc.reset({ 748 | forceClear: true, 749 | }) 750 | expect(cc.value).toBeUndefined() 751 | expect(form.values.cc).toBeUndefined() 752 | 753 | expect(aa.valid).toBeTruthy() 754 | await aa.reset({ 755 | forceClear: true, 756 | validate: true, 757 | }) 758 | expect(aa.valid).toBeFalsy() 759 | 760 | expect(cc.valid).toBeTruthy() 761 | await cc.reset({ 762 | forceClear: true, 763 | validate: true, 764 | }) 765 | expect(cc.valid).toBeFalsy() 766 | }) 767 | 768 | test('match', () => { 769 | const form = attach( 770 | createForm<any>({ 771 | values: { 772 | bb: 123, 773 | }, 774 | initialValues: { 775 | aa: 123, 776 | }, 777 | }) 778 | ) 779 | const aa = attach( 780 | form.createField({ 781 | name: 'aa', 782 | required: true, 783 | }) 784 | ) 785 | expect(aa.match('aa')).toBeTruthy() 786 | expect(aa.match('*')).toBeTruthy() 787 | expect(aa.match('a~')).toBeTruthy() 788 | expect(aa.match('*(aa,bb)')).toBeTruthy() 789 | }) 790 | 791 | test('setState/getState', () => { 792 | const form = attach(createForm()) 793 | const aa = attach( 794 | form.createField({ 795 | name: 'aa', 796 | required: true, 797 | }) 798 | ) 799 | const state = aa.getState() 800 | aa.setState((state) => { 801 | state.value = '123' 802 | state.title = 'AAA' 803 | }) 804 | expect(aa.value).toEqual('123') 805 | expect(aa.title).toEqual('AAA') 806 | state['setState'] = () => {} 807 | aa.setState(state) 808 | expect(aa.value).toBeUndefined() 809 | expect(aa.title).toBeUndefined() 810 | aa.setState((state) => { 811 | state.hidden = false 812 | }) 813 | expect(aa.display).toEqual('visible') 814 | aa.setState((state) => { 815 | state.visible = true 816 | }) 817 | expect(aa.display).toEqual('visible') 818 | aa.setState((state) => { 819 | state.readOnly = false 820 | }) 821 | expect(aa.pattern).toEqual('editable') 822 | aa.setState((state) => { 823 | state.disabled = false 824 | }) 825 | expect(aa.pattern).toEqual('editable') 826 | aa.setState((state) => { 827 | state.editable = true 828 | }) 829 | expect(aa.pattern).toEqual('editable') 830 | aa.setState((state) => { 831 | state.editable = false 832 | }) 833 | expect(aa.pattern).toEqual('readPretty') 834 | aa.setState((state) => { 835 | state.readPretty = true 836 | }) 837 | expect(aa.pattern).toEqual('readPretty') 838 | aa.setState((state) => { 839 | state.readPretty = false 840 | }) 841 | expect(aa.pattern).toEqual('editable') 842 | form.setFieldState('bb', (state) => { 843 | state.value = 'bbb' 844 | }) 845 | form.setFieldState('bb', (state) => { 846 | state.visible = false 847 | }) 848 | const bb = attach( 849 | form.createField({ 850 | name: 'bb', 851 | }) 852 | ) 853 | expect(bb.value).toEqual(undefined) 854 | expect(bb.visible).toBeFalsy() 855 | form.setFieldState('*', (state) => { 856 | state.value = '123' 857 | }) 858 | const cc = attach( 859 | form.createField({ 860 | name: 'cc', 861 | }) 862 | ) 863 | expect(aa.value).toEqual('123') 864 | expect(bb.value).toBeUndefined() 865 | expect(cc.value).toEqual('123') 866 | form.setFieldState(form.query('cc'), (state) => { 867 | state.value = 'ccc' 868 | }) 869 | expect(cc.value).toEqual('ccc') 870 | form.setFieldState(cc, (state) => { 871 | state.value = '123' 872 | }) 873 | expect(cc.value).toEqual('123') 874 | expect(form.getFieldState(aa)).not.toBeUndefined() 875 | expect(form.getFieldState(form.query('aa'))).not.toBeUndefined() 876 | }) 877 | 878 | test('setDataSource', () => { 879 | const form = attach(createForm()) 880 | const aa = attach( 881 | form.createField({ 882 | name: 'aa', 883 | required: true, 884 | }) 885 | ) 886 | aa.setDataSource([ 887 | { label: 's1', value: 's1' }, 888 | { label: 's2', value: 's2' }, 889 | ]) 890 | expect(aa.dataSource).toEqual([ 891 | { label: 's1', value: 's1' }, 892 | { label: 's2', value: 's2' }, 893 | ]) 894 | }) 895 | 896 | test('setTitle/setDescription', () => { 897 | const form = attach(createForm()) 898 | const aa = attach( 899 | form.createField({ 900 | name: 'aa', 901 | required: true, 902 | }) 903 | ) 904 | aa.setTitle('AAA') 905 | aa.setDescription('This is AAA') 906 | expect(aa.title).toEqual('AAA') 907 | expect(aa.description).toEqual('This is AAA') 908 | }) 909 | 910 | test('required/setRequired', () => { 911 | const form = attach(createForm()) 912 | const aa = attach( 913 | form.createField({ 914 | name: 'aa', 915 | }) 916 | ) 917 | aa.setRequired(true) 918 | expect(aa.required).toBeTruthy() 919 | aa.setRequired(false) 920 | expect(aa.required).toBeFalsy() 921 | const bb = attach( 922 | form.createField({ 923 | name: 'bb', 924 | validator: { 925 | max: 3, 926 | required: true, 927 | }, 928 | }) 929 | ) 930 | expect(bb.required).toBeTruthy() 931 | bb.setRequired(false) 932 | expect(bb.required).toBeFalsy() 933 | const cc = attach( 934 | form.createField({ 935 | name: 'cc', 936 | validator: [ 937 | 'date', 938 | { 939 | max: 3, 940 | }, 941 | { 942 | required: true, 943 | }, 944 | ], 945 | }) 946 | ) 947 | expect(cc.required).toBeTruthy() 948 | cc.setRequired(false) 949 | expect(cc.required).toBeFalsy() 950 | const dd = attach( 951 | form.createField({ 952 | name: 'dd', 953 | validator: { 954 | max: 3, 955 | }, 956 | }) 957 | ) 958 | expect(dd.required).toBeFalsy() 959 | dd.setRequired(true) 960 | expect(dd.required).toBeTruthy() 961 | }) 962 | 963 | test('setData/setContent', () => { 964 | const form = attach(createForm()) 965 | const aa = attach( 966 | form.createField({ 967 | name: 'aa', 968 | required: true, 969 | }) 970 | ) 971 | aa.setData('This is data') 972 | aa.setContent('This is Content') 973 | expect(aa.data).toEqual('This is data') 974 | expect(aa.content).toEqual('This is Content') 975 | }) 976 | 977 | test('setData/setContent in void field', () => { 978 | const form = attach(createForm()) 979 | const voidFeild = attach( 980 | form.createVoidField({ 981 | name: 'voidFeild', 982 | }) 983 | ) 984 | voidFeild.setData('This is data') 985 | voidFeild.setContent('This is Content') 986 | expect(voidFeild.data).toEqual('This is data') 987 | expect(voidFeild.content).toEqual('This is Content') 988 | }) 989 | 990 | test('setErrors/setWarnings/setSuccesses/setValidator', async () => { 991 | const form = attach(createForm()) 992 | const aa = attach( 993 | form.createField({ 994 | name: 'aa', 995 | }) 996 | ) 997 | const bb = attach( 998 | form.createField({ 999 | name: 'bb', 1000 | }) 1001 | ) 1002 | const cc = attach( 1003 | form.createField({ 1004 | name: 'cc', 1005 | }) 1006 | ) 1007 | const dd = attach( 1008 | form.createField({ 1009 | name: 'dd', 1010 | validator() { 1011 | return new Promise(() => {}) 1012 | }, 1013 | }) 1014 | ) 1015 | aa.setSelfErrors(['error']) 1016 | aa.setSelfWarnings(['warning']) 1017 | aa.setSelfSuccesses(['success']) 1018 | bb.setSelfSuccesses(['success']) 1019 | cc.setSelfWarnings(['warning']) 1020 | expect(aa.selfErrors).toEqual(['error']) 1021 | expect(aa.valid).toBeFalsy() 1022 | expect(aa.selfWarnings).toEqual(['warning']) 1023 | expect(aa.selfSuccesses).toEqual(['success']) 1024 | expect(bb.validateStatus).toEqual('success') 1025 | expect(cc.validateStatus).toEqual('warning') 1026 | aa.setValidator('date') 1027 | await aa.onInput('123') 1028 | expect(aa.selfErrors.length).toEqual(2) 1029 | dd.onInput('123') 1030 | await sleep() 1031 | expect(dd.validateStatus).toEqual('validating') 1032 | }) 1033 | 1034 | test('reactions', async () => { 1035 | const form = attach(createForm()) 1036 | const aa = attach( 1037 | form.createField({ 1038 | name: 'aa', 1039 | }) 1040 | ) 1041 | const bb = attach( 1042 | form.createField({ 1043 | name: 'bb', 1044 | reactions: [ 1045 | (field) => { 1046 | const aa = field.query('aa') 1047 | if (aa.get('value') === '123') { 1048 | field.visible = false 1049 | } else { 1050 | field.visible = true 1051 | } 1052 | if (aa.get('inputValue') === '333') { 1053 | field.editable = false 1054 | } else if (aa.get('inputValue') === '444') { 1055 | field.editable = true 1056 | } 1057 | if (aa.get('initialValue') === '555') { 1058 | field.readOnly = true 1059 | } else if (aa.get('initialValue') === '666') { 1060 | field.readOnly = false 1061 | } 1062 | }, 1063 | null, 1064 | ], 1065 | }) 1066 | ) 1067 | expect(bb.visible).toBeTruthy() 1068 | aa.setValue('123') 1069 | expect(bb.visible).toBeFalsy() 1070 | await aa.onInput('333') 1071 | expect(bb.editable).toBeFalsy() 1072 | await aa.onInput('444') 1073 | expect(bb.editable).toBeTruthy() 1074 | aa.setInitialValue('555') 1075 | expect(bb.readOnly).toBeTruthy() 1076 | aa.setInitialValue('666') 1077 | expect(bb.readOnly).toBeFalsy() 1078 | form.onUnmount() 1079 | }) 1080 | 1081 | test('fault tolerance', () => { 1082 | const form = attach(createForm()) 1083 | const field = attach( 1084 | form.createField({ 1085 | name: 'aa', 1086 | value: 123, 1087 | }) 1088 | ) 1089 | field.setDisplay('none') 1090 | expect(field.value).toBeUndefined() 1091 | field.setDisplay('visible') 1092 | expect(field.value).toEqual(123) 1093 | field.setDisplay('none') 1094 | expect(field.value).toBeUndefined() 1095 | field.setValue(321) 1096 | expect(field.value).toBeUndefined() 1097 | field.setDisplay('visible') 1098 | expect(field.value).toEqual(321) 1099 | form.setDisplay(null) 1100 | form.setPattern(null) 1101 | const field2 = attach( 1102 | form.createField({ 1103 | name: 'xxx', 1104 | }) 1105 | ) 1106 | expect(field2.display).toEqual('visible') 1107 | expect(field2.pattern).toEqual('editable') 1108 | }) 1109 | 1110 | test('initialValue', () => { 1111 | const form = attach(createForm()) 1112 | const field = attach( 1113 | form.createField({ 1114 | name: 'aaa', 1115 | initialValue: 123, 1116 | }) 1117 | ) 1118 | expect(form.values.aaa).toEqual(123) 1119 | expect(form.initialValues.aaa).toEqual(123) 1120 | expect(field.value).toEqual(123) 1121 | expect(field.initialValue).toEqual(123) 1122 | }) 1123 | 1124 | test('array path calculation with none index', async () => { 1125 | const form = attach(createForm()) 1126 | const array = attach( 1127 | form.createArrayField({ 1128 | name: 'array', 1129 | }) 1130 | ) 1131 | await array.push({}) 1132 | const input = attach( 1133 | form.createField({ 1134 | name: '0.input', 1135 | basePath: 'array', 1136 | }) 1137 | ) 1138 | expect(input.path.toString()).toEqual('array.0.input') 1139 | }) 1140 | 1141 | test('array path calculation with none index and void nested', async () => { 1142 | const form = attach(createForm()) 1143 | const array = attach( 1144 | form.createArrayField({ 1145 | name: 'array', 1146 | }) 1147 | ) 1148 | await array.push({}) 1149 | attach( 1150 | form.createVoidField({ 1151 | name: '0.column', 1152 | basePath: 'array', 1153 | }) 1154 | ) 1155 | const input = attach( 1156 | form.createField({ 1157 | name: 'input', 1158 | basePath: 'array.0.column', 1159 | }) 1160 | ) 1161 | expect(input.path.toString()).toEqual('array.0.input') 1162 | }) 1163 | 1164 | test('array path calculation with object index', async () => { 1165 | const form = attach(createForm()) 1166 | const array = attach( 1167 | form.createArrayField({ 1168 | name: 'array', 1169 | }) 1170 | ) 1171 | await array.push({}) 1172 | attach( 1173 | form.createObjectField({ 1174 | name: '0', 1175 | basePath: 'array', 1176 | }) 1177 | ) 1178 | const input = attach( 1179 | form.createField({ 1180 | name: 'input', 1181 | basePath: 'array.0', 1182 | }) 1183 | ) 1184 | expect(input.path.toString()).toEqual('array.0.input') 1185 | }) 1186 | 1187 | test('array path calculation with void index', async () => { 1188 | const form = attach(createForm()) 1189 | const array = attach( 1190 | form.createArrayField({ 1191 | name: 'array', 1192 | }) 1193 | ) 1194 | await array.push('') 1195 | attach( 1196 | form.createVoidField({ 1197 | name: '0', 1198 | basePath: 'array', 1199 | }) 1200 | ) 1201 | const input = attach( 1202 | form.createField({ 1203 | name: 'input', 1204 | basePath: 'array.0', 1205 | }) 1206 | ) 1207 | expect(input.path.toString()).toEqual('array.0') 1208 | }) 1209 | 1210 | test('array path calculation with void index and void wrapper', async () => { 1211 | const form = attach(createForm()) 1212 | attach( 1213 | form.createVoidField({ 1214 | name: 'layout', 1215 | }) 1216 | ) 1217 | const array_in_layout = attach( 1218 | form.createArrayField({ 1219 | name: 'array_in_layout', 1220 | basePath: 'layout', 1221 | }) 1222 | ) 1223 | await array_in_layout.push('') 1224 | attach( 1225 | form.createVoidField({ 1226 | name: '0', 1227 | basePath: 'layout.array_in_layout', 1228 | }) 1229 | ) 1230 | const input = attach( 1231 | form.createField({ 1232 | name: 'input', 1233 | basePath: 'layout.array_in_layout.0', 1234 | }) 1235 | ) 1236 | expect(input.path.toString()).toEqual('array_in_layout.0') 1237 | }) 1238 | 1239 | test('reaction in reaction', () => { 1240 | const form = attach(createForm()) 1241 | const void_ = attach( 1242 | form.createVoidField({ 1243 | name: 'void', 1244 | }) 1245 | ) 1246 | attach( 1247 | form.createField({ 1248 | name: 'field1', 1249 | basePath: 'void', 1250 | initialValue: 123, 1251 | }) 1252 | ) 1253 | const field2 = attach( 1254 | form.createField({ 1255 | name: 'field2', 1256 | basePath: 'void', 1257 | initialValue: 456, 1258 | reactions: (field) => { 1259 | const f1 = field.query('field1') 1260 | if (f1.get('value') === 123) { 1261 | field.display = 'visible' 1262 | } else { 1263 | field.display = 'none' 1264 | } 1265 | }, 1266 | }) 1267 | ) 1268 | void_.setDisplay('none') 1269 | expect(field2.value).toEqual(undefined) 1270 | expect(field2.display).toEqual('none') 1271 | }) 1272 | 1273 | test('nested fields hidden and selfValidate', async () => { 1274 | const form = attach(createForm()) 1275 | const parent = attach( 1276 | form.createVoidField({ 1277 | name: 'parent', 1278 | }) 1279 | ) 1280 | attach( 1281 | form.createField({ 1282 | name: 'aa', 1283 | basePath: 'parent', 1284 | required: true, 1285 | }) 1286 | ) 1287 | attach( 1288 | form.createField({ 1289 | name: 'bb', 1290 | basePath: 'parent', 1291 | required: true, 1292 | }) 1293 | ) 1294 | try { 1295 | await form.validate() 1296 | } catch {} 1297 | expect(form.invalid).toBeTruthy() 1298 | parent.display = 'hidden' 1299 | await form.validate() 1300 | expect(form.invalid).toBeFalsy() 1301 | }) 1302 | 1303 | test('deep nested fields hidden and selfValidate', async () => { 1304 | const form = attach(createForm()) 1305 | const parent1 = attach( 1306 | form.createVoidField({ 1307 | name: 'parent1', 1308 | }) 1309 | ) 1310 | const parent2 = attach( 1311 | form.createVoidField({ 1312 | name: 'parent2', 1313 | basePath: 'parent1', 1314 | }) 1315 | ) 1316 | const aa = attach( 1317 | form.createField({ 1318 | name: 'aa', 1319 | basePath: 'parent1.parent2', 1320 | required: true, 1321 | }) 1322 | ) 1323 | const bb = attach( 1324 | form.createField({ 1325 | name: 'bb', 1326 | basePath: 'parent1.parent2', 1327 | required: true, 1328 | }) 1329 | ) 1330 | try { 1331 | await form.validate() 1332 | } catch {} 1333 | expect(form.invalid).toBeTruthy() 1334 | parent2.display = 'visible' 1335 | parent1.display = 'hidden' 1336 | expect(parent2.display).toEqual('hidden') 1337 | expect(aa.display).toEqual('hidden') 1338 | expect(bb.display).toEqual('hidden') 1339 | await form.validate() 1340 | expect(form.invalid).toBeFalsy() 1341 | }) 1342 | 1343 | test('deep nested fields hidden and selfValidate with middle hidden', async () => { 1344 | const form = attach(createForm()) 1345 | const parent1 = attach( 1346 | form.createVoidField({ 1347 | name: 'parent1', 1348 | }) 1349 | ) 1350 | const parent2 = attach( 1351 | form.createVoidField({ 1352 | name: 'parent2', 1353 | basePath: 'parent1', 1354 | }) 1355 | ) 1356 | const aa = attach( 1357 | form.createField({ 1358 | name: 'aa', 1359 | basePath: 'parent1.parent2', 1360 | required: true, 1361 | }) 1362 | ) 1363 | const bb = attach( 1364 | form.createField({ 1365 | name: 'bb', 1366 | basePath: 'parent1.parent2', 1367 | required: true, 1368 | }) 1369 | ) 1370 | try { 1371 | await form.validate() 1372 | } catch {} 1373 | expect(form.invalid).toBeTruthy() 1374 | parent2.display = 'hidden' 1375 | parent1.display = 'none' 1376 | expect(parent2.display).toEqual('hidden') 1377 | expect(aa.display).toEqual('hidden') 1378 | expect(bb.display).toEqual('hidden') 1379 | await form.validate() 1380 | expect(form.invalid).toBeFalsy() 1381 | }) 1382 | 1383 | test('fields unmount and selfValidate', async () => { 1384 | const form = attach(createForm()) 1385 | const field = attach( 1386 | form.createField({ 1387 | name: 'parent', 1388 | required: true, 1389 | }) 1390 | ) 1391 | try { 1392 | await form.validate() 1393 | } catch {} 1394 | expect(form.invalid).toBeTruthy() 1395 | field.onUnmount() 1396 | try { 1397 | await form.validate() 1398 | } catch {} 1399 | expect(form.invalid).toBeTruthy() 1400 | form.clearFormGraph('parent') 1401 | await form.validate() 1402 | expect(form.invalid).toBeFalsy() 1403 | }) 1404 | 1405 | test('auto clean with ArrayField', () => { 1406 | const form = attach(createForm()) 1407 | attach( 1408 | form.createArrayField({ 1409 | name: 'array', 1410 | initialValue: [{}, {}], 1411 | }) 1412 | ) 1413 | attach( 1414 | form.createField({ 1415 | name: '0.aa', 1416 | basePath: 'array', 1417 | }) 1418 | ) 1419 | attach( 1420 | form.createField({ 1421 | name: '1.aa', 1422 | basePath: 'array', 1423 | }) 1424 | ) 1425 | const array1 = attach( 1426 | form.createArrayField({ 1427 | name: 'array1', 1428 | initialValue: [{}, {}], 1429 | }) 1430 | ) 1431 | attach( 1432 | form.createField({ 1433 | name: '0.aa', 1434 | basePath: 'array1', 1435 | }) 1436 | ) 1437 | attach( 1438 | form.createField({ 1439 | name: '1.aa', 1440 | basePath: 'array1', 1441 | }) 1442 | ) 1443 | const array2 = attach( 1444 | form.createArrayField({ 1445 | name: 'array2', 1446 | initialValue: [{}, {}], 1447 | }) 1448 | ) 1449 | attach( 1450 | form.createField({ 1451 | name: '0.aa', 1452 | basePath: 'array2', 1453 | }) 1454 | ) 1455 | attach( 1456 | form.createField({ 1457 | name: '1.aa', 1458 | basePath: 'array2', 1459 | }) 1460 | ) 1461 | expect(form.fields['array.1.aa']).not.toBeUndefined() 1462 | expect(form.values.array).toEqual([{}, {}]) 1463 | form.setValues( 1464 | { 1465 | array: [{}], 1466 | }, 1467 | 'shallowMerge' 1468 | ) 1469 | expect(form.values.array).toEqual([{}]) 1470 | expect(form.fields['array.1.aa']).toBeUndefined() 1471 | expect(form.fields['array1.0.aa']).not.toBeUndefined() 1472 | expect(form.fields['array1.1.aa']).not.toBeUndefined() 1473 | expect(form.values.array1).toEqual([{}, {}]) 1474 | array1.setValue([]) 1475 | expect(form.fields['array1.0.aa']).toBeUndefined() 1476 | expect(form.fields['array1.1.aa']).toBeUndefined() 1477 | expect(form.fields['array2.0.aa']).not.toBeUndefined() 1478 | expect(form.fields['array2.1.aa']).not.toBeUndefined() 1479 | array2.setValue([]) 1480 | expect(form.fields['array2.0.aa']).toBeUndefined() 1481 | expect(form.fields['array2.1.aa']).toBeUndefined() 1482 | }) 1483 | 1484 | test('auto clean with ObjectField', () => { 1485 | const form = attach(createForm()) 1486 | attach( 1487 | form.createObjectField({ 1488 | name: 'obj', 1489 | initialValue: { 1490 | aa: 'aa', 1491 | bb: 'bb', 1492 | }, 1493 | }) 1494 | ) 1495 | attach( 1496 | form.createField({ 1497 | name: 'aa', 1498 | basePath: 'obj', 1499 | }) 1500 | ) 1501 | attach( 1502 | form.createField({ 1503 | name: 'bb', 1504 | basePath: 'obj', 1505 | }) 1506 | ) 1507 | const obj1 = attach( 1508 | form.createObjectField({ 1509 | name: 'obj1', 1510 | initialValue: { 1511 | aa: 'aa', 1512 | bb: 'bb', 1513 | }, 1514 | }) 1515 | ) 1516 | attach( 1517 | form.createField({ 1518 | name: 'aa', 1519 | basePath: 'obj1', 1520 | }) 1521 | ) 1522 | attach( 1523 | form.createField({ 1524 | name: 'bb', 1525 | basePath: 'obj1', 1526 | }) 1527 | ) 1528 | const obj2 = attach( 1529 | form.createObjectField({ 1530 | name: 'obj2', 1531 | initialValue: { 1532 | aa: 'aa', 1533 | bb: 'bb', 1534 | }, 1535 | }) 1536 | ) 1537 | attach( 1538 | form.createField({ 1539 | name: 'aa', 1540 | basePath: 'obj2', 1541 | }) 1542 | ) 1543 | attach( 1544 | form.createField({ 1545 | name: 'bb', 1546 | basePath: 'obj2', 1547 | }) 1548 | ) 1549 | expect(form.fields['obj.aa']).not.toBeUndefined() 1550 | expect(form.fields['obj.bb']).not.toBeUndefined() 1551 | expect(form.values.obj).toEqual({ aa: 'aa', bb: 'bb' }) 1552 | form.setValues( 1553 | { 1554 | obj: { 1555 | aa: '123', 1556 | }, 1557 | }, 1558 | 'shallowMerge' 1559 | ) 1560 | expect(form.values.obj).toEqual({ aa: '123' }) 1561 | expect(form.fields['obj.aa']).not.toBeUndefined() 1562 | expect(form.fields['obj.bb']).not.toBeUndefined() 1563 | expect(form.fields['obj1.aa']).not.toBeUndefined() 1564 | expect(form.fields['obj1.bb']).not.toBeUndefined() 1565 | expect(form.values.obj1).toEqual({ aa: 'aa', bb: 'bb' }) 1566 | obj1.setValue({}) 1567 | expect(form.values.obj1).toEqual({}) 1568 | expect(form.fields['obj1.aa']).not.toBeUndefined() 1569 | expect(form.fields['obj1.bb']).not.toBeUndefined() 1570 | expect(form.fields['obj2.aa']).not.toBeUndefined() 1571 | expect(form.fields['obj2.bb']).not.toBeUndefined() 1572 | expect(form.values.obj2).toEqual({ aa: 'aa', bb: 'bb' }) 1573 | obj2.setValue({ aa: 'aa', bb: 'bb', cc: 'cc' }) 1574 | expect(form.fields['obj2.aa']).not.toBeUndefined() 1575 | expect(form.fields['obj2.bb']).not.toBeUndefined() 1576 | expect(form.fields['obj2.cc']).toBeUndefined() 1577 | obj2.addProperty('cc', '123') 1578 | attach( 1579 | form.createField({ 1580 | name: 'cc', 1581 | basePath: 'obj2', 1582 | }) 1583 | ) 1584 | expect(form.fields['obj2.cc']).not.toBeUndefined() 1585 | obj2.removeProperty('cc') 1586 | expect(form.fields['obj2.cc']).toBeUndefined() 1587 | }) 1588 | 1589 | test('initial value with empty', () => { 1590 | const form = attach(createForm()) 1591 | const array = attach(form.createField({ name: 'array', initialValue: '' })) 1592 | expect(array.value).toEqual('') 1593 | 1594 | const beNull = attach(form.createField({ name: 'null', initialValue: null })) 1595 | expect(beNull.value).toEqual(null) 1596 | }) 1597 | 1598 | test('field submit', async () => { 1599 | const form = attach( 1600 | createForm({ 1601 | initialValues: { 1602 | aa: { 1603 | cc: 'cc', 1604 | }, 1605 | bb: 'bb', 1606 | }, 1607 | }) 1608 | ) 1609 | const childForm = attach( 1610 | form.createObjectField({ 1611 | name: 'aa', 1612 | }) 1613 | ) 1614 | attach( 1615 | form.createField({ 1616 | name: 'bb', 1617 | }) 1618 | ) 1619 | attach( 1620 | form.createField({ 1621 | name: 'cc', 1622 | basePath: 'aa', 1623 | }) 1624 | ) 1625 | const onSubmit = jest.fn() 1626 | await childForm.submit(onSubmit) 1627 | expect(onSubmit).toBeCalledWith({ 1628 | cc: 'cc', 1629 | }) 1630 | }) 1631 | 1632 | test('field submit with error', async () => { 1633 | const form = attach(createForm()) 1634 | const childForm = attach( 1635 | form.createObjectField({ 1636 | name: 'aa', 1637 | }) 1638 | ) 1639 | attach( 1640 | form.createField({ 1641 | name: 'bb', 1642 | required: true, 1643 | }) 1644 | ) 1645 | attach( 1646 | form.createField({ 1647 | name: 'cc', 1648 | basePath: 'aa', 1649 | required: true, 1650 | }) 1651 | ) 1652 | const onSubmit = jest.fn() 1653 | try { 1654 | await childForm.submit(onSubmit) 1655 | } catch (e) { 1656 | expect(e).not.toBeUndefined() 1657 | } 1658 | expect(onSubmit).toBeCalledTimes(0) 1659 | }) 1660 | 1661 | test('initial display with value', () => { 1662 | const form = attach(createForm()) 1663 | const aa = attach( 1664 | form.createField({ 1665 | name: 'aa', 1666 | value: 123, 1667 | visible: false, 1668 | }) 1669 | ) 1670 | const bb = attach( 1671 | form.createField({ 1672 | name: 'bb', 1673 | value: 123, 1674 | visible: true, 1675 | }) 1676 | ) 1677 | const cc = attach( 1678 | form.createField({ 1679 | name: 'cc', 1680 | value: 123, 1681 | hidden: true, 1682 | }) 1683 | ) 1684 | expect(aa.value).toBeUndefined() 1685 | expect(aa.visible).toBeFalsy() 1686 | expect(bb.value).toEqual(123) 1687 | expect(bb.visible).toBeTruthy() 1688 | expect(cc.value).toEqual(123) 1689 | expect(cc.hidden).toBeTruthy() 1690 | }) 1691 | 1692 | test('state depend field visible value', async () => { 1693 | const form = attach(createForm()) 1694 | const aa = attach( 1695 | form.createField({ 1696 | name: 'aa', 1697 | }) 1698 | ) 1699 | const bb = attach( 1700 | form.createField({ 1701 | name: 'bb', 1702 | reactions(field) { 1703 | field.visible = aa.value === '123' 1704 | }, 1705 | }) 1706 | ) 1707 | const cc = attach( 1708 | form.createField({ 1709 | name: 'cc', 1710 | reactions(field) { 1711 | field.visible = aa.value === '123' 1712 | field.disabled = !bb.value 1713 | }, 1714 | }) 1715 | ) 1716 | expect(bb.visible).toBeFalsy() 1717 | expect(cc.visible).toBeFalsy() 1718 | expect(cc.disabled).toBeTruthy() 1719 | aa.value = '123' 1720 | await sleep(10) 1721 | expect(bb.visible).toBeTruthy() 1722 | expect(cc.visible).toBeTruthy() 1723 | expect(cc.disabled).toBeTruthy() 1724 | bb.value = '321' 1725 | await sleep(10) 1726 | expect(bb.visible).toBeTruthy() 1727 | expect(cc.visible).toBeTruthy() 1728 | expect(cc.disabled).toBeFalsy() 1729 | aa.value = '' 1730 | await sleep(10) 1731 | expect(bb.visible).toBeFalsy() 1732 | expect(cc.visible).toBeFalsy() 1733 | expect(cc.disabled).toBeTruthy() 1734 | aa.value = '123' 1735 | await sleep(10) 1736 | expect(bb.visible).toBeTruthy() 1737 | expect(cc.visible).toBeTruthy() 1738 | expect(cc.disabled).toBeFalsy() 1739 | }) 1740 | 1741 | test('reactions initialValue and value', () => { 1742 | const form = attach( 1743 | createForm({ 1744 | values: { 1745 | aa: { 1746 | input: '111', 1747 | }, 1748 | }, 1749 | }) 1750 | ) 1751 | attach( 1752 | form.createObjectField({ 1753 | name: 'aa', 1754 | reactions: [ 1755 | (field) => { 1756 | field.initialValue = {} 1757 | field.initialValue.input = 123 1758 | }, 1759 | ], 1760 | }) 1761 | ) 1762 | attach( 1763 | form.createField({ 1764 | name: 'input', 1765 | basePath: 'aa', 1766 | }) 1767 | ) 1768 | expect(form.values.aa.input).toEqual('111') 1769 | }) 1770 | 1771 | test('field name is length in initialize', () => { 1772 | const form = attach(createForm()) 1773 | const field = attach( 1774 | form.createField({ 1775 | name: 'length', 1776 | initialValue: 123, 1777 | }) 1778 | ) 1779 | expect(field.value).toEqual(123) 1780 | }) 1781 | 1782 | test('field name is length in dynamic assign', () => { 1783 | const form = attach(createForm()) 1784 | const field = attach( 1785 | form.createField({ 1786 | name: 'length', 1787 | }) 1788 | ) 1789 | field.initialValue = 123 1790 | expect(field.value).toEqual(123) 1791 | }) 1792 | 1793 | test('nested field modified', async () => { 1794 | const form = attach(createForm()) 1795 | const obj = attach( 1796 | form.createObjectField({ 1797 | name: 'object', 1798 | }) 1799 | ) 1800 | const child = attach( 1801 | form.createField({ 1802 | name: 'child', 1803 | basePath: 'object', 1804 | }) 1805 | ) 1806 | await child.onInput() 1807 | expect(child.modified).toBeTruthy() 1808 | expect(child.selfModified).toBeTruthy() 1809 | expect(obj.modified).toBeTruthy() 1810 | expect(obj.selfModified).toBeFalsy() 1811 | expect(form.modified).toBeTruthy() 1812 | await obj.reset() 1813 | expect(child.modified).toBeFalsy() 1814 | expect(child.selfModified).toBeFalsy() 1815 | expect(obj.modified).toBeFalsy() 1816 | expect(obj.selfModified).toBeFalsy() 1817 | expect(form.modified).toBeTruthy() 1818 | await form.reset() 1819 | expect(form.modified).toBeFalsy() 1820 | }) 1821 | 1822 | test('field setValidator repeat call', async () => { 1823 | const form = attach(createForm()) 1824 | const field = attach( 1825 | form.createField({ 1826 | name: 'normal', 1827 | }) 1828 | ) 1829 | 1830 | const validator1 = jest.fn(() => '') 1831 | const validator2 = jest.fn(() => '') 1832 | const validator3 = jest.fn(() => '') 1833 | 1834 | field.setValidator([validator1, validator2, validator3]) 1835 | 1836 | await form.validate() 1837 | expect(validator1).toBeCalledTimes(1) 1838 | }) 1839 | 1840 | test('custom validator to get ctx.field', async () => { 1841 | const form = attach(createForm()) 1842 | let ctxField = null 1843 | let ctxForm = null 1844 | attach( 1845 | form.createField({ 1846 | name: 'aaa', 1847 | validator(value, rule, ctx) { 1848 | ctxField = ctx.field 1849 | ctxForm = ctx.form 1850 | return '' 1851 | }, 1852 | }) 1853 | ) 1854 | await form.submit() 1855 | expect(!!ctxField).toBeTruthy() 1856 | expect(!!ctxForm).toBeTruthy() 1857 | }) 1858 | 1859 | test('single direction linkage effect', async () => { 1860 | const form = attach(createForm()) 1861 | 1862 | const input1 = form.createField({ 1863 | name: 'input1', 1864 | reactions: (field: DataField) => { 1865 | if (!field.selfModified) { 1866 | return 1867 | } 1868 | input2.value = field.value 1869 | }, 1870 | }) 1871 | 1872 | const input2 = form.createField({ 1873 | name: 'input2', 1874 | }) 1875 | 1876 | await input1.onInput('123') 1877 | expect(input2.value).toBe('123') 1878 | await input2.onInput('321') 1879 | expect(input2.value).toBe('321') 1880 | }) 1881 | 1882 | test('path change will update computed value', () => { 1883 | const form = attach(createForm()) 1884 | 1885 | const input = form.createField({ 1886 | name: 'input', 1887 | }) 1888 | 1889 | const value = jest.fn() 1890 | 1891 | autorun(() => { 1892 | value(input.value) 1893 | }) 1894 | batch(() => { 1895 | input.locate('select') 1896 | input.value = '123' 1897 | }) 1898 | expect(value).nthCalledWith(2, '123') 1899 | }) 1900 | 1901 | test('object field reset', async () => { 1902 | const form = attach(createForm()) 1903 | 1904 | attach( 1905 | form.createObjectField({ 1906 | name: 'obj', 1907 | }) 1908 | ) 1909 | 1910 | const input = attach( 1911 | form.createField({ 1912 | name: 'input', 1913 | basePath: 'obj', 1914 | }) 1915 | ) 1916 | 1917 | await form.reset() 1918 | form.setValues({ 1919 | obj: { 1920 | input: '123', 1921 | }, 1922 | }) 1923 | expect(input.value).toBe('123') 1924 | }) 1925 | 1926 | test('field visible default value should work', () => { 1927 | const form = attach( 1928 | createForm({ 1929 | effects(form) { 1930 | onFieldReact('obj.input1', (field) => { 1931 | field.pattern = 'disabled' 1932 | }) 1933 | onFieldReact('obj', (field) => { 1934 | field.visible = form.values.select !== 'none' 1935 | }) 1936 | onFieldReact('obj.input1', (field) => { 1937 | if (isField(field)) { 1938 | field.initialValue = '123' 1939 | } 1940 | }) 1941 | onFieldReact('obj.input2', (field) => { 1942 | if (isField(field)) { 1943 | field.value = form.values.select 1944 | } 1945 | }) 1946 | }, 1947 | }) 1948 | ) 1949 | 1950 | const select = attach( 1951 | form.createField({ 1952 | name: 'select', 1953 | }) 1954 | ) 1955 | 1956 | attach( 1957 | form.createObjectField({ 1958 | name: 'obj', 1959 | }) 1960 | ) 1961 | 1962 | attach( 1963 | form.createField({ 1964 | name: 'input1', 1965 | basePath: 'obj', 1966 | }) 1967 | ) 1968 | 1969 | attach( 1970 | form.createField({ 1971 | name: 'input2', 1972 | basePath: 'obj', 1973 | }) 1974 | ) 1975 | 1976 | select.value = 'none' 1977 | expect(form.values.obj?.input1).toBeUndefined() 1978 | select.value = 'visible' 1979 | expect(form.values.obj.input1).toBe('123') 1980 | }) 1981 | 1982 | test('query value with sibling path syntax', () => { 1983 | const form = attach(createForm()) 1984 | const fn = jest.fn() 1985 | attach( 1986 | form.createVoidField({ 1987 | name: 'void', 1988 | }) 1989 | ) 1990 | attach( 1991 | form.createObjectField({ 1992 | name: 'obj', 1993 | basePath: 'void', 1994 | }) 1995 | ) 1996 | attach( 1997 | form.createField({ 1998 | name: 'input', 1999 | basePath: 'void.obj', 2000 | reactions: [ 2001 | (field) => { 2002 | fn( 2003 | field.query('.textarea').value(), 2004 | field.query('.textarea').initialValue() 2005 | ) 2006 | }, 2007 | ], 2008 | }) 2009 | ) 2010 | const textarea = attach( 2011 | form.createField({ 2012 | name: 'textarea', 2013 | basePath: 'void.obj', 2014 | initialValue: 'aaa', 2015 | }) 2016 | ) 2017 | textarea.value = '123' 2018 | expect(fn).toBeCalledWith('123', 'aaa') 2019 | }) 2020 | 2021 | test('relative query with void field', () => { 2022 | const form = attach(createForm()) 2023 | attach( 2024 | form.createVoidField({ 2025 | name: 'void', 2026 | }) 2027 | ) 2028 | const aa = attach( 2029 | form.createField({ 2030 | name: 'aa', 2031 | basePath: 'void', 2032 | }) 2033 | ) 2034 | attach( 2035 | form.createVoidField({ 2036 | name: 'mm', 2037 | }) 2038 | ) 2039 | const bb = attach( 2040 | form.createField({ 2041 | name: 'bb', 2042 | basePath: 'mm', 2043 | }) 2044 | ) 2045 | 2046 | expect(bb.query('.aa').take()).toBe(aa) 2047 | }) 2048 | 2049 | test('empty string or number or null value need rewrite default value', () => { 2050 | const form = attach( 2051 | createForm<any>({ 2052 | values: { 2053 | aa: '', 2054 | bb: 0, 2055 | ee: null, 2056 | }, 2057 | }) 2058 | ) 2059 | attach( 2060 | form.createField({ 2061 | name: 'aa', 2062 | initialValue: 'test', 2063 | }) 2064 | ) 2065 | attach( 2066 | form.createField({ 2067 | name: 'bb', 2068 | initialValue: 123, 2069 | }) 2070 | ) 2071 | attach( 2072 | form.createField({ 2073 | name: 'cc', 2074 | initialValue: 'test', 2075 | }) 2076 | ) 2077 | attach( 2078 | form.createField({ 2079 | name: 'dd', 2080 | initialValue: 123, 2081 | }) 2082 | ) 2083 | attach( 2084 | form.createField({ 2085 | name: 'ee', 2086 | initialValue: 'test', 2087 | }) 2088 | ) 2089 | expect(form.values.aa).toEqual('') 2090 | expect(form.values.bb).toEqual(0) 2091 | expect(form.values.cc).toEqual('test') 2092 | expect(form.values.dd).toEqual(123) 2093 | expect(form.values.ee).toEqual(null) 2094 | }) 2095 | 2096 | test('destroy field need auto remove initialValues', () => { 2097 | const form = attach(createForm<any>()) 2098 | const aa = attach( 2099 | form.createField({ 2100 | name: 'aa', 2101 | initialValue: 'test', 2102 | }) 2103 | ) 2104 | expect(form.initialValues.aa).toEqual('test') 2105 | expect(form.values.aa).toEqual('test') 2106 | aa.destroy() 2107 | expect(form.initialValues.aa).toBeUndefined() 2108 | expect(form.values.aa).toBeUndefined() 2109 | }) 2110 | 2111 | test('validateFirst', async () => { 2112 | const form = attach( 2113 | createForm<any>({ 2114 | validateFirst: false, 2115 | }) 2116 | ) 2117 | const aaValidate = jest.fn(() => 'aaError') 2118 | const aa = attach( 2119 | form.createField({ 2120 | name: 'aa', 2121 | validateFirst: true, 2122 | validator: [aaValidate, aaValidate], 2123 | }) 2124 | ) 2125 | await aa.onInput('aa') 2126 | const bbValidate = jest.fn(() => 'bbError') 2127 | const bb = attach( 2128 | form.createField({ 2129 | name: 'bb', 2130 | validator: [bbValidate, bbValidate], 2131 | validateFirst: false, 2132 | }) 2133 | ) 2134 | await bb.onInput('bb') 2135 | const ccValidate = jest.fn(() => 'ccError') 2136 | const cc = attach( 2137 | form.createField({ 2138 | name: 'cc', 2139 | validator: [ccValidate, ccValidate], 2140 | }) 2141 | ) 2142 | await cc.onInput('cc') 2143 | 2144 | expect(aaValidate).toBeCalledTimes(1) 2145 | expect(bbValidate).toBeCalledTimes(2) 2146 | expect(ccValidate).toBeCalledTimes(2) 2147 | }) 2148 | 2149 | test('reactions should not be triggered when field destroyed', () => { 2150 | const form = attach(createForm<any>()) 2151 | const handler = jest.fn() 2152 | const obs = observable({ bb: 123 }) 2153 | const aa = attach( 2154 | form.createField({ 2155 | name: 'aa', 2156 | initialValue: 'test', 2157 | reactions() { 2158 | handler(obs.bb) 2159 | }, 2160 | }) 2161 | ) 2162 | obs.bb = 321 2163 | aa.destroy() 2164 | obs.bb = 111 2165 | expect(handler).toBeCalledTimes(2) 2166 | }) 2167 | 2168 | test('parent readPretty will overwrite self disabled or readOnly', () => { 2169 | const form = attach( 2170 | createForm<any>({ 2171 | readPretty: true, 2172 | }) 2173 | ) 2174 | const aa = attach( 2175 | form.createField({ 2176 | name: 'aa', 2177 | initialValue: 'test', 2178 | disabled: true, 2179 | }) 2180 | ) 2181 | const bb = attach( 2182 | form.createField({ 2183 | name: 'bb', 2184 | initialValue: 'test', 2185 | editable: true, 2186 | }) 2187 | ) 2188 | expect(aa.pattern).toBe('readPretty') 2189 | expect(bb.pattern).toBe('editable') 2190 | }) 2191 | 2192 | test('conflict name for errors filter', async () => { 2193 | const form = attach(createForm<any>()) 2194 | const aa = attach( 2195 | form.createField({ 2196 | name: 'aa', 2197 | required: true, 2198 | }) 2199 | ) 2200 | const aa1 = attach( 2201 | form.createField({ 2202 | name: 'aa1', 2203 | required: true, 2204 | }) 2205 | ) 2206 | 2207 | await aa1.onInput('') 2208 | expect(aa.invalid).toBe(false) 2209 | }) 2210 | 2211 | test('field destroyed can not be assign value', () => { 2212 | const form = attach(createForm<any>()) 2213 | const aa = attach( 2214 | form.createField({ 2215 | name: 'aa', 2216 | }) 2217 | ) 2218 | aa.destroy() 2219 | aa.initialValue = 222 2220 | aa.value = 111 2221 | expect(form.values).toEqual({}) 2222 | expect(form.initialValues).toEqual({}) 2223 | }) 2224 | 2225 | test('onInput could pass value with target', async () => { 2226 | const form = attach(createForm<any>()) 2227 | const aa = attach( 2228 | form.createField({ 2229 | name: 'aa', 2230 | }) 2231 | ) 2232 | await aa.onInput({ 2233 | target: '123', 2234 | }) 2235 | expect(aa.value).toEqual({ target: '123' }) 2236 | }) 2237 | 2238 | test('field destroyed or display none should not be assign value from patch initialValues', () => { 2239 | const form = attach(createForm()) 2240 | const aa = attach( 2241 | form.createField({ 2242 | name: 'aa', 2243 | display: 'none', 2244 | }) 2245 | ) 2246 | 2247 | aa.initialValue = '123' 2248 | 2249 | expect(form.values).toEqual({}) 2250 | 2251 | aa.display = 'visible' 2252 | 2253 | expect(aa.value).toBe('123') 2254 | expect(form.values).toEqual({ aa: '123' }) 2255 | }) 2256 | 2257 | test('onFieldReact with field destroyed', () => { 2258 | const fn = jest.fn() 2259 | const obs = observable<any>({ value: 123 }) 2260 | const form = attach( 2261 | createForm({ 2262 | effects() { 2263 | onFieldReact('aa', () => { 2264 | fn(obs.value) 2265 | }) 2266 | }, 2267 | }) 2268 | ) 2269 | const aa = attach( 2270 | form.createField({ 2271 | name: 'aa', 2272 | }) 2273 | ) 2274 | obs.value = '321' 2275 | expect(fn).toBeCalledTimes(2) 2276 | aa.destroy() 2277 | obs.value = '111' 2278 | expect(fn).toBeCalledTimes(2) 2279 | }) 2280 | 2281 | test('field actions', () => { 2282 | const form = attach(createForm()) 2283 | const aa = attach( 2284 | form.createField({ 2285 | name: 'aa', 2286 | }) 2287 | ) 2288 | expect(aa.actions).toEqual({}) 2289 | aa.inject({ 2290 | test: () => 123, 2291 | }) 2292 | expect(aa.invoke('test')).toEqual(123) 2293 | aa.inject({ 2294 | test: () => 321, 2295 | }) 2296 | expect(aa.invoke('test')).toEqual(321) 2297 | }) 2298 | 2299 | test('field hidden value', () => { 2300 | const form = attach(createForm()) 2301 | const aa = attach( 2302 | form.createField({ 2303 | name: 'aa', 2304 | hidden: true, 2305 | initialValue: '123', 2306 | }) 2307 | ) 2308 | expect(form.values).toEqual({ aa: '123' }) 2309 | 2310 | const objectField = attach( 2311 | form.createObjectField({ 2312 | name: 'object', 2313 | hidden: true, 2314 | }) 2315 | ) 2316 | const arrayField = attach( 2317 | form.createArrayField({ 2318 | name: 'array', 2319 | hidden: true, 2320 | }) 2321 | ) 2322 | 2323 | aa.setDisplay('none') 2324 | objectField.setDisplay('none') 2325 | arrayField.setDisplay('none') 2326 | expect(aa.value).toBeUndefined() 2327 | expect(objectField.value).toBeUndefined() 2328 | expect(arrayField.value).toBeUndefined() 2329 | 2330 | aa.setDisplay('hidden') 2331 | objectField.setDisplay('hidden') 2332 | arrayField.setDisplay('hidden') 2333 | expect(aa.value).toEqual('123') 2334 | expect(objectField.value).toEqual({}) 2335 | expect(arrayField.value).toEqual([]) 2336 | }) 2337 | 2338 | test('field destructor path with display none', () => { 2339 | const form = attach(createForm()) 2340 | const aa = attach( 2341 | form.createArrayField({ 2342 | name: '[aa,bb]', 2343 | }) 2344 | ) 2345 | aa.setDisplay('none') 2346 | expect(form.values).toEqual({}) 2347 | expect(aa.value).toEqual([]) 2348 | }) 2349 | 2350 | test('onInput should ignore HTMLInputEvent propagation', async () => { 2351 | const form = attach(createForm<any>()) 2352 | const mockHTMLInput = { value: '321' } 2353 | const mockDomEvent = { target: mockHTMLInput, currentTarget: mockHTMLInput } 2354 | const aa = attach( 2355 | form.createField({ 2356 | name: 'aa', 2357 | }) 2358 | ) 2359 | await aa.onInput(mockDomEvent) 2360 | expect(aa.value).toEqual('321') 2361 | 2362 | await aa.onInput({ target: { value: '2' }, currentTarget: { value: '4' } }) 2363 | expect(aa.value).toEqual('321') 2364 | 2365 | // currentTarget is undefined, skip ignore 2366 | await aa.onInput({ target: { value: '123' } }) 2367 | expect(aa.value).toEqual('123') 2368 | }) 2369 | 2370 | test('onFocus and onBlur with invalid target value', async () => { 2371 | const form = attach(createForm<any>()) 2372 | const field = attach( 2373 | form.createField({ 2374 | name: 'aa', 2375 | validateFirst: true, 2376 | value: '111', 2377 | validator: [ 2378 | { 2379 | triggerType: 'onFocus', 2380 | format: 'date', 2381 | }, 2382 | { 2383 | triggerType: 'onBlur', 2384 | format: 'url', 2385 | }, 2386 | ], 2387 | }) 2388 | ) 2389 | 2390 | await field.onFocus({ target: {} }) 2391 | expect(field.selfErrors).toEqual([]) 2392 | await field.onBlur({ target: {} }) 2393 | expect(field.selfErrors).toEqual([]) 2394 | 2395 | await field.onFocus() 2396 | expect(field.selfErrors).toEqual([ 2397 | 'The field value is not a valid date format', 2398 | ]) 2399 | await field.onBlur() 2400 | expect(field.selfErrors).toEqual([ 2401 | 'The field value is not a valid date format', 2402 | 'The field value is a invalid url', 2403 | ]) 2404 | }) 2405 | 2406 | test('validatePattern and validateDisplay', async () => { 2407 | const form = attach( 2408 | createForm<any>({ 2409 | validatePattern: ['editable'], 2410 | validateDisplay: ['visible'], 2411 | }) 2412 | ) 2413 | const field1 = attach( 2414 | form.createField({ 2415 | name: 'a', 2416 | required: true, 2417 | }) 2418 | ) 2419 | const field2 = attach( 2420 | form.createField({ 2421 | name: 'b', 2422 | required: true, 2423 | validatePattern: ['readOnly'], 2424 | validateDisplay: ['hidden'], 2425 | }) 2426 | ) 2427 | const field3 = attach( 2428 | form.createField({ 2429 | name: 'c', 2430 | required: true, 2431 | validatePattern: ['readOnly', 'editable'], 2432 | validateDisplay: ['hidden', 'visible'], 2433 | }) 2434 | ) 2435 | 2436 | try { 2437 | await form.validate() 2438 | } catch {} 2439 | expect(field1.selfErrors.length).toBe(1) 2440 | expect(field2.selfErrors.length).toBe(0) 2441 | expect(field3.selfErrors.length).toBe(1) 2442 | 2443 | form.setPattern('readOnly') 2444 | form.setDisplay('hidden') 2445 | try { 2446 | await form.validate() 2447 | } catch {} 2448 | expect(field1.selfErrors.length).toBe(0) 2449 | expect(field2.selfErrors.length).toBe(1) 2450 | expect(field3.selfErrors.length).toBe(1) 2451 | }) 2452 | ```