This is page 50 of 52. Use http://codebase.md/alibaba/formily?lines=true&page={x} to view the full context. # Directory Structure ``` ├── .all-contributorsrc ├── .codecov.yml ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .github │ ├── CONTRIBUTING.md │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE │ │ └── config.yml │ ├── PULL_REQUEST_TEMPLATE.md │ └── workflows │ ├── check-pr-title.yml │ ├── ci.yml │ ├── commitlint.yml │ ├── issue-open-check.yml │ ├── package-size.yml │ └── pr-welcome.yml ├── .gitignore ├── .prettierrc.js ├── .umirc.js ├── .vscode │ └── cspell.json ├── .yarnrc ├── CHANGELOG.md ├── commitlint.config.js ├── devtools │ ├── .eslintrc │ └── chrome-extension │ ├── .npmignore │ ├── assets │ │ └── img │ │ ├── loading.svg │ │ └── logo │ │ ├── 128x128.png │ │ ├── 16x16.png │ │ ├── 38x38.png │ │ ├── 48x48.png │ │ ├── error.png │ │ ├── gray.png │ │ └── scalable.png │ ├── config │ │ ├── webpack.base.ts │ │ ├── webpack.dev.ts │ │ └── webpack.prod.ts │ ├── LICENSE.md │ ├── package.json │ ├── src │ │ ├── app │ │ │ ├── components │ │ │ │ ├── FieldTree.tsx │ │ │ │ ├── filter.ts │ │ │ │ ├── LeftPanel.tsx │ │ │ │ ├── RightPanel.tsx │ │ │ │ ├── SearchBox.tsx │ │ │ │ └── Tabs.tsx │ │ │ ├── demo.tsx │ │ │ └── index.tsx │ │ └── extension │ │ ├── backend.ts │ │ ├── background.ts │ │ ├── content.ts │ │ ├── devpanel.tsx │ │ ├── devtools.tsx │ │ ├── inject.ts │ │ ├── manifest.json │ │ ├── popup.tsx │ │ └── views │ │ ├── devpanel.ejs │ │ ├── devtools.ejs │ │ └── popup.ejs │ ├── tsconfig.build.json │ └── tsconfig.json ├── docs │ ├── functions │ │ ├── contributors.ts │ │ └── npm-search.ts │ ├── guide │ │ ├── advanced │ │ │ ├── async.md │ │ │ ├── async.zh-CN.md │ │ │ ├── build.md │ │ │ ├── build.zh-CN.md │ │ │ ├── business-logic.md │ │ │ ├── business-logic.zh-CN.md │ │ │ ├── calculator.md │ │ │ ├── calculator.zh-CN.md │ │ │ ├── controlled.md │ │ │ ├── controlled.zh-CN.md │ │ │ ├── custom.md │ │ │ ├── custom.zh-CN.md │ │ │ ├── destructor.md │ │ │ ├── destructor.zh-CN.md │ │ │ ├── input.less │ │ │ ├── layout.md │ │ │ ├── layout.zh-CN.md │ │ │ ├── linkages.md │ │ │ ├── linkages.zh-CN.md │ │ │ ├── validate.md │ │ │ └── validate.zh-CN.md │ │ ├── contribution.md │ │ ├── contribution.zh-CN.md │ │ ├── form-builder.md │ │ ├── form-builder.zh-CN.md │ │ ├── index.md │ │ ├── index.zh-CN.md │ │ ├── issue-helper.md │ │ ├── issue-helper.zh-CN.md │ │ ├── learn-formily.md │ │ ├── learn-formily.zh-CN.md │ │ ├── quick-start.md │ │ ├── quick-start.zh-CN.md │ │ ├── scenes │ │ │ ├── dialog-drawer.md │ │ │ ├── dialog-drawer.zh-CN.md │ │ │ ├── edit-detail.md │ │ │ ├── edit-detail.zh-CN.md │ │ │ ├── index.less │ │ │ ├── login-register.md │ │ │ ├── login-register.zh-CN.md │ │ │ ├── more.md │ │ │ ├── more.zh-CN.md │ │ │ ├── query-list.md │ │ │ ├── query-list.zh-CN.md │ │ │ ├── step-form.md │ │ │ ├── step-form.zh-CN.md │ │ │ ├── tab-form.md │ │ │ ├── tab-form.zh-CN.md │ │ │ └── VerifyCode.tsx │ │ ├── upgrade.md │ │ └── upgrade.zh-CN.md │ ├── index.md │ ├── index.zh-CN.md │ └── site │ ├── Contributors.less │ ├── Contributors.tsx │ ├── QrCode.less │ ├── QrCode.tsx │ ├── Section.less │ ├── Section.tsx │ └── styles.less ├── global.config.ts ├── jest.config.js ├── lerna.json ├── LICENSE.md ├── package.json ├── packages │ ├── .eslintrc │ ├── antd │ │ ├── __tests__ │ │ │ ├── moment.spec.ts │ │ │ └── sideEffects.spec.ts │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── build-style.ts │ │ ├── create-style.ts │ │ ├── docs │ │ │ ├── components │ │ │ │ ├── ArrayCards.md │ │ │ │ ├── ArrayCards.zh-CN.md │ │ │ │ ├── ArrayCollapse.md │ │ │ │ ├── ArrayCollapse.zh-CN.md │ │ │ │ ├── ArrayItems.md │ │ │ │ ├── ArrayItems.zh-CN.md │ │ │ │ ├── ArrayTable.md │ │ │ │ ├── ArrayTable.zh-CN.md │ │ │ │ ├── ArrayTabs.md │ │ │ │ ├── ArrayTabs.zh-CN.md │ │ │ │ ├── Cascader.md │ │ │ │ ├── Cascader.zh-CN.md │ │ │ │ ├── Checkbox.md │ │ │ │ ├── Checkbox.zh-CN.md │ │ │ │ ├── DatePicker.md │ │ │ │ ├── DatePicker.zh-CN.md │ │ │ │ ├── Editable.md │ │ │ │ ├── Editable.zh-CN.md │ │ │ │ ├── Form.md │ │ │ │ ├── Form.zh-CN.md │ │ │ │ ├── FormButtonGroup.md │ │ │ │ ├── FormButtonGroup.zh-CN.md │ │ │ │ ├── FormCollapse.md │ │ │ │ ├── FormCollapse.zh-CN.md │ │ │ │ ├── FormDialog.md │ │ │ │ ├── FormDialog.zh-CN.md │ │ │ │ ├── FormDrawer.md │ │ │ │ ├── FormDrawer.zh-CN.md │ │ │ │ ├── FormGrid.md │ │ │ │ ├── FormGrid.zh-CN.md │ │ │ │ ├── FormItem.md │ │ │ │ ├── FormItem.zh-CN.md │ │ │ │ ├── FormLayout.md │ │ │ │ ├── FormLayout.zh-CN.md │ │ │ │ ├── FormStep.md │ │ │ │ ├── FormStep.zh-CN.md │ │ │ │ ├── FormTab.md │ │ │ │ ├── FormTab.zh-CN.md │ │ │ │ ├── index.md │ │ │ │ ├── index.zh-CN.md │ │ │ │ ├── Input.md │ │ │ │ ├── Input.zh-CN.md │ │ │ │ ├── NumberPicker.md │ │ │ │ ├── NumberPicker.zh-CN.md │ │ │ │ ├── Password.md │ │ │ │ ├── Password.zh-CN.md │ │ │ │ ├── PreviewText.md │ │ │ │ ├── PreviewText.zh-CN.md │ │ │ │ ├── Radio.md │ │ │ │ ├── Radio.zh-CN.md │ │ │ │ ├── Reset.md │ │ │ │ ├── Reset.zh-CN.md │ │ │ │ ├── Select.md │ │ │ │ ├── Select.zh-CN.md │ │ │ │ ├── SelectTable.md │ │ │ │ ├── SelectTable.zh-CN.md │ │ │ │ ├── Space.md │ │ │ │ ├── Space.zh-CN.md │ │ │ │ ├── Submit.md │ │ │ │ ├── Submit.zh-CN.md │ │ │ │ ├── Switch.md │ │ │ │ ├── Switch.zh-CN.md │ │ │ │ ├── TimePicker.md │ │ │ │ ├── TimePicker.zh-CN.md │ │ │ │ ├── Transfer.md │ │ │ │ ├── Transfer.zh-CN.md │ │ │ │ ├── TreeSelect.md │ │ │ │ ├── TreeSelect.zh-CN.md │ │ │ │ ├── Upload.md │ │ │ │ └── Upload.zh-CN.md │ │ │ ├── index.md │ │ │ └── index.zh-CN.md │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __builtins__ │ │ │ │ ├── hooks │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── useClickAway.ts │ │ │ │ │ └── usePrefixCls.ts │ │ │ │ ├── index.ts │ │ │ │ ├── loading.ts │ │ │ │ ├── moment.ts │ │ │ │ ├── pickDataProps.ts │ │ │ │ ├── portal.tsx │ │ │ │ ├── render.ts │ │ │ │ └── sort.tsx │ │ │ ├── array-base │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── array-cards │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── array-collapse │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── array-items │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── array-table │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── array-tabs │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── cascader │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── checkbox │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── date-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── editable │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── form │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── form-button-group │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── form-collapse │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-dialog │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-drawer │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-grid │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── form-item │ │ │ │ ├── animation.less │ │ │ │ ├── grid.less │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── form-layout │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ ├── style.ts │ │ │ │ └── useResponsiveFormLayout.ts │ │ │ ├── form-step │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-tab │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── index.ts │ │ │ ├── input │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── number-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── password │ │ │ │ ├── index.tsx │ │ │ │ ├── PasswordStrength.tsx │ │ │ │ └── style.ts │ │ │ ├── preview-text │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── radio │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── reset │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── select │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── select-table │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ ├── style.ts │ │ │ │ ├── useCheckSlackly.tsx │ │ │ │ ├── useFilterOptions.tsx │ │ │ │ ├── useFlatOptions.tsx │ │ │ │ ├── useSize.tsx │ │ │ │ ├── useTitleAddon.tsx │ │ │ │ └── utils.ts │ │ │ ├── space │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── style.less │ │ │ ├── style.ts │ │ │ ├── submit │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── switch │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── time-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── transfer │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── tree-select │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ └── upload │ │ │ ├── index.tsx │ │ │ ├── placeholder.ts │ │ │ └── style.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── benchmark │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── src │ │ │ └── index.tsx │ │ ├── template.ejs │ │ ├── tsconfig.build.json │ │ ├── tsconfig.json │ │ ├── webpack.base.ts │ │ ├── webpack.dev.ts │ │ └── webpack.prod.ts │ ├── core │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── docs │ │ │ ├── api │ │ │ │ ├── entry │ │ │ │ │ ├── ActionResponse.less │ │ │ │ │ ├── ActionResponse.tsx │ │ │ │ │ ├── createForm.md │ │ │ │ │ ├── createForm.zh-CN.md │ │ │ │ │ ├── FieldEffectHooks.md │ │ │ │ │ ├── FieldEffectHooks.zh-CN.md │ │ │ │ │ ├── FormChecker.md │ │ │ │ │ ├── FormChecker.zh-CN.md │ │ │ │ │ ├── FormEffectHooks.md │ │ │ │ │ ├── FormEffectHooks.zh-CN.md │ │ │ │ │ ├── FormHooksAPI.md │ │ │ │ │ ├── FormHooksAPI.zh-CN.md │ │ │ │ │ ├── FormPath.md │ │ │ │ │ ├── FormPath.zh-CN.md │ │ │ │ │ ├── FormValidatorRegistry.md │ │ │ │ │ └── FormValidatorRegistry.zh-CN.md │ │ │ │ └── models │ │ │ │ ├── ArrayField.md │ │ │ │ ├── ArrayField.zh-CN.md │ │ │ │ ├── Field.md │ │ │ │ ├── Field.zh-CN.md │ │ │ │ ├── Form.md │ │ │ │ ├── Form.zh-CN.md │ │ │ │ ├── ObjectField.md │ │ │ │ ├── ObjectField.zh-CN.md │ │ │ │ ├── Query.md │ │ │ │ ├── Query.zh-CN.md │ │ │ │ ├── VoidField.md │ │ │ │ └── VoidField.zh-CN.md │ │ │ ├── guide │ │ │ │ ├── architecture.md │ │ │ │ ├── architecture.zh-CN.md │ │ │ │ ├── field.md │ │ │ │ ├── field.zh-CN.md │ │ │ │ ├── form.md │ │ │ │ ├── form.zh-CN.md │ │ │ │ ├── index.md │ │ │ │ ├── index.zh-CN.md │ │ │ │ ├── mvvm.md │ │ │ │ ├── mvvm.zh-CN.md │ │ │ │ ├── values.md │ │ │ │ └── values.zh-CN.md │ │ │ ├── index.md │ │ │ └── index.zh-CN.md │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── array.spec.ts │ │ │ │ ├── effects.spec.ts │ │ │ │ ├── externals.spec.ts │ │ │ │ ├── field.spec.ts │ │ │ │ ├── form.spec.ts │ │ │ │ ├── graph.spec.ts │ │ │ │ ├── heart.spec.ts │ │ │ │ ├── internals.spec.ts │ │ │ │ ├── lifecycle.spec.ts │ │ │ │ ├── object.spec.ts │ │ │ │ ├── shared.ts │ │ │ │ └── void.spec.ts │ │ │ ├── effects │ │ │ │ ├── index.ts │ │ │ │ ├── onFieldEffects.ts │ │ │ │ └── onFormEffects.ts │ │ │ ├── global.d.ts │ │ │ ├── index.ts │ │ │ ├── models │ │ │ │ ├── ArrayField.ts │ │ │ │ ├── BaseField.ts │ │ │ │ ├── Field.ts │ │ │ │ ├── Form.ts │ │ │ │ ├── Graph.ts │ │ │ │ ├── Heart.ts │ │ │ │ ├── index.ts │ │ │ │ ├── LifeCycle.ts │ │ │ │ ├── ObjectField.ts │ │ │ │ ├── Query.ts │ │ │ │ ├── types.ts │ │ │ │ └── VoidField.ts │ │ │ ├── shared │ │ │ │ ├── checkers.ts │ │ │ │ ├── constants.ts │ │ │ │ ├── effective.ts │ │ │ │ ├── externals.ts │ │ │ │ └── internals.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── element │ │ ├── .npmignore │ │ ├── build-style.ts │ │ ├── create-style.ts │ │ ├── docs │ │ │ ├── .vuepress │ │ │ │ ├── components │ │ │ │ │ ├── createCodeSandBox.js │ │ │ │ │ ├── dumi-previewer.vue │ │ │ │ │ └── highlight.js │ │ │ │ ├── config.js │ │ │ │ ├── enhanceApp.js │ │ │ │ ├── styles │ │ │ │ │ └── index.styl │ │ │ │ └── util.js │ │ │ ├── demos │ │ │ │ ├── guide │ │ │ │ │ ├── array-cards │ │ │ │ │ │ ├── effects-json-schema.vue │ │ │ │ │ │ ├── effects-markup-schema.vue │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── array-collapse │ │ │ │ │ │ ├── effects-json-schema.vue │ │ │ │ │ │ ├── effects-markup-schema.vue │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── array-items │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── array-table │ │ │ │ │ │ ├── effects-json-schema.vue │ │ │ │ │ │ ├── effects-markup-schema.vue │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── array-tabs │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── cascader │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── checkbox │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── date-picker │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── editable │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── form-button-group.vue │ │ │ │ │ ├── form-collapse │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── form-dialog │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── form-drawer │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── form-grid │ │ │ │ │ │ ├── form.vue │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── native.vue │ │ │ │ │ ├── form-item │ │ │ │ │ │ ├── bordered-none.vue │ │ │ │ │ │ ├── common.vue │ │ │ │ │ │ ├── feedback.vue │ │ │ │ │ │ ├── inset.vue │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ ├── size.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── form-layout │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── form-step │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── form-tab │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── form.vue │ │ │ │ │ ├── input │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── input-number │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── password │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── preview-text │ │ │ │ │ │ ├── base.vue │ │ │ │ │ │ └── extend.vue │ │ │ │ │ ├── radio │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── reset │ │ │ │ │ │ ├── base.vue │ │ │ │ │ │ ├── force.vue │ │ │ │ │ │ └── validate.vue │ │ │ │ │ ├── select │ │ │ │ │ │ ├── json-schema-async.vue │ │ │ │ │ │ ├── json-schema-sync.vue │ │ │ │ │ │ ├── markup-schema-async-search.vue │ │ │ │ │ │ ├── markup-schema-async.vue │ │ │ │ │ │ ├── markup-schema-sync.vue │ │ │ │ │ │ ├── template-async.vue │ │ │ │ │ │ └── template-sync.vue │ │ │ │ │ ├── space │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── submit │ │ │ │ │ │ ├── base.vue │ │ │ │ │ │ └── loading.vue │ │ │ │ │ ├── switch │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── time-picker │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── transfer │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ └── upload │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ └── template.vue │ │ │ │ └── index.vue │ │ │ ├── guide │ │ │ │ ├── array-cards.md │ │ │ │ ├── array-collapse.md │ │ │ │ ├── array-items.md │ │ │ │ ├── array-table.md │ │ │ │ ├── array-tabs.md │ │ │ │ ├── cascader.md │ │ │ │ ├── checkbox.md │ │ │ │ ├── date-picker.md │ │ │ │ ├── editable.md │ │ │ │ ├── form-button-group.md │ │ │ │ ├── form-collapse.md │ │ │ │ ├── form-dialog.md │ │ │ │ ├── form-drawer.md │ │ │ │ ├── form-grid.md │ │ │ │ ├── form-item.md │ │ │ │ ├── form-layout.md │ │ │ │ ├── form-step.md │ │ │ │ ├── form-tab.md │ │ │ │ ├── form.md │ │ │ │ ├── index.md │ │ │ │ ├── input-number.md │ │ │ │ ├── input.md │ │ │ │ ├── password.md │ │ │ │ ├── preview-text.md │ │ │ │ ├── radio.md │ │ │ │ ├── reset.md │ │ │ │ ├── select.md │ │ │ │ ├── space.md │ │ │ │ ├── submit.md │ │ │ │ ├── switch.md │ │ │ │ ├── time-picker.md │ │ │ │ ├── transfer.md │ │ │ │ └── upload.md │ │ │ └── README.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __builtins__ │ │ │ │ ├── configs │ │ │ │ │ └── index.ts │ │ │ │ ├── index.ts │ │ │ │ ├── shared │ │ │ │ │ ├── create-context.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── loading.ts │ │ │ │ │ ├── portal.ts │ │ │ │ │ ├── resolve-component.ts │ │ │ │ │ ├── transform-component.ts │ │ │ │ │ ├── types.ts │ │ │ │ │ └── utils.ts │ │ │ │ └── styles │ │ │ │ └── common.scss │ │ │ ├── array-base │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── array-cards │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── array-collapse │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── array-items │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── array-table │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── array-tabs │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── cascader │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── checkbox │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── date-picker │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── editable │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── el-form │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── el-form-item │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── form │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── form-button-group │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── form-collapse │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── form-dialog │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── form-drawer │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── form-grid │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── form-item │ │ │ │ ├── animation.scss │ │ │ │ ├── grid.scss │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ ├── style.ts │ │ │ │ └── var.scss │ │ │ ├── form-layout │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ ├── style.ts │ │ │ │ └── useResponsiveFormLayout.ts │ │ │ ├── form-step │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── form-tab │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── index.ts │ │ │ ├── input │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── input-number │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── password │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── preview-text │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── radio │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── reset │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── select │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── space │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── style.ts │ │ │ ├── submit │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── switch │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── time-picker │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── transfer │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ └── upload │ │ │ ├── index.ts │ │ │ └── style.ts │ │ ├── transformer.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── grid │ │ ├── .npmignore │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── index.ts │ │ │ └── observer.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── json-schema │ │ ├── .npmignore │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── __snapshots__ │ │ │ │ │ └── schema.spec.ts.snap │ │ │ │ ├── compiler.spec.ts │ │ │ │ ├── patches.spec.ts │ │ │ │ ├── schema.spec.ts │ │ │ │ ├── server-validate.spec.ts │ │ │ │ ├── shared.spec.ts │ │ │ │ ├── transformer.spec.ts │ │ │ │ └── traverse.spec.ts │ │ │ ├── compiler.ts │ │ │ ├── global.d.ts │ │ │ ├── index.ts │ │ │ ├── patches.ts │ │ │ ├── polyfills │ │ │ │ ├── index.ts │ │ │ │ └── SPECIFICATION_1_0.ts │ │ │ ├── schema.ts │ │ │ ├── shared.ts │ │ │ ├── transformer.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── next │ │ ├── __tests__ │ │ │ ├── moment.spec.ts │ │ │ └── sideEffects.spec.ts │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── build-style.ts │ │ ├── create-style.ts │ │ ├── docs │ │ │ ├── components │ │ │ │ ├── ArrayCards.md │ │ │ │ ├── ArrayCards.zh-CN.md │ │ │ │ ├── ArrayCollapse.md │ │ │ │ ├── ArrayCollapse.zh-CN.md │ │ │ │ ├── ArrayItems.md │ │ │ │ ├── ArrayItems.zh-CN.md │ │ │ │ ├── ArrayTable.md │ │ │ │ ├── ArrayTable.zh-CN.md │ │ │ │ ├── Cascader.md │ │ │ │ ├── Cascader.zh-CN.md │ │ │ │ ├── Checkbox.md │ │ │ │ ├── Checkbox.zh-CN.md │ │ │ │ ├── DatePicker.md │ │ │ │ ├── DatePicker.zh-CN.md │ │ │ │ ├── DatePicker2.md │ │ │ │ ├── DatePicker2.zh-CN.md │ │ │ │ ├── Editable.md │ │ │ │ ├── Editable.zh-CN.md │ │ │ │ ├── Form.md │ │ │ │ ├── Form.zh-CN.md │ │ │ │ ├── FormButtonGroup.md │ │ │ │ ├── FormButtonGroup.zh-CN.md │ │ │ │ ├── FormCollapse.md │ │ │ │ ├── FormCollapse.zh-CN.md │ │ │ │ ├── FormDialog.md │ │ │ │ ├── FormDialog.zh-CN.md │ │ │ │ ├── FormDrawer.md │ │ │ │ ├── FormDrawer.zh-CN.md │ │ │ │ ├── FormGrid.md │ │ │ │ ├── FormGrid.zh-CN.md │ │ │ │ ├── FormItem.md │ │ │ │ ├── FormItem.zh-CN.md │ │ │ │ ├── FormLayout.md │ │ │ │ ├── FormLayout.zh-CN.md │ │ │ │ ├── FormStep.md │ │ │ │ ├── FormStep.zh-CN.md │ │ │ │ ├── FormTab.md │ │ │ │ ├── FormTab.zh-CN.md │ │ │ │ ├── index.md │ │ │ │ ├── index.zh-CN.md │ │ │ │ ├── Input.md │ │ │ │ ├── Input.zh-CN.md │ │ │ │ ├── NumberPicker.md │ │ │ │ ├── NumberPicker.zh-CN.md │ │ │ │ ├── Password.md │ │ │ │ ├── Password.zh-CN.md │ │ │ │ ├── PreviewText.md │ │ │ │ ├── PreviewText.zh-CN.md │ │ │ │ ├── Radio.md │ │ │ │ ├── Radio.zh-CN.md │ │ │ │ ├── Reset.md │ │ │ │ ├── Reset.zh-CN.md │ │ │ │ ├── Select.md │ │ │ │ ├── Select.zh-CN.md │ │ │ │ ├── SelectTable.md │ │ │ │ ├── SelectTable.zh-CN.md │ │ │ │ ├── Space.md │ │ │ │ ├── Space.zh-CN.md │ │ │ │ ├── Submit.md │ │ │ │ ├── Submit.zh-CN.md │ │ │ │ ├── Switch.md │ │ │ │ ├── Switch.zh-CN.md │ │ │ │ ├── TimePicker.md │ │ │ │ ├── TimePicker.zh-CN.md │ │ │ │ ├── TimePicker2.md │ │ │ │ ├── TimePicker2.zh-CN.md │ │ │ │ ├── Transfer.md │ │ │ │ ├── Transfer.zh-CN.md │ │ │ │ ├── TreeSelect.md │ │ │ │ ├── TreeSelect.zh-CN.md │ │ │ │ ├── Upload.md │ │ │ │ └── Upload.zh-CN.md │ │ │ ├── index.md │ │ │ └── index.zh-CN.md │ │ ├── LESENCE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __builtins__ │ │ │ │ ├── empty.tsx │ │ │ │ ├── hooks │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── useClickAway.ts │ │ │ │ │ └── usePrefixCls.ts │ │ │ │ ├── icons.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── loading.ts │ │ │ │ ├── mapSize.ts │ │ │ │ ├── mapStatus.ts │ │ │ │ ├── moment.ts │ │ │ │ ├── pickDataProps.ts │ │ │ │ ├── portal.tsx │ │ │ │ ├── render.ts │ │ │ │ └── toArray.ts │ │ │ ├── array-base │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── array-cards │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── array-collapse │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── array-items │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── array-table │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── cascader │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── checkbox │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── date-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── date-picker2 │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── editable │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── form │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── form-button-group │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── form-collapse │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-dialog │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-drawer │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-grid │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── form-item │ │ │ │ ├── animation.scss │ │ │ │ ├── grid.scss │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ ├── scss │ │ │ │ │ └── variable.scss │ │ │ │ └── style.ts │ │ │ ├── form-layout │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ ├── style.ts │ │ │ │ └── useResponsiveFormLayout.ts │ │ │ ├── form-step │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-tab │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── index.ts │ │ │ ├── input │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── main.scss │ │ │ ├── number-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── password │ │ │ │ ├── index.tsx │ │ │ │ ├── PasswordStrength.tsx │ │ │ │ └── style.ts │ │ │ ├── preview-text │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── radio │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── reset │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── select │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── select-table │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ ├── style.ts │ │ │ │ ├── useCheckSlackly.tsx │ │ │ │ ├── useFilterOptions.tsx │ │ │ │ ├── useFlatOptions.tsx │ │ │ │ ├── useSize.tsx │ │ │ │ ├── useTitleAddon.tsx │ │ │ │ └── utils.ts │ │ │ ├── space │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── style.ts │ │ │ ├── submit │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── switch │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── time-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── time-picker2 │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── transfer │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── tree-select │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ └── upload │ │ │ ├── index.tsx │ │ │ ├── main.scss │ │ │ ├── placeholder.ts │ │ │ └── style.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── path │ │ ├── .npmignore │ │ ├── benchmark.ts │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── accessor.spec.ts │ │ │ │ ├── basic.spec.ts │ │ │ │ ├── match.spec.ts │ │ │ │ ├── parser.spec.ts │ │ │ │ └── share.spec.ts │ │ │ ├── contexts.ts │ │ │ ├── destructor.ts │ │ │ ├── index.ts │ │ │ ├── matcher.ts │ │ │ ├── parser.ts │ │ │ ├── shared.ts │ │ │ ├── tokenizer.ts │ │ │ ├── tokens.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── react │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── docs │ │ │ ├── api │ │ │ │ ├── components │ │ │ │ │ ├── ArrayField.md │ │ │ │ │ ├── ArrayField.zh-CN.md │ │ │ │ │ ├── ExpressionScope.md │ │ │ │ │ ├── ExpressionScope.zh-CN.md │ │ │ │ │ ├── Field.md │ │ │ │ │ ├── Field.zh-CN.md │ │ │ │ │ ├── FormConsumer.md │ │ │ │ │ ├── FormConsumer.zh-CN.md │ │ │ │ │ ├── FormProvider.md │ │ │ │ │ ├── FormProvider.zh-CN.md │ │ │ │ │ ├── ObjectField.md │ │ │ │ │ ├── ObjectField.zh-CN.md │ │ │ │ │ ├── RecordScope.md │ │ │ │ │ ├── RecordScope.zh-CN.md │ │ │ │ │ ├── RecordsScope.md │ │ │ │ │ ├── RecordsScope.zh-CN.md │ │ │ │ │ ├── RecursionField.md │ │ │ │ │ ├── RecursionField.zh-CN.md │ │ │ │ │ ├── SchemaField.md │ │ │ │ │ ├── SchemaField.zh-CN.md │ │ │ │ │ ├── VoidField.md │ │ │ │ │ └── VoidField.zh-CN.md │ │ │ │ ├── hooks │ │ │ │ │ ├── useExpressionScope.md │ │ │ │ │ ├── useExpressionScope.zh-CN.md │ │ │ │ │ ├── useField.md │ │ │ │ │ ├── useField.zh-CN.md │ │ │ │ │ ├── useFieldSchema.md │ │ │ │ │ ├── useFieldSchema.zh-CN.md │ │ │ │ │ ├── useForm.md │ │ │ │ │ ├── useForm.zh-CN.md │ │ │ │ │ ├── useFormEffects.md │ │ │ │ │ ├── useFormEffects.zh-CN.md │ │ │ │ │ ├── useParentForm.md │ │ │ │ │ └── useParentForm.zh-CN.md │ │ │ │ └── shared │ │ │ │ ├── connect.md │ │ │ │ ├── connect.zh-CN.md │ │ │ │ ├── context.md │ │ │ │ ├── context.zh-CN.md │ │ │ │ ├── mapProps.md │ │ │ │ ├── mapProps.zh-CN.md │ │ │ │ ├── mapReadPretty.md │ │ │ │ ├── mapReadPretty.zh-CN.md │ │ │ │ ├── observer.md │ │ │ │ ├── observer.zh-CN.md │ │ │ │ ├── Schema.md │ │ │ │ └── Schema.zh-CN.md │ │ │ ├── guide │ │ │ │ ├── architecture.md │ │ │ │ ├── architecture.zh-CN.md │ │ │ │ ├── concept.md │ │ │ │ ├── concept.zh-CN.md │ │ │ │ ├── index.md │ │ │ │ └── index.zh-CN.md │ │ │ ├── index.md │ │ │ └── index.zh-CN.md │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── expression.spec.tsx │ │ │ │ ├── field.spec.tsx │ │ │ │ ├── form.spec.tsx │ │ │ │ ├── schema.json.spec.tsx │ │ │ │ ├── schema.markup.spec.tsx │ │ │ │ └── shared.tsx │ │ │ ├── components │ │ │ │ ├── ArrayField.tsx │ │ │ │ ├── ExpressionScope.tsx │ │ │ │ ├── Field.tsx │ │ │ │ ├── FormConsumer.tsx │ │ │ │ ├── FormProvider.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── ObjectField.tsx │ │ │ │ ├── ReactiveField.tsx │ │ │ │ ├── RecordScope.tsx │ │ │ │ ├── RecordsScope.tsx │ │ │ │ ├── RecursionField.tsx │ │ │ │ ├── SchemaField.tsx │ │ │ │ └── VoidField.tsx │ │ │ ├── global.d.ts │ │ │ ├── hooks │ │ │ │ ├── index.ts │ │ │ │ ├── useAttach.ts │ │ │ │ ├── useExpressionScope.ts │ │ │ │ ├── useField.ts │ │ │ │ ├── useFieldSchema.ts │ │ │ │ ├── useForm.ts │ │ │ │ ├── useFormEffects.ts │ │ │ │ └── useParentForm.ts │ │ │ ├── index.ts │ │ │ ├── shared │ │ │ │ ├── connect.ts │ │ │ │ ├── context.ts │ │ │ │ ├── index.ts │ │ │ │ └── render.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── reactive │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── benchmark.ts │ │ ├── docs │ │ │ ├── api │ │ │ │ ├── action.md │ │ │ │ ├── action.zh-CN.md │ │ │ │ ├── autorun.md │ │ │ │ ├── autorun.zh-CN.md │ │ │ │ ├── batch.md │ │ │ │ ├── batch.zh-CN.md │ │ │ │ ├── define.md │ │ │ │ ├── define.zh-CN.md │ │ │ │ ├── hasCollected.md │ │ │ │ ├── hasCollected.zh-CN.md │ │ │ │ ├── markObservable.md │ │ │ │ ├── markObservable.zh-CN.md │ │ │ │ ├── markRaw.md │ │ │ │ ├── markRaw.zh-CN.md │ │ │ │ ├── model.md │ │ │ │ ├── model.zh-CN.md │ │ │ │ ├── observable.md │ │ │ │ ├── observable.zh-CN.md │ │ │ │ ├── observe.md │ │ │ │ ├── observe.zh-CN.md │ │ │ │ ├── raw.md │ │ │ │ ├── raw.zh-CN.md │ │ │ │ ├── react │ │ │ │ │ ├── observer.md │ │ │ │ │ └── observer.zh-CN.md │ │ │ │ ├── reaction.md │ │ │ │ ├── reaction.zh-CN.md │ │ │ │ ├── toJS.md │ │ │ │ ├── toJS.zh-CN.md │ │ │ │ ├── tracker.md │ │ │ │ ├── tracker.zh-CN.md │ │ │ │ ├── typeChecker.md │ │ │ │ ├── typeChecker.zh-CN.md │ │ │ │ ├── untracked.md │ │ │ │ ├── untracked.zh-CN.md │ │ │ │ └── vue │ │ │ │ ├── observer.md │ │ │ │ └── observer.zh-CN.md │ │ │ ├── guide │ │ │ │ ├── best-practice.md │ │ │ │ ├── best-practice.zh-CN.md │ │ │ │ ├── concept.md │ │ │ │ ├── concept.zh-CN.md │ │ │ │ ├── index.md │ │ │ │ └── index.zh-CN.md │ │ │ ├── index.md │ │ │ └── index.zh-CN.md │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── action.spec.ts │ │ │ │ ├── annotations.spec.ts │ │ │ │ ├── array.spec.ts │ │ │ │ ├── autorun.spec.ts │ │ │ │ ├── batch.spec.ts │ │ │ │ ├── collections-map.spec.ts │ │ │ │ ├── collections-set.spec.ts │ │ │ │ ├── collections-weakmap.spec.ts │ │ │ │ ├── collections-weakset.spec.ts │ │ │ │ ├── define.spec.ts │ │ │ │ ├── externals.spec.ts │ │ │ │ ├── hasCollected.spec.ts │ │ │ │ ├── observable.spec.ts │ │ │ │ ├── observe.spec.ts │ │ │ │ ├── tracker.spec.ts │ │ │ │ └── untracked.spec.ts │ │ │ ├── action.ts │ │ │ ├── annotations │ │ │ │ ├── box.ts │ │ │ │ ├── computed.ts │ │ │ │ ├── index.ts │ │ │ │ ├── observable.ts │ │ │ │ ├── ref.ts │ │ │ │ └── shallow.ts │ │ │ ├── array.ts │ │ │ ├── autorun.ts │ │ │ ├── batch.ts │ │ │ ├── checkers.ts │ │ │ ├── environment.ts │ │ │ ├── externals.ts │ │ │ ├── global.d.ts │ │ │ ├── handlers.ts │ │ │ ├── index.ts │ │ │ ├── internals.ts │ │ │ ├── model.ts │ │ │ ├── observable.ts │ │ │ ├── observe.ts │ │ │ ├── reaction.ts │ │ │ ├── tracker.ts │ │ │ ├── tree.ts │ │ │ ├── types.ts │ │ │ └── untracked.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── reactive-react │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── hooks │ │ │ │ ├── index.ts │ │ │ │ ├── useCompatEffect.ts │ │ │ │ ├── useCompatFactory.ts │ │ │ │ ├── useDidUpdate.ts │ │ │ │ ├── useForceUpdate.ts │ │ │ │ ├── useLayoutEffect.ts │ │ │ │ └── useObserver.ts │ │ │ ├── index.ts │ │ │ ├── observer.ts │ │ │ ├── shared │ │ │ │ ├── gc.ts │ │ │ │ ├── global.ts │ │ │ │ ├── immediate.ts │ │ │ │ └── index.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── reactive-test-cases-for-react18 │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── src │ │ │ ├── index.js │ │ │ └── MySlowList.js │ │ ├── template.ejs │ │ ├── tsconfig.build.json │ │ ├── tsconfig.json │ │ ├── webpack.base.ts │ │ ├── webpack.dev.ts │ │ └── webpack.prod.ts │ ├── reactive-vue │ │ ├── .npmignore │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ └── observer.spec.ts │ │ │ ├── hooks │ │ │ │ ├── index.ts │ │ │ │ └── useObserver.ts │ │ │ ├── index.ts │ │ │ ├── observer │ │ │ │ ├── collectData.ts │ │ │ │ ├── index.ts │ │ │ │ ├── observerInVue2.ts │ │ │ │ └── observerInVue3.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── shared │ │ ├── .npmignore │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ └── index.spec.ts │ │ │ ├── array.ts │ │ │ ├── case.ts │ │ │ ├── checkers.ts │ │ │ ├── clone.ts │ │ │ ├── compare.ts │ │ │ ├── defaults.ts │ │ │ ├── deprecate.ts │ │ │ ├── global.ts │ │ │ ├── index.ts │ │ │ ├── instanceof.ts │ │ │ ├── isEmpty.ts │ │ │ ├── merge.ts │ │ │ ├── middleware.ts │ │ │ ├── path.ts │ │ │ ├── string.ts │ │ │ ├── subscribable.ts │ │ │ └── uid.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── validator │ │ ├── .npmignore │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── parser.spec.ts │ │ │ │ ├── registry.spec.ts │ │ │ │ └── validator.spec.ts │ │ │ ├── formats.ts │ │ │ ├── index.ts │ │ │ ├── locale.ts │ │ │ ├── parser.ts │ │ │ ├── registry.ts │ │ │ ├── rules.ts │ │ │ ├── template.ts │ │ │ ├── types.ts │ │ │ └── validator.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ └── vue │ ├── .npmignore │ ├── bin │ │ ├── formily-vue-fix.js │ │ └── formily-vue-switch.js │ ├── docs │ │ ├── .vuepress │ │ │ ├── components │ │ │ │ ├── createCodeSandBox.js │ │ │ │ ├── dumi-previewer.vue │ │ │ │ └── highlight.js │ │ │ ├── config.js │ │ │ ├── enhanceApp.js │ │ │ └── styles │ │ │ └── index.styl │ │ ├── api │ │ │ ├── components │ │ │ │ ├── array-field.md │ │ │ │ ├── expression-scope.md │ │ │ │ ├── field.md │ │ │ │ ├── form-consumer.md │ │ │ │ ├── form-provider.md │ │ │ │ ├── object-field.md │ │ │ │ ├── recursion-field-with-component.md │ │ │ │ ├── recursion-field.md │ │ │ │ ├── schema-field-with-schema.md │ │ │ │ ├── schema-field.md │ │ │ │ └── void-field.md │ │ │ ├── hooks │ │ │ │ ├── use-field-schema.md │ │ │ │ ├── use-field.md │ │ │ │ ├── use-form-effects.md │ │ │ │ ├── use-form.md │ │ │ │ └── use-parent-form.md │ │ │ └── shared │ │ │ ├── connect.md │ │ │ ├── injections.md │ │ │ ├── map-props.md │ │ │ ├── map-read-pretty.md │ │ │ ├── observer.md │ │ │ └── schema.md │ │ ├── demos │ │ │ ├── api │ │ │ │ ├── components │ │ │ │ │ ├── array-field.vue │ │ │ │ │ ├── expression-scope.vue │ │ │ │ │ ├── field.vue │ │ │ │ │ ├── form-consumer.vue │ │ │ │ │ ├── form-provider.vue │ │ │ │ │ ├── object-field.vue │ │ │ │ │ ├── recursion-field-with-component.vue │ │ │ │ │ ├── recursion-field.vue │ │ │ │ │ ├── schema-field-with-schema.vue │ │ │ │ │ ├── schema-field.vue │ │ │ │ │ └── void-field.vue │ │ │ │ ├── hooks │ │ │ │ │ ├── use-field-schema.vue │ │ │ │ │ ├── use-field.vue │ │ │ │ │ ├── use-form-effects.vue │ │ │ │ │ ├── use-form.vue │ │ │ │ │ └── use-parent-form.vue │ │ │ │ └── shared │ │ │ │ ├── connect.vue │ │ │ │ ├── map-props.vue │ │ │ │ ├── map-read-pretty.vue │ │ │ │ └── observer.vue │ │ │ ├── index.vue │ │ │ └── questions │ │ │ ├── default-slot.vue │ │ │ ├── events.vue │ │ │ ├── named-slot.vue │ │ │ └── scoped-slot.vue │ │ ├── guide │ │ │ ├── architecture.md │ │ │ ├── concept.md │ │ │ └── README.md │ │ ├── questions │ │ │ └── README.md │ │ └── README.md │ ├── package.json │ ├── README.md │ ├── rollup.config.js │ ├── scripts │ │ ├── postinstall.js │ │ ├── switch-cli.js │ │ └── utils.js │ ├── src │ │ ├── __tests__ │ │ │ ├── expression.scope.spec.ts │ │ │ ├── field.spec.ts │ │ │ ├── form.spec.ts │ │ │ ├── schema.json.spec.ts │ │ │ ├── schema.markup.spec.ts │ │ │ ├── shared.spec.ts │ │ │ └── utils.spec.ts │ │ ├── components │ │ │ ├── ArrayField.ts │ │ │ ├── ExpressionScope.ts │ │ │ ├── Field.ts │ │ │ ├── FormConsumer.ts │ │ │ ├── FormProvider.ts │ │ │ ├── index.ts │ │ │ ├── ObjectField.ts │ │ │ ├── ReactiveField.ts │ │ │ ├── RecursionField.ts │ │ │ ├── SchemaField.ts │ │ │ └── VoidField.ts │ │ ├── global.d.ts │ │ ├── hooks │ │ │ ├── index.ts │ │ │ ├── useAttach.ts │ │ │ ├── useField.ts │ │ │ ├── useFieldSchema.ts │ │ │ ├── useForm.ts │ │ │ ├── useFormEffects.ts │ │ │ ├── useInjectionCleaner.ts │ │ │ └── useParentForm.ts │ │ ├── index.ts │ │ ├── shared │ │ │ ├── connect.ts │ │ │ ├── context.ts │ │ │ ├── createForm.ts │ │ │ ├── fragment.ts │ │ │ ├── h.ts │ │ │ └── index.ts │ │ ├── types │ │ │ └── index.ts │ │ ├── utils │ │ │ ├── formatVNodeData.ts │ │ │ ├── getFieldProps.ts │ │ │ ├── getRawComponent.ts │ │ │ └── resolveSchemaProps.ts │ │ └── vue2-components.ts │ ├── tsconfig.build.json │ ├── tsconfig.json │ └── tsconfig.types.json ├── README.md ├── README.zh-cn.md ├── scripts │ ├── build-style │ │ ├── buildAllStyles.ts │ │ ├── copy.ts │ │ ├── helper.ts │ │ └── index.ts │ └── rollup.base.js ├── tsconfig.build.json ├── tsconfig.jest.json ├── tsconfig.json └── yarn.lock ``` # Files -------------------------------------------------------------------------------- /docs/guide/scenes/edit-detail.md: -------------------------------------------------------------------------------- ```markdown 1 | # Edit Details 2 | 3 | ## Edit 4 | 5 | #### Markup Schema Cases 6 | 7 | ```tsx 8 | import React, { useState, useEffect } from 'react' 9 | import { createForm } from '@formily/core' 10 | import { createSchemaField } from '@formily/react' 11 | import { 12 | Form, 13 | FormItem, 14 | FormLayout, 15 | Input, 16 | Select, 17 | Cascader, 18 | DatePicker, 19 | Submit, 20 | FormGrid, 21 | Upload, 22 | ArrayItems, 23 | Editable, 24 | FormButtonGroup, 25 | } from '@formily/antd' 26 | import { action } from '@formily/reactive' 27 | import { Card, Button, Spin } from 'antd' 28 | import { UploadOutlined } from '@ant-design/icons' 29 | 30 | const form = createForm({ 31 | validateFirst: true, 32 | }) 33 | 34 | const IDUpload = (props) => { 35 | return ( 36 | <Upload 37 | {...props} 38 | action="https://www.mocky.io/v2/5cc8019d300000980a055e76" 39 | headers={{ 40 | authorization: 'authorization-text', 41 | }} 42 | > 43 | <Button icon={<UploadOutlined />}>Upload a copy</Button> 44 | </Upload> 45 | ) 46 | } 47 | 48 | const SchemaField = createSchemaField({ 49 | components: { 50 | FormItem, 51 | FormGrid, 52 | FormLayout, 53 | Input, 54 | DatePicker, 55 | Cascader, 56 | Select, 57 | IDUpload, 58 | ArrayItems, 59 | Editable, 60 | }, 61 | scope: { 62 | fetchAddress: (field) => { 63 | const transform = (data = {}) => { 64 | return Object.entries(data).reduce((buf, [key, value]) => { 65 | if (typeof value === 'string') 66 | return buf.concat({ 67 | label: value, 68 | value: key, 69 | }) 70 | const { name, code, cities, districts } = value 71 | const _cities = transform(cities) 72 | const _districts = transform(districts) 73 | return buf.concat({ 74 | label: name, 75 | value: code, 76 | children: _cities.length 77 | ? _cities 78 | : _districts.length 79 | ? _districts 80 | : undefined, 81 | }) 82 | }, []) 83 | } 84 | 85 | field.loading = true 86 | fetch('//unpkg.com/china-location/dist/location.json') 87 | .then((res) => res.json()) 88 | .then( 89 | action.bound((data) => { 90 | field.dataSource = transform(data) 91 | field.loading = false 92 | }) 93 | ) 94 | }, 95 | }, 96 | }) 97 | 98 | export default () => { 99 | const [loading, setLoading] = useState(true) 100 | useEffect(() => { 101 | setTimeout(() => { 102 | form.setInitialValues({ 103 | username: 'Aston Martin', 104 | firstName: 'Aston', 105 | lastName: 'Martin', 106 | email: '[email protected]', 107 | gender: 1, 108 | birthday: '1836-01-03', 109 | address: ['110000', '110000', '110101'], 110 | idCard: [ 111 | { 112 | name: 'this is image', 113 | thumbUrl: 114 | 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', 115 | uid: 'rc-upload-1615825692847-2', 116 | url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', 117 | }, 118 | ], 119 | contacts: [ 120 | { 121 | name: 'Zhang San', 122 | phone: '13245633378', 123 | email: '[email protected]', 124 | }, 125 | { name: 'Li Si', phone: '16873452678', email: '[email protected]' }, 126 | ], 127 | }) 128 | setLoading(false) 129 | }, 2000) 130 | }, []) 131 | return ( 132 | <div 133 | style={{ 134 | display: 'flex', 135 | justifyContent: 'center', 136 | background: '#eee', 137 | padding: '40px 0', 138 | }} 139 | > 140 | <Card title="Edit User" style={{ width: 620 }}> 141 | <Spin spinning={loading}> 142 | <Form 143 | form={form} 144 | labelCol={5} 145 | wrapperCol={16} 146 | onAutoSubmit={console.log} 147 | > 148 | <SchemaField> 149 | <SchemaField.String 150 | name="username" 151 | title="Username" 152 | required 153 | x-decorator="FormItem" 154 | x-component="Input" 155 | /> 156 | <SchemaField.Void 157 | title="Name" 158 | x-decorator="FormItem" 159 | x-decorator-props={{ 160 | asterisk: true, 161 | feedbackLayout: 'none', 162 | }} 163 | x-component="FormGrid" 164 | > 165 | <SchemaField.String 166 | name="firstName" 167 | x-decorator="FormItem" 168 | x-component="Input" 169 | x-component-props={{ 170 | placeholder: 'firstName', 171 | }} 172 | required 173 | /> 174 | <SchemaField.String 175 | name="lastName" 176 | x-decorator="FormItem" 177 | x-component="Input" 178 | x-component-props={{ 179 | placeholder: 'lastname', 180 | }} 181 | required 182 | /> 183 | </SchemaField.Void> 184 | <SchemaField.String 185 | name="email" 186 | title="Email" 187 | required 188 | x-validator="email" 189 | x-decorator="FormItem" 190 | x-component="Input" 191 | /> 192 | <SchemaField.String 193 | name="gender" 194 | title="Gender" 195 | x-decorator="FormItem" 196 | x-component="Select" 197 | enum={[ 198 | { 199 | label: 'male', 200 | value: 1, 201 | }, 202 | { 203 | label: 'female', 204 | value: 2, 205 | }, 206 | { 207 | label: 'third gender', 208 | value: 3, 209 | }, 210 | ]} 211 | required 212 | /> 213 | <SchemaField.String 214 | name="birthday" 215 | title="Birthday" 216 | required 217 | x-decorator="FormItem" 218 | x-component="DatePicker" 219 | /> 220 | <SchemaField.String 221 | name="address" 222 | title="Address" 223 | required 224 | x-decorator="FormItem" 225 | x-component="Cascader" 226 | x-reactions="{{fetchAddress}}" 227 | /> 228 | <SchemaField.String 229 | name="idCard" 230 | title="ID" 231 | required 232 | x-decorator="FormItem" 233 | x-component="IDUpload" 234 | /> 235 | <SchemaField.Array 236 | name="contacts" 237 | title="Contacts" 238 | required 239 | x-decorator="FormItem" 240 | x-component="ArrayItems" 241 | > 242 | <SchemaField.Object x-component="ArrayItems.Item"> 243 | <SchemaField.Void 244 | x-decorator="FormItem" 245 | x-component="ArrayItems.SortHandle" 246 | /> 247 | <SchemaField.Void 248 | name="popover" 249 | title="Contact Informations" 250 | x-decorator="Editable.Popover" 251 | x-component="FormLayout" 252 | x-component-props={{ 253 | layout: 'vertical', 254 | }} 255 | x-reactions={[ 256 | { 257 | fulfill: { 258 | schema: { 259 | title: '{{$self.query(".name").value() }}', 260 | }, 261 | }, 262 | }, 263 | ]} 264 | > 265 | <SchemaField.String 266 | name="name" 267 | required 268 | title="Name" 269 | x-decorator="FormItem" 270 | x-component="Input" 271 | x-component-props={{ 272 | style: { 273 | width: 300, 274 | }, 275 | }} 276 | /> 277 | <SchemaField.String 278 | name="email" 279 | title="Email" 280 | x-validator={[{ required: true }, 'email']} 281 | x-decorator="FormItem" 282 | x-component="Input" 283 | x-component-props={{ 284 | style: { 285 | width: 300, 286 | }, 287 | }} 288 | /> 289 | <SchemaField.String 290 | name="phone" 291 | required 292 | title="Phone Number" 293 | x-validator="phone" 294 | x-decorator="FormItem" 295 | x-component="Input" 296 | x-component-props={{ 297 | style: { 298 | width: 300, 299 | }, 300 | }} 301 | /> 302 | </SchemaField.Void> 303 | <SchemaField.Void 304 | x-decorator="FormItem" 305 | x-component="ArrayItems.Remove" 306 | /> 307 | </SchemaField.Object> 308 | <SchemaField.Void 309 | x-component="ArrayItems.Addition" 310 | title="Add Contact" 311 | /> 312 | </SchemaField.Array> 313 | </SchemaField> 314 | <FormButtonGroup.FormItem> 315 | <Submit block size="large"> 316 | Submit 317 | </Submit> 318 | </FormButtonGroup.FormItem> 319 | </Form> 320 | </Spin> 321 | </Card> 322 | </div> 323 | ) 324 | } 325 | ``` 326 | 327 | #### JSON Schema Cases 328 | 329 | ```tsx 330 | import React, { useState, useEffect } from 'react' 331 | import { createForm } from '@formily/core' 332 | import { createSchemaField } from '@formily/react' 333 | import { 334 | Form, 335 | FormItem, 336 | FormLayout, 337 | Input, 338 | Select, 339 | Cascader, 340 | DatePicker, 341 | Submit, 342 | FormGrid, 343 | Upload, 344 | ArrayItems, 345 | Editable, 346 | FormButtonGroup, 347 | } from '@formily/antd' 348 | import { action } from '@formily/reactive' 349 | import { Card, Button, Spin } from 'antd' 350 | import { UploadOutlined } from '@ant-design/icons' 351 | 352 | const form = createForm({ 353 | validateFirst: true, 354 | }) 355 | 356 | const IDUpload = (props) => { 357 | return ( 358 | <Upload 359 | {...props} 360 | action="https://www.mocky.io/v2/5cc8019d300000980a055e76" 361 | headers={{ 362 | authorization: 'authorization-text', 363 | }} 364 | > 365 | <Button icon={<UploadOutlined />}>Upload a copy</Button> 366 | </Upload> 367 | ) 368 | } 369 | 370 | const SchemaField = createSchemaField({ 371 | components: { 372 | FormItem, 373 | FormGrid, 374 | FormLayout, 375 | Input, 376 | DatePicker, 377 | Cascader, 378 | Select, 379 | IDUpload, 380 | ArrayItems, 381 | Editable, 382 | }, 383 | scope: { 384 | fetchAddress: (field) => { 385 | const transform = (data = {}) => { 386 | return Object.entries(data).reduce((buf, [key, value]) => { 387 | if (typeof value === 'string') 388 | return buf.concat({ 389 | label: value, 390 | value: key, 391 | }) 392 | const { name, code, cities, districts } = value 393 | const _cities = transform(cities) 394 | const _districts = transform(districts) 395 | return buf.concat({ 396 | label: name, 397 | value: code, 398 | children: _cities.length 399 | ? _cities 400 | : _districts.length 401 | ? _districts 402 | : undefined, 403 | }) 404 | }, []) 405 | } 406 | 407 | field.loading = true 408 | fetch('//unpkg.com/china-location/dist/location.json') 409 | .then((res) => res.json()) 410 | .then( 411 | action.bound((data) => { 412 | field.dataSource = transform(data) 413 | field.loading = false 414 | }) 415 | ) 416 | }, 417 | }, 418 | }) 419 | 420 | const schema = { 421 | type: 'object', 422 | properties: { 423 | username: { 424 | type: 'string', 425 | title: 'Username', 426 | required: true, 427 | 'x-decorator': 'FormItem', 428 | 'x-component': 'Input', 429 | }, 430 | name: { 431 | type: 'void', 432 | title: 'Name', 433 | 'x-decorator': 'FormItem', 434 | 'x-decorator-props': { 435 | asterisk: true, 436 | feedbackLayout: 'none', 437 | }, 438 | 'x-component': 'FormGrid', 439 | properties: { 440 | firstName: { 441 | type: 'string', 442 | required: true, 443 | 'x-decorator': 'FormItem', 444 | 'x-component': 'Input', 445 | 'x-component-props': { 446 | placeholder: 'firstName', 447 | }, 448 | }, 449 | lastName: { 450 | type: 'string', 451 | required: true, 452 | 'x-decorator': 'FormItem', 453 | 'x-component': 'Input', 454 | 'x-component-props': { 455 | placeholder: 'lastname', 456 | }, 457 | }, 458 | }, 459 | }, 460 | email: { 461 | type: 'string', 462 | title: 'Email', 463 | required: true, 464 | 'x-decorator': 'FormItem', 465 | 'x-component': 'Input', 466 | 'x-validator': 'email', 467 | }, 468 | gender: { 469 | type: 'string', 470 | title: 'Gender', 471 | enum: [ 472 | { 473 | label: 'male', 474 | value: 1, 475 | }, 476 | { 477 | label: 'female', 478 | value: 2, 479 | }, 480 | { 481 | label: 'third gender', 482 | value: 3, 483 | }, 484 | ], 485 | 'x-decorator': 'FormItem', 486 | 'x-component': 'Select', 487 | }, 488 | birthday: { 489 | type: 'string', 490 | required: true, 491 | title: 'Birthday', 492 | 'x-decorator': 'FormItem', 493 | 'x-component': 'DatePicker', 494 | }, 495 | address: { 496 | type: 'string', 497 | required: true, 498 | title: 'Address', 499 | 'x-decorator': 'FormItem', 500 | 'x-component': 'Cascader', 501 | 'x-reactions': '{{fetchAddress}}', 502 | }, 503 | idCard: { 504 | type: 'string', 505 | required: true, 506 | title: 'ID', 507 | 'x-decorator': 'FormItem', 508 | 'x-component': 'IDUpload', 509 | }, 510 | contacts: { 511 | type: 'array', 512 | required: true, 513 | title: 'Contacts', 514 | 'x-decorator': 'FormItem', 515 | 'x-component': 'ArrayItems', 516 | items: { 517 | type: 'object', 518 | 'x-component': 'ArrayItems.Item', 519 | properties: { 520 | sort: { 521 | type: 'void', 522 | 'x-decorator': 'FormItem', 523 | 'x-component': 'ArrayItems.SortHandle', 524 | }, 525 | popover: { 526 | type: 'void', 527 | title: 'Contact Informations', 528 | 'x-decorator': 'Editable.Popover', 529 | 'x-component': 'FormLayout', 530 | 'x-component-props': { 531 | layout: 'vertical', 532 | }, 533 | 'x-reactions': [ 534 | { 535 | fulfill: { 536 | schema: { 537 | title: '{{$self.query(".name").value() }}', 538 | }, 539 | }, 540 | }, 541 | ], 542 | properties: { 543 | name: { 544 | type: 'string', 545 | title: 'Name', 546 | required: true, 547 | 'x-decorator': 'FormItem', 548 | 'x-component': 'Input', 549 | 'x-component-props': { 550 | style: { 551 | width: 300, 552 | }, 553 | }, 554 | }, 555 | email: { 556 | type: 'string', 557 | title: 'Email', 558 | 'x-decorator': 'FormItem', 559 | 'x-component': 'Input', 560 | 'x-validator': [{ required: true }, 'email'], 561 | 'x-component-props': { 562 | style: { 563 | width: 300, 564 | }, 565 | }, 566 | }, 567 | phone: { 568 | type: 'string', 569 | title: 'Phone Number', 570 | 'x-decorator': 'FormItem', 571 | 'x-component': 'Input', 572 | 'x-validator': [{ required: true }, 'phone'], 573 | 'x-component-props': { 574 | style: { 575 | width: 300, 576 | }, 577 | }, 578 | }, 579 | }, 580 | }, 581 | remove: { 582 | type: 'void', 583 | 'x-decorator': 'FormItem', 584 | 'x-component': 'ArrayItems.Remove', 585 | }, 586 | }, 587 | }, 588 | properties: { 589 | addition: { 590 | type: 'void', 591 | title: 'Add Contact', 592 | 'x-component': 'ArrayItems.Addition', 593 | }, 594 | }, 595 | }, 596 | }, 597 | } 598 | 599 | export default () => { 600 | const [loading, setLoading] = useState(true) 601 | useEffect(() => { 602 | setTimeout(() => { 603 | form.setInitialValues({ 604 | username: 'Aston Martin', 605 | firstName: 'Aston', 606 | lastName: 'Martin', 607 | email: '[email protected]', 608 | gender: 1, 609 | birthday: '1836-01-03', 610 | address: ['110000', '110000', '110101'], 611 | idCard: [ 612 | { 613 | name: 'this is image', 614 | thumbUrl: 615 | 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', 616 | uid: 'rc-upload-1615825692847-2', 617 | url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', 618 | }, 619 | ], 620 | contacts: [ 621 | { 622 | name: 'Zhang San', 623 | phone: '13245633378', 624 | email: '[email protected]', 625 | }, 626 | { name: 'Li Si', phone: '16873452678', email: '[email protected]' }, 627 | ], 628 | }) 629 | setLoading(false) 630 | }, 2000) 631 | }, []) 632 | return ( 633 | <div 634 | style={{ 635 | display: 'flex', 636 | justifyContent: 'center', 637 | background: '#eee', 638 | padding: '40px 0', 639 | }} 640 | > 641 | <Card title="Edit User" style={{ width: 620 }}> 642 | <Spin spinning={loading}> 643 | <Form 644 | form={form} 645 | labelCol={5} 646 | wrapperCol={16} 647 | onAutoSubmit={console.log} 648 | > 649 | <SchemaField schema={schema} /> 650 | <FormButtonGroup.FormItem> 651 | <Submit block size="large"> 652 | Submit 653 | </Submit> 654 | </FormButtonGroup.FormItem> 655 | </Form> 656 | </Spin> 657 | </Card> 658 | </div> 659 | ) 660 | } 661 | ``` 662 | 663 | #### Pure JSX Cases 664 | 665 | ```tsx 666 | import React, { useState, useEffect } from 'react' 667 | import { createForm } from '@formily/core' 668 | import { Field, VoidField, ArrayField } from '@formily/react' 669 | import { 670 | Form, 671 | FormItem, 672 | FormLayout, 673 | Input, 674 | Select, 675 | Cascader, 676 | DatePicker, 677 | Submit, 678 | FormGrid, 679 | Upload, 680 | ArrayBase, 681 | Editable, 682 | FormButtonGroup, 683 | } from '@formily/antd' 684 | import { action } from '@formily/reactive' 685 | import { Card, Button, Spin } from 'antd' 686 | import { UploadOutlined } from '@ant-design/icons' 687 | import './index.less' 688 | 689 | const form = createForm({ 690 | validateFirst: true, 691 | }) 692 | 693 | const IDUpload = (props) => { 694 | return ( 695 | <Upload 696 | {...props} 697 | action="https://www.mocky.io/v2/5cc8019d300000980a055e76" 698 | headers={{ 699 | authorization: 'authorization-text', 700 | }} 701 | > 702 | <Button icon={<UploadOutlined />}>Upload a copy</Button> 703 | </Upload> 704 | ) 705 | } 706 | 707 | const fetchAddress = (field) => { 708 | const transform = (data = {}) => { 709 | return Object.entries(data).reduce((buf, [key, value]) => { 710 | if (typeof value === 'string') 711 | return buf.concat({ 712 | label: value, 713 | value: key, 714 | }) 715 | const { name, code, cities, districts } = value 716 | const _cities = transform(cities) 717 | const _districts = transform(districts) 718 | return buf.concat({ 719 | label: name, 720 | value: code, 721 | children: _cities.length 722 | ? _cities 723 | : _districts.length 724 | ? _districts 725 | : undefined, 726 | }) 727 | }, []) 728 | } 729 | 730 | field.loading = true 731 | fetch('//unpkg.com/china-location/dist/location.json') 732 | .then((res) => res.json()) 733 | .then( 734 | action.bound((data) => { 735 | field.dataSource = transform(data) 736 | field.loading = false 737 | }) 738 | ) 739 | } 740 | 741 | export default () => { 742 | const [loading, setLoading] = useState(true) 743 | useEffect(() => { 744 | setTimeout(() => { 745 | form.setInitialValues({ 746 | username: 'Aston Martin', 747 | firstName: 'Aston', 748 | lastName: 'Martin', 749 | email: '[email protected]', 750 | gender: 1, 751 | birthday: '1836-01-03', 752 | address: ['110000', '110000', '110101'], 753 | idCard: [ 754 | { 755 | name: 'this is image', 756 | thumbUrl: 757 | 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', 758 | uid: 'rc-upload-1615825692847-2', 759 | url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', 760 | }, 761 | ], 762 | contacts: [ 763 | { 764 | name: 'Zhang San', 765 | phone: '13245633378', 766 | email: '[email protected]', 767 | }, 768 | { name: 'Li Si', phone: '16873452678', email: '[email protected]' }, 769 | ], 770 | }) 771 | setLoading(false) 772 | }, 2000) 773 | }, []) 774 | return ( 775 | <div 776 | style={{ 777 | display: 'flex', 778 | justifyContent: 'center', 779 | background: '#eee', 780 | padding: '40px 0', 781 | }} 782 | > 783 | <Card title="Edit User" style={{ width: 620 }}> 784 | <Spin spinning={loading}> 785 | <Form 786 | form={form} 787 | labelCol={5} 788 | wrapperCol={16} 789 | onAutoSubmit={console.log} 790 | > 791 | <Field 792 | name="username" 793 | title="Username" 794 | required 795 | decorator={[FormItem]} 796 | component={[Input]} 797 | /> 798 | <VoidField 799 | name="name" 800 | title="Name" 801 | decorator={[ 802 | FormItem, 803 | { 804 | asterisk: true, 805 | feedbackLayout: 'none', 806 | }, 807 | ]} 808 | component={[FormGrid]} 809 | > 810 | <Field 811 | name="firstName" 812 | decorator={[FormItem]} 813 | component={[ 814 | Input, 815 | { 816 | placeholder: 'firstName', 817 | }, 818 | ]} 819 | required 820 | /> 821 | <Field 822 | name="lastName" 823 | decorator={[FormItem]} 824 | component={[ 825 | Input, 826 | { 827 | placeholder: 'lastname', 828 | }, 829 | ]} 830 | required 831 | /> 832 | </VoidField> 833 | <Field 834 | name="email" 835 | title="Email" 836 | required 837 | validator="email" 838 | decorator={[FormItem]} 839 | component={[Input]} 840 | /> 841 | <Field 842 | name="gender" 843 | title="Gender" 844 | decorator={[FormItem]} 845 | component={[Select]} 846 | dataSource={[ 847 | { 848 | label: 'male', 849 | value: 1, 850 | }, 851 | { 852 | label: 'female', 853 | value: 2, 854 | }, 855 | { 856 | label: 'third gender', 857 | value: 3, 858 | }, 859 | ]} 860 | required 861 | /> 862 | <Field 863 | name="birthday" 864 | title="Birthday" 865 | required 866 | decorator={[FormItem]} 867 | component={[DatePicker]} 868 | /> 869 | <Field 870 | name="address" 871 | title="Address" 872 | required 873 | decorator={[FormItem]} 874 | component={[Cascader]} 875 | reactions={fetchAddress} 876 | /> 877 | <Field 878 | name="idCard" 879 | title="ID" 880 | required 881 | decorator={[FormItem]} 882 | component={[IDUpload]} 883 | /> 884 | <ArrayField name="contacts" title="Contacts" decorator={[FormItem]}> 885 | {(field) => ( 886 | <ArrayBase> 887 | {field.value?.map((item, index) => ( 888 | <div key={index} className="array-items-item"> 889 | <Field 890 | name={`${index}`} 891 | title="Contact Informations" 892 | component={[Editable.Popover]} 893 | reactions={(field) => { 894 | field.title = 895 | field.query('.[].name').value() || field.title 896 | }} 897 | > 898 | <VoidField 899 | name="layout" 900 | component={[FormLayout, { layout: 'vertical' }]} 901 | > 902 | <Field 903 | name="name" 904 | title="Name" 905 | required 906 | decorator={[FormItem]} 907 | component={[ 908 | Input, 909 | { 910 | style: { 911 | width: 300, 912 | }, 913 | }, 914 | ]} 915 | /> 916 | <Field 917 | name="email" 918 | title="Email" 919 | required 920 | validator="email" 921 | decorator={[FormItem]} 922 | component={[ 923 | Input, 924 | { 925 | style: { 926 | width: 300, 927 | }, 928 | }, 929 | ]} 930 | /> 931 | <Field 932 | name="phone" 933 | title="Phone Number" 934 | required 935 | validator="phone" 936 | decorator={[FormItem]} 937 | component={[ 938 | Input, 939 | { 940 | style: { 941 | width: 300, 942 | }, 943 | }, 944 | ]} 945 | /> 946 | </VoidField> 947 | </Field> 948 | <FormItem.BaseItem> 949 | <ArrayBase.Remove index={index} /> 950 | <ArrayBase.MoveDown index={index} /> 951 | <ArrayBase.MoveUp index={index} /> 952 | </FormItem.BaseItem> 953 | </div> 954 | ))} 955 | <ArrayBase.Addition title="Add Contact" /> 956 | </ArrayBase> 957 | )} 958 | </ArrayField> 959 | <FormButtonGroup.FormItem> 960 | <Submit block size="large"> 961 | Submit 962 | </Submit> 963 | </FormButtonGroup.FormItem> 964 | </Form> 965 | </Spin> 966 | </Card> 967 | </div> 968 | ) 969 | } 970 | ``` 971 | 972 | ## Details 973 | 974 | #### Markup Schema Cases 975 | 976 | ```tsx 977 | import React, { useState, useEffect } from 'react' 978 | import { createForm } from '@formily/core' 979 | import { createSchemaField, useField } from '@formily/react' 980 | import { 981 | Form, 982 | FormItem, 983 | FormLayout, 984 | Input, 985 | Select, 986 | Cascader, 987 | DatePicker, 988 | FormGrid, 989 | Upload, 990 | ArrayItems, 991 | Editable, 992 | PreviewText, 993 | } from '@formily/antd' 994 | import { action } from '@formily/reactive' 995 | import { Card, Button, Spin } from 'antd' 996 | import { UploadOutlined } from '@ant-design/icons' 997 | 998 | const form = createForm({ 999 | readPretty: true, 1000 | validateFirst: true, 1001 | }) 1002 | 1003 | const IDUpload = (props) => { 1004 | const field = useField() 1005 | return ( 1006 | <Upload 1007 | {...props} 1008 | action="https://www.mocky.io/v2/5cc8019d300000980a055e76" 1009 | headers={{ 1010 | authorization: 'authorization-text', 1011 | }} 1012 | > 1013 | {field.editable && ( 1014 | <Button icon={<UploadOutlined />}>Upload a copy</Button> 1015 | )} 1016 | </Upload> 1017 | ) 1018 | } 1019 | 1020 | const SchemaField = createSchemaField({ 1021 | components: { 1022 | FormItem, 1023 | FormGrid, 1024 | FormLayout, 1025 | Input, 1026 | DatePicker, 1027 | Cascader, 1028 | Select, 1029 | IDUpload, 1030 | ArrayItems, 1031 | Editable, 1032 | }, 1033 | scope: { 1034 | fetchAddress: (field) => { 1035 | const transform = (data = {}) => { 1036 | return Object.entries(data).reduce((buf, [key, value]) => { 1037 | if (typeof value === 'string') 1038 | return buf.concat({ 1039 | label: value, 1040 | value: key, 1041 | }) 1042 | const { name, code, cities, districts } = value 1043 | const _cities = transform(cities) 1044 | const _districts = transform(districts) 1045 | return buf.concat({ 1046 | label: name, 1047 | value: code, 1048 | children: _cities.length 1049 | ? _cities 1050 | : _districts.length 1051 | ? _districts 1052 | : undefined, 1053 | }) 1054 | }, []) 1055 | } 1056 | 1057 | field.loading = true 1058 | fetch('//unpkg.com/china-location/dist/location.json') 1059 | .then((res) => res.json()) 1060 | .then( 1061 | action.bound((data) => { 1062 | field.dataSource = transform(data) 1063 | field.loading = false 1064 | }) 1065 | ) 1066 | }, 1067 | }, 1068 | }) 1069 | 1070 | export default () => { 1071 | const [loading, setLoading] = useState(true) 1072 | useEffect(() => { 1073 | setTimeout(() => { 1074 | form.setInitialValues({ 1075 | username: 'Aston Martin', 1076 | firstName: 'Aston', 1077 | lastName: 'Martin', 1078 | email: '[email protected]', 1079 | gender: 1, 1080 | birthday: '1836-01-03', 1081 | address: ['110000', '110000', '110101'], 1082 | idCard: [ 1083 | { 1084 | name: 'this is image', 1085 | thumbUrl: 1086 | 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', 1087 | uid: 'rc-upload-1615825692847-2', 1088 | url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', 1089 | }, 1090 | ], 1091 | contacts: [ 1092 | { 1093 | name: 'Zhang San', 1094 | phone: '13245633378', 1095 | email: '[email protected]', 1096 | }, 1097 | { name: 'Li Si', phone: '16873452678', email: '[email protected]' }, 1098 | ], 1099 | }) 1100 | setLoading(false) 1101 | }, 2000) 1102 | }, []) 1103 | return ( 1104 | <div 1105 | style={{ 1106 | display: 'flex', 1107 | justifyContent: 'center', 1108 | background: '#eee', 1109 | padding: '40px 0', 1110 | }} 1111 | > 1112 | <PreviewText.Placeholder value="-"> 1113 | <Card title="User Details" style={{ width: 620 }}> 1114 | <Spin spinning={loading}> 1115 | <Form 1116 | form={form} 1117 | labelCol={5} 1118 | wrapperCol={16} 1119 | onAutoSubmit={console.log} 1120 | > 1121 | <SchemaField> 1122 | <SchemaField.String 1123 | name="username" 1124 | title="Username" 1125 | required 1126 | x-decorator="FormItem" 1127 | x-component="Input" 1128 | /> 1129 | <SchemaField.Void 1130 | title="Name" 1131 | x-decorator="FormItem" 1132 | x-decorator-props={{ 1133 | feedbackLayout: 'none', 1134 | }} 1135 | x-component="FormGrid" 1136 | > 1137 | <SchemaField.String 1138 | name="firstName" 1139 | x-decorator="FormItem" 1140 | x-component="Input" 1141 | x-component-props={{ 1142 | placeholder: 'firstName', 1143 | }} 1144 | required 1145 | /> 1146 | <SchemaField.String 1147 | name="lastName" 1148 | x-decorator="FormItem" 1149 | x-component="Input" 1150 | x-component-props={{ 1151 | placeholder: 'lastname', 1152 | }} 1153 | required 1154 | /> 1155 | </SchemaField.Void> 1156 | <SchemaField.String 1157 | name="email" 1158 | title="Email" 1159 | required 1160 | x-validator="email" 1161 | x-decorator="FormItem" 1162 | x-component="Input" 1163 | /> 1164 | <SchemaField.String 1165 | name="gender" 1166 | title="Gender" 1167 | x-decorator="FormItem" 1168 | x-component="Select" 1169 | enum={[ 1170 | { 1171 | label: 'male', 1172 | value: 1, 1173 | }, 1174 | { 1175 | label: 'female', 1176 | value: 2, 1177 | }, 1178 | { 1179 | label: 'third gender', 1180 | value: 3, 1181 | }, 1182 | ]} 1183 | required 1184 | /> 1185 | <SchemaField.String 1186 | name="birthday" 1187 | title="Birthday" 1188 | required 1189 | x-decorator="FormItem" 1190 | x-component="DatePicker" 1191 | /> 1192 | <SchemaField.String 1193 | name="address" 1194 | title="Address" 1195 | required 1196 | x-decorator="FormItem" 1197 | x-component="Cascader" 1198 | x-reactions="{{fetchAddress}}" 1199 | /> 1200 | <SchemaField.String 1201 | name="idCard" 1202 | title="ID" 1203 | required 1204 | x-decorator="FormItem" 1205 | x-component="IDUpload" 1206 | /> 1207 | <SchemaField.Array 1208 | name="contacts" 1209 | title="Contacts" 1210 | required 1211 | x-decorator="FormItem" 1212 | x-component="ArrayItems" 1213 | > 1214 | <SchemaField.Object x-component="ArrayItems.Item"> 1215 | <SchemaField.Void 1216 | x-decorator="FormItem" 1217 | x-component="ArrayItems.SortHandle" 1218 | /> 1219 | <SchemaField.Void 1220 | name="popover" 1221 | title="Contact Informations" 1222 | x-decorator="Editable.Popover" 1223 | x-component="FormLayout" 1224 | x-component-props={{ 1225 | layout: 'vertical', 1226 | }} 1227 | x-reactions={[ 1228 | { 1229 | fulfill: { 1230 | schema: { 1231 | title: '{{$self.query(".name").value() }}', 1232 | }, 1233 | }, 1234 | }, 1235 | ]} 1236 | > 1237 | <SchemaField.String 1238 | name="name" 1239 | required 1240 | title="Name" 1241 | x-decorator="FormItem" 1242 | x-component="Input" 1243 | x-component-props={{ 1244 | style: { 1245 | width: 300, 1246 | }, 1247 | }} 1248 | /> 1249 | <SchemaField.String 1250 | name="email" 1251 | title="Email" 1252 | x-validator={[{ required: true }, 'email']} 1253 | x-decorator="FormItem" 1254 | x-component="Input" 1255 | x-component-props={{ 1256 | style: { 1257 | width: 300, 1258 | }, 1259 | }} 1260 | /> 1261 | <SchemaField.String 1262 | name="phone" 1263 | required 1264 | title="Phone Number" 1265 | x-validator="phone" 1266 | x-decorator="FormItem" 1267 | x-component="Input" 1268 | x-component-props={{ 1269 | style: { 1270 | width: 300, 1271 | }, 1272 | }} 1273 | /> 1274 | </SchemaField.Void> 1275 | <SchemaField.Void 1276 | x-decorator="FormItem" 1277 | x-component="ArrayItems.Remove" 1278 | /> 1279 | </SchemaField.Object> 1280 | <SchemaField.Void 1281 | x-component="ArrayItems.Addition" 1282 | title="Add Contact" 1283 | /> 1284 | </SchemaField.Array> 1285 | </SchemaField> 1286 | </Form> 1287 | </Spin> 1288 | </Card> 1289 | </PreviewText.Placeholder> 1290 | </div> 1291 | ) 1292 | } 1293 | ``` 1294 | 1295 | #### JSON Schema Cases 1296 | 1297 | ```tsx 1298 | import React, { useState, useEffect } from 'react' 1299 | import { createForm } from '@formily/core' 1300 | import { createSchemaField, useField } from '@formily/react' 1301 | import { 1302 | Form, 1303 | FormItem, 1304 | FormLayout, 1305 | Input, 1306 | Select, 1307 | Cascader, 1308 | DatePicker, 1309 | FormGrid, 1310 | Upload, 1311 | ArrayItems, 1312 | Editable, 1313 | PreviewText, 1314 | } from '@formily/antd' 1315 | import { action } from '@formily/reactive' 1316 | import { Card, Button, Spin } from 'antd' 1317 | import { UploadOutlined } from '@ant-design/icons' 1318 | 1319 | const form = createForm({ 1320 | readPretty: true, 1321 | validateFirst: true, 1322 | }) 1323 | 1324 | const IDUpload = (props) => { 1325 | const field = useField() 1326 | return ( 1327 | <Upload 1328 | {...props} 1329 | action="https://www.mocky.io/v2/5cc8019d300000980a055e76" 1330 | headers={{ 1331 | authorization: 'authorization-text', 1332 | }} 1333 | > 1334 | {field.editable && ( 1335 | <Button icon={<UploadOutlined />}>Upload a copy</Button> 1336 | )} 1337 | </Upload> 1338 | ) 1339 | } 1340 | 1341 | const SchemaField = createSchemaField({ 1342 | components: { 1343 | FormItem, 1344 | FormGrid, 1345 | FormLayout, 1346 | Input, 1347 | DatePicker, 1348 | Cascader, 1349 | Select, 1350 | IDUpload, 1351 | ArrayItems, 1352 | Editable, 1353 | }, 1354 | scope: { 1355 | fetchAddress: (field) => { 1356 | const transform = (data = {}) => { 1357 | return Object.entries(data).reduce((buf, [key, value]) => { 1358 | if (typeof value === 'string') 1359 | return buf.concat({ 1360 | label: value, 1361 | value: key, 1362 | }) 1363 | const { name, code, cities, districts } = value 1364 | const _cities = transform(cities) 1365 | const _districts = transform(districts) 1366 | return buf.concat({ 1367 | label: name, 1368 | value: code, 1369 | children: _cities.length 1370 | ? _cities 1371 | : _districts.length 1372 | ? _districts 1373 | : undefined, 1374 | }) 1375 | }, []) 1376 | } 1377 | 1378 | field.loading = true 1379 | fetch('//unpkg.com/china-location/dist/location.json') 1380 | .then((res) => res.json()) 1381 | .then( 1382 | action.bound((data) => { 1383 | field.dataSource = transform(data) 1384 | field.loading = false 1385 | }) 1386 | ) 1387 | }, 1388 | }, 1389 | }) 1390 | 1391 | const schema = { 1392 | type: 'object', 1393 | properties: { 1394 | username: { 1395 | type: 'string', 1396 | title: 'Username', 1397 | required: true, 1398 | 'x-decorator': 'FormItem', 1399 | 'x-component': 'Input', 1400 | }, 1401 | name: { 1402 | type: 'void', 1403 | title: 'Name', 1404 | 'x-decorator': 'FormItem', 1405 | 'x-decorator-props': { 1406 | asterisk: true, 1407 | feedbackLayout: 'none', 1408 | }, 1409 | 'x-component': 'FormGrid', 1410 | properties: { 1411 | firstName: { 1412 | type: 'string', 1413 | required: true, 1414 | 'x-decorator': 'FormItem', 1415 | 'x-component': 'Input', 1416 | 'x-component-props': { 1417 | placeholder: 'firstName', 1418 | }, 1419 | }, 1420 | lastName: { 1421 | type: 'string', 1422 | required: true, 1423 | 'x-decorator': 'FormItem', 1424 | 'x-component': 'Input', 1425 | 'x-component-props': { 1426 | placeholder: 'lastname', 1427 | }, 1428 | }, 1429 | }, 1430 | }, 1431 | email: { 1432 | type: 'string', 1433 | title: 'Email', 1434 | required: true, 1435 | 'x-decorator': 'FormItem', 1436 | 'x-component': 'Input', 1437 | 'x-validator': 'email', 1438 | }, 1439 | gender: { 1440 | type: 'string', 1441 | title: 'Gender', 1442 | enum: [ 1443 | { 1444 | label: 'male', 1445 | value: 1, 1446 | }, 1447 | { 1448 | label: 'female', 1449 | value: 2, 1450 | }, 1451 | { 1452 | label: 'third gender', 1453 | value: 3, 1454 | }, 1455 | ], 1456 | 'x-decorator': 'FormItem', 1457 | 'x-component': 'Select', 1458 | }, 1459 | birthday: { 1460 | type: 'string', 1461 | required: true, 1462 | title: 'Birthday', 1463 | 'x-decorator': 'FormItem', 1464 | 'x-component': 'DatePicker', 1465 | }, 1466 | address: { 1467 | type: 'string', 1468 | required: true, 1469 | title: 'Address', 1470 | 'x-decorator': 'FormItem', 1471 | 'x-component': 'Cascader', 1472 | 'x-reactions': '{{fetchAddress}}', 1473 | }, 1474 | idCard: { 1475 | type: 'string', 1476 | required: true, 1477 | title: 'ID', 1478 | 'x-decorator': 'FormItem', 1479 | 'x-component': 'IDUpload', 1480 | }, 1481 | contacts: { 1482 | type: 'array', 1483 | required: true, 1484 | title: 'Contacts', 1485 | 'x-decorator': 'FormItem', 1486 | 'x-component': 'ArrayItems', 1487 | items: { 1488 | type: 'object', 1489 | 'x-component': 'ArrayItems.Item', 1490 | properties: { 1491 | sort: { 1492 | type: 'void', 1493 | 'x-decorator': 'FormItem', 1494 | 'x-component': 'ArrayItems.SortHandle', 1495 | }, 1496 | popover: { 1497 | type: 'void', 1498 | title: 'Contact Informations', 1499 | 'x-decorator': 'Editable.Popover', 1500 | 'x-component': 'FormLayout', 1501 | 'x-component-props': { 1502 | layout: 'vertical', 1503 | }, 1504 | 'x-reactions': [ 1505 | { 1506 | fulfill: { 1507 | schema: { 1508 | title: '{{$self.query(".name").value() }}', 1509 | }, 1510 | }, 1511 | }, 1512 | ], 1513 | properties: { 1514 | name: { 1515 | type: 'string', 1516 | title: 'Name', 1517 | required: true, 1518 | 'x-decorator': 'FormItem', 1519 | 'x-component': 'Input', 1520 | 'x-component-props': { 1521 | style: { 1522 | width: 300, 1523 | }, 1524 | }, 1525 | }, 1526 | email: { 1527 | type: 'string', 1528 | title: 'Email', 1529 | 'x-decorator': 'FormItem', 1530 | 'x-component': 'Input', 1531 | 'x-validator': [{ required: true }, 'email'], 1532 | 'x-component-props': { 1533 | style: { 1534 | width: 300, 1535 | }, 1536 | }, 1537 | }, 1538 | phone: { 1539 | type: 'string', 1540 | title: 'Phone Number', 1541 | 'x-decorator': 'FormItem', 1542 | 'x-component': 'Input', 1543 | 'x-validator': [{ required: true }, 'phone'], 1544 | 'x-component-props': { 1545 | style: { 1546 | width: 300, 1547 | }, 1548 | }, 1549 | }, 1550 | }, 1551 | }, 1552 | remove: { 1553 | type: 'void', 1554 | 'x-decorator': 'FormItem', 1555 | 'x-component': 'ArrayItems.Remove', 1556 | }, 1557 | }, 1558 | }, 1559 | properties: { 1560 | addition: { 1561 | type: 'void', 1562 | title: 'Add Contact', 1563 | 'x-component': 'ArrayItems.Addition', 1564 | }, 1565 | }, 1566 | }, 1567 | }, 1568 | } 1569 | 1570 | export default () => { 1571 | const [loading, setLoading] = useState(true) 1572 | useEffect(() => { 1573 | setTimeout(() => { 1574 | form.setInitialValues({ 1575 | username: 'Aston Martin', 1576 | firstName: 'Aston', 1577 | lastName: 'Martin', 1578 | email: '[email protected]', 1579 | gender: 1, 1580 | birthday: '1836-01-03', 1581 | address: ['110000', '110000', '110101'], 1582 | idCard: [ 1583 | { 1584 | name: 'this is image', 1585 | thumbUrl: 1586 | 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', 1587 | uid: 'rc-upload-1615825692847-2', 1588 | url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', 1589 | }, 1590 | ], 1591 | contacts: [ 1592 | { 1593 | name: 'Zhang San', 1594 | phone: '13245633378', 1595 | email: '[email protected]', 1596 | }, 1597 | { name: 'Li Si', phone: '16873452678', email: '[email protected]' }, 1598 | ], 1599 | }) 1600 | setLoading(false) 1601 | }, 2000) 1602 | }, []) 1603 | return ( 1604 | <div 1605 | style={{ 1606 | display: 'flex', 1607 | justifyContent: 'center', 1608 | background: '#eee', 1609 | padding: '40px 0', 1610 | }} 1611 | > 1612 | <PreviewText.Placeholder value="-"> 1613 | <Card title="User Details" style={{ width: 620 }}> 1614 | <Spin spinning={loading}> 1615 | <Form 1616 | form={form} 1617 | labelCol={5} 1618 | wrapperCol={16} 1619 | onAutoSubmit={console.log} 1620 | > 1621 | <SchemaField schema={schema} /> 1622 | </Form> 1623 | </Spin> 1624 | </Card> 1625 | </PreviewText.Placeholder> 1626 | </div> 1627 | ) 1628 | } 1629 | ``` 1630 | 1631 | #### Pure JSX Cases 1632 | 1633 | ```tsx 1634 | import React, { useState, useEffect } from 'react' 1635 | import { createForm } from '@formily/core' 1636 | import { Field, VoidField, ArrayField, useField } from '@formily/react' 1637 | import { 1638 | Form, 1639 | FormItem, 1640 | FormLayout, 1641 | Input, 1642 | Select, 1643 | Cascader, 1644 | DatePicker, 1645 | FormGrid, 1646 | ArrayBase, 1647 | Upload, 1648 | PreviewText, 1649 | Editable, 1650 | } from '@formily/antd' 1651 | import { action } from '@formily/reactive' 1652 | import { Card, Button, Spin } from 'antd' 1653 | import { UploadOutlined } from '@ant-design/icons' 1654 | import './index.less' 1655 | 1656 | const form = createForm({ 1657 | validateFirst: true, 1658 | readPretty: true, 1659 | }) 1660 | 1661 | const IDUpload = (props) => { 1662 | const field = useField() 1663 | return ( 1664 | <Upload 1665 | {...props} 1666 | action="https://www.mocky.io/v2/5cc8019d300000980a055e76" 1667 | headers={{ 1668 | authorization: 'authorization-text', 1669 | }} 1670 | > 1671 | {field.editable && ( 1672 | <Button icon={<UploadOutlined />}>Upload a copy</Button> 1673 | )} 1674 | </Upload> 1675 | ) 1676 | } 1677 | 1678 | const fetchAddress = (field) => { 1679 | const transform = (data = {}) => { 1680 | return Object.entries(data).reduce((buf, [key, value]) => { 1681 | if (typeof value === 'string') 1682 | return buf.concat({ 1683 | label: value, 1684 | value: key, 1685 | }) 1686 | const { name, code, cities, districts } = value 1687 | const _cities = transform(cities) 1688 | const _districts = transform(districts) 1689 | return buf.concat({ 1690 | label: name, 1691 | value: code, 1692 | children: _cities.length 1693 | ? _cities 1694 | : _districts.length 1695 | ? _districts 1696 | : undefined, 1697 | }) 1698 | }, []) 1699 | } 1700 | 1701 | field.loading = true 1702 | fetch('//unpkg.com/china-location/dist/location.json') 1703 | .then((res) => res.json()) 1704 | .then( 1705 | action.bound((data) => { 1706 | field.dataSource = transform(data) 1707 | field.loading = false 1708 | }) 1709 | ) 1710 | } 1711 | 1712 | export default () => { 1713 | const [loading, setLoading] = useState(true) 1714 | useEffect(() => { 1715 | setTimeout(() => { 1716 | form.setInitialValues({ 1717 | username: 'Aston Martin', 1718 | firstName: 'Aston', 1719 | lastName: 'Martin', 1720 | email: '[email protected]', 1721 | gender: 1, 1722 | birthday: '1836-01-03', 1723 | address: ['110000', '110000', '110101'], 1724 | idCard: [ 1725 | { 1726 | name: 'this is image', 1727 | thumbUrl: 1728 | 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', 1729 | uid: 'rc-upload-1615825692847-2', 1730 | url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png', 1731 | }, 1732 | ], 1733 | contacts: [ 1734 | { 1735 | name: 'Zhang San', 1736 | phone: '13245633378', 1737 | email: '[email protected]', 1738 | }, 1739 | { name: 'Li Si', phone: '16873452678', email: '[email protected]' }, 1740 | ], 1741 | }) 1742 | setLoading(false) 1743 | }, 2000) 1744 | }, []) 1745 | return ( 1746 | <div 1747 | style={{ 1748 | display: 'flex', 1749 | justifyContent: 'center', 1750 | background: '#eee', 1751 | padding: '40px 0', 1752 | }} 1753 | > 1754 | <PreviewText.Placeholder value="-"> 1755 | <Card title="Edit User" style={{ width: 620 }}> 1756 | <Spin spinning={loading}> 1757 | <Form 1758 | form={form} 1759 | labelCol={5} 1760 | wrapperCol={16} 1761 | onAutoSubmit={console.log} 1762 | > 1763 | <Field 1764 | name="username" 1765 | title="Username" 1766 | required 1767 | decorator={[FormItem]} 1768 | component={[Input]} 1769 | /> 1770 | <VoidField 1771 | name="name" 1772 | title="Name" 1773 | decorator={[ 1774 | FormItem, 1775 | { 1776 | feedbackLayout: 'none', 1777 | }, 1778 | ]} 1779 | component={[FormGrid]} 1780 | > 1781 | <Field 1782 | name="firstName" 1783 | decorator={[FormItem]} 1784 | component={[ 1785 | Input, 1786 | { 1787 | placeholder: 'firstName', 1788 | }, 1789 | ]} 1790 | required 1791 | /> 1792 | <Field 1793 | name="lastName" 1794 | decorator={[FormItem]} 1795 | component={[ 1796 | Input, 1797 | { 1798 | placeholder: 'lastname', 1799 | }, 1800 | ]} 1801 | required 1802 | /> 1803 | </VoidField> 1804 | <Field 1805 | name="email" 1806 | title="Email" 1807 | required 1808 | validator="email" 1809 | decorator={[FormItem]} 1810 | component={[Input]} 1811 | /> 1812 | <Field 1813 | name="gender" 1814 | title="Gender" 1815 | decorator={[FormItem]} 1816 | component={[Select]} 1817 | dataSource={[ 1818 | { 1819 | label: 'male', 1820 | value: 1, 1821 | }, 1822 | { 1823 | label: 'female', 1824 | value: 2, 1825 | }, 1826 | { 1827 | label: 'third gender', 1828 | value: 3, 1829 | }, 1830 | ]} 1831 | required 1832 | /> 1833 | <Field 1834 | name="birthday" 1835 | title="Birthday" 1836 | required 1837 | decorator={[FormItem]} 1838 | component={[DatePicker]} 1839 | /> 1840 | <Field 1841 | name="address" 1842 | title="Address" 1843 | required 1844 | decorator={[FormItem]} 1845 | component={[Cascader]} 1846 | reactions={fetchAddress} 1847 | /> 1848 | <Field 1849 | name="idCard" 1850 | title="ID" 1851 | required 1852 | decorator={[FormItem]} 1853 | component={[IDUpload]} 1854 | /> 1855 | <ArrayField 1856 | name="contacts" 1857 | title="Contacts" 1858 | decorator={[FormItem]} 1859 | > 1860 | {(field) => ( 1861 | <ArrayBase> 1862 | {field.value?.map((item, index) => ( 1863 | <div key={index} className="array-items-item"> 1864 | <Field 1865 | name={`${index}`} 1866 | title="Contact Informations" 1867 | component={[Editable.Popover]} 1868 | reactions={(field) => { 1869 | field.title = 1870 | field.query('.[].name').value() || field.title 1871 | }} 1872 | > 1873 | <VoidField 1874 | name="layout" 1875 | component={[FormLayout, { layout: 'vertical' }]} 1876 | > 1877 | <Field 1878 | name="name" 1879 | title="Name" 1880 | required 1881 | decorator={[FormItem]} 1882 | component={[ 1883 | Input, 1884 | { 1885 | style: { 1886 | width: 300, 1887 | }, 1888 | }, 1889 | ]} 1890 | /> 1891 | <Field 1892 | name="email" 1893 | title="Email" 1894 | required 1895 | validator="email" 1896 | decorator={[FormItem]} 1897 | component={[ 1898 | Input, 1899 | { 1900 | style: { 1901 | width: 300, 1902 | }, 1903 | }, 1904 | ]} 1905 | /> 1906 | <Field 1907 | name="phone" 1908 | title="Phone Number" 1909 | required 1910 | validator="phone" 1911 | decorator={[FormItem]} 1912 | component={[ 1913 | Input, 1914 | { 1915 | style: { 1916 | width: 300, 1917 | }, 1918 | }, 1919 | ]} 1920 | /> 1921 | </VoidField> 1922 | </Field> 1923 | </div> 1924 | ))} 1925 | <ArrayBase.Addition title="Add Contact" /> 1926 | </ArrayBase> 1927 | )} 1928 | </ArrayField> 1929 | </Form> 1930 | </Spin> 1931 | </Card> 1932 | </PreviewText.Placeholder> 1933 | </div> 1934 | ) 1935 | } 1936 | ``` 1937 | ```