This is page 25 of 35. Use http://codebase.md/alibaba/formily?lines=false&page={x} to view the full context. # Directory Structure ``` ├── .all-contributorsrc ├── .codecov.yml ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .github │ ├── CONTRIBUTING.md │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE │ │ └── config.yml │ ├── PULL_REQUEST_TEMPLATE.md │ └── workflows │ ├── check-pr-title.yml │ ├── ci.yml │ ├── commitlint.yml │ ├── issue-open-check.yml │ ├── package-size.yml │ └── pr-welcome.yml ├── .gitignore ├── .prettierrc.js ├── .umirc.js ├── .vscode │ └── cspell.json ├── .yarnrc ├── CHANGELOG.md ├── commitlint.config.js ├── devtools │ ├── .eslintrc │ └── chrome-extension │ ├── .npmignore │ ├── assets │ │ └── img │ │ ├── loading.svg │ │ └── logo │ │ ├── 128x128.png │ │ ├── 16x16.png │ │ ├── 38x38.png │ │ ├── 48x48.png │ │ ├── error.png │ │ ├── gray.png │ │ └── scalable.png │ ├── config │ │ ├── webpack.base.ts │ │ ├── webpack.dev.ts │ │ └── webpack.prod.ts │ ├── LICENSE.md │ ├── package.json │ ├── src │ │ ├── app │ │ │ ├── components │ │ │ │ ├── FieldTree.tsx │ │ │ │ ├── filter.ts │ │ │ │ ├── LeftPanel.tsx │ │ │ │ ├── RightPanel.tsx │ │ │ │ ├── SearchBox.tsx │ │ │ │ └── Tabs.tsx │ │ │ ├── demo.tsx │ │ │ └── index.tsx │ │ └── extension │ │ ├── backend.ts │ │ ├── background.ts │ │ ├── content.ts │ │ ├── devpanel.tsx │ │ ├── devtools.tsx │ │ ├── inject.ts │ │ ├── manifest.json │ │ ├── popup.tsx │ │ └── views │ │ ├── devpanel.ejs │ │ ├── devtools.ejs │ │ └── popup.ejs │ ├── tsconfig.build.json │ └── tsconfig.json ├── docs │ ├── functions │ │ ├── contributors.ts │ │ └── npm-search.ts │ ├── guide │ │ ├── advanced │ │ │ ├── async.md │ │ │ ├── async.zh-CN.md │ │ │ ├── build.md │ │ │ ├── build.zh-CN.md │ │ │ ├── business-logic.md │ │ │ ├── business-logic.zh-CN.md │ │ │ ├── calculator.md │ │ │ ├── calculator.zh-CN.md │ │ │ ├── controlled.md │ │ │ ├── controlled.zh-CN.md │ │ │ ├── custom.md │ │ │ ├── custom.zh-CN.md │ │ │ ├── destructor.md │ │ │ ├── destructor.zh-CN.md │ │ │ ├── input.less │ │ │ ├── layout.md │ │ │ ├── layout.zh-CN.md │ │ │ ├── linkages.md │ │ │ ├── linkages.zh-CN.md │ │ │ ├── validate.md │ │ │ └── validate.zh-CN.md │ │ ├── contribution.md │ │ ├── contribution.zh-CN.md │ │ ├── form-builder.md │ │ ├── form-builder.zh-CN.md │ │ ├── index.md │ │ ├── index.zh-CN.md │ │ ├── issue-helper.md │ │ ├── issue-helper.zh-CN.md │ │ ├── learn-formily.md │ │ ├── learn-formily.zh-CN.md │ │ ├── quick-start.md │ │ ├── quick-start.zh-CN.md │ │ ├── scenes │ │ │ ├── dialog-drawer.md │ │ │ ├── dialog-drawer.zh-CN.md │ │ │ ├── edit-detail.md │ │ │ ├── edit-detail.zh-CN.md │ │ │ ├── index.less │ │ │ ├── login-register.md │ │ │ ├── login-register.zh-CN.md │ │ │ ├── more.md │ │ │ ├── more.zh-CN.md │ │ │ ├── query-list.md │ │ │ ├── query-list.zh-CN.md │ │ │ ├── step-form.md │ │ │ ├── step-form.zh-CN.md │ │ │ ├── tab-form.md │ │ │ ├── tab-form.zh-CN.md │ │ │ └── VerifyCode.tsx │ │ ├── upgrade.md │ │ └── upgrade.zh-CN.md │ ├── index.md │ ├── index.zh-CN.md │ └── site │ ├── Contributors.less │ ├── Contributors.tsx │ ├── QrCode.less │ ├── QrCode.tsx │ ├── Section.less │ ├── Section.tsx │ └── styles.less ├── global.config.ts ├── jest.config.js ├── lerna.json ├── LICENSE.md ├── package.json ├── packages │ ├── .eslintrc │ ├── antd │ │ ├── __tests__ │ │ │ ├── moment.spec.ts │ │ │ └── sideEffects.spec.ts │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── build-style.ts │ │ ├── create-style.ts │ │ ├── docs │ │ │ ├── components │ │ │ │ ├── ArrayCards.md │ │ │ │ ├── ArrayCards.zh-CN.md │ │ │ │ ├── ArrayCollapse.md │ │ │ │ ├── ArrayCollapse.zh-CN.md │ │ │ │ ├── ArrayItems.md │ │ │ │ ├── ArrayItems.zh-CN.md │ │ │ │ ├── ArrayTable.md │ │ │ │ ├── ArrayTable.zh-CN.md │ │ │ │ ├── ArrayTabs.md │ │ │ │ ├── ArrayTabs.zh-CN.md │ │ │ │ ├── Cascader.md │ │ │ │ ├── Cascader.zh-CN.md │ │ │ │ ├── Checkbox.md │ │ │ │ ├── Checkbox.zh-CN.md │ │ │ │ ├── DatePicker.md │ │ │ │ ├── DatePicker.zh-CN.md │ │ │ │ ├── Editable.md │ │ │ │ ├── Editable.zh-CN.md │ │ │ │ ├── Form.md │ │ │ │ ├── Form.zh-CN.md │ │ │ │ ├── FormButtonGroup.md │ │ │ │ ├── FormButtonGroup.zh-CN.md │ │ │ │ ├── FormCollapse.md │ │ │ │ ├── FormCollapse.zh-CN.md │ │ │ │ ├── FormDialog.md │ │ │ │ ├── FormDialog.zh-CN.md │ │ │ │ ├── FormDrawer.md │ │ │ │ ├── FormDrawer.zh-CN.md │ │ │ │ ├── FormGrid.md │ │ │ │ ├── FormGrid.zh-CN.md │ │ │ │ ├── FormItem.md │ │ │ │ ├── FormItem.zh-CN.md │ │ │ │ ├── FormLayout.md │ │ │ │ ├── FormLayout.zh-CN.md │ │ │ │ ├── FormStep.md │ │ │ │ ├── FormStep.zh-CN.md │ │ │ │ ├── FormTab.md │ │ │ │ ├── FormTab.zh-CN.md │ │ │ │ ├── index.md │ │ │ │ ├── index.zh-CN.md │ │ │ │ ├── Input.md │ │ │ │ ├── Input.zh-CN.md │ │ │ │ ├── NumberPicker.md │ │ │ │ ├── NumberPicker.zh-CN.md │ │ │ │ ├── Password.md │ │ │ │ ├── Password.zh-CN.md │ │ │ │ ├── PreviewText.md │ │ │ │ ├── PreviewText.zh-CN.md │ │ │ │ ├── Radio.md │ │ │ │ ├── Radio.zh-CN.md │ │ │ │ ├── Reset.md │ │ │ │ ├── Reset.zh-CN.md │ │ │ │ ├── Select.md │ │ │ │ ├── Select.zh-CN.md │ │ │ │ ├── SelectTable.md │ │ │ │ ├── SelectTable.zh-CN.md │ │ │ │ ├── Space.md │ │ │ │ ├── Space.zh-CN.md │ │ │ │ ├── Submit.md │ │ │ │ ├── Submit.zh-CN.md │ │ │ │ ├── Switch.md │ │ │ │ ├── Switch.zh-CN.md │ │ │ │ ├── TimePicker.md │ │ │ │ ├── TimePicker.zh-CN.md │ │ │ │ ├── Transfer.md │ │ │ │ ├── Transfer.zh-CN.md │ │ │ │ ├── TreeSelect.md │ │ │ │ ├── TreeSelect.zh-CN.md │ │ │ │ ├── Upload.md │ │ │ │ └── Upload.zh-CN.md │ │ │ ├── index.md │ │ │ └── index.zh-CN.md │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __builtins__ │ │ │ │ ├── hooks │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── useClickAway.ts │ │ │ │ │ └── usePrefixCls.ts │ │ │ │ ├── index.ts │ │ │ │ ├── loading.ts │ │ │ │ ├── moment.ts │ │ │ │ ├── pickDataProps.ts │ │ │ │ ├── portal.tsx │ │ │ │ ├── render.ts │ │ │ │ └── sort.tsx │ │ │ ├── array-base │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── array-cards │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── array-collapse │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── array-items │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── array-table │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── array-tabs │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── cascader │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── checkbox │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── date-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── editable │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── form │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── form-button-group │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── form-collapse │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-dialog │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-drawer │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-grid │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── form-item │ │ │ │ ├── animation.less │ │ │ │ ├── grid.less │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── form-layout │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ ├── style.ts │ │ │ │ └── useResponsiveFormLayout.ts │ │ │ ├── form-step │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-tab │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── index.ts │ │ │ ├── input │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── number-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── password │ │ │ │ ├── index.tsx │ │ │ │ ├── PasswordStrength.tsx │ │ │ │ └── style.ts │ │ │ ├── preview-text │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── radio │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── reset │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── select │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── select-table │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ ├── style.ts │ │ │ │ ├── useCheckSlackly.tsx │ │ │ │ ├── useFilterOptions.tsx │ │ │ │ ├── useFlatOptions.tsx │ │ │ │ ├── useSize.tsx │ │ │ │ ├── useTitleAddon.tsx │ │ │ │ └── utils.ts │ │ │ ├── space │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── style.less │ │ │ ├── style.ts │ │ │ ├── submit │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── switch │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── time-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── transfer │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── tree-select │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ └── upload │ │ │ ├── index.tsx │ │ │ ├── placeholder.ts │ │ │ └── style.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── benchmark │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── src │ │ │ └── index.tsx │ │ ├── template.ejs │ │ ├── tsconfig.build.json │ │ ├── tsconfig.json │ │ ├── webpack.base.ts │ │ ├── webpack.dev.ts │ │ └── webpack.prod.ts │ ├── core │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── docs │ │ │ ├── api │ │ │ │ ├── entry │ │ │ │ │ ├── ActionResponse.less │ │ │ │ │ ├── ActionResponse.tsx │ │ │ │ │ ├── createForm.md │ │ │ │ │ ├── createForm.zh-CN.md │ │ │ │ │ ├── FieldEffectHooks.md │ │ │ │ │ ├── FieldEffectHooks.zh-CN.md │ │ │ │ │ ├── FormChecker.md │ │ │ │ │ ├── FormChecker.zh-CN.md │ │ │ │ │ ├── FormEffectHooks.md │ │ │ │ │ ├── FormEffectHooks.zh-CN.md │ │ │ │ │ ├── FormHooksAPI.md │ │ │ │ │ ├── FormHooksAPI.zh-CN.md │ │ │ │ │ ├── FormPath.md │ │ │ │ │ ├── FormPath.zh-CN.md │ │ │ │ │ ├── FormValidatorRegistry.md │ │ │ │ │ └── FormValidatorRegistry.zh-CN.md │ │ │ │ └── models │ │ │ │ ├── ArrayField.md │ │ │ │ ├── ArrayField.zh-CN.md │ │ │ │ ├── Field.md │ │ │ │ ├── Field.zh-CN.md │ │ │ │ ├── Form.md │ │ │ │ ├── Form.zh-CN.md │ │ │ │ ├── ObjectField.md │ │ │ │ ├── ObjectField.zh-CN.md │ │ │ │ ├── Query.md │ │ │ │ ├── Query.zh-CN.md │ │ │ │ ├── VoidField.md │ │ │ │ └── VoidField.zh-CN.md │ │ │ ├── guide │ │ │ │ ├── architecture.md │ │ │ │ ├── architecture.zh-CN.md │ │ │ │ ├── field.md │ │ │ │ ├── field.zh-CN.md │ │ │ │ ├── form.md │ │ │ │ ├── form.zh-CN.md │ │ │ │ ├── index.md │ │ │ │ ├── index.zh-CN.md │ │ │ │ ├── mvvm.md │ │ │ │ ├── mvvm.zh-CN.md │ │ │ │ ├── values.md │ │ │ │ └── values.zh-CN.md │ │ │ ├── index.md │ │ │ └── index.zh-CN.md │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── array.spec.ts │ │ │ │ ├── effects.spec.ts │ │ │ │ ├── externals.spec.ts │ │ │ │ ├── field.spec.ts │ │ │ │ ├── form.spec.ts │ │ │ │ ├── graph.spec.ts │ │ │ │ ├── heart.spec.ts │ │ │ │ ├── internals.spec.ts │ │ │ │ ├── lifecycle.spec.ts │ │ │ │ ├── object.spec.ts │ │ │ │ ├── shared.ts │ │ │ │ └── void.spec.ts │ │ │ ├── effects │ │ │ │ ├── index.ts │ │ │ │ ├── onFieldEffects.ts │ │ │ │ └── onFormEffects.ts │ │ │ ├── global.d.ts │ │ │ ├── index.ts │ │ │ ├── models │ │ │ │ ├── ArrayField.ts │ │ │ │ ├── BaseField.ts │ │ │ │ ├── Field.ts │ │ │ │ ├── Form.ts │ │ │ │ ├── Graph.ts │ │ │ │ ├── Heart.ts │ │ │ │ ├── index.ts │ │ │ │ ├── LifeCycle.ts │ │ │ │ ├── ObjectField.ts │ │ │ │ ├── Query.ts │ │ │ │ ├── types.ts │ │ │ │ └── VoidField.ts │ │ │ ├── shared │ │ │ │ ├── checkers.ts │ │ │ │ ├── constants.ts │ │ │ │ ├── effective.ts │ │ │ │ ├── externals.ts │ │ │ │ └── internals.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── element │ │ ├── .npmignore │ │ ├── build-style.ts │ │ ├── create-style.ts │ │ ├── docs │ │ │ ├── .vuepress │ │ │ │ ├── components │ │ │ │ │ ├── createCodeSandBox.js │ │ │ │ │ ├── dumi-previewer.vue │ │ │ │ │ └── highlight.js │ │ │ │ ├── config.js │ │ │ │ ├── enhanceApp.js │ │ │ │ ├── styles │ │ │ │ │ └── index.styl │ │ │ │ └── util.js │ │ │ ├── demos │ │ │ │ ├── guide │ │ │ │ │ ├── array-cards │ │ │ │ │ │ ├── effects-json-schema.vue │ │ │ │ │ │ ├── effects-markup-schema.vue │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── array-collapse │ │ │ │ │ │ ├── effects-json-schema.vue │ │ │ │ │ │ ├── effects-markup-schema.vue │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── array-items │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── array-table │ │ │ │ │ │ ├── effects-json-schema.vue │ │ │ │ │ │ ├── effects-markup-schema.vue │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── array-tabs │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── cascader │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── checkbox │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── date-picker │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── editable │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── form-button-group.vue │ │ │ │ │ ├── form-collapse │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── form-dialog │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── form-drawer │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── form-grid │ │ │ │ │ │ ├── form.vue │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── native.vue │ │ │ │ │ ├── form-item │ │ │ │ │ │ ├── bordered-none.vue │ │ │ │ │ │ ├── common.vue │ │ │ │ │ │ ├── feedback.vue │ │ │ │ │ │ ├── inset.vue │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ ├── size.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── form-layout │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── form-step │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── form-tab │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── form.vue │ │ │ │ │ ├── input │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── input-number │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── password │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── preview-text │ │ │ │ │ │ ├── base.vue │ │ │ │ │ │ └── extend.vue │ │ │ │ │ ├── radio │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── reset │ │ │ │ │ │ ├── base.vue │ │ │ │ │ │ ├── force.vue │ │ │ │ │ │ └── validate.vue │ │ │ │ │ ├── select │ │ │ │ │ │ ├── json-schema-async.vue │ │ │ │ │ │ ├── json-schema-sync.vue │ │ │ │ │ │ ├── markup-schema-async-search.vue │ │ │ │ │ │ ├── markup-schema-async.vue │ │ │ │ │ │ ├── markup-schema-sync.vue │ │ │ │ │ │ ├── template-async.vue │ │ │ │ │ │ └── template-sync.vue │ │ │ │ │ ├── space │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── submit │ │ │ │ │ │ ├── base.vue │ │ │ │ │ │ └── loading.vue │ │ │ │ │ ├── switch │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── time-picker │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── transfer │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ └── upload │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ └── template.vue │ │ │ │ └── index.vue │ │ │ ├── guide │ │ │ │ ├── array-cards.md │ │ │ │ ├── array-collapse.md │ │ │ │ ├── array-items.md │ │ │ │ ├── array-table.md │ │ │ │ ├── array-tabs.md │ │ │ │ ├── cascader.md │ │ │ │ ├── checkbox.md │ │ │ │ ├── date-picker.md │ │ │ │ ├── editable.md │ │ │ │ ├── form-button-group.md │ │ │ │ ├── form-collapse.md │ │ │ │ ├── form-dialog.md │ │ │ │ ├── form-drawer.md │ │ │ │ ├── form-grid.md │ │ │ │ ├── form-item.md │ │ │ │ ├── form-layout.md │ │ │ │ ├── form-step.md │ │ │ │ ├── form-tab.md │ │ │ │ ├── form.md │ │ │ │ ├── index.md │ │ │ │ ├── input-number.md │ │ │ │ ├── input.md │ │ │ │ ├── password.md │ │ │ │ ├── preview-text.md │ │ │ │ ├── radio.md │ │ │ │ ├── reset.md │ │ │ │ ├── select.md │ │ │ │ ├── space.md │ │ │ │ ├── submit.md │ │ │ │ ├── switch.md │ │ │ │ ├── time-picker.md │ │ │ │ ├── transfer.md │ │ │ │ └── upload.md │ │ │ └── README.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __builtins__ │ │ │ │ ├── configs │ │ │ │ │ └── index.ts │ │ │ │ ├── index.ts │ │ │ │ ├── shared │ │ │ │ │ ├── create-context.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── loading.ts │ │ │ │ │ ├── portal.ts │ │ │ │ │ ├── resolve-component.ts │ │ │ │ │ ├── transform-component.ts │ │ │ │ │ ├── types.ts │ │ │ │ │ └── utils.ts │ │ │ │ └── styles │ │ │ │ └── common.scss │ │ │ ├── array-base │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── array-cards │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── array-collapse │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── array-items │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── array-table │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── array-tabs │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── cascader │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── checkbox │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── date-picker │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── editable │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── el-form │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── el-form-item │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── form │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── form-button-group │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── form-collapse │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── form-dialog │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── form-drawer │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── form-grid │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── form-item │ │ │ │ ├── animation.scss │ │ │ │ ├── grid.scss │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ ├── style.ts │ │ │ │ └── var.scss │ │ │ ├── form-layout │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ ├── style.ts │ │ │ │ └── useResponsiveFormLayout.ts │ │ │ ├── form-step │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── form-tab │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── index.ts │ │ │ ├── input │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── input-number │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── password │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── preview-text │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── radio │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── reset │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── select │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── space │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── style.ts │ │ │ ├── submit │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── switch │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── time-picker │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── transfer │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ └── upload │ │ │ ├── index.ts │ │ │ └── style.ts │ │ ├── transformer.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── grid │ │ ├── .npmignore │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── index.ts │ │ │ └── observer.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── json-schema │ │ ├── .npmignore │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── __snapshots__ │ │ │ │ │ └── schema.spec.ts.snap │ │ │ │ ├── compiler.spec.ts │ │ │ │ ├── patches.spec.ts │ │ │ │ ├── schema.spec.ts │ │ │ │ ├── server-validate.spec.ts │ │ │ │ ├── shared.spec.ts │ │ │ │ ├── transformer.spec.ts │ │ │ │ └── traverse.spec.ts │ │ │ ├── compiler.ts │ │ │ ├── global.d.ts │ │ │ ├── index.ts │ │ │ ├── patches.ts │ │ │ ├── polyfills │ │ │ │ ├── index.ts │ │ │ │ └── SPECIFICATION_1_0.ts │ │ │ ├── schema.ts │ │ │ ├── shared.ts │ │ │ ├── transformer.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── next │ │ ├── __tests__ │ │ │ ├── moment.spec.ts │ │ │ └── sideEffects.spec.ts │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── build-style.ts │ │ ├── create-style.ts │ │ ├── docs │ │ │ ├── components │ │ │ │ ├── ArrayCards.md │ │ │ │ ├── ArrayCards.zh-CN.md │ │ │ │ ├── ArrayCollapse.md │ │ │ │ ├── ArrayCollapse.zh-CN.md │ │ │ │ ├── ArrayItems.md │ │ │ │ ├── ArrayItems.zh-CN.md │ │ │ │ ├── ArrayTable.md │ │ │ │ ├── ArrayTable.zh-CN.md │ │ │ │ ├── Cascader.md │ │ │ │ ├── Cascader.zh-CN.md │ │ │ │ ├── Checkbox.md │ │ │ │ ├── Checkbox.zh-CN.md │ │ │ │ ├── DatePicker.md │ │ │ │ ├── DatePicker.zh-CN.md │ │ │ │ ├── DatePicker2.md │ │ │ │ ├── DatePicker2.zh-CN.md │ │ │ │ ├── Editable.md │ │ │ │ ├── Editable.zh-CN.md │ │ │ │ ├── Form.md │ │ │ │ ├── Form.zh-CN.md │ │ │ │ ├── FormButtonGroup.md │ │ │ │ ├── FormButtonGroup.zh-CN.md │ │ │ │ ├── FormCollapse.md │ │ │ │ ├── FormCollapse.zh-CN.md │ │ │ │ ├── FormDialog.md │ │ │ │ ├── FormDialog.zh-CN.md │ │ │ │ ├── FormDrawer.md │ │ │ │ ├── FormDrawer.zh-CN.md │ │ │ │ ├── FormGrid.md │ │ │ │ ├── FormGrid.zh-CN.md │ │ │ │ ├── FormItem.md │ │ │ │ ├── FormItem.zh-CN.md │ │ │ │ ├── FormLayout.md │ │ │ │ ├── FormLayout.zh-CN.md │ │ │ │ ├── FormStep.md │ │ │ │ ├── FormStep.zh-CN.md │ │ │ │ ├── FormTab.md │ │ │ │ ├── FormTab.zh-CN.md │ │ │ │ ├── index.md │ │ │ │ ├── index.zh-CN.md │ │ │ │ ├── Input.md │ │ │ │ ├── Input.zh-CN.md │ │ │ │ ├── NumberPicker.md │ │ │ │ ├── NumberPicker.zh-CN.md │ │ │ │ ├── Password.md │ │ │ │ ├── Password.zh-CN.md │ │ │ │ ├── PreviewText.md │ │ │ │ ├── PreviewText.zh-CN.md │ │ │ │ ├── Radio.md │ │ │ │ ├── Radio.zh-CN.md │ │ │ │ ├── Reset.md │ │ │ │ ├── Reset.zh-CN.md │ │ │ │ ├── Select.md │ │ │ │ ├── Select.zh-CN.md │ │ │ │ ├── SelectTable.md │ │ │ │ ├── SelectTable.zh-CN.md │ │ │ │ ├── Space.md │ │ │ │ ├── Space.zh-CN.md │ │ │ │ ├── Submit.md │ │ │ │ ├── Submit.zh-CN.md │ │ │ │ ├── Switch.md │ │ │ │ ├── Switch.zh-CN.md │ │ │ │ ├── TimePicker.md │ │ │ │ ├── TimePicker.zh-CN.md │ │ │ │ ├── TimePicker2.md │ │ │ │ ├── TimePicker2.zh-CN.md │ │ │ │ ├── Transfer.md │ │ │ │ ├── Transfer.zh-CN.md │ │ │ │ ├── TreeSelect.md │ │ │ │ ├── TreeSelect.zh-CN.md │ │ │ │ ├── Upload.md │ │ │ │ └── Upload.zh-CN.md │ │ │ ├── index.md │ │ │ └── index.zh-CN.md │ │ ├── LESENCE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __builtins__ │ │ │ │ ├── empty.tsx │ │ │ │ ├── hooks │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── useClickAway.ts │ │ │ │ │ └── usePrefixCls.ts │ │ │ │ ├── icons.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── loading.ts │ │ │ │ ├── mapSize.ts │ │ │ │ ├── mapStatus.ts │ │ │ │ ├── moment.ts │ │ │ │ ├── pickDataProps.ts │ │ │ │ ├── portal.tsx │ │ │ │ ├── render.ts │ │ │ │ └── toArray.ts │ │ │ ├── array-base │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── array-cards │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── array-collapse │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── array-items │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── array-table │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── cascader │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── checkbox │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── date-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── date-picker2 │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── editable │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── form │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── form-button-group │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── form-collapse │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-dialog │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-drawer │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-grid │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── form-item │ │ │ │ ├── animation.scss │ │ │ │ ├── grid.scss │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ ├── scss │ │ │ │ │ └── variable.scss │ │ │ │ └── style.ts │ │ │ ├── form-layout │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ ├── style.ts │ │ │ │ └── useResponsiveFormLayout.ts │ │ │ ├── form-step │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-tab │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── index.ts │ │ │ ├── input │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── main.scss │ │ │ ├── number-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── password │ │ │ │ ├── index.tsx │ │ │ │ ├── PasswordStrength.tsx │ │ │ │ └── style.ts │ │ │ ├── preview-text │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── radio │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── reset │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── select │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── select-table │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ ├── style.ts │ │ │ │ ├── useCheckSlackly.tsx │ │ │ │ ├── useFilterOptions.tsx │ │ │ │ ├── useFlatOptions.tsx │ │ │ │ ├── useSize.tsx │ │ │ │ ├── useTitleAddon.tsx │ │ │ │ └── utils.ts │ │ │ ├── space │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── style.ts │ │ │ ├── submit │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── switch │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── time-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── time-picker2 │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── transfer │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── tree-select │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ └── upload │ │ │ ├── index.tsx │ │ │ ├── main.scss │ │ │ ├── placeholder.ts │ │ │ └── style.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── path │ │ ├── .npmignore │ │ ├── benchmark.ts │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── accessor.spec.ts │ │ │ │ ├── basic.spec.ts │ │ │ │ ├── match.spec.ts │ │ │ │ ├── parser.spec.ts │ │ │ │ └── share.spec.ts │ │ │ ├── contexts.ts │ │ │ ├── destructor.ts │ │ │ ├── index.ts │ │ │ ├── matcher.ts │ │ │ ├── parser.ts │ │ │ ├── shared.ts │ │ │ ├── tokenizer.ts │ │ │ ├── tokens.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── react │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── docs │ │ │ ├── api │ │ │ │ ├── components │ │ │ │ │ ├── ArrayField.md │ │ │ │ │ ├── ArrayField.zh-CN.md │ │ │ │ │ ├── ExpressionScope.md │ │ │ │ │ ├── ExpressionScope.zh-CN.md │ │ │ │ │ ├── Field.md │ │ │ │ │ ├── Field.zh-CN.md │ │ │ │ │ ├── FormConsumer.md │ │ │ │ │ ├── FormConsumer.zh-CN.md │ │ │ │ │ ├── FormProvider.md │ │ │ │ │ ├── FormProvider.zh-CN.md │ │ │ │ │ ├── ObjectField.md │ │ │ │ │ ├── ObjectField.zh-CN.md │ │ │ │ │ ├── RecordScope.md │ │ │ │ │ ├── RecordScope.zh-CN.md │ │ │ │ │ ├── RecordsScope.md │ │ │ │ │ ├── RecordsScope.zh-CN.md │ │ │ │ │ ├── RecursionField.md │ │ │ │ │ ├── RecursionField.zh-CN.md │ │ │ │ │ ├── SchemaField.md │ │ │ │ │ ├── SchemaField.zh-CN.md │ │ │ │ │ ├── VoidField.md │ │ │ │ │ └── VoidField.zh-CN.md │ │ │ │ ├── hooks │ │ │ │ │ ├── useExpressionScope.md │ │ │ │ │ ├── useExpressionScope.zh-CN.md │ │ │ │ │ ├── useField.md │ │ │ │ │ ├── useField.zh-CN.md │ │ │ │ │ ├── useFieldSchema.md │ │ │ │ │ ├── useFieldSchema.zh-CN.md │ │ │ │ │ ├── useForm.md │ │ │ │ │ ├── useForm.zh-CN.md │ │ │ │ │ ├── useFormEffects.md │ │ │ │ │ ├── useFormEffects.zh-CN.md │ │ │ │ │ ├── useParentForm.md │ │ │ │ │ └── useParentForm.zh-CN.md │ │ │ │ └── shared │ │ │ │ ├── connect.md │ │ │ │ ├── connect.zh-CN.md │ │ │ │ ├── context.md │ │ │ │ ├── context.zh-CN.md │ │ │ │ ├── mapProps.md │ │ │ │ ├── mapProps.zh-CN.md │ │ │ │ ├── mapReadPretty.md │ │ │ │ ├── mapReadPretty.zh-CN.md │ │ │ │ ├── observer.md │ │ │ │ ├── observer.zh-CN.md │ │ │ │ ├── Schema.md │ │ │ │ └── Schema.zh-CN.md │ │ │ ├── guide │ │ │ │ ├── architecture.md │ │ │ │ ├── architecture.zh-CN.md │ │ │ │ ├── concept.md │ │ │ │ ├── concept.zh-CN.md │ │ │ │ ├── index.md │ │ │ │ └── index.zh-CN.md │ │ │ ├── index.md │ │ │ └── index.zh-CN.md │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── expression.spec.tsx │ │ │ │ ├── field.spec.tsx │ │ │ │ ├── form.spec.tsx │ │ │ │ ├── schema.json.spec.tsx │ │ │ │ ├── schema.markup.spec.tsx │ │ │ │ └── shared.tsx │ │ │ ├── components │ │ │ │ ├── ArrayField.tsx │ │ │ │ ├── ExpressionScope.tsx │ │ │ │ ├── Field.tsx │ │ │ │ ├── FormConsumer.tsx │ │ │ │ ├── FormProvider.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── ObjectField.tsx │ │ │ │ ├── ReactiveField.tsx │ │ │ │ ├── RecordScope.tsx │ │ │ │ ├── RecordsScope.tsx │ │ │ │ ├── RecursionField.tsx │ │ │ │ ├── SchemaField.tsx │ │ │ │ └── VoidField.tsx │ │ │ ├── global.d.ts │ │ │ ├── hooks │ │ │ │ ├── index.ts │ │ │ │ ├── useAttach.ts │ │ │ │ ├── useExpressionScope.ts │ │ │ │ ├── useField.ts │ │ │ │ ├── useFieldSchema.ts │ │ │ │ ├── useForm.ts │ │ │ │ ├── useFormEffects.ts │ │ │ │ └── useParentForm.ts │ │ │ ├── index.ts │ │ │ ├── shared │ │ │ │ ├── connect.ts │ │ │ │ ├── context.ts │ │ │ │ ├── index.ts │ │ │ │ └── render.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── reactive │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── benchmark.ts │ │ ├── docs │ │ │ ├── api │ │ │ │ ├── action.md │ │ │ │ ├── action.zh-CN.md │ │ │ │ ├── autorun.md │ │ │ │ ├── autorun.zh-CN.md │ │ │ │ ├── batch.md │ │ │ │ ├── batch.zh-CN.md │ │ │ │ ├── define.md │ │ │ │ ├── define.zh-CN.md │ │ │ │ ├── hasCollected.md │ │ │ │ ├── hasCollected.zh-CN.md │ │ │ │ ├── markObservable.md │ │ │ │ ├── markObservable.zh-CN.md │ │ │ │ ├── markRaw.md │ │ │ │ ├── markRaw.zh-CN.md │ │ │ │ ├── model.md │ │ │ │ ├── model.zh-CN.md │ │ │ │ ├── observable.md │ │ │ │ ├── observable.zh-CN.md │ │ │ │ ├── observe.md │ │ │ │ ├── observe.zh-CN.md │ │ │ │ ├── raw.md │ │ │ │ ├── raw.zh-CN.md │ │ │ │ ├── react │ │ │ │ │ ├── observer.md │ │ │ │ │ └── observer.zh-CN.md │ │ │ │ ├── reaction.md │ │ │ │ ├── reaction.zh-CN.md │ │ │ │ ├── toJS.md │ │ │ │ ├── toJS.zh-CN.md │ │ │ │ ├── tracker.md │ │ │ │ ├── tracker.zh-CN.md │ │ │ │ ├── typeChecker.md │ │ │ │ ├── typeChecker.zh-CN.md │ │ │ │ ├── untracked.md │ │ │ │ ├── untracked.zh-CN.md │ │ │ │ └── vue │ │ │ │ ├── observer.md │ │ │ │ └── observer.zh-CN.md │ │ │ ├── guide │ │ │ │ ├── best-practice.md │ │ │ │ ├── best-practice.zh-CN.md │ │ │ │ ├── concept.md │ │ │ │ ├── concept.zh-CN.md │ │ │ │ ├── index.md │ │ │ │ └── index.zh-CN.md │ │ │ ├── index.md │ │ │ └── index.zh-CN.md │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── action.spec.ts │ │ │ │ ├── annotations.spec.ts │ │ │ │ ├── array.spec.ts │ │ │ │ ├── autorun.spec.ts │ │ │ │ ├── batch.spec.ts │ │ │ │ ├── collections-map.spec.ts │ │ │ │ ├── collections-set.spec.ts │ │ │ │ ├── collections-weakmap.spec.ts │ │ │ │ ├── collections-weakset.spec.ts │ │ │ │ ├── define.spec.ts │ │ │ │ ├── externals.spec.ts │ │ │ │ ├── hasCollected.spec.ts │ │ │ │ ├── observable.spec.ts │ │ │ │ ├── observe.spec.ts │ │ │ │ ├── tracker.spec.ts │ │ │ │ └── untracked.spec.ts │ │ │ ├── action.ts │ │ │ ├── annotations │ │ │ │ ├── box.ts │ │ │ │ ├── computed.ts │ │ │ │ ├── index.ts │ │ │ │ ├── observable.ts │ │ │ │ ├── ref.ts │ │ │ │ └── shallow.ts │ │ │ ├── array.ts │ │ │ ├── autorun.ts │ │ │ ├── batch.ts │ │ │ ├── checkers.ts │ │ │ ├── environment.ts │ │ │ ├── externals.ts │ │ │ ├── global.d.ts │ │ │ ├── handlers.ts │ │ │ ├── index.ts │ │ │ ├── internals.ts │ │ │ ├── model.ts │ │ │ ├── observable.ts │ │ │ ├── observe.ts │ │ │ ├── reaction.ts │ │ │ ├── tracker.ts │ │ │ ├── tree.ts │ │ │ ├── types.ts │ │ │ └── untracked.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── reactive-react │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── hooks │ │ │ │ ├── index.ts │ │ │ │ ├── useCompatEffect.ts │ │ │ │ ├── useCompatFactory.ts │ │ │ │ ├── useDidUpdate.ts │ │ │ │ ├── useForceUpdate.ts │ │ │ │ ├── useLayoutEffect.ts │ │ │ │ └── useObserver.ts │ │ │ ├── index.ts │ │ │ ├── observer.ts │ │ │ ├── shared │ │ │ │ ├── gc.ts │ │ │ │ ├── global.ts │ │ │ │ ├── immediate.ts │ │ │ │ └── index.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── reactive-test-cases-for-react18 │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── src │ │ │ ├── index.js │ │ │ └── MySlowList.js │ │ ├── template.ejs │ │ ├── tsconfig.build.json │ │ ├── tsconfig.json │ │ ├── webpack.base.ts │ │ ├── webpack.dev.ts │ │ └── webpack.prod.ts │ ├── reactive-vue │ │ ├── .npmignore │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ └── observer.spec.ts │ │ │ ├── hooks │ │ │ │ ├── index.ts │ │ │ │ └── useObserver.ts │ │ │ ├── index.ts │ │ │ ├── observer │ │ │ │ ├── collectData.ts │ │ │ │ ├── index.ts │ │ │ │ ├── observerInVue2.ts │ │ │ │ └── observerInVue3.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── shared │ │ ├── .npmignore │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ └── index.spec.ts │ │ │ ├── array.ts │ │ │ ├── case.ts │ │ │ ├── checkers.ts │ │ │ ├── clone.ts │ │ │ ├── compare.ts │ │ │ ├── defaults.ts │ │ │ ├── deprecate.ts │ │ │ ├── global.ts │ │ │ ├── index.ts │ │ │ ├── instanceof.ts │ │ │ ├── isEmpty.ts │ │ │ ├── merge.ts │ │ │ ├── middleware.ts │ │ │ ├── path.ts │ │ │ ├── string.ts │ │ │ ├── subscribable.ts │ │ │ └── uid.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── validator │ │ ├── .npmignore │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── parser.spec.ts │ │ │ │ ├── registry.spec.ts │ │ │ │ └── validator.spec.ts │ │ │ ├── formats.ts │ │ │ ├── index.ts │ │ │ ├── locale.ts │ │ │ ├── parser.ts │ │ │ ├── registry.ts │ │ │ ├── rules.ts │ │ │ ├── template.ts │ │ │ ├── types.ts │ │ │ └── validator.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ └── vue │ ├── .npmignore │ ├── bin │ │ ├── formily-vue-fix.js │ │ └── formily-vue-switch.js │ ├── docs │ │ ├── .vuepress │ │ │ ├── components │ │ │ │ ├── createCodeSandBox.js │ │ │ │ ├── dumi-previewer.vue │ │ │ │ └── highlight.js │ │ │ ├── config.js │ │ │ ├── enhanceApp.js │ │ │ └── styles │ │ │ └── index.styl │ │ ├── api │ │ │ ├── components │ │ │ │ ├── array-field.md │ │ │ │ ├── expression-scope.md │ │ │ │ ├── field.md │ │ │ │ ├── form-consumer.md │ │ │ │ ├── form-provider.md │ │ │ │ ├── object-field.md │ │ │ │ ├── recursion-field-with-component.md │ │ │ │ ├── recursion-field.md │ │ │ │ ├── schema-field-with-schema.md │ │ │ │ ├── schema-field.md │ │ │ │ └── void-field.md │ │ │ ├── hooks │ │ │ │ ├── use-field-schema.md │ │ │ │ ├── use-field.md │ │ │ │ ├── use-form-effects.md │ │ │ │ ├── use-form.md │ │ │ │ └── use-parent-form.md │ │ │ └── shared │ │ │ ├── connect.md │ │ │ ├── injections.md │ │ │ ├── map-props.md │ │ │ ├── map-read-pretty.md │ │ │ ├── observer.md │ │ │ └── schema.md │ │ ├── demos │ │ │ ├── api │ │ │ │ ├── components │ │ │ │ │ ├── array-field.vue │ │ │ │ │ ├── expression-scope.vue │ │ │ │ │ ├── field.vue │ │ │ │ │ ├── form-consumer.vue │ │ │ │ │ ├── form-provider.vue │ │ │ │ │ ├── object-field.vue │ │ │ │ │ ├── recursion-field-with-component.vue │ │ │ │ │ ├── recursion-field.vue │ │ │ │ │ ├── schema-field-with-schema.vue │ │ │ │ │ ├── schema-field.vue │ │ │ │ │ └── void-field.vue │ │ │ │ ├── hooks │ │ │ │ │ ├── use-field-schema.vue │ │ │ │ │ ├── use-field.vue │ │ │ │ │ ├── use-form-effects.vue │ │ │ │ │ ├── use-form.vue │ │ │ │ │ └── use-parent-form.vue │ │ │ │ └── shared │ │ │ │ ├── connect.vue │ │ │ │ ├── map-props.vue │ │ │ │ ├── map-read-pretty.vue │ │ │ │ └── observer.vue │ │ │ ├── index.vue │ │ │ └── questions │ │ │ ├── default-slot.vue │ │ │ ├── events.vue │ │ │ ├── named-slot.vue │ │ │ └── scoped-slot.vue │ │ ├── guide │ │ │ ├── architecture.md │ │ │ ├── concept.md │ │ │ └── README.md │ │ ├── questions │ │ │ └── README.md │ │ └── README.md │ ├── package.json │ ├── README.md │ ├── rollup.config.js │ ├── scripts │ │ ├── postinstall.js │ │ ├── switch-cli.js │ │ └── utils.js │ ├── src │ │ ├── __tests__ │ │ │ ├── expression.scope.spec.ts │ │ │ ├── field.spec.ts │ │ │ ├── form.spec.ts │ │ │ ├── schema.json.spec.ts │ │ │ ├── schema.markup.spec.ts │ │ │ ├── shared.spec.ts │ │ │ └── utils.spec.ts │ │ ├── components │ │ │ ├── ArrayField.ts │ │ │ ├── ExpressionScope.ts │ │ │ ├── Field.ts │ │ │ ├── FormConsumer.ts │ │ │ ├── FormProvider.ts │ │ │ ├── index.ts │ │ │ ├── ObjectField.ts │ │ │ ├── ReactiveField.ts │ │ │ ├── RecursionField.ts │ │ │ ├── SchemaField.ts │ │ │ └── VoidField.ts │ │ ├── global.d.ts │ │ ├── hooks │ │ │ ├── index.ts │ │ │ ├── useAttach.ts │ │ │ ├── useField.ts │ │ │ ├── useFieldSchema.ts │ │ │ ├── useForm.ts │ │ │ ├── useFormEffects.ts │ │ │ ├── useInjectionCleaner.ts │ │ │ └── useParentForm.ts │ │ ├── index.ts │ │ ├── shared │ │ │ ├── connect.ts │ │ │ ├── context.ts │ │ │ ├── createForm.ts │ │ │ ├── fragment.ts │ │ │ ├── h.ts │ │ │ └── index.ts │ │ ├── types │ │ │ └── index.ts │ │ ├── utils │ │ │ ├── formatVNodeData.ts │ │ │ ├── getFieldProps.ts │ │ │ ├── getRawComponent.ts │ │ │ └── resolveSchemaProps.ts │ │ └── vue2-components.ts │ ├── tsconfig.build.json │ ├── tsconfig.json │ └── tsconfig.types.json ├── README.md ├── README.zh-cn.md ├── scripts │ ├── build-style │ │ ├── buildAllStyles.ts │ │ ├── copy.ts │ │ ├── helper.ts │ │ └── index.ts │ └── rollup.base.js ├── tsconfig.build.json ├── tsconfig.jest.json ├── tsconfig.json └── yarn.lock ``` # Files -------------------------------------------------------------------------------- /packages/next/docs/components/TreeSelect.md: -------------------------------------------------------------------------------- ```markdown # TreeSelect > Tree selector ## Markup Schema synchronization data source case ```tsx import React from 'react' import { TreeSelect, FormItem, FormButtonGroup, Submit } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { TreeSelect, FormItem, }, }) const form = createForm() export default () => ( <FormProvider form={form}> <SchemaField> <SchemaField.Number name="select" label="select box" x-decorator="FormItem" x-component="TreeSelect" enum={[ { label: 'Option 1', value: 1, children: [ { label: 'Child Node1', value: '0-0-0', key: '0-0-0', }, { label: 'Child Node2', value: '0-0-1', key: '0-0-1', }, { label: 'Child Node3', value: '0-0-2', key: '0-0-2', }, ], }, { label: 'Option 2', value: 2, children: [ { label: 'Child Node3', value: '0-1-0', key: '0-1-0', }, { label: 'Child Node4', value: '0-1-1', key: '0-1-1', }, { label: 'Child Node5', value: '0-1-2', key: '0-1-2', }, ], }, ]} x-component-props={{ style: { width: 200, }, }} /> </SchemaField> <FormButtonGroup> <Submit onSubmit={console.log}>Submit</Submit> </FormButtonGroup> </FormProvider> ) ``` ## Markup Schema Asynchronous Linkage Data Source Case ```tsx import React from 'react' import { TreeSelect, Select, FormItem, FormButtonGroup, Submit, } from '@formily/next' import { createForm, onFieldReact, FormPathPattern, Field } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' import { action } from '@formily/reactive' const SchemaField = createSchemaField({ components: { Select, TreeSelect, FormItem, }, }) const useAsyncDataSource = ( pattern: FormPathPattern, service: (field: Field) => Promise<{ label: string; value: any }[]> ) => { onFieldReact(pattern, (field) => { field.loading = true service(field).then( action.bound((data) => { field.dataSource = data field.loading = false }) ) }) } const form = createForm({ effects: () => { useAsyncDataSource('select', async (field) => { const linkage = field.query('linkage').get('value') if (!linkage) return [] return new Promise((resolve) => { setTimeout(() => { if (linkage === 1) { resolve([ { label: 'AAA', value: 'aaa', children: [ { label: 'Child Node1', value: '0-0-0', key: '0-0-0', }, { label: 'Child Node2', value: '0-0-1', key: '0-0-1', }, { label: 'Child Node3', value: '0-0-2', key: '0-0-2', }, ], }, { label: 'BBB', value: 'ccc', children: [ { label: 'Child Node1', value: '0-1-0', key: '0-1-0', }, { label: 'Child Node2', value: '0-1-1', key: '0-1-1', }, { label: 'Child Node3', value: '0-1-2', key: '0-1-2', }, ], }, ]) } else if (linkage === 2) { resolve([ { label: 'CCC', value: 'ccc', children: [ { label: 'Child Node1', value: '0-0-0', key: '0-0-0', }, { label: 'Child Node2', value: '0-0-1', key: '0-0-1', }, { label: 'Child Node3', value: '0-0-2', key: '0-0-2', }, ], }, { label: 'DDD', value: 'ddd', children: [ { label: 'Child Node1', value: '0-1-0', key: '0-1-0', }, { label: 'Child Node2', value: '0-1-1', key: '0-1-1', }, { label: 'Child Node3', value: '0-1-2', key: '0-1-2', }, ], }, ]) } }, 1500) }) }) }, }) export default () => ( <FormProvider form={form}> <SchemaField> <SchemaField.Number name="linkage" label="Linkage selection box" x-decorator="FormItem" x-component="Select" enum={[ { label: 'Request 1', value: 1 }, { label: 'Request 2', value: 2 }, ]} x-component-props={{ style: { width: 200, }, }} /> <SchemaField.String name="select" label="Asynchronous selection box" x-decorator="FormItem" x-component="TreeSelect" x-component-props={{ style: { width: 200, }, }} /> </SchemaField> <FormButtonGroup> <Submit onSubmit={console.log}>Submit</Submit> </FormButtonGroup> </FormProvider> ) ``` ## JSON Schema synchronization data source case ```tsx import React from 'react' import { TreeSelect, FormItem, FormButtonGroup, Submit } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { TreeSelect, FormItem, }, }) const form = createForm() const schema = { type: 'object', properties: { select: { type: 'string', label: 'Select box', 'x-decorator': 'FormItem', 'x-component': 'TreeSelect', enum: [ { label: 'Option 1', value: 1, children: [ { label: 'Child Node1', value: '0-0-0', key: '0-0-0', }, { label: 'Child Node2', value: '0-0-1', key: '0-0-1', }, { label: 'Child Node3', value: '0-0-2', key: '0-0-2', }, ], }, { label: 'Option 2', value: 2, children: [ { label: 'Child Node1', value: '0-1-0', key: '0-1-0', }, { label: 'Child Node2', value: '0-1-1', key: '0-1-1', }, { label: 'Child Node3', value: '0-1-2', key: '0-1-2', }, ], }, ], 'x-component-props': { style: { width: 200, }, }, }, }, } export default () => ( <FormProvider form={form}> <SchemaField schema={schema} /> <FormButtonGroup> <Submit onSubmit={console.log}>Submit</Submit> </FormButtonGroup> </FormProvider> ) ``` ## JSON Schema asynchronous linkage data source case ```tsx import React from 'react' import { TreeSelect, Select, FormItem, FormButtonGroup, Submit, } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' import { action } from '@formily/reactive' const SchemaField = createSchemaField({ components: { Select, TreeSelect, FormItem, }, }) const loadData = async (field) => { const linkage = field.query('linkage').get('value') if (!linkage) return [] return new Promise((resolve) => { setTimeout(() => { if (linkage === 1) { resolve([ { label: 'AAA', value: 'aaa', children: [ { label: 'Child Node1', value: '0-0-0', key: '0-0-0', }, { label: 'Child Node2', value: '0-0-1', key: '0-0-1', }, { label: 'Child Node3', value: '0-0-2', key: '0-0-2', }, ], }, { label: 'BBB', value: 'ccc', children: [ { label: 'Child Node1', value: '0-1-0', key: '0-1-0', }, { label: 'Child Node2', value: '0-1-1', key: '0-1-1', }, { label: 'Child Node3', value: '0-1-2', key: '0-1-2', }, ], }, ]) } else if (linkage === 2) { resolve([ { label: 'CCC', value: 'ccc', children: [ { label: 'Child Node1', value: '0-0-0', key: '0-0-0', }, { label: 'Child Node2', value: '0-0-1', key: '0-0-1', }, { label: 'Child Node3', value: '0-0-2', key: '0-0-2', }, ], }, { label: 'DDD', value: 'ddd', children: [ { label: 'Child Node1', value: '0-1-0', key: '0-1-0', }, { label: 'Child Node2', value: '0-1-1', key: '0-1-1', }, { label: 'Child Node3', value: '0-1-2', key: '0-1-2', }, ], }, ]) } }, 1500) }) } const useAsyncDataSource = (service) => (field) => { field.loading = true service(field).then( action.bound((data) => { field.dataSource = data field.loading = false }) ) } const form = createForm() const schema = { type: 'object', properties: { linkage: { type: 'string', label: 'Linkage selection box', enum: [ { label: 'Request 1', value: 1 }, { label: 'Request 2', value: 2 }, ], 'x-decorator': 'FormItem', 'x-component': 'Select', 'x-component-props': { style: { width: 200, }, }, }, select: { type: 'string', label: 'Asynchronous selection box', 'x-decorator': 'FormItem', 'x-component': 'TreeSelect', 'x-component-props': { style: { width: 200, }, }, 'x-reactions': ['{{useAsyncDataSource(loadData)}}'], }, }, } export default () => ( <FormProvider form={form}> <SchemaField schema={schema} scope={{ useAsyncDataSource, loadData }} /> <FormButtonGroup> <Submit onSubmit={console.log}>Submit</Submit> </FormButtonGroup> </FormProvider> ) ``` ## Pure JSX synchronization data source case ```tsx import React from 'react' import { TreeSelect, FormItem, FormButtonGroup, Submit } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, Field } from '@formily/react' const form = createForm() export default () => ( <FormProvider form={form}> <Field name="select" label="select box" dataSource={[ { label: 'Option 1', value: 1, children: [ { label: 'Child Node1', value: '0-0-0', key: '0-0-0', }, { label: 'Child Node2', value: '0-0-1', key: '0-0-1', }, { label: 'Child Node3', value: '0-0-2', key: '0-0-2', }, ], }, { label: 'Option 2', value: 2, children: [ { label: 'Child Node3', value: '0-1-0', key: '0-1-0', }, { label: 'Child Node4', value: '0-1-1', key: '0-1-1', }, { label: 'Child Node5', value: '0-1-2', key: '0-1-2', }, ], }, ]} decorator={[FormItem]} component={[ TreeSelect, { style: { width: 200, }, }, ]} /> <FormButtonGroup> <Submit onSubmit={console.log}>Submit</Submit> </FormButtonGroup> </FormProvider> ) ``` ## Pure JSX asynchronous linkage data source case ```tsx import React from 'react' import { TreeSelect, Select, FormItem, FormButtonGroup, Submit, } from '@formily/next' import { createForm, onFieldReact, FormPathPattern, FieldType, } from '@formily/core' import { FormProvider, Field } from '@formily/react' import { action } from '@formily/reactive' const useAsyncDataSource = ( pattern: FormPathPattern, service: (field: FieldType) => Promise<{ label: string; value: any }[]> ) => { onFieldReact(pattern, (field) => { field.loading = true service(field).then( action.bound((data) => { field.dataSource = data field.loading = false }) ) }) } const form = createForm({ effects: () => { useAsyncDataSource('select', async (field) => { const linkage = field.query('linkage').get('value') if (!linkage) return [] return new Promise((resolve) => { setTimeout(() => { if (linkage === 1) { resolve([ { label: 'AAA', value: 'aaa', children: [ { label: 'Child Node1', value: '0-0-0', key: '0-0-0', }, { label: 'Child Node2', value: '0-0-1', key: '0-0-1', }, { label: 'Child Node3', value: '0-0-2', key: '0-0-2', }, ], }, { label: 'BBB', value: 'ccc', children: [ { label: 'Child Node1', value: '0-1-0', key: '0-1-0', }, { label: 'Child Node2', value: '0-1-1', key: '0-1-1', }, { label: 'Child Node3', value: '0-1-2', key: '0-1-2', }, ], }, ]) } else if (linkage === 2) { resolve([ { label: 'CCC', value: 'ccc', children: [ { label: 'Child Node1', value: '0-0-0', key: '0-0-0', }, { label: 'Child Node2', value: '0-0-1', key: '0-0-1', }, { label: 'Child Node3', value: '0-0-2', key: '0-0-2', }, ], }, { label: 'DDD', value: 'ddd', children: [ { label: 'Child Node1', value: '0-1-0', key: '0-1-0', }, { label: 'Child Node2', value: '0-1-1', key: '0-1-1', }, { label: 'Child Node3', value: '0-1-2', key: '0-1-2', }, ], }, ]) } }, 1500) }) }) }, }) export default () => ( <FormProvider form={form}> <Field name="linkage" label="Linkage selection box" dataSource={[ { label: 'Request 1', value: 1 }, { label: 'Request 2', value: 2 }, ]} decorator={[FormItem]} component={[ Select, { style: { width: 200, }, }, ]} /> <Field name="select" label="Asynchronous selection box" decorator={[FormItem]} component={[ TreeSelect, { style: { width: 200, }, }, ]} /> <FormButtonGroup> <Submit onSubmit={console.log}>Submit</Submit> </FormButtonGroup> </FormProvider> ) ``` ## API Reference https://fusion.design/pc/component/basic/tree-select ``` -------------------------------------------------------------------------------- /packages/core/docs/api/entry/FormPath.md: -------------------------------------------------------------------------------- ```markdown --- order: 5 --- # FormPath The core of FormPath in Formily is to solve 2 types of problems: - Path matching problem - Data manipulation issues Path matching requires that the given path must be a valid path matching syntax, such as `*(aa,bb,cc)`. Data operation requires that the given path must be a legal data operation path, that is, it must be in the form of `a.b.c` and cannot carry `*` ## Constructor ```ts class FormPath { constructor(pattern: FormPathPattern, base?: FormPathPattern) } ``` ## Attributes | Property | Description | Type | Default Value | | ------------------ | ------------------------------------------------------------------------------- | ------------------------- | ------------- | | length | If the path is a non-matching path, the length of the path can be read | Number | `0` | | entire | Path complete string, consistent with the input parameter data | String | | | segments | If the path is a non-matching path, you can read the complete path segmentation | `Array<String \| Number>` | `[]` | | isMatchPattern | Is the path a matching path | Boolean | | | isWildMatchPattern | Is the path a fully wildcarded path, such as `a.b.*` | Boolean | | | haveExcludePattern | Does the path have reverse matching, such as `*(!a.b.c)` | Boolean | | | tree | Parsed AST tree | Node | | ## FormPathPattern ### Signature ```ts type FormPathPattern = string | number | Array<string | number> | RegExp ``` ### Data path syntax #### Point path **description** It is our most commonly used `a.b.c` format, which uses dot notation to divide each path node, mainly used to read and write data **Example** ```ts import { FormPath } from '@formily/core' const target = {} FormPath.setIn(target, 'a.b.c', 'value') console.log(FormPath.getIn(target, 'a.b.c')) //'value' console.log(target) //{a:{b:{c:'value'}}} ``` #### Subscript path For array paths, there will be subscripts. Our subscripts can use dot syntax or square brackets. ```ts import { FormPath } from '@formily/core' const target = { array: [], } FormPath.setIn(target, 'array.0.aa', '000') console.log(FormPath.getIn(target, 'array.0.aa')) //000 console.log(target) //{array:[{aa:'000'}]} FormPath.setIn(target, 'array[1].aa', '111') console.log(FormPath.getIn(target, 'array.1.aa')) //111 console.log(target) //{array:[{aa:'000'},{aa:'111'}]} ``` #### Deconstruction expression The deconstruction expression is similar to the ES6 deconstruction grammar, except that it does not support `...` deconstruction. It is very suitable for scenarios where the front and back data is inconsistent. It has several characteristics: - The deconstruction expression will be regarded as a node of the point path, we can regard it as a normal string node, but it will take effect during data manipulation, so only the deconstruction expression needs to be matched as a normal node node in the matching grammar Can - Use the deconstruction path in setIn, the data will be deconstructed - Use the deconstruction path in getIn, the data will be reorganized ```ts import { FormPath } from '@formily/core' const target = {} FormPath.setIn(target, 'parent.[aa,bb]', [11, 22]) console.log(target) //{parent:{aa:11,bb:22}} console.log(FormPath.getIn(target, 'parent.[aa,bb]')) //[11,22] console.log(FormPath.parse('parent.[aa,bb]').toString()) //parent.[aa,bb] ``` #### relative path The relative path syntax is mainly expressed in dot syntax at the head of the data type path. It is very useful for calculating adjacent elements of the array. It has several characteristics: - A dot represents the current path - n dots represent n-1 steps forward - Subscripts can be used to calculate expressions in square brackets: `[+]` represents the current subscript +1, `[-]` represents the current subscript - 1, `[+n]` represents the current subscript +n, ` [-n]` represents the current subscript - n - When path matching, group matching and range matching cannot be used, such as `*(..[+1].aa,..[+2].bb)` ```ts import { FormPath } from '@formily/core' console.log(FormPath.parse('.dd', 'aa.bb.cc').toString()) //aa.bb.dd console.log(FormPath.parse('..[].dd', 'aa.1.cc').toString()) //aa.1.dd console.log(FormPath.parse('..[+].dd', 'aa.1.cc').toString()) //aa.2.dd console.log(FormPath.parse('..[+10].dd', 'aa.1.cc').toString()) //aa.11.dd ``` ### Match path syntax #### Full match Full match is equivalent to matching all paths, only a `*` identification is required ```ts import { FormPath } from '@formily/core' console.log(FormPath.parse('*').match('aa')) //true console.log(FormPath.parse('*').match('aa.bb')) //true console.log(FormPath.parse('*').match('cc')) //true ``` #### Partial match Local matching is equivalent to matching all paths of a node position, and also only needs to use a `*` mark ```ts import { FormPath } from '@formily/core' console.log(FormPath.parse('aa.*.cc').match('aa.bb.cc')) //true console.log(FormPath.parse('aa.*.cc').match('aa.kk.cc')) //true console.log(FormPath.parse('aa.*.cc').match('aa.dd.cc')) //true ``` #### Group Match Grouped matching can match multiple paths, and also supports nesting, syntax: `*(pattern1,pattern2,pattern3...)` ```ts import { FormPath } from '@formily/core' console.log( FormPath.parse('aa.*(bb,kk,dd,ee.*(oo,gg).gg).cc').match('aa.bb.cc') ) //true console.log( FormPath.parse('aa.*(bb,kk,dd,ee.*(oo,gg).gg).cc').match('aa.kk.cc') ) //true console.log( FormPath.parse('aa.*(bb,kk,dd,ee.*(oo,gg).gg).cc').match('aa.dd.cc') ) //true console.log( FormPath.parse('aa.*(bb,kk,dd,ee.*(oo,gg).gg).cc').match('aa.ee.oo.gg.cc') ) //true console.log( FormPath.parse('aa.*(bb,kk,dd,ee.*(oo,gg).gg).cc').match('aa.ee.gg.gg.cc') ) //true ``` #### Reverse match Reverse matching is mainly used to exclude the specified path, syntax: `*(!pattern1,pattern2,pattern3)` ```ts import { FormPath } from '@formily/core' console.log(FormPath.parse('*(!aa,bb,cc)').match('aa')) //false console.log(FormPath.parse('*(!aa,bb,cc)').match('kk')) //true ``` #### Extended matching Extended matching is mainly used to match the starting substring of the path, syntax: `pattern~` ```ts import { FormPath } from '@formily/core' console.log(FormPath.parse('test~').match('test_111')) //true console.log(FormPath.parse('test~').match('test_222')) //true ``` #### Range match Range matching is mainly used to match the array index range, syntax: `*[x:y]`, x and y can be empty, representing open range matching ```ts import { FormPath } from '@formily/core' console.log(FormPath.parse('aa.*[1:2].bb').match('aa.1.bb')) //true console.log(FormPath.parse('aa.*[1:2].bb').match('aa.2.bb')) //true console.log(FormPath.parse('aa.*[1:2].bb').match('aa.3.bb')) //false console.log(FormPath.parse('aa.*[1:].bb').match('aa.3.bb')) //true console.log(FormPath.parse('aa.*[:100].bb').match('aa.3.bb')) //true console.log(FormPath.parse('aa.*[:100].bb').match('aa.1000.bb')) //false ``` #### Escape match For path nodes that contain keywords, we can use escape syntax matching, the syntax is `\\` or `[[]]` ```ts import { FormPath } from '@formily/core' console.log( FormPath.parse('aa.\\,\\*\\{\\}\\.\\(\\).bb').match( 'aa.\\,\\*\\{\\}\\.\\(\\).bb' ) ) //true console.log(FormPath.parse('aa.[[,*{}.()]].bb').match('aa.[[,*{}.()]].bb')) // true ``` #### Destructuring matching For the path with deconstruction expression, if we match, we can directly match without escaping ```ts import { FormPath } from '@formily/core' console.log(FormPath.parse('target.[aa,bb]').match('target.[aa,bb]')) //true ``` ## Method ### toString #### Description The complete string of the output path, supporting matching paths and data manipulation paths #### Signature ```ts interface toString { (): string } ``` #### Example ```ts import { FormPath } from '@formily/core' console.log(FormPath.parse('aa.bb.cc').toString()) //aa.bb.cc console.log(FormPath.parse('aa.bb.*').toString()) //aa.bb.* console.log(FormPath.parse('*(aa,bb,cc)').toString()) //*(aa,bb,cc) ``` ### toArray #### Description Array fragment of output path, only supports data manipulation path #### Signature ```ts interface toArray { (): Array<string | number> } ``` #### Example ```ts import { FormPath } from '@formily/core' console.log(FormPath.parse('aa.bb.cc').toArray().join('--')) //aa-bb-cc console.log(FormPath.parse('aa.bb.*').toArray()) //[] console.log(FormPath.parse('*(aa,bb,cc)').toArray()) //[] ``` ### concat #### Description Connection data operation path #### Signature ```ts interface concat { (...args: FormPathPattern[]): FormPath } ``` #### Example ```ts import { FormPath } from '@formily/core' console.log(FormPath.parse('aa.bb.cc').concat('dd.ee.mm').toString()) //aa.bb.cc.dd.ee.mm console.log( FormPath.parse('aa.bb.cc').concat(['dd', 'ee', 'mm'], 'kk.oo').toString() ) //aa.bb.cc.dd.ee.mm.kk.oo ``` ### slice #### Description Select a segment of the data operation path #### Signature ```ts interface slice { (start?: number, end?: number): FormPath } ``` #### Example ```ts import { FormPath } from '@formily/core' console.log(FormPath.parse('aa.bb.cc').slice(1).toString()) //bb.cc ``` ### push #### Description Push a fragment path to the data operation path #### Signature ```ts interface push { (...args: FormPathPattern[]): FormPath } ``` #### Example ```ts import { FormPath } from '@formily/core' console.log(FormPath.parse('aa.bb.cc').push('dd.kk').toString()) //aa.bb.cc.dd.kk ``` ### pop #### Description Pop the last key from the data operation path #### Signature ```ts interface pop { (): FormPath } ``` #### Example ```ts import { FormPath } from '@formily/core' console.log(FormPath.parse('aa.bb.cc').pop().toString()) //aa.bb ``` ### splice #### Description Splice the data operation path #### Signature ```ts interface splice { ( startIndex: number, deleteCount?: number, ...inertItems: Array<string | number> ): FormPath } ``` #### Example ```ts import { FormPath } from '@formily/core' console.log(FormPath.parse('aa.bb.cc').splice(2, 1).toString()) //aa.bb console.log(FormPath.parse('aa.bb.cc').splice(2, 0, 'ee.gg').toString()) //aa.bb.ee.gg.cc console.log(FormPath.parse('aa.bb.cc').splice(2, 0, ['kk', 'mm']).toString()) //aa.bb.kk.mm.cc ``` ### forEach #### Description Traverse the data operation path #### Signature ```ts interface forEach { (eacher: (key: string | number, index: number) => void): void } ``` #### Example ```ts import { FormPath } from '@formily/core' const keys = [] FormPath.parse('aa.bb.cc').forEach((key) => { keys.push(key) }) console.log(keys) //['aa','bb','cc'] ``` ### map #### Description Loop mapping data operation path #### Signature ```ts interface map { (mapper: (key: string | number, index: number) => void): void } ``` #### Example ```ts import { FormPath } from '@formily/core' console.log( FormPath.parse('aa.bb.cc').map((key) => { return key + '~' }) //['aa~','bb~','cc~'] ) ``` ### reduce #### Description The reduce method executes a reducer function (executed in ascending order) provided by you on each element in the path, and aggregates the results into a single return value. #### Signature ```ts interface reduce<T> { (reducer: (value: T, key: string | number, index: number) => void): void accumulator: T } ``` #### Example ```ts import { FormPath } from '@formily/core' console.log( FormPath.parse('aa.bb.cc').reduce((count) => { return count + 1 }, 0) ) //3 ``` ### parent #### Description Get the parent path of the current data operation path #### Signature ```ts interface parent { (): FormPath } ``` #### Example ```ts import { FormPath } from '@formily/core' console.log(FormPath.parse('aa.bb.cc').parent().toString()) //aa.bb ``` ### includes #### Description Used to determine whether a given data operation path is a subpath of the current data operation path #### Signature ```ts interface includes { (pattern: FormPathPattern): boolean } ``` #### Example ```ts import { FormPath } from '@formily/core' console.log(FormPath.parse('aa.bb.cc').includes('aa.bb')) //true console.log(FormPath.parse('aa.bb.cc').includes('cc.bb')) //false ``` ### transform #### Description Based on regular extraction data to do path assembly #### Signature ```ts interface transform<T> { (regExp: RegExp, callback: (...matches: string[]) => T): T } ``` #### Example ```ts import { FormPath } from '@formily/core' console.log( FormPath.parse('aa.1.cc').transform( /\d+/, (index) => `aa.${parseInt(index) + 1}.cc` ) ) //aa.2.cc ``` ### match #### Description Use path matching syntax to match the current path #### Signature ```ts interface match { (pattern: FormPathPattern): boolean } ``` #### Example ```ts import { FormPath } from '@formily/core' console.log(FormPath.parse('aa.1.cc').match('aa.*.cc')) //true ``` ### matchAliasGroup #### Description Alias group matching, mainly used to match address and path in formily #### Signature ```ts interface matchAliasGroup { (pattern: FormPathPattern, alias: FormPathPattern): boolean } ``` #### Example ```ts import { FormPath } from '@formily/core' console.log(FormPath.parse('aa.bb.cc').matchAliasGroup('aa.bb.cc', 'aa.cc')) //true ``` ### existIn #### Description Determine whether the specified data exists based on the current path #### Signature ```ts interface existIn { (pattern: FormPathPattern): boolean } ``` #### Example ```ts import { FormPath } from '@formily/core' console.log(FormPath.parse('aa.bb.cc').existIn({})) //false ``` ### getIn #### Description Obtain the specified data based on the current path #### Signature ```ts interface getIn { (pattern: FormPathPattern): any } ``` #### Example ```ts import { FormPath } from '@formily/core' console.log(FormPath.parse('aa.bb.cc').getIn({ aa: { bb: { cc: 'value' } } })) //value ``` ### setIn #### Description Update the specified data based on the current path #### Signature ```ts interface setIn { (pattern: FormPathPattern, value: any): void } ``` #### Example ```ts import { FormPath } from '@formily/core' const target = {} FormPath.parse('aa.bb.cc').setIn(target, 'value') console.log(FormPath.parse('aa.bb.cc').getIn(target)) //value ``` ### deleteIn #### Description Delete the specified data based on the current path #### Signature ```ts interface deleteIn { (pattern: FormPathPattern): boolean } ``` #### Example ```ts import { FormPath } from '@formily/core' const target = { aa: { bb: { cc: 'value', }, }, } FormPath.parse('aa.bb.cc').deleteIn(target) console.log(FormPath.parse('aa.bb.cc').getIn(target)) //undefined ``` ### ensureIn #### Description Ensure that there must be data under a certain path, if not, create data #### Signature ```ts interface ensureIn { (pattern: FormPathPattern, value: any): any } ``` #### Example ```ts import { FormPath } from '@formily/core' const target = {} FormPath.parse('aa.bb.cc').ensureIn(target, 'value') console.log(FormPath.parse('aa.bb.cc').getIn(target)) //value ``` ## Static method ### match #### Description Generate a path matcher based on matching paths #### Signature ```ts interface match { (pattern: FormPathPattern): (pattern: FormPathPattern) => boolean } ``` #### Example ```ts import { FormPath } from '@formily/core' console.log(FormPath.match('aa.*.cc')('aa.bb.cc')) // true ``` ### transform #### Description Regular conversion path #### Signature ```ts interface transform<T> { ( pattern: FormPathPattern, regexp: RegExp, callback: (...matches: string[]) => T ): T } ``` #### Example ```ts import { FormPath } from '@formily/core' console.log( FormPath.transform( 'aa.0.bb', /\d+/, (index) => `aa.${parseInt(index) + 1}.bb` ) ) // `aa.1.bb` ``` ### parse #### Description Resolve path #### Signature ```ts interface parse { (pattern: FormPathPattern): FormPath } ``` #### Example ```ts import { FormPath } from '@formily/core' console.log(FormPath.parse('aa.0.bb')) ``` ### getIn Get data based on path #### Signature ```ts interface getIn { (target: any, pattern: FormPathPattern): any } ``` #### Example ```ts import { FormPath } from '@formily/core' console.log(FormPath.getIn({ aa: [{ bb: 'value' }] }, 'aa.0.bb')) ``` ### setIn Update data based on path #### Signature ```ts interface setIn { (target: any, pattern: FormPathPattern, value: any): void } ``` #### Example ```ts import { FormPath } from '@formily/core' const target = {} FormPath.setIn(target, 'aa.bb.cc', 'value') console.log(target) //{aa:{bb:{cc:'value'}}} ``` ### deleteIn Delete data based on path #### Signature ```ts interface deleteIn { (target: any, pattern: FormPathPattern): void } ``` #### Example ```ts import { FormPath } from '@formily/core' const target = { aa: { bb: { cc: 'value', }, }, } FormPath.deleteIn(target, 'aa.bb.cc') console.log(target) //{aa:{bb:{}}} ``` ### existIn #### Description Determine whether there is data in the specified path #### Signature ```ts interface existIn { (target: any, pattern: FormPathPattern): void } ``` #### Example ```ts import { FormPath } from '@formily/core' const target = { aa: { bb: { cc: 'value', }, }, } console.log(FormPath.existIn(target, 'aa.bb.cc')) //true console.log(FormPath.existIn(target, 'aa.bb.kk')) //false ``` ### ensureIn #### Description Ensure that there must be data under a certain path, if not, create data #### Signature ```ts interface ensureIn { (target: any, pattern: FormPathPattern, defaultValue: any): any } ``` #### Example ```ts import { FormPath } from '@formily/core' const target = {} FormPath.ensureIn(target, 'aa.bb.cc', 'value') console.log(FormPath.getIn(target, 'aa.bb.cc')) //value ``` ``` -------------------------------------------------------------------------------- /packages/antd/src/form-item/style.less: -------------------------------------------------------------------------------- ``` @root-entry-name: 'default'; @import (reference) '~antd/es/style/themes/index.less'; @import (reference) '~antd/lib/input/style/mixin.less'; @import './grid.less'; @import './animation.less'; @form-item-cls: ~'@{ant-prefix}-formily-item'; .@{form-item-cls} { display: flex; margin-bottom: @form-item-margin-bottom - 2; position: relative; font-size: @font-size-base; &-label { line-height: @height-base; min-height: @height-base - 2; label { cursor: text; } } textarea.@{ant-prefix}-input { height: auto; } // input[type=file] .@{ant-prefix}-upload { background: transparent; } .@{ant-prefix}-upload.@{ant-prefix}-upload-drag { background: @background-color-light; } input[type='radio'], input[type='checkbox'] { width: @font-size-base; height: @font-size-base; } // Radios and checkboxes on same line .@{ant-prefix}-radio-inline, .@{ant-prefix}-checkbox-inline { display: inline-block; margin-left: 8px; font-weight: normal; vertical-align: middle; cursor: pointer; &:first-child { margin-left: 0; } } .@{ant-prefix}-checkbox-vertical, .@{ant-prefix}-radio-vertical { display: block; } .@{ant-prefix}-checkbox-vertical + .@{ant-prefix}-checkbox-vertical, .@{ant-prefix}-radio-vertical + .@{ant-prefix}-radio-vertical { margin-left: 0; } .@{ant-prefix}-input-number { width: 100%; + .@{ant-prefix}-form-text { margin-left: 8px; } &-handler-wrap { z-index: 2; // https://github.com/ant-design/ant-design/issues/6289 } } .@{ant-prefix}-select, .@{ant-prefix}-cascader-picker, .@{ant-prefix}-picker { width: 100%; } // Don't impact select inside input group .@{ant-prefix}-input-group .@{ant-prefix}-select, .@{ant-prefix}-input-group .@{ant-prefix}-cascader-picker { width: auto; } } .@{form-item-cls}-label { position: relative; display: flex; &-content { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } &-tooltip { cursor: help; * { cursor: help; } label { border-bottom: 1px dashed currentColor; } } } .@{form-item-cls}-label { color: @heading-color; } .@{form-item-cls}-label-align-left { > .@{form-item-cls}-label { justify-content: flex-start; } } .@{form-item-cls}-label-align-right { > .@{form-item-cls}-label { justify-content: flex-end; } } .@{form-item-cls}-label-wrap { .@{form-item-cls}-label { label { white-space: pre-line; word-break: break-all; } } } .@{form-item-cls}-feedback-layout-terse { margin-bottom: 8px; &.@{form-item-cls}-feedback-has-text:not(.@{form-item-cls}-inset) { margin-bottom: 0; } } .@{form-item-cls}-feedback-layout-loose { margin-bottom: 22px; &.@{form-item-cls}-feedback-has-text:not(.@{form-item-cls}-inset) { margin-bottom: 0; } } .@{form-item-cls}-feedback-layout-none { margin-bottom: 0px; &.@{form-item-cls}-feedback-has-text:not(.@{form-item-cls}-inset) { margin-bottom: 0; } } .@{form-item-cls}-control { flex: 1; max-width: 100%; .@{form-item-cls}-control-content { display: flex; .@{form-item-cls}-control-content-component { width: 100%; min-height: @height-base - 2; line-height: @height-base + 2; &-has-feedback-icon { flex: 1; position: relative; display: flex; align-items: center; } } .@{form-item-cls}-addon-before { margin-right: 8px; display: inline-flex; align-items: center; min-height: @height-base; flex-shrink: 0; } .@{form-item-cls}-addon-after { margin-left: 8px; display: inline-flex; align-items: center; min-height: @height-base; flex-shrink: 0; } } .@{form-item-cls}-help, .@{form-item-cls}-extra { min-height: 22px; line-height: 22px; color: @text-color-secondary; } } .@{form-item-cls}-size-small { font-size: @font-size-sm; line-height: @height-sm; .@{form-item-cls}-label { line-height: @height-sm; min-height: @height-sm - 2; } .@{form-item-cls}-control-content { .@{form-item-cls}-control-content-component { min-height: @height-sm - 2; line-height: @height-sm + 2; } } .@{form-item-cls}-help, .@{form-item-cls}-extra { min-height: @height-sm - 4; line-height: @height-sm - 4; } .@{form-item-cls}-control-content { min-height: @height-sm - 2; } .@{form-item-cls}-label > label { height: @height-sm - 2; } .@{ant-prefix}-input-affix-wrapper, .@{ant-prefix}-input-number, .@{ant-prefix}-picker { padding: 0px 11px; input { height: @height-sm - 2; font-size: @font-size-sm; } } .@{ant-prefix}-cascader-picker { height: @height-sm - 2; input { padding: 0 7px; height: @height-sm - 2; font-size: @font-size-sm; } } .@{ant-prefix}-select-single:not(.@{ant-prefix}-select-customize-input) .@{ant-prefix}-select-selector { padding: 0px 11px; height: @height-sm - 2; font-size: @font-size-sm; line-height: @height-sm; .@{ant-prefix}-select-selection-search { height: @height-sm; line-height: @height-sm - 2; &-input { height: @height-sm; line-height: @height-sm - 2; } } .@{ant-prefix}-select-selection-placeholder { line-height: @height-sm - 2; height: @height-sm; } .@{ant-prefix}-select-selection-item { line-height: @height-sm - 2; height: @height-sm; } } .@{ant-prefix}-select-multiple:not(.@{ant-prefix}-select-customize-input) .@{ant-prefix}-select-selector { padding: 0px 2px; height: @height-sm - 2; font-size: @font-size-sm; line-height: @height-sm; &::after { height: @height-sm - 8; line-height: @height-sm - 8; } .@{ant-prefix}-select-selection-search { height: @height-sm - 8; line-height: @height-sm - 8; margin-inline-start: 0; &-input { height: @height-sm - 12; line-height: @height-sm - 12; } } .@{ant-prefix}-select-selection-placeholder { line-height: @height-sm - 8; height: @height-sm - 8; left: 4px; } .@{ant-prefix}-select-selection-overflow-item { align-self: flex-start; } .@{ant-prefix}-select-selection-item { line-height: @height-sm - 10; height: @height-sm - 8; } } &.@{form-item-cls}-feedback-layout-terse { margin-bottom: 8px; &.@{form-item-cls}-feedback-has-text:not(.@{form-item-cls}-inset) { margin-bottom: 0; } } &.@{form-item-cls}-feedback-layout-loose { margin-bottom: @height-sm - 4; &.@{form-item-cls}-feedback-has-text:not(.@{form-item-cls}-inset) { margin-bottom: 0; } } } .@{form-item-cls}-size-large { font-size: @font-size-lg; line-height: @height-lg; .@{form-item-cls}-label { line-height: @height-lg; min-height: @height-lg - 2; } .@{form-item-cls}-control-content { .@{form-item-cls}-control-content-component { min-height: @height-lg - 2; line-height: @height-lg; } } .@{form-item-cls}-help, .@{form-item-cls}-extra { min-height: @form-item-margin-bottom; line-height: @form-item-margin-bottom; } .@{form-item-cls}-control-content { min-height: @height-lg - 2; } .@{ant-prefix}-input { font-size: @font-size-lg; } .@{ant-prefix}-input-number { font-size: @font-size-lg; input { height: @height-lg - 2; } } .@{ant-prefix}-input-affix-wrapper, .@{ant-prefix}-picker { padding: 0px 11px; line-height: @height-lg - 2; input { height: @height-lg - 2; font-size: @font-size-lg; } } .@{ant-prefix}-btn { height: @height-lg; padding: 0px 8px; } .@{ant-prefix}-radio-button-wrapper { height: @height-lg; line-height: @height-lg; } .@{ant-prefix}-cascader-picker { height: @height-lg - 2; input { padding: 0px 11px; height: @height-lg - 2; font-size: @font-size-lg; } } .@{ant-prefix}-select-single:not(.@{ant-prefix}-select-customize-input) .@{ant-prefix}-select-selector { padding: 0px 11px; height: @height-lg; font-size: @font-size-lg; line-height: @height-lg; .@{ant-prefix}-select-selection-search { height: @height-lg; line-height: @height-lg - 2; &-input { height: @height-lg; line-height: @height-lg - 2; } } .@{ant-prefix}-select-selection-placeholder { line-height: @height-lg - 2; height: @height-lg; } .@{ant-prefix}-select-selection-item { line-height: @height-lg - 2; height: @height-lg; } } .@{ant-prefix}-select-multiple:not(.@{ant-prefix}-select-customize-input) .@{ant-prefix}-select-selector { padding: 0px 2px; height: @height-lg - 2; font-size: @font-size-lg; line-height: @height-lg; &::after { height: @height-lg - 8; line-height: @height-lg - 8; } .@{ant-prefix}-select-selection-search { height: @height-lg - 8; line-height: @height-lg - 8; &-input { height: @height-lg - 12; line-height: @height-lg - 12; } } .@{ant-prefix}-select-selection-placeholder { line-height: @height-lg - 8; height: @height-lg - 8; } .@{ant-prefix}-select-selection-overflow-item { align-self: flex-start; } .@{ant-prefix}-select-selection-item { line-height: @height-lg - 10; height: @height-lg - 8; } } &.@{form-item-cls}-feedback-layout-terse { margin-bottom: 8px; &.@{form-item-cls}-feedback-has-text:not(.@{form-item-cls}-inset) { margin-bottom: 0; } } &.@{form-item-cls}-feedback-layout-loose { margin-bottom: @form-item-margin-bottom; &.@{form-item-cls}-feedback-has-text:not(.@{form-item-cls}-inset) { margin-bottom: 0; } } } .@{form-item-cls} { &-layout-vertical { display: block; .@{form-item-cls}-label { min-height: @height-base - 10; line-height: 1.5715; } } } .@{form-item-cls}-feedback-layout-popover { margin-bottom: 8px; } .@{form-item-cls}-label-tooltip-icon { margin-left: 4px; color: #00000073; display: flex; align-items: center; max-height: @height-base; span { display: inline-flex; } } .@{form-item-cls}-control-align-left { .@{form-item-cls}-control-content { justify-content: flex-start; } } .@{form-item-cls}-control-align-right { .@{form-item-cls}-control-content { justify-content: flex-end; } } .@{form-item-cls}-control-wrap { .@{form-item-cls}-control { white-space: pre-line; word-break: break-all; } } .@{form-item-cls}-asterisk { color: @error-color; margin-right: 4px; display: inline-block; font-family: SimSun, sans-serif; } .@{form-item-cls}-optional { color: rgba(0, 0, 0, 0.45); } .@{form-item-cls}-colon { margin-left: 2px; margin-right: 8px; } .@{form-item-cls}-help, .@{form-item-cls}-extra { clear: both; min-height: @form-item-margin-bottom - 2; color: rgba(0, 0, 0, 0.45); transition: color 0.3s cubic-bezier(0.215, 0.61, 0.355, 1); padding-top: 0px; } .@{form-item-cls}-fullness { > .@{form-item-cls}-control { > .@{form-item-cls}-control-content { > .@{form-item-cls}-control-content-component { > *:first-child { width: 100%; } } } } } .@{form-item-cls}-control-content-component-has-feedback-icon { border-radius: 2px; border: 1px solid @border-color-base; padding-right: 8px; transition: all 0.3s; touch-action: manipulation; outline: none; .@{ant-prefix}-input-number, .@{ant-prefix}-picker, .@{ant-prefix}-cascader-picker:focus .@{ant-prefix}-cascader-input, .@{ant-prefix}-select:not(.@{ant-prefix}-select-customize-input) .@{ant-prefix}-select-selector, .@{ant-prefix}-input-affix-wrapper, .@{ant-prefix}-input { border: none !important; box-shadow: none !important; } } .@{form-item-cls}-bordered-none { .@{ant-prefix}-input-number, .@{ant-prefix}-input-affix-wrapper, .@{ant-prefix}-picker, .@{ant-prefix}-cascader-picker:focus .@{ant-prefix}-cascader-input, .@{ant-prefix}-select:not(.@{ant-prefix}-select-customize-input) .@{ant-prefix}-select-selector, .@{ant-prefix}-input { border: none !important; box-shadow: none !important; } .@{ant-prefix}-input-number-handler-wrap { border: none !important; .@{ant-prefix}-input-number-handler { border: none !important; } } } .@{form-item-cls}-inset { border-radius: 2px; border: 1px solid @border-color-base; padding-left: 12px; transition: 0.3s all; .@{ant-prefix}-input-number, .@{ant-prefix}-picker, .@{ant-prefix}-cascader-picker:focus .@{ant-prefix}-cascader-input, .@{ant-prefix}-select:not(.@{ant-prefix}-select-customize-input) .@{ant-prefix}-select-selector, .@{ant-prefix}-input-affix-wrapper, .@{ant-prefix}-input { border: none !important; box-shadow: none !important; } .@{ant-prefix}-input-number-handler-wrap { border: none !important; .@{ant-prefix}-input-number-handler { border: none !important; } } &:hover { .hover(); } } .@{form-item-cls}-inset-active { .active(); } .@{form-item-cls}-active { .@{form-item-cls}-control-content-component-has-feedback-icon { .active(); } .@{ant-prefix}-input-number, .@{ant-prefix}-picker, .@{ant-prefix}-cascader-picker:focus .@{ant-prefix}-cascader-input, .@{ant-prefix}-select:not(.@{ant-prefix}-select-customize-input) .@{ant-prefix}-select-selector, .@{ant-prefix}-input { .active(); } } .@{form-item-cls} { &:hover { .@{form-item-cls}-control-content-component-has-feedback-icon { .hover(); } } } .@{form-item-cls}-error { .@{ant-prefix}-select-selector, .@{ant-prefix}-cascader-picker, .@{ant-prefix}-picker, .@{ant-prefix}-input, .@{ant-prefix}-input-number, .@{ant-prefix}-input-affix-wrapper, .@{ant-prefix}-input-affix-wrapper, .@{ant-prefix}-input { border-color: @error-color !important; } .@{ant-prefix}-select-selector, .@{ant-prefix}-cascader-picker, .@{ant-prefix}-picker, .@{ant-prefix}-input, .@{ant-prefix}-input-number, .@{ant-prefix}-input-affix-wrapper, .@{ant-prefix}-input-affix-wrapper:hover, .@{ant-prefix}-input:hover { border-color: @error-color !important; } .@{ant-prefix}-select:not(.@{ant-prefix}-select-disabled):not(.@{ant-prefix}-select-customize-input) { .@{ant-prefix}-select-selector { background-color: @form-error-input-bg; border-color: @error-color !important; } &.@{ant-prefix}-select-open .@{ant-prefix}-select-selector, &.@{ant-prefix}-select-focused .@{ant-prefix}-select-selector { .active(@error-color); } } .@{ant-prefix}-input-number, .@{ant-prefix}-picker { background-color: @form-error-input-bg; border-color: @error-color; &-focused, &:focus { .active(@error-color); } &:not([disabled]):hover { background-color: @form-error-input-bg; border-color: @error-color; } } .@{ant-prefix}-cascader-picker:focus .@{ant-prefix}-cascader-input { background-color: @form-error-input-bg; .active(@error-color); } .@{ant-prefix}-input-affix-wrapper-focused, .@{ant-prefix}-input-affix-wrapper:focus, .@{ant-prefix}-input-focused, .@{ant-prefix}-input:focus { .active(@error-color); } } .@{form-item-cls}-error-help { color: @error-color !important; } .@{form-item-cls}-warning-help { color: @warning-color !important; } .@{form-item-cls}-success-help { color: @success-color !important; } .@{form-item-cls}-warning { .@{ant-prefix}-select-selector, .@{ant-prefix}-cascader-picker, .@{ant-prefix}-picker, .@{ant-prefix}-input, .@{ant-prefix}-input-number, .@{ant-prefix}-input-affix-wrapper, .@{ant-prefix}-input-affix-wrapper, .@{ant-prefix}-input { border-color: @warning-color !important; } .@{ant-prefix}-select-selector, .@{ant-prefix}-cascader-picker, .@{ant-prefix}-picker, .@{ant-prefix}-input, .@{ant-prefix}-input-number, .@{ant-prefix}-input-affix-wrapper, .@{ant-prefix}-input-affix-wrapper:hover, .@{ant-prefix}-input:hover { border-color: @warning-color !important; } .@{ant-prefix}-select:not(.@{ant-prefix}-select-disabled):not(.@{ant-prefix}-select-customize-input) { .@{ant-prefix}-select-selector { background-color: @form-warning-input-bg; border-color: @warning-color !important; } &.@{ant-prefix}-select-open .@{ant-prefix}-select-selector, &.@{ant-prefix}-select-focused .@{ant-prefix}-select-selector { .active(@warning-color); } } .@{ant-prefix}-input-number, .@{ant-prefix}-picker { background-color: @form-warning-input-bg; border-color: @warning-color; &-focused, &:focus { .active(@warning-color); } &:not([disabled]):hover { background-color: @form-warning-input-bg; border-color: @warning-color; } } .@{ant-prefix}-cascader-picker:focus .@{ant-prefix}-cascader-input { background-color: @form-warning-input-bg; .active(@warning-color); } .@{ant-prefix}-input-affix-wrapper-focused, .@{ant-prefix}-input-affix-wrapper:focus, .@{ant-prefix}-input-focused, .@{ant-prefix}-input:focus { .active(@warning-color); } } .@{form-item-cls}-success { .@{ant-prefix}-select-selector, .@{ant-prefix}-cascader-picker, .@{ant-prefix}-picker, .@{ant-prefix}-input, .@{ant-prefix}-input-number, .@{ant-prefix}-input-affix-wrapper, .@{ant-prefix}-input-affix-wrapper, .@{ant-prefix}-input { border-color: @success-color !important; } .@{ant-prefix}-select-selector, .@{ant-prefix}-cascader-picker, .@{ant-prefix}-picker, .@{ant-prefix}-input, .@{ant-prefix}-input-number, .@{ant-prefix}-input-affix-wrapper, .@{ant-prefix}-input-affix-wrapper:hover, .@{ant-prefix}-input:hover { border-color: @success-color !important; } .@{ant-prefix}-input-affix-wrapper-focused, .@{ant-prefix}-input-affix-wrapper:focus, .@{ant-prefix}-input-focused, .@{ant-prefix}-input:focus { border-color: @success-color !important; border-right-width: 1px !important; outline: 0; } } ``` -------------------------------------------------------------------------------- /packages/core/docs/api/models/Form.md: -------------------------------------------------------------------------------- ```markdown --- order: 0 --- # Form Call the core [Form Model](/guide/form) API returned by [createForm](/api/entry/create-form), the following will list all model attributes, if the attribute is writable, then we can directly The reference is to modify the attribute, and @formily/reactive will respond to trigger the UI update. ## Attributes | Property | Description | Type | Read-only or not | Default value | | ------------- | ------------------------------------------------- | ------------------------------------- | ---------------- | ----------------- | | initialized | Whether the form is initialized | Boolean | No | `false` | | validating | Is the form being validated | Boolean | No | `false` | | submitting | Is the form being submitted | Boolean | No | `false` | | modified | Whether the form value has been manually modified | Boolean | No | `false` | | pattern | Form interaction mode | [FormPatternTypes](#formpatterntypes) | No | `"editable"` | | display | Form display form | [FormDisplayTypes](#formdisplaytypes) | No | `"visible"` | | mounted | Is the form mounted | Boolean | No | `false` | | unmounted | Is the form unmounted | Boolean | No | `false` | | values | form values | Object | No | `{}` | | initialValues | Form default values | Object | No | `{}` | | valid | Is the form valid | Boolean | Yes | `true` | | invalid | Is the form illegal | Boolean | Yes | `false` | | errors | Form validation error message | [IFormFeedback](#iformfeedback)[] | Yes | `[]` | | warnings | Form verification warning message | [IFormFeedback](#iformfeedback)[] | Yes | `[]` | | successes | Form verification success message | [IFormFeedback](#iformfeedback)[] | Yes | `[]` | | hidden | Whether the form is hidden | Boolean | No | `false` | | visible | Whether the form is displayed | Boolean | No | `true` | | editable | Is the form editable | Boolean | No | `true` | | readOnly | Is the form read-only | Boolean | No | `false` | | disabled | Whether the form is disabled | Boolean | No | `false` | | readPretty | Is the form in a read state | Boolean | No | `false` | | id | Form ID | String | No | `{RANDOM_STRING}` | | displayName | Model label | String | No | `"Form"` | ## Method ### createField #### Description Create a factory function for a Field instance. If the path is the same and called multiple times, the instance object will be reused #### Signature ```ts interface createField { (props: IFieldFactoryProps): Field } ``` For function entry, please refer to [IFieldFactoryProps](#ifieldfactoryprops) ### createArrayField #### Description A factory function for creating an ArrayField instance. If the path is the same and called multiple times, the instance object will be reused #### Signature ```ts interface createArrayField { (props: IFieldFactoryProps): ArrayField } ``` For function entry, please refer to [IFieldFactoryProps](#ifieldfactoryprops) ### createObjectField #### Description A factory function to create an ObjectField instance. If the path is the same and called multiple times, it will reuse the instance object #### Signature ```ts interface createObjectField { (props: IFieldFactoryProps): ArrayField } ``` For function entry, please refer to [IFieldFactoryProps](#ifieldfactoryprops) ### createVoidField #### Description A factory function to create a VoidField instance. If the path is the same and called multiple times, the instance object will be reused #### Signature ```ts interface createVoidField { (props: IVoidFieldFactoryProps): ArrayField } ``` For function entry, please refer to [IVoidFieldFactoryProps](#ivoidfieldfactoryprops) ### setValues #### Description Set the form value, you can set the merge strategy [IFormMergeStrategy](#iformmergestrategy) #### Signature ```ts interface setValues { (values: object, strategy: IFormMergeStrategy = 'merge'): void } ``` ### setInitialValues #### Description Set the default value of the form, you can set the merge strategy #### Signature ```ts interface setInitialValues { (initialValues: object, strategy: IFormMergeStrategy = 'merge'): void } ``` ### setValuesIn #### Description Precisely set form values #### Signature ```ts interface setValuesIn { (path: FormPathPattern, value: any): void } ``` FormPathPattern API Reference [FormPath](/api/entry/form-path#formpathpattern) ### setInitialValuesIn #### Description Precisely set the form default value #### Signature ```ts interface setInitialValuesIn { (path: FormPathPattern, initialValue: any): void } ``` FormPathPattern API Reference [FormPath](/api/entry/form-path#formpathpattern) ### existValuesIn #### Description Determine whether the value exists according to the specified path #### Signature ```ts interface existValuesIn { (path: FormPathPattern): boolean } ``` FormPathPattern API Reference [FormPath](/api/entry/form-path#formpathpattern) ### existInitialValuesIn #### Description Determine whether the default value exists according to the specified path #### Signature ```ts interface existInitialValuesIn { (path: FormPathPattern): boolean } ``` FormPathPattern API Reference [FormPath](/api/entry/form-path#formpathpattern) ### getValuesIn #### Description Get the form value according to the specified path #### Signature ```ts interface getValuesIn { (path: FormPathPattern): any } ``` FormPathPattern API Reference [FormPath](/api/entry/form-path#formpathpattern) ### getInitialValuesIn #### Description Get the default value of the form according to the specified path #### Signature ```ts interface getInitialValuesIn { (path: FormPathPattern): any } ``` FormPathPattern API Reference [FormPath](/api/entry/form-path#formpathpattern) ### deleteValuesIn #### Description Delete the form value according to the specified path #### Signature ```ts interface deleteValuesIn { (path: FormPathPattern): void } ``` FormPathPattern API Reference [FormPath](/api/entry/form-path#formpathpattern) ### deleteInitialValuesIn #### Description Delete the default value of the form according to the specified path #### Signature ```ts interface deleteInitialValuesIn { (path: FormPathPattern): void } ``` FormPathPattern API Reference [FormPath](/api/entry/form-path#formpathpattern) ### setSubmitting #### Description Set whether the form is being submitted #### Signature ```ts interface setSubmitting { (submitting: boolean): void } ``` ### setValidating #### Description Set whether the form is verifying status #### Signature ```ts interface setValidating { (validating: boolean): void } ``` ### setDisplay #### Description Set form display status #### Signature ```ts interface setDisplay { (display: FormDisplayTypes): void } ``` For function entry, please refer to [FormDisplayTypes](#formdisplaytypes) ### setPattern #### Description Set the form interaction mode #### Signature ```ts interface setPattern { (pattern: FormPatternTypes): void } ``` For function entry, please refer to [FormPatternTypes](#formpatterntypes) ### addEffects #### Description Add side effects #### Signature ```ts interface addEffects { (id: string, effects: (form: Form) => void): void } ``` ### removeEffects #### Description Remove side effects, the id is consistent with the id of addEffects #### Signature ```ts interface removeEffects { (id: string): void } ``` ### setEffects #### Description Overwrite update side effects #### Signature ```ts interface setEffects { (effects: (form: Form) => void): void } ``` ### clearErrors #### Description Clear error message #### Signature ```ts interface clearErrors { (pattern?: FormPathPattern): void } ``` FormPathPattern API Reference [FormPath](/api/entry/form-path#formpathpattern) ### clearWarnings #### Description Clear warning message #### Signature ```ts interface clearWarnings { (pattern?: FormPathPattern): void } ``` FormPathPattern API Reference [FormPath](/api/entry/form-path#formpathpattern) ### clearSuccesses #### Description Clear success message #### Signature ```ts interface clearSuccesses { (pattern?: FormPathPattern): void } ``` FormPathPattern API Reference [FormPath](/api/entry/form-path#formpathpattern) ### query #### Description Query field node #### Signature ```ts interface query { (pattern: FormPathPattern): Query } ``` FormPathPattern API Reference [FormPath](/api/entry/form-path#formpathpattern) Query object API reference [Query](/api/models/query) ### queryFeedbacks #### Description Query message feedback #### Signature ```ts interface queryFeedbacks { (search: ISearchFeedback): IFormFeedback[] } ``` ISearchFeedback Reference [ISearchFeedback](/api/models/field#isearchfeedback) IFormFeedback Reference [IFormFeedback](#iformfeedback) ### notify #### Description Broadcast message #### Signature ```ts interface notify<T> { (type?: string, payload: T): void } ``` ### subscribe #### Description Subscribe to news #### Signature ```ts interface subscribe<T> { (callback: (payload: T) => void): number } ``` ### unsubscribe #### Description unsubscribe #### Signature ```ts interface unsubscribe { (id: number): void } ``` ### onInit #### Description Trigger form initialization, no need to manually call by default #### Signature ```ts interface onInit { (): void } ``` ### onMount #### Description Trigger mount #### Signature ```ts interface onMount { (): void } ``` ### onUnmount #### Description Trigger offload #### Signature ```ts interface onUnmount { (): void } ``` ### setState #### Description Set form status #### Signature ```ts interface setState { (callback: (state: IFormState) => void): void (state: IFormState): void } ``` IFormState Reference [IFormState](#iformstate) ### getState #### Description Get form status #### Signature ```ts interface getState<T> { (): IFormState (callback: (state: IFormState) => T): T } ``` IFormState Reference [IFormState](#iformstate) ### setFormState Consistent with setState API ### getFormState Consistent with getState API ### setFieldState #### Description Set field status #### Signature ```ts interface setFieldState { (pattern: FormPathPattern, setter: (state: IGeneralFieldState) => void): void (pattern: FormPathPattern, setter: IGeneralFieldState): void } ``` FormPathPattern API Reference [FormPath](/api/entry/form-path#formpathpattern) IGeneralFieldState Reference [IGeneralFieldState](/api/models/field/#igeneralfieldstate) ### getFieldState #### Description Get field status #### Signature ```ts interface getFieldState<T> { (pattern: FormPathPattern): IGeneralFieldState (pattern: FormPathPattern, callback: (state: IGeneralFieldState) => T): T } ``` FormPathPattern API Reference [FormPath](/api/entry/form-path#formpathpattern) IGeneralFieldState Reference [IGeneralFieldState](/api/models/field/#igeneralfieldstate) ### getFormGraph #### Description Get form field set #### Signature ```ts interface getFormGraph { (): { [key: string]: GeneralFieldState | FormState } } ``` ### setFormGraph #### Description Set the form field set #### Signature ```ts interface setFormGraph { (graph: { [key: string]: GeneralFieldState | FormState }): void } ``` ### clearFormGraph #### Description Clear the field set #### Signature ```ts interface clearFormGraph { (pattern: FormPathPattern): void } ``` ### validate #### Description The form verification trigger can be verified according to the specified path. If the verification is successful, there will be no return, and the verification failure will be returned in the promise reject [IFormFeedback](#iformfeedback)[] #### Signature ```ts interface validate { (pattern: FormPathPattern): Promise<void> } ``` ### submit #### Description In the form submission method, if the Promise is returned in the onSubmit callback function, the form will set the submitting status to true at the beginning of the submission, and then set it to false when the Promise resolves. The view layer can consume the submitting status to prevent repeated submissions. #### Signature ```ts interface submit<T> { (): Promise<Form['values']> (onSubmit?: (values: Form['values']) => Promise<T> | void): Promise<T> } ``` ### reset #### Description Form reset method, you can specify the specific field to be reset, or you can specify automatic verification when reset #### Description ```ts interface reset { (pattern: FormPathPattern, options?: IFieldResetOptions): Promise<void> } ``` FormPathPattern API Reference [FormPath](/api/entry/form-path#formpathpattern) IFieldResetOptions Reference [IFieldResetOptions](/api/models/field/#ifieldresetoptions) ## Types of <Alert> Note: If you want to manually consume the type, just export it directly from the package module </Alert> ### FormPatternTypes ```ts type FormPatternTypes = 'editable' | 'disabled' | 'readOnly' | 'readPretty' ``` ### FormDisplayTypes ```ts type FormDisplayTypes = 'none' | 'hidden' | 'visible' ``` ### IFormFeedback ```ts interface IFormFeedback { path?: string //Check field data path address?: string //The absolute path of the verification field triggerType?: 'onInput' | 'onFocus' | 'onBlur' //Verify the trigger type type?: 'error' | 'success' | 'warning' //feedback type code?: //Feedback code | 'ValidateError' | 'ValidateSuccess' | 'ValidateWarning' | 'EffectError' | 'EffectSuccess' | 'EffectWarning' messages?: string[] //Feedback message } ``` ### IFormState ```ts interface IFormState { editable?: boolean readOnly?: boolean disabled?: boolean readPretty?: boolean hidden?: boolean visible?: boolean initialized?: boolean validating?: boolean submitting?: boolean modified?: boolean pattern?: FormPatternTypes display?: FormDisplayTypes values?: any initialValues?: any mounted?: boolean unmounted?: boolean readonly valid?: boolean readonly invalid?: boolean readonly errors?: IFormFeedback[] readonly warnings?: IFormFeedback[] readonly successes?: IFormFeedback[] } ``` ### IFormMergeStrategy ```ts type IFormMergeStrategy = 'overwrite' | 'merge' | 'deepMerge' | 'shallowMerge' ``` ### IFieldFactoryProps ```ts interface IFieldFactoryProps { name: FormPathPattern //Field name, the path name of the current node basePath?: FormPathPattern //base path title?: string | JSXElement //Field title description?: string | JSXElement //Field description value?: any //Field value initialValue?: any //Field default value required?: boolean //Is the field required display?: 'none' | 'hidden' | 'visible' //Field display form pattern?: 'editable' | 'disabled' | 'readOnly' | 'readPretty' //Field interaction mode hidden?: boolean //whether the field is hidden visible?: boolean //Whether the field is displayed editable?: boolean //Is the field editable disabled?: boolean //Whether the field is disabled readOnly?: boolean //Is the field read-only readPretty?: boolean //Whether the field is in the read state dataSource?: any[] //Field data source validateFirst?: boolean //Does the field verification only verify the first illegal rule? validatePattern?: ('editable' | 'disabled' | 'readOnly' | 'readPretty')[] // Which patterns the validator can run in validateDisplay?: ('none' | 'hidden' | 'visible')[] // Which displays the validator can run in validator?: FieldValidator //Field validator decorator?: any[] //Field decorator, the first element represents the component reference, the second element represents the component attribute component?: any[] //Field component, the first element represents the component reference, the second element represents the component attribute reactions?: FieldReaction[] | FieldReaction //Field responder } ``` FormPathPattern API Reference [FormPath](/api/entry/form-path#formpathpattern) FieldValidator Reference [FieldValidator](/api/models/field#fieldvalidator) FieldReaction Reference [FieldReaction](/api/models/field#fieldreaction) ### IVoidFieldFactoryProps ```ts interface IFieldFactoryProps { name: FormPathPattern //Field name, the path name of the current node basePath?: FormPathPattern //base path title?: string | JSXElement //Field title description?: string | JSXElement //Field description required?: boolean //Is the field required display?: 'none' | 'hidden' | 'visible' //Field display form pattern?: 'editable' | 'disabled' | 'readOnly' | 'readPretty' //Field interaction mode hidden?: boolean //whether the field is hidden visible?: boolean //Whether the field is displayed editable?: boolean //Is the field editable disabled?: boolean //Whether the field is disabled readOnly?: boolean //Is the field read-only readPretty?: boolean //Whether the field is in the read state decorator?: any[] //Field decorator, the first element represents the component reference, the second element represents the component attribute component?: any[] //Field component, the first element represents the component reference, the second element represents the component attribute reactions?: FieldReaction[] | FieldReaction //Field responder } ``` FormPathPattern API Reference [FormPath](/api/entry/form-path#formpathpattern) FieldReaction Reference [FieldReaction](/api/models/field#fieldreaction) > Formily Typescript type convention > > - Simple non-object data types or Union data types use type to define the type, and cannot start with an uppercase `I` character > - Simple object types use interface to define the type uniformly, and start with an uppercase `I` character. If there are combinations of different interfaces (Intersection or Extends), use type to define the type, and also start with an uppercase `I` character ``` -------------------------------------------------------------------------------- /packages/next/docs/components/ArrayTable.zh-CN.md: -------------------------------------------------------------------------------- ```markdown # ArrayTable > 自增表格,对于数据量超大的场景比较适合使用该组件,虽然数据量大到一定程度会有些许卡顿,但是不会影响基本操作 > > 注意:该组件只适用于 Schema 场景,且只能是对象数组 ## Markup Schema 案例 ```tsx import React from 'react' import { FormItem, Input, ArrayTable, Editable, FormButtonGroup, Submit, } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' import { Button, Message } from '@alifd/next' const SchemaField = createSchemaField({ components: { FormItem, Editable, Input, ArrayTable, }, }) const form = createForm() const range = (count: number) => Array.from(new Array(count)).map((_, key) => ({ aaa: key, })) export default () => { return ( <FormProvider form={form}> <SchemaField> <SchemaField.Array name="array" x-decorator="FormItem" x-component="ArrayTable" x-component-props={{ pagination: { pageSize: 10 }, style: { width: '100%' }, }} > <SchemaField.Object> <SchemaField.Void x-component="ArrayTable.Column" x-component-props={{ width: 80, title: 'Sort', align: 'center' }} > <SchemaField.Void x-decorator="FormItem" required x-component="ArrayTable.SortHandle" /> </SchemaField.Void> <SchemaField.Void x-component="ArrayTable.Column" x-component-props={{ width: 120, title: 'Index', align: 'center', }} > <SchemaField.Void x-decorator="FormItem" required x-component="ArrayTable.Index" /> </SchemaField.Void> <SchemaField.Void x-component="ArrayTable.Column" x-component-props={{ title: 'A1', dataIndex: 'a1', width: 200 }} > <SchemaField.String name="a1" x-decorator="Editable" required x-component="Input" /> </SchemaField.Void> <SchemaField.Void x-component="ArrayTable.Column" x-component-props={{ title: 'A2', width: 200 }} > <SchemaField.String x-decorator="FormItem" name="a2" required x-component="Input" /> </SchemaField.Void> <SchemaField.Void x-component="ArrayTable.Column" x-component-props={{ title: 'A3', width: 200 }} > <SchemaField.String x-decorator="FormItem" name="a3" required x-component="Input" /> </SchemaField.Void> <SchemaField.Void x-component="ArrayTable.Column" x-component-props={{ title: 'Operations', dataIndex: 'operations', width: 200, fixed: 'right', }} > <SchemaField.Void x-component="FormItem"> <SchemaField.Void x-component="ArrayTable.Remove" /> <SchemaField.Void x-component="ArrayTable.MoveDown" /> <SchemaField.Void x-component="ArrayTable.MoveUp" /> </SchemaField.Void> </SchemaField.Void> </SchemaField.Object> <SchemaField.Void x-component="ArrayTable.Addition" title="添加条目" /> </SchemaField.Array> </SchemaField> <FormButtonGroup> <Submit onSubmit={console.log}>提交</Submit> <Button onClick={() => { form.setInitialValues({ array: range(100000), }) }} > 加载10W条超大数据 </Button> </FormButtonGroup> <Message style={{ marginTop: 10 }} type="warning"> 注意:开启formily插件的页面,因为后台有数据通信,会占用浏览器算力,最好在无痕模式(无formily插件)下测试 </Message> </FormProvider> ) } ``` ## JSON Schema 案例 ```tsx import React from 'react' import { FormItem, Input, ArrayTable, Editable, FormButtonGroup, Submit, } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { FormItem, Editable, Input, ArrayTable, }, }) const form = createForm() const schema = { type: 'object', properties: { array: { type: 'array', 'x-decorator': 'FormItem', 'x-component': 'ArrayTable', 'x-component-props': { pagination: { pageSize: 10 }, style: { width: '100%' }, }, items: { type: 'object', properties: { column1: { type: 'void', 'x-component': 'ArrayTable.Column', 'x-component-props': { width: 80, title: 'Sort', align: 'center' }, properties: { sort: { type: 'void', 'x-component': 'ArrayTable.SortHandle', }, }, }, column2: { type: 'void', 'x-component': 'ArrayTable.Column', 'x-component-props': { width: 120, title: 'Index', align: 'center', }, properties: { index: { type: 'void', 'x-component': 'ArrayTable.Index', }, }, }, column3: { type: 'void', 'x-component': 'ArrayTable.Column', 'x-component-props': { width: 200, title: 'A1' }, properties: { a1: { type: 'string', 'x-decorator': 'Editable', 'x-component': 'Input', }, }, }, column4: { type: 'void', 'x-component': 'ArrayTable.Column', 'x-component-props': { width: 200, title: 'A2' }, properties: { a2: { type: 'string', 'x-decorator': 'FormItem', 'x-component': 'Input', }, }, }, column5: { type: 'void', 'x-component': 'ArrayTable.Column', 'x-component-props': { width: 200, title: 'A3' }, properties: { a3: { type: 'string', 'x-decorator': 'FormItem', 'x-component': 'Input', }, }, }, column6: { type: 'void', 'x-component': 'ArrayTable.Column', 'x-component-props': { title: 'Operations', dataIndex: 'operations', width: 200, fixed: 'right', }, properties: { item: { type: 'void', 'x-component': 'FormItem', properties: { remove: { type: 'void', 'x-component': 'ArrayTable.Remove', }, moveDown: { type: 'void', 'x-component': 'ArrayTable.MoveDown', }, moveUp: { type: 'void', 'x-component': 'ArrayTable.MoveUp', }, }, }, }, }, }, }, properties: { add: { type: 'void', 'x-component': 'ArrayTable.Addition', title: '添加条目', }, }, }, }, } export default () => { return ( <FormProvider form={form}> <SchemaField schema={schema} /> <FormButtonGroup> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup> </FormProvider> ) } ``` ## Effects 联动案例 ```tsx import React from 'react' import { FormItem, Input, ArrayTable, Switch, FormButtonGroup, Submit, } from '@formily/next' import { createForm, onFieldChange, onFieldReact } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' import { Button } from '@alifd/next' const SchemaField = createSchemaField({ components: { FormItem, Switch, Input, Button, ArrayTable, }, }) const form = createForm({ effects: () => { //主动联动模式 onFieldChange('hideFirstColumn', ['value'], (field) => { field.query('array.column4').take((target) => { target.visible = !field.value }) field.query('array.*.a2').take((target) => { target.visible = !field.value }) }) //被动联动模式 onFieldReact('array.*.a2', (field) => { field.visible = !field.query('.a1').get('value') }) }, }) export default () => { return ( <FormProvider form={form}> <SchemaField> <SchemaField.Boolean name="hideFirstColumn" x-decorator="FormItem" x-component="Switch" title="隐藏A2" /> <SchemaField.Array name="array" x-decorator="FormItem" x-component="ArrayTable" x-component-props={{ pagination: { pageSize: 10 }, style: { width: '100%' }, }} > <SchemaField.Object> <SchemaField.Void x-component="ArrayTable.Column" name="column1" x-component-props={{ width: 80, title: 'Sort', align: 'center' }} > <SchemaField.Void x-decorator="FormItem" required x-component="ArrayTable.SortHandle" /> </SchemaField.Void> <SchemaField.Void x-component="ArrayTable.Column" name="column2" x-component-props={{ width: 120, title: 'Index', align: 'center', }} > <SchemaField.String x-decorator="FormItem" required x-component="ArrayTable.Index" /> </SchemaField.Void> <SchemaField.Void x-component="ArrayTable.Column" name="column3" x-component-props={{ title: '显隐->A2', dataIndex: 'a1', width: 100, }} > <SchemaField.Boolean name="a1" x-decorator="FormItem" required x-component="Switch" /> </SchemaField.Void> <SchemaField.Void x-component="ArrayTable.Column" name="column4" x-component-props={{ title: 'A2', width: 200 }} > <SchemaField.String x-decorator="FormItem" name="a2" required x-component="Input" /> </SchemaField.Void> <SchemaField.Void x-component="ArrayTable.Column" name="column5" x-component-props={{ title: 'A3', width: 200 }} > <SchemaField.String x-decorator="FormItem" name="a3" required x-component="Input" /> </SchemaField.Void> <SchemaField.Void x-component="ArrayTable.Column" name="column6" x-component-props={{ title: 'Operations', dataIndex: 'operations', width: 200, fixed: 'right', }} > <SchemaField.Void x-component="FormItem"> <SchemaField.Void x-component="ArrayTable.Remove" /> <SchemaField.Void x-component="ArrayTable.MoveDown" /> <SchemaField.Void x-component="ArrayTable.MoveUp" /> </SchemaField.Void> </SchemaField.Void> </SchemaField.Object> <SchemaField.Void x-component="ArrayTable.Addition" title="添加条目" /> </SchemaField.Array> </SchemaField> <FormButtonGroup> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup> </FormProvider> ) } ``` ## JSON Schema 联动案例 ```tsx import React from 'react' import { FormItem, Input, ArrayTable, Switch, FormButtonGroup, Submit, } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { FormItem, Switch, Input, ArrayTable, }, }) const form = createForm() const schema = { type: 'object', properties: { hideFirstColumn: { type: 'boolean', title: '隐藏A2', 'x-decorator': 'FormItem', 'x-component': 'Switch', }, array: { type: 'array', 'x-decorator': 'FormItem', 'x-component': 'ArrayTable', 'x-component-props': { pagination: { pageSize: 10 }, style: { width: '100%' }, }, items: { type: 'object', properties: { column1: { type: 'void', 'x-component': 'ArrayTable.Column', 'x-component-props': { width: 80, title: 'Sort', align: 'center' }, properties: { sort: { type: 'void', 'x-component': 'ArrayTable.SortHandle', }, }, }, column2: { type: 'void', 'x-component': 'ArrayTable.Column', 'x-component-props': { width: 120, title: 'Index', align: 'center', }, properties: { index: { type: 'void', 'x-component': 'ArrayTable.Index', }, }, }, column3: { type: 'void', 'x-component': 'ArrayTable.Column', 'x-component-props': { width: 100, title: '显隐->A2' }, properties: { a1: { type: 'boolean', 'x-decorator': 'FormItem', 'x-component': 'Switch', }, }, }, column4: { type: 'void', 'x-component': 'ArrayTable.Column', 'x-component-props': { width: 200, title: 'A2' }, 'x-reactions': [ { dependencies: ['hideFirstColumn'], when: '{{$deps[0]}}', fulfill: { schema: { 'x-visible': false, }, }, otherwise: { schema: { 'x-visible': true, }, }, }, ], properties: { a2: { type: 'string', 'x-decorator': 'FormItem', 'x-component': 'Input', required: true, 'x-reactions': [ { dependencies: ['.a1', 'hideFirstColumn'], when: '{{$deps[1] || $deps[0]}}', fulfill: { schema: { 'x-visible': false, }, }, otherwise: { schema: { 'x-visible': true, }, }, }, ], }, }, }, column5: { type: 'void', 'x-component': 'ArrayTable.Column', 'x-component-props': { width: 200, title: 'A3' }, properties: { a3: { type: 'string', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', }, }, }, column6: { type: 'void', 'x-component': 'ArrayTable.Column', 'x-component-props': { title: 'Operations', dataIndex: 'operations', width: 200, fixed: 'right', }, properties: { item: { type: 'void', 'x-component': 'FormItem', properties: { remove: { type: 'void', 'x-component': 'ArrayTable.Remove', }, moveDown: { type: 'void', 'x-component': 'ArrayTable.MoveDown', }, moveUp: { type: 'void', 'x-component': 'ArrayTable.MoveUp', }, }, }, }, }, }, }, properties: { add: { type: 'void', 'x-component': 'ArrayTable.Addition', title: '添加条目', }, }, }, }, } export default () => { return ( <FormProvider form={form}> <SchemaField schema={schema} /> <FormButtonGroup> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup> </FormProvider> ) } ``` ## API ### ArrayTable > 表格组件 扩展属性 | 属性名 | 类型 | 描述 | 默认值 | | ---------- | ------------------------- | ------------ | ------ | | onAdd | `(index: number) => void` | 增加方法 | | | onRemove | `(index: number) => void` | 删除方法 | | | onCopy | `(index: number) => void` | 复制方法 | | | onMoveUp | `(index: number) => void` | 向上移动方法 | | | onMoveDown | `(index: number) => void` | 向下移动方法 | | 其余参考 https://fusion.design/pc/component/basic/table ### ArrayTable.Column > 表格列 参考 https://fusion.design/pc/component/basic/table ### ArrayTable.SortHandle > 拖拽手柄 参考 https://ant.design/components/icon-cn/ ### ArrayTable.Addition > 添加按钮 扩展属性 | 属性名 | 类型 | 描述 | 默认值 | | ------------ | --------------------- | -------- | -------- | | title | ReactText | 文案 | | | method | `'push' \| 'unshift'` | 添加方式 | `'push'` | | defaultValue | `any` | 默认值 | | 其余参考 https://fusion.design/pc/component/basic/button 注意:title 属性可以接收 Field 模型中的 title 映射,也就是在 Field 上传 title 也是生效的 ### ArrayTable.Remove > 删除按钮 | 属性名 | 类型 | 描述 | 默认值 | | ------ | --------- | ---- | ------ | | title | ReactText | 文案 | | 其余参考 https://ant.design/components/icon-cn/ 注意:title 属性可以接收 Field 模型中的 title 映射,也就是在 Field 上传 title 也是生效的 ### ArrayTable.MoveDown > 下移按钮 | 属性名 | 类型 | 描述 | 默认值 | | ------ | --------- | ---- | ------ | | title | ReactText | 文案 | | 其余参考 https://ant.design/components/icon-cn/ 注意:title 属性可以接收 Field 模型中的 title 映射,也就是在 Field 上传 title 也是生效的 ### ArrayTable.MoveUp > 上移按钮 | 属性名 | 类型 | 描述 | 默认值 | | ------ | --------- | ---- | ------ | | title | ReactText | 文案 | | 其余参考 https://ant.design/components/icon-cn/ 注意:title 属性可以接收 Field 模型中的 title 映射,也就是在 Field 上传 title 也是生效的 ### ArrayTable.Index > 索引渲染器 无属性 ### ArrayTable.useIndex > 读取当前渲染行索引的 React Hook ### ArrayTable.useRecord > 读取当前渲染记录的 React Hook ``` -------------------------------------------------------------------------------- /packages/core/docs/api/entry/FormEffectHooks.md: -------------------------------------------------------------------------------- ```markdown --- order: 1 --- # Form Effect Hooks ## onFormInit #### Description Used to monitor the side effect hook of a form initialization, we will trigger the initialization event when we call createForm #### Signature ```ts interface onFormInit { (callback: (form: Form) => void) } ``` #### Example ```tsx import React, { useMemo, useState } from 'react' import { createForm, onFormInit } from '@formily/core' import { ActionResponse } from './ActionResponse' export default () => { const [response, setResponse] = useState('') useMemo( () => createForm({ effects() { onFormInit(() => { setResponse('The form has been initialized') }) }, }), [] ) return <ActionResponse response={response} /> } ``` ## onFormMount #### Description Used to monitor the side-effect hook that the form has been mounted, we will trigger the mount event when we call onMount #### Signature ```ts interface onFormMount { (callback: (form: Form) => void) } ``` #### Example ```tsx import React, { useMemo, useState } from 'react' import { createForm, onFormMount } from '@formily/core' import { ActionResponse } from './ActionResponse' export default () => { const [response, setResponse] = useState('') const form = useMemo( () => createForm({ effects() { onFormMount(() => { setResponse('The form has been mounted') }) }, }), [] ) return ( <ActionResponse response={response}> <button onClick={() => { form.onMount() }} > Mount form </button> </ActionResponse> ) } ``` ## onFormUnmount #### Description Used to monitor the side effect hook that the form has been unloaded, we will trigger the unmount event when we call onUnmount #### Signature ```ts interface onFormUnmount { (callback: (form: Form) => void) } ``` #### Example ```tsx import React, { useMemo, useState } from 'react' import { createForm, onFormUnmount } from '@formily/core' import { ActionResponse } from './ActionResponse' export default () => { const [response, setResponse] = useState('') const form = useMemo( () => createForm({ effects() { onFormUnmount(() => { setResponse('Form has been uninstalled') }) }, }), [] ) return ( <ActionResponse response={response}> <button onClick={() => { form.onUnmount() }} > Uninstall form </button> </ActionResponse> ) } ``` ## onFormReact #### Description The side effect hook used to implement form response logic. Its core principle is that the callback function will be executed when the form is initialized, and dependencies will be automatically tracked at the same time. The callback function will be executed repeatedly when the dependent data changes. #### Signature ```ts interface onFormReact { (callback: (form: Form) => void) } ``` #### Example ```tsx import React, { useMemo, useState } from 'react' import { createForm, onFormReact } from '@formily/core' import { ActionResponse } from './ActionResponse' export default () => { const [response, setResponse] = useState('') const form = useMemo( () => createForm({ effects() { onFormReact((form) => { if (form.values.input == 'Hello') { setResponse('Response Hello') } else if (form.values.input == 'World') { setResponse('Response to World') } }) }, }), [] ) return ( <ActionResponse response={response}> <button onClick={() => { form.setValuesIn('input', 'Hello') }} > Hello </button> <button onClick={() => { form.setValuesIn('input', 'World') }} > World </button> </ActionResponse> ) } ``` ## onFormValuesChange #### Description Side effect hooks for monitoring form value changes <Alert> It should be noted that this hook is triggered synchronously. For some behaviors that trigger `set` operation of `Proxy` multiple times, the results may not be as expected. For example, when deleting elements from array by `splice`, the array length will be the same as before deletion. (<a href="https://github.com/alibaba/formily/issues/2128">#2128</a>) </Alert> #### Signature ```ts interface onFormValuesChange { (callback: (form: Form) => void) } ``` #### Example ```tsx import React, { useMemo, useState } from 'react' import { createForm, onFormValuesChange } from '@formily/core' import { ActionResponse } from './ActionResponse' export default () => { const [response, setResponse] = useState('') const form = useMemo( () => createForm({ effects() { onFormValuesChange((form) => { setResponse('Form value change: ' + form.values.input) }) }, }), [] ) return ( <ActionResponse response={response}> <button onClick={() => { form.setValuesIn('input', 'Hello World') }} > Hello World </button> </ActionResponse> ) } ``` ## onFormInitialValuesChange #### Description Side effect hooks used to monitor the changes of the default value of the form #### Signature ```ts interface onFormInitialValuesChange { (callback: (form: Form) => void) } ``` #### Example ```tsx import React, { useMemo, useState } from 'react' import { createForm, onFormInitialValuesChange } from '@formily/core' import { ActionResponse } from './ActionResponse' export default () => { const [response, setResponse] = useState('') const form = useMemo( () => createForm({ effects() { onFormInitialValuesChange((form) => { setResponse('Form default value change: ' + form.values.input) }) }, }), [] ) return ( <ActionResponse response={response}> <button onClick={() => { form.setInitialValuesIn('input', 'Hello World') }} > Hello World </button> </ActionResponse> ) } ``` ## onFormInputChange #### Description Side effect hook for listening to field input #### Signature ```ts interface onFormInputChange { (callback: (form: Form) => void) } ``` #### Example ```tsx import React, { useMemo, useState } from 'react' import { createForm, onFormInputChange } from '@formily/core' import { ActionResponse } from './ActionResponse' export default () => { const [response, setResponse] = useState('') const form = useMemo( () => createForm({ effects() { onFormInputChange((form) => { setResponse('Character input change: ' + form.values.input) }) }, }), [] ) return ( <ActionResponse response={response}> <button onClick={() => { form .createField({ name: 'input', }) .onInput('Hello World') }} > Hello World </button> </ActionResponse> ) } ``` ## onFormSubmit #### Description Side effect hook for monitoring form submission #### Signature ```ts interface onFormSubmit { (callback: (form: Form) => void) } ``` #### Example ```tsx import React, { useMemo, useState } from 'react' import { createForm, onFormSubmit } from '@formily/core' import { ActionResponse } from './ActionResponse' export default () => { const [response, setResponse] = useState('') const form = useMemo( () => createForm({ effects() { onFormSubmit(() => { setResponse('Form has been submitted') }) }, }), [] ) return ( <ActionResponse response={response}> <button onClick={() => { form.submit() }} > Submit </button> </ActionResponse> ) } ``` ## onFormSubmitStart #### Description Side effect hook for monitoring the start of form submission #### Signature ```ts interface onFormSubmitStart { (callback: (form: Form) => void) } ``` #### Example ```tsx import React, { useMemo, useState } from 'react' import { createForm, onFormSubmitStart } from '@formily/core' import { ActionResponse } from './ActionResponse' export default () => { const [response, setResponse] = useState('') const form = useMemo( () => createForm({ effects() { onFormSubmitStart(() => { setResponse('form submission start') }) }, }), [] ) return ( <ActionResponse response={response}> <button onClick={() => { form.submit() }} > Submit </button> </ActionResponse> ) } ``` ## onFormSubmitEnd #### Description Side effect hook for monitoring the end of form submission #### Signature ```ts interface onFormSubmitEnd { (callback: (form: Form) => void) } ``` #### Example ```tsx import React, { useMemo, useState } from 'react' import { createForm, onFormSubmitEnd } from '@formily/core' import { ActionResponse } from './ActionResponse' export default () => { const [response, setResponse] = useState('') const form = useMemo( () => createForm({ effects() { onFormSubmitEnd(() => { setResponse('End of form submission') }) }, }), [] ) return ( <ActionResponse response={response}> <button onClick={() => { form.submit() }} > Submit </button> </ActionResponse> ) } ``` ## onFormSubmitFailed #### Description Side-effect hooks used to monitor form submission failures #### Signature ```ts interface onFormSubmitFailed { (callback: (form: Form) => void) } ``` #### Example ```tsx import React, { useMemo, useState } from 'react' import { createForm, onFormSubmitFailed } from '@formily/core' import { ActionResponse } from './ActionResponse' export default () => { const [response, setResponse] = useState('') const form = useMemo( () => createForm({ effects() { onFormSubmitFailed(() => { setResponse('Form submission failed') }) }, }), [] ) const form2 = useMemo( () => createForm({ effects() { onFormSubmitFailed(() => { setResponse('Form verification failed') }) }, }), [] ) return ( <ActionResponse response={response}> <button onClick={() => { form.submit(() => { return Promise.reject('Runtime Error') }) }} > Submit Runtime Error </button> <button onClick={() => { form2.createField({ name: 'input', required: true, }) form2.submit() }} > Submit Validate Error </button> </ActionResponse> ) } ``` ## onFormSubmitSuccess #### Description Side effect hook used to monitor the success of form submission #### Signature ```ts interface onFormSubmitSuccess { (callback: (form: Form) => void) } ``` #### Example ```tsx import React, { useMemo, useState } from 'react' import { createForm, onFormSubmitSuccess } from '@formily/core' import { ActionResponse } from './ActionResponse' export default () => { const [response, setResponse] = useState('') const form = useMemo( () => createForm({ effects() { onFormSubmitSuccess(() => { setResponse('Form submission is successful') }) }, }), [] ) return ( <ActionResponse response={response}> <button onClick={() => { form.submit() }} > Submit </button> </ActionResponse> ) } ``` ## onFormSubmitValidateStart #### Description Side effect hook used to monitor the start of field validation of the form submission process #### Signature ```ts interface onFormSubmitValidateStart { (callback: (form: Form) => void) } ``` #### Example ```tsx import React, { useMemo, useState } from 'react' import { createForm, onFormSubmitValidateStart } from '@formily/core' import { ActionResponse } from './ActionResponse' export default () => { const [response, setResponse] = useState('') const form = useMemo( () => createForm({ effects() { onFormSubmitValidateStart(() => { setResponse('Form submission verification starts') }) }, }), [] ) return ( <ActionResponse response={response}> <button onClick={() => { form.createField({ name: 'input', required: true, }) form.submit() }} > Submit </button> </ActionResponse> ) } ``` ## onFormSubmitValidateEnd #### Description Side effect hook used to monitor the end of the field validation of the form submission process #### Signature ```ts interface onFormSubmitValidateEnd { (callback: (form: Form) => void) } ``` #### Example ```tsx import React, { useMemo, useState } from 'react' import { createForm, onFormSubmitValidateEnd } from '@formily/core' import { ActionResponse } from './ActionResponse' export default () => { const [response, setResponse] = useState('') const form = useMemo( () => createForm({ effects() { onFormSubmitValidateEnd(() => { setResponse('Form submission verification is over') }) }, }), [] ) return ( <ActionResponse response={response}> <button onClick={() => { form.createField({ name: 'input', required: true, }) form.submit() }} > Submit </button> </ActionResponse> ) } ``` ## onFormSubmitValidateFailed #### Description Side effect hook used to monitor the field validation failure of the form submission process #### Signature ```ts interface onFormSubmitValidateFailed { (callback: (form: Form) => void) } ``` #### Example ```tsx import React, { useMemo, useState } from 'react' import { createForm, onFormSubmitValidateFailed } from '@formily/core' import { ActionResponse } from './ActionResponse' export default () => { const [response, setResponse] = useState('') const form = useMemo( () => createForm({ effects() { onFormSubmitValidateFailed(() => { setResponse('Form submission verification failed') }) }, }), [] ) return ( <ActionResponse response={response}> <button onClick={() => { form.createField({ name: 'input', required: true, }) form.submit() }} > Submit </button> </ActionResponse> ) } ``` ## onFormSubmitValidateSuccess #### Description Side-effect hook used to monitor the successful field verification of the form submission process #### Signature ```ts interface onFormSubmitValidateSuccess { (callback: (form: Form) => void) } ``` #### Example ```tsx import React, { useMemo, useState } from 'react' import { createForm, onFormSubmitValidateSuccess } from '@formily/core' import { ActionResponse } from './ActionResponse' export default () => { const [response, setResponse] = useState('') const form = useMemo( () => createForm({ effects() { onFormSubmitValidateSuccess(() => { setResponse('Form submission verification succeeded') }) }, }), [] ) return ( <ActionResponse response={response}> <button onClick={() => { form.createField({ name: 'input', }) form.submit() }} > Submit </button> </ActionResponse> ) } ``` ## onFormValidateStart #### Description Side effect hook for monitoring the start of form validation #### Signature ```ts interface onFormValidateStart { (callback: (form: Form) => void) } ``` #### Example ```tsx import React, { useMemo, useState } from 'react' import { createForm, onFormValidateStart } from '@formily/core' import { ActionResponse } from './ActionResponse' export default () => { const [response, setResponse] = useState('') const form = useMemo( () => createForm({ effects() { onFormValidateStart(() => { setResponse('Form verification starts') }) }, }), [] ) return ( <ActionResponse response={response}> <button onClick={() => { form.createField({ name: 'input', required: true, }) form.validate() }} > Submit </button> </ActionResponse> ) } ``` ## onFormValidateEnd #### Description Side effect hook for monitoring the end of form validation #### Signature ```ts interface onFormValidateEnd { (callback: (form: Form) => void) } ``` #### Example ```tsx import React, { useMemo, useState } from 'react' import { createForm, onFormValidateEnd } from '@formily/core' import { ActionResponse } from './ActionResponse' export default () => { const [response, setResponse] = useState('') const form = useMemo( () => createForm({ effects() { onFormValidateEnd(() => { setResponse('Form verification end') }) }, }), [] ) return ( <ActionResponse response={response}> <button onClick={() => { form.createField({ name: 'input', required: true, }) form.validate() }} > Submit </button> </ActionResponse> ) } ``` ## onFormValidateFailed #### Description Side-effect hooks used to monitor form validation failures #### Signature ```ts interface onFormValidateFailed { (callback: (form: Form) => void) } ``` #### Example ```tsx import React, { useMemo, useState } from 'react' import { createForm, onFormValidateFailed } from '@formily/core' import { ActionResponse } from './ActionResponse' export default () => { const [response, setResponse] = useState('') const form = useMemo( () => createForm({ effects() { onFormValidateFailed(() => { setResponse('Form verification failed') }) }, }), [] ) return ( <ActionResponse response={response}> <button onClick={() => { form.createField({ name: 'input', required: true, }) form.validate() }} > Submit </button> </ActionResponse> ) } ``` ## onFormValidateSuccess #### Description Side effect hook for monitoring the start of form validation #### Signature ```ts interface onFormValidateSuccess { (callback: (form: Form) => void) } ``` #### Example ```tsx import React, { useMemo, useState } from 'react' import { createForm, onFormValidateSuccess } from '@formily/core' import { ActionResponse } from './ActionResponse' export default () => { const [response, setResponse] = useState('') const form = useMemo( () => createForm({ effects() { onFormValidateSuccess(() => { setResponse('Form verification succeeded') }) }, }), [] ) return ( <ActionResponse response={response}> <button onClick={() => { form.createField({ name: 'input', }) form.validate() }} > Submit </button> </ActionResponse> ) } ``` ```