This is page 28 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/antd/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/antd' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' import { Button, Alert } from 'antd' 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 }, scroll: { x: '100%' }, }} > <SchemaField.Object> <SchemaField.Void x-component="ArrayTable.Column" x-component-props={{ width: 50, 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: 80, 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 block onClick={() => { form.setInitialValues({ array: range(100000), }) }} > 加载10W条超大数据 </Button> </FormButtonGroup> <Alert style={{ marginTop: 10 }} message="注意:开启formily插件的页面,因为后台有数据通信,会占用浏览器算力,最好在无痕模式(无formily插件)下测试" type="warning" /> </FormProvider> ) } ``` ## JSON Schema 案例 ```tsx import React from 'react' import { FormItem, Input, ArrayTable, Editable, FormButtonGroup, Submit, } from '@formily/antd' 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 }, scroll: { x: '100%' }, }, items: { type: 'object', properties: { column1: { type: 'void', 'x-component': 'ArrayTable.Column', 'x-component-props': { width: 50, title: 'Sort', align: 'center' }, properties: { sort: { type: 'void', 'x-component': 'ArrayTable.SortHandle', }, }, }, column2: { type: 'void', 'x-component': 'ArrayTable.Column', 'x-component-props': { width: 80, 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/antd' import { createForm, onFieldChange, onFieldReact } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' import { Button } from 'antd' 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 }, scroll: { x: '100%' }, }} > <SchemaField.Object> <SchemaField.Void x-component="ArrayTable.Column" name="column1" x-component-props={{ width: 50, 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: 80, 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/antd' 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 }, scroll: { x: '100%' }, }, items: { type: 'object', properties: { column1: { type: 'void', 'x-component': 'ArrayTable.Column', 'x-component-props': { width: 50, title: 'Sort', align: 'center' }, properties: { sort: { type: 'void', 'x-component': 'ArrayTable.SortHandle', }, }, }, column2: { type: 'void', 'x-component': 'ArrayTable.Column', 'x-component-props': { width: 80, 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> ) } ``` ## 重写内置操作项的默认行为 ```tsx import React from 'react' import { FormItem, Input, ArrayTable, FormButtonGroup, Submit, } from '@formily/antd' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' import { message } from 'antd' const SchemaField = createSchemaField({ components: { FormItem, Input, ArrayTable, }, }) const form = createForm() export default () => { return ( <FormProvider form={form}> <SchemaField> <SchemaField.Array name="array" x-decorator="FormItem" x-component="ArrayTable" x-component-props={{ pagination: { pageSize: 10 }, scroll: { x: '100%' }, }} > <SchemaField.Object> <SchemaField.Void x-component="ArrayTable.Column" x-component-props={{ width: 80, 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', width: 200 }} > <SchemaField.String x-decorator="FormItem" name="a1" 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: 'Operations', dataIndex: 'operations', width: 200, fixed: 'right', }} > <SchemaField.Void x-component="FormItem"> <SchemaField.Void x-component="ArrayTable.Remove" x-component-props={{ onClick: (e) => { e.preventDefault() message.info('remove 已被禁用!') }, }} /> <SchemaField.Void x-component="ArrayTable.Copy" x-component-props={{ onClick: (e) => { e.preventDefault() message.info('copy 已被禁用!') }, }} /> <SchemaField.Void x-component="ArrayTable.MoveDown" x-component-props={{ onClick: (e) => { e.preventDefault() message.info('moveDown 已被禁用!') }, }} /> <SchemaField.Void x-component="ArrayTable.MoveUp" x-component-props={{ onClick: (e) => { e.preventDefault() message.info('moveUp 已被禁用!') }, }} /> </SchemaField.Void> </SchemaField.Void> </SchemaField.Object> <SchemaField.Void x-component="ArrayTable.Addition" x-component-props={{ onClick: (e) => { e.preventDefault() const base = form.values.array.length form.values.array.push( { a1: base + 1 }, { a1: base + 2, a2: base + 2 } ) }, }} title="添加2个条目" /> </SchemaField.Array> </SchemaField> <FormButtonGroup> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup> </FormProvider> ) } ``` ```tsx /** * title: JSON Schema */ import React from 'react' import { FormItem, Input, ArrayTable, FormButtonGroup, Submit, } from '@formily/antd' import { createForm, onFieldMount } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' import { message } from 'antd' const SchemaField = createSchemaField({ components: { FormItem, Input, ArrayTable, }, }) const form = createForm({ effects() { onFieldMount('array.add', (field, form) => { field.componentProps.onClick = (e) => { e.preventDefault() const base = form.values.array.length form.values.array.push({ a1: base + 1 }, { a1: base + 2, a2: base + 2 }) } }) onFieldMount('array.*[0:].item.*', (field) => { field.componentProps.onClick = (e) => { e.preventDefault() message.info(`${field.address.segments.slice(-1)[0]} 已被禁用!`) } }) }, }) const schema = { type: 'object', properties: { array: { type: 'array', 'x-decorator': 'FormItem', 'x-component': 'ArrayTable', 'x-component-props': { pagination: { pageSize: 10 }, scroll: { x: '100%' }, }, items: { type: 'object', properties: { column1: { type: 'void', 'x-component': 'ArrayTable.Column', 'x-component-props': { width: 80, title: 'Index', align: 'center' }, properties: { index: { type: 'void', 'x-component': 'ArrayTable.Index', }, }, }, column2: { type: 'void', 'x-component': 'ArrayTable.Column', 'x-component-props': { width: 200, title: 'A1' }, properties: { a1: { type: 'string', 'x-decorator': 'FormItem', 'x-component': 'Input', }, }, }, column3: { type: 'void', 'x-component': 'ArrayTable.Column', 'x-component-props': { width: 200, title: 'A2' }, properties: { a2: { type: 'string', 'x-decorator': 'FormItem', 'x-component': 'Input', }, }, }, column4: { 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', }, copy: { type: 'void', 'x-component': 'ArrayTable.Copy', }, moveDown: { type: 'void', 'x-component': 'ArrayTable.MoveDown', }, moveUp: { type: 'void', 'x-component': 'ArrayTable.MoveUp', }, }, }, }, }, }, }, properties: { add: { type: 'void', 'x-component': 'ArrayTable.Addition', title: '添加2个条目', }, }, }, }, } export default () => { return ( <FormProvider form={form}> <SchemaField schema={schema} /> <FormButtonGroup> <Submit onSubmit={console.log}>Submit</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://ant.design/components/table-cn/ ### ArrayTable.Column > 表格列 参考 https://ant.design/components/table-cn/ ### ArrayTable.SortHandle > 拖拽手柄 参考 https://ant.design/components/icon-cn/ ### ArrayTable.Addition > 添加按钮 扩展属性 | 属性名 | 类型 | 描述 | 默认值 | | ------------ | --------------------- | -------- | -------- | | title | ReactText | 文案 | | | method | `'push' \| 'unshift'` | 添加方式 | `'push'` | | defaultValue | `any` | 默认值 | | 其余参考 https://ant.design/components/button-cn/ 注意:title 属性可以接收 Field 模型中的 title 映射,也就是在 Field 上传 title 也是生效的 注意:使用`onClick={e => e.preventDefault()}`可禁用默认行为。 ### ArrayTable.Remove > 删除按钮 | 属性名 | 类型 | 描述 | 默认值 | | ------ | --------- | ---- | ------ | | title | ReactText | 文案 | | 其余参考 https://ant.design/components/icon-cn/ 注意:title 属性可以接收 Field 模型中的 title 映射,也就是在 Field 上传 title 也是生效的 注意:使用`onClick={e => e.preventDefault()}`可禁用默认行为。 ### ArrayTable.Copy > 复制按钮 | 属性名 | 类型 | 描述 | 默认值 | | ------ | --------- | ---- | ------ | | title | ReactText | 文案 | | 其余参考 https://ant.design/components/icon-cn/ 注意:title 属性可以接收 Field 模型中的 title 映射,也就是在 Field 上传 title 也是生效的 注意:使用`onClick={e => e.preventDefault()}`可禁用默认行为。 ### ArrayTable.MoveDown > 下移按钮 | 属性名 | 类型 | 描述 | 默认值 | | ------ | --------- | ---- | ------ | | title | ReactText | 文案 | | 其余参考 https://ant.design/components/icon-cn/ 注意:title 属性可以接收 Field 模型中的 title 映射,也就是在 Field 上传 title 也是生效的 注意:使用`onClick={e => e.preventDefault()}`可禁用默认行为。 ### ArrayTable.MoveUp > 上移按钮 | 属性名 | 类型 | 描述 | 默认值 | | ------ | --------- | ---- | ------ | | title | ReactText | 文案 | | 其余参考 https://ant.design/components/icon-cn/ 注意:title 属性可以接收 Field 模型中的 title 映射,也就是在 Field 上传 title 也是生效的 注意:使用`onClick={e => e.preventDefault()}`可禁用默认行为。 ### ArrayTable.Index > 索引渲染器 无属性 ### ArrayTable.useIndex > 读取当前渲染行索引的 React Hook ### ArrayTable.useRecord > 读取当前渲染记录的 React Hook ``` -------------------------------------------------------------------------------- /packages/react/src/__tests__/schema.markup.spec.tsx: -------------------------------------------------------------------------------- ```typescript import React from 'react' import { createForm } from '@formily/core' import { FormProvider, createSchemaField, useFieldSchema, useField, RecursionField, RecordScope, RecordsScope, } from '../index' import { render, fireEvent, waitFor, act } from '@testing-library/react' const Input: React.FC<{ value?: string onChange?: (...args: any) => void [key: string]: any }> = ({ value, onChange, ...others }) => { return ( <input data-testid="input" {...others} value={value || ''} onChange={onChange} /> ) } describe('markup schema field', () => { test('string', () => { const form = createForm() const SchemaField = createSchemaField({ components: { Input, }, }) const { queryByTestId } = render( <FormProvider form={form}> <SchemaField> <SchemaField.String x-component="Input" /> </SchemaField> </FormProvider> ) expect(queryByTestId('input')).toBeVisible() }) test('boolean', () => { const form = createForm() const SchemaField = createSchemaField({ components: { Input, }, }) const { queryByTestId } = render( <FormProvider form={form}> <SchemaField> <SchemaField.Boolean x-component="Input" /> </SchemaField> </FormProvider> ) expect(queryByTestId('input')).toBeVisible() }) test('number', () => { const form = createForm() const SchemaField = createSchemaField({ components: { Input, }, }) const { queryByTestId } = render( <FormProvider form={form}> <SchemaField> <SchemaField.Number x-component="Input" /> </SchemaField> </FormProvider> ) expect(queryByTestId('input')).toBeVisible() }) test('date', () => { const form = createForm() const SchemaField = createSchemaField({ components: { Input, }, }) const { queryByTestId } = render( <FormProvider form={form}> <SchemaField> <SchemaField.Date x-component="Input" /> </SchemaField> </FormProvider> ) expect(queryByTestId('input')).toBeVisible() }) test('datetime', () => { const form = createForm() const SchemaField = createSchemaField({ components: { Input, }, }) const { queryByTestId } = render( <FormProvider form={form}> <SchemaField> <SchemaField.DateTime x-component="Input" /> </SchemaField> </FormProvider> ) expect(queryByTestId('input')).toBeVisible() }) test('void', () => { const form = createForm() const VoidComponent = (props) => { return <div data-testid="void-component">{props.children}</div> } const SchemaField = createSchemaField({ components: { VoidComponent, }, }) const { queryByTestId } = render( <FormProvider form={form}> <SchemaField> <SchemaField.Void x-component="VoidComponent" /> </SchemaField> </FormProvider> ) expect(queryByTestId('void-component')).toBeVisible() }) test('array', () => { const form = createForm() const SchemaField = createSchemaField({ components: { Input, }, }) render( <FormProvider form={form}> <SchemaField> <SchemaField.Array> <SchemaField.Object> <SchemaField.String x-component="Input" /> </SchemaField.Object> <SchemaField.Void /> </SchemaField.Array> </SchemaField> </FormProvider> ) }) test('other', () => { const form = createForm() const SchemaField = createSchemaField({ components: { Input, }, }) render( <FormProvider form={form}> <SchemaField> <SchemaField.Markup type="other"> <SchemaField.Markup /> </SchemaField.Markup> </SchemaField> </FormProvider> ) }) test('no parent', () => { const form = createForm() const SchemaField = createSchemaField({ components: { Input, }, }) render( <FormProvider form={form}> <SchemaField.Markup type="other"> <SchemaField.Markup /> </SchemaField.Markup> </FormProvider> ) }) test('props children', () => { const form = createForm() const Text = (props) => { return <div data-testid="children-test">{props.children}</div> } const SchemaField = createSchemaField({ components: { Text, }, }) const { queryByTestId } = render( <FormProvider form={form}> <SchemaField> <SchemaField.Void x-component="Text" x-component-props={{ children: 'props' }} /> </SchemaField> </FormProvider> ) expect(queryByTestId('children-test')).toBeVisible() expect(queryByTestId('children-test').innerHTML).toEqual('props') }) test('x-content', () => { const form = createForm() const Text = (props) => { return <div data-testid="content-test">{props.children}</div> } const SchemaField = createSchemaField({ components: { Text, }, }) const { queryByTestId } = render( <FormProvider form={form}> <SchemaField> <SchemaField.Void x-component="Text" x-content="content" /> </SchemaField> </FormProvider> ) expect(queryByTestId('content-test')).toBeVisible() expect(queryByTestId('content-test').innerHTML).toEqual('content') }) }) describe('recursion field', () => { test('onlyRenderProperties', () => { const form = createForm() const CustomObject: React.FC = () => { const schema = useFieldSchema() return ( <div data-testid="object"> <RecursionField schema={schema} /> </div> ) } const CustomObject2: React.FC = () => { const field = useField() const schema = useFieldSchema() return ( <div data-testid="only-properties"> <RecursionField name={schema.name} basePath={field.address} schema={schema} onlyRenderProperties /> </div> ) } const SchemaField = createSchemaField({ components: { Input, CustomObject, CustomObject2, }, }) const { queryAllByTestId } = render( <FormProvider form={form}> <SchemaField> <SchemaField.Object x-component="CustomObject"> <SchemaField.String x-component="Input" /> </SchemaField.Object> <SchemaField.Object x-component="CustomObject2"> <SchemaField.String x-component="Input" /> </SchemaField.Object> <SchemaField.Void x-component="CustomObject2"> <SchemaField.String x-component="Input" /> </SchemaField.Void> </SchemaField> </FormProvider> ) expect(queryAllByTestId('input').length).toEqual(3) expect(queryAllByTestId('object').length).toEqual(1) expect(queryAllByTestId('only-properties').length).toEqual(2) }) test('mapProperties', () => { const form = createForm() const CustomObject: React.FC = () => { const schema = useFieldSchema() return ( <div data-testid="object"> <RecursionField schema={schema} mapProperties={(schema) => { schema.default = '123' return schema }} /> </div> ) } const CustomObject2: React.FC = () => { const schema = useFieldSchema() return ( <div data-testid="object"> <RecursionField schema={schema} mapProperties={() => { return null }} /> </div> ) } const SchemaField = createSchemaField({ components: { Input, CustomObject, CustomObject2, }, }) const { queryAllByTestId } = render( <FormProvider form={form}> <SchemaField> <SchemaField.Object x-component="CustomObject"> <SchemaField.String x-component="Input" /> </SchemaField.Object> <SchemaField.Object x-component="CustomObject2"> <SchemaField.String x-component="Input" /> </SchemaField.Object> </SchemaField> </FormProvider> ) expect(queryAllByTestId('input').length).toEqual(2) expect(queryAllByTestId('input')[0].getAttribute('value')).toEqual('123') expect(queryAllByTestId('input')[1].getAttribute('value')).toEqual('') }) test('filterProperties', () => { const form = createForm() const CustomObject: React.FC = () => { const schema = useFieldSchema() return ( <div data-testid="object"> <RecursionField schema={schema} filterProperties={(schema) => { if (schema['x-component'] === 'Input') return false return true }} /> </div> ) } const CustomObject2: React.FC = () => { const schema = useFieldSchema() return ( <div data-testid="object"> <RecursionField schema={schema} filterProperties={(schema) => { if (schema['x-component'] === 'Input') return true return false }} /> </div> ) } const SchemaField = createSchemaField({ components: { Input, CustomObject, CustomObject2, }, }) const { queryAllByTestId } = render( <FormProvider form={form}> <SchemaField> <SchemaField.Object x-component="CustomObject"> <SchemaField.String x-component="Input" /> </SchemaField.Object> <SchemaField.Object x-component="CustomObject2"> <SchemaField.String x-component="Input" /> </SchemaField.Object> </SchemaField> </FormProvider> ) expect(queryAllByTestId('input').length).toEqual(1) expect(queryAllByTestId('object').length).toEqual(2) }) test('onlyRenderSelf', () => { const form = createForm() const CustomObject: React.FC = () => { const schema = useFieldSchema() return ( <div data-testid="object"> <RecursionField schema={schema} onlyRenderSelf /> </div> ) } const SchemaField = createSchemaField({ components: { Input, CustomObject, }, }) const { queryAllByTestId } = render( <FormProvider form={form}> <SchemaField> <SchemaField.Object x-component="CustomObject"> <SchemaField.String x-component="Input" /> </SchemaField.Object> </SchemaField> </FormProvider> ) expect(queryAllByTestId('input').length).toEqual(0) expect(queryAllByTestId('object').length).toEqual(1) }) test('illegal schema', () => { const form = createForm() const CustomObject: React.FC = () => { return ( <div data-testid="object"> <RecursionField schema={null} /> </div> ) } const CustomObject2: React.FC = () => { return ( <div data-testid="object"> <RecursionField schema={{} as any} /> </div> ) } const SchemaField = createSchemaField({ components: { Input, CustomObject, CustomObject2, }, }) const { queryByTestId } = render( <FormProvider form={form}> <SchemaField> <SchemaField.Object x-component="CustomObject"> <SchemaField.String x-component="Input" /> </SchemaField.Object> <SchemaField.Object x-component="CustomObject2"> <SchemaField.String x-component="Input" /> </SchemaField.Object> </SchemaField> </FormProvider> ) expect(queryByTestId('input')).toBeNull() }) }) test('schema reactions', async () => { const form = createForm() const SchemaField = createSchemaField({ components: { Input, }, }) const { queryByTestId } = render( <FormProvider form={form}> <SchemaField> <SchemaField.String name="aaa" x-component="Input" x-component-props={{ 'data-testid': 'aaa', }} /> <SchemaField.String name="bbb" x-component="Input" x-component-props={{ 'data-testid': 'bbb', }} x-reactions={[ { when: '{{$form.values.aaa === "123"}}', fulfill: { state: { visible: true, }, }, otherwise: { state: { visible: false, }, }, }, { when: '{{$self.value === "123"}}', target: 'ccc', fulfill: { schema: { 'x-visible': true, }, }, otherwise: { schema: { 'x-visible': false, }, }, }, ]} /> <SchemaField.String name="ccc" x-component="Input" x-component-props={{ 'data-testid': 'ccc', }} /> </SchemaField> </FormProvider> ) expect(queryByTestId('bbb')).toBeNull() fireEvent.change(queryByTestId('aaa'), { target: { value: '123', }, }) await waitFor(() => { expect(queryByTestId('bbb')).toBeVisible() }) expect(queryByTestId('ccc')).toBeNull() fireEvent.change(queryByTestId('bbb'), { target: { value: '123', }, }) await waitFor(() => { expect(queryByTestId('ccc')).toBeVisible() }) }) test('expression scope', async () => { let aa = false let bb = false let cc = false const form = createForm() const SchemaField = createSchemaField({ components: { Input, }, scope: { aa() { aa = true }, }, }) const scope = { bb() { bb = true }, cc() { cc = true }, } const schema = { type: 'object', properties: { aa: { type: 'string', 'x-component': 'Input', 'x-reactions': '{{ aa }}', }, bb: { type: 'string', 'x-component': 'Input', 'x-reactions': '{{ bb }}', }, cc: { type: 'string', 'x-component': 'Input', 'x-reactions': { dependencies: ['aa'], fulfill: { run: 'cc()', }, }, }, }, } const { queryByTestId } = render( <FormProvider form={form}> <SchemaField schema={schema} scope={scope} /> </FormProvider> ) await waitFor(() => queryByTestId('aa')) expect(aa).toBeTruthy() await waitFor(() => queryByTestId('bb')) expect(bb).toBeTruthy() await waitFor(() => queryByTestId('cc')) expect(cc).toBeTruthy() }) test('expression x-content', async () => { const form = createForm() const SchemaField = createSchemaField({ components: { Wrapper: (props) => props.children, }, scope: { child: <div data-testid="child"></div>, }, }) const { queryByTestId } = render( <FormProvider form={form}> <SchemaField> <SchemaField.String name="aaa" x-component="Wrapper" x-content="{{child}}" /> </SchemaField> </FormProvider> ) await waitFor(() => { expect(queryByTestId('child')).not.toBeUndefined() }) }) test('expression x-visible', async () => { const form = createForm() const SchemaField = createSchemaField({ components: { AAA: () => <div>AAA</div>, BBB: () => <div>BBB</div>, }, }) const { queryByText } = render( <FormProvider form={form}> <SchemaField> <SchemaField.String name="aaa" x-component="AAA" /> <SchemaField.String name="bbb" x-component="BBB" x-visible="{{$form.values.aaa === 123}}" /> </SchemaField> </FormProvider> ) await waitFor(() => { expect(queryByText('BBB')).toBeNull() }) act(() => { form.values.aaa = 123 }) await waitFor(() => { expect(queryByText('BBB')).not.toBeNull() }) }) test('expression x-value', async () => { const form = createForm({ values: { aaa: 1, }, }) const SchemaField = createSchemaField({ components: { Text: (props) => <div>{props.value}</div>, }, }) const { queryByText } = render( <FormProvider form={form}> <SchemaField> <SchemaField.String name="aaa" x-component="Text" /> <SchemaField.String name="bbb" x-component="Text" x-value="{{$form.values.aaa * 10}}" /> </SchemaField> </FormProvider> ) await waitFor(() => { expect(queryByText('10')).not.toBeNull() }) act(() => { form.values.aaa = 10 }) await waitFor(() => { expect(queryByText('100')).not.toBeNull() }) }) test('nested update component props with expression', async () => { const form = createForm({ values: { aaa: 'xxx', }, }) const SchemaField = createSchemaField({ components: { Text: (props) => <div>{props.aa?.bb?.cc}</div>, }, }) const { queryByText } = render( <FormProvider form={form}> <SchemaField> <SchemaField.String name="aaa" x-component="Text" /> <SchemaField.String name="bbb" x-component="Text" x-component-props={{ aa: { bb: { cc: '{{$form.values.aaa}}' } } }} /> </SchemaField> </FormProvider> ) await waitFor(() => { expect(queryByText('xxx')).not.toBeNull() }) act(() => { form.values.aaa = '10' }) await waitFor(() => { expect(queryByText('10')).not.toBeNull() }) }) test('nested update component props with x-reactions', async () => { const form = createForm({ values: { aaa: 'xxx', }, }) const SchemaField = createSchemaField({ components: { Text: (props) => <div>{props.aa?.bb?.cc}</div>, }, }) const { queryByText } = render( <FormProvider form={form}> <SchemaField> <SchemaField.String name="aaa" x-component="Text" /> <SchemaField.String name="bbb" x-component="Text" x-reactions={{ fulfill: { schema: { 'x-component-props.aa.bb.cc': '{{$form.values.aaa}}', } as any, }, }} /> </SchemaField> </FormProvider> ) await waitFor(() => { expect(queryByText('xxx')).not.toBeNull() }) act(() => { form.values.aaa = '10' }) await waitFor(() => { expect(queryByText('10')).not.toBeNull() }) }) test('schema x-validator/required', async () => { const form = createForm({ values: { aaa: 'xxx', }, }) const SchemaField = createSchemaField({ components: { Input: () => <div></div>, }, }) render( <FormProvider form={form}> <SchemaField> <SchemaField.String name="input" required x-validator="email" x-component="Input" /> </SchemaField> </FormProvider> ) await waitFor(() => { expect(form.query('input').get('required')).toBeTruthy() expect(form.query('input').get('validator')).toEqual([ { required: true }, { format: 'email' }, ]) }) }) test('schema x-reactions when undefined', async () => { const form = createForm({ values: { aaa: 'xxx', }, }) const SchemaField = createSchemaField({ components: { Input: () => <div data-testid="input"></div>, Select: () => <div data-testid="select"></div>, }, }) const { queryByTestId } = render( <FormProvider form={form}> <SchemaField> <SchemaField.String name="input" required x-component="Input" /> <SchemaField.String name="select" required x-component="Select" x-reactions={{ when: '{{$values.input}}', fulfill: { state: { visible: true, }, }, otherwise: { state: { visible: false, }, }, }} /> </SchemaField> </FormProvider> ) await waitFor(() => { expect(queryByTestId('input')).not.toBeNull() expect(queryByTestId('select')).toBeNull() }) }) test('void field children', async () => { const form = createForm() const SchemaField = createSchemaField({ components: { Button: (props) => ( <div data-testid="btn">{props.children || 'placeholder'}</div> ), }, }) const { queryByTestId } = render( <FormProvider form={form}> <SchemaField> <SchemaField.Void x-component="Button" /> </SchemaField> </FormProvider> ) await waitFor(() => { expect(queryByTestId('btn')?.textContent).toBe('placeholder') }) }) test('x-reactions runner for target', async () => { const form = createForm() const getTarget = jest.fn() const SchemaField = createSchemaField({ components: { Input: () => <div></div>, Button: (props) => ( <button data-testid="btn" onClick={(e) => { e.preventDefault() props.onChange('123') }} > Click {props.value} </button> ), }, scope: { getTarget, }, }) const { getByTestId } = render( <FormProvider form={form}> <SchemaField> <SchemaField.String name="target" default="333" x-component="Input" /> <SchemaField.String x-component="Button" x-reactions={{ target: 'target', effects: ['onFieldInputValueChange'], fulfill: { run: 'getTarget($target.value)', }, }} /> </SchemaField> </FormProvider> ) fireEvent.click(getByTestId('btn')) await waitFor(() => { expect(getByTestId('btn').textContent).toBe('Click 123') expect(getTarget).toBeCalledWith('333') expect(getTarget).toBeCalledTimes(1) }) }) test('multi x-reactions isolate effect', async () => { const form = createForm() const otherEffect = jest.fn() const SchemaField = createSchemaField({ components: { Input: () => <div data-testid="input"></div>, Button: (props) => ( <button data-testid="btn" onClick={(e) => { e.preventDefault() props.onChange('123') }} > Click {props.value} </button> ), }, }) const { getByTestId, queryByTestId } = render( <FormProvider form={form}> <SchemaField> <SchemaField.String name="target" x-reactions={[ otherEffect, { dependencies: ['btn'], fulfill: { state: { visible: '{{$deps[0] === "123"}}', }, }, }, ]} x-component="Input" /> <SchemaField.String name="btn" x-component="Button" /> </SchemaField> </FormProvider> ) await waitFor(() => { expect(queryByTestId('input')).toBeNull() }) fireEvent.click(getByTestId('btn')) await waitFor(() => { expect(getByTestId('btn').textContent).toBe('Click 123') expect(getByTestId('input')).not.toBeNull() expect(otherEffect).toBeCalledTimes(1) }) }) test('nested record scope', async () => { const form = createForm() const SchemaField = createSchemaField({ components: { Text: (props) => <div data-testid="text">{props.text}</div>, }, }) const { queryByTestId } = render( <FormProvider form={form}> <RecordScope getRecord={() => ({ bb: '321' })} getIndex={() => 1}> <RecordScope getRecord={() => ({ aa: '123' })} getIndex={() => 2}> <SchemaField> <SchemaField.Void x-component="Text" x-component-props={{ text: '{{$record.aa + $record.$lookup.bb + $index + $lookup.$index}}', }} /> </SchemaField> </RecordScope> </RecordScope> </FormProvider> ) await waitFor(() => { expect(queryByTestId('text')?.textContent).toBe('12332121') }) }) test('literal record scope', async () => { const form = createForm() const SchemaField = createSchemaField({ components: { Text: (props) => <div data-testid="text">{props.text}</div>, }, }) const { queryByTestId } = render( <FormProvider form={form}> <RecordScope getRecord={() => '123'} getIndex={() => 2}> <SchemaField> <SchemaField.Void x-component="Text" x-component-props={{ text: '{{$record + $index}}', }} /> </SchemaField> </RecordScope> </FormProvider> ) await waitFor(() => { expect(queryByTestId('text')?.textContent).toBe('1232') }) }) test('records scope', async () => { const form = createForm() const SchemaField = createSchemaField({ components: { Text: (props) => <div data-testid="text">{props.text}</div>, }, }) const { queryByTestId } = render( <FormProvider form={form}> <RecordsScope getRecords={() => [1, 2, 3]}> <SchemaField> <SchemaField.Void x-component="Text" x-component-props={{ text: '{{$records[2]}}', }} /> </SchemaField> </RecordsScope> </FormProvider> ) await waitFor(() => { expect(queryByTestId('text')?.textContent).toBe('3') }) }) test('propsRecursion as true', () => { const form = createForm() const CustomObject: React.FC = () => { const schema = useFieldSchema() return ( <div data-testid="object"> <RecursionField schema={schema} propsRecursion={true} filterProperties={(schema) => { if (schema['x-component'] === 'Input') { return false } return true }} /> </div> ) } const SchemaField = createSchemaField({ components: { Input, CustomObject, }, }) const { queryAllByTestId } = render( <FormProvider form={form}> <SchemaField> <SchemaField.Object x-component="CustomObject"> <SchemaField.String x-component="Input" /> <SchemaField.Object> <SchemaField.String x-component="Input" /> </SchemaField.Object> </SchemaField.Object> </SchemaField> </FormProvider> ) expect(queryAllByTestId('input').length).toEqual(0) expect(queryAllByTestId('object').length).toEqual(1) }) test('propsRecursion as empty', () => { const form = createForm() const CustomObject: React.FC = () => { const schema = useFieldSchema() return ( <div data-testid="object"> <RecursionField schema={schema} filterProperties={(schema) => { if (schema['x-component'] === 'Input') { return false } return true }} /> </div> ) } const SchemaField = createSchemaField({ components: { Input, CustomObject, }, }) const { queryAllByTestId } = render( <FormProvider form={form}> <SchemaField> <SchemaField.Object x-component="CustomObject"> <SchemaField.String x-component="Input" /> <SchemaField.Object> <SchemaField.String x-component="Input" /> </SchemaField.Object> </SchemaField.Object> </SchemaField> </FormProvider> ) expect(queryAllByTestId('input').length).toEqual(1) expect(queryAllByTestId('object').length).toEqual(1) }) ``` -------------------------------------------------------------------------------- /packages/next/docs/components/FormItem.zh-CN.md: -------------------------------------------------------------------------------- ```markdown # FormItem > 全新的 FormItem 组件,相比于 Fusion Next 的 FormItem,它支持的功能更多,同时它的定位是纯样式组件,不管理表单状态,所以也会更轻量,更方便定制 ## Markup Schema 案例 ```tsx import React from 'react' import { Input, Select, FormItem, FormButtonGroup, Submit } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { Input, Select, FormItem, }, }) const form = createForm() export default () => ( <FormProvider form={form}> <SchemaField> <SchemaField.String name="input" title="输入框" x-decorator="FormItem" x-component="Input" required /> </SchemaField> <FormButtonGroup> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup> </FormProvider> ) ``` ## JOSN Schema 案例 ```tsx import React from 'react' import { Input, FormItem, FormButtonGroup, Submit } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { Input, FormItem, }, }) const form = createForm() const schema = { type: 'object', properties: { input: { type: 'string', title: '输入框', 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-component-props': { style: { width: 240, }, }, }, }, } export default () => ( <FormProvider form={form}> <SchemaField schema={schema} /> <FormButtonGroup> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup> </FormProvider> ) ``` ## 纯 JSX 案例 ```tsx import React from 'react' import { Input, 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="input" title="输入框" required decorator={[FormItem]} component={[ Input, { style: { width: 240, }, }, ]} /> <FormButtonGroup> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup> </FormProvider> ) ``` ## 常用属性案例 ```tsx import React from 'react' import { Input, Radio, TreeSelect, Cascader, Select, DatePicker, FormItem, NumberPicker, Switch, } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const Title = (props) => <h3>{props.text}</h3> const SchemaField = createSchemaField({ components: { Input, Select, Cascader, TreeSelect, DatePicker, NumberPicker, Switch, Radio, FormItem, Title, }, }) const form = createForm() export default () => { return ( <FormProvider form={form}> <SchemaField> <SchemaField.Void x-component="Title" x-component-props={{ text: 'label为空时的展示' }} /> <SchemaField.String x-decorator="FormItem" x-component="Input" x-decorator-props={{ labelWidth: 300, }} /> <SchemaField.String title="" x-decorator="FormItem" x-component="Input" x-decorator-props={{ labelWidth: 300, }} /> <SchemaField.Void x-component="Title" x-component-props={{ text: '冒号' }} /> <SchemaField.String title="默认" x-decorator="FormItem" x-component="Input" /> <SchemaField.String title="无冒号(colon=false)" x-decorator="FormItem" x-component="Input" x-decorator-props={{ colon: false, }} /> <SchemaField.Void x-component="Title" x-component-props={{ text: '固定宽度设置' }} /> <SchemaField.String title="固定label宽度(labelWidth)" x-decorator="FormItem" x-component="Input" x-decorator-props={{ labelWidth: 300, }} /> <SchemaField.String title="固定label宽度(labelWidth)溢出溢出溢出溢出溢出溢出溢出溢出溢出溢出溢出溢出溢出溢出溢出溢出溢出溢出溢出溢出溢出溢出溢出溢出溢出溢出溢出溢出" description="描述描述" x-decorator="FormItem" x-component="Input" x-decorator-props={{ labelWidth: 300, tooltip: '提示提示', tooltipLayout: 'text', }} /> <SchemaField.String title="固定label宽度(labelWidth)换行换行换行换行换行换行换行换行换行换行换行换行换行换行换行换行换行换行换行换行换行换行换行换行换行换行" description="描述描述" x-decorator="FormItem" x-component="Input" x-decorator-props={{ labelWidth: 300, labelWrap: true, tooltip: '提示提示', }} /> <SchemaField.String title="固定内容宽度(wrapperWidth)" x-decorator="FormItem" x-component="Input" x-decorator-props={{ labelWidth: 300, wrapperWidth: 300, }} /> <SchemaField.Void x-component="Title" x-component-props={{ text: '对齐方式设置' }} /> <SchemaField.String title="label左对齐(labelAlign=left)" x-decorator="FormItem" x-component="Input" x-decorator-props={{ labelWidth: 300, labelAlign: 'left', }} /> <SchemaField.String title="label右对齐(labelAlign=right默认)" x-decorator="FormItem" x-component="Input" x-decorator-props={{ labelWidth: 300, labelAlign: 'right', }} /> <SchemaField.String title="内容左对齐(wrapperAlign=left默认)" x-decorator="FormItem" x-component="Input" x-decorator-props={{ labelWidth: 300, wrapperWidth: 240, wrapperAlign: 'left', }} /> <SchemaField.String title="内容右对齐(wrapperAlign=right)" x-decorator="FormItem" x-component="Input" x-decorator-props={{ labelWidth: 300, wrapperWidth: 240, wrapperAlign: 'right', }} /> <SchemaField.String title="tooltip" x-decorator="FormItem" x-component="Input" x-decorator-props={{ tooltip: 'tooltip', }} /> <SchemaField.Void x-component="Title" x-component-props={{ text: '是否撑满' }} /> <SchemaField.String title="默认撑满(fullness=true)" x-decorator="FormItem" x-component="Select" /> <SchemaField.String title="不撑满(fullness=false)" x-decorator="FormItem" x-component="Select" x-decorator-props={{ fullness: false, }} /> <SchemaField.Void x-component="Title" x-component-props={{ text: '辅助信息' }} /> <SchemaField.String title="必填星号" x-decorator="FormItem" x-component="Input" x-decorator-props={{ asterisk: true, labelCol: 6, wrapperCol: 10, }} /> <SchemaField.String title="前缀" x-decorator="FormItem" x-component="Input" x-decorator-props={{ addonBefore: 'addonBefore', labelCol: 6, wrapperCol: 10, }} /> <SchemaField.String title="后缀" x-decorator="FormItem" x-component="Input" x-decorator-props={{ addonAfter: 'addonAfter', labelCol: 6, wrapperCol: 10, }} /> <SchemaField.String title="帮助信息feedbackText" x-decorator="FormItem" x-component="Input" x-decorator-props={{ feedbackText: 'feedbackText', labelCol: 6, wrapperCol: 10, }} /> <SchemaField.String title="额外信息extra" x-decorator="FormItem" x-component="Input" x-decorator-props={{ feedbackText: 'feedbackText', extra: 'extra', labelCol: 6, wrapperCol: 10, }} /> </SchemaField> </FormProvider> ) } ``` ## 无边框案例 设置去除组件边框 ```tsx import React from 'react' import { Input, Radio, TreeSelect, Cascader, Select, DatePicker, FormItem, NumberPicker, Switch, } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const Title = (props) => <h3>{props.text}</h3> const SchemaField = createSchemaField({ components: { Input, Select, Cascader, TreeSelect, DatePicker, NumberPicker, Switch, Radio, FormItem, Title, }, }) const form = createForm() export default () => { return ( <FormProvider form={form}> <SchemaField> <SchemaField.String name="input" title="Input" x-decorator="FormItem" x-component="Input" required x-decorator-props={{ bordered: false, }} /> <SchemaField.String name="Select" title="Select" x-decorator="FormItem" x-component="Select" required x-decorator-props={{ bordered: false, }} /> <SchemaField.String name="Select" title="Select" x-decorator="FormItem" x-component="Select" required x-decorator-props={{ bordered: false, }} /> <SchemaField.String name="Cascader" title="Cascader" x-decorator="FormItem" x-component="Cascader" required x-decorator-props={{ bordered: false, }} /> <SchemaField.String name="DatePicker" title="DatePicker" x-decorator="FormItem" x-component="DatePicker" required x-decorator-props={{ bordered: false, }} /> <SchemaField.String name="NumberPicker" title="NumberPicker" x-decorator="FormItem" x-component="NumberPicker" required x-decorator-props={{ bordered: false, }} /> <SchemaField.String name="TreeSelect" title="TreeSelect" x-decorator="FormItem" x-component="TreeSelect" required x-decorator-props={{ bordered: false, }} /> <SchemaField.Boolean name="Switch" title="Switch" x-decorator="FormItem" x-component="Switch" required x-decorator-props={{ bordered: false, }} /> </SchemaField> </FormProvider> ) } ``` ## 内嵌模式案例 设置表单组件为内嵌模式 ```tsx import React from 'react' import { Input, Radio, TreeSelect, Cascader, Select, DatePicker, FormItem, NumberPicker, Switch, } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const Title = (props) => <h3>{props.text}</h3> const SchemaField = createSchemaField({ components: { Input, Select, Cascader, TreeSelect, DatePicker, NumberPicker, Switch, Radio, FormItem, Title, }, }) const form = createForm() export default () => { return ( <FormProvider form={form}> <SchemaField> <SchemaField.String name="input" title="Input" x-decorator="FormItem" x-component="Input" required x-decorator-props={{ inset: true, }} /> <SchemaField.String name="Select" title="Select" x-decorator="FormItem" x-component="Select" required x-decorator-props={{ inset: true, }} /> <SchemaField.String name="Select" title="Select" x-decorator="FormItem" x-component="Select" required x-decorator-props={{ inset: true, }} /> <SchemaField.String name="Cascader" title="Cascader" x-decorator="FormItem" x-component="Cascader" required x-decorator-props={{ inset: true, }} /> <SchemaField.String name="DatePicker" title="DatePicker" x-decorator="FormItem" x-component="DatePicker" required x-decorator-props={{ inset: true, }} /> <SchemaField.String name="NumberPicker" title="NumberPicker" x-decorator="FormItem" x-component="NumberPicker" required x-decorator-props={{ inset: true, }} /> <SchemaField.String name="TreeSelect" title="TreeSelect" x-decorator="FormItem" x-component="TreeSelect" required x-decorator-props={{ inset: true, }} /> <SchemaField.Boolean name="Switch" title="Switch" x-decorator="FormItem" x-component="Switch" required x-decorator-props={{ inset: false, }} /> </SchemaField> </FormProvider> ) } ``` ## 反馈信息定制案例 可通过 `feedbackIcon` 传入指定反馈的按钮 ```tsx import React from 'react' import { Input, Radio, TreeSelect, Cascader, Select, DatePicker, TimePicker, FormItem, FormLayout, NumberPicker, Switch, } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' import { CheckCircleFilled, LoadingOutlined } from '@ant-design/icons' const Title = (props) => <h3>{props.text}</h3> const SchemaField = createSchemaField({ components: { Input, Select, Cascader, TreeSelect, DatePicker, TimePicker, NumberPicker, Switch, Radio, FormItem, Title, FormLayout, }, }) const form = createForm() export default () => { return ( <FormProvider form={form}> <SchemaField> <SchemaField.String title="错误状态(feedbackStatus=error)" x-decorator="FormItem" x-component="Input" description="description" x-decorator-props={{ feedbackStatus: 'error', }} /> <SchemaField.String title="警告状态(feedbackStatus=warning)" x-decorator="FormItem" x-component="Input" description="description" x-decorator-props={{ feedbackStatus: 'warning', }} /> <SchemaField.String title="成功状态(feedbackStatus=success)" x-decorator="FormItem" x-component="Input" description="description" x-decorator-props={{ feedbackStatus: 'success', feedbackIcon: <CheckCircleFilled style={{ color: '#52c41a' }} />, }} /> <SchemaField.String title="加载状态(feedbackStatus=pending)" x-decorator="FormItem" x-component="Input" description="description" x-decorator-props={{ feedbackStatus: 'pending', feedbackIcon: <LoadingOutlined style={{ color: '#1890ff' }} />, }} /> <SchemaField.Void x-component="Title" x-component-props={{ text: '反馈信息的布局' }} /> <SchemaField.String title="紧凑模式required" x-decorator="FormItem" x-component="Input" required x-decorator-props={{ feedbackLayout: 'terse', }} /> <SchemaField.String title="紧凑模式有feedback(feedbackLayout=terse)" x-decorator="FormItem" x-component="Input" x-decorator-props={{ feedbackStatus: 'error', feedbackText: 'error message', feedbackLayout: 'terse', }} /> <SchemaField.String title="紧凑模式无feedback(feedbackLayout=terse)" x-decorator="FormItem" x-component="Input" x-decorator-props={{ feedbackLayout: 'terse', }} /> <SchemaField.String title="松散模式(feedbackLayout=loose)" x-decorator="FormItem" x-component="Input" x-decorator-props={{ feedbackStatus: 'error', feedbackText: 'error message', feedbackLayout: 'loose', }} /> <SchemaField.String title="弹出模式(feedbackLayout=popover)" x-decorator="FormItem" x-component="Input" x-decorator-props={{ feedbackStatus: 'warning', feedbackText: 'warning message', feedbackLayout: 'popover', }} /> <SchemaField.String title="弹出模式(feedbackLayout=popover)" x-decorator="FormItem" x-component="Input" x-decorator-props={{ feedbackStatus: 'error', feedbackText: 'error message', feedbackLayout: 'popover', }} /> <SchemaField.String title="弹出模式(feedbackLayout=popover)" x-decorator="FormItem" x-component="Input" x-decorator-props={{ feedbackStatus: 'success', feedbackText: 'success message', feedbackLayout: 'popover', }} /> <SchemaField.Void x-component="Title" x-component-props={{ text: '组件的适配情况' }} /> <SchemaField.Void x-component="FormLayout" x-component-props={{ layout: 'vertical' }} > <SchemaField.String title="Select" x-decorator="FormItem" x-component="Select" x-decorator-props={{ feedbackStatus: 'success', feedbackIcon: <CheckCircleFilled style={{ color: '#52c41a' }} />, }} /> <SchemaField.String title="DatePicker" x-decorator="FormItem" x-component="DatePicker" x-decorator-props={{ feedbackStatus: 'success', feedbackIcon: <CheckCircleFilled style={{ color: '#52c41a' }} />, }} /> <SchemaField.String title="DatePicker.RangePicker" x-decorator="FormItem" x-component="DatePicker.RangePicker" x-decorator-props={{ feedbackStatus: 'success', feedbackIcon: <CheckCircleFilled style={{ color: '#52c41a' }} />, }} /> <SchemaField.String title="DatePicker.YearPicker" x-decorator="FormItem" x-component="DatePicker.YearPicker" x-decorator-props={{ feedbackStatus: 'success', feedbackIcon: <CheckCircleFilled style={{ color: '#52c41a' }} />, }} /> <SchemaField.String title="DatePicker.MonthPicker" x-decorator="FormItem" x-component="DatePicker.MonthPicker" x-decorator-props={{ feedbackStatus: 'success', feedbackIcon: <CheckCircleFilled style={{ color: '#52c41a' }} />, }} /> <SchemaField.String title="DatePicker.TimePicker" x-decorator="FormItem" x-component="TimePicker" x-decorator-props={{ feedbackStatus: 'success', feedbackIcon: <CheckCircleFilled style={{ color: '#52c41a' }} />, }} /> <SchemaField.String title="NumberPicker" x-decorator="FormItem" x-component="NumberPicker" x-decorator-props={{ feedbackStatus: 'success', feedbackIcon: <CheckCircleFilled style={{ color: '#52c41a' }} />, }} /> <SchemaField.String title="TreeSelect" x-decorator="FormItem" x-component="TreeSelect" x-decorator-props={{ feedbackStatus: 'success', feedbackIcon: <CheckCircleFilled style={{ color: '#52c41a' }} />, }} /> <SchemaField.String title="Cascader" x-decorator="FormItem" x-component="Cascader" x-decorator-props={{ feedbackStatus: 'success', feedbackIcon: <CheckCircleFilled style={{ color: '#52c41a' }} />, }} /> </SchemaField.Void> </SchemaField> </FormProvider> ) } ``` ## 尺寸控制案例 ```tsx import React from 'react' import { Input, Radio, TreeSelect, Cascader, Select, DatePicker, FormItem, NumberPicker, Switch, } from '@formily/next' import { createForm, onFieldChange } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const Div = (props) => <div {...props} /> const SchemaField = createSchemaField({ components: { Input, Select, Cascader, TreeSelect, DatePicker, NumberPicker, Switch, Radio, FormItem, Div, }, }) const form = createForm({ values: { size: 'default', }, effects: () => { onFieldChange('size', ['value'], (field, form) => { form.setFieldState('sizeWrap.*', (state) => { if (state.decorator[1]) { state.decorator[1].size = field.value } }) }) }, }) export default () => { return ( <FormProvider form={form}> <SchemaField> <SchemaField.String name="size" title="Radio.Group" x-decorator="FormItem" x-component="Radio.Group" enum={[ { value: 'small', label: 'Small' }, { value: 'default', label: 'Default' }, { value: 'large', label: 'Large' }, ]} /> <SchemaField.Void name="sizeWrap" x-component="Div"> <SchemaField.String name="input" title="Input" x-decorator="FormItem" x-component="Input" required /> <SchemaField.String name="Select" title="Select" x-decorator="FormItem" x-component="Select" required /> <SchemaField.String name="Select" title="Select" x-decorator="FormItem" x-component="Select" enum={[ { label: '选项1', value: 1, }, { label: '选项2', value: 2, }, ]} required /> <SchemaField.String name="Cascader" title="Cascader" x-decorator="FormItem" x-component="Cascader" required /> <SchemaField.String name="DatePicker" title="DatePicker" x-decorator="FormItem" x-component="DatePicker" required /> <SchemaField.String name="NumberPicker" title="NumberPicker" x-decorator="FormItem" x-component="NumberPicker" required /> <SchemaField.String name="TreeSelect" title="TreeSelect" x-decorator="FormItem" x-component="TreeSelect" required /> <SchemaField.Boolean name="Switch" title="Switch" x-decorator="FormItem" x-component="Switch" required /> </SchemaField.Void> </SchemaField> </FormProvider> ) } ``` ## API ### FormItem | 属性名 | 类型 | 描述 | 默认值 | | -------------- | ------------------------------------------------------ | ------------------------------------------- | --------- | | label | ReactNode | 标签 | - | | style | CSSProperties | 样式 | - | | labelStyle | CSSProperties | 标签样式 | - | | wrapperStyle | CSSProperties | 组件容器样式 | - | | className | string | 组件样式类名 | - | | colon | boolean | 冒号 | true | | tooltip | ReactNode | 问号提示 | - | | tooltipLayout | `"icon" \| "text"` | 问号提示布局 | `"icon"` | | tooltipIcon | ReactNode | 问号提示图标 | `?` | | labelAlign | `"left"` \| `"right"` | 标签文本对齐方式 | `"right"` | | labelWrap | boolean | 标签换⾏,否则出现省略号,hover 有 tooltip | false | | labelWidth | `number \| string` | 标签固定宽度 | - | | wrapperWidth | `number \| string` | 内容固定宽度 | - | | labelCol | number | 标签⽹格所占列数,和内容列数加起来总和为 24 | - | | wrapperCol | number | 内容⽹格所占列数,和标签列数加起来总和为 24 | - | | wrapperAlign | `"left"` \| `"right"` | 内容文本对齐方式⻬ | `"left"` | | wrapperWrap | boolean | 内容换⾏,否则出现省略号,hover 有 tooltip | false | | fullness | boolean | 内容撑满 | true | | addonBefore | ReactNode | 前缀内容 | - | | addonAfter | ReactNode | 后缀内容 | - | | size | `"small"` \| `"default"` \| `"large"` | 尺⼨ | - | | inset | boolean | 是否是内嵌布局 | false | | extra | ReactNode | 扩展描述⽂案 | - | | feedbackText | ReactNode | 反馈⽂案 | - | | feedbackLayout | `"loose"` \| `"terse"` \| `"popover" \| "none"` | 反馈布局 | - | | feedbackStatus | `"error"` \| `"warning"` \| `"success"` \| `"pending"` | 反馈布局 | - | | feedbackIcon | ReactNode | 反馈图标 | - | | asterisk | boolean | 星号提醒 | - | | gridSpan | number | ⽹格布局占宽 | - | | bordered | boolean | 是否有边框 | - | ### FormItem.BaseItem 纯样式组件,属性与 FormItem 一样,与 Formily Core 不做状态桥接,主要用于一些需要依赖 FormItem 的样式布局能力,但不希望接入 Field 状态的场景 ``` -------------------------------------------------------------------------------- /packages/antd/docs/components/ArrayTable.md: -------------------------------------------------------------------------------- ```markdown # ArrayTable > Self-increasing table, it is more suitable to use this component for scenes with a large amount of data. Although the amount of data is large to a certain extent, it will be a little bit stuck, but it will not affect the basic operation > > Note: This component is only applicable to Schema scenarios and can only be an array of objects ## Markup Schema example ```tsx import React from 'react' import { FormItem, Input, ArrayTable, Editable, FormButtonGroup, Submit, } from '@formily/antd' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' import { Button, Alert } from 'antd' 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 }, scroll: { x: '100%' }, }} > <SchemaField.Object> <SchemaField.Void x-component="ArrayTable.Column" x-component-props={{ width: 50, 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: 80, 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="Add entry" /> </SchemaField.Array> </SchemaField> <FormButtonGroup> <Submit onSubmit={console.log}>Submit</Submit> <Button block onClick={() => { form.setInitialValues({ array: range(100000), }) }} > Load 10W pieces of large data </Button> </FormButtonGroup> <Alert style={{ marginTop: 10 }} message="Note: Open the formily plug-in page, because there is data communication in the background, which will occupy the browser's computing power, it is best to test in the incognito mode (no formily plug-in)" type="warning" /> </FormProvider> ) } ``` ## JSON Schema case ```tsx import React from 'react' import { FormItem, Input, ArrayTable, Editable, FormButtonGroup, Submit, } from '@formily/antd' 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 }, scroll: { x: '100%' }, }, items: { type: 'object', properties: { column1: { type: 'void', 'x-component': 'ArrayTable.Column', 'x-component-props': { width: 50, title: 'Sort', align: 'center' }, properties: { sort: { type: 'void', 'x-component': 'ArrayTable.SortHandle', }, }, }, column2: { type: 'void', 'x-component': 'ArrayTable.Column', 'x-component-props': { width: 80, 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: 'Add entry', }, }, }, }, } export default () => { return ( <FormProvider form={form}> <SchemaField schema={schema} /> <FormButtonGroup> <Submit onSubmit={console.log}>Submit</Submit> </FormButtonGroup> </FormProvider> ) } ``` ## Effects linkage case ```tsx import React from 'react' import { FormItem, Input, ArrayTable, Switch, FormButtonGroup, Submit, } from '@formily/antd' import { createForm, onFieldChange, onFieldReact } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' import { Button } from 'antd' const SchemaField = createSchemaField({ components: { FormItem, Switch, Input, Button, ArrayTable, }, }) const form = createForm({ effects: () => { //Active linkage mode onFieldChange('hideFirstColumn', ['value'], (field) => { field.query('array.column4').take((target) => { target.visible = !field.value }) field.query('array.*.a2').take((target) => { target.visible = !field.value }) }) //Passive linkage mode 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="Hide A2" /> <SchemaField.Array name="array" x-decorator="FormItem" x-component="ArrayTable" x-component-props={{ pagination: { pageSize: 10 }, scroll: { x: '100%' }, }} > <SchemaField.Object> <SchemaField.Void x-component="ArrayTable.Column" name="column1" x-component-props={{ width: 50, 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: 80, 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: 'Explicitly hidden->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="Add entry" /> </SchemaField.Array> </SchemaField> <FormButtonGroup> <Submit onSubmit={console.log}>Submit</Submit> </FormButtonGroup> </FormProvider> ) } ``` ## JSON Schema linkage case ```tsx import React from 'react' import { FormItem, Input, ArrayTable, Switch, FormButtonGroup, Submit, } from '@formily/antd' 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: 'Hide A2', 'x-decorator': 'FormItem', 'x-component': 'Switch', }, array: { type: 'array', 'x-decorator': 'FormItem', 'x-component': 'ArrayTable', 'x-component-props': { pagination: { pageSize: 10 }, scroll: { x: '100%' }, }, items: { type: 'object', properties: { column1: { type: 'void', 'x-component': 'ArrayTable.Column', 'x-component-props': { width: 50, title: 'Sort', align: 'center' }, properties: { sort: { type: 'void', 'x-component': 'ArrayTable.SortHandle', }, }, }, column2: { type: 'void', 'x-component': 'ArrayTable.Column', 'x-component-props': { width: 80, 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: 'Explicitly hidden->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: 'Add entry', }, }, }, }, } export default () => { return ( <FormProvider form={form}> <SchemaField schema={schema} /> <FormButtonGroup> <Submit onSubmit={console.log}>Submit</Submit> </FormButtonGroup> </FormProvider> ) } ``` ## Overwrite default behavior of build-in operations ```tsx /** * title: Markup Schema */ import React from 'react' import { FormItem, Input, ArrayTable, FormButtonGroup, Submit, } from '@formily/antd' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' import { message } from 'antd' const SchemaField = createSchemaField({ components: { FormItem, Input, ArrayTable, }, }) const form = createForm() export default () => { return ( <FormProvider form={form}> <SchemaField> <SchemaField.Array name="array" x-decorator="FormItem" x-component="ArrayTable" x-component-props={{ pagination: { pageSize: 10 }, scroll: { x: '100%' }, }} > <SchemaField.Object> <SchemaField.Void x-component="ArrayTable.Column" x-component-props={{ width: 80, 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', width: 200 }} > <SchemaField.String x-decorator="FormItem" name="a1" 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: 'Operations', dataIndex: 'operations', width: 200, fixed: 'right', }} > <SchemaField.Void x-component="FormItem"> <SchemaField.Void x-component="ArrayTable.Remove" x-component-props={{ onClick: (e) => { e.preventDefault() message.info('remove is disabled!') }, }} /> <SchemaField.Void x-component="ArrayTable.Copy" x-component-props={{ onClick: (e) => { e.preventDefault() message.info('copy is disabled!') }, }} /> <SchemaField.Void x-component="ArrayTable.MoveDown" x-component-props={{ onClick: (e) => { e.preventDefault() message.info('moveDown is disabled!') }, }} /> <SchemaField.Void x-component="ArrayTable.MoveUp" x-component-props={{ onClick: (e) => { e.preventDefault() message.info('moveUp is disabled!') }, }} /> </SchemaField.Void> </SchemaField.Void> </SchemaField.Object> <SchemaField.Void x-component="ArrayTable.Addition" x-component-props={{ onClick: (e) => { e.preventDefault() const base = form.values.array.length form.values.array.push( { a1: base + 1 }, { a1: base + 2, a2: base + 2 } ) }, }} title="Add two entries" /> </SchemaField.Array> </SchemaField> <FormButtonGroup> <Submit onSubmit={console.log}>Submit</Submit> </FormButtonGroup> </FormProvider> ) } ``` ```tsx /** * title: JSON Schema */ import React from 'react' import { FormItem, Input, ArrayTable, FormButtonGroup, Submit, } from '@formily/antd' import { createForm, onFieldMount } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' import { message } from 'antd' const SchemaField = createSchemaField({ components: { FormItem, Input, ArrayTable, }, }) const form = createForm({ effects() { onFieldMount('array.add', (field, form) => { field.componentProps.onClick = (e) => { e.preventDefault() const base = form.values.array.length form.values.array.push({ a1: base + 1 }, { a1: base + 2, a2: base + 2 }) } }) onFieldMount('array.*[0:].item.*', (field) => { field.componentProps.onClick = (e) => { e.preventDefault() message.info(`${field.address.segments.slice(-1)[0]} is disabled!`) } }) }, }) const schema = { type: 'object', properties: { array: { type: 'array', 'x-decorator': 'FormItem', 'x-component': 'ArrayTable', 'x-component-props': { pagination: { pageSize: 10 }, scroll: { x: '100%' }, }, items: { type: 'object', properties: { column1: { type: 'void', 'x-component': 'ArrayTable.Column', 'x-component-props': { width: 80, title: 'Index', align: 'center' }, properties: { index: { type: 'void', 'x-component': 'ArrayTable.Index', }, }, }, column2: { type: 'void', 'x-component': 'ArrayTable.Column', 'x-component-props': { width: 200, title: 'A1' }, properties: { a1: { type: 'string', 'x-decorator': 'FormItem', 'x-component': 'Input', }, }, }, column3: { type: 'void', 'x-component': 'ArrayTable.Column', 'x-component-props': { width: 200, title: 'A2' }, properties: { a2: { type: 'string', 'x-decorator': 'FormItem', 'x-component': 'Input', }, }, }, column4: { 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: 'Add two entries', }, }, }, }, } export default () => { return ( <FormProvider form={form}> <SchemaField schema={schema} /> <FormButtonGroup> <Submit onSubmit={console.log}>Submit</Submit> </FormButtonGroup> </FormProvider> ) } ``` ## API ### ArrayTable > Form Components Extended attributes | Property name | Type | Description | Default value | | ------------- | ------------------------- | --------------- | ------------- | | onAdd | `(index: number) => void` | add method | | | onRemove | `(index: number) => void` | remove method | | | onCopy | `(index: number) => void` | copy method | | | onMoveUp | `(index: number) => void` | moveUp method | | | onMoveDown | `(index: number) => void` | moveDown method | | Other Reference https://ant.design/components/table-cn/ ### ArrayTable.Column > Table Column Reference https://ant.design/components/table-cn/ ### ArrayTable.SortHandle > Drag handle Reference https://ant.design/components/icon-cn/ ### ArrayTable.Addition > Add button Extended attributes | Property name | Type | Description | Default value | | ------------- | -------------------- | ------------- | ------------- | | title | ReactText | Copywriting | | | method | `'push' \|'unshift'` | add method | `'push'` | | defaultValue | `any` | Default value | | Other references https://ant.design/components/button-cn/ Note: The title attribute can receive the title mapping in the Field model, that is, uploading the title in the Field is also effective Note: You can disable default behavior with `onClick={e => e.preventDefault()}` in props. ### ArrayTable.Remove > Delete button | Property name | Type | Description | Default value | | ------------- | --------- | ----------- | ------------- | | title | ReactText | Copywriting | | Other references https://ant.design/components/icon-cn/ Note: The title attribute can receive the title mapping in the Field model, that is, uploading the title in the Field is also effective Note: You can disable default behavior with `onClick={e => e.preventDefault()}` in props. ### ArrayTable.Copy > Copy button | Property name | Type | Description | Default value | | ------------- | --------- | ----------- | ------------- | | title | ReactText | Copywriting | | Other references https://ant.design/components/icon-cn/ Note: The title attribute can receive the title mapping in the Field model, that is, uploading the title in the Field is also effective Note: You can disable default behavior with `onClick={e => e.preventDefault()}` in props. ### ArrayTable.MoveDown > Move down button | Property name | Type | Description | Default value | | ------------- | --------- | ----------- | ------------- | | title | ReactText | Copywriting | | Other references https://ant.design/components/icon-cn/ Note: The title attribute can receive the title mapping in the Field model, that is, uploading the title in the Field is also effective Note: You can disable default behavior with `onClick={e => e.preventDefault()}` in props. ### ArrayTable.MoveUp > Move up button | Property name | Type | Description | Default value | | ------------- | --------- | ----------- | ------------- | | title | ReactText | Copywriting | | Other references https://ant.design/components/icon-cn/ Note: The title attribute can receive the title mapping in the Field model, that is, uploading the title in the Field is also effective Note: You can disable default behavior with `onClick={e => e.preventDefault()}` in props. ### ArrayTable.Index > Index Renderer No attributes ### ArrayTable.useIndex > Read the React Hook of the current rendering row index ### ArrayTable.useRecord > Read the React Hook of the current rendering row ```