This is page 6 of 35. Use http://codebase.md/alibaba/formily?page={x} to view the full context. # Directory Structure ``` ├── .all-contributorsrc ├── .codecov.yml ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .github │ ├── CONTRIBUTING.md │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE │ │ └── config.yml │ ├── PULL_REQUEST_TEMPLATE.md │ └── workflows │ ├── check-pr-title.yml │ ├── ci.yml │ ├── commitlint.yml │ ├── issue-open-check.yml │ ├── package-size.yml │ └── pr-welcome.yml ├── .gitignore ├── .prettierrc.js ├── .umirc.js ├── .vscode │ └── cspell.json ├── .yarnrc ├── CHANGELOG.md ├── commitlint.config.js ├── devtools │ ├── .eslintrc │ └── chrome-extension │ ├── .npmignore │ ├── assets │ │ └── img │ │ ├── loading.svg │ │ └── logo │ │ ├── 128x128.png │ │ ├── 16x16.png │ │ ├── 38x38.png │ │ ├── 48x48.png │ │ ├── error.png │ │ ├── gray.png │ │ └── scalable.png │ ├── config │ │ ├── webpack.base.ts │ │ ├── webpack.dev.ts │ │ └── webpack.prod.ts │ ├── LICENSE.md │ ├── package.json │ ├── src │ │ ├── app │ │ │ ├── components │ │ │ │ ├── FieldTree.tsx │ │ │ │ ├── filter.ts │ │ │ │ ├── LeftPanel.tsx │ │ │ │ ├── RightPanel.tsx │ │ │ │ ├── SearchBox.tsx │ │ │ │ └── Tabs.tsx │ │ │ ├── demo.tsx │ │ │ └── index.tsx │ │ └── extension │ │ ├── backend.ts │ │ ├── background.ts │ │ ├── content.ts │ │ ├── devpanel.tsx │ │ ├── devtools.tsx │ │ ├── inject.ts │ │ ├── manifest.json │ │ ├── popup.tsx │ │ └── views │ │ ├── devpanel.ejs │ │ ├── devtools.ejs │ │ └── popup.ejs │ ├── tsconfig.build.json │ └── tsconfig.json ├── docs │ ├── functions │ │ ├── contributors.ts │ │ └── npm-search.ts │ ├── guide │ │ ├── advanced │ │ │ ├── async.md │ │ │ ├── async.zh-CN.md │ │ │ ├── build.md │ │ │ ├── build.zh-CN.md │ │ │ ├── business-logic.md │ │ │ ├── business-logic.zh-CN.md │ │ │ ├── calculator.md │ │ │ ├── calculator.zh-CN.md │ │ │ ├── controlled.md │ │ │ ├── controlled.zh-CN.md │ │ │ ├── custom.md │ │ │ ├── custom.zh-CN.md │ │ │ ├── destructor.md │ │ │ ├── destructor.zh-CN.md │ │ │ ├── input.less │ │ │ ├── layout.md │ │ │ ├── layout.zh-CN.md │ │ │ ├── linkages.md │ │ │ ├── linkages.zh-CN.md │ │ │ ├── validate.md │ │ │ └── validate.zh-CN.md │ │ ├── contribution.md │ │ ├── contribution.zh-CN.md │ │ ├── form-builder.md │ │ ├── form-builder.zh-CN.md │ │ ├── index.md │ │ ├── index.zh-CN.md │ │ ├── issue-helper.md │ │ ├── issue-helper.zh-CN.md │ │ ├── learn-formily.md │ │ ├── learn-formily.zh-CN.md │ │ ├── quick-start.md │ │ ├── quick-start.zh-CN.md │ │ ├── scenes │ │ │ ├── dialog-drawer.md │ │ │ ├── dialog-drawer.zh-CN.md │ │ │ ├── edit-detail.md │ │ │ ├── edit-detail.zh-CN.md │ │ │ ├── index.less │ │ │ ├── login-register.md │ │ │ ├── login-register.zh-CN.md │ │ │ ├── more.md │ │ │ ├── more.zh-CN.md │ │ │ ├── query-list.md │ │ │ ├── query-list.zh-CN.md │ │ │ ├── step-form.md │ │ │ ├── step-form.zh-CN.md │ │ │ ├── tab-form.md │ │ │ ├── tab-form.zh-CN.md │ │ │ └── VerifyCode.tsx │ │ ├── upgrade.md │ │ └── upgrade.zh-CN.md │ ├── index.md │ ├── index.zh-CN.md │ └── site │ ├── Contributors.less │ ├── Contributors.tsx │ ├── QrCode.less │ ├── QrCode.tsx │ ├── Section.less │ ├── Section.tsx │ └── styles.less ├── global.config.ts ├── jest.config.js ├── lerna.json ├── LICENSE.md ├── package.json ├── packages │ ├── .eslintrc │ ├── antd │ │ ├── __tests__ │ │ │ ├── moment.spec.ts │ │ │ └── sideEffects.spec.ts │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── build-style.ts │ │ ├── create-style.ts │ │ ├── docs │ │ │ ├── components │ │ │ │ ├── ArrayCards.md │ │ │ │ ├── ArrayCards.zh-CN.md │ │ │ │ ├── ArrayCollapse.md │ │ │ │ ├── ArrayCollapse.zh-CN.md │ │ │ │ ├── ArrayItems.md │ │ │ │ ├── ArrayItems.zh-CN.md │ │ │ │ ├── ArrayTable.md │ │ │ │ ├── ArrayTable.zh-CN.md │ │ │ │ ├── ArrayTabs.md │ │ │ │ ├── ArrayTabs.zh-CN.md │ │ │ │ ├── Cascader.md │ │ │ │ ├── Cascader.zh-CN.md │ │ │ │ ├── Checkbox.md │ │ │ │ ├── Checkbox.zh-CN.md │ │ │ │ ├── DatePicker.md │ │ │ │ ├── DatePicker.zh-CN.md │ │ │ │ ├── Editable.md │ │ │ │ ├── Editable.zh-CN.md │ │ │ │ ├── Form.md │ │ │ │ ├── Form.zh-CN.md │ │ │ │ ├── FormButtonGroup.md │ │ │ │ ├── FormButtonGroup.zh-CN.md │ │ │ │ ├── FormCollapse.md │ │ │ │ ├── FormCollapse.zh-CN.md │ │ │ │ ├── FormDialog.md │ │ │ │ ├── FormDialog.zh-CN.md │ │ │ │ ├── FormDrawer.md │ │ │ │ ├── FormDrawer.zh-CN.md │ │ │ │ ├── FormGrid.md │ │ │ │ ├── FormGrid.zh-CN.md │ │ │ │ ├── FormItem.md │ │ │ │ ├── FormItem.zh-CN.md │ │ │ │ ├── FormLayout.md │ │ │ │ ├── FormLayout.zh-CN.md │ │ │ │ ├── FormStep.md │ │ │ │ ├── FormStep.zh-CN.md │ │ │ │ ├── FormTab.md │ │ │ │ ├── FormTab.zh-CN.md │ │ │ │ ├── index.md │ │ │ │ ├── index.zh-CN.md │ │ │ │ ├── Input.md │ │ │ │ ├── Input.zh-CN.md │ │ │ │ ├── NumberPicker.md │ │ │ │ ├── NumberPicker.zh-CN.md │ │ │ │ ├── Password.md │ │ │ │ ├── Password.zh-CN.md │ │ │ │ ├── PreviewText.md │ │ │ │ ├── PreviewText.zh-CN.md │ │ │ │ ├── Radio.md │ │ │ │ ├── Radio.zh-CN.md │ │ │ │ ├── Reset.md │ │ │ │ ├── Reset.zh-CN.md │ │ │ │ ├── Select.md │ │ │ │ ├── Select.zh-CN.md │ │ │ │ ├── SelectTable.md │ │ │ │ ├── SelectTable.zh-CN.md │ │ │ │ ├── Space.md │ │ │ │ ├── Space.zh-CN.md │ │ │ │ ├── Submit.md │ │ │ │ ├── Submit.zh-CN.md │ │ │ │ ├── Switch.md │ │ │ │ ├── Switch.zh-CN.md │ │ │ │ ├── TimePicker.md │ │ │ │ ├── TimePicker.zh-CN.md │ │ │ │ ├── Transfer.md │ │ │ │ ├── Transfer.zh-CN.md │ │ │ │ ├── TreeSelect.md │ │ │ │ ├── TreeSelect.zh-CN.md │ │ │ │ ├── Upload.md │ │ │ │ └── Upload.zh-CN.md │ │ │ ├── index.md │ │ │ └── index.zh-CN.md │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __builtins__ │ │ │ │ ├── hooks │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── useClickAway.ts │ │ │ │ │ └── usePrefixCls.ts │ │ │ │ ├── index.ts │ │ │ │ ├── loading.ts │ │ │ │ ├── moment.ts │ │ │ │ ├── pickDataProps.ts │ │ │ │ ├── portal.tsx │ │ │ │ ├── render.ts │ │ │ │ └── sort.tsx │ │ │ ├── array-base │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── array-cards │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── array-collapse │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── array-items │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── array-table │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── array-tabs │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── cascader │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── checkbox │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── date-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── editable │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── form │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── form-button-group │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── form-collapse │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-dialog │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-drawer │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-grid │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── form-item │ │ │ │ ├── animation.less │ │ │ │ ├── grid.less │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── form-layout │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ ├── style.ts │ │ │ │ └── useResponsiveFormLayout.ts │ │ │ ├── form-step │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-tab │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── index.ts │ │ │ ├── input │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── number-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── password │ │ │ │ ├── index.tsx │ │ │ │ ├── PasswordStrength.tsx │ │ │ │ └── style.ts │ │ │ ├── preview-text │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── radio │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── reset │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── select │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── select-table │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ ├── style.ts │ │ │ │ ├── useCheckSlackly.tsx │ │ │ │ ├── useFilterOptions.tsx │ │ │ │ ├── useFlatOptions.tsx │ │ │ │ ├── useSize.tsx │ │ │ │ ├── useTitleAddon.tsx │ │ │ │ └── utils.ts │ │ │ ├── space │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── style.less │ │ │ ├── style.ts │ │ │ ├── submit │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── switch │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── time-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── transfer │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── tree-select │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ └── upload │ │ │ ├── index.tsx │ │ │ ├── placeholder.ts │ │ │ └── style.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── benchmark │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── src │ │ │ └── index.tsx │ │ ├── template.ejs │ │ ├── tsconfig.build.json │ │ ├── tsconfig.json │ │ ├── webpack.base.ts │ │ ├── webpack.dev.ts │ │ └── webpack.prod.ts │ ├── core │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── docs │ │ │ ├── api │ │ │ │ ├── entry │ │ │ │ │ ├── ActionResponse.less │ │ │ │ │ ├── ActionResponse.tsx │ │ │ │ │ ├── createForm.md │ │ │ │ │ ├── createForm.zh-CN.md │ │ │ │ │ ├── FieldEffectHooks.md │ │ │ │ │ ├── FieldEffectHooks.zh-CN.md │ │ │ │ │ ├── FormChecker.md │ │ │ │ │ ├── FormChecker.zh-CN.md │ │ │ │ │ ├── FormEffectHooks.md │ │ │ │ │ ├── FormEffectHooks.zh-CN.md │ │ │ │ │ ├── FormHooksAPI.md │ │ │ │ │ ├── FormHooksAPI.zh-CN.md │ │ │ │ │ ├── FormPath.md │ │ │ │ │ ├── FormPath.zh-CN.md │ │ │ │ │ ├── FormValidatorRegistry.md │ │ │ │ │ └── FormValidatorRegistry.zh-CN.md │ │ │ │ └── models │ │ │ │ ├── ArrayField.md │ │ │ │ ├── ArrayField.zh-CN.md │ │ │ │ ├── Field.md │ │ │ │ ├── Field.zh-CN.md │ │ │ │ ├── Form.md │ │ │ │ ├── Form.zh-CN.md │ │ │ │ ├── ObjectField.md │ │ │ │ ├── ObjectField.zh-CN.md │ │ │ │ ├── Query.md │ │ │ │ ├── Query.zh-CN.md │ │ │ │ ├── VoidField.md │ │ │ │ └── VoidField.zh-CN.md │ │ │ ├── guide │ │ │ │ ├── architecture.md │ │ │ │ ├── architecture.zh-CN.md │ │ │ │ ├── field.md │ │ │ │ ├── field.zh-CN.md │ │ │ │ ├── form.md │ │ │ │ ├── form.zh-CN.md │ │ │ │ ├── index.md │ │ │ │ ├── index.zh-CN.md │ │ │ │ ├── mvvm.md │ │ │ │ ├── mvvm.zh-CN.md │ │ │ │ ├── values.md │ │ │ │ └── values.zh-CN.md │ │ │ ├── index.md │ │ │ └── index.zh-CN.md │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── array.spec.ts │ │ │ │ ├── effects.spec.ts │ │ │ │ ├── externals.spec.ts │ │ │ │ ├── field.spec.ts │ │ │ │ ├── form.spec.ts │ │ │ │ ├── graph.spec.ts │ │ │ │ ├── heart.spec.ts │ │ │ │ ├── internals.spec.ts │ │ │ │ ├── lifecycle.spec.ts │ │ │ │ ├── object.spec.ts │ │ │ │ ├── shared.ts │ │ │ │ └── void.spec.ts │ │ │ ├── effects │ │ │ │ ├── index.ts │ │ │ │ ├── onFieldEffects.ts │ │ │ │ └── onFormEffects.ts │ │ │ ├── global.d.ts │ │ │ ├── index.ts │ │ │ ├── models │ │ │ │ ├── ArrayField.ts │ │ │ │ ├── BaseField.ts │ │ │ │ ├── Field.ts │ │ │ │ ├── Form.ts │ │ │ │ ├── Graph.ts │ │ │ │ ├── Heart.ts │ │ │ │ ├── index.ts │ │ │ │ ├── LifeCycle.ts │ │ │ │ ├── ObjectField.ts │ │ │ │ ├── Query.ts │ │ │ │ ├── types.ts │ │ │ │ └── VoidField.ts │ │ │ ├── shared │ │ │ │ ├── checkers.ts │ │ │ │ ├── constants.ts │ │ │ │ ├── effective.ts │ │ │ │ ├── externals.ts │ │ │ │ └── internals.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── element │ │ ├── .npmignore │ │ ├── build-style.ts │ │ ├── create-style.ts │ │ ├── docs │ │ │ ├── .vuepress │ │ │ │ ├── components │ │ │ │ │ ├── createCodeSandBox.js │ │ │ │ │ ├── dumi-previewer.vue │ │ │ │ │ └── highlight.js │ │ │ │ ├── config.js │ │ │ │ ├── enhanceApp.js │ │ │ │ ├── styles │ │ │ │ │ └── index.styl │ │ │ │ └── util.js │ │ │ ├── demos │ │ │ │ ├── guide │ │ │ │ │ ├── array-cards │ │ │ │ │ │ ├── effects-json-schema.vue │ │ │ │ │ │ ├── effects-markup-schema.vue │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── array-collapse │ │ │ │ │ │ ├── effects-json-schema.vue │ │ │ │ │ │ ├── effects-markup-schema.vue │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── array-items │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── array-table │ │ │ │ │ │ ├── effects-json-schema.vue │ │ │ │ │ │ ├── effects-markup-schema.vue │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── array-tabs │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── cascader │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── checkbox │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── date-picker │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── editable │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── form-button-group.vue │ │ │ │ │ ├── form-collapse │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── form-dialog │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── form-drawer │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── form-grid │ │ │ │ │ │ ├── form.vue │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── native.vue │ │ │ │ │ ├── form-item │ │ │ │ │ │ ├── bordered-none.vue │ │ │ │ │ │ ├── common.vue │ │ │ │ │ │ ├── feedback.vue │ │ │ │ │ │ ├── inset.vue │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ ├── size.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── form-layout │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── form-step │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── form-tab │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── form.vue │ │ │ │ │ ├── input │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── input-number │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── password │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── preview-text │ │ │ │ │ │ ├── base.vue │ │ │ │ │ │ └── extend.vue │ │ │ │ │ ├── radio │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── reset │ │ │ │ │ │ ├── base.vue │ │ │ │ │ │ ├── force.vue │ │ │ │ │ │ └── validate.vue │ │ │ │ │ ├── select │ │ │ │ │ │ ├── json-schema-async.vue │ │ │ │ │ │ ├── json-schema-sync.vue │ │ │ │ │ │ ├── markup-schema-async-search.vue │ │ │ │ │ │ ├── markup-schema-async.vue │ │ │ │ │ │ ├── markup-schema-sync.vue │ │ │ │ │ │ ├── template-async.vue │ │ │ │ │ │ └── template-sync.vue │ │ │ │ │ ├── space │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── submit │ │ │ │ │ │ ├── base.vue │ │ │ │ │ │ └── loading.vue │ │ │ │ │ ├── switch │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── time-picker │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── transfer │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ └── upload │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ └── template.vue │ │ │ │ └── index.vue │ │ │ ├── guide │ │ │ │ ├── array-cards.md │ │ │ │ ├── array-collapse.md │ │ │ │ ├── array-items.md │ │ │ │ ├── array-table.md │ │ │ │ ├── array-tabs.md │ │ │ │ ├── cascader.md │ │ │ │ ├── checkbox.md │ │ │ │ ├── date-picker.md │ │ │ │ ├── editable.md │ │ │ │ ├── form-button-group.md │ │ │ │ ├── form-collapse.md │ │ │ │ ├── form-dialog.md │ │ │ │ ├── form-drawer.md │ │ │ │ ├── form-grid.md │ │ │ │ ├── form-item.md │ │ │ │ ├── form-layout.md │ │ │ │ ├── form-step.md │ │ │ │ ├── form-tab.md │ │ │ │ ├── form.md │ │ │ │ ├── index.md │ │ │ │ ├── input-number.md │ │ │ │ ├── input.md │ │ │ │ ├── password.md │ │ │ │ ├── preview-text.md │ │ │ │ ├── radio.md │ │ │ │ ├── reset.md │ │ │ │ ├── select.md │ │ │ │ ├── space.md │ │ │ │ ├── submit.md │ │ │ │ ├── switch.md │ │ │ │ ├── time-picker.md │ │ │ │ ├── transfer.md │ │ │ │ └── upload.md │ │ │ └── README.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __builtins__ │ │ │ │ ├── configs │ │ │ │ │ └── index.ts │ │ │ │ ├── index.ts │ │ │ │ ├── shared │ │ │ │ │ ├── create-context.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── loading.ts │ │ │ │ │ ├── portal.ts │ │ │ │ │ ├── resolve-component.ts │ │ │ │ │ ├── transform-component.ts │ │ │ │ │ ├── types.ts │ │ │ │ │ └── utils.ts │ │ │ │ └── styles │ │ │ │ └── common.scss │ │ │ ├── array-base │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── array-cards │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── array-collapse │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── array-items │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── array-table │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── array-tabs │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── cascader │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── checkbox │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── date-picker │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── editable │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── el-form │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── el-form-item │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── form │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── form-button-group │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── form-collapse │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── form-dialog │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── form-drawer │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── form-grid │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── form-item │ │ │ │ ├── animation.scss │ │ │ │ ├── grid.scss │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ ├── style.ts │ │ │ │ └── var.scss │ │ │ ├── form-layout │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ ├── style.ts │ │ │ │ └── useResponsiveFormLayout.ts │ │ │ ├── form-step │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── form-tab │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── index.ts │ │ │ ├── input │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── input-number │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── password │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── preview-text │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── radio │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── reset │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── select │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── space │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── style.ts │ │ │ ├── submit │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── switch │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── time-picker │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── transfer │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ └── upload │ │ │ ├── index.ts │ │ │ └── style.ts │ │ ├── transformer.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── grid │ │ ├── .npmignore │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── index.ts │ │ │ └── observer.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── json-schema │ │ ├── .npmignore │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── __snapshots__ │ │ │ │ │ └── schema.spec.ts.snap │ │ │ │ ├── compiler.spec.ts │ │ │ │ ├── patches.spec.ts │ │ │ │ ├── schema.spec.ts │ │ │ │ ├── server-validate.spec.ts │ │ │ │ ├── shared.spec.ts │ │ │ │ ├── transformer.spec.ts │ │ │ │ └── traverse.spec.ts │ │ │ ├── compiler.ts │ │ │ ├── global.d.ts │ │ │ ├── index.ts │ │ │ ├── patches.ts │ │ │ ├── polyfills │ │ │ │ ├── index.ts │ │ │ │ └── SPECIFICATION_1_0.ts │ │ │ ├── schema.ts │ │ │ ├── shared.ts │ │ │ ├── transformer.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── next │ │ ├── __tests__ │ │ │ ├── moment.spec.ts │ │ │ └── sideEffects.spec.ts │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── build-style.ts │ │ ├── create-style.ts │ │ ├── docs │ │ │ ├── components │ │ │ │ ├── ArrayCards.md │ │ │ │ ├── ArrayCards.zh-CN.md │ │ │ │ ├── ArrayCollapse.md │ │ │ │ ├── ArrayCollapse.zh-CN.md │ │ │ │ ├── ArrayItems.md │ │ │ │ ├── ArrayItems.zh-CN.md │ │ │ │ ├── ArrayTable.md │ │ │ │ ├── ArrayTable.zh-CN.md │ │ │ │ ├── Cascader.md │ │ │ │ ├── Cascader.zh-CN.md │ │ │ │ ├── Checkbox.md │ │ │ │ ├── Checkbox.zh-CN.md │ │ │ │ ├── DatePicker.md │ │ │ │ ├── DatePicker.zh-CN.md │ │ │ │ ├── DatePicker2.md │ │ │ │ ├── DatePicker2.zh-CN.md │ │ │ │ ├── Editable.md │ │ │ │ ├── Editable.zh-CN.md │ │ │ │ ├── Form.md │ │ │ │ ├── Form.zh-CN.md │ │ │ │ ├── FormButtonGroup.md │ │ │ │ ├── FormButtonGroup.zh-CN.md │ │ │ │ ├── FormCollapse.md │ │ │ │ ├── FormCollapse.zh-CN.md │ │ │ │ ├── FormDialog.md │ │ │ │ ├── FormDialog.zh-CN.md │ │ │ │ ├── FormDrawer.md │ │ │ │ ├── FormDrawer.zh-CN.md │ │ │ │ ├── FormGrid.md │ │ │ │ ├── FormGrid.zh-CN.md │ │ │ │ ├── FormItem.md │ │ │ │ ├── FormItem.zh-CN.md │ │ │ │ ├── FormLayout.md │ │ │ │ ├── FormLayout.zh-CN.md │ │ │ │ ├── FormStep.md │ │ │ │ ├── FormStep.zh-CN.md │ │ │ │ ├── FormTab.md │ │ │ │ ├── FormTab.zh-CN.md │ │ │ │ ├── index.md │ │ │ │ ├── index.zh-CN.md │ │ │ │ ├── Input.md │ │ │ │ ├── Input.zh-CN.md │ │ │ │ ├── NumberPicker.md │ │ │ │ ├── NumberPicker.zh-CN.md │ │ │ │ ├── Password.md │ │ │ │ ├── Password.zh-CN.md │ │ │ │ ├── PreviewText.md │ │ │ │ ├── PreviewText.zh-CN.md │ │ │ │ ├── Radio.md │ │ │ │ ├── Radio.zh-CN.md │ │ │ │ ├── Reset.md │ │ │ │ ├── Reset.zh-CN.md │ │ │ │ ├── Select.md │ │ │ │ ├── Select.zh-CN.md │ │ │ │ ├── SelectTable.md │ │ │ │ ├── SelectTable.zh-CN.md │ │ │ │ ├── Space.md │ │ │ │ ├── Space.zh-CN.md │ │ │ │ ├── Submit.md │ │ │ │ ├── Submit.zh-CN.md │ │ │ │ ├── Switch.md │ │ │ │ ├── Switch.zh-CN.md │ │ │ │ ├── TimePicker.md │ │ │ │ ├── TimePicker.zh-CN.md │ │ │ │ ├── TimePicker2.md │ │ │ │ ├── TimePicker2.zh-CN.md │ │ │ │ ├── Transfer.md │ │ │ │ ├── Transfer.zh-CN.md │ │ │ │ ├── TreeSelect.md │ │ │ │ ├── TreeSelect.zh-CN.md │ │ │ │ ├── Upload.md │ │ │ │ └── Upload.zh-CN.md │ │ │ ├── index.md │ │ │ └── index.zh-CN.md │ │ ├── LESENCE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __builtins__ │ │ │ │ ├── empty.tsx │ │ │ │ ├── hooks │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── useClickAway.ts │ │ │ │ │ └── usePrefixCls.ts │ │ │ │ ├── icons.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── loading.ts │ │ │ │ ├── mapSize.ts │ │ │ │ ├── mapStatus.ts │ │ │ │ ├── moment.ts │ │ │ │ ├── pickDataProps.ts │ │ │ │ ├── portal.tsx │ │ │ │ ├── render.ts │ │ │ │ └── toArray.ts │ │ │ ├── array-base │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── array-cards │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── array-collapse │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── array-items │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── array-table │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── cascader │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── checkbox │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── date-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── date-picker2 │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── editable │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── form │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── form-button-group │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── form-collapse │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-dialog │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-drawer │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-grid │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── form-item │ │ │ │ ├── animation.scss │ │ │ │ ├── grid.scss │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ ├── scss │ │ │ │ │ └── variable.scss │ │ │ │ └── style.ts │ │ │ ├── form-layout │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ ├── style.ts │ │ │ │ └── useResponsiveFormLayout.ts │ │ │ ├── form-step │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-tab │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── index.ts │ │ │ ├── input │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── main.scss │ │ │ ├── number-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── password │ │ │ │ ├── index.tsx │ │ │ │ ├── PasswordStrength.tsx │ │ │ │ └── style.ts │ │ │ ├── preview-text │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── radio │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── reset │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── select │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── select-table │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ ├── style.ts │ │ │ │ ├── useCheckSlackly.tsx │ │ │ │ ├── useFilterOptions.tsx │ │ │ │ ├── useFlatOptions.tsx │ │ │ │ ├── useSize.tsx │ │ │ │ ├── useTitleAddon.tsx │ │ │ │ └── utils.ts │ │ │ ├── space │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── style.ts │ │ │ ├── submit │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── switch │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── time-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── time-picker2 │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── transfer │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── tree-select │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ └── upload │ │ │ ├── index.tsx │ │ │ ├── main.scss │ │ │ ├── placeholder.ts │ │ │ └── style.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── path │ │ ├── .npmignore │ │ ├── benchmark.ts │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── accessor.spec.ts │ │ │ │ ├── basic.spec.ts │ │ │ │ ├── match.spec.ts │ │ │ │ ├── parser.spec.ts │ │ │ │ └── share.spec.ts │ │ │ ├── contexts.ts │ │ │ ├── destructor.ts │ │ │ ├── index.ts │ │ │ ├── matcher.ts │ │ │ ├── parser.ts │ │ │ ├── shared.ts │ │ │ ├── tokenizer.ts │ │ │ ├── tokens.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── react │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── docs │ │ │ ├── api │ │ │ │ ├── components │ │ │ │ │ ├── ArrayField.md │ │ │ │ │ ├── ArrayField.zh-CN.md │ │ │ │ │ ├── ExpressionScope.md │ │ │ │ │ ├── ExpressionScope.zh-CN.md │ │ │ │ │ ├── Field.md │ │ │ │ │ ├── Field.zh-CN.md │ │ │ │ │ ├── FormConsumer.md │ │ │ │ │ ├── FormConsumer.zh-CN.md │ │ │ │ │ ├── FormProvider.md │ │ │ │ │ ├── FormProvider.zh-CN.md │ │ │ │ │ ├── ObjectField.md │ │ │ │ │ ├── ObjectField.zh-CN.md │ │ │ │ │ ├── RecordScope.md │ │ │ │ │ ├── RecordScope.zh-CN.md │ │ │ │ │ ├── RecordsScope.md │ │ │ │ │ ├── RecordsScope.zh-CN.md │ │ │ │ │ ├── RecursionField.md │ │ │ │ │ ├── RecursionField.zh-CN.md │ │ │ │ │ ├── SchemaField.md │ │ │ │ │ ├── SchemaField.zh-CN.md │ │ │ │ │ ├── VoidField.md │ │ │ │ │ └── VoidField.zh-CN.md │ │ │ │ ├── hooks │ │ │ │ │ ├── useExpressionScope.md │ │ │ │ │ ├── useExpressionScope.zh-CN.md │ │ │ │ │ ├── useField.md │ │ │ │ │ ├── useField.zh-CN.md │ │ │ │ │ ├── useFieldSchema.md │ │ │ │ │ ├── useFieldSchema.zh-CN.md │ │ │ │ │ ├── useForm.md │ │ │ │ │ ├── useForm.zh-CN.md │ │ │ │ │ ├── useFormEffects.md │ │ │ │ │ ├── useFormEffects.zh-CN.md │ │ │ │ │ ├── useParentForm.md │ │ │ │ │ └── useParentForm.zh-CN.md │ │ │ │ └── shared │ │ │ │ ├── connect.md │ │ │ │ ├── connect.zh-CN.md │ │ │ │ ├── context.md │ │ │ │ ├── context.zh-CN.md │ │ │ │ ├── mapProps.md │ │ │ │ ├── mapProps.zh-CN.md │ │ │ │ ├── mapReadPretty.md │ │ │ │ ├── mapReadPretty.zh-CN.md │ │ │ │ ├── observer.md │ │ │ │ ├── observer.zh-CN.md │ │ │ │ ├── Schema.md │ │ │ │ └── Schema.zh-CN.md │ │ │ ├── guide │ │ │ │ ├── architecture.md │ │ │ │ ├── architecture.zh-CN.md │ │ │ │ ├── concept.md │ │ │ │ ├── concept.zh-CN.md │ │ │ │ ├── index.md │ │ │ │ └── index.zh-CN.md │ │ │ ├── index.md │ │ │ └── index.zh-CN.md │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── expression.spec.tsx │ │ │ │ ├── field.spec.tsx │ │ │ │ ├── form.spec.tsx │ │ │ │ ├── schema.json.spec.tsx │ │ │ │ ├── schema.markup.spec.tsx │ │ │ │ └── shared.tsx │ │ │ ├── components │ │ │ │ ├── ArrayField.tsx │ │ │ │ ├── ExpressionScope.tsx │ │ │ │ ├── Field.tsx │ │ │ │ ├── FormConsumer.tsx │ │ │ │ ├── FormProvider.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── ObjectField.tsx │ │ │ │ ├── ReactiveField.tsx │ │ │ │ ├── RecordScope.tsx │ │ │ │ ├── RecordsScope.tsx │ │ │ │ ├── RecursionField.tsx │ │ │ │ ├── SchemaField.tsx │ │ │ │ └── VoidField.tsx │ │ │ ├── global.d.ts │ │ │ ├── hooks │ │ │ │ ├── index.ts │ │ │ │ ├── useAttach.ts │ │ │ │ ├── useExpressionScope.ts │ │ │ │ ├── useField.ts │ │ │ │ ├── useFieldSchema.ts │ │ │ │ ├── useForm.ts │ │ │ │ ├── useFormEffects.ts │ │ │ │ └── useParentForm.ts │ │ │ ├── index.ts │ │ │ ├── shared │ │ │ │ ├── connect.ts │ │ │ │ ├── context.ts │ │ │ │ ├── index.ts │ │ │ │ └── render.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── reactive │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── benchmark.ts │ │ ├── docs │ │ │ ├── api │ │ │ │ ├── action.md │ │ │ │ ├── action.zh-CN.md │ │ │ │ ├── autorun.md │ │ │ │ ├── autorun.zh-CN.md │ │ │ │ ├── batch.md │ │ │ │ ├── batch.zh-CN.md │ │ │ │ ├── define.md │ │ │ │ ├── define.zh-CN.md │ │ │ │ ├── hasCollected.md │ │ │ │ ├── hasCollected.zh-CN.md │ │ │ │ ├── markObservable.md │ │ │ │ ├── markObservable.zh-CN.md │ │ │ │ ├── markRaw.md │ │ │ │ ├── markRaw.zh-CN.md │ │ │ │ ├── model.md │ │ │ │ ├── model.zh-CN.md │ │ │ │ ├── observable.md │ │ │ │ ├── observable.zh-CN.md │ │ │ │ ├── observe.md │ │ │ │ ├── observe.zh-CN.md │ │ │ │ ├── raw.md │ │ │ │ ├── raw.zh-CN.md │ │ │ │ ├── react │ │ │ │ │ ├── observer.md │ │ │ │ │ └── observer.zh-CN.md │ │ │ │ ├── reaction.md │ │ │ │ ├── reaction.zh-CN.md │ │ │ │ ├── toJS.md │ │ │ │ ├── toJS.zh-CN.md │ │ │ │ ├── tracker.md │ │ │ │ ├── tracker.zh-CN.md │ │ │ │ ├── typeChecker.md │ │ │ │ ├── typeChecker.zh-CN.md │ │ │ │ ├── untracked.md │ │ │ │ ├── untracked.zh-CN.md │ │ │ │ └── vue │ │ │ │ ├── observer.md │ │ │ │ └── observer.zh-CN.md │ │ │ ├── guide │ │ │ │ ├── best-practice.md │ │ │ │ ├── best-practice.zh-CN.md │ │ │ │ ├── concept.md │ │ │ │ ├── concept.zh-CN.md │ │ │ │ ├── index.md │ │ │ │ └── index.zh-CN.md │ │ │ ├── index.md │ │ │ └── index.zh-CN.md │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── action.spec.ts │ │ │ │ ├── annotations.spec.ts │ │ │ │ ├── array.spec.ts │ │ │ │ ├── autorun.spec.ts │ │ │ │ ├── batch.spec.ts │ │ │ │ ├── collections-map.spec.ts │ │ │ │ ├── collections-set.spec.ts │ │ │ │ ├── collections-weakmap.spec.ts │ │ │ │ ├── collections-weakset.spec.ts │ │ │ │ ├── define.spec.ts │ │ │ │ ├── externals.spec.ts │ │ │ │ ├── hasCollected.spec.ts │ │ │ │ ├── observable.spec.ts │ │ │ │ ├── observe.spec.ts │ │ │ │ ├── tracker.spec.ts │ │ │ │ └── untracked.spec.ts │ │ │ ├── action.ts │ │ │ ├── annotations │ │ │ │ ├── box.ts │ │ │ │ ├── computed.ts │ │ │ │ ├── index.ts │ │ │ │ ├── observable.ts │ │ │ │ ├── ref.ts │ │ │ │ └── shallow.ts │ │ │ ├── array.ts │ │ │ ├── autorun.ts │ │ │ ├── batch.ts │ │ │ ├── checkers.ts │ │ │ ├── environment.ts │ │ │ ├── externals.ts │ │ │ ├── global.d.ts │ │ │ ├── handlers.ts │ │ │ ├── index.ts │ │ │ ├── internals.ts │ │ │ ├── model.ts │ │ │ ├── observable.ts │ │ │ ├── observe.ts │ │ │ ├── reaction.ts │ │ │ ├── tracker.ts │ │ │ ├── tree.ts │ │ │ ├── types.ts │ │ │ └── untracked.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── reactive-react │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── hooks │ │ │ │ ├── index.ts │ │ │ │ ├── useCompatEffect.ts │ │ │ │ ├── useCompatFactory.ts │ │ │ │ ├── useDidUpdate.ts │ │ │ │ ├── useForceUpdate.ts │ │ │ │ ├── useLayoutEffect.ts │ │ │ │ └── useObserver.ts │ │ │ ├── index.ts │ │ │ ├── observer.ts │ │ │ ├── shared │ │ │ │ ├── gc.ts │ │ │ │ ├── global.ts │ │ │ │ ├── immediate.ts │ │ │ │ └── index.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── reactive-test-cases-for-react18 │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── src │ │ │ ├── index.js │ │ │ └── MySlowList.js │ │ ├── template.ejs │ │ ├── tsconfig.build.json │ │ ├── tsconfig.json │ │ ├── webpack.base.ts │ │ ├── webpack.dev.ts │ │ └── webpack.prod.ts │ ├── reactive-vue │ │ ├── .npmignore │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ └── observer.spec.ts │ │ │ ├── hooks │ │ │ │ ├── index.ts │ │ │ │ └── useObserver.ts │ │ │ ├── index.ts │ │ │ ├── observer │ │ │ │ ├── collectData.ts │ │ │ │ ├── index.ts │ │ │ │ ├── observerInVue2.ts │ │ │ │ └── observerInVue3.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── shared │ │ ├── .npmignore │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ └── index.spec.ts │ │ │ ├── array.ts │ │ │ ├── case.ts │ │ │ ├── checkers.ts │ │ │ ├── clone.ts │ │ │ ├── compare.ts │ │ │ ├── defaults.ts │ │ │ ├── deprecate.ts │ │ │ ├── global.ts │ │ │ ├── index.ts │ │ │ ├── instanceof.ts │ │ │ ├── isEmpty.ts │ │ │ ├── merge.ts │ │ │ ├── middleware.ts │ │ │ ├── path.ts │ │ │ ├── string.ts │ │ │ ├── subscribable.ts │ │ │ └── uid.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── validator │ │ ├── .npmignore │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── parser.spec.ts │ │ │ │ ├── registry.spec.ts │ │ │ │ └── validator.spec.ts │ │ │ ├── formats.ts │ │ │ ├── index.ts │ │ │ ├── locale.ts │ │ │ ├── parser.ts │ │ │ ├── registry.ts │ │ │ ├── rules.ts │ │ │ ├── template.ts │ │ │ ├── types.ts │ │ │ └── validator.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ └── vue │ ├── .npmignore │ ├── bin │ │ ├── formily-vue-fix.js │ │ └── formily-vue-switch.js │ ├── docs │ │ ├── .vuepress │ │ │ ├── components │ │ │ │ ├── createCodeSandBox.js │ │ │ │ ├── dumi-previewer.vue │ │ │ │ └── highlight.js │ │ │ ├── config.js │ │ │ ├── enhanceApp.js │ │ │ └── styles │ │ │ └── index.styl │ │ ├── api │ │ │ ├── components │ │ │ │ ├── array-field.md │ │ │ │ ├── expression-scope.md │ │ │ │ ├── field.md │ │ │ │ ├── form-consumer.md │ │ │ │ ├── form-provider.md │ │ │ │ ├── object-field.md │ │ │ │ ├── recursion-field-with-component.md │ │ │ │ ├── recursion-field.md │ │ │ │ ├── schema-field-with-schema.md │ │ │ │ ├── schema-field.md │ │ │ │ └── void-field.md │ │ │ ├── hooks │ │ │ │ ├── use-field-schema.md │ │ │ │ ├── use-field.md │ │ │ │ ├── use-form-effects.md │ │ │ │ ├── use-form.md │ │ │ │ └── use-parent-form.md │ │ │ └── shared │ │ │ ├── connect.md │ │ │ ├── injections.md │ │ │ ├── map-props.md │ │ │ ├── map-read-pretty.md │ │ │ ├── observer.md │ │ │ └── schema.md │ │ ├── demos │ │ │ ├── api │ │ │ │ ├── components │ │ │ │ │ ├── array-field.vue │ │ │ │ │ ├── expression-scope.vue │ │ │ │ │ ├── field.vue │ │ │ │ │ ├── form-consumer.vue │ │ │ │ │ ├── form-provider.vue │ │ │ │ │ ├── object-field.vue │ │ │ │ │ ├── recursion-field-with-component.vue │ │ │ │ │ ├── recursion-field.vue │ │ │ │ │ ├── schema-field-with-schema.vue │ │ │ │ │ ├── schema-field.vue │ │ │ │ │ └── void-field.vue │ │ │ │ ├── hooks │ │ │ │ │ ├── use-field-schema.vue │ │ │ │ │ ├── use-field.vue │ │ │ │ │ ├── use-form-effects.vue │ │ │ │ │ ├── use-form.vue │ │ │ │ │ └── use-parent-form.vue │ │ │ │ └── shared │ │ │ │ ├── connect.vue │ │ │ │ ├── map-props.vue │ │ │ │ ├── map-read-pretty.vue │ │ │ │ └── observer.vue │ │ │ ├── index.vue │ │ │ └── questions │ │ │ ├── default-slot.vue │ │ │ ├── events.vue │ │ │ ├── named-slot.vue │ │ │ └── scoped-slot.vue │ │ ├── guide │ │ │ ├── architecture.md │ │ │ ├── concept.md │ │ │ └── README.md │ │ ├── questions │ │ │ └── README.md │ │ └── README.md │ ├── package.json │ ├── README.md │ ├── rollup.config.js │ ├── scripts │ │ ├── postinstall.js │ │ ├── switch-cli.js │ │ └── utils.js │ ├── src │ │ ├── __tests__ │ │ │ ├── expression.scope.spec.ts │ │ │ ├── field.spec.ts │ │ │ ├── form.spec.ts │ │ │ ├── schema.json.spec.ts │ │ │ ├── schema.markup.spec.ts │ │ │ ├── shared.spec.ts │ │ │ └── utils.spec.ts │ │ ├── components │ │ │ ├── ArrayField.ts │ │ │ ├── ExpressionScope.ts │ │ │ ├── Field.ts │ │ │ ├── FormConsumer.ts │ │ │ ├── FormProvider.ts │ │ │ ├── index.ts │ │ │ ├── ObjectField.ts │ │ │ ├── ReactiveField.ts │ │ │ ├── RecursionField.ts │ │ │ ├── SchemaField.ts │ │ │ └── VoidField.ts │ │ ├── global.d.ts │ │ ├── hooks │ │ │ ├── index.ts │ │ │ ├── useAttach.ts │ │ │ ├── useField.ts │ │ │ ├── useFieldSchema.ts │ │ │ ├── useForm.ts │ │ │ ├── useFormEffects.ts │ │ │ ├── useInjectionCleaner.ts │ │ │ └── useParentForm.ts │ │ ├── index.ts │ │ ├── shared │ │ │ ├── connect.ts │ │ │ ├── context.ts │ │ │ ├── createForm.ts │ │ │ ├── fragment.ts │ │ │ ├── h.ts │ │ │ └── index.ts │ │ ├── types │ │ │ └── index.ts │ │ ├── utils │ │ │ ├── formatVNodeData.ts │ │ │ ├── getFieldProps.ts │ │ │ ├── getRawComponent.ts │ │ │ └── resolveSchemaProps.ts │ │ └── vue2-components.ts │ ├── tsconfig.build.json │ ├── tsconfig.json │ └── tsconfig.types.json ├── README.md ├── README.zh-cn.md ├── scripts │ ├── build-style │ │ ├── buildAllStyles.ts │ │ ├── copy.ts │ │ ├── helper.ts │ │ └── index.ts │ └── rollup.base.js ├── tsconfig.build.json ├── tsconfig.jest.json ├── tsconfig.json └── yarn.lock ``` # Files -------------------------------------------------------------------------------- /packages/next/docs/components/Switch.md: -------------------------------------------------------------------------------- ```markdown # Switch > Switch Components ## Markup Schema example ```tsx import React from 'react' import { Switch, FormItem, FormButtonGroup, Submit } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { Switch, FormItem, }, }) const form = createForm() export default () => ( <FormProvider form={form}> <SchemaField> <SchemaField.Boolean name="switch" title="Switch" x-decorator="FormItem" x-component="Switch" /> </SchemaField> <FormButtonGroup> <Submit onSubmit={console.log}>Submit</Submit> </FormButtonGroup> </FormProvider> ) ``` ## JSON Schema case ```tsx import React from 'react' import { Switch, FormItem, FormButtonGroup, Submit } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { Switch, FormItem, }, }) const form = createForm() const schema = { type: 'object', properties: { switch: { type: 'boolean', title: 'Switch', 'x-decorator': 'FormItem', 'x-component': 'Switch', }, }, } export default () => ( <FormProvider form={form}> <SchemaField schema={schema} /> <FormButtonGroup> <Submit onSubmit={console.log}>Submit</Submit> </FormButtonGroup> </FormProvider> ) ``` ## Pure JSX case ```tsx import React from 'react' import { Switch, FormItem, FormButtonGroup, Submit } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, Field } from '@formily/react' const form = createForm() export default () => ( <FormProvider form={form}> <Field name="switch" title="Switch" decorator={[FormItem]} component={[Switch]} /> <FormButtonGroup> <Submit onSubmit={console.log}>Submit</Submit> </FormButtonGroup> </FormProvider> ) ``` ## API Reference https://fusion.design/pc/component/basic/switch ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/upload/json-schema.vue: -------------------------------------------------------------------------------- ```vue <template> <Form :form="form" :label-col="4" :wrapper-col="10"> <SchemaField :schema="schema" /> <FormButtonGroup align-form-item> <Submit @submit="onSubmit">提交</Submit> </FormButtonGroup> </Form> </template> <script> import { createForm } from '@formily/core' import { createSchemaField } from '@formily/vue' import { Form, FormItem, Upload, Submit, FormButtonGroup, } from '@formily/element' import { Button } from 'element-ui' const UploadButton = { functional: true, render(h) { return h(Button, {}, '上传图片') }, } const schema = { type: 'object', properties: { base: { type: 'array', title: '上传', 'x-decorator': 'FormItem', 'x-component': 'Upload', 'x-component-props': { action: 'https://formily-vue.free.beeceptor.com/file', textContent: '上传', }, required: true, }, card: { type: 'array', title: '卡片上传', 'x-decorator': 'FormItem', 'x-component': 'Upload', 'x-component-props': { listType: 'picture-card', action: 'https://formily-vue.free.beeceptor.com/file', }, required: true, }, drag: { type: 'array', title: '拖拽上传', 'x-decorator': 'FormItem', 'x-component': 'Upload', 'x-component-props': { action: 'https://formily-vue.free.beeceptor.com/file', textContent: '将文件拖到此处,或者点击上传', drag: true, }, required: true, }, custom: { type: 'array', title: '自定义按钮', 'x-decorator': 'FormItem', 'x-component': 'Upload', 'x-component-props': { action: 'https://formily-vue.free.beeceptor.com/file', }, 'x-content': UploadButton, required: true, }, }, } const form = createForm() const { SchemaField } = createSchemaField({ components: { FormItem, Upload, }, }) export default { components: { Form, SchemaField, Submit, FormButtonGroup }, data() { return { form, schema, } }, methods: { onSubmit(value) { console.log(value) }, }, } </script> ``` -------------------------------------------------------------------------------- /packages/path/src/shared.ts: -------------------------------------------------------------------------------- ```typescript const toString = Object.prototype.toString const isType = <T>(type: string) => (obj: unknown): obj is T => toString.call(obj) === `[object ${type}]` export const isFn = isType<(...args: any[]) => any>('Function') export const isArr = Array.isArray || isType<unknown[]>('Array') export const isPlainObj = isType<object>('Object') export const isStr = isType<string>('String') export const isBool = isType<boolean>('Boolean') export const isNum = isType<number>('Number') export const isObj = (val: unknown): val is object => typeof val === 'object' export const isRegExp = isType<RegExp>('RegExp') export const isNumberLike = (t: any) => { return isNum(t) || /^(\d+)(\.\d+)?$/.test(t) } const isArray = isArr const keyList = Object.keys const hasProp = Object.prototype.hasOwnProperty export const toArr = <T>(val: T | T[]): T[] => Array.isArray(val) ? val : val !== undefined ? [val] : [] export const isAssignable = (val: any) => { return typeof val === 'object' || typeof val === 'function' } export const isEqual = (a: any, b: any) => { if (a === b) { return true } if (a && b && typeof a === 'object' && typeof b === 'object') { const arrA = isArray(a) const arrB = isArray(b) let i let length let key if (arrA && arrB) { length = a.length if (length !== b.length) { return false } for (i = length; i-- !== 0; ) { if (!isEqual(a[i], b[i])) { return false } } return true } if (arrA !== arrB) { return false } const keys = keyList(a) length = keys.length if (length !== keyList(b).length) { return false } for (i = length; i-- !== 0; ) { if (!hasProp.call(b, keys[i])) { return false } } for (i = length; i-- !== 0; ) { key = keys[i] if (!isEqual(a[key], b[key])) { return false } } return true } return a !== a && b !== b } export const isSegmentEqual = (a: any, b: any) => { a = typeof a === 'symbol' ? a : `${a}` b = typeof b === 'symbol' ? b : `${b}` return a === b } ``` -------------------------------------------------------------------------------- /packages/react/docs/api/shared/mapProps.md: -------------------------------------------------------------------------------- ```markdown # mapProps ## Description Adapter function that maps [Field](https://core.formilyjs.org/api/models/field) attributes and component attributes, mainly used in conjunction with the connect function ## Signature ```ts import { Field, GeneralField } from '@formily/core' type IStateMapper<Props> = | { [key in keyof Field]?: keyof Props | boolean } | ((props: Props, field: GeneralField) => Props) interface mapProps<T extends React.FC> { (...args: IStateMapper<React.ComponentProps<T>>[]): React.FC } ``` - 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) - Parameters can be passed to functions, and functions can directly do more complex mappings to attributes ## Example ```tsx import React, { useMemo } from 'react' import { createForm } from '@formily/core' import { FormProvider, FormConsumer, Field, connect, mapProps, } from '@formily/react' import { Input, Form, Button } from 'antd' // FormItem UI component const FormItem = connect( Form.Item, mapProps( { title: 'label', description: 'extra', required: true, validateStatus: true, }, (props, field) => { return { ...props, help: field.selfErrors?.length ? field.selfErrors : undefined, } } ) ) export default () => { const form = useMemo(() => createForm({ validateFirst: true })) return ( <FormProvider form={form}> <Form layout="vertical"> <Field name="name" title="Name" required decorator={[FormItem]} component={[Input, { placeholder: 'Please Input' }]} /> <code> <pre> <FormConsumer> {(form) => JSON.stringify(form.values, null, 2)} </FormConsumer> </pre> </code> <Button type="primary" onClick={() => { form.submit(console.log) }} > Submit </Button> </Form> </FormProvider> ) } ``` ``` -------------------------------------------------------------------------------- /packages/next/docs/components/TimePicker.md: -------------------------------------------------------------------------------- ```markdown # TimePicker > Time Picker ## Markup Schema example ```tsx import React from 'react' import { TimePicker, FormItem, FormButtonGroup, Submit } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { TimePicker, FormItem, }, }) const form = createForm() export default () => ( <FormProvider form={form}> <SchemaField> <SchemaField.String name="time" title="time" x-decorator="FormItem" x-component="TimePicker" /> </SchemaField> <FormButtonGroup> <Submit onSubmit={console.log}>Submit</Submit> </FormButtonGroup> </FormProvider> ) ``` ## JSON Schema case ```tsx import React from 'react' import { TimePicker, FormItem, FormButtonGroup, Submit } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { TimePicker, FormItem, }, }) const form = createForm() const schema = { type: 'object', properties: { time: { title: 'Time', 'x-decorator': 'FormItem', 'x-component': 'TimePicker', type: 'string', }, }, } export default () => ( <FormProvider form={form}> <SchemaField schema={schema} /> <FormButtonGroup> <Submit onSubmit={console.log}>Submit</Submit> </FormButtonGroup> </FormProvider> ) ``` ## Pure JSX case ```tsx import React from 'react' import { TimePicker, FormItem, FormButtonGroup, Submit } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, Field } from '@formily/react' const form = createForm() export default () => ( <FormProvider form={form}> <Field name="time" title="time" decorator={[FormItem]} component={[TimePicker]} /> <FormButtonGroup> <Submit onSubmit={console.log}>Submit</Submit> </FormButtonGroup> </FormProvider> ) ``` ## API Reference https://fusion.design/pc/component/basic/time-picker ``` -------------------------------------------------------------------------------- /packages/react/docs/api/components/VoidField.md: -------------------------------------------------------------------------------- ```markdown --- order: 3 --- # VoidField ## Description 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) <Alert> When we use the VoidField component, we must remember to pass the name attribute. </Alert> ## Signature ```ts type VoidField = React.FC<React.PropsWithChildren<IVoidFieldFactoryProps>> ``` ## Example ```tsx import React from 'react' import { createForm } from '@formily/core' import { FormProvider, FormConsumer, Field, VoidField } from '@formily/react' import { Input, Button, Space } from 'antd' const form = createForm() export default () => ( <FormProvider form={form}> <Space> <VoidField name="layout"> <Field name="input" component={[Input]} /> </VoidField> <FormConsumer> {() => ( <Space> <Button onClick={() => { form .query('layout') .take() .setState((state) => { state.visible = !state.visible }) }} > {form.query('layout').get('visible') ? 'Hide' : 'Show'} </Button> <div>{JSON.stringify(form.values, null, 2)}</div> </Space> )} </FormConsumer> </Space> </FormProvider> ) ``` 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. 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. ``` -------------------------------------------------------------------------------- /packages/element/package.json: -------------------------------------------------------------------------------- ```json { "name": "@formily/element", "version": "2.3.7", "license": "MIT", "main": "lib", "module": "esm", "umd:main": "dist/formily.element.umd.production.js", "unpkg": "dist/formily.element.umd.production.js", "jsdelivr": "dist/formily.element.umd.production.js", "jsnext:main": "esm", "types": "lib/index.d.ts", "engines": { "npm": ">=3.0.0" }, "sideEffects": [ "dist/*", "esm/*.js", "lib/*.js", "src/*.ts", "*.scss" ], "scripts": { "start": "vuepress dev docs", "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", "create:style": "ts-node create-style", "build:style": "ts-node build-style", "build:cjs": "ttsc --declaration", "build:esm": "ttsc --declaration --module es2015 --outDir esm", "build:umd": "rollup --config", "build:docs": "vuepress build docs" }, "devDependencies": { "@vue/composition-api": "^1.0.0-rc.7", "@vuepress-dumi/vuepress-plugin-dumi-previewer": "0.3.3", "@vuepress-dumi/vuepress-theme-dumi": "0.3.3", "@vuepress/plugin-back-to-top": "^1.8.2", "@vuepress/plugin-medium-zoom": "^1.8.2", "codesandbox": "^2.2.3", "core-js": "^2.4.0", "element-ui": "^2.15.7", "sass": "^1.34.1", "ts-import-plugin": "^2.0.0", "ttypescript": "^1.5.15", "vue": "^2.6.0", "vuepress": "^1.8.2", "vuepress-plugin-typescript": "^0.3.1" }, "dependencies": { "@formily/core": "2.3.7", "@formily/grid": "2.3.7", "@formily/json-schema": "2.3.7", "@formily/reactive": "2.3.7", "@formily/reactive-vue": "2.3.7", "@formily/shared": "2.3.7", "@formily/vue": "2.3.7", "portal-vue": "^2.1.7", "vue-demi": ">=0.13.6", "vue-slicksort": "^1.2.0" }, "peerDependencies": { "@vue/composition-api": "^1.0.0-beta.1", "element-ui": "^2.14.0", "vue": "^2.6.0" }, "peerDependenciesMeta": { "@vue/composition-api": { "optional": true } }, "publishConfig": { "access": "public" }, "gitHead": "ac79c196ae9324889aca5e0501146f9b37b04283" } ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/date-picker/template.vue: -------------------------------------------------------------------------------- ```vue <template> <Form :form="form"> <Field name="date" title="普通日期" :decorator="[FormItem]" :component="[DatePicker]" /> <Field name="week" title="周" :decorator="[FormItem]" :component="[ DatePicker, { type: 'week', }, ]" /> <Field name="month" title="月" :decorator="[FormItem]" :component="[ DatePicker, { type: 'month', }, ]" /> <Field name="year" title="年" :decorator="[FormItem]" :component="[ DatePicker, { type: 'year', }, ]" /> <Field name="dateTime" title="日期时间" :decorator="[FormItem]" :component="[ DatePicker, { type: 'datetime', }, ]" /> <ArrayField name="dates" title="多个日期" :decorator="[FormItem]" :component="[ DatePicker, { type: 'dates', }, ]" /> <ArrayField name="dateRange" title="日期范围" :decorator="[FormItem]" :component="[ DatePicker, { type: 'daterange', }, ]" /> <ArrayField name="monthRange" title="月范围" :decorator="[FormItem]" :component="[ DatePicker, { type: 'monthrange', }, ]" /> <ArrayField name="dateTimeRange" title="日期时间范围" :decorator="[FormItem]" :component="[ DatePicker, { type: 'datetimerange', }, ]" /> <Submit @submit="onSubmit">提交</Submit> </Form> </template> <script> import { createForm } from '@formily/core' import { Field, ArrayField } from '@formily/vue' import { Form, FormItem, DatePicker, Submit } from '@formily/element' const form = createForm() export default { components: { Form, Field, ArrayField, Submit }, data() { return { FormItem, DatePicker, form, } }, methods: { onSubmit(value) { console.log(value) }, }, } </script> ``` -------------------------------------------------------------------------------- /packages/antd/package.json: -------------------------------------------------------------------------------- ```json { "name": "@formily/antd", "version": "2.3.7", "license": "MIT", "main": "lib", "module": "esm", "umd:main": "dist/formily.antd.umd.production.js", "unpkg": "dist/formily.antd.umd.production.js", "jsdelivr": "dist/formily.antd.umd.production.js", "jsnext:main": "esm", "sideEffects": [ "dist/*", "esm/*.js", "lib/*.js", "src/*.ts", "*.less", "**/*/style.js" ], "repository": { "type": "git", "url": "git+https://github.com/alibaba/formily.git" }, "types": "esm/index.d.ts", "bugs": { "url": "https://github.com/alibaba/formily/issues" }, "homepage": "https://github.com/alibaba/formily#readme", "engines": { "npm": ">=3.0.0" }, "scripts": { "start": "dumi dev", "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", "create:style": "ts-node create-style", "build:style": "ts-node build-style", "build:cjs": "tsc --project tsconfig.build.json", "build:esm": "tsc --project tsconfig.build.json --module es2015 --outDir esm", "build:umd": "rollup --config", "build:docs": "dumi build" }, "devDependencies": { "@umijs/plugin-sass": "^1.1.1", "dumi": "^1.1.0-rc.8" }, "peerDependencies": { "@ant-design/icons": "4.x", "@types/react": ">=16.8.0", "@types/react-dom": ">=16.8.0", "antd": "<=4.22.8", "react": ">=16.8.0", "react-dom": ">=16.8.0", "react-is": ">=16.8.0" }, "peerDependenciesMeta": { "@types/react": { "optional": true }, "@types/react-dom": { "optional": true } }, "dependencies": { "@dnd-kit/core": "^6.0.0", "@dnd-kit/sortable": "^7.0.0", "@formily/core": "2.3.7", "@formily/grid": "2.3.7", "@formily/json-schema": "2.3.7", "@formily/react": "2.3.7", "@formily/reactive": "2.3.7", "@formily/reactive-react": "2.3.7", "@formily/shared": "2.3.7", "classnames": "^2.2.6", "react-sticky-box": "^0.9.3" }, "publishConfig": { "access": "public" }, "gitHead": "ac79c196ae9324889aca5e0501146f9b37b04283" } ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/select/template-async.vue: -------------------------------------------------------------------------------- ```vue <template> <Form :form="form"> <Field name="linkage" title="联动选择框" :decorator="[FormItem]" :component="[ Select, { style: { width: '240px', }, }, ]" :dataSource="[ { label: '发请求1', value: 1 }, { label: '发请求2', value: 2 }, ]" /> <Field name="select" title="异步选择框" :decorator="[FormItem]" :component="[ Select, { style: { width: '240px', }, }, ]" /> <Submit @submit="onSubmit">提交</Submit> </Form> </template> <script> import { createForm, onFieldReact } from '@formily/core' import { Field } from '@formily/vue' import { action } from '@formily/reactive' import { Form, FormItem, Select, Submit, Reset } from '@formily/element' const useAsyncDataSource = (pattern, service) => { onFieldReact(pattern, (field) => { field.loading = true service(field).then( action.bound((data) => { field.dataSource = data field.loading = false }) ) }) } const form = createForm({ effects: () => { useAsyncDataSource('select', async (field) => { const linkage = field.query('linkage').get('value') if (!linkage) return [] return new Promise((resolve) => { setTimeout(() => { if (linkage === 1) { resolve([ { label: 'AAA', value: 'aaa', }, { label: 'BBB', value: 'ccc', }, ]) } else if (linkage === 2) { resolve([ { label: 'CCC', value: 'ccc', }, { label: 'DDD', value: 'ddd', }, ]) } }, 1500) }) }) }, }) export default { components: { Form, Field, Submit, Reset }, data() { return { form, FormItem, Select, } }, methods: { onSubmit(value) { console.log(value) }, }, } </script> ``` -------------------------------------------------------------------------------- /packages/antd/src/select-table/useFilterOptions.tsx: -------------------------------------------------------------------------------- ```typescript import * as React from 'react' import { isFn, isArr } from '@formily/shared' type IFilterOption = boolean | ((option: any, keyword: string) => boolean) function includes(test: React.ReactNode, search: string) { return toArray(test).join('').toUpperCase().includes(search) } function includesOption(option: any, search: string) { const searched = new Set() const _includesOption = (option: any) => { const keys = Object.keys(option || {}) return keys.some((key) => { if (key === '__level') { return false } const value = option[key] if (React.isValidElement(value)) return false if (key !== 'children' && !searched.has(value)) { if (typeof value === 'object') { searched.add(value) return _includesOption(value) } return includes(value, search) } return false }) } return _includesOption(option) } function toArray<T>(value: T | T[]): T[] { if (isArr(value)) { return value } return value !== undefined ? [value] : [] } const useFilterOptions = ( options: any[], searchValue?: string | string[], filterOption?: IFilterOption, checkStrictly?: boolean ) => React.useMemo(() => { if (!searchValue || filterOption === false) { return options } const filterFunc = isFn(filterOption) ? filterOption : (value: any, option: any) => includesOption(option, value.toUpperCase()) const doFilter = (arr: any[]) => { const filterArr: any[] = [] arr?.forEach((item) => { if (item?.children?.length) { const filterChildren = doFilter(item.children) if (filterChildren.length) { filterArr.push({ ...item, children: filterChildren }) } else if (filterFunc(searchValue, item) && checkStrictly !== false) { // 父子关系启用时,没有可用子元素,不添加父元素 filterArr.push({ ...item, children: [] }) } } else if (filterFunc(searchValue, item)) { filterArr.push(item) } }) return filterArr } return doFilter(options) }, [options, searchValue, filterOption]) export { useFilterOptions } ``` -------------------------------------------------------------------------------- /packages/next/src/select-table/useFilterOptions.tsx: -------------------------------------------------------------------------------- ```typescript import * as React from 'react' import { isFn, isArr } from '@formily/shared' type IFilterOption = boolean | ((option: any, keyword: string) => boolean) function includes(test: React.ReactNode, search: string) { return toArray(test).join('').toUpperCase().includes(search) } function includesOption(option: any, search: string) { const searched = new Set() const _includesOption = (option: any) => { const keys = Object.keys(option || {}) return keys.some((key) => { if (key === '__level') { return false } const value = option[key] if (React.isValidElement(value)) return false if (key !== 'children' && !searched.has(value)) { if (typeof value === 'object') { searched.add(value) return _includesOption(value) } return includes(value, search) } return false }) } return _includesOption(option) } function toArray<T>(value: T | T[]): T[] { if (isArr(value)) { return value } return value !== undefined ? [value] : [] } const useFilterOptions = ( options: any[], searchValue?: string | string[], filterOption?: IFilterOption, checkStrictly?: boolean ) => React.useMemo(() => { if (!searchValue || filterOption === false) { return options } const filterFunc = isFn(filterOption) ? filterOption : (value: any, option: any) => includesOption(option, value.toUpperCase()) const doFilter = (arr: any[]) => { const filterArr: any[] = [] arr?.forEach((item) => { if (item?.children?.length) { const filterChildren = doFilter(item.children) if (filterChildren.length) { filterArr.push({ ...item, children: filterChildren }) } else if (filterFunc(searchValue, item) && checkStrictly !== false) { // 父子关系启用时,没有可用子元素,不添加父元素 filterArr.push({ ...item, children: [] }) } } else if (filterFunc(searchValue, item)) { filterArr.push(item) } }) return filterArr } return doFilter(options) }, [options, searchValue, filterOption]) export { useFilterOptions } ``` -------------------------------------------------------------------------------- /packages/element/docs/.vuepress/config.js: -------------------------------------------------------------------------------- ```javascript const path = require('path') const utils = require('./util') const componentFiles = utils .getFiles(path.resolve(__dirname, '../guide')) .map((item) => item.replace(/(\.md)/g, '')) .filter((item) => !['el-form', 'el-form-item', 'index'].includes(item)) module.exports = { title: 'Element', description: 'Alibaba unified front-end form solution', dest: './doc-site', theme: '@vuepress-dumi/dumi', head: [ [ 'link', { rel: 'icon', href: '//img.alicdn.com/imgextra/i3/O1CN01XtT3Tv1Wd1b5hNVKy_!!6000000002810-55-tps-360-360.svg', }, ], [ 'link', { rel: 'stylesheet', href: 'https://esm.sh/element-ui/lib/theme-chalk/index.css', }, ], ], themeConfig: { logo: '//img.alicdn.com/imgextra/i2/O1CN01Kq3OHU1fph6LGqjIz_!!6000000004056-55-tps-1141-150.svg', nav: [ { text: 'Element', link: '/guide/', }, { text: '主站', link: 'https://formilyjs.org', }, { text: 'GITHUB', link: 'https://github.com/alibaba/formily', }, ], sidebar: { '/guide/': ['', ...componentFiles], }, lastUpdated: 'Last Updated', smoothScroll: true, }, plugins: [ 'vuepress-plugin-typescript', '@vuepress/back-to-top', '@vuepress/last-updated', '@vuepress-dumi/dumi-previewer', [ '@vuepress/medium-zoom', { selector: '.content__default :not(a) > img', }, ], ], configureWebpack: (config, isServer) => { return { resolve: { alias: { '@formily/element': path.resolve(__dirname, '../../src'), vue$: 'vue/dist/vue.esm.js', }, }, } }, chainWebpack: (config, isServer) => { config.module .rule('js') // Find the rule. .use('babel-loader') // Find the loader .tap((options) => Object.assign(options, { // Modifying options presets: [ [ '@vue/babel-preset-jsx', { vModel: false, compositionAPI: true, }, ], ], }) ) }, } ``` -------------------------------------------------------------------------------- /packages/vue/docs/demos/index.vue: -------------------------------------------------------------------------------- ```vue <template> <FormProvider :form="form"> <Field name="name" title="Name" required :decorator="[FormItem]" :component="[Input, { placeholder: 'Please Input' }]" /> <Field name="password" title="Password" required :decorator="[FormItem]" :component="[Input, { type: 'password', placeholder: 'Please Input' }]" :reactions="createPasswordEqualValidate('confirm_password')" /> <Field name="confirm_password" title="Confirm Password" required :decorator="[FormItem]" :component="[Input, { type: 'password', placeholder: 'Please Input' }]" :reactions="createPasswordEqualValidate('password')" /> <FormConsumer> <template #default="{ form }"> <div style="white-space: pre"> {{ JSON.stringify(form.values, null, 2) }} </div> </template> </FormConsumer> </FormProvider> </template> <script> import { Form, Input } from 'ant-design-vue' import { createForm, isVoidField, setValidateLanguage } from '@formily/core' import { FormProvider, FormConsumer, Field, connect, mapProps, } from '@formily/vue' import 'ant-design-vue/dist/antd.css' setValidateLanguage('en') const FormItem = connect( Form.Item, mapProps( { validateStatus: true, title: 'label', required: true }, (props, field) => { return { help: !isVoidField(field) ? field.selfErrors.length ? field.selfErrors : undefined : undefined, extra: field.description, } } ) ) export default { components: { FormProvider, FormConsumer, Field, }, data() { const form = createForm({ validateFirst: true }) const createPasswordEqualValidate = (equalName) => (field) => { if ( form.values.confirm_password && field.value && form.values[equalName] !== field.value ) { field.selfErrors = ['Password does not match Confirm Password.'] } else { field.selfErrors = [] } } return { FormItem, Input, form, createPasswordEqualValidate, } }, } </script> ``` -------------------------------------------------------------------------------- /packages/antd/docs/index.md: -------------------------------------------------------------------------------- ```markdown --- title: Formily-Alibaba unified front-end form solution order: 10 hero: title: Formily Antd desc: Formily Component System Based on Ant Design Encapsulation actions: - text: Home Site link: //formilyjs.org - text: Document link: /components features: - icon: https://img.alicdn.com/imgextra/i2/O1CN016i72sH1c5wh1kyy9U_!!6000000003550-55-tps-800-800.svg title: Easier To Use desc: Out of the box, rich cases - icon: https://img.alicdn.com/imgextra/i1/O1CN01bHdrZJ1rEOESvXEi5_!!6000000005599-55-tps-800-800.svg title: More Efficient desc: Stupid writing, super high performance - icon: https://img.alicdn.com/imgextra/i3/O1CN01xlETZk1G0WSQT6Xii_!!6000000000560-55-tps-800-800.svg title: More Professional desc: complete, flexible, elegant footer: Open-source MIT Licensed | Copyright © 2019-present<br />Powered by self --- ## Installation ```bash $ npm install --save antd moment $ npm install --save @formily/core @formily/react @formily/antd ``` ## Quick start ```tsx /** * defaultShowCode: true */ import React from 'react' import { NumberPicker, FormItem, Space } from '@formily/antd' import { createForm } from '@formily/core' import { FormProvider, FormConsumer, Field } from '@formily/react' const form = createForm() export default () => ( <FormProvider form={form}> <Space> <Field name="price" title="price" initialValue={5.2} decorator={[FormItem]} component={[ NumberPicker, { placeholder: 'Please enter', style: { width: 100, }, }, ]} /> <FormItem>×</FormItem> <Field name="count" title="quantity" initialValue={100} decorator={[FormItem]} component={[ NumberPicker, { placeholder: 'Please enter', style: { width: 100, }, }, ]} /> <FormConsumer> {(form) => ( <FormItem>={` ${form.values.price * form.values.count}元`}</FormItem> )} </FormConsumer> </Space> </FormProvider> ) ``` ``` -------------------------------------------------------------------------------- /packages/next/docs/index.md: -------------------------------------------------------------------------------- ```markdown --- title: Formily-Alibaba unified front-end form solution order: 10 hero: title: Formily Fusion desc: Formily Component System Based on Alibaba Fusion Encapsulation actions: - text: Home Site link: //formilyjs.org - text: Document link: /components features: - icon: https://img.alicdn.com/imgextra/i2/O1CN016i72sH1c5wh1kyy9U_!!6000000003550-55-tps-800-800.svg title: Easier To Use desc: Out of the box, rich cases - icon: https://img.alicdn.com/imgextra/i1/O1CN01bHdrZJ1rEOESvXEi5_!!6000000005599-55-tps-800-800.svg title: More Efficient desc: Stupid writing, super high performance - icon: https://img.alicdn.com/imgextra/i3/O1CN01xlETZk1G0WSQT6Xii_!!6000000000560-55-tps-800-800.svg title: More Professional desc: complete, flexible, elegant footer: Open-source MIT Licensed | Copyright © 2019-present<br />Powered by self --- ## Installation ```bash $ npm install --save @alifd/next moment $ npm install --save @formily/core @formily/react @formily/next ``` ## Quick start ```tsx /** * defaultShowCode: true */ import React from 'react' import { NumberPicker, FormItem, Space } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, FormConsumer, Field } from '@formily/react' const form = createForm() export default () => ( <FormProvider form={form}> <Space> <Field name="price" title="price" initialValue={5.2} decorator={[FormItem]} component={[ NumberPicker, { placeholder: 'Please enter', style: { width: 100, }, }, ]} /> <FormItem>×</FormItem> <Field name="count" title="quantity" initialValue={100} decorator={[FormItem]} component={[ NumberPicker, { placeholder: 'Please enter', style: { width: 100, }, }, ]} /> <FormConsumer> {(form) => ( <FormItem>={` ${form.values.price * form.values.count}元`}</FormItem> )} </FormConsumer> </Space> </FormProvider> ) ``` ``` -------------------------------------------------------------------------------- /packages/core/docs/api/models/ArrayField.md: -------------------------------------------------------------------------------- ```markdown --- order: 2 --- # ArrayField Call the ArrayField model returned by [createArrayField](/api/models/form#createarrayfield). Because ArrayField is inherited from the [Field](/api/models/field) model, most APIs can refer to the Field model. This document only explains the extension method ## Method <Alert> Note: The following method not only updates the array data, but also transposes the state of the child nodes. If you don't want to automatically transpose the state, you can directly call the `setValue` method to overwrite the update value. </Alert> ### push #### Description Append an element to the end of the array and trigger onInput #### Signature ```ts interface push { (...items: any[]): Promise<void> } ``` ### pop #### Description Pop the last element of the array and trigger onInput #### Signature ```ts interface pop { (): Promise<void> } ``` ### insert #### Description Insert an element into the array and trigger onInput #### Signature ```ts interface insert { (index: number, ...items: any[]): Promise<void> } ``` ### remove #### Description Delete the array element and trigger onInput #### Signature ```ts interface remove { (index: number): Promise<void> } ``` ### shift #### Description Pop the first element of the array and trigger onInput #### Signature ```ts interface shift { (): Promise<void> } ``` ### unshift #### Description Append an element to the head of the array and trigger onInput #### Signature ```ts interface unshift { (...items: any[]): Promise<void> } ``` ### move #### Description Move the array element and trigger onInput #### Signature ```ts interface move { (fromIndex: number, toIndex: number): Promise<void> } ``` ### moveUp #### Description Move the array element up and trigger onInput #### Signature ```ts interface moveUp { (index: number): Promise<void> } ``` ### moveDown #### Description Move the array element down and trigger onInput #### Signature ```ts interface moveDown { (index: number): Promise<void> } ``` ## Types of ### IArrayFieldState The main attributes refer to [IFieldState](/api/models/field#ifieldstate), but the data type of value is required to be an array ``` -------------------------------------------------------------------------------- /packages/react/docs/guide/index.md: -------------------------------------------------------------------------------- ```markdown # Introduction The core positioning of @formily/react is to realize a state binding relationship between ViewModel ([@formily/core](//core.formilyjs.org)) and components. It is not responsible for managing form data and form verification. It is only A rendering glue layer, but such a layer of glue is not dirty, it will elegantly decouple a lot of dirty logic and become maintainable. ## Ultra high performance With the responsive model of [@formily/core](//core.formilyjs.org), @formily/react can obtain super high performance advantages without any optimization, relying on tracking, accurate updates, on-demand rendering, let us The form of really does only need to focus on business logic, without considering performance issues. ## Out of the box @formily/react provides a series of React components, such as Field/ArrayField/ObjectField/VoidField. When using it, users only need to pass the component property to the Field component (supporting two-way binding conventions such as value/onChange). Quick access to @formily/react, the access cost is extremely low. ## JSON Schema Driver @formily/react provides protocol-driven components such as SchemaField. It is also driven by the standard JSON-Schema, so that form development can become more dynamic and configurable. What's more, we can achieve a protocol that allows multiple terminals Render the form. ## Scene Reuse With the help of protocol-driven capabilities, we can abstract a protocol fragment carrying business logic into a scene component to help users develop efficiently in certain scenes, such as scene components such as FormTab and FormStep. ## Smart tips Because formily is a complete Typescript project, users can develop on VSCode or WebStorm to get the maximum intelligent prompt experience  ## Status observable 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  ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/form-dialog/json-schema.vue: -------------------------------------------------------------------------------- ```vue <template> <Button @click="handleOpen">点击打开表单</Button> </template> <script> import { FormDialog, FormLayout, FormItem, Input } from '@formily/element' import { Button } from 'element-ui' import { createSchemaField } from '@formily/vue' const { SchemaField } = createSchemaField({ components: { FormItem, Input, }, }) // 弹框表单组件 const DialogForm = { data() { const schema = { type: 'object', properties: { aaa: { type: 'string', title: '输入框1', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', }, bbb: { type: 'string', title: '输入框2', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', }, ccc: { type: 'string', title: '输入框3', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', }, ddd: { type: 'string', title: '输入框4', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', }, }, } return { schema, } }, render(h) { return ( <FormLayout labelCol={6} wrapperCol={10}> <SchemaField schema={this.schema} /> <FormDialog.Footer> <span style={{ marginLeft: '4px' }}>扩展文案</span> </FormDialog.Footer> </FormLayout> ) }, } export default { components: { Button }, data() { return {} }, methods: { handleOpen() { FormDialog('弹框表单', DialogForm) .forOpen((payload, next) => { setTimeout(() => { next({ initialValues: { aaa: '123', }, }) }, 1000) }) .forConfirm((payload, next) => { setTimeout(() => { console.log(payload) next(payload) }, 1000) }) .forCancel((payload, next) => { setTimeout(() => { console.log(payload) next(payload) }, 1000) }) .open() .then(console.log) .catch(console.error) }, }, } </script> ``` -------------------------------------------------------------------------------- /packages/reactive-test-cases-for-react18/src/index.js: -------------------------------------------------------------------------------- ```javascript import React, { startTransition, useState } from 'react' import ReactDOM from 'react-dom' import MySlowList from './MySlowList' import { observable } from '@formily/reactive' import { observer } from '@formily/reactive-react' const App = observer(function App() { const [text, setText] = useState('hello') const [slowText] = useState(() => observable({ text, }) ) function handleChange(e) { setText(e.target.value) startTransition(() => { slowText.text = e.target.value }) } return ( <div className="App"> <h1>Concurrent React</h1> <label> Type into the input: <input value={text} onChange={handleChange} /> </label> <p> You entered: <b>{text}</b> </p> <p style={{ background: text !== slowText.text ? 'yellow' : '', }} > But we are showing: <Indicator text={slowText} /> </p> <p style={{ background: text !== slowText.text ? 'yellow' : '', }} > But we are showing: <Indicator text={slowText} /> </p> <p style={{ background: text !== slowText.text ? 'yellow' : '', }} > But we are showing: <Indicator text={slowText} /> </p> <p style={{ background: text !== slowText.text ? 'yellow' : '', }} > But we are showing: <Indicator text={slowText} /> </p> <p> Even though{' '} <b> each list item in this demo artificially blocks the main thread for 3 milliseconds </b> , the app is able to stay responsive. </p> <hr /> <MySlowList text={slowText} /> </div> ) }) let Indicator = observer(({ text }) => { const [hover, setHover] = useState(false) return ( <p onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)} style={{ border: '1px solid black', padding: 20, background: hover ? 'yellow' : '', }} > But we are showing: <b>{text.text}</b> </p> ) }) const rootElement = document.getElementById('root') ReactDOM.createRoot(rootElement).render(<App />) ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/preview-text/extend.vue: -------------------------------------------------------------------------------- ```vue <template> <Form :labelCol="6" :wrapperCol="10" :form="form" :previewTextPlaceholder="vnode" > <SchemaField> <SchemaStringField x-decorator="FormItem" title="文本预览" x-component="Input" default="Hello world" /> <SchemaStringField x-decorator="FormItem" title="选择项预览" x-component="PreviewText.Select" :x-component-props="{ multiple: true, }" :default="['123', '222']" :enum="[ { label: 'A111', value: '123' }, { label: 'A222', value: '222', }, ]" /> <SchemaStringField x-decorator="FormItem" title="日期预览" x-component="PreviewText.DatePicker" default="2020-11-23 22:15:20" /> <SchemaStringField x-decorator="FormItem" title="日期范围预览" x-component="PreviewText.DatePicker" :default="['2020-11-23 22:15:20', '2020-11-24 22:15:20']" /> <SchemaStringField x-decorator="FormItem" title="Cascader预览" x-component="PreviewText.Cascader" :default="['hangzhou', 'yuhang']" :enum="[ { label: '杭州', value: 'hangzhou' }, { label: '余杭', value: 'yuhang' }, ]" /> </SchemaField> <FormButtonGroup alignFormItem> <Button @click=" () => { form.setState((state) => { state.editable = !state.editable }) } " >切换阅读态</Button > </FormButtonGroup> </Form> </template> <script> import { h } from 'vue-demi' import { createForm } from '@formily/core' import { createSchemaField } from '@formily/vue' import { Form, FormItem, Input, PreviewText, FormButtonGroup, } from '@formily/element' import { Button } from 'element-ui' const fields = createSchemaField({ components: { FormItem, Input, PreviewText, }, }) export default { components: { Form, FormButtonGroup, Button, ...fields, }, data() { const form = createForm() return { form, vnode: () => h('div', {}, '123'), } }, mounted() {}, } </script> ``` -------------------------------------------------------------------------------- /packages/core/docs/api/models/Query.md: -------------------------------------------------------------------------------- ```markdown --- order: 5 --- # Query The Query object returned by calling the query method in the [Form](/api/models/form#query) or [Field](/api/models/field#query) instance ## Method ### take #### Description Extract the first result from the query result set Note that there must be a corresponding node to be able to read #### Signature ```ts interface take { (): GeneralField <Result>(getter: (field: GeneralField, address: FormPath) => Result): Result } ``` ### map #### Description Traverse and map the query result set Note that there must be a corresponding node to traverse #### Signature ```ts interface map { (): GeneralField[] <Result>( mapper?: (field: GeneralField, address: FormPath) => Result ): Result[] } ``` ### forEach #### Description Traverse the query result set Note that there must be a corresponding node to traverse #### Signature ```ts interface forEach { <Result>(eacher: (field: GeneralField, address: FormPath) => Result): void } ``` ### reduce #### Description Perform a reduce operation on the query result set Note that there must be a corresponding node to traverse #### Signature ```ts interface reduce { <Result>( reducer: (value: Result, field: GeneralField, address: FormPath) => Result, initial?: Result ): Result } ``` ### get #### Description Find the first result from the query result set and read its attributes Note that there must be a corresponding node to be able to read #### Signature ```ts interface get { <K extends keyof IGeneralFieldState>(key: K): IGeneralFieldState[K] } ``` ### getIn #### Description Find the first result from the query result set and read its attributes, support [FormPathPattern](/api/entry/form-path#formpathpattern) path syntax Note that there must be a corresponding node to be able to read #### Signature ```ts interface getIn { (pattern?: FormPathPattern): any } ``` ### value #### Description Query the specified path value, not limited to Field nodes #### Signature ```ts interface value { (): any } ``` ### initialValue #### Description Query the initial value of the specified path, not limited to the Field node #### Signature ```ts interface initialValue { (): any } ``` ``` -------------------------------------------------------------------------------- /packages/next/src/array-table/main.scss: -------------------------------------------------------------------------------- ```scss @import '~@alifd/next/lib/core/index-noreset.scss'; @import '~@alifd/next/lib/form/scss/variable.scss'; $array-table-prefix-cls: '#{$css-prefix}formily-array-table'; .#{$array-table-prefix-cls} { .#{$array-table-prefix-cls}-pagination { display: flex; justify-content: center; margin: 10px 0; .#{$array-table-prefix-cls}-status-select { min-width: 60px; margin-right: 6px; width: auto !important; .#{$css-prefix}input { min-width: 60px; } &.has-error { .#{$css-prefix}input { border-color: red !important; } } } } .#{$css-prefix}table { .#{$css-prefix}table-cell-wrapper { overflow: visible; word-break: normal; } td { visibility: visible; .#{$css-prefix}formily-item:not(.#{$css-prefix}formily-item-feedback-layout-popover) { margin-bottom: 0 !important; position: relative; .#{$css-prefix}formily-item-help { &.#{$css-prefix}formily-item-error-help { color: $form-error-color; } &.#{$css-prefix}formily-item-warning-help { color: $form-warning-color; } & { min-width: 30px; position: absolute; font-size: 12px; top: 100%; width: 100%; z-index: 1; border-radius: 3px; background-color: #fff; border: 1px solid #eee; padding: 6px 8px; margin-top: 6px; transform: translateY(0); opacity: 1; } &:after { content: ' '; background-color: #fff; position: absolute; display: block; width: 5px; height: 5px; pointer-events: none; -webkit-transform: rotate(45deg); top: -4px; transform: rotate(45deg); box-sizing: content-box !important; border-left: 1px solid #eee; border-top: 1px solid #eee; z-index: -1; } } } } } .#{$array-table-prefix-cls}-sort-helper { background: #fff; border: 1px solid #eee; z-index: 10; } } ``` -------------------------------------------------------------------------------- /packages/next/src/password/index.tsx: -------------------------------------------------------------------------------- ```typescript import React from 'react' import { connect, mapProps, mapReadPretty } from '@formily/react' import { Input } from '@alifd/next' import { PasswordProps } from '@alifd/next/lib/input' import { PasswordStrength } from './PasswordStrength' import { PreviewText } from '../preview-text' import { mapStatus, mapSize } from '../__builtins__' export interface IPasswordProps extends PasswordProps { checkStrength: boolean } export const Password = connect( (props: IPasswordProps) => { const { value, className, checkStrength, ...others } = props const blockStyle: React.CSSProperties = { position: 'absolute', zIndex: 1, height: 8, top: 0, background: '#fff', width: 1, transform: 'translate(-50%, 0)', } return ( <span className={className}> <Input.Password {...others} style={{ ...others.style, width: '100%' }} value={value} /> {checkStrength && ( <PasswordStrength value={String(value)}> {(score) => { return ( <div style={{ background: '#e0e0e0', marginBottom: 3, position: 'relative', }} > <div style={{ ...blockStyle, left: '20%' }} /> <div style={{ ...blockStyle, left: '40%' }} /> <div style={{ ...blockStyle, left: '60%' }} /> <div style={{ ...blockStyle, left: '80%' }} /> <div style={{ position: 'relative', backgroundImage: '-webkit-linear-gradient(left, #ff5500, #ff9300)', transition: 'all 0.35s ease-in-out', height: 8, width: '100%', marginTop: 5, clipPath: `polygon(0 0,${score}% 0,${score}% 100%,0 100%)`, }} /> </div> ) }} </PasswordStrength> )} </span> ) }, mapProps(mapSize, mapStatus), mapReadPretty(PreviewText.Input) ) export default Password ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/editable/json-schema.vue: -------------------------------------------------------------------------------- ```vue <template> <FormProvider :form="form"> <SchemaField :schema="schema" /> <FormButtonGroup> <Submit @submit="log">提交</Submit> </FormButtonGroup> </FormProvider> </template> <script> import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/vue' import { FormButtonGroup, FormItem, Submit, Input, DatePicker, Editable, } from '@formily/element' const { SchemaField } = createSchemaField({ components: { FormItem, Input, DatePicker, Editable, }, }) const schema = { type: 'object', properties: { date: { type: 'string', title: '日期', 'x-decorator': 'Editable', 'x-component': 'DatePicker', }, input: { type: 'string', title: '输入框', 'x-decorator': 'Editable', 'x-component': 'Input', }, void: { type: 'void', title: '虚拟节点容器', 'x-component': 'Editable.Popover', 'x-reactions': "{{(field) => field.title = field.query('.void.date2').get('value') || field.title}}", properties: { date2: { type: 'string', title: '日期', 'x-decorator': 'FormItem', 'x-component': 'DatePicker', }, input2: { type: 'string', title: '输入框', 'x-decorator': 'FormItem', 'x-component': 'Input', }, }, }, iobject: { type: 'object', title: '对象节点容器', 'x-component': 'Editable.Popover', 'x-reactions': '{{(field) => field.title = field.value && field.value.date || field.title}}', properties: { date: { type: 'string', title: '日期', 'x-decorator': 'FormItem', 'x-component': 'DatePicker', }, input: { type: 'string', title: '输入框', 'x-decorator': 'FormItem', 'x-component': 'Input', }, }, }, }, } export default { components: { FormButtonGroup, FormProvider, Submit, SchemaField, }, data() { const form = createForm() return { schema, form, } }, methods: { log(values) { console.log(values) }, }, } </script> <style lang="scss" scoped></style> ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/select/json-schema-async.vue: -------------------------------------------------------------------------------- ```vue <template> <Form :form="form"> <SchemaField :schema="schema" :scope="{ useAsyncDataSource, loadData }" /> <Submit @submit="onSubmit">提交</Submit> </Form> </template> <script> import { createForm } from '@formily/core' import { createSchemaField } from '@formily/vue' import { action } from '@formily/reactive' import { Form, FormItem, Select, Submit, Reset } from '@formily/element' const schema = { type: 'object', properties: { linkage: { type: 'string', title: '联动选择框', enum: [ { label: '发请求1', value: 1, }, { label: '发请求2', value: 2, }, ], 'x-decorator': 'FormItem', 'x-component': 'Select', 'x-component-props': { style: 'width: 240px;', }, }, select: { type: 'string', title: '异步选择框', 'x-decorator': 'FormItem', 'x-component': 'Select', 'x-component-props': { style: 'width: 240px;', }, 'x-reactions': ['{{useAsyncDataSource(loadData)}}'], }, }, } const useAsyncDataSource = (service) => (field) => { field.loading = true service(field).then( action.bound((data) => { field.dataSource = data field.loading = false }) ) } const loadData = async (field) => { const linkage = field.query('linkage').get('value') if (!linkage) return [] return new Promise((resolve) => { setTimeout(() => { if (linkage === 1) { resolve([ { label: 'AAA', value: 'aaa', }, { label: 'BBB', value: 'ccc', }, ]) } else if (linkage === 2) { resolve([ { label: 'CCC', value: 'ccc', }, { label: 'DDD', value: 'ddd', }, ]) } }, 1500) }) } const form = createForm() const { SchemaField } = createSchemaField({ components: { FormItem, Select, }, }) export default { components: { Form, SchemaField, Submit, Reset }, data() { return { form, schema, } }, methods: { useAsyncDataSource, loadData, onSubmit(value) { console.log(value) }, }, } </script> ``` -------------------------------------------------------------------------------- /packages/reactive/src/types.ts: -------------------------------------------------------------------------------- ```typescript import { ArraySet } from './array' export * from './tree' export type PropertyKey = string | number | symbol export type OperationType = | 'add' | 'delete' | 'clear' | 'set' | 'get' | 'iterate' | 'has' export interface IOperation { target?: any oldTarget?: any key?: PropertyKey value?: any oldValue?: any type?: OperationType receiver?: any } export interface IChange { key?: PropertyKey path?: ObservablePath value?: any oldValue?: any type?: OperationType } export interface IEffectQueueItem { dispose?: void | Dispose deps?: any[] } export interface IMemoQueueItem { value?: any deps?: any[] } export interface IVisitor<Value = any, Target = any> { target?: Target key?: PropertyKey value?: Value } export type Annotation = (...args: any[]) => any export type Annotations<T = any> = { [key in keyof T]?: Annotation } export type ObservableListener = (operation: IOperation) => void export type ObservablePath = Array<string | number> export type Dispose = () => void export type Effect = () => void | Dispose export type Reaction = ((...args: any[]) => any) & { _boundary?: number _name?: string _isComputed?: boolean _dirty?: boolean _context?: any _disposed?: boolean _property?: PropertyKey _computesSet?: ArraySet<Reaction> _reactionsSet?: ArraySet<ReactionsMap> _scheduler?: (reaction: Reaction) => void _memos?: { queue: IMemoQueueItem[] cursor: number } _effects?: { queue: IEffectQueueItem[] cursor: number } } export type ReactionsMap = Map<PropertyKey, ArraySet<Reaction>> export interface IReactionOptions<T> { name?: string equals?: (oldValue: T, newValue: T) => boolean fireImmediately?: boolean } export type BindFunction<F = (...args: any[]) => any> = ( callback?: F, context?: any ) => F export type BoundaryFunction = <F extends (...args: any) => any>( fn?: F ) => ReturnType<F> export interface IBoundable { bound?: <T extends (...args: any[]) => any>(callback: T, context?: any) => T //高阶绑定 } export interface IAction extends IBoundable { <T>(callback?: () => T): T //原地action scope?: (<T>(callback?: () => T) => T) & IBoundable //原地局部action } export interface IBatch extends IAction { endpoint?: (callback?: () => void) => void } ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/select/markup-schema-async.vue: -------------------------------------------------------------------------------- ```vue <template> <Form :form="form"> <SchemaField> <SchemaNumberField name="linkage" title="联动选择框" x-decorator="FormItem" x-component="Select" :enum="[ { label: '发请求1', value: 1 }, { label: '发请求2', value: 2 }, ]" :x-component-props="{ style: { width: '240px', }, }" /> <SchemaStringField name="select" title="异步选择框" x-decorator="FormItem" x-component="Select" :x-component-props="{ style: { width: '240px', }, }" /> </SchemaField> <Submit @submit="onSubmit">提交</Submit> </Form> </template> <script> import { createForm, onFieldReact } from '@formily/core' import { createSchemaField } from '@formily/vue' import { action } from '@formily/reactive' import { Form, FormItem, Select, Submit, Reset } from '@formily/element' const useAsyncDataSource = (pattern, service) => { onFieldReact(pattern, (field) => { field.loading = true service(field).then( action.bound((data) => { field.dataSource = data field.loading = false }) ) }) } const form = createForm({ effects: () => { useAsyncDataSource('select', async (field) => { const linkage = field.query('linkage').get('value') if (!linkage) return [] return new Promise((resolve) => { setTimeout(() => { if (linkage === 1) { resolve([ { label: 'AAA', value: 'aaa', }, { label: 'BBB', value: 'ccc', }, ]) } else if (linkage === 2) { resolve([ { label: 'CCC', value: 'ccc', }, { label: 'DDD', value: 'ddd', }, ]) } }, 1500) }) }) }, }) const fields = createSchemaField({ components: { FormItem, Select, }, }) export default { components: { Form, ...fields, Submit, Reset }, data() { return { form, } }, methods: { onSubmit(value) { console.log(value) }, }, } </script> ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/editable/markup-schema.vue: -------------------------------------------------------------------------------- ```vue <template> <FormProvider :form="form"> <SchemaField> <SchemaStringField name="date" title="日期" x-decorator="Editable" x-component="DatePicker" /> <SchemaStringField name="input" title="输入框" x-decorator="Editable" x-component="Input" /> <SchemaVoidField name="void" title="虚拟节点容器" x-component="Editable.Popover" :x-reactions=" (field) => { field.title = field.query('.void.date2').get('value') || field.title } " > <SchemaStringField name="date2" title="日期" x-decorator="FormItem" x-component="DatePicker" /> <SchemaStringField name="input2" title="输入框" x-decorator="FormItem" x-component="Input" /> </SchemaVoidField> <SchemaObjectField name="object" title="对象节点容器" x-component="Editable.Popover" :x-reactions=" (field) => { field.title = (field.value && field.value.date) || field.title } " > <SchemaStringField name="date" title="日期" x-decorator="FormItem" x-component="DatePicker" /> <SchemaStringField name="input" title="输入框" x-decorator="FormItem" x-component="Input" /> </SchemaObjectField> </SchemaField> <FormButtonGroup> <Submit @submit="log">提交</Submit> </FormButtonGroup> </FormProvider> </template> <script> import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/vue' import { FormButtonGroup, FormItem, Submit, Input, DatePicker, Editable, } from '@formily/element' import { Button } from 'element-ui' const SchemaField = createSchemaField({ components: { FormItem, Input, DatePicker, Editable, }, }) export default { components: { FormButtonGroup, FormProvider, Button, Submit, ...SchemaField, }, data() { const form = createForm() return { form, } }, methods: { log(values) { console.log(values) }, }, } </script> <style lang="scss" scoped></style> ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/date-picker/json-schema.vue: -------------------------------------------------------------------------------- ```vue <template> <Form :form="form"> <SchemaField :schema="schema" /> <Submit @submit="onSubmit">提交</Submit> </Form> </template> <script> import { createForm } from '@formily/core' import { createSchemaField } from '@formily/vue' import { Form, FormItem, DatePicker, Submit } from '@formily/element' const schema = { type: 'object', properties: { date: { type: 'string', title: '普通日期', 'x-decorator': 'FormItem', 'x-component': 'DatePicker', }, week: { type: 'string', title: '周', 'x-decorator': 'FormItem', 'x-component': 'DatePicker', 'x-component-props': { type: 'week', }, }, month: { type: 'string', title: '月', 'x-decorator': 'FormItem', 'x-component': 'DatePicker', 'x-component-props': { type: 'month', }, }, year: { type: 'string', title: '年', 'x-decorator': 'FormItem', 'x-component': 'DatePicker', 'x-component-props': { type: 'year', }, }, dateTime: { type: 'string', title: '日期时间', 'x-decorator': 'FormItem', 'x-component': 'DatePicker', 'x-component-props': { type: 'datetime', }, }, dates: { type: 'array', title: '多个日期', 'x-decorator': 'FormItem', 'x-component': 'DatePicker', 'x-component-props': { type: 'dates', }, }, dateRange: { type: 'string', title: '日期范围', 'x-decorator': 'FormItem', 'x-component': 'DatePicker', 'x-component-props': { type: 'daterange', }, }, monthRange: { type: 'string', title: '月范围', 'x-decorator': 'FormItem', 'x-component': 'DatePicker', 'x-component-props': { type: 'monthrange', }, }, dateTimeRange: { type: 'string', title: '日期时间范围', 'x-decorator': 'FormItem', 'x-component': 'DatePicker', 'x-component-props': { type: 'datetimerange', }, }, }, } const form = createForm() const { SchemaField } = createSchemaField({ components: { FormItem, DatePicker, }, }) export default { components: { Form, SchemaField, Submit }, data() { return { form, schema, } }, methods: { onSubmit(value) { console.log(value) }, }, } </script> ``` -------------------------------------------------------------------------------- /packages/reactive/docs/api/autorun.md: -------------------------------------------------------------------------------- ```markdown # autorun ## Description Receive a tracker function, if there is observable data in the function, the tracker function will be executed repeatedly when the data changes ## Signature ```ts interface autorun { (tracker: () => void, name?: string): void } ``` ## Example ```ts import { observable, autorun } from '@formily/reactive' const obs = observable({}) const dispose = autorun(() => { console.log(obs.aa) }) obs.aa = 123 dispose() ``` ## autorun.memo ### Description Used in autorun to create persistent reference data, only re-execute memo internal functions due to dependency changes Note: Please do not use it in If/For statements, because it depends on the execution order to bind the current autorun ### Signature ```ts interface memo<T> { (callback: () => T, dependencies: any[] = []): T } ``` Note: The default dependency is `[]`, that is, if the dependency is not passed, it means that the second time will never be executed ### Example ```ts import { observable, autorun } from '@formily/reactive' const obs1 = observable({ aa: 0, }) const dispose = autorun(() => { const obs2 = autorun.memo(() => observable({ bb: 0, }) ) console.log(obs1.aa, obs2.bb++) }) obs1.aa++ obs1.aa++ obs1.aa++ //Execute four times, the output result is /** * 0 0 * 1 1 * twenty two * 3 3 */ dispose() ``` ## autorun.effect ### Description In autorun, it is used to respond to the next micro task timing of autorun's first execution and the dispose of responding to autorun Note: Please do not use it in If/For statements, because it depends on the execution order to bind the current autorun ### Signature ```ts interface effect { (callback: () => void | (() => void), dependencies: any[] = [{}]): void } ``` Note: The default dependency is `[{}]`, that is, if the dependency is not passed, the representative will continue to execute, because the internal dirty check is a shallow comparison ### Example ```ts import { observable, autorun } from '@formily/reactive' const obs1 = observable({ aa: 0, }) const dispose = autorun(() => { const obs2 = autorun.memo(() => observable({ bb: 0, }) ) console.log(obs1.aa, obs2.bb++) autorun.effect(() => { obs2.bb++ }, []) }) obs1.aa++ obs1.aa++ obs1.aa++ //Execute five times, the output result is /** * 0 0 * 1 1 * twenty two * 3 3 * 3 5 */ dispose() ``` ``` -------------------------------------------------------------------------------- /packages/reactive/docs/api/observable.md: -------------------------------------------------------------------------------- ```markdown # observable > Mainly used to create observable objects with different responsive behaviors, and can be used as an annotation to define to mark responsive attributes ## observable/observable.deep ### Description Create deep hijacking responsive objects ### Signature ```ts interface observable<T extends object> { (target: T): T } interface deep<T extends object> { (target: T): T } ``` ### Example ```ts import { observable, autorun } from '@formily/reactive' const obs = observable({ aa: { bb: 123, }, }) autorun(() => { console.log(obs.aa.bb) }) obs.aa.bb = 321 ``` ## observable.shallow ### Description Create shallow hijacking responsive objects, that is, only respond to the first-level attribute operations of the target object ### Signature ```ts interface shallow<T extends object> { (target: T): T } ``` ### Example ```ts import { observable, autorun } from '@formily/reactive' const obs = observable.shallow({ aa: { bb: 111, }, }) autorun(() => { console.log(obs.aa.bb) }) obs.aa.bb = 222 // will not respond obs.aa = { bb: 333 } // can respond ``` ## observable.computed ### Description Create a calculation buffer ### Signature ```ts interface computed { <T extends () => any>(target: T): { value: ReturnType<T> } <T extends { get?: () => any; set?: (value: any) => void }>(target: T): { value: ReturnType<T['get']> } } ``` ### Example ```ts import { observable, autorun } from '@formily/reactive' const obs = observable({ aa: 11, bb: 22, }) const computed = observable.computed(() => obs.aa + obs.bb) autorun(() => { console.log(computed.value) }) obs.aa = 33 ``` ## observable.ref ### Description Create reference hijacking responsive objects ### Signature ```ts interface ref<T extends object> { (target: T): { value: T } } ``` ### Example ```ts import { observable, autorun } from '@formily/reactive' const ref = observable.ref(1) autorun(() => { console.log(ref.value) }) ref.value = 2 ``` ## observable.box ### Description Similar to ref, except that the data is read and written through the get/set method ### Signature ```ts interface box<T extends object> { (target: T): { get: () => T; set: (value: T) => void } } ``` ### Example ```ts import { observable, autorun } from '@formily/reactive' const box = observable.box(1) autorun(() => { console.log(box.get()) }) box.set(2) ``` ``` -------------------------------------------------------------------------------- /packages/grid/src/observer.ts: -------------------------------------------------------------------------------- ```typescript const isHTMLElement = (node: Node): node is HTMLElement => node.nodeType === 1 type ChildNode = { element?: HTMLElement observer?: MutationObserver dispose?: () => void } export class ChildListMutationObserver { observer: MutationObserver callback: MutationCallback childList: ChildNode[] = [] init: MutationObserverInit constructor(callback: MutationCallback) { this.callback = callback this.observer = new MutationObserver(this.handler) } observeChildList(element: HTMLElement) { Array.from(element.children).forEach((node: HTMLElement) => { this.addObserver(node) }) } addObserver(element: HTMLElement) { const child = this.childList.find((t) => t.element === element) if (!child) { const childIndex = this.childList.length const child = { element, observer: new MutationObserver(this.callback), dispose: () => { if (child.observer) { child.observer.disconnect() delete child.observer this.childList.splice(childIndex, 1) } }, } child.observer.observe(child.element, { ...this.init, subtree: false, childList: false, characterData: false, characterDataOldValue: false, attributeOldValue: false, }) this.childList.push(child) } } removeObserver(element: HTMLElement) { const child = this.childList.find((t) => t.element === element) if (child) { child.dispose?.() } } handler = (mutations: MutationRecord[]) => { mutations.forEach((mutation) => { if (mutation.type === 'childList') { mutation.addedNodes.forEach((node) => { if (isHTMLElement(node)) { this.addObserver(node) } }) mutation.removedNodes.forEach((node) => { if (isHTMLElement(node)) { this.removeObserver(node) } }) } }) this.callback(mutations, this.observer) } observe = (element: HTMLElement, init?: MutationObserverInit) => { this.init = init this.observeChildList(element) this.observer.observe(element, { ...this.init, subtree: false, childList: true, characterData: false, characterDataOldValue: false, attributeOldValue: false, }) } disconnect = () => { this.observer.disconnect() } } ``` -------------------------------------------------------------------------------- /packages/vue/src/vue2-components.ts: -------------------------------------------------------------------------------- ```typescript // This file just converts types import * as components from './components' import type Vue from 'vue' import type { VueConstructor } from 'vue' import type { IVoidFieldProps, IArrayFieldProps, IObjectFieldProps, IFieldProps, IRecursionFieldProps, IProviderProps, ISchemaMarkupFieldProps, ISchemaFieldProps, ISchemaFieldVueFactoryOptions, ISchemaTypeFieldProps, SchemaVueComponents, } from './types' const { Field: _Field, ArrayField: _ArrayField, FormConsumer: _FormConsumer, FormProvider: _FormProvider, ObjectField: _ObjectField, RecursionField: _RecursionField, VoidField: _VoidField, createSchemaField: _createSchemaField, } = components type DefineComponent<Props> = Vue & VueConstructor & Props type SchemaFieldComponents = { SchemaField: DefineComponent<Omit<ISchemaFieldProps, 'name' | 'components'>> SchemaMarkupField: DefineComponent<ISchemaMarkupFieldProps> SchemaStringField: DefineComponent<ISchemaTypeFieldProps> SchemaObjectField: DefineComponent<ISchemaTypeFieldProps> SchemaArrayField: DefineComponent<ISchemaTypeFieldProps> SchemaBooleanField: DefineComponent<ISchemaTypeFieldProps> SchemaDateField: DefineComponent<ISchemaTypeFieldProps> SchemaDateTimeField: DefineComponent<ISchemaTypeFieldProps> SchemaVoidField: DefineComponent<ISchemaTypeFieldProps> SchemaNumberField: DefineComponent<ISchemaTypeFieldProps> } type CreateSchemaField< Components extends SchemaVueComponents = SchemaVueComponents > = ( options: ISchemaFieldVueFactoryOptions<Components> ) => SchemaFieldComponents const Field = _Field as unknown as DefineComponent<Omit<IFieldProps, 'name'>> const ArrayField = _ArrayField as unknown as DefineComponent< Omit<IArrayFieldProps, 'name'> > const ObjectField = _ObjectField as unknown as DefineComponent< Omit<IObjectFieldProps, 'name'> > const VoidField = _VoidField as unknown as DefineComponent< Omit<IVoidFieldProps, 'name'> > const RecursionField = _RecursionField as unknown as DefineComponent< Omit<IRecursionFieldProps, 'name'> > const FormConsumer = _FormConsumer as unknown as DefineComponent<{}> const FormProvider = _FormProvider as unknown as DefineComponent<IProviderProps> const createSchemaField = _createSchemaField as unknown as CreateSchemaField export { Field, ArrayField, ObjectField, VoidField, RecursionField, FormConsumer, FormProvider, createSchemaField, } ``` -------------------------------------------------------------------------------- /packages/antd/docs/components/NumberPicker.zh-CN.md: -------------------------------------------------------------------------------- ```markdown # NumberPicker > 数字输入框 ## Markup Schema 案例 ```tsx import React from 'react' import { NumberPicker, FormItem, FormButtonGroup, Submit } from '@formily/antd' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { NumberPicker, FormItem, }, }) const form = createForm() export default () => ( <FormProvider form={form}> <SchemaField> <SchemaField.String name="input" title="输入框" x-decorator="FormItem" x-component="NumberPicker" required x-component-props={{ style: { width: 240, }, }} /> </SchemaField> <FormButtonGroup> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup> </FormProvider> ) ``` ## JSON Schema 案例 ```tsx import React from 'react' import { NumberPicker, FormItem, FormButtonGroup, Submit } from '@formily/antd' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { NumberPicker, FormItem, }, }) const form = createForm() const schema = { type: 'object', properties: { input: { type: 'string', title: '输入框', 'x-decorator': 'FormItem', 'x-component': 'NumberPicker', 'x-component-props': { style: { width: 240, }, }, }, }, } export default () => ( <FormProvider form={form}> <SchemaField schema={schema} /> <FormButtonGroup> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup> </FormProvider> ) ``` ## 纯 JSX 案例 ```tsx import React from 'react' import { NumberPicker, FormItem, FormButtonGroup, Submit } from '@formily/antd' import { createForm } from '@formily/core' import { FormProvider, Field } from '@formily/react' const form = createForm() export default () => ( <FormProvider form={form}> <Field name="input" title="输入框" required decorator={[FormItem]} component={[ NumberPicker, { style: { width: 240, }, }, ]} /> <FormButtonGroup> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup> </FormProvider> ) ``` ## API 参考 https://ant.design/components/input-number-cn/ ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/array-cards/markup-schema.vue: -------------------------------------------------------------------------------- ```vue <template> <FormProvider :form="form"> <SchemaField> <SchemaArrayField name="string_array" :maxItems="3" x-decorator="FormItem" x-component="ArrayCards" :x-component-props="{ title: '字符串数组', }" > <SchemaVoidField> <SchemaVoidField x-component="ArrayCards.Index" /> <SchemaStringField name="input" x-decorator="FormItem" title="Input" required x-component="Input" /> <SchemaVoidField x-component="ArrayCards.Remove" /> <SchemaVoidField x-component="ArrayCards.MoveUp" /> <SchemaVoidField x-component="ArrayCards.MoveDown" /> </SchemaVoidField> <SchemaVoidField x-component="ArrayCards.Addition" title="添加条目" /> </SchemaArrayField> <SchemaArrayField name="array" :maxItems="3" x-decorator="FormItem" x-component="ArrayCards" :x-component-props="{ title: '对象数组', }" > <SchemaObjectField> <SchemaVoidField x-component="ArrayCards.Index" /> <SchemaStringField name="input" x-decorator="FormItem" title="Input" required x-component="Input" /> <SchemaVoidField x-component="ArrayCards.Remove" /> <SchemaVoidField x-component="ArrayCards.MoveUp" /> <SchemaVoidField x-component="ArrayCards.MoveDown" /> </SchemaObjectField> <SchemaVoidField x-component="ArrayCards.Addition" title="添加条目" /> </SchemaArrayField> </SchemaField> <Submit @submit="log">提交</Submit> </FormProvider> </template> <script> import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/vue' import { FormItem, FormButtonGroup, Submit, Input, ArrayCards, } from '@formily/element' import { Button } from 'element-ui' const SchemaField = createSchemaField({ components: { FormItem, Input, ArrayCards, }, }) export default { components: { FormProvider, FormButtonGroup, Button, Submit, ...SchemaField, }, data() { const form = createForm() return { form, } }, methods: { log(values) { console.log(values) }, }, } </script> <style lang="scss" scoped></style> ``` -------------------------------------------------------------------------------- /packages/next/docs/components/NumberPicker.zh-CN.md: -------------------------------------------------------------------------------- ```markdown # NumberPicker > 数字输入框 ## Markup Schema 案例 ```tsx import React from 'react' import { NumberPicker, FormItem, FormButtonGroup, Submit } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { NumberPicker, FormItem, }, }) const form = createForm() export default () => ( <FormProvider form={form}> <SchemaField> <SchemaField.String name="input" title="输入框" x-decorator="FormItem" x-component="NumberPicker" required x-component-props={{ style: { width: 240, }, }} /> </SchemaField> <FormButtonGroup> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup> </FormProvider> ) ``` ## JSON Schema 案例 ```tsx import React from 'react' import { NumberPicker, FormItem, FormButtonGroup, Submit } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { NumberPicker, FormItem, }, }) const form = createForm() const schema = { type: 'object', properties: { input: { type: 'string', title: '输入框', 'x-decorator': 'FormItem', 'x-component': 'NumberPicker', 'x-component-props': { style: { width: 240, }, }, }, }, } export default () => ( <FormProvider form={form}> <SchemaField schema={schema} /> <FormButtonGroup> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup> </FormProvider> ) ``` ## 纯 JSX 案例 ```tsx import React from 'react' import { NumberPicker, FormItem, FormButtonGroup, Submit } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, Field } from '@formily/react' const form = createForm() export default () => ( <FormProvider form={form}> <Field name="input" title="输入框" required decorator={[FormItem]} component={[ NumberPicker, { style: { width: 240, }, }, ]} /> <FormButtonGroup> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup> </FormProvider> ) ``` ## API 参考 https://fusion.design/pc/component/basic/number-picker ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/date-picker/markup-schema.vue: -------------------------------------------------------------------------------- ```vue <template> <Form :form="form"> <SchemaField> <SchemaStringField name="date" title="普通日期" x-decorator="FormItem" x-component="DatePicker" /> <SchemaStringField name="week" title="周" x-decorator="FormItem" x-component="DatePicker" :x-component-props="{ type: 'week', }" /> <SchemaStringField name="month" title="月" x-decorator="FormItem" x-component="DatePicker" :x-component-props="{ type: 'month', }" /> <SchemaStringField name="year" title="年" x-decorator="FormItem" x-component="DatePicker" :x-component-props="{ type: 'year', }" /> <SchemaStringField name="dateTime" title="日期时间" x-decorator="FormItem" x-component="DatePicker" :x-component-props="{ type: 'datetime', }" /> <SchemaArrayField name="dates" title="多个日期" x-decorator="FormItem" x-component="DatePicker" :x-component-props="{ type: 'dates', }" /> <SchemaArrayField name="dateRange" title="日期范围" x-decorator="FormItem" x-component="DatePicker" :x-component-props="{ type: 'daterange', }" /> <SchemaArrayField name="monthRange" title="月范围" x-decorator="FormItem" x-component="DatePicker" :x-component-props="{ type: 'monthrange', }" /> <SchemaArrayField name="dateTimeRange" title="日期时间范围" x-decorator="FormItem" x-component="DatePicker" :x-component-props="{ type: 'datetimerange', }" /> </SchemaField> <Submit @submit="onSubmit">提交</Submit> </Form> </template> <script> import { createForm } from '@formily/core' import { createSchemaField } from '@formily/vue' import { Form, FormItem, DatePicker, Submit } from '@formily/element' const form = createForm() const fields = createSchemaField({ components: { FormItem, DatePicker, }, }) export default { components: { Form, ...fields, Submit }, data() { return { form, } }, methods: { onSubmit(value) { console.log(value) }, }, } </script> ``` -------------------------------------------------------------------------------- /packages/antd/src/__builtins__/render.ts: -------------------------------------------------------------------------------- ```typescript import { ReactElement } from 'react' import * as ReactDOM from 'react-dom' import type { Root } from 'react-dom/client' // 移植自rc-util: https://github.com/react-component/util/blob/master/src/React/render.ts type CreateRoot = (container: ContainerType) => Root // Let compiler not to search module usage const fullClone = { ...ReactDOM, } as typeof ReactDOM & { __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED?: { usingClientEntryPoint?: boolean } createRoot?: CreateRoot } const { version, render: reactRender, unmountComponentAtNode } = fullClone let createRoot: CreateRoot try { const mainVersion = Number((version || '').split('.')[0]) if (mainVersion >= 18 && fullClone.createRoot) { // eslint-disable-next-line @typescript-eslint/no-var-requires createRoot = fullClone.createRoot } } catch (e) { // Do nothing; } function toggleWarning(skip: boolean) { const { __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED } = fullClone if ( __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED && typeof __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED === 'object' ) { __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.usingClientEntryPoint = skip } } const MARK = '__antd_mobile_root__' // ========================== Render ========================== type ContainerType = (Element | DocumentFragment) & { [MARK]?: Root } function legacyRender(node: ReactElement, container: ContainerType) { reactRender(node, container) } function concurrentRender(node: ReactElement, container: ContainerType) { toggleWarning(true) const root = container[MARK] || createRoot(container) toggleWarning(false) root.render(node) container[MARK] = root } export function render(node: ReactElement, container: ContainerType) { if (createRoot as unknown) { concurrentRender(node, container) return } legacyRender(node, container) } // ========================== Unmount ========================= function legacyUnmount(container: ContainerType) { return unmountComponentAtNode(container) } async function concurrentUnmount(container: ContainerType) { // Delay to unmount to avoid React 18 sync warning return Promise.resolve().then(() => { container[MARK]?.unmount() delete container[MARK] }) } export function unmount(container: ContainerType) { if (createRoot as unknown) { return concurrentUnmount(container) } return legacyUnmount(container) } ``` -------------------------------------------------------------------------------- /packages/next/src/__builtins__/render.ts: -------------------------------------------------------------------------------- ```typescript import { ReactElement } from 'react' import * as ReactDOM from 'react-dom' import type { Root } from 'react-dom/client' // 移植自rc-util: https://github.com/react-component/util/blob/master/src/React/render.ts type CreateRoot = (container: ContainerType) => Root // Let compiler not to search module usage const fullClone = { ...ReactDOM, } as typeof ReactDOM & { __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED?: { usingClientEntryPoint?: boolean } createRoot?: CreateRoot } const { version, render: reactRender, unmountComponentAtNode } = fullClone let createRoot: CreateRoot try { const mainVersion = Number((version || '').split('.')[0]) if (mainVersion >= 18 && fullClone.createRoot) { // eslint-disable-next-line @typescript-eslint/no-var-requires createRoot = fullClone.createRoot } } catch (e) { // Do nothing; } function toggleWarning(skip: boolean) { const { __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED } = fullClone if ( __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED && typeof __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED === 'object' ) { __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.usingClientEntryPoint = skip } } const MARK = '__antd_mobile_root__' // ========================== Render ========================== type ContainerType = (Element | DocumentFragment) & { [MARK]?: Root } function legacyRender(node: ReactElement, container: ContainerType) { reactRender(node, container) } function concurrentRender(node: ReactElement, container: ContainerType) { toggleWarning(true) const root = container[MARK] || createRoot(container) toggleWarning(false) root.render(node) container[MARK] = root } export function render(node: ReactElement, container: ContainerType) { if (createRoot as unknown) { concurrentRender(node, container) return } legacyRender(node, container) } // ========================== Unmount ========================= function legacyUnmount(container: ContainerType) { return unmountComponentAtNode(container) } async function concurrentUnmount(container: ContainerType) { // Delay to unmount to avoid React 18 sync warning return Promise.resolve().then(() => { container[MARK]?.unmount() delete container[MARK] }) } export function unmount(container: ContainerType) { if (createRoot as unknown) { return concurrentUnmount(container) } return legacyUnmount(container) } ``` -------------------------------------------------------------------------------- /packages/reactive-test-cases-for-react18/webpack.base.ts: -------------------------------------------------------------------------------- ```typescript import path from 'path' import fs from 'fs-extra' import { GlobSync } from 'glob' import MiniCssExtractPlugin from 'mini-css-extract-plugin' //import { getThemeVariables } from 'antd/dist/theme' const getWorkspaceAlias = () => { const basePath = path.resolve(__dirname, '../../') const pkg = fs.readJSONSync(path.resolve(basePath, 'package.json')) || {} const results = {} const workspaces = pkg.workspaces if (Array.isArray(workspaces)) { workspaces.forEach((pattern) => { const { found } = new GlobSync(pattern, { cwd: basePath }) found.forEach((name) => { const pkg = fs.readJSONSync( path.resolve(basePath, name, './package.json') ) results[pkg.name] = path.resolve(basePath, name, './src') }) }) } return results } export default { mode: 'development', devtool: 'inline-source-map', // 嵌入到源文件中 stats: { entrypoints: false, children: false, }, entry: { index: path.resolve(__dirname, './src/index'), }, output: { path: path.resolve(__dirname, '../build'), filename: '[name].[hash].bundle.js', }, resolve: { modules: ['node_modules'], extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'], alias: getWorkspaceAlias(), }, externals: { react: 'React', 'react-dom': 'ReactDOM', moment: 'moment', antd: 'antd', }, module: { rules: [ { test: /\.(tsx?|jsx?)$/, use: [ { loader: require.resolve('ts-loader'), options: { transpileOnly: true, }, }, ], }, { test: /\.css$/, use: [MiniCssExtractPlugin.loader, require.resolve('css-loader')], }, { test: /\.less$/, use: [ MiniCssExtractPlugin.loader, { loader: 'css-loader' }, { loader: 'postcss-loader', }, { loader: 'less-loader', options: { // modifyVars: getThemeVariables({ // dark: true // 开启暗黑模式 // }), javascriptEnabled: true, }, }, ], }, { test: /\.(woff|woff2|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/, use: ['url-loader'], }, { test: /\.html?$/, loader: require.resolve('file-loader'), options: { name: '[name].[ext]', }, }, ], }, } ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/form-dialog/markup-schema.vue: -------------------------------------------------------------------------------- ```vue <template> <FormDialogPortal :id="portalId"> <Button @click="handleOpen">点击打开表单</Button> </FormDialogPortal> </template> <script> import { FormDialog, FormLayout, FormItem, Input } from '@formily/element' import { Button } from 'element-ui' import { createSchemaField } from '@formily/vue' const { SchemaField, SchemaStringField } = createSchemaField({ components: { FormItem, Input, }, }) // 弹框表单组件 const DialogForm = { props: ['form'], inject: ['foo'], render() { const form = this.form console.log(this.foo) return ( <FormLayout labelCol={6} wrapperCol={10}> <SchemaField> <SchemaStringField name="aaa" required title="输入框1" x-decorator="FormItem" x-component="Input" /> <SchemaStringField name="bbb" required title="输入框2" x-decorator="FormItem" x-component="Input" /> <SchemaStringField name="ccc" required title="输入框3" x-decorator="FormItem" x-component="Input" /> <SchemaStringField name="ddd" required title="输入框4" x-decorator="FormItem" x-component="Input" /> </SchemaField> <FormDialog.Footer> <span style={{ marginLeft: '4px' }}>扩展文案: {form.values.aaa}</span> </FormDialog.Footer> </FormLayout> ) }, } export default { components: { Button, FormDialogPortal: FormDialog.Portal }, data() { return { portalId: '可以传,也可以不传的ID,默认是form-dialog', } }, provide: { foo: '自定义上下文可以直接传到弹窗内部,只需要ID一致即可', }, methods: { handleOpen() { FormDialog('弹框表单', this.portalId, DialogForm) .forOpen((payload, next) => { setTimeout(() => { next({ initialValues: { aaa: '123', }, }) }, 1000) }) .forConfirm((payload, next) => { setTimeout(() => { console.log(payload) next(payload) }, 1000) }) .forCancel((payload, next) => { setTimeout(() => { console.log(payload) next(payload) }, 1000) }) .open() .then(console.log) .catch(console.error) }, }, } </script> ``` -------------------------------------------------------------------------------- /packages/next/docs/components/Transfer.zh-CN.md: -------------------------------------------------------------------------------- ```markdown # Transfer > 穿梭框 ## Markup Schema 案例 ```tsx import React from 'react' import { Transfer, FormItem, FormButtonGroup, Submit } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { Transfer, FormItem, }, }) const form = createForm() export default () => ( <FormProvider form={form}> <SchemaField> <SchemaField.Array name="transfer" title="穿梭框" x-decorator="FormItem" x-component="Transfer" enum={[ { label: '选项1', value: 'aaa' }, { label: '选项2', value: 'bbb' }, ]} /> </SchemaField> <FormButtonGroup> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup> </FormProvider> ) ``` ## JSON Schema 案例 ```tsx import React from 'react' import { Transfer, FormItem, FormButtonGroup, Submit } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { Transfer, FormItem, }, }) const form = createForm() const schema = { type: 'object', properties: { transfer: { type: 'array', title: '穿梭框', 'x-decorator': 'FormItem', 'x-component': 'Transfer', enum: [ { label: '选项1', value: 'aaa' }, { label: '选项2', value: 'bbb' }, ], }, }, } const renderTitle = (item) => item.title export default () => ( <FormProvider form={form}> <SchemaField schema={schema} scope={{ renderTitle }} /> <FormButtonGroup> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup> </FormProvider> ) ``` ## 纯 JSX 案例 ```tsx import React from 'react' import { Transfer, FormItem, FormButtonGroup, Submit } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, Field } from '@formily/react' const form = createForm() export default () => ( <FormProvider form={form}> <Field name="transfer" title="穿梭框" dataSource={[ { label: '选项1', value: 'aaa' }, { label: '选项2', value: 'bbb' }, ]} decorator={[FormItem]} component={[Transfer]} /> <FormButtonGroup> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup> </FormProvider> ) ``` ## API 参考 https://fusion.design/pc/component/basic/transfer ``` -------------------------------------------------------------------------------- /packages/vue/src/shared/h.ts: -------------------------------------------------------------------------------- ```typescript import { h, isVue2 } from 'vue-demi' import { Fragment, FragmentComponent } from './fragment' import { formatVue3VNodeData } from '../utils/formatVNodeData' type RenderChildren = { [key in string]?: (...args: any[]) => (VNode | string)[] } type Tag = any type VNodeData = Record<string, any> type VNode = any type VNodeChildren = any const compatibleCreateElement = ( tag: Tag, data: VNodeData, components: RenderChildren ): any => { /* istanbul ignore else */ if (isVue2) { const hInVue2 = h as ( tag: Tag, data?: VNodeData, components?: VNodeChildren ) => VNode const scopedSlots = components // 默认全部作为 scopedSlots 处理 const children = [] /** * scopedSlots 不会映射为slots,所以这里手动映射一遍 * 主要为了解决 slots.x 问题 */ Object.keys(components).forEach((key) => { const func = components[key] // 转换为 slots 传递 if (typeof func === 'function' && func.length === 0) { /** * func 参数为0的判断不准确,因为composition-api包了一层,导致全部为0 * try catch 解决scoped slots 转换参数异常问题 * */ try { const child = func() children.push( key === 'default' ? child : hInVue2(FragmentComponent, { slot: key }, [child]) ) } catch (error) {} } }) const newData = Object.assign({}, data) if (Object.keys(scopedSlots).length > 0) { if (!newData.scopedSlots) { newData.scopedSlots = scopedSlots } else { newData.scopedSlots = { ...newData.scopedSlots, ...scopedSlots, } } } if (tag === Fragment) { // sometimes we needn't to use Fragment component. if (children.length === 1) { if (!Array.isArray(children[0])) { return children[0] } else if (children[0].length === 1) { if (!Array.isArray(children[0][0])) { return children[0][0] } else if (children[0][0].length === 1) { return children[0][0][0] } } } tag = FragmentComponent } return hInVue2(tag, newData, children) } else { if (tag === Fragment) { tag = FragmentComponent } const hInVue3 = h as ( tag: Tag, data?: VNodeData, components?: RenderChildren ) => VNode return hInVue3(tag, formatVue3VNodeData(data), components) } } export default compatibleCreateElement export { compatibleCreateElement as h } ``` -------------------------------------------------------------------------------- /packages/antd/docs/components/NumberPicker.md: -------------------------------------------------------------------------------- ```markdown # NumberPicker > Number input box ## Markup Schema example ```tsx import React from 'react' import { NumberPicker, FormItem, FormButtonGroup, Submit } from '@formily/antd' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { NumberPicker, FormItem, }, }) const form = createForm() export default () => ( <FormProvider form={form}> <SchemaField> <SchemaField.String name="input" title="input box" x-decorator="FormItem" x-component="NumberPicker" required x-component-props={{ style: { width: 240, }, }} /> </SchemaField> <FormButtonGroup> <Submit onSubmit={console.log}>Submit</Submit> </FormButtonGroup> </FormProvider> ) ``` ## JSON Schema case ```tsx import React from 'react' import { NumberPicker, FormItem, FormButtonGroup, Submit } from '@formily/antd' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { NumberPicker, FormItem, }, }) const form = createForm() const schema = { type: 'object', properties: { input: { type: 'string', title: 'input box', 'x-decorator': 'FormItem', 'x-component': 'NumberPicker', 'x-component-props': { style: { width: 240, }, }, }, }, } export default () => ( <FormProvider form={form}> <SchemaField schema={schema} /> <FormButtonGroup> <Submit onSubmit={console.log}>Submit</Submit> </FormButtonGroup> </FormProvider> ) ``` ## Pure JSX case ```tsx import React from 'react' import { NumberPicker, FormItem, FormButtonGroup, Submit } from '@formily/antd' import { createForm } from '@formily/core' import { FormProvider, Field } from '@formily/react' const form = createForm() export default () => ( <FormProvider form={form}> <Field name="input" title="input box" required decorator={[FormItem]} component={[ NumberPicker, { style: { width: 240, }, }, ]} /> <FormButtonGroup> <Submit onSubmit={console.log}>Submit</Submit> </FormButtonGroup> </FormProvider> ) ``` ## API Reference https://ant.design/components/input-number-cn/ ``` -------------------------------------------------------------------------------- /packages/next/docs/components/NumberPicker.md: -------------------------------------------------------------------------------- ```markdown # NumberPicker > Number input box ## Markup Schema example ```tsx import React from 'react' import { NumberPicker, FormItem, FormButtonGroup, Submit } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { NumberPicker, FormItem, }, }) const form = createForm() export default () => ( <FormProvider form={form}> <SchemaField> <SchemaField.String name="input" title="input box" x-decorator="FormItem" x-component="NumberPicker" required x-component-props={{ style: { width: 240, }, }} /> </SchemaField> <FormButtonGroup> <Submit onSubmit={console.log}>Submit</Submit> </FormButtonGroup> </FormProvider> ) ``` ## JSON Schema case ```tsx import React from 'react' import { NumberPicker, FormItem, FormButtonGroup, Submit } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { NumberPicker, FormItem, }, }) const form = createForm() const schema = { type: 'object', properties: { input: { type: 'string', title: 'input box', 'x-decorator': 'FormItem', 'x-component': 'NumberPicker', 'x-component-props': { style: { width: 240, }, }, }, }, } export default () => ( <FormProvider form={form}> <SchemaField schema={schema} /> <FormButtonGroup> <Submit onSubmit={console.log}>Submit</Submit> </FormButtonGroup> </FormProvider> ) ``` ## Pure JSX case ```tsx import React from 'react' import { NumberPicker, FormItem, FormButtonGroup, Submit } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, Field } from '@formily/react' const form = createForm() export default () => ( <FormProvider form={form}> <Field name="input" title="input box" required decorator={[FormItem]} component={[ NumberPicker, { style: { width: 240, }, }, ]} /> <FormButtonGroup> <Submit onSubmit={console.log}>Submit</Submit> </FormButtonGroup> </FormProvider> ) ``` ## API Reference https://fusion.design/pc/component/basic/number-picker ``` -------------------------------------------------------------------------------- /packages/validator/src/types.ts: -------------------------------------------------------------------------------- ```typescript export type ValidatorFormats = | 'url' | 'email' | 'ipv6' | 'ipv4' | 'number' | 'integer' | 'idcard' | 'qq' | 'phone' | 'money' | 'zh' | 'date' | 'zip' | (string & {}) export interface IValidateResult { type: 'error' | 'warning' | 'success' | (string & {}) message: string } export interface IValidateResults { error?: string[] warning?: string[] success?: string[] } export const isValidateResult = (obj: any): obj is IValidateResult => !!obj['type'] && !!obj['message'] export type ValidatorFunctionResponse = | null | string | boolean | IValidateResult export type ValidatorFunction<Context = any> = ( value: any, rule: IValidatorRules<Context>, ctx: Context, render: (message: string, scope?: any) => string ) => ValidatorFunctionResponse | Promise<ValidatorFunctionResponse> | null export type ValidatorParsedFunction<Context = any> = ( value: any, ctx: Context ) => IValidateResult | Promise<IValidateResult> | null export type ValidatorTriggerType = | 'onInput' | 'onFocus' | 'onBlur' | (string & {}) export interface IValidatorRules<Context = any> { triggerType?: ValidatorTriggerType format?: ValidatorFormats validator?: ValidatorFunction<Context> required?: boolean pattern?: RegExp | string max?: number maximum?: number maxItems?: number minItems?: number maxLength?: number minLength?: number exclusiveMaximum?: number exclusiveMinimum?: number minimum?: number min?: number len?: number whitespace?: boolean enum?: any[] const?: any multipleOf?: number uniqueItems?: boolean maxProperties?: number minProperties?: number message?: string [key: string]: any } export interface IRegistryLocaleMessages { [key: string]: string | IRegistryLocaleMessages } export interface IRegistryLocales { [language: string]: IRegistryLocaleMessages } export interface IRegistryRules<Context = any> { [key: string]: ValidatorFunction<Context> } export interface IRegistryFormats { [key: string]: string | RegExp } export type ValidatorDescription<Context = any> = | ValidatorFormats | ValidatorFunction<Context> | IValidatorRules<Context> export type MultiValidator<Context = any> = ValidatorDescription<Context>[] export type Validator<Context = any> = | ValidatorDescription<Context> | MultiValidator<Context> export interface IValidatorOptions<Context = any> { validateFirst?: boolean triggerType?: ValidatorTriggerType context?: Context } ``` -------------------------------------------------------------------------------- /packages/element/src/space/index.ts: -------------------------------------------------------------------------------- ```typescript // https://github.com/vueComponent/ant-design-vue/blob/next/components/space/index.tsx import { h } from '@formily/vue' import { defineComponent } from 'vue-demi' import { stylePrefix } from '../__builtins__/configs' import type { VNode } from 'vue' import { useFormLayout } from '../form-layout' export type SpaceProps = { size: 'small' | 'middle' | 'large' | number direction: 'horizontal' | 'vertical' align: 'start' | 'end' | 'center' | 'baseline' } const spaceSize = { small: 8, middle: 16, large: 24, } export const Space = defineComponent<SpaceProps>({ name: 'FSpace', props: ['size', 'direction', 'align'], setup(props, { attrs, slots }) { const layout = useFormLayout() return () => { const { align, size = layout.value?.spaceGap ?? 'small', direction = 'horizontal', } = props const prefixCls = `${stylePrefix}-space` const children = slots.default?.() let items: VNode[] = [] if (Array.isArray(children)) { if (children.length === 1) { if ((children[0]['tag'] as string)?.endsWith('Fragment')) { // Fragment hack items = (children[0]['componentOptions'] as { children: VNode[] }) ?.children } else { items = children } } else { items = children } } const len = items.length if (len === 0) { return null } const mergedAlign = align === undefined && direction === 'horizontal' ? 'center' : align const someSpaceClass = { [prefixCls]: true, [`${prefixCls}-${direction}`]: true, [`${prefixCls}-align-${mergedAlign}`]: mergedAlign, } const itemClassName = `${prefixCls}-item` const marginDirection = 'marginRight' // directionConfig === 'rtl' ? 'marginLeft' : 'marginRight'; const renderItems = items.map((child, i) => h( 'div', { class: itemClassName, key: `${itemClassName}-${i}`, }, { default: () => [child] } ) ) return h( 'div', { ...attrs, class: { ...(attrs as any).class, ...someSpaceClass }, style: { ...(attrs as any).style, gap: typeof size === 'string' ? `${spaceSize[size]}px` : `${size}px`, }, }, { default: () => renderItems } ) } }, }) export default Space ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/space/template.vue: -------------------------------------------------------------------------------- ```vue <template> <FormProvider :form="form"> <FormLayout :labelCol="6" :wrapperCol="16"> <VoidField name="name" title="姓名" :decorator="[ FormItem, { asterisk: true, feedbackLayout: 'none', }, ]" :component="[Space]" > <Field name="firstName" :decorator="[FormItem]" :component="[Input]" required /> <Field name="lastName" :decorator="[FormItem]" :component="[Input]" required /> </VoidField> <VoidField name="texts" title="文本串联" :decorator="[ FormItem, { asterisk: true, feedbackLayout: 'none', }, ]" :component="[Space]" > <Field name="aa" :decorator="[ FormItem, { addonAfter: '单位', }, ]" :component="[Input]" required /> <Field name="bb" :decorator="[ FormItem, { addonAfter: '单位', }, ]" :component="[Input]" required /> <Field name="cc" :decorator="[ FormItem, { addonAfter: '单位', }, ]" :component="[Input]" required /> </VoidField> <Field name="textarea" title="文本框" :decorator="[FormItem]" :component="[ TextArea, { style: { width: 400, }, }, ]" required /> <FormButtonGroup alignFormItem> <Submit :onSubmit="log">提交</Submit> </FormButtonGroup> </FormLayout> </FormProvider> </template> <script> import { createForm } from '@formily/core' import { FormProvider, Field, VoidField } from '@formily/vue' import { FormLayout, FormItem, Input, TextArea, Submit, Space, FormButtonGroup, } from '@formily/element' const form = createForm() export default { components: { FormProvider, FormLayout, FormButtonGroup, VoidField, Field, Submit, }, data() { return { FormItem, Input, TextArea, Space, form, } }, methods: { log(value) { console.log(value) }, }, } </script> ``` -------------------------------------------------------------------------------- /packages/core/docs/api/entry/createForm.md: -------------------------------------------------------------------------------- ```markdown --- order: 0 --- # createForm ## Description Create a Form instance as a ViewModel for consumption by the UI framework layer ## Signature ```ts interface createForm { (props: IFormProps): Form } ``` ## IFormProps | Property | Description | Type | Default Value | | ------------- | ---------------------------------------------------------- | -------------------------------------------------------- | ------------- | | values | form values | Object | `{}` | | initialValues | Form default values | Object | `{}` | | pattern | Form interaction mode | `"editable" \| "disabled" \| "readOnly" \| "readPretty"` | `"editable"` | | display | The form is visible and hidden | `"visible" \| "hidden" \| "none"` | `"visible` | | hidden | UI hidden | Boolean | `false` | | visible | show/hide (data hiding) | Boolean | `true` | | editable | Editable | Boolean | `true` | | disabled | Whether to disable | Boolean | `false` | | readOnly | Is it read-only | Boolean | `false` | | readPretty | Is it an elegant reading state | Boolean | `false` | | effects | Side effect logic, used to implement various linkage logic | `(form:Form)=>void` | | | validateFirst | Whether to validate only the first illegal rule | Boolean | `false` | ## Example ```ts import { createForm } from '@formily/core' const form = createForm({ initialValues: { say: 'hello', }, }) ``` ```