This is page 7 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/element/docs/demos/guide/form-dialog/template.vue: -------------------------------------------------------------------------------- ```vue 1 | <template> 2 | <Button @click="handleOpen">点击打开表单</Button> 3 | </template> 4 | 5 | <script> 6 | import { FormDialog, FormLayout, FormItem, Input } from '@formily/element' 7 | import { Button } from 'element-ui' 8 | import { Field } from '@formily/vue' 9 | 10 | export default { 11 | components: { Button }, 12 | data() { 13 | return {} 14 | }, 15 | methods: { 16 | handleOpen() { 17 | FormDialog('弹框表单', () => ( 18 | <FormLayout labelCol={6} wrapperCol={10}> 19 | <Field 20 | name="aaa" 21 | required 22 | title="输入框1" 23 | decorator={[FormItem]} 24 | component={[Input]} 25 | /> 26 | <Field 27 | name="bbb" 28 | required 29 | title="输入框2" 30 | decorator={[FormItem]} 31 | component={[Input]} 32 | /> 33 | <Field 34 | name="ccc" 35 | required 36 | title="输入框3" 37 | decorator={[FormItem]} 38 | component={[Input]} 39 | /> 40 | <Field 41 | name="ddd" 42 | required 43 | title="输入框4" 44 | decorator={[FormItem]} 45 | component={[Input]} 46 | /> 47 | <FormDialog.Footer> 48 | <span style={{ marginLeft: '4px' }}>扩展文案</span> 49 | </FormDialog.Footer> 50 | </FormLayout> 51 | )) 52 | .forOpen((payload, next) => { 53 | setTimeout(() => { 54 | next({ 55 | initialValues: { 56 | aaa: '123', 57 | }, 58 | }) 59 | }, 1000) 60 | }) 61 | .forConfirm((payload, next) => { 62 | setTimeout(() => { 63 | console.log(payload) 64 | next(payload) 65 | }, 1000) 66 | }) 67 | .forCancel((payload, next) => { 68 | setTimeout(() => { 69 | console.log(payload) 70 | next(payload) 71 | }, 1000) 72 | }) 73 | .open() 74 | .then(console.log) 75 | .catch(console.error) 76 | }, 77 | }, 78 | } 79 | </script> 80 | ``` -------------------------------------------------------------------------------- /packages/element/src/__builtins__/shared/transform-component.ts: -------------------------------------------------------------------------------- ```typescript 1 | import type { Component } from 'vue' 2 | import { merge } from '@formily/shared' 3 | import { h } from '@formily/vue' 4 | import { isVue2, defineComponent } from 'vue-demi' 5 | 6 | type ListenersTransformRules = Record<string, string> 7 | const noop = () => {} 8 | 9 | export const transformComponent = <T extends Record<string, any>>( 10 | tag: any, 11 | transformRules?: ListenersTransformRules, 12 | defaultProps?: Partial<T> 13 | ): Component<T> | any => { 14 | if (isVue2) { 15 | return defineComponent({ 16 | setup(props, { attrs, slots, listeners }) { 17 | return () => { 18 | const data = { 19 | attrs: { 20 | ...attrs, 21 | }, 22 | on: { 23 | ...listeners, 24 | }, 25 | } 26 | 27 | if (transformRules) { 28 | const transformListeners = transformRules 29 | Object.keys(transformListeners).forEach((extract) => { 30 | if (data.on !== undefined) { 31 | data.on[transformListeners[extract]] = listeners[extract] || noop 32 | } 33 | }) 34 | } 35 | if (defaultProps) { 36 | data.attrs = merge(defaultProps, data.attrs) 37 | } 38 | 39 | return h(tag, data, slots) 40 | } 41 | }, 42 | }) 43 | } else { 44 | return defineComponent({ 45 | setup(props, { attrs, slots }) { 46 | return () => { 47 | let data = { 48 | ...attrs, 49 | } 50 | if (transformRules) { 51 | const listeners = transformRules 52 | Object.keys(listeners).forEach((extract) => { 53 | const event = listeners[extract] 54 | data[`on${event[0].toUpperCase()}${event.slice(1)}`] = 55 | attrs[`on${extract[0].toUpperCase()}${extract.slice(1)}`] || noop 56 | }) 57 | } 58 | if (defaultProps) { 59 | data = merge(defaultProps, data) 60 | } 61 | return h(tag, data, slots) 62 | } 63 | }, 64 | }) 65 | } 66 | } 67 | ``` -------------------------------------------------------------------------------- /packages/antd/src/date-picker/index.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import moment from 'moment' 2 | import { connect, mapProps, mapReadPretty } from '@formily/react' 3 | import { DatePicker as AntdDatePicker } from 'antd' 4 | import { 5 | DatePickerProps as AntdDatePickerProps, 6 | RangePickerProps, 7 | } from 'antd/lib/date-picker' 8 | import { PreviewText } from '../preview-text' 9 | import { formatMomentValue, momentable } from '../__builtins__' 10 | 11 | type DatePickerProps<PickerProps> = Exclude< 12 | PickerProps, 13 | 'value' | 'onChange' 14 | > & { 15 | value: string 16 | onChange: (value: string | string[]) => void 17 | } 18 | 19 | type ComposedDatePicker = React.FC< 20 | React.PropsWithChildren<AntdDatePickerProps> 21 | > & { 22 | RangePicker?: React.FC<React.PropsWithChildren<RangePickerProps>> 23 | } 24 | 25 | const mapDateFormat = function () { 26 | const getDefaultFormat = (props: DatePickerProps<AntdDatePickerProps>) => { 27 | if (props['picker'] === 'month') { 28 | return 'YYYY-MM' 29 | } else if (props['picker'] === 'quarter') { 30 | return 'YYYY-\\QQ' 31 | } else if (props['picker'] === 'year') { 32 | return 'YYYY' 33 | } else if (props['picker'] === 'week') { 34 | return 'gggg-wo' 35 | } 36 | return props['showTime'] ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD' 37 | } 38 | return (props: any) => { 39 | const format = props['format'] || getDefaultFormat(props) 40 | const onChange = props.onChange 41 | return { 42 | ...props, 43 | format: format, 44 | value: momentable(props.value, format === 'gggg-wo' ? 'gggg-ww' : format), 45 | onChange: (value: moment.Moment | moment.Moment[]) => { 46 | if (onChange) { 47 | onChange(formatMomentValue(value, format)) 48 | } 49 | }, 50 | } 51 | } 52 | } 53 | 54 | export const DatePicker: ComposedDatePicker = connect( 55 | AntdDatePicker, 56 | mapProps(mapDateFormat()), 57 | mapReadPretty(PreviewText.DatePicker) 58 | ) 59 | 60 | DatePicker.RangePicker = connect( 61 | AntdDatePicker.RangePicker, 62 | mapProps(mapDateFormat()), 63 | mapReadPretty(PreviewText.DateRangePicker) 64 | ) 65 | 66 | export default DatePicker 67 | ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/form-drawer/markup-schema.vue: -------------------------------------------------------------------------------- ```vue 1 | <template> 2 | <Button @click="handleOpen">点击打开表单</Button> 3 | </template> 4 | 5 | <script> 6 | import { FormDrawer, FormLayout, FormItem, Input } from '@formily/element' 7 | import { Button } from 'element-ui' 8 | import { createSchemaField } from '@formily/vue' 9 | 10 | const { SchemaField } = createSchemaField({ 11 | components: { 12 | FormItem, 13 | Input, 14 | }, 15 | }) 16 | 17 | // 抽屉表单组件 18 | const DrawerForm = { 19 | props: ['form'], 20 | data() { 21 | const schema = { 22 | type: 'object', 23 | properties: { 24 | aaa: { 25 | type: 'string', 26 | title: '输入框1', 27 | required: true, 28 | 'x-decorator': 'FormItem', 29 | 'x-component': 'Input', 30 | }, 31 | bbb: { 32 | type: 'string', 33 | title: '输入框2', 34 | required: true, 35 | 'x-decorator': 'FormItem', 36 | 'x-component': 'Input', 37 | }, 38 | ccc: { 39 | type: 'string', 40 | title: '输入框3', 41 | required: true, 42 | 'x-decorator': 'FormItem', 43 | 'x-component': 'Input', 44 | }, 45 | ddd: { 46 | type: 'string', 47 | title: '输入框4', 48 | required: true, 49 | 'x-decorator': 'FormItem', 50 | 'x-component': 'Input', 51 | }, 52 | }, 53 | } 54 | return { 55 | schema, 56 | } 57 | }, 58 | render() { 59 | return ( 60 | <FormLayout labelCol={6} wrapperCol={10}> 61 | <SchemaField schema={this.schema} /> 62 | <FormDrawer.Footer> 63 | <span style={{ marginLeft: '4px' }}>扩展文案</span> 64 | </FormDrawer.Footer> 65 | </FormLayout> 66 | ) 67 | }, 68 | } 69 | 70 | export default { 71 | components: { Button }, 72 | data() { 73 | return {} 74 | }, 75 | methods: { 76 | handleOpen() { 77 | FormDrawer('抽屉表单', DrawerForm) 78 | .forOpen((props, next) => { 79 | setTimeout(() => { 80 | next() 81 | }, 1000) 82 | }) 83 | .open({ 84 | initialValues: { 85 | aaa: '123', 86 | }, 87 | }) 88 | .then(console.log) 89 | }, 90 | }, 91 | } 92 | </script> 93 | ``` -------------------------------------------------------------------------------- /packages/react/docs/api/shared/connect.md: -------------------------------------------------------------------------------- ```markdown 1 | # connect 2 | 3 | ## Description 4 | 5 | Mainly used for non-intrusive access to third-party component libraries Formily 6 | 7 | ## Signature 8 | 9 | ```ts 10 | interface IComponentMapper<T extends React.FC> { 11 | (target: T): React.FC 12 | } 13 | interface connect<T extends React.FC> { 14 | (target: T, ...args: IComponentMapper<T>[]): React.FC 15 | } 16 | ``` 17 | 18 | The first parameter passed in is the component to be connected, the following parameters are component mappers, each mapper is a function, usually we will use the built-in [mapProps](/api/shared/map- props) and [mapReadPretty](/api/shared/map-read-pretty) mapper 19 | 20 | ## Example 21 | 22 | ```tsx 23 | import React, { useMemo } from 'react' 24 | import { createForm } from '@formily/core' 25 | import { 26 | FormProvider, 27 | FormConsumer, 28 | Field, 29 | connect, 30 | mapProps, 31 | } from '@formily/react' 32 | import { Input, Form, Button } from 'antd' 33 | 34 | // FormItem UI component 35 | const FormItem = connect( 36 | Form.Item, 37 | mapProps( 38 | { 39 | title: 'label', 40 | description: 'extra', 41 | required: true, 42 | validateStatus: true, 43 | }, 44 | (props, field) => { 45 | return { 46 | ...props, 47 | help: field.selfErrors?.length ? field.selfErrors : undefined, 48 | } 49 | } 50 | ) 51 | ) 52 | 53 | export default () => { 54 | const form = useMemo(() => createForm({ validateFirst: true })) 55 | return ( 56 | <FormProvider form={form}> 57 | <Form layout="vertical"> 58 | <Field 59 | name="name" 60 | title="Name" 61 | required 62 | decorator={[FormItem]} 63 | component={[Input, { placeholder: 'Please Input' }]} 64 | /> 65 | <code> 66 | <pre> 67 | <FormConsumer> 68 | {(form) => JSON.stringify(form.values, null, 2)} 69 | </FormConsumer> 70 | </pre> 71 | </code> 72 | <Button 73 | type="primary" 74 | onClick={() => { 75 | form.submit(console.log) 76 | }} 77 | > 78 | Submit 79 | </Button> 80 | </Form> 81 | </FormProvider> 82 | ) 83 | } 84 | ``` 85 | ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/form-grid/markup-schema.vue: -------------------------------------------------------------------------------- ```vue 1 | <template> 2 | <FormProvider :form="form"> 3 | <SchemaField> 4 | <SchemaVoidField 5 | x-component="FormGrid" 6 | :x-component-props="{ 7 | maxColumns: 3, 8 | minColumns: 2, 9 | }" 10 | > 11 | <SchemaStringField 12 | name="aaa" 13 | title="aaa" 14 | x-decorator="FormItem" 15 | :x-decorator-props="{ gridSpan: 2 }" 16 | x-component="Input" 17 | /> 18 | <SchemaStringField 19 | name="bbb" 20 | title="bbb" 21 | x-decorator="FormItem" 22 | x-component="Input" 23 | /> 24 | <SchemaStringField 25 | name="ccc" 26 | title="ccc" 27 | x-decorator="FormItem" 28 | x-component="Input" 29 | /> 30 | <SchemaStringField 31 | name="ddd" 32 | title="ddd" 33 | x-decorator="FormItem" 34 | x-component="Input" 35 | /> 36 | <SchemaStringField 37 | name="eee" 38 | title="eee" 39 | x-decorator="FormItem" 40 | x-component="Input" 41 | /> 42 | <SchemaStringField 43 | name="fff" 44 | title="fff" 45 | x-decorator="FormItem" 46 | x-component="Input" 47 | /> 48 | <SchemaStringField 49 | name="ggg" 50 | title="ggg" 51 | x-decorator="FormItem" 52 | x-component="Input" 53 | /> 54 | </SchemaVoidField> 55 | </SchemaField> 56 | <Submit @submit="onSubmit">提交</Submit> 57 | </FormProvider> 58 | </template> 59 | 60 | <script> 61 | import { createForm } from '@formily/core' 62 | import { createSchemaField, FormProvider } from '@formily/vue' 63 | import { FormItem, Input, Submit, FormGrid } from '@formily/element' 64 | 65 | const form = createForm() 66 | const fields = createSchemaField({ 67 | components: { 68 | FormItem, 69 | Input, 70 | FormGrid, 71 | }, 72 | }) 73 | 74 | export default { 75 | components: { FormProvider, ...fields, Submit }, 76 | data() { 77 | return { 78 | form, 79 | } 80 | }, 81 | methods: { 82 | onSubmit(value) { 83 | console.log(value) 84 | }, 85 | }, 86 | } 87 | </script> 88 | ``` -------------------------------------------------------------------------------- /packages/react/docs/api/components/RecordsScope.md: -------------------------------------------------------------------------------- ```markdown 1 | --- 2 | order: 10 3 | --- 4 | 5 | # RecordsScope 6 | 7 | ## Description 8 | 9 | Standard scoped injection component for injecting the following built-in variables: 10 | 11 | - `$records` current record list data 12 | 13 | ## Signature 14 | 15 | ```ts 16 | interface IRecordsScopeProps { 17 | getRecords(): any[] 18 | } 19 | 20 | type RecordsScope = React.FC<React.PropsWithChildren<IRecordsScopeProps>> 21 | ``` 22 | 23 | ## Usage 24 | 25 | Any auto-incrementing list extension component should use RecordsScope internally to pass record scope variables. Components that have implemented this convention include: 26 | All components of the ArrayX family in @formily/antd and @formily/next 27 | 28 | ## Custom component extension use case 29 | 30 | ```tsx 31 | import React from 'react' 32 | import { createForm } from '@formily/core' 33 | import { FormProvider, createSchemaField, RecordsScope } from '@formily/react' 34 | import { Input } from 'antd' 35 | 36 | const form = createForm() 37 | 38 | const MyCustomComponent = (props) => { 39 | return ( 40 | <RecordsScope getRecords={() => props.records}> 41 | {props.children} 42 | </RecordsScope> 43 | ) 44 | } 45 | 46 | const SchemaField = createSchemaField({ 47 | components: { 48 | Input, 49 | MyCustomComponent, 50 | }, 51 | }) 52 | 53 | export default () => ( 54 | <FormProvider form={form}> 55 | <SchemaField 56 | schema={{ 57 | type: 'object', 58 | properties: { 59 | records: { 60 | type: 'void', 61 | 'x-component': 'MyCustomComponent', 62 | 'x-component-props': { 63 | records: [ 64 | { 65 | name: 'Name', 66 | code: 'Code', 67 | }, 68 | ], 69 | }, 70 | properties: { 71 | input: { 72 | type: 'string', 73 | 'x-component': 'Input', 74 | 'x-value': 75 | '{{`' + 76 | '${$records[0].name} ' + 77 | '${$records[0].code}' + 78 | '`}}', 79 | }, 80 | }, 81 | }, 82 | }, 83 | }} 84 | ></SchemaField> 85 | </FormProvider> 86 | ) 87 | ``` 88 | ``` -------------------------------------------------------------------------------- /packages/element/src/submit/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { IFormFeedback } from '@formily/core' 2 | import { observer } from '@formily/reactive-vue' 3 | import { h, useParentForm } from '@formily/vue' 4 | import { defineComponent } from 'vue-demi' 5 | 6 | import type { Button as ElButtonProps } from 'element-ui' 7 | import { Button as ElButton } from 'element-ui' 8 | 9 | export interface ISubmitProps extends ElButtonProps { 10 | onClick?: (e: MouseEvent) => any 11 | onSubmit?: (values: any) => any 12 | onSubmitSuccess?: (payload: any) => void 13 | onSubmitFailed?: (feedbacks: IFormFeedback[]) => void 14 | } 15 | 16 | export const Submit = observer( 17 | defineComponent<ISubmitProps>({ 18 | name: 'FSubmit', 19 | props: ['onClick', 'onSubmit', 'onSubmitSuccess', 'onSubmitFailed'], 20 | setup(props, { attrs, slots, listeners }) { 21 | const formRef = useParentForm() 22 | 23 | return () => { 24 | const { 25 | onClick = listeners?.click, 26 | onSubmit = listeners?.submit, 27 | onSubmitSuccess = listeners?.submitSuccess, 28 | onSubmitFailed = listeners?.submitFailed, 29 | } = props 30 | 31 | const form = formRef?.value 32 | return h( 33 | ElButton, 34 | { 35 | attrs: { 36 | nativeType: listeners?.submit ? 'button' : 'submit', 37 | type: 'primary', 38 | ...attrs, 39 | loading: 40 | attrs.loading !== undefined ? attrs.loading : form?.submitting, 41 | }, 42 | on: { 43 | ...listeners, 44 | click: (e: any) => { 45 | if (onClick) { 46 | if (onClick(e) === false) return 47 | } 48 | if (onSubmit) { 49 | form 50 | ?.submit(onSubmit as (e: any) => void) 51 | .then(onSubmitSuccess as (e: any) => void) 52 | .catch(onSubmitFailed as (e: any) => void) 53 | } 54 | }, 55 | }, 56 | }, 57 | slots 58 | ) 59 | } 60 | }, 61 | }) 62 | ) 63 | 64 | export default Submit 65 | ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/cascader/markup-schema.vue: -------------------------------------------------------------------------------- ```vue 1 | <template> 2 | <Form :form="form"> 3 | <SchemaField> 4 | <SchemaStringField 5 | name="address" 6 | title="地址选择" 7 | required 8 | x-decorator="FormItem" 9 | x-component="Cascader" 10 | :x-component-props="{ 11 | style: { 12 | width: '240px', 13 | }, 14 | }" 15 | /> 16 | </SchemaField> 17 | 18 | <Submit @submit="onSubmit">提交</Submit> 19 | </Form> 20 | </template> 21 | 22 | <script> 23 | import { createForm, onFieldReact } from '@formily/core' 24 | import { createSchemaField } from '@formily/vue' 25 | import { Form, FormItem, Cascader, Submit } from '@formily/element' 26 | import { action } from '@formily/reactive' 27 | import axios from 'axios' 28 | 29 | const transformAddress = (data = {}) => { 30 | return Object.entries(data).reduce((buf, [key, value]) => { 31 | if (typeof value === 'string') 32 | return buf.concat({ 33 | label: value, 34 | value: key, 35 | }) 36 | const { name, code, cities, districts } = value 37 | const _cities = transformAddress(cities) 38 | const _districts = transformAddress(districts) 39 | return buf.concat({ 40 | label: name, 41 | value: code, 42 | children: _cities.length 43 | ? _cities 44 | : _districts.length 45 | ? _districts 46 | : undefined, 47 | }) 48 | }, []) 49 | } 50 | 51 | const useAddress = (pattern) => { 52 | onFieldReact(pattern, (field) => { 53 | field.loading = true 54 | axios('//unpkg.com/china-location/dist/location.json') 55 | .then((res) => res.data) 56 | .then( 57 | action.bound((data) => { 58 | field.dataSource = transformAddress(data) 59 | field.loading = false 60 | }) 61 | ) 62 | }) 63 | } 64 | 65 | const form = createForm({ 66 | effects: () => { 67 | useAddress('address') 68 | }, 69 | }) 70 | const fields = createSchemaField({ 71 | components: { 72 | FormItem, 73 | Cascader, 74 | }, 75 | }) 76 | 77 | export default { 78 | components: { Form, ...fields, Submit }, 79 | data() { 80 | return { 81 | form, 82 | } 83 | }, 84 | methods: { 85 | onSubmit(value) { 86 | console.log(value) 87 | }, 88 | }, 89 | } 90 | </script> 91 | ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/upload/template.vue: -------------------------------------------------------------------------------- ```vue 1 | <template> 2 | <Form :form="form" :label-col="4" :wrapper-col="10"> 3 | <ArrayField 4 | name="upload" 5 | title="上传" 6 | :decorator="[FormItem]" 7 | :component="[ 8 | Upload, 9 | { 10 | action: 'https://formily-vue.free.beeceptor.com/file', 11 | textContent: '上传', 12 | }, 13 | ]" 14 | required 15 | /> 16 | <ArrayField 17 | name="upload2" 18 | title="卡片上传" 19 | :decorator="[FormItem]" 20 | :component="[ 21 | Upload, 22 | { 23 | listType: 'picture-card', 24 | action: 'https://formily-vue.free.beeceptor.com/file', 25 | }, 26 | ]" 27 | required 28 | /> 29 | <ArrayField 30 | name="upload3" 31 | title="拖拽上传" 32 | :decorator="[FormItem]" 33 | :component="[ 34 | Upload, 35 | { 36 | action: 'https://formily-vue.free.beeceptor.com/file', 37 | textContent: '将文件拖到此处,或者点击上传', 38 | drag: true, 39 | }, 40 | ]" 41 | required 42 | /> 43 | <ArrayField 44 | name="custom" 45 | title="自定义按钮" 46 | :decorator="[FormItem]" 47 | :component="[ 48 | Upload, 49 | { 50 | action: 'https://formily-vue.free.beeceptor.com/file', 51 | }, 52 | ]" 53 | required 54 | ><UploadButton 55 | /></ArrayField> 56 | <FormButtonGroup align-form-item> 57 | <Submit @submit="onSubmit">提交</Submit> 58 | </FormButtonGroup> 59 | </Form> 60 | </template> 61 | 62 | <script> 63 | import { createForm } from '@formily/core' 64 | import { ArrayField } from '@formily/vue' 65 | import { 66 | Form, 67 | FormItem, 68 | Upload, 69 | Submit, 70 | FormButtonGroup, 71 | } from '@formily/element' 72 | import { Button } from 'element-ui' 73 | 74 | const UploadButton = { 75 | functional: true, 76 | render(h) { 77 | return h(Button, {}, '上传图片') 78 | }, 79 | } 80 | 81 | const form = createForm() 82 | 83 | export default { 84 | components: { UploadButton, Form, ArrayField, Submit, FormButtonGroup }, 85 | data() { 86 | return { 87 | FormItem, 88 | Upload, 89 | form, 90 | } 91 | }, 92 | methods: { 93 | onSubmit(value) { 94 | console.log(value) 95 | }, 96 | }, 97 | } 98 | </script> 99 | ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/form-grid/json-schema.vue: -------------------------------------------------------------------------------- ```vue 1 | <template> 2 | <FormProvider :form="form"> 3 | <SchemaField :schema="schema" /> 4 | <Submit @submit="onSubmit">提交</Submit> 5 | </FormProvider> 6 | </template> 7 | 8 | <script> 9 | import { createForm } from '@formily/core' 10 | import { createSchemaField, FormProvider } from '@formily/vue' 11 | import { FormItem, Input, Submit, FormGrid } from '@formily/element' 12 | 13 | const schema = { 14 | type: 'object', 15 | properties: { 16 | grid: { 17 | type: 'void', 18 | 'x-component': 'FormGrid', 19 | 'x-component-props': { 20 | minColumns: [4, 6, 10], 21 | }, 22 | properties: { 23 | aaa: { 24 | type: 'string', 25 | title: 'AAA', 26 | 'x-decorator': 'FormItem', 27 | 'x-component': 'Input', 28 | }, 29 | bbb: { 30 | type: 'string', 31 | title: 'BBB', 32 | 'x-decorator': 'FormItem', 33 | 'x-component': 'Input', 34 | }, 35 | ccc: { 36 | type: 'string', 37 | title: 'CCC', 38 | 'x-decorator': 'FormItem', 39 | 'x-component': 'Input', 40 | }, 41 | ddd: { 42 | type: 'string', 43 | title: 'DDD', 44 | 'x-decorator': 'FormItem', 45 | 'x-component': 'Input', 46 | }, 47 | eee: { 48 | type: 'string', 49 | title: 'EEE', 50 | 'x-decorator': 'FormItem', 51 | 'x-component': 'Input', 52 | }, 53 | fff: { 54 | type: 'string', 55 | title: 'FFF', 56 | 'x-decorator': 'FormItem', 57 | 'x-component': 'Input', 58 | }, 59 | ggg: { 60 | type: 'string', 61 | title: 'GGG', 62 | 'x-decorator': 'FormItem', 63 | 'x-component': 'Input', 64 | }, 65 | }, 66 | }, 67 | }, 68 | } 69 | 70 | const form = createForm() 71 | const { SchemaField } = createSchemaField({ 72 | components: { 73 | FormItem, 74 | Input, 75 | FormGrid, 76 | }, 77 | }) 78 | 79 | export default { 80 | components: { FormProvider, SchemaField, Submit }, 81 | data() { 82 | return { 83 | form, 84 | schema, 85 | } 86 | }, 87 | methods: { 88 | onSubmit(value) { 89 | console.log(value) 90 | }, 91 | }, 92 | } 93 | </script> 94 | ``` -------------------------------------------------------------------------------- /packages/core/src/models/Heart.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { isStr, isArr, Subscribable } from '@formily/shared' 2 | import { LifeCycle } from './LifeCycle' 3 | import { IHeartProps } from '../types' 4 | export class Heart<Payload = any, Context = any> extends Subscribable { 5 | lifecycles: LifeCycle<Payload>[] = [] 6 | 7 | outerLifecycles: Map<any, LifeCycle<Payload>[]> = new Map() 8 | 9 | context: Context 10 | 11 | constructor({ lifecycles, context }: IHeartProps<Context> = {}) { 12 | super() 13 | this.lifecycles = this.buildLifeCycles(lifecycles || []) 14 | this.context = context 15 | } 16 | 17 | buildLifeCycles = (lifecycles: LifeCycle[]) => { 18 | return lifecycles.reduce((buf, item) => { 19 | if (item instanceof LifeCycle) { 20 | return buf.concat(item) 21 | } else { 22 | if (isArr(item)) { 23 | return this.buildLifeCycles(item) 24 | } else if (typeof item === 'object') { 25 | this.context = item 26 | return buf 27 | } 28 | return buf 29 | } 30 | }, []) 31 | } 32 | 33 | addLifeCycles = (id: any, lifecycles: LifeCycle[] = []) => { 34 | const observers = this.buildLifeCycles(lifecycles) 35 | if (observers.length) { 36 | this.outerLifecycles.set(id, observers) 37 | } 38 | } 39 | 40 | hasLifeCycles = (id: any) => { 41 | return this.outerLifecycles.has(id) 42 | } 43 | 44 | removeLifeCycles = (id: any) => { 45 | this.outerLifecycles.delete(id) 46 | } 47 | 48 | setLifeCycles = (lifecycles: LifeCycle[] = []) => { 49 | this.lifecycles = this.buildLifeCycles(lifecycles) 50 | } 51 | 52 | publish = <P, C>(type: any, payload?: P, context?: C) => { 53 | if (isStr(type)) { 54 | this.lifecycles.forEach((lifecycle) => { 55 | lifecycle.notify(type, payload, context || this.context) 56 | }) 57 | this.outerLifecycles.forEach((lifecycles) => { 58 | lifecycles.forEach((lifecycle) => { 59 | lifecycle.notify(type, payload, context || this.context) 60 | }) 61 | }) 62 | this.notify({ 63 | type, 64 | payload, 65 | }) 66 | } 67 | } 68 | 69 | clear = () => { 70 | this.lifecycles = [] 71 | this.outerLifecycles.clear() 72 | this.unsubscribe() 73 | } 74 | } 75 | ``` -------------------------------------------------------------------------------- /packages/reactive/src/__tests__/tracker.spec.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { Tracker, observable } from '../' 2 | 3 | test('base tracker', () => { 4 | const obs = observable<any>({}) 5 | const fn = jest.fn() 6 | const view = () => { 7 | fn(obs.value) 8 | } 9 | const scheduler = () => { 10 | tracker.track(view) 11 | } 12 | const tracker = new Tracker(scheduler) 13 | 14 | tracker.track(view) 15 | obs.value = 123 16 | expect(fn).nthCalledWith(1, undefined) 17 | expect(fn).nthCalledWith(2, 123) 18 | tracker.dispose() 19 | }) 20 | 21 | test('nested tracker', () => { 22 | const obs = observable<any>({}) 23 | const fn = jest.fn() 24 | const view = () => { 25 | obs.value = obs.value || 321 26 | fn(obs.value) 27 | } 28 | const scheduler = () => { 29 | tracker.track(view) 30 | } 31 | const tracker = new Tracker(scheduler) 32 | 33 | tracker.track(view) 34 | expect(fn).toBeCalledTimes(1) 35 | expect(fn).nthCalledWith(1, 321) 36 | obs.value = 123 37 | expect(fn).toBeCalledTimes(2) 38 | expect(fn).nthCalledWith(2, 123) 39 | tracker.dispose() 40 | }) 41 | 42 | test('tracker recollect dependencies', () => { 43 | const obs = observable<any>({ 44 | aa: 'aaa', 45 | bb: 'bbb', 46 | cc: 'ccc', 47 | }) 48 | const fn = jest.fn() 49 | const view = () => { 50 | fn() 51 | if (obs.aa === 'aaa') { 52 | return obs.bb 53 | } 54 | return obs.cc 55 | } 56 | const scheduler = () => { 57 | tracker.track(view) 58 | } 59 | const tracker = new Tracker(scheduler) 60 | 61 | tracker.track(view) 62 | obs.aa = '111' 63 | obs.bb = '222' 64 | expect(fn).toBeCalledTimes(2) 65 | tracker.dispose() 66 | }) 67 | 68 | test('shared scheduler with multi tracker(mock react strict mode)', () => { 69 | const obs = observable<any>({}) 70 | 71 | const component = () => obs.value 72 | 73 | const render = () => { 74 | tracker1.track(component) 75 | tracker2.track(component) 76 | } 77 | 78 | const scheduler1 = jest.fn(() => { 79 | tracker2.track(component) 80 | }) 81 | const scheduler2 = jest.fn(() => { 82 | tracker1.track(component) 83 | }) 84 | const tracker1 = new Tracker(scheduler1, 'tracker1') 85 | const tracker2 = new Tracker(scheduler2, 'tracker2') 86 | 87 | render() 88 | 89 | obs.value = 123 90 | 91 | expect(scheduler1).toBeCalledTimes(1) 92 | expect(scheduler2).toBeCalledTimes(0) 93 | }) 94 | ``` -------------------------------------------------------------------------------- /packages/antd/src/password/index.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import React from 'react' 2 | import { connect, mapReadPretty } from '@formily/react' 3 | import { Input } from 'antd' 4 | import { PasswordProps } from 'antd/lib/input' 5 | import { PasswordStrength } from './PasswordStrength' 6 | import { PreviewText } from '../preview-text' 7 | 8 | export interface IPasswordProps extends PasswordProps { 9 | checkStrength: boolean 10 | } 11 | 12 | export const Password = connect((props: IPasswordProps) => { 13 | const { value, className, checkStrength, ...others } = props 14 | const blockStyle: React.CSSProperties = { 15 | position: 'absolute', 16 | zIndex: 1, 17 | height: 8, 18 | top: 0, 19 | background: '#fff', 20 | width: 1, 21 | transform: 'translate(-50%, 0)', 22 | } 23 | return ( 24 | <span className={className}> 25 | <Input.Password {...others} value={value} /> 26 | {checkStrength && ( 27 | <PasswordStrength value={String(value)}> 28 | {(score) => { 29 | return ( 30 | <div 31 | style={{ 32 | background: '#e0e0e0', 33 | marginBottom: 3, 34 | position: 'relative', 35 | }} 36 | > 37 | <div style={{ ...blockStyle, left: '20%' }} /> 38 | <div style={{ ...blockStyle, left: '40%' }} /> 39 | <div style={{ ...blockStyle, left: '60%' }} /> 40 | <div style={{ ...blockStyle, left: '80%' }} /> 41 | <div 42 | style={{ 43 | position: 'relative', 44 | backgroundImage: 45 | '-webkit-linear-gradient(left, #ff5500, #ff9300)', 46 | transition: 'all 0.35s ease-in-out', 47 | height: 8, 48 | width: '100%', 49 | marginTop: 5, 50 | clipPath: `polygon(0 0,${score}% 0,${score}% 100%,0 100%)`, 51 | }} 52 | /> 53 | </div> 54 | ) 55 | }} 56 | </PasswordStrength> 57 | )} 58 | </span> 59 | ) 60 | }, mapReadPretty(PreviewText.Input)) 61 | 62 | export default Password 63 | ``` -------------------------------------------------------------------------------- /packages/react/docs/api/hooks/useField.md: -------------------------------------------------------------------------------- ```markdown 1 | # useField 2 | 3 | ## Description 4 | 5 | Mainly used in custom components to read current field properties, manipulate field status, etc. It can be used in the subtree of all Field components. Note that the one you get is [GeneralField](https://core.formilyjs.org/ api/models/field#generalfield), if you need to process different types of fields, please use [Type Checker](https://core.formilyjs.org/api/entry/form-checker) 6 | 7 | <Alert> 8 | Note: If you want to use useField in a custom component and respond to changes in the field model, you need to wrap the observer for the custom component 9 | </Alert> 10 | 11 | ## Signature 12 | 13 | ```ts 14 | interface useField { 15 | (): Field 16 | } 17 | ``` 18 | 19 | ## Example 20 | 21 | ```tsx 22 | import React, { useMemo } from 'react' 23 | import { createForm } from '@formily/core' 24 | import { 25 | FormProvider, 26 | FormConsumer, 27 | Field, 28 | useField, 29 | observer, 30 | } from '@formily/react' 31 | import { Input, Form, Button } from 'antd' 32 | 33 | // FormItem UI component 34 | const FormItem = observer(({ children }) => { 35 | const field = useField() 36 | return ( 37 | <Form.Item 38 | label={field.title} 39 | help={field.selfErrors?.length ? field.selfErrors : undefined} 40 | extra={field.description} 41 | validateStatus={field.validateStatus} 42 | > 43 | {children} 44 | </Form.Item> 45 | ) 46 | }) 47 | 48 | export default () => { 49 | const form = useMemo(() => createForm({ validateFirst: true })) 50 | return ( 51 | <FormProvider form={form}> 52 | <Form layout="vertical"> 53 | <Field 54 | name="name" 55 | title="Name" 56 | required 57 | decorator={[FormItem]} 58 | component={[Input, { placeholder: 'Please Input' }]} 59 | /> 60 | <code> 61 | <pre> 62 | <FormConsumer> 63 | {(form) => JSON.stringify(form.values, null, 2)} 64 | </FormConsumer> 65 | </pre> 66 | </code> 67 | <Button 68 | type="primary" 69 | onClick={() => { 70 | form.submit(console.log) 71 | }} 72 | > 73 | Submit 74 | </Button> 75 | </Form> 76 | </FormProvider> 77 | ) 78 | } 79 | ``` 80 | ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/preview-text/base.vue: -------------------------------------------------------------------------------- ```vue 1 | <template> 2 | <FormLayout :labelCol="6" :wrapperCol="10"> 3 | <FormProvider :form="form"> 4 | <SchemaField> 5 | <SchemaStringField 6 | x-decorator="FormItem" 7 | title="文本预览" 8 | x-component="PreviewText.Input" 9 | default="Hello world" 10 | /> 11 | <SchemaStringField 12 | x-decorator="FormItem" 13 | title="选择项预览" 14 | x-component="PreviewText.Select" 15 | :x-component-props="{ 16 | multiple: true, 17 | }" 18 | :default="['123', '222']" 19 | :enum="[ 20 | { label: 'A111', value: '123' }, 21 | { 22 | label: 'A222', 23 | value: '222', 24 | }, 25 | ]" 26 | /> 27 | <SchemaStringField 28 | x-decorator="FormItem" 29 | title="日期预览" 30 | x-component="PreviewText.DatePicker" 31 | default="2020-11-23 22:15:20" 32 | /> 33 | <SchemaStringField 34 | x-decorator="FormItem" 35 | title="时间预览" 36 | x-component="PreviewText.TimePicker" 37 | :default="['2020-11-23 22:15:20', '2020-11-23 23:15:20']" 38 | /> 39 | <SchemaStringField 40 | x-decorator="FormItem" 41 | title="Cascader预览" 42 | x-component="PreviewText.Cascader" 43 | :default="['hangzhou', 'yuhang']" 44 | :enum="[ 45 | { label: '杭州', value: 'hangzhou' }, 46 | { label: '余杭', value: 'yuhang' }, 47 | ]" 48 | /> 49 | </SchemaField> 50 | </FormProvider> 51 | </FormLayout> 52 | </template> 53 | 54 | <script> 55 | import { createForm } from '@formily/core' 56 | import { FormProvider, createSchemaField } from '@formily/vue' 57 | import { FormLayout, FormItem, PreviewText } from '@formily/element' 58 | 59 | const fields = createSchemaField({ 60 | components: { 61 | FormItem, 62 | PreviewText, 63 | }, 64 | }) 65 | 66 | export default { 67 | components: { 68 | FormProvider, 69 | FormLayout, 70 | ...fields, 71 | }, 72 | data() { 73 | const form = createForm() 74 | return { 75 | form, 76 | } 77 | }, 78 | methods: { 79 | log(v) { 80 | console.log(v) 81 | }, 82 | }, 83 | } 84 | </script> 85 | ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/array-tabs/json-schema.vue: -------------------------------------------------------------------------------- ```vue 1 | <template> 2 | <FormProvider :form="form"> 3 | <SchemaField :schema="schema" /> 4 | <Submit @submit="log">提交</Submit> 5 | </FormProvider> 6 | </template> 7 | 8 | <script> 9 | import { createForm } from '@formily/core' 10 | import { FormProvider, createSchemaField } from '@formily/vue' 11 | import { 12 | FormButtonGroup, 13 | Submit, 14 | FormItem, 15 | Space, 16 | Input, 17 | ArrayTabs, 18 | } from '@formily/element' 19 | import { Button } from 'element-ui' 20 | 21 | const SchemaField = createSchemaField({ 22 | components: { 23 | FormItem, 24 | Space, 25 | Input, 26 | ArrayTabs, 27 | }, 28 | }) 29 | 30 | export default { 31 | components: { 32 | FormProvider, 33 | FormButtonGroup, 34 | Button, 35 | Submit, 36 | ...SchemaField, 37 | }, 38 | 39 | data() { 40 | const form = createForm() 41 | const schema = { 42 | type: 'object', 43 | properties: { 44 | string_array: { 45 | type: 'array', 46 | title: '字符串数组', 47 | 'x-decorator': 'FormItem', 48 | maxItems: 3, 49 | 'x-component': 'ArrayTabs', 50 | items: { 51 | type: 'string', 52 | 'x-decorator': 'FormItem', 53 | required: true, 54 | 'x-component': 'Input', 55 | }, 56 | }, 57 | array: { 58 | type: 'array', 59 | title: '对象数组', 60 | 'x-decorator': 'FormItem', 61 | maxItems: 3, 62 | 'x-component': 'ArrayTabs', 63 | items: { 64 | type: 'object', 65 | properties: { 66 | aaa: { 67 | type: 'string', 68 | 'x-decorator': 'FormItem', 69 | title: 'AAA', 70 | required: true, 71 | 'x-component': 'Input', 72 | }, 73 | bbb: { 74 | type: 'string', 75 | 'x-decorator': 'FormItem', 76 | title: 'BBB', 77 | required: true, 78 | 'x-component': 'Input', 79 | }, 80 | }, 81 | }, 82 | }, 83 | }, 84 | } 85 | 86 | return { 87 | form, 88 | schema, 89 | } 90 | }, 91 | methods: { 92 | log(values) { 93 | console.log(values) 94 | }, 95 | }, 96 | } 97 | </script> 98 | 99 | <style lang="scss" scoped></style> 100 | ``` -------------------------------------------------------------------------------- /packages/vue/docs/demos/api/hooks/use-field.vue: -------------------------------------------------------------------------------- ```vue 1 | <template> 2 | <FormProvider :form="form"> 3 | <Form layout="vertical"> 4 | <Field 5 | name="name" 6 | title="Name" 7 | required 8 | :decorator="[FormItem]" 9 | :component="[Input, { placeholder: 'Please Input' }]" 10 | /> 11 | <FormConsumer> 12 | <template #default="{ form }"> 13 | <div style="white-space: pre; margin-bottom: 16px"> 14 | {{ JSON.stringify(form.values, null, 2) }} 15 | </div> 16 | <Button 17 | type="primary" 18 | @click=" 19 | () => { 20 | form.submit(log) 21 | } 22 | " 23 | > 24 | Submit 25 | </Button> 26 | </template> 27 | </FormConsumer> 28 | </Form> 29 | </FormProvider> 30 | </template> 31 | 32 | <script> 33 | import { defineComponent, h } from '@vue/composition-api' 34 | import { Form, Input, Button } from 'ant-design-vue' 35 | import { createForm, setValidateLanguage } from '@formily/core' 36 | import { FormProvider, FormConsumer, Field, useField } from '@formily/vue' 37 | import { observer } from '@formily/reactive-vue' 38 | import 'ant-design-vue/dist/antd.css' 39 | 40 | setValidateLanguage('en') 41 | 42 | const FormItem = observer( 43 | defineComponent({ 44 | setup(props, { slots }) { 45 | const fieldRef = useField() 46 | return () => { 47 | const field = fieldRef.value 48 | return h( 49 | Form.Item, 50 | { 51 | props: { 52 | label: field.title, 53 | required: field.required, 54 | help: field.selfErrors?.length ? field.selfErrors : undefined, 55 | extra: field.description, 56 | validateStatus: field.validateStatus, 57 | }, 58 | }, 59 | slots?.default() 60 | ) 61 | } 62 | }, 63 | }) 64 | ) 65 | 66 | export default { 67 | components: { 68 | FormProvider, 69 | FormConsumer, 70 | Field, 71 | Form, 72 | Button, 73 | }, 74 | data() { 75 | const form = createForm({ validateFirst: true }) 76 | return { 77 | FormItem, 78 | Input, 79 | form, 80 | } 81 | }, 82 | methods: { 83 | log(...args) { 84 | console.log(...args) 85 | }, 86 | }, 87 | } 88 | </script> 89 | ``` -------------------------------------------------------------------------------- /packages/reactive/src/tree.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { ObModelSymbol, ObModelNodeSymbol, RawNode } from './environment' 2 | import { raw as getRaw } from './externals' 3 | import { PropertyKey, IOperation } from './types' 4 | export class DataChange { 5 | node: DataNode 6 | key: PropertyKey 7 | object: object 8 | type: string 9 | value: any 10 | oldValue: any 11 | constructor(operation: IOperation, node: DataNode) { 12 | this.node = node 13 | this.key = operation.key 14 | this.type = operation.type 15 | this.object = operation.target 16 | this.value = operation.value 17 | this.oldValue = operation.oldValue 18 | } 19 | 20 | get path() { 21 | return this.node.path.concat(this.key) 22 | } 23 | } 24 | export class DataNode { 25 | target: any 26 | 27 | key: PropertyKey 28 | 29 | value: any 30 | 31 | constructor(target: any, key: PropertyKey, value: any) { 32 | this.target = target 33 | this.key = key 34 | this.value = value 35 | } 36 | 37 | get path() { 38 | if (!this.parent) return this.key ? [this.key] : [] 39 | return this.parent.path.concat(this.key) 40 | } 41 | 42 | get targetRaw() { 43 | return getRaw(this.target) 44 | } 45 | 46 | get parent() { 47 | if (!this.target) return 48 | return getDataNode(this.targetRaw) 49 | } 50 | 51 | isEqual(node: DataNode) { 52 | if (this.key) { 53 | return node.targetRaw === this.targetRaw && node.key === this.key 54 | } 55 | return node.value === this.value 56 | } 57 | 58 | contains(node: DataNode) { 59 | if (node === this) return true 60 | let parent = node.parent 61 | while (!!parent) { 62 | if (this.isEqual(parent)) return true 63 | parent = parent.parent 64 | } 65 | return false 66 | } 67 | } 68 | 69 | export const getDataNode = (raw: any) => { 70 | if (raw?.[ObModelNodeSymbol]) { 71 | return raw[ObModelNodeSymbol] 72 | } 73 | return RawNode.get(raw) 74 | } 75 | 76 | export const setDataNode = (raw: any, node: DataNode) => { 77 | if (raw?.[ObModelSymbol]) { 78 | raw[ObModelNodeSymbol] = node 79 | return 80 | } 81 | RawNode.set(raw, node) 82 | } 83 | 84 | export const buildDataTree = (target: any, key: PropertyKey, value: any) => { 85 | const raw = getRaw(value) 86 | const currentNode = getDataNode(raw) 87 | if (currentNode) return currentNode 88 | setDataNode(raw, new DataNode(target, key, value)) 89 | } 90 | ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/editable/template.vue: -------------------------------------------------------------------------------- ```vue 1 | <template> 2 | <FormProvider :form="form"> 3 | <Field 4 | name="date" 5 | title="日期" 6 | :decorator="[Editable]" 7 | :component="[DatePicker]" 8 | /> 9 | <Field 10 | name="input" 11 | title="输入框" 12 | :decorator="[Editable]" 13 | :component="[Input]" 14 | /> 15 | <VoidField 16 | name="void" 17 | title="虚拟节点容器" 18 | :component="[Editable.Popover]" 19 | :reactions=" 20 | (field) => { 21 | field.title = field.query('.void.date2').get('value') || field.title 22 | } 23 | " 24 | > 25 | <Field 26 | name="date2" 27 | title="日期" 28 | :decorator="[FormItem]" 29 | :component="[DatePicker]" 30 | /> 31 | <Field 32 | name="input2" 33 | title="输入框" 34 | :decorator="[FormItem]" 35 | :component="[Input]" 36 | /> 37 | </VoidField> 38 | <ObjectField 39 | name="iobject" 40 | title="对象节点容器" 41 | :component="[Editable.Popover]" 42 | :reactions=" 43 | (field) => { 44 | field.title = (field.value && field.value.date) || field.title 45 | } 46 | " 47 | > 48 | <Field 49 | name="date" 50 | title="日期" 51 | :decorator="[FormItem]" 52 | :component="[DatePicker]" 53 | /> 54 | <Field 55 | name="input" 56 | title="输入框" 57 | :decorator="[FormItem]" 58 | :component="[Input]" 59 | /> 60 | </ObjectField> 61 | 62 | <FormButtonGroup> 63 | <Submit @submit="log">提交</Submit> 64 | </FormButtonGroup> 65 | </FormProvider> 66 | </template> 67 | 68 | <script> 69 | import { createForm } from '@formily/core' 70 | import { FormProvider, Field, VoidField, ObjectField } from '@formily/vue' 71 | import { 72 | FormButtonGroup, 73 | FormItem, 74 | Submit, 75 | Input, 76 | DatePicker, 77 | Editable, 78 | } from '@formily/element' 79 | 80 | export default { 81 | components: { 82 | FormButtonGroup, 83 | FormProvider, 84 | Submit, 85 | Field, 86 | VoidField, 87 | ObjectField, 88 | }, 89 | 90 | data() { 91 | const form = createForm() 92 | 93 | return { 94 | FormItem, 95 | Input, 96 | DatePicker, 97 | Editable, 98 | form, 99 | } 100 | }, 101 | methods: { 102 | log(values) { 103 | console.log(values) 104 | }, 105 | }, 106 | } 107 | </script> 108 | 109 | <style lang="scss" scoped></style> 110 | ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/form-item/inset.vue: -------------------------------------------------------------------------------- ```vue 1 | <template> 2 | <Form :form="form"> 3 | <SchemaField> 4 | <SchemaStringField 5 | name="input" 6 | title="Input" 7 | x-decorator="FormItem" 8 | x-component="Input" 9 | required 10 | :x-decorator-props="{ 11 | inset: true, 12 | }" 13 | /> 14 | <SchemaStringField 15 | name="Select" 16 | title="Select" 17 | x-decorator="FormItem" 18 | x-component="Select" 19 | required 20 | :x-decorator-props="{ 21 | inset: true, 22 | }" 23 | /> 24 | <SchemaStringField 25 | name="Cascader" 26 | title="Cascader" 27 | x-decorator="FormItem" 28 | x-component="Cascader" 29 | required 30 | :x-decorator-props="{ 31 | inset: true, 32 | }" 33 | /> 34 | <SchemaStringField 35 | name="DatePicker" 36 | title="DatePicker" 37 | x-decorator="FormItem" 38 | x-component="DatePicker" 39 | required 40 | :x-decorator-props="{ 41 | inset: true, 42 | }" 43 | /> 44 | <SchemaStringField 45 | name="InputNumber" 46 | title="InputNumber" 47 | x-decorator="FormItem" 48 | x-component="InputNumber" 49 | required 50 | :x-decorator-props="{ 51 | inset: true, 52 | }" 53 | /> 54 | <SchemaBooleanField 55 | name="Switch" 56 | title="Switch" 57 | x-decorator="FormItem" 58 | x-component="Switch" 59 | required 60 | :x-decorator-props="{ 61 | inset: true, 62 | }" 63 | /> 64 | </SchemaField> 65 | </Form> 66 | </template> 67 | 68 | <script> 69 | import { createForm } from '@formily/core' 70 | import { createSchemaField } from '@formily/vue' 71 | import { 72 | Form, 73 | FormItem, 74 | Input, 75 | Select, 76 | Cascader, 77 | DatePicker, 78 | Switch, 79 | InputNumber, 80 | } from '@formily/element' 81 | 82 | const form = createForm() 83 | const fields = createSchemaField({ 84 | components: { 85 | FormItem, 86 | Input, 87 | Select, 88 | Cascader, 89 | DatePicker, 90 | Switch, 91 | InputNumber, 92 | }, 93 | }) 94 | 95 | export default { 96 | components: { Form, ...fields }, 97 | data() { 98 | return { 99 | form, 100 | } 101 | }, 102 | methods: { 103 | onSubmit(value) { 104 | console.log(value) 105 | }, 106 | }, 107 | } 108 | </script> 109 | ``` -------------------------------------------------------------------------------- /packages/antd/docs/components/Switch.zh-CN.md: -------------------------------------------------------------------------------- ```markdown 1 | # Switch 2 | 3 | > 开关组件 4 | 5 | ## Markup Schema 案例 6 | 7 | ```tsx 8 | import React from 'react' 9 | import { Switch, FormItem, FormButtonGroup, Submit } from '@formily/antd' 10 | import { createForm } from '@formily/core' 11 | import { FormProvider, createSchemaField } from '@formily/react' 12 | 13 | const SchemaField = createSchemaField({ 14 | components: { 15 | Switch, 16 | FormItem, 17 | }, 18 | }) 19 | 20 | const form = createForm() 21 | 22 | export default () => ( 23 | <FormProvider form={form}> 24 | <SchemaField> 25 | <SchemaField.Boolean 26 | name="switch" 27 | title="开关" 28 | x-decorator="FormItem" 29 | x-component="Switch" 30 | /> 31 | </SchemaField> 32 | <FormButtonGroup> 33 | <Submit onSubmit={console.log}>提交</Submit> 34 | </FormButtonGroup> 35 | </FormProvider> 36 | ) 37 | ``` 38 | 39 | ## JSON Schema 案例 40 | 41 | ```tsx 42 | import React from 'react' 43 | import { Switch, FormItem, FormButtonGroup, Submit } from '@formily/antd' 44 | import { createForm } from '@formily/core' 45 | import { FormProvider, createSchemaField } from '@formily/react' 46 | 47 | const SchemaField = createSchemaField({ 48 | components: { 49 | Switch, 50 | FormItem, 51 | }, 52 | }) 53 | 54 | const form = createForm() 55 | 56 | const schema = { 57 | type: 'object', 58 | properties: { 59 | switch: { 60 | type: 'boolean', 61 | title: '开关', 62 | 'x-decorator': 'FormItem', 63 | 'x-component': 'Switch', 64 | }, 65 | }, 66 | } 67 | 68 | export default () => ( 69 | <FormProvider form={form}> 70 | <SchemaField schema={schema} /> 71 | <FormButtonGroup> 72 | <Submit onSubmit={console.log}>提交</Submit> 73 | </FormButtonGroup> 74 | </FormProvider> 75 | ) 76 | ``` 77 | 78 | ## 纯 JSX 案例 79 | 80 | ```tsx 81 | import React from 'react' 82 | import { Switch, FormItem, FormButtonGroup, Submit } from '@formily/antd' 83 | import { createForm } from '@formily/core' 84 | import { FormProvider, Field } from '@formily/react' 85 | 86 | const form = createForm() 87 | 88 | export default () => ( 89 | <FormProvider form={form}> 90 | <Field 91 | name="switch" 92 | title="开关" 93 | decorator={[FormItem]} 94 | component={[Switch]} 95 | /> 96 | <FormButtonGroup> 97 | <Submit onSubmit={console.log}>提交</Submit> 98 | </FormButtonGroup> 99 | </FormProvider> 100 | ) 101 | ``` 102 | 103 | ## API 104 | 105 | 参考 https://ant.design/components/switch-cn/ 106 | ``` -------------------------------------------------------------------------------- /packages/core/src/shared/effective.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { isFn, isValid } from '@formily/shared' 2 | import { LifeCycle, Form } from '../models' 3 | import { AnyFunction } from '../types' 4 | import { isForm } from './checkers' 5 | import { GlobalState } from './constants' 6 | 7 | export const createEffectHook = < 8 | F extends (payload: any, ...ctxs: any[]) => AnyFunction 9 | >( 10 | type: string, 11 | callback?: F 12 | ) => { 13 | return (...args: Parameters<ReturnType<F>>) => { 14 | if (GlobalState.effectStart) { 15 | GlobalState.lifecycles.push( 16 | new LifeCycle(type, (payload, ctx) => { 17 | if (isFn(callback)) { 18 | callback(payload, ctx, ...GlobalState.context)(...args) 19 | } 20 | }) 21 | ) 22 | } else { 23 | throw new Error( 24 | 'Effect hooks cannot be used in asynchronous function body' 25 | ) 26 | } 27 | } 28 | } 29 | 30 | export const createEffectContext = <T = any>(defaultValue?: T) => { 31 | let index: number 32 | return { 33 | provide(value?: T) { 34 | if (GlobalState.effectStart) { 35 | index = GlobalState.context.length 36 | GlobalState.context[index] = isValid(value) ? value : defaultValue 37 | } else { 38 | throw new Error( 39 | 'Provide method cannot be used in asynchronous function body' 40 | ) 41 | } 42 | }, 43 | consume(): T { 44 | if (!GlobalState.effectStart) { 45 | throw new Error( 46 | 'Consume method cannot be used in asynchronous function body' 47 | ) 48 | } 49 | return GlobalState.context[index] 50 | }, 51 | } 52 | } 53 | 54 | const FormEffectContext = createEffectContext<Form>() 55 | 56 | export const useEffectForm = FormEffectContext.consume 57 | 58 | export const runEffects = <Context>( 59 | context?: Context, 60 | ...args: ((context: Context) => void)[] 61 | ): LifeCycle[] => { 62 | GlobalState.lifecycles = [] 63 | GlobalState.context = [] 64 | GlobalState.effectStart = true 65 | GlobalState.effectEnd = false 66 | if (isForm(context)) { 67 | FormEffectContext.provide(context) 68 | } 69 | args.forEach((effects) => { 70 | if (isFn(effects)) { 71 | effects(context) 72 | } 73 | }) 74 | GlobalState.context = [] 75 | GlobalState.effectStart = false 76 | GlobalState.effectEnd = true 77 | return GlobalState.lifecycles 78 | } 79 | ``` -------------------------------------------------------------------------------- /packages/next/docs/components/Switch.zh-CN.md: -------------------------------------------------------------------------------- ```markdown 1 | # Switch 2 | 3 | > 开关组件 4 | 5 | ## Markup Schema 案例 6 | 7 | ```tsx 8 | import React from 'react' 9 | import { Switch, FormItem, FormButtonGroup, Submit } from '@formily/next' 10 | import { createForm } from '@formily/core' 11 | import { FormProvider, createSchemaField } from '@formily/react' 12 | 13 | const SchemaField = createSchemaField({ 14 | components: { 15 | Switch, 16 | FormItem, 17 | }, 18 | }) 19 | 20 | const form = createForm() 21 | 22 | export default () => ( 23 | <FormProvider form={form}> 24 | <SchemaField> 25 | <SchemaField.Boolean 26 | name="switch" 27 | title="开关" 28 | x-decorator="FormItem" 29 | x-component="Switch" 30 | /> 31 | </SchemaField> 32 | <FormButtonGroup> 33 | <Submit onSubmit={console.log}>提交</Submit> 34 | </FormButtonGroup> 35 | </FormProvider> 36 | ) 37 | ``` 38 | 39 | ## JSON Schema 案例 40 | 41 | ```tsx 42 | import React from 'react' 43 | import { Switch, FormItem, FormButtonGroup, Submit } from '@formily/next' 44 | import { createForm } from '@formily/core' 45 | import { FormProvider, createSchemaField } from '@formily/react' 46 | 47 | const SchemaField = createSchemaField({ 48 | components: { 49 | Switch, 50 | FormItem, 51 | }, 52 | }) 53 | 54 | const form = createForm() 55 | 56 | const schema = { 57 | type: 'object', 58 | properties: { 59 | switch: { 60 | type: 'boolean', 61 | title: '开关', 62 | 'x-decorator': 'FormItem', 63 | 'x-component': 'Switch', 64 | }, 65 | }, 66 | } 67 | 68 | export default () => ( 69 | <FormProvider form={form}> 70 | <SchemaField schema={schema} /> 71 | <FormButtonGroup> 72 | <Submit onSubmit={console.log}>提交</Submit> 73 | </FormButtonGroup> 74 | </FormProvider> 75 | ) 76 | ``` 77 | 78 | ## 纯 JSX 案例 79 | 80 | ```tsx 81 | import React from 'react' 82 | import { Switch, FormItem, FormButtonGroup, Submit } from '@formily/next' 83 | import { createForm } from '@formily/core' 84 | import { FormProvider, Field } from '@formily/react' 85 | 86 | const form = createForm() 87 | 88 | export default () => ( 89 | <FormProvider form={form}> 90 | <Field 91 | name="switch" 92 | title="开关" 93 | decorator={[FormItem]} 94 | component={[Switch]} 95 | /> 96 | <FormButtonGroup> 97 | <Submit onSubmit={console.log}>提交</Submit> 98 | </FormButtonGroup> 99 | </FormProvider> 100 | ) 101 | ``` 102 | 103 | ## API 104 | 105 | 参考 https://fusion.design/pc/component/basic/switch 106 | ``` -------------------------------------------------------------------------------- /packages/core/docs/guide/index.md: -------------------------------------------------------------------------------- ```markdown 1 | # Introduction 2 | 3 | ## Pure Core,No UI 4 | 5 | Because @formily/core exists as an independent package, its core meaning is to separate the domain model from the UI framework, and at the same time, it can bring the following two intuitive benefits to developers: 6 | 7 | 1. It is convenient for formily developers to release from the coupling relationship between UI and logic, and improve code maintainability; 8 | 9 | 2. Allow formily to have cross-terminal and cross-framework capabilities. Whether you are a React user, Vue user or Angular user, you can enjoy the efficiency improvement brought by formily's domain model. 10 | 11 | ## Ultra high performance 12 | 13 | With the help of @formily/reactive, @formily/core naturally gains the ability to track dependencies, update efficiently, and render on-demand. Whether it is under React or Vue/Angular, whether it is frequent field input or field linkage, it can give Users bring O(1) performance experience, developers do not need to care about performance optimization, only need to focus on business logic implementation. 14 | 15 | ## Domain Model 16 | 17 | If we break down the form question, we can actually break it down: 18 | 19 | - Data management issues 20 | - Field management issues 21 | - Calibration management issues 22 | - Linkage management issues 23 | 24 | The problems in these directions can actually be solved as domain-level problems. Each domain problem is actually a very complex problem. In Formily, all of them are solved by breakthroughs, so you only need to focus on business logic. That's it. 25 | 26 | ## Smart tips 27 | 28 | Because formily is a complete Typescript project, users can develop on VSCode or WebStorm to get the maximum intelligent prompt experience 29 | 30 |  31 | 32 | ## Status observable 33 | 34 | Install [FormilyDevtools](https://chrome.google.com/webstore/detail/formily-devtools/kkocalmbfnplecdmbadaapgapdioecfm?hl=zh-CN) to observe the model status changes in real time and troubleshoot problems 35 | 36 |  37 | ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/form-item/bordered-none.vue: -------------------------------------------------------------------------------- ```vue 1 | <template> 2 | <Form :form="form"> 3 | <SchemaField> 4 | <SchemaStringField 5 | name="input" 6 | title="Input" 7 | x-decorator="FormItem" 8 | x-component="Input" 9 | required 10 | :x-decorator-props="{ 11 | bordered: false, 12 | }" 13 | /> 14 | <SchemaStringField 15 | name="Select" 16 | title="Select" 17 | x-decorator="FormItem" 18 | x-component="Select" 19 | required 20 | :x-decorator-props="{ 21 | bordered: false, 22 | }" 23 | /> 24 | <SchemaStringField 25 | name="Cascader" 26 | title="Cascader" 27 | x-decorator="FormItem" 28 | x-component="Cascader" 29 | required 30 | :x-decorator-props="{ 31 | bordered: false, 32 | }" 33 | /> 34 | <SchemaStringField 35 | name="DatePicker" 36 | title="DatePicker" 37 | x-decorator="FormItem" 38 | x-component="DatePicker" 39 | required 40 | :x-decorator-props="{ 41 | bordered: false, 42 | }" 43 | /> 44 | <SchemaStringField 45 | name="InputNumber" 46 | title="InputNumber" 47 | x-decorator="FormItem" 48 | x-component="InputNumber" 49 | required 50 | :x-decorator-props="{ 51 | bordered: false, 52 | }" 53 | /> 54 | <SchemaBooleanField 55 | name="Switch" 56 | title="Switch" 57 | x-decorator="FormItem" 58 | x-component="Switch" 59 | required 60 | :x-decorator-props="{ 61 | bordered: false, 62 | }" 63 | /> 64 | </SchemaField> 65 | </Form> 66 | </template> 67 | 68 | <script> 69 | import { createForm } from '@formily/core' 70 | import { createSchemaField } from '@formily/vue' 71 | import { 72 | Form, 73 | FormItem, 74 | Input, 75 | Select, 76 | Cascader, 77 | DatePicker, 78 | Switch, 79 | InputNumber, 80 | } from '@formily/element' 81 | 82 | const form = createForm() 83 | const fields = createSchemaField({ 84 | components: { 85 | FormItem, 86 | Input, 87 | Select, 88 | Cascader, 89 | DatePicker, 90 | Switch, 91 | InputNumber, 92 | }, 93 | }) 94 | 95 | export default { 96 | components: { Form, ...fields }, 97 | data() { 98 | return { 99 | form, 100 | } 101 | }, 102 | methods: { 103 | onSubmit(value) { 104 | console.log(value) 105 | }, 106 | }, 107 | } 108 | </script> 109 | ``` -------------------------------------------------------------------------------- /packages/vue/docs/demos/api/components/recursion-field-with-component.vue: -------------------------------------------------------------------------------- ```vue 1 | <template> 2 | <FormProvider :form="form"> 3 | <SchemaField> 4 | <SchemaArrayField name="custom" x-component="ArrayItems"> 5 | <SchemaObjectField> 6 | <SchemaStringField name="input" x-component="Input" /> 7 | </SchemaObjectField> 8 | </SchemaArrayField> 9 | </SchemaField> 10 | </FormProvider> 11 | </template> 12 | 13 | <script> 14 | import { defineComponent, h } from '@vue/composition-api' 15 | // or "import { defineComponent, h } from 'vue'" if using vue3 16 | import { Input, Button, Space } from 'ant-design-vue' 17 | import { createForm } from '@formily/core' 18 | import { 19 | FormProvider, 20 | createSchemaField, 21 | RecursionField, 22 | useField, 23 | useFieldSchema, 24 | } from '@formily/vue' 25 | import { observer } from '@formily/reactive-vue' 26 | import 'ant-design-vue/dist/antd.css' 27 | 28 | const ArrayItems = observer( 29 | defineComponent({ 30 | setup() { 31 | const fieldRef = useField() 32 | const schemaRef = useFieldSchema() 33 | 34 | return () => { 35 | const field = fieldRef.value 36 | const schema = schemaRef.value 37 | const items = field.value?.map((item, index) => { 38 | return h('div', { key: item.id, style: { marginBottom: '10px' } }, [ 39 | h(Space, [ 40 | // params of render function is different in vue3 41 | h(RecursionField, { 42 | props: { schema: schema.items, name: index }, 43 | }), 44 | h(Button, { on: { click: () => field.remove(index) } }, [ 45 | 'Remove', 46 | ]), 47 | ]), 48 | ]) 49 | }) 50 | const button = h( 51 | Button, 52 | { on: { click: () => field.push({ id: Date.now() }) } }, 53 | ['Add'] 54 | ) 55 | return h('div', [items, button]) 56 | } 57 | }, 58 | }) 59 | ) 60 | 61 | const { SchemaField, SchemaStringField, SchemaArrayField, SchemaObjectField } = 62 | createSchemaField({ 63 | components: { 64 | ArrayItems, 65 | Input, 66 | }, 67 | }) 68 | 69 | export default { 70 | components: { 71 | FormProvider, 72 | SchemaField, 73 | SchemaStringField, 74 | SchemaArrayField, 75 | SchemaObjectField, 76 | }, 77 | data() { 78 | return { 79 | form: createForm(), 80 | } 81 | }, 82 | } 83 | </script> 84 | ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/select/markup-schema-async-search.vue: -------------------------------------------------------------------------------- ```vue 1 | <template> 2 | <FormProvider :form="form"> 3 | <SchemaField> 4 | <SchemaStringField 5 | name="select" 6 | title="异步搜索选择框" 7 | x-decorator="FormItem" 8 | x-component="Select" 9 | :x-component-props="{ 10 | filterable: true, 11 | remote: true, 12 | style: { 13 | width: '240px', 14 | }, 15 | }" 16 | /> 17 | </SchemaField> 18 | <Submit @submit="log">提交</Submit> 19 | </FormProvider> 20 | </template> 21 | 22 | <script> 23 | import { createForm, onFieldInit, onFieldReact } from '@formily/core' 24 | import { action, observable } from '@formily/reactive' 25 | import { createSchemaField, FormProvider } from '@formily/vue' 26 | import { FormItem, Select, Submit } from '@formily/element' 27 | 28 | let timeout 29 | let currentValue 30 | 31 | function fetchData(value, callback) { 32 | if (timeout) { 33 | clearTimeout(timeout) 34 | timeout = null 35 | } 36 | currentValue = value 37 | 38 | function fake() { 39 | callback([ 40 | { 41 | label: 'AAA', 42 | value: 'aaa', 43 | }, 44 | { 45 | label: 'BBB', 46 | value: 'ccc', 47 | }, 48 | ]) 49 | } 50 | 51 | timeout = setTimeout(fake, 300) 52 | } 53 | 54 | const useAsyncDataSource = (pattern, service) => { 55 | const keyword = observable.ref('') 56 | 57 | onFieldInit(pattern, (field) => { 58 | field.setComponentProps({ 59 | remoteMethod: (value) => { 60 | keyword.value = value 61 | }, 62 | }) 63 | }) 64 | 65 | onFieldReact(pattern, (field) => { 66 | field.loading = true 67 | service({ field, keyword: keyword.value }).then( 68 | action.bound((data) => { 69 | field.dataSource = data 70 | field.loading = false 71 | }) 72 | ) 73 | }) 74 | } 75 | 76 | const form = createForm({ 77 | effects: () => { 78 | useAsyncDataSource('select', async ({ keyword }) => { 79 | if (!keyword) { 80 | return [] 81 | } 82 | return new Promise((resolve) => { 83 | fetchData(keyword, resolve) 84 | }) 85 | }) 86 | }, 87 | }) 88 | const fields = createSchemaField({ 89 | components: { 90 | FormItem, 91 | Select, 92 | }, 93 | }) 94 | 95 | export default { 96 | components: { FormProvider, ...fields, Submit }, 97 | data() { 98 | return { 99 | form, 100 | } 101 | }, 102 | methods: { 103 | log(value) { 104 | console.log(value) 105 | }, 106 | }, 107 | } 108 | </script> 109 | ``` -------------------------------------------------------------------------------- /packages/next/docs/components/TimePicker.zh-CN.md: -------------------------------------------------------------------------------- ```markdown 1 | # TimePicker 2 | 3 | > 时间选择器 4 | 5 | ## Markup Schema 案例 6 | 7 | ```tsx 8 | import React from 'react' 9 | import { TimePicker, FormItem, FormButtonGroup, Submit } from '@formily/next' 10 | import { createForm } from '@formily/core' 11 | import { FormProvider, createSchemaField } from '@formily/react' 12 | 13 | const SchemaField = createSchemaField({ 14 | components: { 15 | TimePicker, 16 | FormItem, 17 | }, 18 | }) 19 | 20 | const form = createForm() 21 | 22 | export default () => ( 23 | <FormProvider form={form}> 24 | <SchemaField> 25 | <SchemaField.String 26 | name="time" 27 | title="时间" 28 | x-decorator="FormItem" 29 | x-component="TimePicker" 30 | /> 31 | </SchemaField> 32 | <FormButtonGroup> 33 | <Submit onSubmit={console.log}>提交</Submit> 34 | </FormButtonGroup> 35 | </FormProvider> 36 | ) 37 | ``` 38 | 39 | ## JSON Schema 案例 40 | 41 | ```tsx 42 | import React from 'react' 43 | import { TimePicker, FormItem, FormButtonGroup, Submit } from '@formily/next' 44 | import { createForm } from '@formily/core' 45 | import { FormProvider, createSchemaField } from '@formily/react' 46 | 47 | const SchemaField = createSchemaField({ 48 | components: { 49 | TimePicker, 50 | FormItem, 51 | }, 52 | }) 53 | 54 | const form = createForm() 55 | 56 | const schema = { 57 | type: 'object', 58 | properties: { 59 | time: { 60 | title: '时间', 61 | 'x-decorator': 'FormItem', 62 | 'x-component': 'TimePicker', 63 | type: 'string', 64 | }, 65 | }, 66 | } 67 | 68 | export default () => ( 69 | <FormProvider form={form}> 70 | <SchemaField schema={schema} /> 71 | <FormButtonGroup> 72 | <Submit onSubmit={console.log}>提交</Submit> 73 | </FormButtonGroup> 74 | </FormProvider> 75 | ) 76 | ``` 77 | 78 | ## 纯 JSX 案例 79 | 80 | ```tsx 81 | import React from 'react' 82 | import { TimePicker, FormItem, FormButtonGroup, Submit } from '@formily/next' 83 | import { createForm } from '@formily/core' 84 | import { FormProvider, Field } from '@formily/react' 85 | 86 | const form = createForm() 87 | 88 | export default () => ( 89 | <FormProvider form={form}> 90 | <Field 91 | name="time" 92 | title="时间" 93 | decorator={[FormItem]} 94 | component={[TimePicker]} 95 | /> 96 | <FormButtonGroup> 97 | <Submit onSubmit={console.log}>提交</Submit> 98 | </FormButtonGroup> 99 | </FormProvider> 100 | ) 101 | ``` 102 | 103 | ## API 104 | 105 | 参考 https://fusion.design/pc/component/basic/time-picker 106 | ``` -------------------------------------------------------------------------------- /packages/element/src/select/index.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { connect, h, mapProps, mapReadPretty } from '@formily/vue' 2 | import { defineComponent } from 'vue-demi' 3 | import { PreviewText } from '../preview-text' 4 | 5 | import type { 6 | Option as ElOptionProps, 7 | Select as ElSelectProps, 8 | } from 'element-ui' 9 | import { Option as ElOption, Select as ElSelect } from 'element-ui' 10 | import { resolveComponent } from '../__builtins__' 11 | 12 | export type SelectProps = ElSelectProps & { 13 | options?: Array<ElOptionProps> 14 | } 15 | 16 | const SelectOption = defineComponent<SelectProps>({ 17 | name: 'FSelect', 18 | props: ['options'], 19 | setup(customProps, { attrs, slots, listeners }) { 20 | return () => { 21 | const options = customProps.options || [] 22 | const children = 23 | options.length !== 0 24 | ? { 25 | default: () => 26 | options.map((option) => { 27 | if (typeof option === 'string') { 28 | return h( 29 | ElOption, 30 | { props: { value: option, label: option } }, 31 | { 32 | default: () => [ 33 | resolveComponent(slots?.option, { option }), 34 | ], 35 | } 36 | ) 37 | } else { 38 | return h( 39 | ElOption, 40 | { 41 | props: { 42 | ...option, 43 | }, 44 | }, 45 | { 46 | default: () => [ 47 | resolveComponent(slots?.option, { 48 | option, 49 | }), 50 | ], 51 | } 52 | ) 53 | } 54 | }), 55 | } 56 | : slots 57 | return h( 58 | ElSelect, 59 | { 60 | attrs: { 61 | ...attrs, 62 | }, 63 | on: listeners, 64 | }, 65 | children 66 | ) 67 | } 68 | }, 69 | }) 70 | 71 | export const Select = connect( 72 | SelectOption, 73 | mapProps({ dataSource: 'options', loading: true }), 74 | mapReadPretty(PreviewText.Select) 75 | ) 76 | 77 | export default Select 78 | ``` -------------------------------------------------------------------------------- /packages/next/package.json: -------------------------------------------------------------------------------- ```json 1 | { 2 | "name": "@formily/next", 3 | "version": "2.3.7", 4 | "license": "MIT", 5 | "main": "lib", 6 | "umd:main": "dist/formily.next.umd.production.js", 7 | "unpkg": "dist/formily.next.umd.production.js", 8 | "jsdelivr": "dist/formily.next.umd.production.js", 9 | "jsnext:main": "esm", 10 | "module": "esm", 11 | "sideEffects": [ 12 | "dist/*", 13 | "esm/*.js", 14 | "lib/*.js", 15 | "src/*.ts", 16 | "*.scss", 17 | "**/*/style.js" 18 | ], 19 | "repository": { 20 | "type": "git", 21 | "url": "git+https://github.com/alibaba/formily.git" 22 | }, 23 | "types": "esm/index.d.ts", 24 | "bugs": { 25 | "url": "https://github.com/alibaba/formily/issues" 26 | }, 27 | "homepage": "https://github.com/alibaba/formily#readme", 28 | "engines": { 29 | "npm": ">=3.0.0" 30 | }, 31 | "scripts": { 32 | "start": "dumi dev", 33 | "build": "rimraf -rf lib esm dist && npm run create:style && npm run build:cjs && npm run build:esm && npm run build:umd && npm run build:style", 34 | "create:style": "ts-node create-style", 35 | "build:style": "ts-node build-style", 36 | "build:cjs": "tsc --project tsconfig.build.json", 37 | "build:esm": "tsc --project tsconfig.build.json --module es2015 --outDir esm", 38 | "build:umd": "rollup --config", 39 | "build:docs": "dumi build" 40 | }, 41 | "peerDependencies": { 42 | "@alifd/next": "^1.19.0", 43 | "@types/react": ">=16.8.0", 44 | "@types/react-dom": ">=16.8.0", 45 | "react": ">=16.8.0", 46 | "react-dom": ">=16.8.0", 47 | "react-is": ">=16.8.0" 48 | }, 49 | "peerDependenciesMeta": { 50 | "@types/react": { 51 | "optional": true 52 | }, 53 | "@types/react-dom": { 54 | "optional": true 55 | } 56 | }, 57 | "devDependencies": { 58 | "@umijs/plugin-sass": "^1.1.1", 59 | "dumi": "^1.1.0-rc.8" 60 | }, 61 | "dependencies": { 62 | "@formily/core": "2.3.7", 63 | "@formily/grid": "2.3.7", 64 | "@formily/json-schema": "2.3.7", 65 | "@formily/react": "2.3.7", 66 | "@formily/reactive": "2.3.7", 67 | "@formily/reactive-react": "2.3.7", 68 | "@formily/shared": "2.3.7", 69 | "classnames": "^2.2.6", 70 | "react-sortable-hoc": "^1.11.0", 71 | "react-sticky-box": "^0.9.3" 72 | }, 73 | "publishConfig": { 74 | "access": "public" 75 | }, 76 | "gitHead": "ac79c196ae9324889aca5e0501146f9b37b04283" 77 | } 78 | ``` -------------------------------------------------------------------------------- /packages/antd/docs/components/Switch.md: -------------------------------------------------------------------------------- ```markdown 1 | # Switch 2 | 3 | > Switch Components 4 | 5 | ## Markup Schema example 6 | 7 | ```tsx 8 | import React from 'react' 9 | import { Switch, FormItem, FormButtonGroup, Submit } from '@formily/antd' 10 | import { createForm } from '@formily/core' 11 | import { FormProvider, createSchemaField } from '@formily/react' 12 | 13 | const SchemaField = createSchemaField({ 14 | components: { 15 | Switch, 16 | FormItem, 17 | }, 18 | }) 19 | 20 | const form = createForm() 21 | 22 | export default () => ( 23 | <FormProvider form={form}> 24 | <SchemaField> 25 | <SchemaField.Boolean 26 | name="switch" 27 | title="Switch" 28 | x-decorator="FormItem" 29 | x-component="Switch" 30 | /> 31 | </SchemaField> 32 | <FormButtonGroup> 33 | <Submit onSubmit={console.log}>Submit</Submit> 34 | </FormButtonGroup> 35 | </FormProvider> 36 | ) 37 | ``` 38 | 39 | ## JSON Schema case 40 | 41 | ```tsx 42 | import React from 'react' 43 | import { Switch, FormItem, FormButtonGroup, Submit } from '@formily/antd' 44 | import { createForm } from '@formily/core' 45 | import { FormProvider, createSchemaField } from '@formily/react' 46 | 47 | const SchemaField = createSchemaField({ 48 | components: { 49 | Switch, 50 | FormItem, 51 | }, 52 | }) 53 | 54 | const form = createForm() 55 | 56 | const schema = { 57 | type: 'object', 58 | properties: { 59 | switch: { 60 | type: 'boolean', 61 | title: 'Switch', 62 | 'x-decorator': 'FormItem', 63 | 'x-component': 'Switch', 64 | }, 65 | }, 66 | } 67 | 68 | export default () => ( 69 | <FormProvider form={form}> 70 | <SchemaField schema={schema} /> 71 | <FormButtonGroup> 72 | <Submit onSubmit={console.log}>Submit</Submit> 73 | </FormButtonGroup> 74 | </FormProvider> 75 | ) 76 | ``` 77 | 78 | ## Pure JSX case 79 | 80 | ```tsx 81 | import React from 'react' 82 | import { Switch, FormItem, FormButtonGroup, Submit } from '@formily/antd' 83 | import { createForm } from '@formily/core' 84 | import { FormProvider, Field } from '@formily/react' 85 | 86 | const form = createForm() 87 | 88 | export default () => ( 89 | <FormProvider form={form}> 90 | <Field 91 | name="switch" 92 | title="Switch" 93 | decorator={[FormItem]} 94 | component={[Switch]} 95 | /> 96 | <FormButtonGroup> 97 | <Submit onSubmit={console.log}>Submit</Submit> 98 | </FormButtonGroup> 99 | </FormProvider> 100 | ) 101 | ``` 102 | 103 | ## API 104 | 105 | Reference https://ant.design/components/switch-cn/ 106 | ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/upload/markup-schema.vue: -------------------------------------------------------------------------------- ```vue 1 | <template> 2 | <Form :form="form" :label-col="4" :wrapper-col="10"> 3 | <SchemaField> 4 | <SchemaArrayField 5 | name="upload" 6 | title="上传" 7 | x-decorator="FormItem" 8 | x-component="Upload" 9 | :x-component-props="{ 10 | action: 'https://formily-vue.free.beeceptor.com/file', 11 | textContent: '上传', 12 | }" 13 | required 14 | /> 15 | <SchemaArrayField 16 | name="upload2" 17 | title="卡片上传" 18 | x-decorator="FormItem" 19 | x-component="Upload" 20 | :x-component-props="{ 21 | listType: 'picture-card', 22 | action: 'https://formily-vue.free.beeceptor.com/file', 23 | }" 24 | required 25 | /> 26 | <SchemaArrayField 27 | name="upload3" 28 | title="拖拽上传" 29 | x-decorator="FormItem" 30 | x-component="Upload" 31 | :x-component-props="{ 32 | action: 'https://formily-vue.free.beeceptor.com/file', 33 | textContent: '将文件拖到此处,或者点击上传', 34 | drag: true, 35 | }" 36 | required 37 | /> 38 | <SchemaArrayField 39 | name="custom" 40 | title="自定义按钮" 41 | x-decorator="FormItem" 42 | x-component="Upload" 43 | :x-component-props="{ 44 | action: 'https://formily-vue.free.beeceptor.com/file', 45 | }" 46 | required 47 | :x-content="UploadButton" 48 | /> 49 | </SchemaField> 50 | <FormButtonGroup align-form-item> 51 | <Submit @submit="onSubmit">提交</Submit> 52 | </FormButtonGroup> 53 | </Form> 54 | </template> 55 | 56 | <script> 57 | import { createForm } from '@formily/core' 58 | import { createSchemaField } from '@formily/vue' 59 | import { 60 | Form, 61 | FormItem, 62 | Upload, 63 | Submit, 64 | FormButtonGroup, 65 | } from '@formily/element' 66 | import { Button } from 'element-ui' 67 | 68 | const UploadButton = { 69 | functional: true, 70 | render(h) { 71 | return h(Button, {}, '上传图片') 72 | }, 73 | } 74 | 75 | const form = createForm() 76 | const fields = createSchemaField({ 77 | components: { 78 | FormItem, 79 | Upload, 80 | }, 81 | }) 82 | 83 | export default { 84 | components: { Form, ...fields, Submit, FormButtonGroup }, 85 | data() { 86 | return { 87 | UploadButton, 88 | form, 89 | } 90 | }, 91 | methods: { 92 | onSubmit(value) { 93 | console.log(value) 94 | }, 95 | }, 96 | } 97 | </script> 98 | ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/cascader/json-schema.vue: -------------------------------------------------------------------------------- ```vue 1 | <template> 2 | <Form :form="form"> 3 | <SchemaField 4 | :schema="schema" 5 | :scope="{ useAsyncDataSource, transformAddress }" 6 | /> 7 | <Submit @submit="onSubmit">提交</Submit> 8 | </Form> 9 | </template> 10 | 11 | <script> 12 | import { createForm } from '@formily/core' 13 | import { createSchemaField } from '@formily/vue' 14 | import { Form, FormItem, Cascader, Submit } from '@formily/element' 15 | import { action } from '@formily/reactive' 16 | import axios from 'axios' 17 | 18 | const transformAddress = (data = {}) => { 19 | return Object.entries(data).reduce((buf, [key, value]) => { 20 | if (typeof value === 'string') 21 | return buf.concat({ 22 | label: value, 23 | value: key, 24 | }) 25 | const { name, code, cities, districts } = value 26 | const _cities = transformAddress(cities) 27 | const _districts = transformAddress(districts) 28 | return buf.concat({ 29 | label: name, 30 | value: code, 31 | children: _cities.length 32 | ? _cities 33 | : _districts.length 34 | ? _districts 35 | : undefined, 36 | }) 37 | }, []) 38 | } 39 | 40 | const useAsyncDataSource = (url, transform) => { 41 | return (field) => { 42 | field.loading = true 43 | axios 44 | .get(url) 45 | .then((res) => res.data) 46 | .then( 47 | action.bound((data) => { 48 | field.dataSource = transform(data) 49 | field.loading = false 50 | }) 51 | ) 52 | } 53 | } 54 | 55 | const schema = { 56 | type: 'object', 57 | properties: { 58 | cascader: { 59 | type: 'string', 60 | title: '地址选择', 61 | 'x-decorator': 'FormItem', 62 | 'x-component': 'Cascader', 63 | 'x-component-props': { 64 | style: { 65 | width: '240px', 66 | }, 67 | }, 68 | 'x-reactions': [ 69 | '{{useAsyncDataSource("//unpkg.com/china-location/dist/location.json",transformAddress)}}', 70 | ], 71 | }, 72 | }, 73 | } 74 | 75 | const form = createForm() 76 | const { SchemaField } = createSchemaField({ 77 | components: { 78 | FormItem, 79 | Cascader, 80 | }, 81 | }) 82 | 83 | export default { 84 | components: { Form, SchemaField, Submit }, 85 | data() { 86 | return { 87 | useAsyncDataSource, 88 | transformAddress, 89 | form, 90 | schema, 91 | } 92 | }, 93 | methods: { 94 | onSubmit(value) { 95 | console.log(value) 96 | }, 97 | }, 98 | } 99 | </script> 100 | ``` -------------------------------------------------------------------------------- /packages/next/docs/components/Switch.md: -------------------------------------------------------------------------------- ```markdown 1 | # Switch 2 | 3 | > Switch Components 4 | 5 | ## Markup Schema example 6 | 7 | ```tsx 8 | import React from 'react' 9 | import { Switch, FormItem, FormButtonGroup, Submit } from '@formily/next' 10 | import { createForm } from '@formily/core' 11 | import { FormProvider, createSchemaField } from '@formily/react' 12 | 13 | const SchemaField = createSchemaField({ 14 | components: { 15 | Switch, 16 | FormItem, 17 | }, 18 | }) 19 | 20 | const form = createForm() 21 | 22 | export default () => ( 23 | <FormProvider form={form}> 24 | <SchemaField> 25 | <SchemaField.Boolean 26 | name="switch" 27 | title="Switch" 28 | x-decorator="FormItem" 29 | x-component="Switch" 30 | /> 31 | </SchemaField> 32 | <FormButtonGroup> 33 | <Submit onSubmit={console.log}>Submit</Submit> 34 | </FormButtonGroup> 35 | </FormProvider> 36 | ) 37 | ``` 38 | 39 | ## JSON Schema case 40 | 41 | ```tsx 42 | import React from 'react' 43 | import { Switch, FormItem, FormButtonGroup, Submit } from '@formily/next' 44 | import { createForm } from '@formily/core' 45 | import { FormProvider, createSchemaField } from '@formily/react' 46 | 47 | const SchemaField = createSchemaField({ 48 | components: { 49 | Switch, 50 | FormItem, 51 | }, 52 | }) 53 | 54 | const form = createForm() 55 | 56 | const schema = { 57 | type: 'object', 58 | properties: { 59 | switch: { 60 | type: 'boolean', 61 | title: 'Switch', 62 | 'x-decorator': 'FormItem', 63 | 'x-component': 'Switch', 64 | }, 65 | }, 66 | } 67 | 68 | export default () => ( 69 | <FormProvider form={form}> 70 | <SchemaField schema={schema} /> 71 | <FormButtonGroup> 72 | <Submit onSubmit={console.log}>Submit</Submit> 73 | </FormButtonGroup> 74 | </FormProvider> 75 | ) 76 | ``` 77 | 78 | ## Pure JSX case 79 | 80 | ```tsx 81 | import React from 'react' 82 | import { Switch, FormItem, FormButtonGroup, Submit } from '@formily/next' 83 | import { createForm } from '@formily/core' 84 | import { FormProvider, Field } from '@formily/react' 85 | 86 | const form = createForm() 87 | 88 | export default () => ( 89 | <FormProvider form={form}> 90 | <Field 91 | name="switch" 92 | title="Switch" 93 | decorator={[FormItem]} 94 | component={[Switch]} 95 | /> 96 | <FormButtonGroup> 97 | <Submit onSubmit={console.log}>Submit</Submit> 98 | </FormButtonGroup> 99 | </FormProvider> 100 | ) 101 | ``` 102 | 103 | ## API 104 | 105 | Reference https://fusion.design/pc/component/basic/switch 106 | ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/upload/json-schema.vue: -------------------------------------------------------------------------------- ```vue 1 | <template> 2 | <Form :form="form" :label-col="4" :wrapper-col="10"> 3 | <SchemaField :schema="schema" /> 4 | <FormButtonGroup align-form-item> 5 | <Submit @submit="onSubmit">提交</Submit> 6 | </FormButtonGroup> 7 | </Form> 8 | </template> 9 | 10 | <script> 11 | import { createForm } from '@formily/core' 12 | import { createSchemaField } from '@formily/vue' 13 | import { 14 | Form, 15 | FormItem, 16 | Upload, 17 | Submit, 18 | FormButtonGroup, 19 | } from '@formily/element' 20 | import { Button } from 'element-ui' 21 | 22 | const UploadButton = { 23 | functional: true, 24 | render(h) { 25 | return h(Button, {}, '上传图片') 26 | }, 27 | } 28 | 29 | const schema = { 30 | type: 'object', 31 | properties: { 32 | base: { 33 | type: 'array', 34 | title: '上传', 35 | 'x-decorator': 'FormItem', 36 | 'x-component': 'Upload', 37 | 'x-component-props': { 38 | action: 'https://formily-vue.free.beeceptor.com/file', 39 | textContent: '上传', 40 | }, 41 | required: true, 42 | }, 43 | card: { 44 | type: 'array', 45 | title: '卡片上传', 46 | 'x-decorator': 'FormItem', 47 | 'x-component': 'Upload', 48 | 'x-component-props': { 49 | listType: 'picture-card', 50 | action: 'https://formily-vue.free.beeceptor.com/file', 51 | }, 52 | required: true, 53 | }, 54 | drag: { 55 | type: 'array', 56 | title: '拖拽上传', 57 | 'x-decorator': 'FormItem', 58 | 'x-component': 'Upload', 59 | 'x-component-props': { 60 | action: 'https://formily-vue.free.beeceptor.com/file', 61 | textContent: '将文件拖到此处,或者点击上传', 62 | drag: true, 63 | }, 64 | required: true, 65 | }, 66 | custom: { 67 | type: 'array', 68 | title: '自定义按钮', 69 | 'x-decorator': 'FormItem', 70 | 'x-component': 'Upload', 71 | 'x-component-props': { 72 | action: 'https://formily-vue.free.beeceptor.com/file', 73 | }, 74 | 'x-content': UploadButton, 75 | required: true, 76 | }, 77 | }, 78 | } 79 | 80 | const form = createForm() 81 | const { SchemaField } = createSchemaField({ 82 | components: { 83 | FormItem, 84 | Upload, 85 | }, 86 | }) 87 | 88 | export default { 89 | components: { Form, SchemaField, Submit, FormButtonGroup }, 90 | data() { 91 | return { 92 | form, 93 | schema, 94 | } 95 | }, 96 | methods: { 97 | onSubmit(value) { 98 | console.log(value) 99 | }, 100 | }, 101 | } 102 | </script> 103 | ``` -------------------------------------------------------------------------------- /packages/path/src/shared.ts: -------------------------------------------------------------------------------- ```typescript 1 | const toString = Object.prototype.toString 2 | const isType = 3 | <T>(type: string) => 4 | (obj: unknown): obj is T => 5 | toString.call(obj) === `[object ${type}]` 6 | export const isFn = isType<(...args: any[]) => any>('Function') 7 | export const isArr = Array.isArray || isType<unknown[]>('Array') 8 | export const isPlainObj = isType<object>('Object') 9 | export const isStr = isType<string>('String') 10 | export const isBool = isType<boolean>('Boolean') 11 | export const isNum = isType<number>('Number') 12 | export const isObj = (val: unknown): val is object => typeof val === 'object' 13 | export const isRegExp = isType<RegExp>('RegExp') 14 | export const isNumberLike = (t: any) => { 15 | return isNum(t) || /^(\d+)(\.\d+)?$/.test(t) 16 | } 17 | const isArray = isArr 18 | const keyList = Object.keys 19 | const hasProp = Object.prototype.hasOwnProperty 20 | 21 | export const toArr = <T>(val: T | T[]): T[] => 22 | Array.isArray(val) ? val : val !== undefined ? [val] : [] 23 | export const isAssignable = (val: any) => { 24 | return typeof val === 'object' || typeof val === 'function' 25 | } 26 | export const isEqual = (a: any, b: any) => { 27 | if (a === b) { 28 | return true 29 | } 30 | if (a && b && typeof a === 'object' && typeof b === 'object') { 31 | const arrA = isArray(a) 32 | const arrB = isArray(b) 33 | let i 34 | let length 35 | let key 36 | 37 | if (arrA && arrB) { 38 | length = a.length 39 | if (length !== b.length) { 40 | return false 41 | } 42 | for (i = length; i-- !== 0; ) { 43 | if (!isEqual(a[i], b[i])) { 44 | return false 45 | } 46 | } 47 | return true 48 | } 49 | 50 | if (arrA !== arrB) { 51 | return false 52 | } 53 | 54 | const keys = keyList(a) 55 | length = keys.length 56 | 57 | if (length !== keyList(b).length) { 58 | return false 59 | } 60 | 61 | for (i = length; i-- !== 0; ) { 62 | if (!hasProp.call(b, keys[i])) { 63 | return false 64 | } 65 | } 66 | for (i = length; i-- !== 0; ) { 67 | key = keys[i] 68 | if (!isEqual(a[key], b[key])) { 69 | return false 70 | } 71 | } 72 | 73 | return true 74 | } 75 | return a !== a && b !== b 76 | } 77 | export const isSegmentEqual = (a: any, b: any) => { 78 | a = typeof a === 'symbol' ? a : `${a}` 79 | b = typeof b === 'symbol' ? b : `${b}` 80 | return a === b 81 | } 82 | ``` -------------------------------------------------------------------------------- /packages/react/docs/api/shared/mapProps.md: -------------------------------------------------------------------------------- ```markdown 1 | # mapProps 2 | 3 | ## Description 4 | 5 | Adapter function that maps [Field](https://core.formilyjs.org/api/models/field) attributes and component attributes, mainly used in conjunction with the connect function 6 | 7 | ## Signature 8 | 9 | ```ts 10 | import { Field, GeneralField } from '@formily/core' 11 | type IStateMapper<Props> = 12 | | { 13 | [key in keyof Field]?: keyof Props | boolean 14 | } 15 | | ((props: Props, field: GeneralField) => Props) 16 | 17 | interface mapProps<T extends React.FC> { 18 | (...args: IStateMapper<React.ComponentProps<T>>[]): React.FC 19 | } 20 | ``` 21 | 22 | - Parameters can be passed objects (key is the attribute of the field, value is the attribute of the component, if the value is true, the mapped attribute name is the same) 23 | - Parameters can be passed to functions, and functions can directly do more complex mappings to attributes 24 | 25 | ## Example 26 | 27 | ```tsx 28 | import React, { useMemo } from 'react' 29 | import { createForm } from '@formily/core' 30 | import { 31 | FormProvider, 32 | FormConsumer, 33 | Field, 34 | connect, 35 | mapProps, 36 | } from '@formily/react' 37 | import { Input, Form, Button } from 'antd' 38 | 39 | // FormItem UI component 40 | const FormItem = connect( 41 | Form.Item, 42 | mapProps( 43 | { 44 | title: 'label', 45 | description: 'extra', 46 | required: true, 47 | validateStatus: true, 48 | }, 49 | (props, field) => { 50 | return { 51 | ...props, 52 | help: field.selfErrors?.length ? field.selfErrors : undefined, 53 | } 54 | } 55 | ) 56 | ) 57 | 58 | export default () => { 59 | const form = useMemo(() => createForm({ validateFirst: true })) 60 | return ( 61 | <FormProvider form={form}> 62 | <Form layout="vertical"> 63 | <Field 64 | name="name" 65 | title="Name" 66 | required 67 | decorator={[FormItem]} 68 | component={[Input, { placeholder: 'Please Input' }]} 69 | /> 70 | <code> 71 | <pre> 72 | <FormConsumer> 73 | {(form) => JSON.stringify(form.values, null, 2)} 74 | </FormConsumer> 75 | </pre> 76 | </code> 77 | <Button 78 | type="primary" 79 | onClick={() => { 80 | form.submit(console.log) 81 | }} 82 | > 83 | Submit 84 | </Button> 85 | </Form> 86 | </FormProvider> 87 | ) 88 | } 89 | ``` 90 | ``` -------------------------------------------------------------------------------- /packages/next/docs/components/TimePicker.md: -------------------------------------------------------------------------------- ```markdown 1 | # TimePicker 2 | 3 | > Time Picker 4 | 5 | ## Markup Schema example 6 | 7 | ```tsx 8 | import React from 'react' 9 | import { TimePicker, FormItem, FormButtonGroup, Submit } from '@formily/next' 10 | import { createForm } from '@formily/core' 11 | import { FormProvider, createSchemaField } from '@formily/react' 12 | 13 | const SchemaField = createSchemaField({ 14 | components: { 15 | TimePicker, 16 | FormItem, 17 | }, 18 | }) 19 | 20 | const form = createForm() 21 | 22 | export default () => ( 23 | <FormProvider form={form}> 24 | <SchemaField> 25 | <SchemaField.String 26 | name="time" 27 | title="time" 28 | x-decorator="FormItem" 29 | x-component="TimePicker" 30 | /> 31 | </SchemaField> 32 | <FormButtonGroup> 33 | <Submit onSubmit={console.log}>Submit</Submit> 34 | </FormButtonGroup> 35 | </FormProvider> 36 | ) 37 | ``` 38 | 39 | ## JSON Schema case 40 | 41 | ```tsx 42 | import React from 'react' 43 | import { TimePicker, FormItem, FormButtonGroup, Submit } from '@formily/next' 44 | import { createForm } from '@formily/core' 45 | import { FormProvider, createSchemaField } from '@formily/react' 46 | 47 | const SchemaField = createSchemaField({ 48 | components: { 49 | TimePicker, 50 | FormItem, 51 | }, 52 | }) 53 | 54 | const form = createForm() 55 | 56 | const schema = { 57 | type: 'object', 58 | properties: { 59 | time: { 60 | title: 'Time', 61 | 'x-decorator': 'FormItem', 62 | 'x-component': 'TimePicker', 63 | type: 'string', 64 | }, 65 | }, 66 | } 67 | 68 | export default () => ( 69 | <FormProvider form={form}> 70 | <SchemaField schema={schema} /> 71 | <FormButtonGroup> 72 | <Submit onSubmit={console.log}>Submit</Submit> 73 | </FormButtonGroup> 74 | </FormProvider> 75 | ) 76 | ``` 77 | 78 | ## Pure JSX case 79 | 80 | ```tsx 81 | import React from 'react' 82 | import { TimePicker, FormItem, FormButtonGroup, Submit } from '@formily/next' 83 | import { createForm } from '@formily/core' 84 | import { FormProvider, Field } from '@formily/react' 85 | 86 | const form = createForm() 87 | 88 | export default () => ( 89 | <FormProvider form={form}> 90 | <Field 91 | name="time" 92 | title="time" 93 | decorator={[FormItem]} 94 | component={[TimePicker]} 95 | /> 96 | <FormButtonGroup> 97 | <Submit onSubmit={console.log}>Submit</Submit> 98 | </FormButtonGroup> 99 | </FormProvider> 100 | ) 101 | ``` 102 | 103 | ## API 104 | 105 | Reference https://fusion.design/pc/component/basic/time-picker 106 | ``` -------------------------------------------------------------------------------- /packages/react/docs/api/components/VoidField.md: -------------------------------------------------------------------------------- ```markdown 1 | --- 2 | order: 3 3 | --- 4 | 5 | # VoidField 6 | 7 | ## Description 8 | 9 | As @formily/core's [createVoidField](https://core.formilyjs.org/api/models/form#createvoidfield) React implementation, it is a bridge component specifically used to bind ViewModel and virtual layout controls. Used to control the display and hide of data type fields, interactive mode, etc., VoidField component properties refer to [IVoidFieldFactoryProps](https://core.formilyjs.org/api/models/form#ivoidfieldfactoryprops) 10 | 11 | <Alert> 12 | When we use the VoidField component, we must remember to pass the name attribute. 13 | </Alert> 14 | 15 | ## Signature 16 | 17 | ```ts 18 | type VoidField = React.FC<React.PropsWithChildren<IVoidFieldFactoryProps>> 19 | ``` 20 | 21 | ## Example 22 | 23 | ```tsx 24 | import React from 'react' 25 | import { createForm } from '@formily/core' 26 | import { FormProvider, FormConsumer, Field, VoidField } from '@formily/react' 27 | import { Input, Button, Space } from 'antd' 28 | 29 | const form = createForm() 30 | 31 | export default () => ( 32 | <FormProvider form={form}> 33 | <Space> 34 | <VoidField name="layout"> 35 | <Field name="input" component={[Input]} /> 36 | </VoidField> 37 | <FormConsumer> 38 | {() => ( 39 | <Space> 40 | <Button 41 | onClick={() => { 42 | form 43 | .query('layout') 44 | .take() 45 | .setState((state) => { 46 | state.visible = !state.visible 47 | }) 48 | }} 49 | > 50 | {form.query('layout').get('visible') ? 'Hide' : 'Show'} 51 | </Button> 52 | <div>{JSON.stringify(form.values, null, 2)}</div> 53 | </Space> 54 | )} 55 | </FormConsumer> 56 | </Space> 57 | </FormProvider> 58 | ) 59 | ``` 60 | 61 | This example demonstrates how to use VoidField to control the display and hiding of child nodes. Observe that when the VoidField is hidden, the data of the child nodes will be cleared at the same time, because visible is false, which means display is none. This kind of hiding does not retain the field value. 62 | 63 | But when it is displayed again, the scene will be restored again. This is the internal feature of Formily Core, which supports the ability to completely restore the scene. 64 | ```