This is page 22 of 52. Use http://codebase.md/alibaba/formily?lines=true&page={x} to view the full context. # Directory Structure ``` ├── .all-contributorsrc ├── .codecov.yml ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .github │ ├── CONTRIBUTING.md │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE │ │ └── config.yml │ ├── PULL_REQUEST_TEMPLATE.md │ └── workflows │ ├── check-pr-title.yml │ ├── ci.yml │ ├── commitlint.yml │ ├── issue-open-check.yml │ ├── package-size.yml │ └── pr-welcome.yml ├── .gitignore ├── .prettierrc.js ├── .umirc.js ├── .vscode │ └── cspell.json ├── .yarnrc ├── CHANGELOG.md ├── commitlint.config.js ├── devtools │ ├── .eslintrc │ └── chrome-extension │ ├── .npmignore │ ├── assets │ │ └── img │ │ ├── loading.svg │ │ └── logo │ │ ├── 128x128.png │ │ ├── 16x16.png │ │ ├── 38x38.png │ │ ├── 48x48.png │ │ ├── error.png │ │ ├── gray.png │ │ └── scalable.png │ ├── config │ │ ├── webpack.base.ts │ │ ├── webpack.dev.ts │ │ └── webpack.prod.ts │ ├── LICENSE.md │ ├── package.json │ ├── src │ │ ├── app │ │ │ ├── components │ │ │ │ ├── FieldTree.tsx │ │ │ │ ├── filter.ts │ │ │ │ ├── LeftPanel.tsx │ │ │ │ ├── RightPanel.tsx │ │ │ │ ├── SearchBox.tsx │ │ │ │ └── Tabs.tsx │ │ │ ├── demo.tsx │ │ │ └── index.tsx │ │ └── extension │ │ ├── backend.ts │ │ ├── background.ts │ │ ├── content.ts │ │ ├── devpanel.tsx │ │ ├── devtools.tsx │ │ ├── inject.ts │ │ ├── manifest.json │ │ ├── popup.tsx │ │ └── views │ │ ├── devpanel.ejs │ │ ├── devtools.ejs │ │ └── popup.ejs │ ├── tsconfig.build.json │ └── tsconfig.json ├── docs │ ├── functions │ │ ├── contributors.ts │ │ └── npm-search.ts │ ├── guide │ │ ├── advanced │ │ │ ├── async.md │ │ │ ├── async.zh-CN.md │ │ │ ├── build.md │ │ │ ├── build.zh-CN.md │ │ │ ├── business-logic.md │ │ │ ├── business-logic.zh-CN.md │ │ │ ├── calculator.md │ │ │ ├── calculator.zh-CN.md │ │ │ ├── controlled.md │ │ │ ├── controlled.zh-CN.md │ │ │ ├── custom.md │ │ │ ├── custom.zh-CN.md │ │ │ ├── destructor.md │ │ │ ├── destructor.zh-CN.md │ │ │ ├── input.less │ │ │ ├── layout.md │ │ │ ├── layout.zh-CN.md │ │ │ ├── linkages.md │ │ │ ├── linkages.zh-CN.md │ │ │ ├── validate.md │ │ │ └── validate.zh-CN.md │ │ ├── contribution.md │ │ ├── contribution.zh-CN.md │ │ ├── form-builder.md │ │ ├── form-builder.zh-CN.md │ │ ├── index.md │ │ ├── index.zh-CN.md │ │ ├── issue-helper.md │ │ ├── issue-helper.zh-CN.md │ │ ├── learn-formily.md │ │ ├── learn-formily.zh-CN.md │ │ ├── quick-start.md │ │ ├── quick-start.zh-CN.md │ │ ├── scenes │ │ │ ├── dialog-drawer.md │ │ │ ├── dialog-drawer.zh-CN.md │ │ │ ├── edit-detail.md │ │ │ ├── edit-detail.zh-CN.md │ │ │ ├── index.less │ │ │ ├── login-register.md │ │ │ ├── login-register.zh-CN.md │ │ │ ├── more.md │ │ │ ├── more.zh-CN.md │ │ │ ├── query-list.md │ │ │ ├── query-list.zh-CN.md │ │ │ ├── step-form.md │ │ │ ├── step-form.zh-CN.md │ │ │ ├── tab-form.md │ │ │ ├── tab-form.zh-CN.md │ │ │ └── VerifyCode.tsx │ │ ├── upgrade.md │ │ └── upgrade.zh-CN.md │ ├── index.md │ ├── index.zh-CN.md │ └── site │ ├── Contributors.less │ ├── Contributors.tsx │ ├── QrCode.less │ ├── QrCode.tsx │ ├── Section.less │ ├── Section.tsx │ └── styles.less ├── global.config.ts ├── jest.config.js ├── lerna.json ├── LICENSE.md ├── package.json ├── packages │ ├── .eslintrc │ ├── antd │ │ ├── __tests__ │ │ │ ├── moment.spec.ts │ │ │ └── sideEffects.spec.ts │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── build-style.ts │ │ ├── create-style.ts │ │ ├── docs │ │ │ ├── components │ │ │ │ ├── ArrayCards.md │ │ │ │ ├── ArrayCards.zh-CN.md │ │ │ │ ├── ArrayCollapse.md │ │ │ │ ├── ArrayCollapse.zh-CN.md │ │ │ │ ├── ArrayItems.md │ │ │ │ ├── ArrayItems.zh-CN.md │ │ │ │ ├── ArrayTable.md │ │ │ │ ├── ArrayTable.zh-CN.md │ │ │ │ ├── ArrayTabs.md │ │ │ │ ├── ArrayTabs.zh-CN.md │ │ │ │ ├── Cascader.md │ │ │ │ ├── Cascader.zh-CN.md │ │ │ │ ├── Checkbox.md │ │ │ │ ├── Checkbox.zh-CN.md │ │ │ │ ├── DatePicker.md │ │ │ │ ├── DatePicker.zh-CN.md │ │ │ │ ├── Editable.md │ │ │ │ ├── Editable.zh-CN.md │ │ │ │ ├── Form.md │ │ │ │ ├── Form.zh-CN.md │ │ │ │ ├── FormButtonGroup.md │ │ │ │ ├── FormButtonGroup.zh-CN.md │ │ │ │ ├── FormCollapse.md │ │ │ │ ├── FormCollapse.zh-CN.md │ │ │ │ ├── FormDialog.md │ │ │ │ ├── FormDialog.zh-CN.md │ │ │ │ ├── FormDrawer.md │ │ │ │ ├── FormDrawer.zh-CN.md │ │ │ │ ├── FormGrid.md │ │ │ │ ├── FormGrid.zh-CN.md │ │ │ │ ├── FormItem.md │ │ │ │ ├── FormItem.zh-CN.md │ │ │ │ ├── FormLayout.md │ │ │ │ ├── FormLayout.zh-CN.md │ │ │ │ ├── FormStep.md │ │ │ │ ├── FormStep.zh-CN.md │ │ │ │ ├── FormTab.md │ │ │ │ ├── FormTab.zh-CN.md │ │ │ │ ├── index.md │ │ │ │ ├── index.zh-CN.md │ │ │ │ ├── Input.md │ │ │ │ ├── Input.zh-CN.md │ │ │ │ ├── NumberPicker.md │ │ │ │ ├── NumberPicker.zh-CN.md │ │ │ │ ├── Password.md │ │ │ │ ├── Password.zh-CN.md │ │ │ │ ├── PreviewText.md │ │ │ │ ├── PreviewText.zh-CN.md │ │ │ │ ├── Radio.md │ │ │ │ ├── Radio.zh-CN.md │ │ │ │ ├── Reset.md │ │ │ │ ├── Reset.zh-CN.md │ │ │ │ ├── Select.md │ │ │ │ ├── Select.zh-CN.md │ │ │ │ ├── SelectTable.md │ │ │ │ ├── SelectTable.zh-CN.md │ │ │ │ ├── Space.md │ │ │ │ ├── Space.zh-CN.md │ │ │ │ ├── Submit.md │ │ │ │ ├── Submit.zh-CN.md │ │ │ │ ├── Switch.md │ │ │ │ ├── Switch.zh-CN.md │ │ │ │ ├── TimePicker.md │ │ │ │ ├── TimePicker.zh-CN.md │ │ │ │ ├── Transfer.md │ │ │ │ ├── Transfer.zh-CN.md │ │ │ │ ├── TreeSelect.md │ │ │ │ ├── TreeSelect.zh-CN.md │ │ │ │ ├── Upload.md │ │ │ │ └── Upload.zh-CN.md │ │ │ ├── index.md │ │ │ └── index.zh-CN.md │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __builtins__ │ │ │ │ ├── hooks │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── useClickAway.ts │ │ │ │ │ └── usePrefixCls.ts │ │ │ │ ├── index.ts │ │ │ │ ├── loading.ts │ │ │ │ ├── moment.ts │ │ │ │ ├── pickDataProps.ts │ │ │ │ ├── portal.tsx │ │ │ │ ├── render.ts │ │ │ │ └── sort.tsx │ │ │ ├── array-base │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── array-cards │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── array-collapse │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── array-items │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── array-table │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── array-tabs │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── cascader │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── checkbox │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── date-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── editable │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── form │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── form-button-group │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── form-collapse │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-dialog │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-drawer │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-grid │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── form-item │ │ │ │ ├── animation.less │ │ │ │ ├── grid.less │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── form-layout │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ ├── style.ts │ │ │ │ └── useResponsiveFormLayout.ts │ │ │ ├── form-step │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-tab │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── index.ts │ │ │ ├── input │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── number-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── password │ │ │ │ ├── index.tsx │ │ │ │ ├── PasswordStrength.tsx │ │ │ │ └── style.ts │ │ │ ├── preview-text │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── radio │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── reset │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── select │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── select-table │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ ├── style.ts │ │ │ │ ├── useCheckSlackly.tsx │ │ │ │ ├── useFilterOptions.tsx │ │ │ │ ├── useFlatOptions.tsx │ │ │ │ ├── useSize.tsx │ │ │ │ ├── useTitleAddon.tsx │ │ │ │ └── utils.ts │ │ │ ├── space │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── style.less │ │ │ ├── style.ts │ │ │ ├── submit │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── switch │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── time-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── transfer │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── tree-select │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ └── upload │ │ │ ├── index.tsx │ │ │ ├── placeholder.ts │ │ │ └── style.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── benchmark │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── src │ │ │ └── index.tsx │ │ ├── template.ejs │ │ ├── tsconfig.build.json │ │ ├── tsconfig.json │ │ ├── webpack.base.ts │ │ ├── webpack.dev.ts │ │ └── webpack.prod.ts │ ├── core │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── docs │ │ │ ├── api │ │ │ │ ├── entry │ │ │ │ │ ├── ActionResponse.less │ │ │ │ │ ├── ActionResponse.tsx │ │ │ │ │ ├── createForm.md │ │ │ │ │ ├── createForm.zh-CN.md │ │ │ │ │ ├── FieldEffectHooks.md │ │ │ │ │ ├── FieldEffectHooks.zh-CN.md │ │ │ │ │ ├── FormChecker.md │ │ │ │ │ ├── FormChecker.zh-CN.md │ │ │ │ │ ├── FormEffectHooks.md │ │ │ │ │ ├── FormEffectHooks.zh-CN.md │ │ │ │ │ ├── FormHooksAPI.md │ │ │ │ │ ├── FormHooksAPI.zh-CN.md │ │ │ │ │ ├── FormPath.md │ │ │ │ │ ├── FormPath.zh-CN.md │ │ │ │ │ ├── FormValidatorRegistry.md │ │ │ │ │ └── FormValidatorRegistry.zh-CN.md │ │ │ │ └── models │ │ │ │ ├── ArrayField.md │ │ │ │ ├── ArrayField.zh-CN.md │ │ │ │ ├── Field.md │ │ │ │ ├── Field.zh-CN.md │ │ │ │ ├── Form.md │ │ │ │ ├── Form.zh-CN.md │ │ │ │ ├── ObjectField.md │ │ │ │ ├── ObjectField.zh-CN.md │ │ │ │ ├── Query.md │ │ │ │ ├── Query.zh-CN.md │ │ │ │ ├── VoidField.md │ │ │ │ └── VoidField.zh-CN.md │ │ │ ├── guide │ │ │ │ ├── architecture.md │ │ │ │ ├── architecture.zh-CN.md │ │ │ │ ├── field.md │ │ │ │ ├── field.zh-CN.md │ │ │ │ ├── form.md │ │ │ │ ├── form.zh-CN.md │ │ │ │ ├── index.md │ │ │ │ ├── index.zh-CN.md │ │ │ │ ├── mvvm.md │ │ │ │ ├── mvvm.zh-CN.md │ │ │ │ ├── values.md │ │ │ │ └── values.zh-CN.md │ │ │ ├── index.md │ │ │ └── index.zh-CN.md │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── array.spec.ts │ │ │ │ ├── effects.spec.ts │ │ │ │ ├── externals.spec.ts │ │ │ │ ├── field.spec.ts │ │ │ │ ├── form.spec.ts │ │ │ │ ├── graph.spec.ts │ │ │ │ ├── heart.spec.ts │ │ │ │ ├── internals.spec.ts │ │ │ │ ├── lifecycle.spec.ts │ │ │ │ ├── object.spec.ts │ │ │ │ ├── shared.ts │ │ │ │ └── void.spec.ts │ │ │ ├── effects │ │ │ │ ├── index.ts │ │ │ │ ├── onFieldEffects.ts │ │ │ │ └── onFormEffects.ts │ │ │ ├── global.d.ts │ │ │ ├── index.ts │ │ │ ├── models │ │ │ │ ├── ArrayField.ts │ │ │ │ ├── BaseField.ts │ │ │ │ ├── Field.ts │ │ │ │ ├── Form.ts │ │ │ │ ├── Graph.ts │ │ │ │ ├── Heart.ts │ │ │ │ ├── index.ts │ │ │ │ ├── LifeCycle.ts │ │ │ │ ├── ObjectField.ts │ │ │ │ ├── Query.ts │ │ │ │ ├── types.ts │ │ │ │ └── VoidField.ts │ │ │ ├── shared │ │ │ │ ├── checkers.ts │ │ │ │ ├── constants.ts │ │ │ │ ├── effective.ts │ │ │ │ ├── externals.ts │ │ │ │ └── internals.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── element │ │ ├── .npmignore │ │ ├── build-style.ts │ │ ├── create-style.ts │ │ ├── docs │ │ │ ├── .vuepress │ │ │ │ ├── components │ │ │ │ │ ├── createCodeSandBox.js │ │ │ │ │ ├── dumi-previewer.vue │ │ │ │ │ └── highlight.js │ │ │ │ ├── config.js │ │ │ │ ├── enhanceApp.js │ │ │ │ ├── styles │ │ │ │ │ └── index.styl │ │ │ │ └── util.js │ │ │ ├── demos │ │ │ │ ├── guide │ │ │ │ │ ├── array-cards │ │ │ │ │ │ ├── effects-json-schema.vue │ │ │ │ │ │ ├── effects-markup-schema.vue │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── array-collapse │ │ │ │ │ │ ├── effects-json-schema.vue │ │ │ │ │ │ ├── effects-markup-schema.vue │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── array-items │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── array-table │ │ │ │ │ │ ├── effects-json-schema.vue │ │ │ │ │ │ ├── effects-markup-schema.vue │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── array-tabs │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── cascader │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── checkbox │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── date-picker │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── editable │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── form-button-group.vue │ │ │ │ │ ├── form-collapse │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── form-dialog │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── form-drawer │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── form-grid │ │ │ │ │ │ ├── form.vue │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── native.vue │ │ │ │ │ ├── form-item │ │ │ │ │ │ ├── bordered-none.vue │ │ │ │ │ │ ├── common.vue │ │ │ │ │ │ ├── feedback.vue │ │ │ │ │ │ ├── inset.vue │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ ├── size.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── form-layout │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── form-step │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── form-tab │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── form.vue │ │ │ │ │ ├── input │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── input-number │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── password │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── preview-text │ │ │ │ │ │ ├── base.vue │ │ │ │ │ │ └── extend.vue │ │ │ │ │ ├── radio │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── reset │ │ │ │ │ │ ├── base.vue │ │ │ │ │ │ ├── force.vue │ │ │ │ │ │ └── validate.vue │ │ │ │ │ ├── select │ │ │ │ │ │ ├── json-schema-async.vue │ │ │ │ │ │ ├── json-schema-sync.vue │ │ │ │ │ │ ├── markup-schema-async-search.vue │ │ │ │ │ │ ├── markup-schema-async.vue │ │ │ │ │ │ ├── markup-schema-sync.vue │ │ │ │ │ │ ├── template-async.vue │ │ │ │ │ │ └── template-sync.vue │ │ │ │ │ ├── space │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── submit │ │ │ │ │ │ ├── base.vue │ │ │ │ │ │ └── loading.vue │ │ │ │ │ ├── switch │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── time-picker │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── transfer │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ └── upload │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ └── template.vue │ │ │ │ └── index.vue │ │ │ ├── guide │ │ │ │ ├── array-cards.md │ │ │ │ ├── array-collapse.md │ │ │ │ ├── array-items.md │ │ │ │ ├── array-table.md │ │ │ │ ├── array-tabs.md │ │ │ │ ├── cascader.md │ │ │ │ ├── checkbox.md │ │ │ │ ├── date-picker.md │ │ │ │ ├── editable.md │ │ │ │ ├── form-button-group.md │ │ │ │ ├── form-collapse.md │ │ │ │ ├── form-dialog.md │ │ │ │ ├── form-drawer.md │ │ │ │ ├── form-grid.md │ │ │ │ ├── form-item.md │ │ │ │ ├── form-layout.md │ │ │ │ ├── form-step.md │ │ │ │ ├── form-tab.md │ │ │ │ ├── form.md │ │ │ │ ├── index.md │ │ │ │ ├── input-number.md │ │ │ │ ├── input.md │ │ │ │ ├── password.md │ │ │ │ ├── preview-text.md │ │ │ │ ├── radio.md │ │ │ │ ├── reset.md │ │ │ │ ├── select.md │ │ │ │ ├── space.md │ │ │ │ ├── submit.md │ │ │ │ ├── switch.md │ │ │ │ ├── time-picker.md │ │ │ │ ├── transfer.md │ │ │ │ └── upload.md │ │ │ └── README.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __builtins__ │ │ │ │ ├── configs │ │ │ │ │ └── index.ts │ │ │ │ ├── index.ts │ │ │ │ ├── shared │ │ │ │ │ ├── create-context.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── loading.ts │ │ │ │ │ ├── portal.ts │ │ │ │ │ ├── resolve-component.ts │ │ │ │ │ ├── transform-component.ts │ │ │ │ │ ├── types.ts │ │ │ │ │ └── utils.ts │ │ │ │ └── styles │ │ │ │ └── common.scss │ │ │ ├── array-base │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── array-cards │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── array-collapse │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── array-items │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── array-table │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── array-tabs │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── cascader │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── checkbox │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── date-picker │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── editable │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── el-form │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── el-form-item │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── form │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── form-button-group │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── form-collapse │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── form-dialog │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── form-drawer │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── form-grid │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── form-item │ │ │ │ ├── animation.scss │ │ │ │ ├── grid.scss │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ ├── style.ts │ │ │ │ └── var.scss │ │ │ ├── form-layout │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ ├── style.ts │ │ │ │ └── useResponsiveFormLayout.ts │ │ │ ├── form-step │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── form-tab │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── index.ts │ │ │ ├── input │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── input-number │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── password │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── preview-text │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── radio │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── reset │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── select │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── space │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── style.ts │ │ │ ├── submit │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── switch │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── time-picker │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── transfer │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ └── upload │ │ │ ├── index.ts │ │ │ └── style.ts │ │ ├── transformer.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── grid │ │ ├── .npmignore │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── index.ts │ │ │ └── observer.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── json-schema │ │ ├── .npmignore │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── __snapshots__ │ │ │ │ │ └── schema.spec.ts.snap │ │ │ │ ├── compiler.spec.ts │ │ │ │ ├── patches.spec.ts │ │ │ │ ├── schema.spec.ts │ │ │ │ ├── server-validate.spec.ts │ │ │ │ ├── shared.spec.ts │ │ │ │ ├── transformer.spec.ts │ │ │ │ └── traverse.spec.ts │ │ │ ├── compiler.ts │ │ │ ├── global.d.ts │ │ │ ├── index.ts │ │ │ ├── patches.ts │ │ │ ├── polyfills │ │ │ │ ├── index.ts │ │ │ │ └── SPECIFICATION_1_0.ts │ │ │ ├── schema.ts │ │ │ ├── shared.ts │ │ │ ├── transformer.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── next │ │ ├── __tests__ │ │ │ ├── moment.spec.ts │ │ │ └── sideEffects.spec.ts │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── build-style.ts │ │ ├── create-style.ts │ │ ├── docs │ │ │ ├── components │ │ │ │ ├── ArrayCards.md │ │ │ │ ├── ArrayCards.zh-CN.md │ │ │ │ ├── ArrayCollapse.md │ │ │ │ ├── ArrayCollapse.zh-CN.md │ │ │ │ ├── ArrayItems.md │ │ │ │ ├── ArrayItems.zh-CN.md │ │ │ │ ├── ArrayTable.md │ │ │ │ ├── ArrayTable.zh-CN.md │ │ │ │ ├── Cascader.md │ │ │ │ ├── Cascader.zh-CN.md │ │ │ │ ├── Checkbox.md │ │ │ │ ├── Checkbox.zh-CN.md │ │ │ │ ├── DatePicker.md │ │ │ │ ├── DatePicker.zh-CN.md │ │ │ │ ├── DatePicker2.md │ │ │ │ ├── DatePicker2.zh-CN.md │ │ │ │ ├── Editable.md │ │ │ │ ├── Editable.zh-CN.md │ │ │ │ ├── Form.md │ │ │ │ ├── Form.zh-CN.md │ │ │ │ ├── FormButtonGroup.md │ │ │ │ ├── FormButtonGroup.zh-CN.md │ │ │ │ ├── FormCollapse.md │ │ │ │ ├── FormCollapse.zh-CN.md │ │ │ │ ├── FormDialog.md │ │ │ │ ├── FormDialog.zh-CN.md │ │ │ │ ├── FormDrawer.md │ │ │ │ ├── FormDrawer.zh-CN.md │ │ │ │ ├── FormGrid.md │ │ │ │ ├── FormGrid.zh-CN.md │ │ │ │ ├── FormItem.md │ │ │ │ ├── FormItem.zh-CN.md │ │ │ │ ├── FormLayout.md │ │ │ │ ├── FormLayout.zh-CN.md │ │ │ │ ├── FormStep.md │ │ │ │ ├── FormStep.zh-CN.md │ │ │ │ ├── FormTab.md │ │ │ │ ├── FormTab.zh-CN.md │ │ │ │ ├── index.md │ │ │ │ ├── index.zh-CN.md │ │ │ │ ├── Input.md │ │ │ │ ├── Input.zh-CN.md │ │ │ │ ├── NumberPicker.md │ │ │ │ ├── NumberPicker.zh-CN.md │ │ │ │ ├── Password.md │ │ │ │ ├── Password.zh-CN.md │ │ │ │ ├── PreviewText.md │ │ │ │ ├── PreviewText.zh-CN.md │ │ │ │ ├── Radio.md │ │ │ │ ├── Radio.zh-CN.md │ │ │ │ ├── Reset.md │ │ │ │ ├── Reset.zh-CN.md │ │ │ │ ├── Select.md │ │ │ │ ├── Select.zh-CN.md │ │ │ │ ├── SelectTable.md │ │ │ │ ├── SelectTable.zh-CN.md │ │ │ │ ├── Space.md │ │ │ │ ├── Space.zh-CN.md │ │ │ │ ├── Submit.md │ │ │ │ ├── Submit.zh-CN.md │ │ │ │ ├── Switch.md │ │ │ │ ├── Switch.zh-CN.md │ │ │ │ ├── TimePicker.md │ │ │ │ ├── TimePicker.zh-CN.md │ │ │ │ ├── TimePicker2.md │ │ │ │ ├── TimePicker2.zh-CN.md │ │ │ │ ├── Transfer.md │ │ │ │ ├── Transfer.zh-CN.md │ │ │ │ ├── TreeSelect.md │ │ │ │ ├── TreeSelect.zh-CN.md │ │ │ │ ├── Upload.md │ │ │ │ └── Upload.zh-CN.md │ │ │ ├── index.md │ │ │ └── index.zh-CN.md │ │ ├── LESENCE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __builtins__ │ │ │ │ ├── empty.tsx │ │ │ │ ├── hooks │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── useClickAway.ts │ │ │ │ │ └── usePrefixCls.ts │ │ │ │ ├── icons.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── loading.ts │ │ │ │ ├── mapSize.ts │ │ │ │ ├── mapStatus.ts │ │ │ │ ├── moment.ts │ │ │ │ ├── pickDataProps.ts │ │ │ │ ├── portal.tsx │ │ │ │ ├── render.ts │ │ │ │ └── toArray.ts │ │ │ ├── array-base │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── array-cards │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── array-collapse │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── array-items │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── array-table │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── cascader │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── checkbox │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── date-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── date-picker2 │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── editable │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── form │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── form-button-group │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── form-collapse │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-dialog │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-drawer │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-grid │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── form-item │ │ │ │ ├── animation.scss │ │ │ │ ├── grid.scss │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ ├── scss │ │ │ │ │ └── variable.scss │ │ │ │ └── style.ts │ │ │ ├── form-layout │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ ├── style.ts │ │ │ │ └── useResponsiveFormLayout.ts │ │ │ ├── form-step │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-tab │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── index.ts │ │ │ ├── input │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── main.scss │ │ │ ├── number-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── password │ │ │ │ ├── index.tsx │ │ │ │ ├── PasswordStrength.tsx │ │ │ │ └── style.ts │ │ │ ├── preview-text │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── radio │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── reset │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── select │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── select-table │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ ├── style.ts │ │ │ │ ├── useCheckSlackly.tsx │ │ │ │ ├── useFilterOptions.tsx │ │ │ │ ├── useFlatOptions.tsx │ │ │ │ ├── useSize.tsx │ │ │ │ ├── useTitleAddon.tsx │ │ │ │ └── utils.ts │ │ │ ├── space │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── style.ts │ │ │ ├── submit │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── switch │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── time-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── time-picker2 │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── transfer │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── tree-select │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ └── upload │ │ │ ├── index.tsx │ │ │ ├── main.scss │ │ │ ├── placeholder.ts │ │ │ └── style.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── path │ │ ├── .npmignore │ │ ├── benchmark.ts │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── accessor.spec.ts │ │ │ │ ├── basic.spec.ts │ │ │ │ ├── match.spec.ts │ │ │ │ ├── parser.spec.ts │ │ │ │ └── share.spec.ts │ │ │ ├── contexts.ts │ │ │ ├── destructor.ts │ │ │ ├── index.ts │ │ │ ├── matcher.ts │ │ │ ├── parser.ts │ │ │ ├── shared.ts │ │ │ ├── tokenizer.ts │ │ │ ├── tokens.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── react │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── docs │ │ │ ├── api │ │ │ │ ├── components │ │ │ │ │ ├── ArrayField.md │ │ │ │ │ ├── ArrayField.zh-CN.md │ │ │ │ │ ├── ExpressionScope.md │ │ │ │ │ ├── ExpressionScope.zh-CN.md │ │ │ │ │ ├── Field.md │ │ │ │ │ ├── Field.zh-CN.md │ │ │ │ │ ├── FormConsumer.md │ │ │ │ │ ├── FormConsumer.zh-CN.md │ │ │ │ │ ├── FormProvider.md │ │ │ │ │ ├── FormProvider.zh-CN.md │ │ │ │ │ ├── ObjectField.md │ │ │ │ │ ├── ObjectField.zh-CN.md │ │ │ │ │ ├── RecordScope.md │ │ │ │ │ ├── RecordScope.zh-CN.md │ │ │ │ │ ├── RecordsScope.md │ │ │ │ │ ├── RecordsScope.zh-CN.md │ │ │ │ │ ├── RecursionField.md │ │ │ │ │ ├── RecursionField.zh-CN.md │ │ │ │ │ ├── SchemaField.md │ │ │ │ │ ├── SchemaField.zh-CN.md │ │ │ │ │ ├── VoidField.md │ │ │ │ │ └── VoidField.zh-CN.md │ │ │ │ ├── hooks │ │ │ │ │ ├── useExpressionScope.md │ │ │ │ │ ├── useExpressionScope.zh-CN.md │ │ │ │ │ ├── useField.md │ │ │ │ │ ├── useField.zh-CN.md │ │ │ │ │ ├── useFieldSchema.md │ │ │ │ │ ├── useFieldSchema.zh-CN.md │ │ │ │ │ ├── useForm.md │ │ │ │ │ ├── useForm.zh-CN.md │ │ │ │ │ ├── useFormEffects.md │ │ │ │ │ ├── useFormEffects.zh-CN.md │ │ │ │ │ ├── useParentForm.md │ │ │ │ │ └── useParentForm.zh-CN.md │ │ │ │ └── shared │ │ │ │ ├── connect.md │ │ │ │ ├── connect.zh-CN.md │ │ │ │ ├── context.md │ │ │ │ ├── context.zh-CN.md │ │ │ │ ├── mapProps.md │ │ │ │ ├── mapProps.zh-CN.md │ │ │ │ ├── mapReadPretty.md │ │ │ │ ├── mapReadPretty.zh-CN.md │ │ │ │ ├── observer.md │ │ │ │ ├── observer.zh-CN.md │ │ │ │ ├── Schema.md │ │ │ │ └── Schema.zh-CN.md │ │ │ ├── guide │ │ │ │ ├── architecture.md │ │ │ │ ├── architecture.zh-CN.md │ │ │ │ ├── concept.md │ │ │ │ ├── concept.zh-CN.md │ │ │ │ ├── index.md │ │ │ │ └── index.zh-CN.md │ │ │ ├── index.md │ │ │ └── index.zh-CN.md │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── expression.spec.tsx │ │ │ │ ├── field.spec.tsx │ │ │ │ ├── form.spec.tsx │ │ │ │ ├── schema.json.spec.tsx │ │ │ │ ├── schema.markup.spec.tsx │ │ │ │ └── shared.tsx │ │ │ ├── components │ │ │ │ ├── ArrayField.tsx │ │ │ │ ├── ExpressionScope.tsx │ │ │ │ ├── Field.tsx │ │ │ │ ├── FormConsumer.tsx │ │ │ │ ├── FormProvider.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── ObjectField.tsx │ │ │ │ ├── ReactiveField.tsx │ │ │ │ ├── RecordScope.tsx │ │ │ │ ├── RecordsScope.tsx │ │ │ │ ├── RecursionField.tsx │ │ │ │ ├── SchemaField.tsx │ │ │ │ └── VoidField.tsx │ │ │ ├── global.d.ts │ │ │ ├── hooks │ │ │ │ ├── index.ts │ │ │ │ ├── useAttach.ts │ │ │ │ ├── useExpressionScope.ts │ │ │ │ ├── useField.ts │ │ │ │ ├── useFieldSchema.ts │ │ │ │ ├── useForm.ts │ │ │ │ ├── useFormEffects.ts │ │ │ │ └── useParentForm.ts │ │ │ ├── index.ts │ │ │ ├── shared │ │ │ │ ├── connect.ts │ │ │ │ ├── context.ts │ │ │ │ ├── index.ts │ │ │ │ └── render.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── reactive │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── benchmark.ts │ │ ├── docs │ │ │ ├── api │ │ │ │ ├── action.md │ │ │ │ ├── action.zh-CN.md │ │ │ │ ├── autorun.md │ │ │ │ ├── autorun.zh-CN.md │ │ │ │ ├── batch.md │ │ │ │ ├── batch.zh-CN.md │ │ │ │ ├── define.md │ │ │ │ ├── define.zh-CN.md │ │ │ │ ├── hasCollected.md │ │ │ │ ├── hasCollected.zh-CN.md │ │ │ │ ├── markObservable.md │ │ │ │ ├── markObservable.zh-CN.md │ │ │ │ ├── markRaw.md │ │ │ │ ├── markRaw.zh-CN.md │ │ │ │ ├── model.md │ │ │ │ ├── model.zh-CN.md │ │ │ │ ├── observable.md │ │ │ │ ├── observable.zh-CN.md │ │ │ │ ├── observe.md │ │ │ │ ├── observe.zh-CN.md │ │ │ │ ├── raw.md │ │ │ │ ├── raw.zh-CN.md │ │ │ │ ├── react │ │ │ │ │ ├── observer.md │ │ │ │ │ └── observer.zh-CN.md │ │ │ │ ├── reaction.md │ │ │ │ ├── reaction.zh-CN.md │ │ │ │ ├── toJS.md │ │ │ │ ├── toJS.zh-CN.md │ │ │ │ ├── tracker.md │ │ │ │ ├── tracker.zh-CN.md │ │ │ │ ├── typeChecker.md │ │ │ │ ├── typeChecker.zh-CN.md │ │ │ │ ├── untracked.md │ │ │ │ ├── untracked.zh-CN.md │ │ │ │ └── vue │ │ │ │ ├── observer.md │ │ │ │ └── observer.zh-CN.md │ │ │ ├── guide │ │ │ │ ├── best-practice.md │ │ │ │ ├── best-practice.zh-CN.md │ │ │ │ ├── concept.md │ │ │ │ ├── concept.zh-CN.md │ │ │ │ ├── index.md │ │ │ │ └── index.zh-CN.md │ │ │ ├── index.md │ │ │ └── index.zh-CN.md │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── action.spec.ts │ │ │ │ ├── annotations.spec.ts │ │ │ │ ├── array.spec.ts │ │ │ │ ├── autorun.spec.ts │ │ │ │ ├── batch.spec.ts │ │ │ │ ├── collections-map.spec.ts │ │ │ │ ├── collections-set.spec.ts │ │ │ │ ├── collections-weakmap.spec.ts │ │ │ │ ├── collections-weakset.spec.ts │ │ │ │ ├── define.spec.ts │ │ │ │ ├── externals.spec.ts │ │ │ │ ├── hasCollected.spec.ts │ │ │ │ ├── observable.spec.ts │ │ │ │ ├── observe.spec.ts │ │ │ │ ├── tracker.spec.ts │ │ │ │ └── untracked.spec.ts │ │ │ ├── action.ts │ │ │ ├── annotations │ │ │ │ ├── box.ts │ │ │ │ ├── computed.ts │ │ │ │ ├── index.ts │ │ │ │ ├── observable.ts │ │ │ │ ├── ref.ts │ │ │ │ └── shallow.ts │ │ │ ├── array.ts │ │ │ ├── autorun.ts │ │ │ ├── batch.ts │ │ │ ├── checkers.ts │ │ │ ├── environment.ts │ │ │ ├── externals.ts │ │ │ ├── global.d.ts │ │ │ ├── handlers.ts │ │ │ ├── index.ts │ │ │ ├── internals.ts │ │ │ ├── model.ts │ │ │ ├── observable.ts │ │ │ ├── observe.ts │ │ │ ├── reaction.ts │ │ │ ├── tracker.ts │ │ │ ├── tree.ts │ │ │ ├── types.ts │ │ │ └── untracked.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── reactive-react │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── hooks │ │ │ │ ├── index.ts │ │ │ │ ├── useCompatEffect.ts │ │ │ │ ├── useCompatFactory.ts │ │ │ │ ├── useDidUpdate.ts │ │ │ │ ├── useForceUpdate.ts │ │ │ │ ├── useLayoutEffect.ts │ │ │ │ └── useObserver.ts │ │ │ ├── index.ts │ │ │ ├── observer.ts │ │ │ ├── shared │ │ │ │ ├── gc.ts │ │ │ │ ├── global.ts │ │ │ │ ├── immediate.ts │ │ │ │ └── index.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── reactive-test-cases-for-react18 │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── src │ │ │ ├── index.js │ │ │ └── MySlowList.js │ │ ├── template.ejs │ │ ├── tsconfig.build.json │ │ ├── tsconfig.json │ │ ├── webpack.base.ts │ │ ├── webpack.dev.ts │ │ └── webpack.prod.ts │ ├── reactive-vue │ │ ├── .npmignore │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ └── observer.spec.ts │ │ │ ├── hooks │ │ │ │ ├── index.ts │ │ │ │ └── useObserver.ts │ │ │ ├── index.ts │ │ │ ├── observer │ │ │ │ ├── collectData.ts │ │ │ │ ├── index.ts │ │ │ │ ├── observerInVue2.ts │ │ │ │ └── observerInVue3.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── shared │ │ ├── .npmignore │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ └── index.spec.ts │ │ │ ├── array.ts │ │ │ ├── case.ts │ │ │ ├── checkers.ts │ │ │ ├── clone.ts │ │ │ ├── compare.ts │ │ │ ├── defaults.ts │ │ │ ├── deprecate.ts │ │ │ ├── global.ts │ │ │ ├── index.ts │ │ │ ├── instanceof.ts │ │ │ ├── isEmpty.ts │ │ │ ├── merge.ts │ │ │ ├── middleware.ts │ │ │ ├── path.ts │ │ │ ├── string.ts │ │ │ ├── subscribable.ts │ │ │ └── uid.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── validator │ │ ├── .npmignore │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── parser.spec.ts │ │ │ │ ├── registry.spec.ts │ │ │ │ └── validator.spec.ts │ │ │ ├── formats.ts │ │ │ ├── index.ts │ │ │ ├── locale.ts │ │ │ ├── parser.ts │ │ │ ├── registry.ts │ │ │ ├── rules.ts │ │ │ ├── template.ts │ │ │ ├── types.ts │ │ │ └── validator.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ └── vue │ ├── .npmignore │ ├── bin │ │ ├── formily-vue-fix.js │ │ └── formily-vue-switch.js │ ├── docs │ │ ├── .vuepress │ │ │ ├── components │ │ │ │ ├── createCodeSandBox.js │ │ │ │ ├── dumi-previewer.vue │ │ │ │ └── highlight.js │ │ │ ├── config.js │ │ │ ├── enhanceApp.js │ │ │ └── styles │ │ │ └── index.styl │ │ ├── api │ │ │ ├── components │ │ │ │ ├── array-field.md │ │ │ │ ├── expression-scope.md │ │ │ │ ├── field.md │ │ │ │ ├── form-consumer.md │ │ │ │ ├── form-provider.md │ │ │ │ ├── object-field.md │ │ │ │ ├── recursion-field-with-component.md │ │ │ │ ├── recursion-field.md │ │ │ │ ├── schema-field-with-schema.md │ │ │ │ ├── schema-field.md │ │ │ │ └── void-field.md │ │ │ ├── hooks │ │ │ │ ├── use-field-schema.md │ │ │ │ ├── use-field.md │ │ │ │ ├── use-form-effects.md │ │ │ │ ├── use-form.md │ │ │ │ └── use-parent-form.md │ │ │ └── shared │ │ │ ├── connect.md │ │ │ ├── injections.md │ │ │ ├── map-props.md │ │ │ ├── map-read-pretty.md │ │ │ ├── observer.md │ │ │ └── schema.md │ │ ├── demos │ │ │ ├── api │ │ │ │ ├── components │ │ │ │ │ ├── array-field.vue │ │ │ │ │ ├── expression-scope.vue │ │ │ │ │ ├── field.vue │ │ │ │ │ ├── form-consumer.vue │ │ │ │ │ ├── form-provider.vue │ │ │ │ │ ├── object-field.vue │ │ │ │ │ ├── recursion-field-with-component.vue │ │ │ │ │ ├── recursion-field.vue │ │ │ │ │ ├── schema-field-with-schema.vue │ │ │ │ │ ├── schema-field.vue │ │ │ │ │ └── void-field.vue │ │ │ │ ├── hooks │ │ │ │ │ ├── use-field-schema.vue │ │ │ │ │ ├── use-field.vue │ │ │ │ │ ├── use-form-effects.vue │ │ │ │ │ ├── use-form.vue │ │ │ │ │ └── use-parent-form.vue │ │ │ │ └── shared │ │ │ │ ├── connect.vue │ │ │ │ ├── map-props.vue │ │ │ │ ├── map-read-pretty.vue │ │ │ │ └── observer.vue │ │ │ ├── index.vue │ │ │ └── questions │ │ │ ├── default-slot.vue │ │ │ ├── events.vue │ │ │ ├── named-slot.vue │ │ │ └── scoped-slot.vue │ │ ├── guide │ │ │ ├── architecture.md │ │ │ ├── concept.md │ │ │ └── README.md │ │ ├── questions │ │ │ └── README.md │ │ └── README.md │ ├── package.json │ ├── README.md │ ├── rollup.config.js │ ├── scripts │ │ ├── postinstall.js │ │ ├── switch-cli.js │ │ └── utils.js │ ├── src │ │ ├── __tests__ │ │ │ ├── expression.scope.spec.ts │ │ │ ├── field.spec.ts │ │ │ ├── form.spec.ts │ │ │ ├── schema.json.spec.ts │ │ │ ├── schema.markup.spec.ts │ │ │ ├── shared.spec.ts │ │ │ └── utils.spec.ts │ │ ├── components │ │ │ ├── ArrayField.ts │ │ │ ├── ExpressionScope.ts │ │ │ ├── Field.ts │ │ │ ├── FormConsumer.ts │ │ │ ├── FormProvider.ts │ │ │ ├── index.ts │ │ │ ├── ObjectField.ts │ │ │ ├── ReactiveField.ts │ │ │ ├── RecursionField.ts │ │ │ ├── SchemaField.ts │ │ │ └── VoidField.ts │ │ ├── global.d.ts │ │ ├── hooks │ │ │ ├── index.ts │ │ │ ├── useAttach.ts │ │ │ ├── useField.ts │ │ │ ├── useFieldSchema.ts │ │ │ ├── useForm.ts │ │ │ ├── useFormEffects.ts │ │ │ ├── useInjectionCleaner.ts │ │ │ └── useParentForm.ts │ │ ├── index.ts │ │ ├── shared │ │ │ ├── connect.ts │ │ │ ├── context.ts │ │ │ ├── createForm.ts │ │ │ ├── fragment.ts │ │ │ ├── h.ts │ │ │ └── index.ts │ │ ├── types │ │ │ └── index.ts │ │ ├── utils │ │ │ ├── formatVNodeData.ts │ │ │ ├── getFieldProps.ts │ │ │ ├── getRawComponent.ts │ │ │ └── resolveSchemaProps.ts │ │ └── vue2-components.ts │ ├── tsconfig.build.json │ ├── tsconfig.json │ └── tsconfig.types.json ├── README.md ├── README.zh-cn.md ├── scripts │ ├── build-style │ │ ├── buildAllStyles.ts │ │ ├── copy.ts │ │ ├── helper.ts │ │ └── index.ts │ └── rollup.base.js ├── tsconfig.build.json ├── tsconfig.jest.json ├── tsconfig.json └── yarn.lock ``` # Files -------------------------------------------------------------------------------- /packages/json-schema/src/__tests__/schema.spec.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { Schema } from '../' 2 | import { isFn } from '@formily/shared' 3 | 4 | test('has methods', () => { 5 | const schema = new Schema({ 6 | type: 'object', 7 | properties: { 8 | aa: { 9 | type: 'string', 10 | }, 11 | }, 12 | }) 13 | expect(isFn(schema.setAdditionalItems)).toBeTruthy() 14 | expect(isFn(schema.setAdditionalProperties)).toBeTruthy() 15 | expect(isFn(schema.setItems)).toBeTruthy() 16 | expect(isFn(schema.setPatternProperties)).toBeTruthy() 17 | expect(isFn(schema.setProperties)).toBeTruthy() 18 | expect(isFn(schema.addPatternProperty)).toBeTruthy() 19 | expect(isFn(schema.addProperty)).toBeTruthy() 20 | expect(isFn(schema.fromJSON)).toBeTruthy() 21 | expect(isFn(schema.toJSON)).toBeTruthy() 22 | expect(isFn(schema.reducePatternProperties)).toBeTruthy() 23 | expect(isFn(schema.reduceProperties)).toBeTruthy() 24 | expect(isFn(schema.removeProperty)).toBeTruthy() 25 | expect(isFn(schema.removePatternProperty)).toBeTruthy() 26 | expect(isFn(schema.mapPatternProperties)).toBeTruthy() 27 | expect(isFn(schema.mapProperties)).toBeTruthy() 28 | 29 | expect(isFn(Schema.isSchemaInstance)).toBeTruthy() 30 | expect(isFn(Schema.registerCompiler)).toBeTruthy() 31 | expect(isFn(Schema.registerPatches)).toBeTruthy() 32 | expect(isFn(Schema.shallowCompile)).toBeTruthy() 33 | expect(isFn(Schema.compile)).toBeTruthy() 34 | expect(isFn(Schema.getOrderProperties)).toBeTruthy() 35 | }) 36 | 37 | test('all props', () => { 38 | const schema = new Schema({ 39 | type: 'object', 40 | title: 'title', 41 | description: 'description', 42 | patternProperties: { 43 | '^[a-zA-Z0-9]*$': { 44 | properties: { 45 | model: { type: 'string' }, 46 | made: { type: 'string' }, 47 | year: { type: 'string' }, 48 | }, 49 | }, 50 | }, 51 | additionalProperties: { 52 | type: 'string', 53 | }, 54 | properties: { 55 | string: { 56 | type: 'string', 57 | default: 'default', 58 | required: true, 59 | 'x-component': 'Input', 60 | 'x-component-props': { 61 | placeholder: 'placeholder', 62 | }, 63 | 'x-decorator': 'FormItem', 64 | 'x-decorator-props': { 65 | labelCol: 3, 66 | }, 67 | 'x-disabled': true, 68 | 'x-display': 'visible', 69 | 'x-editable': false, 70 | 'x-hidden': false, 71 | 'x-pattern': 'readPretty', 72 | 'x-read-only': true, 73 | 'x-validator': ['phone'], 74 | 'x-reactions': [ 75 | { 76 | target: 'xxx', 77 | when: '{{aa > bb}}', 78 | }, 79 | ], 80 | }, 81 | boolean: { 82 | type: 'boolean', 83 | default: false, 84 | }, 85 | number: { 86 | type: 'number', 87 | default: 100, 88 | }, 89 | date: { 90 | type: 'date', 91 | default: '2020-12-23', 92 | }, 93 | datetime: { 94 | type: 'datetime', 95 | default: '2020-12-23 23:00:00', 96 | }, 97 | array: { 98 | type: 'array', 99 | items: { 100 | type: 'string', 101 | }, 102 | additionalItems: { 103 | type: 'number', 104 | }, 105 | }, 106 | array2: { 107 | type: 'array', 108 | items: [ 109 | { 110 | type: 'string', 111 | }, 112 | { 113 | type: 'object', 114 | }, 115 | ], 116 | }, 117 | void: { 118 | type: 'void', 119 | }, 120 | }, 121 | }) 122 | expect(schema).toMatchSnapshot() 123 | }) 124 | 125 | test('all methods', () => { 126 | const schema = new Schema({ 127 | type: 'object', 128 | 'x-reactions': null, 129 | }) 130 | const schema2 = new Schema({ 131 | type: 'object', 132 | fn: () => {}, 133 | } as any) 134 | const schema3 = new Schema({ 135 | type: 'object', 136 | additionalItems: null, 137 | additionalProperties: null, 138 | properties: null, 139 | }) 140 | schema3.additionalItems = null 141 | schema3.additionalProperties = null 142 | schema3.properties = { 143 | xxx: null, 144 | } 145 | schema3.items = [null] 146 | const schema4 = new Schema({ 147 | type: 'object', 148 | additionalItems: {}, 149 | additionalProperties: {}, 150 | properties: {}, 151 | }) 152 | schema4.additionalItems = {} as any 153 | schema4.additionalProperties = {} as any 154 | schema4.properties = { 155 | xxx: {} as any, 156 | } 157 | schema4.items = [{}] as any 158 | const schema5 = new Schema({ 159 | type: 'array', 160 | }) 161 | schema5.items = null 162 | const schema6 = new Schema({ 163 | type: 'array', 164 | }) 165 | schema6.items = {} as any 166 | const schema7 = new Schema({ 167 | type: 'array', 168 | items: { 169 | type: 'string', 170 | }, 171 | }) 172 | const string = schema.addProperty('string', { 173 | type: 'string', 174 | title: 'string', 175 | description: null, 176 | 'x-reactions': [ 177 | { 178 | target: 'xxx', 179 | when: true, 180 | fulfill: { 181 | schema: {}, 182 | }, 183 | }, 184 | ], 185 | }) 186 | 187 | const array = schema.addProperty('array', { 188 | type: 'string', 189 | title: 'string', 190 | items: [{ type: 'integer' }, { type: 'integer' }], 191 | }) 192 | const pattern = schema.addPatternProperty('^[a-zA-Z0-9]*$', { 193 | properties: { 194 | model: { type: 'string', 'x-index': 2 }, 195 | made: { type: 'string', 'x-index': 1 }, 196 | year: { type: 'string', 'x-index': 0 }, 197 | }, 198 | }) 199 | schema.addPatternProperty('xxx', null) 200 | schema.setAdditionalProperties({ 201 | type: 'string', 202 | }) 203 | schema.setAdditionalProperties(null) 204 | array.setItems(null) 205 | array.setAdditionalItems({ 206 | type: 'string', 207 | }) 208 | array.setAdditionalItems(null) 209 | schema.setPatternProperties(null) 210 | schema.fromJSON(null) 211 | expect(schema2['fn']).toBeUndefined() 212 | expect(schema.properties.string).not.toBeUndefined() 213 | expect(schema.patternProperties['^[a-zA-Z0-9]*$']).not.toBeUndefined() 214 | expect(schema).toMatchSnapshot() 215 | expect(schema.toJSON()).toMatchSnapshot() 216 | expect(pattern.mapProperties((schema, key) => key)).toEqual([ 217 | 'year', 218 | 'made', 219 | 'model', 220 | ]) 221 | expect( 222 | pattern.reduceProperties((buf, schema, key) => buf.concat('_' + key), []) 223 | ).toEqual(['_year', '_made', '_model']) 224 | expect(schema.mapPatternProperties((schema, key) => key)).toEqual([ 225 | '^[a-zA-Z0-9]*$', 226 | ]) 227 | expect( 228 | schema.reducePatternProperties( 229 | (buf, schema, key) => buf.concat('_' + key), 230 | [] 231 | ) 232 | ).toEqual(['_^[a-zA-Z0-9]*$']) 233 | schema5.toJSON() 234 | schema6.toJSON() 235 | schema7.toJSON() 236 | schema.removeProperty('string') 237 | expect(schema.properties.string).toBeUndefined() 238 | schema.removePatternProperty('^[a-zA-Z0-9]*$') 239 | expect(schema.patternProperties['^[a-zA-Z0-9]*$']).toBeUndefined() 240 | expect(schema.compile()).toMatchSnapshot() 241 | expect(string.compile()).toMatchSnapshot() 242 | expect(schema3.toJSON()).toMatchSnapshot() 243 | expect(schema4.toJSON()).toMatchSnapshot() 244 | }) 245 | 246 | describe('all static methods', () => { 247 | expect(Schema.compile({ aa: '{{123}}' })).toEqual({ aa: 123 }) 248 | expect(Schema.shallowCompile('{{123}}')).toEqual(123) 249 | expect(Schema.getOrderProperties()).toEqual([]) 250 | Schema.registerPatches(null) 251 | }) 252 | 253 | test('single function x-reactions', () => { 254 | const reactions = () => console.info('x-reactions') 255 | const schema = new Schema({ 256 | type: 'string', 257 | 'x-reactions': reactions, 258 | }) 259 | expect(schema.compile()['x-reactions']).toEqual(reactions) 260 | }) 261 | 262 | test('definitions and $ref', () => { 263 | const schema = new Schema({ 264 | definitions: { 265 | address: { 266 | type: 'object', 267 | properties: { 268 | street_address: { 269 | type: 'string', 270 | }, 271 | city: { 272 | type: 'string', 273 | }, 274 | state: { 275 | type: 'string', 276 | }, 277 | }, 278 | required: ['street_address', 'city', 'state'], 279 | }, 280 | }, 281 | type: 'object', 282 | properties: { 283 | billing_address: { 284 | title: 'Billing address', 285 | $ref: '#/definitions/address', 286 | }, 287 | shipping_address: { 288 | title: 'Shipping address', 289 | $ref: '#/definitions/address', 290 | }, 291 | }, 292 | }) 293 | expect(schema.properties.billing_address.required).toEqual([ 294 | 'street_address', 295 | 'city', 296 | 'state', 297 | ]) 298 | }) 299 | ``` -------------------------------------------------------------------------------- /packages/next/src/array-collapse/index.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import React, { Fragment, useState, useEffect } from 'react' 2 | import { Badge, Card, Collapse } from '@alifd/next' 3 | import { ArrayField } from '@formily/core' 4 | import { 5 | RecursionField, 6 | useField, 7 | useFieldSchema, 8 | observer, 9 | ISchema, 10 | } from '@formily/react' 11 | import { toArr } from '@formily/shared' 12 | import cls from 'classnames' 13 | import ArrayBase, { ArrayBaseMixins, IArrayBaseProps } from '../array-base' 14 | import { usePrefixCls, Empty } from '../__builtins__' 15 | import { CollapseProps, PanelProps } from '@alifd/next/lib/collapse' 16 | 17 | export interface IArrayCollapseProps extends CollapseProps { 18 | defaultOpenPanelCount?: number 19 | } 20 | type ComposedArrayCollapse = React.FC< 21 | React.PropsWithChildren<IArrayCollapseProps & IArrayBaseProps> 22 | > & 23 | ArrayBaseMixins & { 24 | CollapsePanel?: React.FC<React.PropsWithChildren<PanelProps>> 25 | } 26 | 27 | const isAdditionComponent = (schema: ISchema) => { 28 | return schema['x-component']?.indexOf?.('Addition') > -1 29 | } 30 | 31 | const isIndexComponent = (schema: ISchema) => { 32 | return schema['x-component']?.indexOf?.('Index') > -1 33 | } 34 | 35 | const isRemoveComponent = (schema: ISchema) => { 36 | return schema['x-component']?.indexOf?.('Remove') > -1 37 | } 38 | 39 | const isMoveUpComponent = (schema: ISchema) => { 40 | return schema['x-component']?.indexOf?.('MoveUp') > -1 41 | } 42 | 43 | const isMoveDownComponent = (schema: ISchema) => { 44 | return schema['x-component']?.indexOf?.('MoveDown') > -1 45 | } 46 | 47 | const isOperationComponent = (schema: ISchema) => { 48 | return ( 49 | isAdditionComponent(schema) || 50 | isRemoveComponent(schema) || 51 | isMoveDownComponent(schema) || 52 | isMoveUpComponent(schema) 53 | ) 54 | } 55 | 56 | const range = (count: number) => Array.from({ length: count }).map((_, i) => i) 57 | 58 | const takeDefaultExpandedKeys = ( 59 | dataSourceLength: number, 60 | defaultOpenPanelCount: number 61 | ) => { 62 | if (dataSourceLength < defaultOpenPanelCount) return range(dataSourceLength) 63 | return range(defaultOpenPanelCount) 64 | } 65 | 66 | const insertExpandedKeys = (expandedKeys: number[], index: number) => { 67 | if (expandedKeys.length <= index) return expandedKeys.concat(index) 68 | return expandedKeys.reduce((buf, key) => { 69 | if (key < index) return buf.concat(key) 70 | if (key === index) return buf.concat([key, key + 1]) 71 | return buf.concat(key + 1) 72 | }, []) 73 | } 74 | 75 | export const ArrayCollapse: ComposedArrayCollapse = observer( 76 | ({ defaultOpenPanelCount = 5, ...props }) => { 77 | const field = useField<ArrayField>() 78 | const dataSource = Array.isArray(field.value) ? field.value : [] 79 | 80 | const [expandKeys, setExpandKeys] = useState<number[]>( 81 | takeDefaultExpandedKeys(dataSource.length, defaultOpenPanelCount) 82 | ) 83 | const schema = useFieldSchema() 84 | const prefixCls = usePrefixCls('formily-array-collapse', props) 85 | useEffect(() => { 86 | if (!field.modified && dataSource.length) { 87 | setExpandKeys( 88 | takeDefaultExpandedKeys(dataSource.length, defaultOpenPanelCount) 89 | ) 90 | } 91 | }, [dataSource.length, field]) 92 | if (!schema) throw new Error('can not found schema object') 93 | const { onAdd, onCopy, onRemove, onMoveDown, onMoveUp } = props 94 | 95 | const renderAddition = () => { 96 | return schema.reduceProperties((addition, schema, key) => { 97 | if (isAdditionComponent(schema)) { 98 | return <RecursionField schema={schema} name={key} /> 99 | } 100 | return addition 101 | }, null) 102 | } 103 | const renderEmpty = () => { 104 | if (dataSource.length) return 105 | return ( 106 | <Card className={cls(`${prefixCls}-item`, props.className)}> 107 | <Empty /> 108 | </Card> 109 | ) 110 | } 111 | 112 | const renderItems = () => { 113 | return ( 114 | <Collapse 115 | {...props} 116 | onChange={() => {}} 117 | expandedKeys={expandKeys.map(String)} 118 | onExpand={(keys: string[]) => setExpandKeys(toArr(keys).map(Number))} 119 | className={cls(`${prefixCls}-item`, props.className)} 120 | > 121 | {dataSource.map((item, index) => { 122 | const items = Array.isArray(schema.items) 123 | ? schema.items[index] || schema.items[0] 124 | : schema.items 125 | const panelProps = field 126 | .query(`${field.address}.${index}`) 127 | .get('componentProps') 128 | const props: PanelProps = items['x-component-props'] 129 | const title = () => { 130 | const title = `${ 131 | panelProps?.title || props?.title || field.title 132 | }` 133 | const path = field.address.concat(index) 134 | const errors = field.form.queryFeedbacks({ 135 | type: 'error', 136 | address: `${path}.**`, 137 | }) 138 | return ( 139 | <ArrayBase.Item index={index} record={item}> 140 | <div 141 | className={cls(`${prefixCls}-item-title`, props.className)} 142 | > 143 | <div> 144 | <RecursionField 145 | schema={items} 146 | name={index} 147 | filterProperties={(schema) => { 148 | if (!isIndexComponent(schema)) return false 149 | return true 150 | }} 151 | onlyRenderProperties 152 | /> 153 | {errors.length ? ( 154 | <Badge className="errors-badge" count={errors.length}> 155 | {title} 156 | </Badge> 157 | ) : ( 158 | title 159 | )} 160 | </div> 161 | <div> 162 | <RecursionField 163 | schema={items} 164 | name={index} 165 | filterProperties={(schema) => { 166 | if (!isOperationComponent(schema)) return false 167 | return true 168 | }} 169 | onlyRenderProperties 170 | /> 171 | </div> 172 | </div> 173 | </ArrayBase.Item> 174 | ) 175 | } 176 | 177 | const content = ( 178 | <RecursionField 179 | schema={items} 180 | name={index} 181 | filterProperties={(schema) => { 182 | if (isIndexComponent(schema)) return false 183 | if (isOperationComponent(schema)) return false 184 | return true 185 | }} 186 | /> 187 | ) 188 | return ( 189 | <Collapse.Panel 190 | {...props} 191 | {...panelProps} 192 | onChange={() => {}} 193 | key={index} 194 | title={title()} 195 | > 196 | <ArrayBase.Item 197 | index={index} 198 | key={index} 199 | record={() => field.value?.[index]} 200 | > 201 | {content} 202 | </ArrayBase.Item> 203 | </Collapse.Panel> 204 | ) 205 | })} 206 | </Collapse> 207 | ) 208 | } 209 | return ( 210 | <ArrayBase 211 | onAdd={(index) => { 212 | onAdd?.(index) 213 | setExpandKeys(insertExpandedKeys(expandKeys, index)) 214 | }} 215 | onCopy={onCopy} 216 | onRemove={onRemove} 217 | onMoveUp={onMoveUp} 218 | onMoveDown={onMoveDown} 219 | > 220 | {renderEmpty()} 221 | {renderItems()} 222 | {renderAddition()} 223 | </ArrayBase> 224 | ) 225 | } 226 | ) 227 | 228 | const CollapsePanel: React.FC<React.PropsWithChildren<PanelProps>> = ({ 229 | children, 230 | }) => { 231 | return <Fragment>{children}</Fragment> 232 | } 233 | 234 | CollapsePanel.displayName = 'CollapsePanel' 235 | 236 | ArrayCollapse.displayName = 'ArrayCollapse' 237 | ArrayCollapse.CollapsePanel = CollapsePanel 238 | 239 | ArrayBase.mixin(ArrayCollapse) 240 | 241 | export default ArrayCollapse 242 | ``` -------------------------------------------------------------------------------- /packages/antd/docs/components/Upload.md: -------------------------------------------------------------------------------- ```markdown 1 | # Upload 2 | 3 | > Upload components 4 | > 5 | > Note: Using the upload component, it is recommended that users perform secondary packaging. Users do not need to care about the data communication between the upload component and Formily, only the style and basic upload configuration are required. 6 | 7 | ## Markup Schema example 8 | 9 | ```tsx 10 | import React from 'react' 11 | import { 12 | Upload, 13 | FormItem, 14 | FormLayout, 15 | FormButtonGroup, 16 | Submit, 17 | } from '@formily/antd' 18 | import { createForm } from '@formily/core' 19 | import { FormProvider, createSchemaField } from '@formily/react' 20 | import { Button } from 'antd' 21 | import { UploadOutlined, InboxOutlined } from '@ant-design/icons' 22 | 23 | const NormalUpload = (props) => { 24 | return ( 25 | <Upload 26 | {...props} 27 | action="https://www.mocky.io/v2/5cc8019d300000980a055e76" 28 | headers={{ 29 | authorization: 'authorization-text', 30 | }} 31 | > 32 | <Button icon={<UploadOutlined />}>Upload files</Button> 33 | </Upload> 34 | ) 35 | } 36 | 37 | const CardUpload = (props) => { 38 | return ( 39 | <Upload 40 | {...props} 41 | action="https://www.mocky.io/v2/5cc8019d300000980a055e76" 42 | listType="picture-card" 43 | headers={{ 44 | authorization: 'authorization-text', 45 | }} 46 | > 47 | <UploadOutlined style={{ fontSize: 20 }} /> 48 | </Upload> 49 | ) 50 | } 51 | 52 | const DraggerUpload = (props) => { 53 | return ( 54 | <Upload.Dragger 55 | {...props} 56 | action="https://www.mocky.io/v2/5cc8019d300000980a055e76" 57 | > 58 | <p className="ant-upload-drag-icon"> 59 | <InboxOutlined /> 60 | </p> 61 | <p className="ant-upload-text"> 62 | Click or drag file to this area to upload 63 | </p> 64 | <p className="ant-upload-hint"> 65 | Support for a single or bulk upload. Strictly prohibit from uploading 66 | company data or other band files 67 | </p> 68 | </Upload.Dragger> 69 | ) 70 | } 71 | 72 | const SchemaField = createSchemaField({ 73 | components: { 74 | NormalUpload, 75 | CardUpload, 76 | DraggerUpload, 77 | FormItem, 78 | }, 79 | }) 80 | 81 | const form = createForm() 82 | 83 | export default () => ( 84 | <FormProvider form={form}> 85 | <FormLayout labelCol={6} wrapperCol={10}> 86 | <SchemaField> 87 | <SchemaField.Array 88 | name="upload" 89 | title="Upload" 90 | x-decorator="FormItem" 91 | x-component="NormalUpload" 92 | required 93 | /> 94 | <SchemaField.Array 95 | name="upload2" 96 | title="Card upload" 97 | x-decorator="FormItem" 98 | x-component="CardUpload" 99 | required 100 | /> 101 | <SchemaField.Array 102 | name="upload3" 103 | title="Drag and drop upload" 104 | x-decorator="FormItem" 105 | x-component="DraggerUpload" 106 | required 107 | /> 108 | </SchemaField> 109 | <FormButtonGroup.FormItem> 110 | <Submit onSubmit={console.log}>Submit</Submit> 111 | </FormButtonGroup.FormItem> 112 | </FormLayout> 113 | </FormProvider> 114 | ) 115 | ``` 116 | 117 | ## JSON Schema case 118 | 119 | ```tsx 120 | import React from 'react' 121 | import { 122 | Upload, 123 | FormItem, 124 | FormLayout, 125 | FormButtonGroup, 126 | Submit, 127 | } from '@formily/antd' 128 | import { createForm } from '@formily/core' 129 | import { FormProvider, createSchemaField } from '@formily/react' 130 | import { Button } from 'antd' 131 | import { UploadOutlined, InboxOutlined } from '@ant-design/icons' 132 | 133 | const NormalUpload = (props) => { 134 | return ( 135 | <Upload 136 | {...props} 137 | action="https://www.mocky.io/v2/5cc8019d300000980a055e76" 138 | headers={{ 139 | authorization: 'authorization-text', 140 | }} 141 | > 142 | <Button icon={<UploadOutlined />}>Upload files</Button> 143 | </Upload> 144 | ) 145 | } 146 | 147 | const CardUpload = (props) => { 148 | return ( 149 | <Upload 150 | {...props} 151 | action="https://www.mocky.io/v2/5cc8019d300000980a055e76" 152 | listType="picture-card" 153 | headers={{ 154 | authorization: 'authorization-text', 155 | }} 156 | > 157 | <UploadOutlined style={{ fontSize: 20 }} /> 158 | </Upload> 159 | ) 160 | } 161 | 162 | const DraggerUpload = (props) => { 163 | return ( 164 | <Upload.Dragger 165 | {...props} 166 | action="https://www.mocky.io/v2/5cc8019d300000980a055e76" 167 | > 168 | <p className="ant-upload-drag-icon"> 169 | <InboxOutlined /> 170 | </p> 171 | <p className="ant-upload-text"> 172 | Click or drag file to this area to upload 173 | </p> 174 | <p className="ant-upload-hint"> 175 | Support for a single or bulk upload. Strictly prohibit from uploading 176 | company data or other band files 177 | </p> 178 | </Upload.Dragger> 179 | ) 180 | } 181 | 182 | const SchemaField = createSchemaField({ 183 | components: { 184 | NormalUpload, 185 | CardUpload, 186 | DraggerUpload, 187 | FormItem, 188 | }, 189 | }) 190 | 191 | const form = createForm() 192 | 193 | const schema = { 194 | type: 'object', 195 | properties: { 196 | upload: { 197 | type: 'array', 198 | title: 'Upload', 199 | required: true, 200 | 'x-decorator': 'FormItem', 201 | 'x-component': 'NormalUpload', 202 | }, 203 | upload2: { 204 | type: 'array', 205 | title: 'Card upload', 206 | required: true, 207 | 'x-decorator': 'FormItem', 208 | 'x-component': 'CardUpload', 209 | }, 210 | upload3: { 211 | type: 'array', 212 | title: 'Drag and drop upload', 213 | required: true, 214 | 'x-decorator': 'FormItem', 215 | 'x-component': 'DraggerUpload', 216 | }, 217 | }, 218 | } 219 | 220 | export default () => ( 221 | <FormProvider form={form}> 222 | <FormLayout labelCol={6} wrapperCol={10}> 223 | <SchemaField schema={schema} /> 224 | <FormButtonGroup.FormItem> 225 | <Submit onSubmit={console.log}>Submit</Submit> 226 | </FormButtonGroup.FormItem> 227 | </FormLayout> 228 | </FormProvider> 229 | ) 230 | ``` 231 | 232 | ## Pure JSX case 233 | 234 | ```tsx 235 | import React from 'react' 236 | import { 237 | Upload, 238 | FormItem, 239 | FormLayout, 240 | FormButtonGroup, 241 | Submit, 242 | } from '@formily/antd' 243 | import { createForm } from '@formily/core' 244 | import { FormProvider, Field } from '@formily/react' 245 | import { Button } from 'antd' 246 | import { UploadOutlined, InboxOutlined } from '@ant-design/icons' 247 | 248 | const NormalUpload = (props) => { 249 | return ( 250 | <Upload 251 | {...props} 252 | action="https://www.mocky.io/v2/5cc8019d300000980a055e76" 253 | headers={{ 254 | authorization: 'authorization-text', 255 | }} 256 | > 257 | <Button icon={<UploadOutlined />}>Upload files</Button> 258 | </Upload> 259 | ) 260 | } 261 | 262 | const CardUpload = (props) => { 263 | return ( 264 | <Upload 265 | {...props} 266 | action="https://www.mocky.io/v2/5cc8019d300000980a055e76" 267 | listType="picture-card" 268 | headers={{ 269 | authorization: 'authorization-text', 270 | }} 271 | > 272 | <UploadOutlined style={{ fontSize: 20 }} /> 273 | </Upload> 274 | ) 275 | } 276 | 277 | const DraggerUpload = (props) => { 278 | return ( 279 | <Upload.Dragger 280 | {...props} 281 | action="https://www.mocky.io/v2/5cc8019d300000980a055e76" 282 | > 283 | <p className="ant-upload-drag-icon"> 284 | <InboxOutlined /> 285 | </p> 286 | <p className="ant-upload-text"> 287 | Click or drag file to this area to upload 288 | </p> 289 | <p className="ant-upload-hint"> 290 | Support for a single or bulk upload. Strictly prohibit from uploading 291 | company data or other band files 292 | </p> 293 | </Upload.Dragger> 294 | ) 295 | } 296 | 297 | const form = createForm() 298 | 299 | export default () => ( 300 | <FormProvider form={form}> 301 | <FormLayout labelCol={6} wrapperCol={10}> 302 | <Field 303 | name="upload" 304 | title="Upload" 305 | required 306 | decorator={[FormItem]} 307 | component={[NormalUpload]} 308 | /> 309 | <Field 310 | name="upload2" 311 | title="Card upload" 312 | required 313 | decorator={[FormItem]} 314 | component={[CardUpload]} 315 | /> 316 | <Field 317 | name="upload3" 318 | title="Drag and drop upload" 319 | required 320 | decorator={[FormItem]} 321 | component={[DraggerUpload]} 322 | /> 323 | <FormButtonGroup.FormItem> 324 | <Submit onSubmit={console.log}>Submit</Submit> 325 | </FormButtonGroup.FormItem> 326 | </FormLayout> 327 | </FormProvider> 328 | ) 329 | ``` 330 | 331 | ## API 332 | 333 | Reference https://ant.design/components/upload-cn/ 334 | ``` -------------------------------------------------------------------------------- /packages/element/src/preview-text/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { Field } from '@formily/core' 2 | import { observer } from '@formily/reactive-vue' 3 | import { isArr, isValid } from '@formily/shared' 4 | import { h, useField } from '@formily/vue' 5 | import { Tag } from 'element-ui' 6 | import { formatDate } from 'element-ui/src/utils/date-util' 7 | import { computed, defineComponent, Ref, toRef } from 'vue-demi' 8 | import type { CascaderProps } from '../cascader' 9 | import type { DatePickerProps } from '../date-picker' 10 | import { InputProps } from '../input' 11 | import type { SelectProps } from '../select' 12 | import { Space } from '../space' 13 | import type { TimePickerProps } from '../time-picker' 14 | import { stylePrefix } from '../__builtins__/configs' 15 | import { 16 | composeExport, 17 | createContext, 18 | resolveComponent, 19 | useContext, 20 | } from '../__builtins__/shared' 21 | 22 | const prefixCls = `${stylePrefix}-preview-text` 23 | const PlaceholderContext = createContext('N/A') 24 | 25 | export const usePlaceholder = (value?: Ref<any>) => { 26 | const placeholderCtx = useContext(PlaceholderContext) 27 | const placeholder = computed(() => { 28 | return isValid(value?.value) && value.value !== '' 29 | ? value.value 30 | : resolveComponent(placeholderCtx.value) || 'N/A' 31 | }) 32 | return placeholder 33 | } 34 | 35 | const Input = defineComponent<InputProps>({ 36 | name: 'FPreviewTextInput', 37 | props: ['value'], 38 | setup(props, { attrs, slots }) { 39 | const value = toRef(props, 'value') 40 | const placeholder = usePlaceholder(value) 41 | return () => { 42 | return h( 43 | Space, 44 | { 45 | class: [prefixCls], 46 | style: attrs.style, 47 | }, 48 | { 49 | default: () => [ 50 | slots?.prepend?.(), 51 | slots?.prefix?.(), 52 | placeholder.value, 53 | slots?.suffix?.(), 54 | slots?.append?.(), 55 | ], 56 | } 57 | ) 58 | } 59 | }, 60 | }) 61 | 62 | const Select = observer( 63 | defineComponent<SelectProps>({ 64 | name: 'FPreviewTextSelect', 65 | props: [], 66 | setup(_props, { attrs }) { 67 | const fieldRef = useField<Field>() 68 | const field = fieldRef.value 69 | const props = attrs as unknown as SelectProps 70 | const dataSource: any[] = field?.dataSource?.length 71 | ? field.dataSource 72 | : props?.options?.length 73 | ? props.options 74 | : [] 75 | const placeholder = usePlaceholder() 76 | const getSelected = () => { 77 | const value = props.value 78 | if (props.multiple) { 79 | return isArr(value) 80 | ? value.map((val) => ({ label: val, value: val })) 81 | : [] 82 | } else { 83 | return isValid(value) ? [{ label: value, value }] : [] 84 | } 85 | } 86 | 87 | const getLabels = () => { 88 | const selected = getSelected() 89 | if (!selected.length) { 90 | return h( 91 | Tag, 92 | {}, 93 | { 94 | default: () => placeholder.value, 95 | } 96 | ) 97 | } 98 | return selected.map(({ value, label }, key) => { 99 | const text = 100 | dataSource?.find((item) => item.value == value)?.label || label 101 | return h( 102 | Tag, 103 | { 104 | key, 105 | props: { 106 | type: 'info', 107 | effect: 'light', 108 | }, 109 | }, 110 | { 111 | default: () => text || placeholder.value, 112 | } 113 | ) 114 | }) 115 | } 116 | 117 | return () => { 118 | return h( 119 | Space, 120 | { 121 | class: [prefixCls], 122 | style: attrs.style, 123 | }, 124 | { 125 | default: () => getLabels(), 126 | } 127 | ) 128 | } 129 | }, 130 | }) 131 | ) 132 | 133 | const Cascader = observer( 134 | defineComponent<CascaderProps>({ 135 | name: 'FPreviewTextCascader', 136 | props: [], 137 | setup(_props, { attrs }) { 138 | const fieldRef = useField<Field>() 139 | const field = fieldRef.value 140 | const props = attrs as unknown as CascaderProps 141 | const dataSource: any[] = field?.dataSource?.length 142 | ? field.dataSource 143 | : props?.options?.length 144 | ? props.options 145 | : [] 146 | const placeholder = usePlaceholder() 147 | const valueKey = props.props?.value || 'value' 148 | const labelKey = props.props?.label || 'label' 149 | const getSelected = () => { 150 | return isArr(props.value) ? props.value : [] 151 | } 152 | 153 | const findLabel = (value: any, dataSource: any[]) => { 154 | for (let i = 0; i < dataSource?.length; i++) { 155 | const item = dataSource[i] 156 | if (item?.[valueKey] === value) { 157 | return item?.[labelKey] 158 | } else { 159 | const childLabel = findLabel(value, item?.children) 160 | if (childLabel) return childLabel 161 | } 162 | } 163 | } 164 | 165 | const getLabels = () => { 166 | const selected = getSelected() 167 | if (!selected?.length) { 168 | return h( 169 | Tag, 170 | {}, 171 | { 172 | default: () => placeholder.value, 173 | } 174 | ) 175 | } 176 | return selected.map((value, key) => { 177 | const text = findLabel(value, dataSource) 178 | return h( 179 | Tag, 180 | { 181 | key, 182 | props: { 183 | type: 'info', 184 | effect: 'light', 185 | }, 186 | }, 187 | { 188 | default: () => text || placeholder.value, 189 | } 190 | ) 191 | }) 192 | } 193 | 194 | return () => { 195 | return h( 196 | Space, 197 | { 198 | class: [prefixCls], 199 | style: attrs.style, 200 | }, 201 | { 202 | default: () => getLabels(), 203 | } 204 | ) 205 | } 206 | }, 207 | }) 208 | ) 209 | 210 | const DatePicker = defineComponent<DatePickerProps>({ 211 | name: 'FPreviewTextDatePicker', 212 | props: [], 213 | setup(_props, { attrs }) { 214 | const props = attrs as unknown as DatePickerProps 215 | const placeholder = usePlaceholder() 216 | const getLabels = () => { 217 | if (isArr(props.value)) { 218 | const labels = (props.value as any[]).map( 219 | (value: String | Date) => 220 | formatDate(value, props.format) || placeholder.value 221 | ) 222 | 223 | return labels.join('~') 224 | } else { 225 | return formatDate(props.value, props.format) || placeholder.value 226 | } 227 | } 228 | 229 | return () => { 230 | return h( 231 | 'div', 232 | { 233 | class: [prefixCls], 234 | style: attrs.style, 235 | }, 236 | { 237 | default: () => getLabels(), 238 | } 239 | ) 240 | } 241 | }, 242 | }) 243 | 244 | const TimePicker = defineComponent<TimePickerProps>({ 245 | name: 'FPreviewTextTimePicker', 246 | props: [], 247 | setup(_props, { attrs }) { 248 | const props = attrs as unknown as TimePickerProps 249 | const format = props.pickerOptions?.format || 'HH:mm:ss' 250 | const placeholder = usePlaceholder() 251 | const getLabels = () => { 252 | if (isArr(props.value)) { 253 | const labels = props.value.map( 254 | (value) => formatDate(value, format) || placeholder.value 255 | ) 256 | 257 | return labels.join('~') 258 | } else { 259 | return formatDate(props.value, format) || placeholder.value 260 | } 261 | } 262 | 263 | return () => { 264 | return h( 265 | 'div', 266 | { 267 | class: [prefixCls], 268 | style: attrs.style, 269 | }, 270 | { 271 | default: () => getLabels(), 272 | } 273 | ) 274 | } 275 | }, 276 | }) 277 | 278 | const Text = defineComponent<any>({ 279 | name: 'FPreviewText', 280 | setup(_props, { attrs }) { 281 | const placeholder = usePlaceholder() 282 | 283 | return () => { 284 | return h( 285 | 'div', 286 | { 287 | class: [prefixCls], 288 | style: attrs.style, 289 | }, 290 | { 291 | default: () => placeholder.value, 292 | } 293 | ) 294 | } 295 | }, 296 | }) 297 | 298 | export const PreviewText = composeExport(Text, { 299 | Input, 300 | Select, 301 | Cascader, 302 | DatePicker, 303 | TimePicker, 304 | Placeholder: PlaceholderContext.Provider, 305 | usePlaceholder, 306 | }) 307 | 308 | export default PreviewText 309 | ``` -------------------------------------------------------------------------------- /packages/next/src/__builtins__/icons.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import React from 'react' 2 | import cls from 'classnames' 3 | import { usePrefixCls } from './hooks/usePrefixCls' 4 | export type IconProps = React.HTMLAttributes<SVGSVGElement> & { 5 | ref?: React.ForwardedRef<SVGSVGElement> 6 | } 7 | 8 | export type IconType = React.ForwardRefExoticComponent<IconProps> 9 | 10 | export const Icon: IconType = React.forwardRef((props, ref) => { 11 | const prefix = usePrefixCls('formily-icon') 12 | return ( 13 | <svg 14 | {...props} 15 | ref={ref} 16 | className={cls(prefix, props.className)} 17 | style={{ 18 | ...props.style, 19 | cursor: props.onClick ? 'pointer' : '', 20 | display: 'inline-block', 21 | verticalAlign: 'middle', 22 | }} 23 | viewBox="64 64 896 896" 24 | fill="currentColor" 25 | width="1em" 26 | height="1em" 27 | focusable="false" 28 | aria-hidden="true" 29 | > 30 | {props.children} 31 | </svg> 32 | ) 33 | }) 34 | 35 | export const MenuOutlinedIcon: IconType = React.forwardRef((props, ref) => ( 36 | <Icon {...props} ref={ref}> 37 | <path d="M904 160H120c-4.4 0-8 3.6-8 8v64c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-64c0-4.4-3.6-8-8-8zm0 624H120c-4.4 0-8 3.6-8 8v64c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-64c0-4.4-3.6-8-8-8zm0-312H120c-4.4 0-8 3.6-8 8v64c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-64c0-4.4-3.6-8-8-8z"></path> 38 | </Icon> 39 | )) 40 | 41 | export const PlusOutlinedIcon: IconType = React.forwardRef((props, ref) => ( 42 | <Icon {...props} ref={ref}> 43 | <path d="M482 152h60q8 0 8 8v704q0 8-8 8h-60q-8 0-8-8V160q0-8 8-8z"></path> 44 | <path d="M176 474h672q8 0 8 8v60q0 8-8 8H176q-8 0-8-8v-60q0-8 8-8z"></path> 45 | </Icon> 46 | )) 47 | 48 | export const UpOutlinedIcon: IconType = React.forwardRef((props, ref) => ( 49 | <Icon {...props} ref={ref}> 50 | <path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path> 51 | </Icon> 52 | )) 53 | 54 | export const DownOutlinedIcon: IconType = React.forwardRef((props, ref) => ( 55 | <Icon {...props} ref={ref}> 56 | <path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path> 57 | </Icon> 58 | )) 59 | 60 | export const DeleteOutlinedIcon: IconType = React.forwardRef((props, ref) => ( 61 | <Icon {...props} ref={ref}> 62 | <path d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"></path> 63 | </Icon> 64 | )) 65 | 66 | export const CopyOutlinedIcon: IconType = React.forwardRef((props, ref) => ( 67 | <Icon {...props} ref={ref}> 68 | <path d="M832 64H296c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h496v688c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8V96c0-17.7-14.3-32-32-32zM704 192H192c-17.7 0-32 14.3-32 32v530.7c0 8.5 3.4 16.6 9.4 22.6l173.3 173.3c2.2 2.2 4.7 4 7.4 5.5v1.9h4.2c3.5 1.3 7.2 2 11 2H704c17.7 0 32-14.3 32-32V224c0-17.7-14.3-32-32-32zM350 856.2L263.9 770H350v86.2zM664 888H414V746c0-22.1-17.9-40-40-40H232V264h432v624z"></path> 69 | </Icon> 70 | )) 71 | 72 | export const QuestionCircleOutlinedIcon: IconType = React.forwardRef( 73 | (props, ref) => ( 74 | <Icon {...props} ref={ref}> 75 | <path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"></path> 76 | <path d="M623.6 316.7C593.6 290.4 554 276 512 276s-81.6 14.5-111.6 40.7C369.2 344 352 380.7 352 420v7.6c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8V420c0-44.1 43.1-80 96-80s96 35.9 96 80c0 31.1-22 59.6-56.1 72.7-21.2 8.1-39.2 22.3-52.1 40.9-13.1 19-19.9 41.8-19.9 64.9V620c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8v-22.7a48.3 48.3 0 0130.9-44.8c59-22.7 97.1-74.7 97.1-132.5.1-39.3-17.1-76-48.3-103.3zM472 732a40 40 0 1080 0 40 40 0 10-80 0z"></path> 77 | </Icon> 78 | ) 79 | ) 80 | export const CloseCircleOutlinedIcon: IconType = React.forwardRef( 81 | (props, ref) => ( 82 | <Icon {...props} ref={ref}> 83 | <path d="M685.4 354.8c0-4.4-3.6-8-8-8l-66 .3L512 465.6l-99.3-118.4-66.1-.3c-4.4 0-8 3.5-8 8 0 1.9.7 3.7 1.9 5.2l130.1 155L340.5 670a8.32 8.32 0 00-1.9 5.2c0 4.4 3.6 8 8 8l66.1-.3L512 564.4l99.3 118.4 66 .3c4.4 0 8-3.5 8-8 0-1.9-.7-3.7-1.9-5.2L553.5 515l130.1-155c1.2-1.4 1.8-3.3 1.8-5.2z"></path> 84 | <path d="M512 65C264.6 65 64 265.6 64 513s200.6 448 448 448 448-200.6 448-448S759.4 65 512 65zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"></path> 85 | </Icon> 86 | ) 87 | ) 88 | export const CheckCircleOutlinedIcon: IconType = React.forwardRef( 89 | (props, ref) => ( 90 | <Icon {...props} ref={ref}> 91 | <path d="M699 353h-46.9c-10.2 0-19.9 4.9-25.9 13.3L469 584.3l-71.2-98.8c-6-8.3-15.6-13.3-25.9-13.3H325c-6.5 0-10.3 7.4-6.5 12.7l124.6 172.8a31.8 31.8 0 0051.7 0l210.6-292c3.9-5.3.1-12.7-6.4-12.7z"></path> 92 | <path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"></path> 93 | </Icon> 94 | ) 95 | ) 96 | export const ExclamationCircleOutlinedIcon: IconType = React.forwardRef( 97 | (props, ref) => ( 98 | <Icon {...props} ref={ref}> 99 | <path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"></path> 100 | <path d="M464 688a48 48 0 1096 0 48 48 0 10-96 0zm24-112h48c4.4 0 8-3.6 8-8V296c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v272c0 4.4 3.6 8 8 8z"></path> 101 | </Icon> 102 | ) 103 | ) 104 | 105 | export const EditOutlinedIcon: IconType = React.forwardRef((props, ref) => ( 106 | <Icon {...props} ref={ref}> 107 | <path d="M257.7 752c2 0 4-.2 6-.5L431.9 722c2-.4 3.9-1.3 5.3-2.8l423.9-423.9a9.96 9.96 0 000-14.1L694.9 114.9c-1.9-1.9-4.4-2.9-7.1-2.9s-5.2 1-7.1 2.9L256.8 538.8c-1.5 1.5-2.4 3.3-2.8 5.3l-29.5 168.2a33.5 33.5 0 009.4 29.8c6.6 6.4 14.9 9.9 23.8 9.9zm67.4-174.4L687.8 215l73.3 73.3-362.7 362.6-88.9 15.7 15.6-89zM880 836H144c-17.7 0-32 14.3-32 32v36c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-36c0-17.7-14.3-32-32-32z"></path>{' '} 108 | </Icon> 109 | )) 110 | export const CloseOutlinedIcon: IconType = React.forwardRef((props, ref) => ( 111 | <Icon {...props} ref={ref}> 112 | <path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path> 113 | </Icon> 114 | )) 115 | export const MessageOutlinedIcon: IconType = React.forwardRef((props, ref) => ( 116 | <Icon {...props} ref={ref}> 117 | <path d="M464 512a48 48 0 1096 0 48 48 0 10-96 0zm200 0a48 48 0 1096 0 48 48 0 10-96 0zm-400 0a48 48 0 1096 0 48 48 0 10-96 0zm661.2-173.6c-22.6-53.7-55-101.9-96.3-143.3a444.35 444.35 0 00-143.3-96.3C630.6 75.7 572.2 64 512 64h-2c-60.6.3-119.3 12.3-174.5 35.9a445.35 445.35 0 00-142 96.5c-40.9 41.3-73 89.3-95.2 142.8-23 55.4-34.6 114.3-34.3 174.9A449.4 449.4 0 00112 714v152a46 46 0 0046 46h152.1A449.4 449.4 0 00510 960h2.1c59.9 0 118-11.6 172.7-34.3a444.48 444.48 0 00142.8-95.2c41.3-40.9 73.8-88.7 96.5-142 23.6-55.2 35.6-113.9 35.9-174.5.3-60.9-11.5-120-34.8-175.6zm-151.1 438C704 845.8 611 884 512 884h-1.7c-60.3-.3-120.2-15.3-173.1-43.5l-8.4-4.5H188V695.2l-4.5-8.4C155.3 633.9 140.3 574 140 513.7c-.4-99.7 37.7-193.3 107.6-263.8 69.8-70.5 163.1-109.5 262.8-109.9h1.7c50 0 98.5 9.7 144.2 28.9 44.6 18.7 84.6 45.6 119 80 34.3 34.3 61.3 74.4 80 119 19.4 46.2 29.1 95.2 28.9 145.8-.6 99.6-39.7 192.9-110.1 262.7z"></path> 118 | </Icon> 119 | )) 120 | ``` -------------------------------------------------------------------------------- /packages/antd/docs/components/FormLayout.zh-CN.md: -------------------------------------------------------------------------------- ```markdown 1 | # FormLayout 2 | 3 | > 区块级布局批量控制组件,借助该组件,我们可以轻松的控制被 FormLayout 圈住的所有 FormItem 组件的布局模式 4 | 5 | ## Markup Schema 案例 6 | 7 | ```tsx 8 | import React from 'react' 9 | import { Input, Select, FormItem, FormLayout } from '@formily/antd' 10 | import { createForm } from '@formily/core' 11 | import { FormProvider, createSchemaField } from '@formily/react' 12 | 13 | const SchemaField = createSchemaField({ 14 | components: { 15 | Input, 16 | Select, 17 | FormItem, 18 | FormLayout, 19 | }, 20 | }) 21 | 22 | const form = createForm() 23 | 24 | export default () => ( 25 | <FormProvider form={form}> 26 | <SchemaField> 27 | <SchemaField.Void 28 | x-component="FormLayout" 29 | x-component-props={{ 30 | labelCol: 6, 31 | wrapperCol: 10, 32 | }} 33 | > 34 | <SchemaField.String 35 | name="input" 36 | title="输入框" 37 | x-decorator="FormItem" 38 | x-decorator-props={{ 39 | tooltip: <div>123</div>, 40 | }} 41 | x-component="Input" 42 | required 43 | /> 44 | <SchemaField.String 45 | name="select" 46 | title="选择框" 47 | x-decorator="FormItem" 48 | x-component="Select" 49 | required 50 | /> 51 | </SchemaField.Void> 52 | </SchemaField> 53 | </FormProvider> 54 | ) 55 | ``` 56 | 57 | ## JSON Schema 案例 58 | 59 | ```tsx 60 | import React from 'react' 61 | import { Input, Select, FormItem, FormLayout } from '@formily/antd' 62 | import { createForm } from '@formily/core' 63 | import { FormProvider, createSchemaField } from '@formily/react' 64 | 65 | const SchemaField = createSchemaField({ 66 | components: { 67 | Input, 68 | Select, 69 | FormItem, 70 | FormLayout, 71 | }, 72 | }) 73 | 74 | const schema = { 75 | type: 'object', 76 | properties: { 77 | layout: { 78 | type: 'void', 79 | 'x-component': 'FormLayout', 80 | 'x-component-props': { 81 | labelCol: 6, 82 | wrapperCol: 10, 83 | layout: 'vertical', 84 | }, 85 | properties: { 86 | input: { 87 | type: 'string', 88 | title: '输入框', 89 | required: true, 90 | 'x-decorator': 'FormItem', 91 | 'x-decorator-props': { 92 | tooltip: <div>123</div>, 93 | }, 94 | 'x-component': 'Input', 95 | }, 96 | select: { 97 | type: 'string', 98 | title: '选择框', 99 | required: true, 100 | 'x-decorator': 'FormItem', 101 | 'x-component': 'Select', 102 | }, 103 | }, 104 | }, 105 | }, 106 | } 107 | 108 | const form = createForm() 109 | 110 | export default () => ( 111 | <FormProvider form={form}> 112 | <SchemaField schema={schema} /> 113 | </FormProvider> 114 | ) 115 | ``` 116 | 117 | ## 纯 JSX 案例 118 | 119 | ```tsx 120 | import React from 'react' 121 | import { 122 | Input, 123 | Select, 124 | FormItem, 125 | FormButtonGroup, 126 | Submit, 127 | FormLayout, 128 | } from '@formily/antd' 129 | import { createForm } from '@formily/core' 130 | import { FormProvider, Field } from '@formily/react' 131 | 132 | const form = createForm() 133 | 134 | export default () => ( 135 | <FormProvider form={form}> 136 | <FormLayout 137 | breakpoints={[680]} 138 | layout={['vertical', 'horizontal']} 139 | labelAlign={['left', 'right']} 140 | labelCol={[24, 6]} 141 | wrapperCol={[24, 10]} 142 | > 143 | <Field 144 | name="input" 145 | required 146 | title="输入框" 147 | decorator={[FormItem]} 148 | component={[Input]} 149 | /> 150 | <Field 151 | name="select" 152 | required 153 | title="选择框" 154 | decorator={[FormItem]} 155 | component={[Select]} 156 | /> 157 | <FormButtonGroup.FormItem> 158 | <Submit onSubmit={console.log}>提交</Submit> 159 | </FormButtonGroup.FormItem> 160 | </FormLayout> 161 | </FormProvider> 162 | ) 163 | ``` 164 | 165 | ## API 166 | 167 | | 属性名 | 类型 | 描述 | 默认值 | 168 | | -------------- | -------------------------------------------------------------------------------------- | ---------------------------------------- | ---------- | 169 | | style | CSSProperties | 样式 | - | 170 | | className | string | 类名 | - | 171 | | colon | boolean | 是否有冒号 | true | 172 | | requiredMark | boolean \| `"optional"` | 必选样式,可以切换为必选或者可选展示样式 | true | 173 | | labelAlign | `'right' \| 'left' \| ('right' \| 'left')[]` | 标签内容对齐 | - | 174 | | wrapperAlign | `'right' \| 'left' \| ('right' \| 'left')[]` | 组件容器内容对齐 | - | 175 | | labelWrap | boolean | 标签内容换行 | false | 176 | | labelWidth | number | 标签宽度(px) | - | 177 | | wrapperWidth | number | 组件容器宽度(px) | - | 178 | | wrapperWrap | boolean | 组件容器换行 | false | 179 | | labelCol | `number \| number[]` | 标签宽度(24 column) | - | 180 | | wrapperCol | `number \| number[]` | 组件容器宽度(24 column) | - | 181 | | fullness | boolean | 组件容器宽度 100% | false | 182 | | size | `'small' \| 'default' \| 'large'` | 组件尺寸 | default | 183 | | layout | `'vertical' \| 'horizontal' \| 'inline' \| ('vertical' \| 'horizontal' \| 'inline')[]` | 布局模式 | horizontal | 184 | | direction | `'rtl' \| 'ltr'` | 方向(暂不支持) | ltr | 185 | | inset | boolean | 内联布局 | false | 186 | | shallow | boolean | 上下文浅层传递 | true | 187 | | feedbackLayout | `'loose' \| 'terse' \| 'popover' \| 'none'` | 反馈布局 | true | 188 | | tooltipLayout | `"icon" \| "text"` | 问号提示布局 | `"icon"` | 189 | | tooltipIcon | ReactNode | 问号提示图标 | - | 190 | | bordered | boolean | 是否有边框 | true | 191 | | breakpoints | number[] | 容器尺寸断点 | - | 192 | | gridColumnGap | number | 网格布局列间距 | 8 | 193 | | gridRowGap | number | 网格布局行间距 | 4 | 194 | | spaceGap | number | 弹性间距 | 8 | 195 | ``` -------------------------------------------------------------------------------- /packages/core/src/models/BaseField.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { 2 | FormPath, 3 | FormPathPattern, 4 | isValid, 5 | toArr, 6 | each, 7 | isFn, 8 | } from '@formily/shared' 9 | import { 10 | JSXComponent, 11 | LifeCycleTypes, 12 | FieldDisplayTypes, 13 | FieldPatternTypes, 14 | FieldDecorator, 15 | FieldComponent, 16 | IFieldActions, 17 | } from '../types' 18 | import { 19 | locateNode, 20 | destroy, 21 | initFieldUpdate, 22 | getArrayParent, 23 | getObjectParent, 24 | } from '../shared/internals' 25 | import { Form } from './Form' 26 | import { Query } from './Query' 27 | 28 | export class BaseField<Decorator = any, Component = any, TextType = any> { 29 | title: TextType 30 | description: TextType 31 | 32 | selfDisplay: FieldDisplayTypes 33 | selfPattern: FieldPatternTypes 34 | initialized: boolean 35 | mounted: boolean 36 | unmounted: boolean 37 | 38 | content: any 39 | 40 | data: any 41 | 42 | decoratorType: Decorator 43 | decoratorProps: Record<string, any> 44 | componentType: Component 45 | componentProps: Record<string, any> 46 | 47 | designable: boolean 48 | address: FormPath 49 | path: FormPath 50 | form: Form 51 | 52 | disposers: (() => void)[] = [] 53 | 54 | actions: IFieldActions = {} 55 | 56 | locate(address: FormPathPattern) { 57 | this.form.fields[address.toString()] = this as any 58 | locateNode(this as any, address) 59 | } 60 | 61 | get indexes(): number[] { 62 | return this.path.transform(/^\d+$/, (...args) => 63 | args.map((index) => Number(index)) 64 | ) as number[] 65 | } 66 | 67 | get index() { 68 | return this.indexes[this.indexes.length - 1] ?? -1 69 | } 70 | 71 | get records() { 72 | const array = getArrayParent(this) 73 | return array?.value 74 | } 75 | 76 | get record() { 77 | const obj = getObjectParent(this) 78 | if (obj) { 79 | return obj.value 80 | } 81 | const index = this.index 82 | const array = getArrayParent(this, index) 83 | if (array) { 84 | return array.value?.[index] 85 | } 86 | return this.form.values 87 | } 88 | 89 | get component() { 90 | return [this.componentType, this.componentProps] 91 | } 92 | 93 | set component(value: FieldComponent<Component>) { 94 | const component = toArr(value) 95 | this.componentType = component[0] 96 | this.componentProps = component[1] || {} 97 | } 98 | 99 | get decorator() { 100 | return [this.decoratorType, this.decoratorProps] 101 | } 102 | 103 | set decorator(value: FieldDecorator<Decorator>) { 104 | const decorator = toArr(value) 105 | this.decoratorType = decorator[0] 106 | this.decoratorProps = decorator[1] || {} 107 | } 108 | 109 | get parent() { 110 | let parent = this.address.parent() 111 | let identifier = parent.toString() 112 | while (!this.form.fields[identifier]) { 113 | parent = parent.parent() 114 | identifier = parent.toString() 115 | if (!identifier) return 116 | } 117 | return this.form.fields[identifier] 118 | } 119 | 120 | get display(): FieldDisplayTypes { 121 | const parentDisplay = (this.parent as any)?.display 122 | if (parentDisplay && parentDisplay !== 'visible') { 123 | if (this.selfDisplay && this.selfDisplay !== 'visible') 124 | return this.selfDisplay 125 | return parentDisplay 126 | } 127 | if (isValid(this.selfDisplay)) return this.selfDisplay 128 | return parentDisplay || this.form.display || 'visible' 129 | } 130 | 131 | get pattern(): FieldPatternTypes { 132 | const parentPattern: FieldPatternTypes = 133 | (this.parent as any)?.pattern || this.form.pattern || 'editable' 134 | const selfPattern = this.selfPattern 135 | if (isValid(selfPattern)) { 136 | if (parentPattern === 'readPretty' && selfPattern !== 'editable') { 137 | return parentPattern 138 | } 139 | return selfPattern 140 | } 141 | return parentPattern 142 | } 143 | 144 | get editable() { 145 | return this.pattern === 'editable' 146 | } 147 | 148 | get disabled() { 149 | return this.pattern === 'disabled' 150 | } 151 | 152 | get readOnly() { 153 | return this.pattern === 'readOnly' 154 | } 155 | 156 | get readPretty() { 157 | return this.pattern === 'readPretty' 158 | } 159 | 160 | get hidden() { 161 | return this.display === 'hidden' 162 | } 163 | 164 | get visible() { 165 | return this.display === 'visible' 166 | } 167 | 168 | get destroyed() { 169 | return !this.form.fields[this.address.toString()] 170 | } 171 | 172 | set hidden(hidden: boolean) { 173 | if (!isValid(hidden)) return 174 | if (hidden) { 175 | this.display = 'hidden' 176 | } else { 177 | this.display = 'visible' 178 | } 179 | } 180 | 181 | set visible(visible: boolean) { 182 | if (!isValid(visible)) return 183 | if (visible) { 184 | this.display = 'visible' 185 | } else { 186 | this.display = 'none' 187 | } 188 | } 189 | 190 | set editable(editable: boolean) { 191 | if (!isValid(editable)) return 192 | if (editable) { 193 | this.pattern = 'editable' 194 | } else { 195 | this.pattern = 'readPretty' 196 | } 197 | } 198 | 199 | set readOnly(readOnly: boolean) { 200 | if (!isValid(readOnly)) return 201 | if (readOnly) { 202 | this.pattern = 'readOnly' 203 | } else { 204 | this.pattern = 'editable' 205 | } 206 | } 207 | 208 | set disabled(disabled: boolean) { 209 | if (!isValid(disabled)) return 210 | if (disabled) { 211 | this.pattern = 'disabled' 212 | } else { 213 | this.pattern = 'editable' 214 | } 215 | } 216 | 217 | set readPretty(readPretty: boolean) { 218 | if (!isValid(readPretty)) return 219 | if (readPretty) { 220 | this.pattern = 'readPretty' 221 | } else { 222 | this.pattern = 'editable' 223 | } 224 | } 225 | 226 | set pattern(pattern: FieldPatternTypes) { 227 | this.selfPattern = pattern 228 | } 229 | 230 | set display(display: FieldDisplayTypes) { 231 | this.selfDisplay = display 232 | } 233 | 234 | setTitle = (title?: TextType) => { 235 | this.title = title 236 | } 237 | 238 | setDescription = (description?: TextType) => { 239 | this.description = description 240 | } 241 | 242 | setDisplay = (type?: FieldDisplayTypes) => { 243 | this.display = type 244 | } 245 | 246 | setPattern = (type?: FieldPatternTypes) => { 247 | this.pattern = type 248 | } 249 | 250 | setComponent = <C extends JSXComponent, ComponentProps extends object = {}>( 251 | component?: C, 252 | props?: ComponentProps 253 | ) => { 254 | if (component) { 255 | this.componentType = component as any 256 | } 257 | if (props) { 258 | this.componentProps = this.componentProps || {} 259 | Object.assign(this.componentProps, props) 260 | } 261 | } 262 | 263 | setComponentProps = <ComponentProps extends object = {}>( 264 | props?: ComponentProps 265 | ) => { 266 | if (props) { 267 | this.componentProps = this.componentProps || {} 268 | Object.assign(this.componentProps, props) 269 | } 270 | } 271 | 272 | setDecorator = <D extends JSXComponent, ComponentProps extends object = {}>( 273 | component?: D, 274 | props?: ComponentProps 275 | ) => { 276 | if (component) { 277 | this.decoratorType = component as any 278 | } 279 | if (props) { 280 | this.decoratorProps = this.decoratorProps || {} 281 | Object.assign(this.decoratorProps, props) 282 | } 283 | } 284 | 285 | setDecoratorProps = <ComponentProps extends object = {}>( 286 | props?: ComponentProps 287 | ) => { 288 | if (props) { 289 | this.decoratorProps = this.decoratorProps || {} 290 | Object.assign(this.decoratorProps, props) 291 | } 292 | } 293 | 294 | setData = (data: any) => { 295 | this.data = data 296 | } 297 | 298 | setContent = (content: any) => { 299 | this.content = content 300 | } 301 | 302 | onInit = () => { 303 | this.initialized = true 304 | initFieldUpdate(this as any) 305 | this.notify(LifeCycleTypes.ON_FIELD_INIT) 306 | } 307 | 308 | onMount = () => { 309 | this.mounted = true 310 | this.unmounted = false 311 | this.notify(LifeCycleTypes.ON_FIELD_MOUNT) 312 | } 313 | 314 | onUnmount = () => { 315 | this.mounted = false 316 | this.unmounted = true 317 | this.notify(LifeCycleTypes.ON_FIELD_UNMOUNT) 318 | } 319 | 320 | query = (pattern: FormPathPattern | RegExp) => { 321 | return new Query({ 322 | pattern, 323 | base: this.address, 324 | form: this.form, 325 | }) 326 | } 327 | 328 | notify = (type: LifeCycleTypes, payload?: any) => { 329 | return this.form.notify(type, payload ?? this) 330 | } 331 | 332 | dispose = () => { 333 | this.disposers.forEach((dispose) => { 334 | dispose() 335 | }) 336 | this.form.removeEffects(this) 337 | } 338 | 339 | destroy = (forceClear = true) => { 340 | destroy(this.form.fields, this.address.toString(), forceClear) 341 | } 342 | 343 | match = (pattern: FormPathPattern) => { 344 | return FormPath.parse(pattern).matchAliasGroup(this.address, this.path) 345 | } 346 | 347 | inject = (actions: IFieldActions) => { 348 | each(actions, (action, key) => { 349 | if (isFn(action)) { 350 | this.actions[key] = action 351 | } 352 | }) 353 | } 354 | 355 | invoke = (name: string, ...args: any[]) => { 356 | return this.actions[name]?.(...args) 357 | } 358 | } 359 | ``` -------------------------------------------------------------------------------- /packages/next/docs/components/FormLayout.md: -------------------------------------------------------------------------------- ```markdown 1 | # FormLayout 2 | 3 | > Block-level layout batch control component, with the help of this component, we can easily control the layout mode of all FormItem components enclosed by FormLayout 4 | 5 | ## Markup Schema example 6 | 7 | ```tsx 8 | import React from 'react' 9 | import { Input, Select, FormItem, FormLayout } from '@formily/next' 10 | import { createForm } from '@formily/core' 11 | import { FormProvider, createSchemaField } from '@formily/react' 12 | 13 | const SchemaField = createSchemaField({ 14 | components: { 15 | Input, 16 | Select, 17 | FormItem, 18 | FormLayout, 19 | }, 20 | }) 21 | 22 | const form = createForm() 23 | 24 | export default () => ( 25 | <FormProvider form={form}> 26 | <SchemaField> 27 | <SchemaField.Void 28 | x-component="FormLayout" 29 | x-component-props={{ 30 | labelCol: 6, 31 | wrapperCol: 10, 32 | }} 33 | > 34 | <SchemaField.String 35 | name="input" 36 | title="input box" 37 | x-decorator="FormItem" 38 | x-decorator-props={{ 39 | tooltip: <div>123</div>, 40 | }} 41 | x-component="Input" 42 | required 43 | /> 44 | <SchemaField.String 45 | name="select" 46 | title="select box" 47 | x-decorator="FormItem" 48 | x-component="Select" 49 | required 50 | /> 51 | </SchemaField.Void> 52 | </SchemaField> 53 | </FormProvider> 54 | ) 55 | ``` 56 | 57 | ## JSON Schema case 58 | 59 | ```tsx 60 | import React from 'react' 61 | import { Input, Select, FormItem, FormLayout } from '@formily/next' 62 | import { createForm } from '@formily/core' 63 | import { FormProvider, createSchemaField } from '@formily/react' 64 | 65 | const SchemaField = createSchemaField({ 66 | components: { 67 | Input, 68 | Select, 69 | FormItem, 70 | FormLayout, 71 | }, 72 | }) 73 | 74 | const schema = { 75 | type: 'object', 76 | properties: { 77 | layout: { 78 | type: 'void', 79 | 'x-component': 'FormLayout', 80 | 'x-component-props': { 81 | labelCol: 6, 82 | wrapperCol: 10, 83 | layout: 'vertical', 84 | }, 85 | properties: { 86 | input: { 87 | type: 'string', 88 | title: 'input box', 89 | required: true, 90 | 'x-decorator': 'FormItem', 91 | 'x-decorator-props': { 92 | tooltip: <div>123</div>, 93 | }, 94 | 'x-component': 'Input', 95 | }, 96 | select: { 97 | type: 'string', 98 | title: 'Select box', 99 | required: true, 100 | 'x-decorator': 'FormItem', 101 | 'x-component': 'Select', 102 | }, 103 | }, 104 | }, 105 | }, 106 | } 107 | 108 | const form = createForm() 109 | 110 | export default () => ( 111 | <FormProvider form={form}> 112 | <SchemaField schema={schema} /> 113 | </FormProvider> 114 | ) 115 | ``` 116 | 117 | ## Pure JSX case 118 | 119 | ```tsx 120 | import React from 'react' 121 | import { 122 | Input, 123 | Select, 124 | FormItem, 125 | FormButtonGroup, 126 | Submit, 127 | FormLayout, 128 | } from '@formily/next' 129 | import { createForm } from '@formily/core' 130 | import { FormProvider, Field } from '@formily/react' 131 | 132 | const form = createForm() 133 | 134 | export default () => ( 135 | <FormProvider form={form}> 136 | <FormLayout labelCol={6} wrapperCol={10}> 137 | <Field 138 | name="input" 139 | required 140 | title="input box" 141 | decorator={[FormItem]} 142 | component={[Input]} 143 | /> 144 | <Field 145 | name="select" 146 | required 147 | title="select box" 148 | decorator={[FormItem]} 149 | component={[Select]} 150 | /> 151 | <FormButtonGroup.FormItem> 152 | <Submit onSubmit={console.log}>Submit</Submit> 153 | </FormButtonGroup.FormItem> 154 | </FormLayout> 155 | </FormProvider> 156 | ) 157 | ``` 158 | 159 | ## API 160 | 161 | | Property name | Type | Description | Default value | 162 | | -------------- | -------------------------------------------------------------------------------------- | ------------------------------------- | ------------- | 163 | | style | CSSProperties | Style | - | 164 | | className | string | class name | - | 165 | | colon | boolean | Is there a colon | true | 166 | | labelAlign | `'right' \| 'left' \| ('right' \| 'left')[]` | Label content alignment | - | 167 | | wrapperAlign | `'right' \| 'left' \| ('right' \| 'left')[]` | Component container content alignment | - | 168 | | labelWrap | boolean | Wrap label content | false | 169 | | labelWidth | number | Label width (px) | - | 170 | | wrapperWidth | number | Component container width (px) | - | 171 | | wrapperWrap | boolean | Component container wrap | false | 172 | | labelCol | `number \| number[]` | Label width (24 column) | - | 173 | | wrapperCol | `number \| number[]` | Component container width (24 column) | - | 174 | | fullness | boolean | Component container width 100% | false | 175 | | size | `'small' \|'default' \|'large'` | component size | default | 176 | | layout | `'vertical' \| 'horizontal' \| 'inline' \| ('vertical' \| 'horizontal' \| 'inline')[]` | layout mode | horizontal | 177 | | direction | `'rtl' \|'ltr'` | direction (not supported yet) | ltr | 178 | | inset | boolean | Inline layout | false | 179 | | shallow | boolean | shallow context transfer | true | 180 | | feedbackLayout | `'loose' \|'terse' \|'popover' \|'none'` | feedback layout | true | 181 | | tooltipLayout | `"icon" \| "text"` | Ask the prompt layout | `"icon"` | 182 | | tooltipIcon | ReactNode | Ask the prompt icon | - | 183 | | bordered | boolean | Is there a border | true | 184 | | breakpoints | number[] | Container size breakpoints | - | 185 | | gridColumnGap | number | Grid Column Gap | 8 | 186 | | gridRowGap | number | Grid Row Gap | 4 | 187 | | spaceGap | number | Space Gap | 8 | 188 | ``` -------------------------------------------------------------------------------- /packages/antd/docs/components/FormButtonGroup.md: -------------------------------------------------------------------------------- ```markdown 1 | # FormButtonGroup 2 | 3 | > Form button group layout component 4 | 5 | ## Common case 6 | 7 | ```tsx 8 | import React from 'react' 9 | import { 10 | FormButtonGroup, 11 | Submit, 12 | Reset, 13 | FormItem, 14 | Input, 15 | FormLayout, 16 | } from '@formily/antd' 17 | import { createForm } from '@formily/core' 18 | import { FormProvider, createSchemaField } from '@formily/react' 19 | const SchemaField = createSchemaField({ 20 | components: { 21 | FormItem, 22 | Input, 23 | }, 24 | }) 25 | 26 | const form = createForm() 27 | 28 | export default () => { 29 | return ( 30 | <FormProvider form={form}> 31 | <FormLayout labelCol={6} wrapperCol={10}> 32 | <SchemaField> 33 | <SchemaField.String 34 | title="input box" 35 | x-decorator="FormItem" 36 | required 37 | x-component="Input" 38 | /> 39 | <SchemaField.String 40 | title="input box" 41 | x-decorator="FormItem" 42 | required 43 | x-component="Input" 44 | /> 45 | <SchemaField.String 46 | title="input box" 47 | x-decorator="FormItem" 48 | required 49 | x-component="Input" 50 | /> 51 | <SchemaField.String 52 | title="input box" 53 | x-decorator="FormItem" 54 | required 55 | x-component="Input" 56 | /> 57 | <SchemaField.String 58 | title="input box" 59 | x-decorator="FormItem" 60 | required 61 | x-component="Input" 62 | /> 63 | <SchemaField.String 64 | title="input box" 65 | x-decorator="FormItem" 66 | required 67 | x-component="Input" 68 | /> 69 | <SchemaField.String 70 | title="input box" 71 | x-decorator="FormItem" 72 | required 73 | x-component="Input" 74 | /> 75 | <SchemaField.String 76 | title="input box" 77 | x-decorator="FormItem" 78 | required 79 | x-component="Input" 80 | /> 81 | <SchemaField.String 82 | title="input box" 83 | x-decorator="FormItem" 84 | required 85 | x-component="Input" 86 | /> 87 | </SchemaField> 88 | <FormButtonGroup.FormItem> 89 | <Submit onSubmit={console.log}>Submit</Submit> 90 | <Reset>Reset</Reset> 91 | </FormButtonGroup.FormItem> 92 | </FormLayout> 93 | </FormProvider> 94 | ) 95 | } 96 | ``` 97 | 98 | ## Suction bottom case 99 | 100 | ```tsx 101 | import React from 'react' 102 | import { 103 | FormButtonGroup, 104 | Submit, 105 | Reset, 106 | FormItem, 107 | FormLayout, 108 | Input, 109 | } from '@formily/antd' 110 | import { createForm } from '@formily/core' 111 | import { FormProvider, createSchemaField } from '@formily/react' 112 | 113 | const SchemaField = createSchemaField({ 114 | components: { 115 | FormItem, 116 | Input, 117 | }, 118 | }) 119 | 120 | const form = createForm() 121 | 122 | export default () => { 123 | return ( 124 | <FormProvider form={form}> 125 | <FormLayout labelCol={6} wrapperCol={10}> 126 | <SchemaField> 127 | <SchemaField.String 128 | title="input box" 129 | x-decorator="FormItem" 130 | required 131 | x-component="Input" 132 | /> 133 | <SchemaField.String 134 | title="input box" 135 | x-decorator="FormItem" 136 | required 137 | x-component="Input" 138 | /> 139 | <SchemaField.String 140 | title="input box" 141 | x-decorator="FormItem" 142 | required 143 | x-component="Input" 144 | /> 145 | <SchemaField.String 146 | title="input box" 147 | x-decorator="FormItem" 148 | required 149 | x-component="Input" 150 | /> 151 | <SchemaField.String 152 | title="input box" 153 | x-decorator="FormItem" 154 | required 155 | x-component="Input" 156 | /> 157 | <SchemaField.String 158 | title="input box" 159 | x-decorator="FormItem" 160 | required 161 | x-component="Input" 162 | /> 163 | <SchemaField.String 164 | title="input box" 165 | x-decorator="FormItem" 166 | required 167 | x-component="Input" 168 | /> 169 | <SchemaField.String 170 | title="input box" 171 | x-decorator="FormItem" 172 | required 173 | x-component="Input" 174 | /> 175 | <SchemaField.String 176 | title="input box" 177 | x-decorator="FormItem" 178 | required 179 | x-component="Input" 180 | /> 181 | </SchemaField> 182 | <FormButtonGroup.Sticky> 183 | <FormButtonGroup.FormItem> 184 | <Submit onSubmit={console.log}>Submit</Submit> 185 | <Reset>Reset</Reset> 186 | </FormButtonGroup.FormItem> 187 | </FormButtonGroup.Sticky> 188 | </FormLayout> 189 | </FormProvider> 190 | ) 191 | } 192 | ``` 193 | 194 | ## Suction bottom centering case 195 | 196 | ```tsx 197 | import React from 'react' 198 | import { 199 | FormButtonGroup, 200 | Submit, 201 | Reset, 202 | FormItem, 203 | FormLayout, 204 | Input, 205 | } from '@formily/antd' 206 | import { createForm } from '@formily/core' 207 | import { FormProvider, createSchemaField } from '@formily/react' 208 | 209 | const SchemaField = createSchemaField({ 210 | components: { 211 | FormItem, 212 | Input, 213 | }, 214 | }) 215 | 216 | const form = createForm() 217 | 218 | export default () => { 219 | return ( 220 | <FormProvider form={form}> 221 | <FormLayout labelCol={6} wrapperCol={10}> 222 | <SchemaField> 223 | <SchemaField.String 224 | title="input box" 225 | x-decorator="FormItem" 226 | required 227 | x-component="Input" 228 | /> 229 | <SchemaField.String 230 | title="input box" 231 | x-decorator="FormItem" 232 | required 233 | x-component="Input" 234 | /> 235 | <SchemaField.String 236 | title="input box" 237 | x-decorator="FormItem" 238 | required 239 | x-component="Input" 240 | /> 241 | <SchemaField.String 242 | title="input box" 243 | x-decorator="FormItem" 244 | required 245 | x-component="Input" 246 | /> 247 | <SchemaField.String 248 | title="input box" 249 | x-decorator="FormItem" 250 | required 251 | x-component="Input" 252 | /> 253 | <SchemaField.String 254 | title="input box" 255 | x-decorator="FormItem" 256 | required 257 | x-component="Input" 258 | /> 259 | <SchemaField.String 260 | title="input box" 261 | x-decorator="FormItem" 262 | required 263 | x-component="Input" 264 | /> 265 | <SchemaField.String 266 | title="input box" 267 | x-decorator="FormItem" 268 | required 269 | x-component="Input" 270 | /> 271 | <SchemaField.String 272 | title="input box" 273 | x-decorator="FormItem" 274 | required 275 | x-component="Input" 276 | /> 277 | </SchemaField> 278 | <FormButtonGroup.Sticky align="center"> 279 | <FormButtonGroup> 280 | <Submit onSubmit={console.log}>Submit</Submit> 281 | <Reset>Reset</Reset> 282 | </FormButtonGroup> 283 | </FormButtonGroup.Sticky> 284 | </FormLayout> 285 | </FormProvider> 286 | ) 287 | } 288 | ``` 289 | 290 | ## API 291 | 292 | ### FormButtonGroup 293 | 294 | > This component is mainly used to handle the button group gap 295 | 296 | | Property name | Type | Description | Default value | 297 | | ------------- | --------------------------- | ----------- | ------------- | 298 | | gutter | number | Gap size | 8px | 299 | | align | `'left'\|'center'\|'right'` | Alignment | `'left'` | 300 | 301 | ### FormButtonGroup.FormItem 302 | 303 | > This component is mainly used to deal with the alignment of the button group and the main form FormItem 304 | 305 | Refer to [FormItem](/components/form-item) property 306 | 307 | ### FormButtonGroup.Sticky 308 | 309 | > This component is mainly used to deal with the floating positioning problem of the button group 310 | 311 | | Property name | Type | Description | Default value | 312 | | ------------- | --------------------------- | ----------- | ------------- | 313 | | align | `'left'\|'center'\|'right'` | Alignment | `'left'` | 314 | ``` -------------------------------------------------------------------------------- /packages/next/docs/components/Upload.md: -------------------------------------------------------------------------------- ```markdown 1 | # Upload 2 | 3 | > Upload components 4 | > 5 | > Note: Using the upload component, it is recommended that users perform secondary packaging. Users do not need to care about the data communication between the upload component and Formily, only the style and basic upload configuration are required. 6 | 7 | ## Markup Schema example 8 | 9 | ```tsx 10 | import React from 'react' 11 | import { 12 | Upload, 13 | FormItem, 14 | FormButtonGroup, 15 | Submit, 16 | FormLayout, 17 | } from '@formily/next' 18 | import { createForm } from '@formily/core' 19 | import { FormProvider, createSchemaField } from '@formily/react' 20 | import { Button } from '@alifd/next' 21 | import { UploadOutlined, InboxOutlined } from '@ant-design/icons' 22 | 23 | const NormalUpload = (props) => { 24 | return ( 25 | <Upload 26 | {...props} 27 | withCredentials={false} 28 | action="https://www.mocky.io/v2/5cc8019d300000980a055e76" 29 | headers={{ 30 | authorization: 'authorization-text', 31 | }} 32 | > 33 | <Button> 34 | <UploadOutlined /> 35 | upload files 36 | </Button> 37 | </Upload> 38 | ) 39 | } 40 | 41 | const CardUpload = (props) => { 42 | return ( 43 | <Upload.Card 44 | {...props} 45 | action="https://www.mocky.io/v2/5cc8019d300000980a055e76" 46 | withCredentials={false} 47 | headers={{ 48 | authorization: 'authorization-text', 49 | }} 50 | /> 51 | ) 52 | } 53 | 54 | const DraggerUpload = (props) => { 55 | return ( 56 | <Upload.Dragger 57 | {...props} 58 | withCredentials={false} 59 | action="https://www.mocky.io/v2/5cc8019d300000980a055e76" 60 | > 61 | <div className="next-upload-drag"> 62 | <p className="next-upload-drag-icon" style={{ fontSize: 50 }}> 63 | <InboxOutlined /> 64 | </p> 65 | <p className="next-upload-drag-text"> 66 | click to <Button text>download template</Button> or drag file here 67 | </p> 68 | <p className="next-upload-drag-hint">supports docx, xls, PDF </p> 69 | </div> 70 | </Upload.Dragger> 71 | ) 72 | } 73 | 74 | const SchemaField = createSchemaField({ 75 | components: { 76 | NormalUpload, 77 | CardUpload, 78 | DraggerUpload, 79 | FormItem, 80 | }, 81 | }) 82 | 83 | const form = createForm() 84 | 85 | export default () => ( 86 | <FormProvider form={form}> 87 | <FormLayout labelCol={6} wrapperCol={14}> 88 | <SchemaField> 89 | <SchemaField.Array 90 | name="upload" 91 | title="Upload" 92 | x-decorator="FormItem" 93 | x-component="NormalUpload" 94 | required 95 | /> 96 | <SchemaField.Array 97 | name="upload2" 98 | title="Card upload" 99 | x-decorator="FormItem" 100 | x-component="CardUpload" 101 | required 102 | /> 103 | <SchemaField.Array 104 | name="upload3" 105 | title="Drag and drop upload" 106 | x-decorator="FormItem" 107 | x-component="DraggerUpload" 108 | required 109 | /> 110 | </SchemaField> 111 | <FormButtonGroup.FormItem> 112 | <Submit onSubmit={console.log}>Submit</Submit> 113 | </FormButtonGroup.FormItem> 114 | </FormLayout> 115 | </FormProvider> 116 | ) 117 | ``` 118 | 119 | ## JSON Schema case 120 | 121 | ```tsx 122 | import React from 'react' 123 | import { 124 | Upload, 125 | FormItem, 126 | FormButtonGroup, 127 | Submit, 128 | FormLayout, 129 | } from '@formily/next' 130 | import { createForm } from '@formily/core' 131 | import { FormProvider, createSchemaField } from '@formily/react' 132 | import { Button } from '@alifd/next' 133 | import { UploadOutlined, InboxOutlined } from '@ant-design/icons' 134 | 135 | const NormalUpload = (props) => { 136 | return ( 137 | <Upload 138 | {...props} 139 | withCredentials={false} 140 | action="https://www.mocky.io/v2/5cc8019d300000980a055e76" 141 | headers={{ 142 | authorization: 'authorization-text', 143 | }} 144 | > 145 | <Button> 146 | <UploadOutlined /> 147 | upload files 148 | </Button> 149 | </Upload> 150 | ) 151 | } 152 | 153 | const CardUpload = (props) => { 154 | return ( 155 | <Upload.Card 156 | {...props} 157 | withCredentials={false} 158 | action="https://www.mocky.io/v2/5cc8019d300000980a055e76" 159 | headers={{ 160 | authorization: 'authorization-text', 161 | }} 162 | /> 163 | ) 164 | } 165 | 166 | const DraggerUpload = (props) => { 167 | return ( 168 | <Upload.Dragger 169 | {...props} 170 | withCredentials={false} 171 | action="https://www.mocky.io/v2/5cc8019d300000980a055e76" 172 | > 173 | <div className="next-upload-drag"> 174 | <p className="next-upload-drag-icon" style={{ fontSize: 50 }}> 175 | <InboxOutlined /> 176 | </p> 177 | <p className="next-upload-drag-text"> 178 | click to <Button text>download template</Button> or drag file here 179 | </p> 180 | <p className="next-upload-drag-hint">supports docx, xls, PDF </p> 181 | </div> 182 | </Upload.Dragger> 183 | ) 184 | } 185 | 186 | const SchemaField = createSchemaField({ 187 | components: { 188 | NormalUpload, 189 | CardUpload, 190 | DraggerUpload, 191 | FormItem, 192 | }, 193 | }) 194 | 195 | const form = createForm() 196 | 197 | const schema = { 198 | type: 'object', 199 | properties: { 200 | upload: { 201 | type: 'array', 202 | title: 'Upload', 203 | required: true, 204 | 'x-decorator': 'FormItem', 205 | 'x-component': 'NormalUpload', 206 | }, 207 | upload2: { 208 | type: 'array', 209 | title: 'Card upload', 210 | required: true, 211 | 'x-decorator': 'FormItem', 212 | 'x-component': 'CardUpload', 213 | }, 214 | upload3: { 215 | type: 'array', 216 | title: 'Drag and drop upload', 217 | required: true, 218 | 'x-decorator': 'FormItem', 219 | 'x-component': 'DraggerUpload', 220 | }, 221 | }, 222 | } 223 | 224 | export default () => ( 225 | <FormProvider form={form}> 226 | <FormLayout labelCol={6} wrapperCol={14}> 227 | <SchemaField schema={schema} /> 228 | <FormButtonGroup.FormItem> 229 | <Submit onSubmit={console.log}>Submit</Submit> 230 | </FormButtonGroup.FormItem> 231 | </FormLayout> 232 | </FormProvider> 233 | ) 234 | ``` 235 | 236 | ## Pure JSX case 237 | 238 | ```tsx 239 | import React from 'react' 240 | import { 241 | Upload, 242 | FormItem, 243 | FormButtonGroup, 244 | Submit, 245 | FormLayout, 246 | } from '@formily/next' 247 | import { createForm } from '@formily/core' 248 | import { FormProvider, Field } from '@formily/react' 249 | import { Button } from '@alifd/next' 250 | import { UploadOutlined, InboxOutlined } from '@ant-design/icons' 251 | 252 | const NormalUpload = (props) => { 253 | return ( 254 | <Upload 255 | {...props} 256 | withCredentials={false} 257 | action="https://www.mocky.io/v2/5cc8019d300000980a055e76" 258 | headers={{ 259 | authorization: 'authorization-text', 260 | }} 261 | > 262 | <Button> 263 | <UploadOutlined /> 264 | upload files 265 | </Button> 266 | </Upload> 267 | ) 268 | } 269 | 270 | const CardUpload = (props) => { 271 | return ( 272 | <Upload.Card 273 | {...props} 274 | withCredentials={false} 275 | action="https://www.mocky.io/v2/5cc8019d300000980a055e76" 276 | headers={{ 277 | authorization: 'authorization-text', 278 | }} 279 | /> 280 | ) 281 | } 282 | 283 | const DraggerUpload = (props) => { 284 | return ( 285 | <Upload.Dragger 286 | {...props} 287 | withCredentials={false} 288 | action="https://www.mocky.io/v2/5cc8019d300000980a055e76" 289 | > 290 | <div className="next-upload-drag"> 291 | <p className="next-upload-drag-icon" style={{ fontSize: 50 }}> 292 | <InboxOutlined /> 293 | </p> 294 | <p className="next-upload-drag-text"> 295 | click to <Button text>download template</Button> or drag file here 296 | </p> 297 | <p className="next-upload-drag-hint">supports docx, xls, PDF </p> 298 | </div> 299 | </Upload.Dragger> 300 | ) 301 | } 302 | 303 | const form = createForm() 304 | 305 | export default () => ( 306 | <FormProvider form={form}> 307 | <FormLayout labelCol={6} wrapperCol={14}> 308 | <Field 309 | name="upload" 310 | title="Upload" 311 | required 312 | decorator={[FormItem]} 313 | component={[NormalUpload]} 314 | /> 315 | <Field 316 | name="upload2" 317 | title="Card upload" 318 | required 319 | decorator={[FormItem]} 320 | component={[CardUpload]} 321 | /> 322 | <Field 323 | name="upload3" 324 | title="Drag and drop upload" 325 | required 326 | decorator={[FormItem]} 327 | component={[DraggerUpload]} 328 | /> 329 | <FormButtonGroup.FormItem> 330 | <Submit onSubmit={console.log}>Submit</Submit> 331 | </FormButtonGroup.FormItem> 332 | </FormLayout> 333 | </FormProvider> 334 | ) 335 | ``` 336 | 337 | ## API 338 | 339 | Reference https://fusion.design/pc/component/basic/upload 340 | ``` -------------------------------------------------------------------------------- /packages/next/docs/components/FormButtonGroup.md: -------------------------------------------------------------------------------- ```markdown 1 | # FormButtonGroup 2 | 3 | > Form button group layout component 4 | 5 | ## Common case 6 | 7 | ```tsx 8 | import React from 'react' 9 | import { 10 | FormButtonGroup, 11 | Submit, 12 | Reset, 13 | FormItem, 14 | Input, 15 | FormLayout, 16 | } from '@formily/next' 17 | import { createForm } from '@formily/core' 18 | import { FormProvider, createSchemaField } from '@formily/react' 19 | 20 | const SchemaField = createSchemaField({ 21 | components: { 22 | FormItem, 23 | Input, 24 | }, 25 | }) 26 | 27 | const form = createForm() 28 | 29 | export default () => { 30 | return ( 31 | <FormProvider form={form}> 32 | <FormLayout labelCol={6} wrapperCol={10}> 33 | <SchemaField> 34 | <SchemaField.String 35 | title="input box" 36 | x-decorator="FormItem" 37 | required 38 | x-component="Input" 39 | /> 40 | <SchemaField.String 41 | title="input box" 42 | x-decorator="FormItem" 43 | required 44 | x-component="Input" 45 | /> 46 | <SchemaField.String 47 | title="input box" 48 | x-decorator="FormItem" 49 | required 50 | x-component="Input" 51 | /> 52 | <SchemaField.String 53 | title="input box" 54 | x-decorator="FormItem" 55 | required 56 | x-component="Input" 57 | /> 58 | <SchemaField.String 59 | title="input box" 60 | x-decorator="FormItem" 61 | required 62 | x-component="Input" 63 | /> 64 | <SchemaField.String 65 | title="input box" 66 | x-decorator="FormItem" 67 | required 68 | x-component="Input" 69 | /> 70 | <SchemaField.String 71 | title="input box" 72 | x-decorator="FormItem" 73 | required 74 | x-component="Input" 75 | /> 76 | <SchemaField.String 77 | title="input box" 78 | x-decorator="FormItem" 79 | required 80 | x-component="Input" 81 | /> 82 | <SchemaField.String 83 | title="input box" 84 | x-decorator="FormItem" 85 | required 86 | x-component="Input" 87 | /> 88 | </SchemaField> 89 | <FormButtonGroup.FormItem> 90 | <Submit onSubmit={console.log}>Submit</Submit> 91 | <Reset>Reset</Reset> 92 | </FormButtonGroup.FormItem> 93 | </FormLayout> 94 | </FormProvider> 95 | ) 96 | } 97 | ``` 98 | 99 | ## Suction bottom case 100 | 101 | ```tsx 102 | import React from 'react' 103 | import { 104 | FormButtonGroup, 105 | Submit, 106 | Reset, 107 | FormItem, 108 | FormLayout, 109 | Input, 110 | } from '@formily/next' 111 | import { createForm } from '@formily/core' 112 | import { FormProvider, createSchemaField } from '@formily/react' 113 | 114 | const SchemaField = createSchemaField({ 115 | components: { 116 | FormItem, 117 | Input, 118 | }, 119 | }) 120 | 121 | const form = createForm() 122 | 123 | export default () => { 124 | return ( 125 | <FormProvider form={form}> 126 | <FormLayout labelCol={6} wrapperCol={10}> 127 | <SchemaField> 128 | <SchemaField.String 129 | title="input box" 130 | x-decorator="FormItem" 131 | required 132 | x-component="Input" 133 | /> 134 | <SchemaField.String 135 | title="input box" 136 | x-decorator="FormItem" 137 | required 138 | x-component="Input" 139 | /> 140 | <SchemaField.String 141 | title="input box" 142 | x-decorator="FormItem" 143 | required 144 | x-component="Input" 145 | /> 146 | <SchemaField.String 147 | title="input box" 148 | x-decorator="FormItem" 149 | required 150 | x-component="Input" 151 | /> 152 | <SchemaField.String 153 | title="input box" 154 | x-decorator="FormItem" 155 | required 156 | x-component="Input" 157 | /> 158 | <SchemaField.String 159 | title="input box" 160 | x-decorator="FormItem" 161 | required 162 | x-component="Input" 163 | /> 164 | <SchemaField.String 165 | title="input box" 166 | x-decorator="FormItem" 167 | required 168 | x-component="Input" 169 | /> 170 | <SchemaField.String 171 | title="input box" 172 | x-decorator="FormItem" 173 | required 174 | x-component="Input" 175 | /> 176 | <SchemaField.String 177 | title="input box" 178 | x-decorator="FormItem" 179 | required 180 | x-component="Input" 181 | /> 182 | </SchemaField> 183 | <FormButtonGroup.Sticky> 184 | <FormButtonGroup.FormItem> 185 | <Submit onSubmit={console.log}>Submit</Submit> 186 | <Reset>Reset</Reset> 187 | </FormButtonGroup.FormItem> 188 | </FormButtonGroup.Sticky> 189 | </FormLayout> 190 | </FormProvider> 191 | ) 192 | } 193 | ``` 194 | 195 | ## Suction bottom centering case 196 | 197 | ```tsx 198 | import React from 'react' 199 | import { 200 | FormButtonGroup, 201 | Submit, 202 | Reset, 203 | FormItem, 204 | FormLayout, 205 | Input, 206 | } from '@formily/next' 207 | import { createForm } from '@formily/core' 208 | import { FormProvider, createSchemaField } from '@formily/react' 209 | 210 | const SchemaField = createSchemaField({ 211 | components: { 212 | FormItem, 213 | Input, 214 | }, 215 | }) 216 | 217 | const form = createForm() 218 | 219 | export default () => { 220 | return ( 221 | <FormProvider form={form}> 222 | <FormLayout labelCol={6} wrapperCol={10}> 223 | <SchemaField> 224 | <SchemaField.String 225 | title="input box" 226 | x-decorator="FormItem" 227 | required 228 | x-component="Input" 229 | /> 230 | <SchemaField.String 231 | title="input box" 232 | x-decorator="FormItem" 233 | required 234 | x-component="Input" 235 | /> 236 | <SchemaField.String 237 | title="input box" 238 | x-decorator="FormItem" 239 | required 240 | x-component="Input" 241 | /> 242 | <SchemaField.String 243 | title="input box" 244 | x-decorator="FormItem" 245 | required 246 | x-component="Input" 247 | /> 248 | <SchemaField.String 249 | title="input box" 250 | x-decorator="FormItem" 251 | required 252 | x-component="Input" 253 | /> 254 | <SchemaField.String 255 | title="input box" 256 | x-decorator="FormItem" 257 | required 258 | x-component="Input" 259 | /> 260 | <SchemaField.String 261 | title="input box" 262 | x-decorator="FormItem" 263 | required 264 | x-component="Input" 265 | /> 266 | <SchemaField.String 267 | title="input box" 268 | x-decorator="FormItem" 269 | required 270 | x-component="Input" 271 | /> 272 | <SchemaField.String 273 | title="input box" 274 | x-decorator="FormItem" 275 | required 276 | x-component="Input" 277 | /> 278 | </SchemaField> 279 | <FormButtonGroup.Sticky align="center"> 280 | <FormButtonGroup> 281 | <Submit onSubmit={console.log}>Submit</Submit> 282 | <Reset>Reset</Reset> 283 | </FormButtonGroup> 284 | </FormButtonGroup.Sticky> 285 | </FormLayout> 286 | </FormProvider> 287 | ) 288 | } 289 | ``` 290 | 291 | ## API 292 | 293 | ### FormButtonGroup 294 | 295 | > This component is mainly used to handle the button group gap 296 | 297 | | Property name | Type | Description | Default value | 298 | | ------------- | --------------------------- | ----------- | ------------- | 299 | | gutter | number | Gap size | 8px | 300 | | align | `'left'\|'center'\|'right'` | Alignment | `'left'` | 301 | 302 | ### FormButtonGroup.FormItem 303 | 304 | > This component is mainly used to deal with the alignment of the button group and the main form FormItem 305 | 306 | Refer to [FormItem](/components/form-item) property 307 | 308 | ### FormButtonGroup.Sticky 309 | 310 | > This component is mainly used to deal with the floating positioning problem of the button group 311 | 312 | | Property name | Type | Description | Default value | 313 | | ------------- | --------------------------- | ----------- | ------------- | 314 | | align | `'left'\|'center'\|'right'` | Alignment | `'left'` | 315 | ```