This is page 23 of 52. Use http://codebase.md/alibaba/formily?lines=true&page={x} to view the full context. # Directory Structure ``` ├── .all-contributorsrc ├── .codecov.yml ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .github │ ├── CONTRIBUTING.md │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE │ │ └── config.yml │ ├── PULL_REQUEST_TEMPLATE.md │ └── workflows │ ├── check-pr-title.yml │ ├── ci.yml │ ├── commitlint.yml │ ├── issue-open-check.yml │ ├── package-size.yml │ └── pr-welcome.yml ├── .gitignore ├── .prettierrc.js ├── .umirc.js ├── .vscode │ └── cspell.json ├── .yarnrc ├── CHANGELOG.md ├── commitlint.config.js ├── devtools │ ├── .eslintrc │ └── chrome-extension │ ├── .npmignore │ ├── assets │ │ └── img │ │ ├── loading.svg │ │ └── logo │ │ ├── 128x128.png │ │ ├── 16x16.png │ │ ├── 38x38.png │ │ ├── 48x48.png │ │ ├── error.png │ │ ├── gray.png │ │ └── scalable.png │ ├── config │ │ ├── webpack.base.ts │ │ ├── webpack.dev.ts │ │ └── webpack.prod.ts │ ├── LICENSE.md │ ├── package.json │ ├── src │ │ ├── app │ │ │ ├── components │ │ │ │ ├── FieldTree.tsx │ │ │ │ ├── filter.ts │ │ │ │ ├── LeftPanel.tsx │ │ │ │ ├── RightPanel.tsx │ │ │ │ ├── SearchBox.tsx │ │ │ │ └── Tabs.tsx │ │ │ ├── demo.tsx │ │ │ └── index.tsx │ │ └── extension │ │ ├── backend.ts │ │ ├── background.ts │ │ ├── content.ts │ │ ├── devpanel.tsx │ │ ├── devtools.tsx │ │ ├── inject.ts │ │ ├── manifest.json │ │ ├── popup.tsx │ │ └── views │ │ ├── devpanel.ejs │ │ ├── devtools.ejs │ │ └── popup.ejs │ ├── tsconfig.build.json │ └── tsconfig.json ├── docs │ ├── functions │ │ ├── contributors.ts │ │ └── npm-search.ts │ ├── guide │ │ ├── advanced │ │ │ ├── async.md │ │ │ ├── async.zh-CN.md │ │ │ ├── build.md │ │ │ ├── build.zh-CN.md │ │ │ ├── business-logic.md │ │ │ ├── business-logic.zh-CN.md │ │ │ ├── calculator.md │ │ │ ├── calculator.zh-CN.md │ │ │ ├── controlled.md │ │ │ ├── controlled.zh-CN.md │ │ │ ├── custom.md │ │ │ ├── custom.zh-CN.md │ │ │ ├── destructor.md │ │ │ ├── destructor.zh-CN.md │ │ │ ├── input.less │ │ │ ├── layout.md │ │ │ ├── layout.zh-CN.md │ │ │ ├── linkages.md │ │ │ ├── linkages.zh-CN.md │ │ │ ├── validate.md │ │ │ └── validate.zh-CN.md │ │ ├── contribution.md │ │ ├── contribution.zh-CN.md │ │ ├── form-builder.md │ │ ├── form-builder.zh-CN.md │ │ ├── index.md │ │ ├── index.zh-CN.md │ │ ├── issue-helper.md │ │ ├── issue-helper.zh-CN.md │ │ ├── learn-formily.md │ │ ├── learn-formily.zh-CN.md │ │ ├── quick-start.md │ │ ├── quick-start.zh-CN.md │ │ ├── scenes │ │ │ ├── dialog-drawer.md │ │ │ ├── dialog-drawer.zh-CN.md │ │ │ ├── edit-detail.md │ │ │ ├── edit-detail.zh-CN.md │ │ │ ├── index.less │ │ │ ├── login-register.md │ │ │ ├── login-register.zh-CN.md │ │ │ ├── more.md │ │ │ ├── more.zh-CN.md │ │ │ ├── query-list.md │ │ │ ├── query-list.zh-CN.md │ │ │ ├── step-form.md │ │ │ ├── step-form.zh-CN.md │ │ │ ├── tab-form.md │ │ │ ├── tab-form.zh-CN.md │ │ │ └── VerifyCode.tsx │ │ ├── upgrade.md │ │ └── upgrade.zh-CN.md │ ├── index.md │ ├── index.zh-CN.md │ └── site │ ├── Contributors.less │ ├── Contributors.tsx │ ├── QrCode.less │ ├── QrCode.tsx │ ├── Section.less │ ├── Section.tsx │ └── styles.less ├── global.config.ts ├── jest.config.js ├── lerna.json ├── LICENSE.md ├── package.json ├── packages │ ├── .eslintrc │ ├── antd │ │ ├── __tests__ │ │ │ ├── moment.spec.ts │ │ │ └── sideEffects.spec.ts │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── build-style.ts │ │ ├── create-style.ts │ │ ├── docs │ │ │ ├── components │ │ │ │ ├── ArrayCards.md │ │ │ │ ├── ArrayCards.zh-CN.md │ │ │ │ ├── ArrayCollapse.md │ │ │ │ ├── ArrayCollapse.zh-CN.md │ │ │ │ ├── ArrayItems.md │ │ │ │ ├── ArrayItems.zh-CN.md │ │ │ │ ├── ArrayTable.md │ │ │ │ ├── ArrayTable.zh-CN.md │ │ │ │ ├── ArrayTabs.md │ │ │ │ ├── ArrayTabs.zh-CN.md │ │ │ │ ├── Cascader.md │ │ │ │ ├── Cascader.zh-CN.md │ │ │ │ ├── Checkbox.md │ │ │ │ ├── Checkbox.zh-CN.md │ │ │ │ ├── DatePicker.md │ │ │ │ ├── DatePicker.zh-CN.md │ │ │ │ ├── Editable.md │ │ │ │ ├── Editable.zh-CN.md │ │ │ │ ├── Form.md │ │ │ │ ├── Form.zh-CN.md │ │ │ │ ├── FormButtonGroup.md │ │ │ │ ├── FormButtonGroup.zh-CN.md │ │ │ │ ├── FormCollapse.md │ │ │ │ ├── FormCollapse.zh-CN.md │ │ │ │ ├── FormDialog.md │ │ │ │ ├── FormDialog.zh-CN.md │ │ │ │ ├── FormDrawer.md │ │ │ │ ├── FormDrawer.zh-CN.md │ │ │ │ ├── FormGrid.md │ │ │ │ ├── FormGrid.zh-CN.md │ │ │ │ ├── FormItem.md │ │ │ │ ├── FormItem.zh-CN.md │ │ │ │ ├── FormLayout.md │ │ │ │ ├── FormLayout.zh-CN.md │ │ │ │ ├── FormStep.md │ │ │ │ ├── FormStep.zh-CN.md │ │ │ │ ├── FormTab.md │ │ │ │ ├── FormTab.zh-CN.md │ │ │ │ ├── index.md │ │ │ │ ├── index.zh-CN.md │ │ │ │ ├── Input.md │ │ │ │ ├── Input.zh-CN.md │ │ │ │ ├── NumberPicker.md │ │ │ │ ├── NumberPicker.zh-CN.md │ │ │ │ ├── Password.md │ │ │ │ ├── Password.zh-CN.md │ │ │ │ ├── PreviewText.md │ │ │ │ ├── PreviewText.zh-CN.md │ │ │ │ ├── Radio.md │ │ │ │ ├── Radio.zh-CN.md │ │ │ │ ├── Reset.md │ │ │ │ ├── Reset.zh-CN.md │ │ │ │ ├── Select.md │ │ │ │ ├── Select.zh-CN.md │ │ │ │ ├── SelectTable.md │ │ │ │ ├── SelectTable.zh-CN.md │ │ │ │ ├── Space.md │ │ │ │ ├── Space.zh-CN.md │ │ │ │ ├── Submit.md │ │ │ │ ├── Submit.zh-CN.md │ │ │ │ ├── Switch.md │ │ │ │ ├── Switch.zh-CN.md │ │ │ │ ├── TimePicker.md │ │ │ │ ├── TimePicker.zh-CN.md │ │ │ │ ├── Transfer.md │ │ │ │ ├── Transfer.zh-CN.md │ │ │ │ ├── TreeSelect.md │ │ │ │ ├── TreeSelect.zh-CN.md │ │ │ │ ├── Upload.md │ │ │ │ └── Upload.zh-CN.md │ │ │ ├── index.md │ │ │ └── index.zh-CN.md │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __builtins__ │ │ │ │ ├── hooks │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── useClickAway.ts │ │ │ │ │ └── usePrefixCls.ts │ │ │ │ ├── index.ts │ │ │ │ ├── loading.ts │ │ │ │ ├── moment.ts │ │ │ │ ├── pickDataProps.ts │ │ │ │ ├── portal.tsx │ │ │ │ ├── render.ts │ │ │ │ └── sort.tsx │ │ │ ├── array-base │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── array-cards │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── array-collapse │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── array-items │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── array-table │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── array-tabs │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── cascader │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── checkbox │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── date-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── editable │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── form │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── form-button-group │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── form-collapse │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-dialog │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-drawer │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-grid │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── form-item │ │ │ │ ├── animation.less │ │ │ │ ├── grid.less │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── form-layout │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ ├── style.ts │ │ │ │ └── useResponsiveFormLayout.ts │ │ │ ├── form-step │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-tab │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── index.ts │ │ │ ├── input │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── number-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── password │ │ │ │ ├── index.tsx │ │ │ │ ├── PasswordStrength.tsx │ │ │ │ └── style.ts │ │ │ ├── preview-text │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── radio │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── reset │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── select │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── select-table │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ ├── style.ts │ │ │ │ ├── useCheckSlackly.tsx │ │ │ │ ├── useFilterOptions.tsx │ │ │ │ ├── useFlatOptions.tsx │ │ │ │ ├── useSize.tsx │ │ │ │ ├── useTitleAddon.tsx │ │ │ │ └── utils.ts │ │ │ ├── space │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── style.less │ │ │ ├── style.ts │ │ │ ├── submit │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── switch │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── time-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── transfer │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── tree-select │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ └── upload │ │ │ ├── index.tsx │ │ │ ├── placeholder.ts │ │ │ └── style.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── benchmark │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── src │ │ │ └── index.tsx │ │ ├── template.ejs │ │ ├── tsconfig.build.json │ │ ├── tsconfig.json │ │ ├── webpack.base.ts │ │ ├── webpack.dev.ts │ │ └── webpack.prod.ts │ ├── core │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── docs │ │ │ ├── api │ │ │ │ ├── entry │ │ │ │ │ ├── ActionResponse.less │ │ │ │ │ ├── ActionResponse.tsx │ │ │ │ │ ├── createForm.md │ │ │ │ │ ├── createForm.zh-CN.md │ │ │ │ │ ├── FieldEffectHooks.md │ │ │ │ │ ├── FieldEffectHooks.zh-CN.md │ │ │ │ │ ├── FormChecker.md │ │ │ │ │ ├── FormChecker.zh-CN.md │ │ │ │ │ ├── FormEffectHooks.md │ │ │ │ │ ├── FormEffectHooks.zh-CN.md │ │ │ │ │ ├── FormHooksAPI.md │ │ │ │ │ ├── FormHooksAPI.zh-CN.md │ │ │ │ │ ├── FormPath.md │ │ │ │ │ ├── FormPath.zh-CN.md │ │ │ │ │ ├── FormValidatorRegistry.md │ │ │ │ │ └── FormValidatorRegistry.zh-CN.md │ │ │ │ └── models │ │ │ │ ├── ArrayField.md │ │ │ │ ├── ArrayField.zh-CN.md │ │ │ │ ├── Field.md │ │ │ │ ├── Field.zh-CN.md │ │ │ │ ├── Form.md │ │ │ │ ├── Form.zh-CN.md │ │ │ │ ├── ObjectField.md │ │ │ │ ├── ObjectField.zh-CN.md │ │ │ │ ├── Query.md │ │ │ │ ├── Query.zh-CN.md │ │ │ │ ├── VoidField.md │ │ │ │ └── VoidField.zh-CN.md │ │ │ ├── guide │ │ │ │ ├── architecture.md │ │ │ │ ├── architecture.zh-CN.md │ │ │ │ ├── field.md │ │ │ │ ├── field.zh-CN.md │ │ │ │ ├── form.md │ │ │ │ ├── form.zh-CN.md │ │ │ │ ├── index.md │ │ │ │ ├── index.zh-CN.md │ │ │ │ ├── mvvm.md │ │ │ │ ├── mvvm.zh-CN.md │ │ │ │ ├── values.md │ │ │ │ └── values.zh-CN.md │ │ │ ├── index.md │ │ │ └── index.zh-CN.md │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── array.spec.ts │ │ │ │ ├── effects.spec.ts │ │ │ │ ├── externals.spec.ts │ │ │ │ ├── field.spec.ts │ │ │ │ ├── form.spec.ts │ │ │ │ ├── graph.spec.ts │ │ │ │ ├── heart.spec.ts │ │ │ │ ├── internals.spec.ts │ │ │ │ ├── lifecycle.spec.ts │ │ │ │ ├── object.spec.ts │ │ │ │ ├── shared.ts │ │ │ │ └── void.spec.ts │ │ │ ├── effects │ │ │ │ ├── index.ts │ │ │ │ ├── onFieldEffects.ts │ │ │ │ └── onFormEffects.ts │ │ │ ├── global.d.ts │ │ │ ├── index.ts │ │ │ ├── models │ │ │ │ ├── ArrayField.ts │ │ │ │ ├── BaseField.ts │ │ │ │ ├── Field.ts │ │ │ │ ├── Form.ts │ │ │ │ ├── Graph.ts │ │ │ │ ├── Heart.ts │ │ │ │ ├── index.ts │ │ │ │ ├── LifeCycle.ts │ │ │ │ ├── ObjectField.ts │ │ │ │ ├── Query.ts │ │ │ │ ├── types.ts │ │ │ │ └── VoidField.ts │ │ │ ├── shared │ │ │ │ ├── checkers.ts │ │ │ │ ├── constants.ts │ │ │ │ ├── effective.ts │ │ │ │ ├── externals.ts │ │ │ │ └── internals.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── element │ │ ├── .npmignore │ │ ├── build-style.ts │ │ ├── create-style.ts │ │ ├── docs │ │ │ ├── .vuepress │ │ │ │ ├── components │ │ │ │ │ ├── createCodeSandBox.js │ │ │ │ │ ├── dumi-previewer.vue │ │ │ │ │ └── highlight.js │ │ │ │ ├── config.js │ │ │ │ ├── enhanceApp.js │ │ │ │ ├── styles │ │ │ │ │ └── index.styl │ │ │ │ └── util.js │ │ │ ├── demos │ │ │ │ ├── guide │ │ │ │ │ ├── array-cards │ │ │ │ │ │ ├── effects-json-schema.vue │ │ │ │ │ │ ├── effects-markup-schema.vue │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── array-collapse │ │ │ │ │ │ ├── effects-json-schema.vue │ │ │ │ │ │ ├── effects-markup-schema.vue │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── array-items │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── array-table │ │ │ │ │ │ ├── effects-json-schema.vue │ │ │ │ │ │ ├── effects-markup-schema.vue │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── array-tabs │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── cascader │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── checkbox │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── date-picker │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── editable │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── form-button-group.vue │ │ │ │ │ ├── form-collapse │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── form-dialog │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── form-drawer │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── form-grid │ │ │ │ │ │ ├── form.vue │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── native.vue │ │ │ │ │ ├── form-item │ │ │ │ │ │ ├── bordered-none.vue │ │ │ │ │ │ ├── common.vue │ │ │ │ │ │ ├── feedback.vue │ │ │ │ │ │ ├── inset.vue │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ ├── size.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── form-layout │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── form-step │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── form-tab │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── form.vue │ │ │ │ │ ├── input │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── input-number │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── password │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── preview-text │ │ │ │ │ │ ├── base.vue │ │ │ │ │ │ └── extend.vue │ │ │ │ │ ├── radio │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── reset │ │ │ │ │ │ ├── base.vue │ │ │ │ │ │ ├── force.vue │ │ │ │ │ │ └── validate.vue │ │ │ │ │ ├── select │ │ │ │ │ │ ├── json-schema-async.vue │ │ │ │ │ │ ├── json-schema-sync.vue │ │ │ │ │ │ ├── markup-schema-async-search.vue │ │ │ │ │ │ ├── markup-schema-async.vue │ │ │ │ │ │ ├── markup-schema-sync.vue │ │ │ │ │ │ ├── template-async.vue │ │ │ │ │ │ └── template-sync.vue │ │ │ │ │ ├── space │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── submit │ │ │ │ │ │ ├── base.vue │ │ │ │ │ │ └── loading.vue │ │ │ │ │ ├── switch │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── time-picker │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── transfer │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ └── upload │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ └── template.vue │ │ │ │ └── index.vue │ │ │ ├── guide │ │ │ │ ├── array-cards.md │ │ │ │ ├── array-collapse.md │ │ │ │ ├── array-items.md │ │ │ │ ├── array-table.md │ │ │ │ ├── array-tabs.md │ │ │ │ ├── cascader.md │ │ │ │ ├── checkbox.md │ │ │ │ ├── date-picker.md │ │ │ │ ├── editable.md │ │ │ │ ├── form-button-group.md │ │ │ │ ├── form-collapse.md │ │ │ │ ├── form-dialog.md │ │ │ │ ├── form-drawer.md │ │ │ │ ├── form-grid.md │ │ │ │ ├── form-item.md │ │ │ │ ├── form-layout.md │ │ │ │ ├── form-step.md │ │ │ │ ├── form-tab.md │ │ │ │ ├── form.md │ │ │ │ ├── index.md │ │ │ │ ├── input-number.md │ │ │ │ ├── input.md │ │ │ │ ├── password.md │ │ │ │ ├── preview-text.md │ │ │ │ ├── radio.md │ │ │ │ ├── reset.md │ │ │ │ ├── select.md │ │ │ │ ├── space.md │ │ │ │ ├── submit.md │ │ │ │ ├── switch.md │ │ │ │ ├── time-picker.md │ │ │ │ ├── transfer.md │ │ │ │ └── upload.md │ │ │ └── README.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __builtins__ │ │ │ │ ├── configs │ │ │ │ │ └── index.ts │ │ │ │ ├── index.ts │ │ │ │ ├── shared │ │ │ │ │ ├── create-context.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── loading.ts │ │ │ │ │ ├── portal.ts │ │ │ │ │ ├── resolve-component.ts │ │ │ │ │ ├── transform-component.ts │ │ │ │ │ ├── types.ts │ │ │ │ │ └── utils.ts │ │ │ │ └── styles │ │ │ │ └── common.scss │ │ │ ├── array-base │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── array-cards │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── array-collapse │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── array-items │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── array-table │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── array-tabs │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── cascader │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── checkbox │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── date-picker │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── editable │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── el-form │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── el-form-item │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── form │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── form-button-group │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── form-collapse │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── form-dialog │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── form-drawer │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── form-grid │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── form-item │ │ │ │ ├── animation.scss │ │ │ │ ├── grid.scss │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ ├── style.ts │ │ │ │ └── var.scss │ │ │ ├── form-layout │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ ├── style.ts │ │ │ │ └── useResponsiveFormLayout.ts │ │ │ ├── form-step │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── form-tab │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── index.ts │ │ │ ├── input │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── input-number │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── password │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── preview-text │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── radio │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── reset │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── select │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── space │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── style.ts │ │ │ ├── submit │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── switch │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── time-picker │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── transfer │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ └── upload │ │ │ ├── index.ts │ │ │ └── style.ts │ │ ├── transformer.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── grid │ │ ├── .npmignore │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── index.ts │ │ │ └── observer.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── json-schema │ │ ├── .npmignore │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── __snapshots__ │ │ │ │ │ └── schema.spec.ts.snap │ │ │ │ ├── compiler.spec.ts │ │ │ │ ├── patches.spec.ts │ │ │ │ ├── schema.spec.ts │ │ │ │ ├── server-validate.spec.ts │ │ │ │ ├── shared.spec.ts │ │ │ │ ├── transformer.spec.ts │ │ │ │ └── traverse.spec.ts │ │ │ ├── compiler.ts │ │ │ ├── global.d.ts │ │ │ ├── index.ts │ │ │ ├── patches.ts │ │ │ ├── polyfills │ │ │ │ ├── index.ts │ │ │ │ └── SPECIFICATION_1_0.ts │ │ │ ├── schema.ts │ │ │ ├── shared.ts │ │ │ ├── transformer.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── next │ │ ├── __tests__ │ │ │ ├── moment.spec.ts │ │ │ └── sideEffects.spec.ts │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── build-style.ts │ │ ├── create-style.ts │ │ ├── docs │ │ │ ├── components │ │ │ │ ├── ArrayCards.md │ │ │ │ ├── ArrayCards.zh-CN.md │ │ │ │ ├── ArrayCollapse.md │ │ │ │ ├── ArrayCollapse.zh-CN.md │ │ │ │ ├── ArrayItems.md │ │ │ │ ├── ArrayItems.zh-CN.md │ │ │ │ ├── ArrayTable.md │ │ │ │ ├── ArrayTable.zh-CN.md │ │ │ │ ├── Cascader.md │ │ │ │ ├── Cascader.zh-CN.md │ │ │ │ ├── Checkbox.md │ │ │ │ ├── Checkbox.zh-CN.md │ │ │ │ ├── DatePicker.md │ │ │ │ ├── DatePicker.zh-CN.md │ │ │ │ ├── DatePicker2.md │ │ │ │ ├── DatePicker2.zh-CN.md │ │ │ │ ├── Editable.md │ │ │ │ ├── Editable.zh-CN.md │ │ │ │ ├── Form.md │ │ │ │ ├── Form.zh-CN.md │ │ │ │ ├── FormButtonGroup.md │ │ │ │ ├── FormButtonGroup.zh-CN.md │ │ │ │ ├── FormCollapse.md │ │ │ │ ├── FormCollapse.zh-CN.md │ │ │ │ ├── FormDialog.md │ │ │ │ ├── FormDialog.zh-CN.md │ │ │ │ ├── FormDrawer.md │ │ │ │ ├── FormDrawer.zh-CN.md │ │ │ │ ├── FormGrid.md │ │ │ │ ├── FormGrid.zh-CN.md │ │ │ │ ├── FormItem.md │ │ │ │ ├── FormItem.zh-CN.md │ │ │ │ ├── FormLayout.md │ │ │ │ ├── FormLayout.zh-CN.md │ │ │ │ ├── FormStep.md │ │ │ │ ├── FormStep.zh-CN.md │ │ │ │ ├── FormTab.md │ │ │ │ ├── FormTab.zh-CN.md │ │ │ │ ├── index.md │ │ │ │ ├── index.zh-CN.md │ │ │ │ ├── Input.md │ │ │ │ ├── Input.zh-CN.md │ │ │ │ ├── NumberPicker.md │ │ │ │ ├── NumberPicker.zh-CN.md │ │ │ │ ├── Password.md │ │ │ │ ├── Password.zh-CN.md │ │ │ │ ├── PreviewText.md │ │ │ │ ├── PreviewText.zh-CN.md │ │ │ │ ├── Radio.md │ │ │ │ ├── Radio.zh-CN.md │ │ │ │ ├── Reset.md │ │ │ │ ├── Reset.zh-CN.md │ │ │ │ ├── Select.md │ │ │ │ ├── Select.zh-CN.md │ │ │ │ ├── SelectTable.md │ │ │ │ ├── SelectTable.zh-CN.md │ │ │ │ ├── Space.md │ │ │ │ ├── Space.zh-CN.md │ │ │ │ ├── Submit.md │ │ │ │ ├── Submit.zh-CN.md │ │ │ │ ├── Switch.md │ │ │ │ ├── Switch.zh-CN.md │ │ │ │ ├── TimePicker.md │ │ │ │ ├── TimePicker.zh-CN.md │ │ │ │ ├── TimePicker2.md │ │ │ │ ├── TimePicker2.zh-CN.md │ │ │ │ ├── Transfer.md │ │ │ │ ├── Transfer.zh-CN.md │ │ │ │ ├── TreeSelect.md │ │ │ │ ├── TreeSelect.zh-CN.md │ │ │ │ ├── Upload.md │ │ │ │ └── Upload.zh-CN.md │ │ │ ├── index.md │ │ │ └── index.zh-CN.md │ │ ├── LESENCE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __builtins__ │ │ │ │ ├── empty.tsx │ │ │ │ ├── hooks │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── useClickAway.ts │ │ │ │ │ └── usePrefixCls.ts │ │ │ │ ├── icons.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── loading.ts │ │ │ │ ├── mapSize.ts │ │ │ │ ├── mapStatus.ts │ │ │ │ ├── moment.ts │ │ │ │ ├── pickDataProps.ts │ │ │ │ ├── portal.tsx │ │ │ │ ├── render.ts │ │ │ │ └── toArray.ts │ │ │ ├── array-base │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── array-cards │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── array-collapse │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── array-items │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── array-table │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── cascader │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── checkbox │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── date-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── date-picker2 │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── editable │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── form │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── form-button-group │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── form-collapse │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-dialog │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-drawer │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-grid │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── form-item │ │ │ │ ├── animation.scss │ │ │ │ ├── grid.scss │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ ├── scss │ │ │ │ │ └── variable.scss │ │ │ │ └── style.ts │ │ │ ├── form-layout │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ ├── style.ts │ │ │ │ └── useResponsiveFormLayout.ts │ │ │ ├── form-step │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-tab │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── index.ts │ │ │ ├── input │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── main.scss │ │ │ ├── number-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── password │ │ │ │ ├── index.tsx │ │ │ │ ├── PasswordStrength.tsx │ │ │ │ └── style.ts │ │ │ ├── preview-text │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── radio │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── reset │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── select │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── select-table │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ ├── style.ts │ │ │ │ ├── useCheckSlackly.tsx │ │ │ │ ├── useFilterOptions.tsx │ │ │ │ ├── useFlatOptions.tsx │ │ │ │ ├── useSize.tsx │ │ │ │ ├── useTitleAddon.tsx │ │ │ │ └── utils.ts │ │ │ ├── space │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── style.ts │ │ │ ├── submit │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── switch │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── time-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── time-picker2 │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── transfer │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── tree-select │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ └── upload │ │ │ ├── index.tsx │ │ │ ├── main.scss │ │ │ ├── placeholder.ts │ │ │ └── style.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── path │ │ ├── .npmignore │ │ ├── benchmark.ts │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── accessor.spec.ts │ │ │ │ ├── basic.spec.ts │ │ │ │ ├── match.spec.ts │ │ │ │ ├── parser.spec.ts │ │ │ │ └── share.spec.ts │ │ │ ├── contexts.ts │ │ │ ├── destructor.ts │ │ │ ├── index.ts │ │ │ ├── matcher.ts │ │ │ ├── parser.ts │ │ │ ├── shared.ts │ │ │ ├── tokenizer.ts │ │ │ ├── tokens.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── react │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── docs │ │ │ ├── api │ │ │ │ ├── components │ │ │ │ │ ├── ArrayField.md │ │ │ │ │ ├── ArrayField.zh-CN.md │ │ │ │ │ ├── ExpressionScope.md │ │ │ │ │ ├── ExpressionScope.zh-CN.md │ │ │ │ │ ├── Field.md │ │ │ │ │ ├── Field.zh-CN.md │ │ │ │ │ ├── FormConsumer.md │ │ │ │ │ ├── FormConsumer.zh-CN.md │ │ │ │ │ ├── FormProvider.md │ │ │ │ │ ├── FormProvider.zh-CN.md │ │ │ │ │ ├── ObjectField.md │ │ │ │ │ ├── ObjectField.zh-CN.md │ │ │ │ │ ├── RecordScope.md │ │ │ │ │ ├── RecordScope.zh-CN.md │ │ │ │ │ ├── RecordsScope.md │ │ │ │ │ ├── RecordsScope.zh-CN.md │ │ │ │ │ ├── RecursionField.md │ │ │ │ │ ├── RecursionField.zh-CN.md │ │ │ │ │ ├── SchemaField.md │ │ │ │ │ ├── SchemaField.zh-CN.md │ │ │ │ │ ├── VoidField.md │ │ │ │ │ └── VoidField.zh-CN.md │ │ │ │ ├── hooks │ │ │ │ │ ├── useExpressionScope.md │ │ │ │ │ ├── useExpressionScope.zh-CN.md │ │ │ │ │ ├── useField.md │ │ │ │ │ ├── useField.zh-CN.md │ │ │ │ │ ├── useFieldSchema.md │ │ │ │ │ ├── useFieldSchema.zh-CN.md │ │ │ │ │ ├── useForm.md │ │ │ │ │ ├── useForm.zh-CN.md │ │ │ │ │ ├── useFormEffects.md │ │ │ │ │ ├── useFormEffects.zh-CN.md │ │ │ │ │ ├── useParentForm.md │ │ │ │ │ └── useParentForm.zh-CN.md │ │ │ │ └── shared │ │ │ │ ├── connect.md │ │ │ │ ├── connect.zh-CN.md │ │ │ │ ├── context.md │ │ │ │ ├── context.zh-CN.md │ │ │ │ ├── mapProps.md │ │ │ │ ├── mapProps.zh-CN.md │ │ │ │ ├── mapReadPretty.md │ │ │ │ ├── mapReadPretty.zh-CN.md │ │ │ │ ├── observer.md │ │ │ │ ├── observer.zh-CN.md │ │ │ │ ├── Schema.md │ │ │ │ └── Schema.zh-CN.md │ │ │ ├── guide │ │ │ │ ├── architecture.md │ │ │ │ ├── architecture.zh-CN.md │ │ │ │ ├── concept.md │ │ │ │ ├── concept.zh-CN.md │ │ │ │ ├── index.md │ │ │ │ └── index.zh-CN.md │ │ │ ├── index.md │ │ │ └── index.zh-CN.md │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── expression.spec.tsx │ │ │ │ ├── field.spec.tsx │ │ │ │ ├── form.spec.tsx │ │ │ │ ├── schema.json.spec.tsx │ │ │ │ ├── schema.markup.spec.tsx │ │ │ │ └── shared.tsx │ │ │ ├── components │ │ │ │ ├── ArrayField.tsx │ │ │ │ ├── ExpressionScope.tsx │ │ │ │ ├── Field.tsx │ │ │ │ ├── FormConsumer.tsx │ │ │ │ ├── FormProvider.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── ObjectField.tsx │ │ │ │ ├── ReactiveField.tsx │ │ │ │ ├── RecordScope.tsx │ │ │ │ ├── RecordsScope.tsx │ │ │ │ ├── RecursionField.tsx │ │ │ │ ├── SchemaField.tsx │ │ │ │ └── VoidField.tsx │ │ │ ├── global.d.ts │ │ │ ├── hooks │ │ │ │ ├── index.ts │ │ │ │ ├── useAttach.ts │ │ │ │ ├── useExpressionScope.ts │ │ │ │ ├── useField.ts │ │ │ │ ├── useFieldSchema.ts │ │ │ │ ├── useForm.ts │ │ │ │ ├── useFormEffects.ts │ │ │ │ └── useParentForm.ts │ │ │ ├── index.ts │ │ │ ├── shared │ │ │ │ ├── connect.ts │ │ │ │ ├── context.ts │ │ │ │ ├── index.ts │ │ │ │ └── render.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── reactive │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── benchmark.ts │ │ ├── docs │ │ │ ├── api │ │ │ │ ├── action.md │ │ │ │ ├── action.zh-CN.md │ │ │ │ ├── autorun.md │ │ │ │ ├── autorun.zh-CN.md │ │ │ │ ├── batch.md │ │ │ │ ├── batch.zh-CN.md │ │ │ │ ├── define.md │ │ │ │ ├── define.zh-CN.md │ │ │ │ ├── hasCollected.md │ │ │ │ ├── hasCollected.zh-CN.md │ │ │ │ ├── markObservable.md │ │ │ │ ├── markObservable.zh-CN.md │ │ │ │ ├── markRaw.md │ │ │ │ ├── markRaw.zh-CN.md │ │ │ │ ├── model.md │ │ │ │ ├── model.zh-CN.md │ │ │ │ ├── observable.md │ │ │ │ ├── observable.zh-CN.md │ │ │ │ ├── observe.md │ │ │ │ ├── observe.zh-CN.md │ │ │ │ ├── raw.md │ │ │ │ ├── raw.zh-CN.md │ │ │ │ ├── react │ │ │ │ │ ├── observer.md │ │ │ │ │ └── observer.zh-CN.md │ │ │ │ ├── reaction.md │ │ │ │ ├── reaction.zh-CN.md │ │ │ │ ├── toJS.md │ │ │ │ ├── toJS.zh-CN.md │ │ │ │ ├── tracker.md │ │ │ │ ├── tracker.zh-CN.md │ │ │ │ ├── typeChecker.md │ │ │ │ ├── typeChecker.zh-CN.md │ │ │ │ ├── untracked.md │ │ │ │ ├── untracked.zh-CN.md │ │ │ │ └── vue │ │ │ │ ├── observer.md │ │ │ │ └── observer.zh-CN.md │ │ │ ├── guide │ │ │ │ ├── best-practice.md │ │ │ │ ├── best-practice.zh-CN.md │ │ │ │ ├── concept.md │ │ │ │ ├── concept.zh-CN.md │ │ │ │ ├── index.md │ │ │ │ └── index.zh-CN.md │ │ │ ├── index.md │ │ │ └── index.zh-CN.md │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── action.spec.ts │ │ │ │ ├── annotations.spec.ts │ │ │ │ ├── array.spec.ts │ │ │ │ ├── autorun.spec.ts │ │ │ │ ├── batch.spec.ts │ │ │ │ ├── collections-map.spec.ts │ │ │ │ ├── collections-set.spec.ts │ │ │ │ ├── collections-weakmap.spec.ts │ │ │ │ ├── collections-weakset.spec.ts │ │ │ │ ├── define.spec.ts │ │ │ │ ├── externals.spec.ts │ │ │ │ ├── hasCollected.spec.ts │ │ │ │ ├── observable.spec.ts │ │ │ │ ├── observe.spec.ts │ │ │ │ ├── tracker.spec.ts │ │ │ │ └── untracked.spec.ts │ │ │ ├── action.ts │ │ │ ├── annotations │ │ │ │ ├── box.ts │ │ │ │ ├── computed.ts │ │ │ │ ├── index.ts │ │ │ │ ├── observable.ts │ │ │ │ ├── ref.ts │ │ │ │ └── shallow.ts │ │ │ ├── array.ts │ │ │ ├── autorun.ts │ │ │ ├── batch.ts │ │ │ ├── checkers.ts │ │ │ ├── environment.ts │ │ │ ├── externals.ts │ │ │ ├── global.d.ts │ │ │ ├── handlers.ts │ │ │ ├── index.ts │ │ │ ├── internals.ts │ │ │ ├── model.ts │ │ │ ├── observable.ts │ │ │ ├── observe.ts │ │ │ ├── reaction.ts │ │ │ ├── tracker.ts │ │ │ ├── tree.ts │ │ │ ├── types.ts │ │ │ └── untracked.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── reactive-react │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── hooks │ │ │ │ ├── index.ts │ │ │ │ ├── useCompatEffect.ts │ │ │ │ ├── useCompatFactory.ts │ │ │ │ ├── useDidUpdate.ts │ │ │ │ ├── useForceUpdate.ts │ │ │ │ ├── useLayoutEffect.ts │ │ │ │ └── useObserver.ts │ │ │ ├── index.ts │ │ │ ├── observer.ts │ │ │ ├── shared │ │ │ │ ├── gc.ts │ │ │ │ ├── global.ts │ │ │ │ ├── immediate.ts │ │ │ │ └── index.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── reactive-test-cases-for-react18 │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── src │ │ │ ├── index.js │ │ │ └── MySlowList.js │ │ ├── template.ejs │ │ ├── tsconfig.build.json │ │ ├── tsconfig.json │ │ ├── webpack.base.ts │ │ ├── webpack.dev.ts │ │ └── webpack.prod.ts │ ├── reactive-vue │ │ ├── .npmignore │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ └── observer.spec.ts │ │ │ ├── hooks │ │ │ │ ├── index.ts │ │ │ │ └── useObserver.ts │ │ │ ├── index.ts │ │ │ ├── observer │ │ │ │ ├── collectData.ts │ │ │ │ ├── index.ts │ │ │ │ ├── observerInVue2.ts │ │ │ │ └── observerInVue3.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── shared │ │ ├── .npmignore │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ └── index.spec.ts │ │ │ ├── array.ts │ │ │ ├── case.ts │ │ │ ├── checkers.ts │ │ │ ├── clone.ts │ │ │ ├── compare.ts │ │ │ ├── defaults.ts │ │ │ ├── deprecate.ts │ │ │ ├── global.ts │ │ │ ├── index.ts │ │ │ ├── instanceof.ts │ │ │ ├── isEmpty.ts │ │ │ ├── merge.ts │ │ │ ├── middleware.ts │ │ │ ├── path.ts │ │ │ ├── string.ts │ │ │ ├── subscribable.ts │ │ │ └── uid.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── validator │ │ ├── .npmignore │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── parser.spec.ts │ │ │ │ ├── registry.spec.ts │ │ │ │ └── validator.spec.ts │ │ │ ├── formats.ts │ │ │ ├── index.ts │ │ │ ├── locale.ts │ │ │ ├── parser.ts │ │ │ ├── registry.ts │ │ │ ├── rules.ts │ │ │ ├── template.ts │ │ │ ├── types.ts │ │ │ └── validator.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ └── vue │ ├── .npmignore │ ├── bin │ │ ├── formily-vue-fix.js │ │ └── formily-vue-switch.js │ ├── docs │ │ ├── .vuepress │ │ │ ├── components │ │ │ │ ├── createCodeSandBox.js │ │ │ │ ├── dumi-previewer.vue │ │ │ │ └── highlight.js │ │ │ ├── config.js │ │ │ ├── enhanceApp.js │ │ │ └── styles │ │ │ └── index.styl │ │ ├── api │ │ │ ├── components │ │ │ │ ├── array-field.md │ │ │ │ ├── expression-scope.md │ │ │ │ ├── field.md │ │ │ │ ├── form-consumer.md │ │ │ │ ├── form-provider.md │ │ │ │ ├── object-field.md │ │ │ │ ├── recursion-field-with-component.md │ │ │ │ ├── recursion-field.md │ │ │ │ ├── schema-field-with-schema.md │ │ │ │ ├── schema-field.md │ │ │ │ └── void-field.md │ │ │ ├── hooks │ │ │ │ ├── use-field-schema.md │ │ │ │ ├── use-field.md │ │ │ │ ├── use-form-effects.md │ │ │ │ ├── use-form.md │ │ │ │ └── use-parent-form.md │ │ │ └── shared │ │ │ ├── connect.md │ │ │ ├── injections.md │ │ │ ├── map-props.md │ │ │ ├── map-read-pretty.md │ │ │ ├── observer.md │ │ │ └── schema.md │ │ ├── demos │ │ │ ├── api │ │ │ │ ├── components │ │ │ │ │ ├── array-field.vue │ │ │ │ │ ├── expression-scope.vue │ │ │ │ │ ├── field.vue │ │ │ │ │ ├── form-consumer.vue │ │ │ │ │ ├── form-provider.vue │ │ │ │ │ ├── object-field.vue │ │ │ │ │ ├── recursion-field-with-component.vue │ │ │ │ │ ├── recursion-field.vue │ │ │ │ │ ├── schema-field-with-schema.vue │ │ │ │ │ ├── schema-field.vue │ │ │ │ │ └── void-field.vue │ │ │ │ ├── hooks │ │ │ │ │ ├── use-field-schema.vue │ │ │ │ │ ├── use-field.vue │ │ │ │ │ ├── use-form-effects.vue │ │ │ │ │ ├── use-form.vue │ │ │ │ │ └── use-parent-form.vue │ │ │ │ └── shared │ │ │ │ ├── connect.vue │ │ │ │ ├── map-props.vue │ │ │ │ ├── map-read-pretty.vue │ │ │ │ └── observer.vue │ │ │ ├── index.vue │ │ │ └── questions │ │ │ ├── default-slot.vue │ │ │ ├── events.vue │ │ │ ├── named-slot.vue │ │ │ └── scoped-slot.vue │ │ ├── guide │ │ │ ├── architecture.md │ │ │ ├── concept.md │ │ │ └── README.md │ │ ├── questions │ │ │ └── README.md │ │ └── README.md │ ├── package.json │ ├── README.md │ ├── rollup.config.js │ ├── scripts │ │ ├── postinstall.js │ │ ├── switch-cli.js │ │ └── utils.js │ ├── src │ │ ├── __tests__ │ │ │ ├── expression.scope.spec.ts │ │ │ ├── field.spec.ts │ │ │ ├── form.spec.ts │ │ │ ├── schema.json.spec.ts │ │ │ ├── schema.markup.spec.ts │ │ │ ├── shared.spec.ts │ │ │ └── utils.spec.ts │ │ ├── components │ │ │ ├── ArrayField.ts │ │ │ ├── ExpressionScope.ts │ │ │ ├── Field.ts │ │ │ ├── FormConsumer.ts │ │ │ ├── FormProvider.ts │ │ │ ├── index.ts │ │ │ ├── ObjectField.ts │ │ │ ├── ReactiveField.ts │ │ │ ├── RecursionField.ts │ │ │ ├── SchemaField.ts │ │ │ └── VoidField.ts │ │ ├── global.d.ts │ │ ├── hooks │ │ │ ├── index.ts │ │ │ ├── useAttach.ts │ │ │ ├── useField.ts │ │ │ ├── useFieldSchema.ts │ │ │ ├── useForm.ts │ │ │ ├── useFormEffects.ts │ │ │ ├── useInjectionCleaner.ts │ │ │ └── useParentForm.ts │ │ ├── index.ts │ │ ├── shared │ │ │ ├── connect.ts │ │ │ ├── context.ts │ │ │ ├── createForm.ts │ │ │ ├── fragment.ts │ │ │ ├── h.ts │ │ │ └── index.ts │ │ ├── types │ │ │ └── index.ts │ │ ├── utils │ │ │ ├── formatVNodeData.ts │ │ │ ├── getFieldProps.ts │ │ │ ├── getRawComponent.ts │ │ │ └── resolveSchemaProps.ts │ │ └── vue2-components.ts │ ├── tsconfig.build.json │ ├── tsconfig.json │ └── tsconfig.types.json ├── README.md ├── README.zh-cn.md ├── scripts │ ├── build-style │ │ ├── buildAllStyles.ts │ │ ├── copy.ts │ │ ├── helper.ts │ │ └── index.ts │ └── rollup.base.js ├── tsconfig.build.json ├── tsconfig.jest.json ├── tsconfig.json └── yarn.lock ``` # Files -------------------------------------------------------------------------------- /packages/antd/docs/components/DatePicker.md: -------------------------------------------------------------------------------- ```markdown 1 | # DatePicker 2 | 3 | > Date Picker 4 | 5 | ## Markup Schema example 6 | 7 | ```tsx 8 | import React from 'react' 9 | import { DatePicker, FormItem, FormButtonGroup, Submit } from '@formily/antd' 10 | import { createForm } from '@formily/core' 11 | import { FormProvider, createSchemaField } from '@formily/react' 12 | 13 | const SchemaField = createSchemaField({ 14 | components: { 15 | DatePicker, 16 | FormItem, 17 | }, 18 | }) 19 | 20 | const form = createForm() 21 | 22 | export default () => ( 23 | <FormProvider form={form}> 24 | <SchemaField> 25 | <SchemaField.String 26 | name="date" 27 | required 28 | title="normal date" 29 | x-decorator="FormItem" 30 | x-component="DatePicker" 31 | /> 32 | <SchemaField.String 33 | name="week" 34 | title="Week Selection" 35 | x-decorator="FormItem" 36 | x-component="DatePicker" 37 | x-component-props={{ 38 | picker: 'week', 39 | }} 40 | /> 41 | <SchemaField.String 42 | name="month" 43 | title="Month Selection" 44 | x-decorator="FormItem" 45 | x-component="DatePicker" 46 | x-component-props={{ 47 | picker: 'month', 48 | }} 49 | /> 50 | <SchemaField.String 51 | name="quarter" 52 | title="Financial Year Selection" 53 | x-decorator="FormItem" 54 | x-component="DatePicker" 55 | x-component-props={{ 56 | picker: 'quarter', 57 | }} 58 | /> 59 | <SchemaField.String 60 | name="year" 61 | title="Year selection" 62 | x-decorator="FormItem" 63 | x-component="DatePicker" 64 | x-component-props={{ 65 | picker: 'year', 66 | }} 67 | /> 68 | <SchemaField.String 69 | name="[startDate,endDate]" 70 | title="Date Range" 71 | x-decorator="FormItem" 72 | x-component="DatePicker.RangePicker" 73 | x-component-props={{ 74 | showTime: true, 75 | }} 76 | /> 77 | <SchemaField.String 78 | name="range_week" 79 | title="Week range selection" 80 | x-decorator="FormItem" 81 | x-component="DatePicker.RangePicker" 82 | x-component-props={{ 83 | picker: 'week', 84 | }} 85 | /> 86 | <SchemaField.String 87 | name="range_month" 88 | title="Month Range Selection" 89 | x-decorator="FormItem" 90 | x-component="DatePicker.RangePicker" 91 | x-component-props={{ 92 | picker: 'month', 93 | }} 94 | /> 95 | <SchemaField.String 96 | name="range_quarter" 97 | title="Financial Year Range Selection" 98 | x-decorator="FormItem" 99 | x-component="DatePicker.RangePicker" 100 | x-component-props={{ 101 | picker: 'quarter', 102 | }} 103 | /> 104 | <SchemaField.String 105 | name="range_year" 106 | title="Year range selection" 107 | x-decorator="FormItem" 108 | x-component="DatePicker.RangePicker" 109 | x-component-props={{ 110 | picker: 'year', 111 | }} 112 | /> 113 | </SchemaField> 114 | <FormButtonGroup> 115 | <Submit onSubmit={console.log}>Submit</Submit> 116 | </FormButtonGroup> 117 | </FormProvider> 118 | ) 119 | ``` 120 | 121 | ## JSON Schema case 122 | 123 | ```tsx 124 | import React from 'react' 125 | import { DatePicker, FormItem, FormButtonGroup, Submit } from '@formily/antd' 126 | import { createForm } from '@formily/core' 127 | import { FormProvider, createSchemaField } from '@formily/react' 128 | 129 | const SchemaField = createSchemaField({ 130 | components: { 131 | DatePicker, 132 | FormItem, 133 | }, 134 | }) 135 | 136 | const form = createForm() 137 | 138 | const schema = { 139 | type: 'object', 140 | properties: { 141 | date: { 142 | title: 'Normal date', 143 | 'x-decorator': 'FormItem', 144 | 'x-component': 'DatePicker', 145 | type: 'string', 146 | }, 147 | week: { 148 | title: 'Week Selection', 149 | 'x-decorator': 'FormItem', 150 | 'x-component': 'DatePicker', 151 | 'x-component-props': { 152 | picker: 'week', 153 | }, 154 | type: 'string', 155 | }, 156 | month: { 157 | title: 'Month Selection', 158 | 'x-decorator': 'FormItem', 159 | 'x-component': 'DatePicker', 160 | 'x-component-props': { 161 | picker: 'month', 162 | }, 163 | type: 'string', 164 | }, 165 | quarter: { 166 | title: 'Fiscal Year Selection', 167 | 'x-decorator': 'FormItem', 168 | 'x-component': 'DatePicker', 169 | 'x-component-props': { 170 | picker: 'quarter', 171 | }, 172 | type: 'string', 173 | }, 174 | year: { 175 | title: 'Year selection', 176 | 'x-decorator': 'FormItem', 177 | 'x-component': 'DatePicker', 178 | 'x-component-props': { 179 | picker: 'year', 180 | }, 181 | type: 'string', 182 | }, 183 | '[startDate,endDate]': { 184 | title: 'Date range', 185 | 'x-decorator': 'FormItem', 186 | 'x-component': 'DatePicker.RangePicker', 187 | 'x-component-props': { 188 | showTime: true, 189 | }, 190 | type: 'string', 191 | }, 192 | range_week: { 193 | title: 'Week range selection', 194 | 'x-decorator': 'FormItem', 195 | 'x-component': 'DatePicker.RangePicker', 196 | 'x-component-props': { 197 | picker: 'week', 198 | }, 199 | type: 'string', 200 | }, 201 | range_month: { 202 | title: 'Month Range Selection', 203 | 'x-decorator': 'FormItem', 204 | 'x-component': 'DatePicker.RangePicker', 205 | 'x-component-props': { 206 | picker: 'month', 207 | }, 208 | type: 'string', 209 | }, 210 | range_quarter: { 211 | title: 'Financial year range selection', 212 | 'x-decorator': 'FormItem', 213 | 'x-component': 'DatePicker.RangePicker', 214 | 'x-component-props': { 215 | picker: 'quarter', 216 | }, 217 | type: 'string', 218 | }, 219 | range_year: { 220 | name: 'range_year', 221 | title: 'Year range selection', 222 | 'x-decorator': 'FormItem', 223 | 'x-component': 'DatePicker.RangePicker', 224 | 'x-component-props': { 225 | picker: 'year', 226 | }, 227 | type: 'string', 228 | }, 229 | }, 230 | } 231 | 232 | export default () => ( 233 | <FormProvider form={form}> 234 | <SchemaField schema={schema} /> 235 | <FormButtonGroup> 236 | <Submit onSubmit={console.log}>Submit</Submit> 237 | </FormButtonGroup> 238 | </FormProvider> 239 | ) 240 | ``` 241 | 242 | ## Pure JSX case 243 | 244 | ```tsx 245 | import React from 'react' 246 | import { DatePicker, FormItem, FormButtonGroup, Submit } from '@formily/antd' 247 | import { createForm } from '@formily/core' 248 | import { FormProvider, Field } from '@formily/react' 249 | 250 | const form = createForm() 251 | 252 | export default () => ( 253 | <FormProvider form={form}> 254 | <Field 255 | name="date" 256 | title="date selection" 257 | decorator={[FormItem]} 258 | component={[DatePicker]} 259 | /> 260 | <Field 261 | name="week" 262 | title="Week Selection" 263 | decorator={[FormItem]} 264 | component={[ 265 | DatePicker, 266 | { 267 | picker: 'week', 268 | }, 269 | ]} 270 | /> 271 | <Field 272 | name="quarter" 273 | title="Financial Year Selection" 274 | decorator={[FormItem]} 275 | component={[ 276 | DatePicker, 277 | { 278 | picker: 'month', 279 | }, 280 | ]} 281 | /> 282 | <Field 283 | name="year" 284 | title="Year selection" 285 | decorator={[FormItem]} 286 | component={[ 287 | DatePicker, 288 | { 289 | picker: 'year', 290 | }, 291 | ]} 292 | /> 293 | <Field 294 | name="[startDate,endDate]" 295 | title="Date range selection" 296 | decorator={[FormItem]} 297 | component={[DatePicker.RangePicker]} 298 | /> 299 | <Field 300 | name="range_week" 301 | title="Week range selection" 302 | decorator={[FormItem]} 303 | component={[ 304 | DatePicker.RangePicker, 305 | { 306 | picker: 'week', 307 | }, 308 | ]} 309 | /> 310 | <Field 311 | name="range_month" 312 | title="Month Range Selection" 313 | decorator={[FormItem]} 314 | component={[ 315 | DatePicker.RangePicker, 316 | { 317 | picker: 'month', 318 | }, 319 | ]} 320 | /> 321 | <Field 322 | name="range_quarter" 323 | title="Financial Year Range Selection" 324 | decorator={[FormItem]} 325 | component={[ 326 | DatePicker.RangePicker, 327 | { 328 | picker: 'quarter', 329 | }, 330 | ]} 331 | /> 332 | <Field 333 | name="range_year" 334 | title="Year range selection" 335 | decorator={[FormItem]} 336 | component={[ 337 | DatePicker.RangePicker, 338 | { 339 | picker: 'year', 340 | }, 341 | ]} 342 | /> 343 | <FormButtonGroup> 344 | <Submit onSubmit={console.log}>Submit</Submit> 345 | </FormButtonGroup> 346 | </FormProvider> 347 | ) 348 | ``` 349 | 350 | ## API 351 | 352 | Reference https://ant.design/components/date-picker-cn/ 353 | ``` -------------------------------------------------------------------------------- /packages/vue/src/components/ReactiveField.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { inject, provide, Ref, ref, shallowRef, watch, isVue2 } from 'vue-demi' 2 | import { GeneralField, isVoidField } from '@formily/core' 3 | import { each, FormPath } from '@formily/shared' 4 | import { observer } from '@formily/reactive-vue' 5 | import { toJS, reaction } from '@formily/reactive' 6 | import { SchemaOptionsSymbol, FieldSymbol, h, Fragment } from '../shared' 7 | import { useAttach } from '../hooks/useAttach' 8 | import { useField, useForm } from '../hooks' 9 | 10 | import type { 11 | IReactiveFieldProps, 12 | VueComponentProps, 13 | DefineComponent, 14 | } from '../types' 15 | import type { VNode } from 'vue' 16 | 17 | function isVueOptions(options: Record<string, unknown>) { 18 | return ( 19 | typeof options.template === 'string' || 20 | typeof options.render === 'function' || 21 | typeof options.setup === 'function' 22 | ) 23 | } 24 | 25 | const wrapFragment = (childNodes: VNode[] | VNode): VNode => { 26 | if (!Array.isArray(childNodes)) { 27 | return childNodes 28 | } 29 | if (childNodes.length > 1) { 30 | return h(Fragment, {}, { default: () => childNodes }) 31 | } 32 | return childNodes[0] 33 | } 34 | 35 | const resolveComponent = (render: () => unknown[], extra?: any) => { 36 | if (extra === undefined || extra === null) { 37 | return render 38 | } 39 | if (typeof extra === 'string') { 40 | return () => [...render(), extra] 41 | } 42 | // not component 43 | if (!isVueOptions(extra) && typeof extra !== 'function') { 44 | return render 45 | } 46 | // for scoped slot 47 | if (extra.length > 1 || extra?.render?.length > 1) { 48 | return (scopedProps: VueComponentProps<any>) => [ 49 | ...render(), 50 | h(extra, { props: scopedProps }, {}), 51 | ] 52 | } 53 | return () => [...render(), h(extra, {}, {})] 54 | } 55 | 56 | const mergeSlots = ( 57 | field: GeneralField, 58 | slots: Record<string, any>, 59 | content: any 60 | ): Record<string, (...args: any) => any[]> => { 61 | const slotNames = Object.keys(slots) 62 | if (!slotNames.length) { 63 | if (!content) { 64 | return {} 65 | } 66 | if (typeof content === 'string') { 67 | return { 68 | default: resolveComponent(() => [], content), 69 | } 70 | } 71 | } 72 | const patchSlot = 73 | (slotName: string) => 74 | (...originArgs) => 75 | slots[slotName]?.({ field, form: field.form, ...originArgs[0] }) ?? [] 76 | 77 | const patchedSlots: Record<string, (...args: any) => unknown[]> = {} 78 | slotNames.forEach((name) => { 79 | patchedSlots[name] = patchSlot(name) 80 | }) 81 | 82 | // for named slots 83 | if (content && typeof content === 'object' && !isVueOptions(content)) { 84 | Object.keys(content).forEach((key) => { 85 | const child = content[key] 86 | const slot = patchedSlots[key] ?? (() => []) 87 | patchedSlots[key] = resolveComponent(slot, child) 88 | }) 89 | return patchedSlots 90 | } 91 | // maybe default slot is empty 92 | patchedSlots['default'] = resolveComponent( 93 | patchedSlots['default'] ?? (() => []), 94 | content 95 | ) 96 | return patchedSlots 97 | } 98 | 99 | const createFieldInVue2 = (innerCreateField) => { 100 | return () => { 101 | let res: GeneralField 102 | const disposer = reaction(() => { 103 | res = innerCreateField() 104 | }) 105 | disposer() 106 | return res 107 | } 108 | } 109 | 110 | export default observer({ 111 | name: 'ReactiveField', 112 | props: { 113 | fieldType: { 114 | type: String, 115 | default: 'Field', 116 | }, 117 | fieldProps: { 118 | type: Object, 119 | default: () => ({}), 120 | }, 121 | }, 122 | setup(props: IReactiveFieldProps, { slots }) { 123 | const formRef = useForm() 124 | const parentRef = useField() 125 | const optionsRef = inject(SchemaOptionsSymbol, ref(null)) 126 | let createField = () => 127 | formRef?.value?.[`create${props.fieldType}`]?.({ 128 | ...props.fieldProps, 129 | basePath: props.fieldProps?.basePath ?? parentRef.value?.address, 130 | }) 131 | 132 | if (isVue2) { 133 | createField = createFieldInVue2(createField) 134 | } 135 | 136 | const fieldRef = shallowRef(createField()) as Ref<GeneralField> 137 | watch( 138 | () => props.fieldProps, 139 | () => (fieldRef.value = createField()) 140 | ) 141 | useAttach(fieldRef) 142 | provide(FieldSymbol, fieldRef) 143 | return () => { 144 | const field = fieldRef.value 145 | const options = optionsRef.value 146 | if (!field) { 147 | return slots.default?.() 148 | } 149 | if (field.display !== 'visible') { 150 | return h('template', {}, {}) 151 | } 152 | 153 | const mergedSlots = mergeSlots(field, slots, field.content) 154 | 155 | const renderDecorator = (childNodes: any[]) => { 156 | if (!field.decoratorType) { 157 | return wrapFragment(childNodes) 158 | } 159 | const finalComponent = 160 | FormPath.getIn(options?.components, field.decoratorType as string) ?? 161 | field.decoratorType 162 | const componentAttrs = toJS(field.decorator[1]) || {} 163 | 164 | const events: Record<string, any> = {} 165 | each(componentAttrs, (value, eventKey) => { 166 | const onEvent = eventKey.startsWith('on') 167 | const atEvent = eventKey.startsWith('@') 168 | if (!onEvent && !atEvent) return 169 | if (onEvent) { 170 | const eventName = `${eventKey[2].toLowerCase()}${eventKey.slice(3)}` 171 | // '@xxx' has higher priority 172 | events[eventName] = events[eventName] || value 173 | } else if (atEvent) { 174 | const eventName = eventKey.slice(1) 175 | events[eventName] = value 176 | delete componentAttrs[eventKey] 177 | } 178 | }) 179 | 180 | const componentData = { 181 | attrs: componentAttrs, 182 | style: componentAttrs?.style, 183 | class: componentAttrs?.class, 184 | on: events, 185 | } 186 | delete componentData.attrs.style 187 | delete componentData.attrs.class 188 | 189 | return h(finalComponent, componentData, { 190 | default: () => childNodes, 191 | }) 192 | } 193 | 194 | const renderComponent = () => { 195 | if (!field.componentType) return wrapFragment(mergedSlots?.default?.()) 196 | 197 | const component = 198 | FormPath.getIn(options?.components, field.componentType as string) ?? 199 | field.componentType 200 | 201 | const originData = toJS(field.component[1]) || {} 202 | const events = {} as Record<string, any> 203 | const originChange = originData['@change'] || originData['onChange'] 204 | const originFocus = originData['@focus'] || originData['onFocus'] 205 | const originBlur = originData['@blur'] || originData['onBlur'] 206 | 207 | each(originData, (value, eventKey) => { 208 | const onEvent = eventKey.startsWith('on') 209 | const atEvent = eventKey.startsWith('@') 210 | if (!onEvent && !atEvent) return 211 | if (onEvent) { 212 | const eventName = `${eventKey[2].toLowerCase()}${eventKey.slice(3)}` 213 | // '@xxx' has higher priority 214 | events[eventName] = events[eventName] || value 215 | } else if (atEvent) { 216 | const eventName = eventKey.slice(1) 217 | events[eventName] = value 218 | delete originData[eventKey] 219 | } 220 | }) 221 | 222 | events.change = (...args: any[]) => { 223 | if (!isVoidField(field)) field.onInput(...args) 224 | originChange?.(...args) 225 | } 226 | events.focus = (...args: any[]) => { 227 | if (!isVoidField(field)) field.onFocus(...args) 228 | originFocus?.(...args) 229 | } 230 | events.blur = (...args: any[]) => { 231 | if (!isVoidField(field)) field.onBlur(...args) 232 | originBlur?.(...args) 233 | } 234 | 235 | const componentData = { 236 | attrs: { 237 | disabled: !isVoidField(field) 238 | ? field.pattern === 'disabled' || field.pattern === 'readPretty' 239 | : undefined, 240 | readOnly: !isVoidField(field) 241 | ? field.pattern === 'readOnly' 242 | : undefined, 243 | ...originData, 244 | value: !isVoidField(field) ? field.value : undefined, 245 | }, 246 | style: originData?.style, 247 | class: originData?.class, 248 | on: events, 249 | } 250 | delete componentData.attrs.style 251 | delete componentData.attrs.class 252 | 253 | return h(component, componentData, mergedSlots) 254 | } 255 | 256 | return renderDecorator([renderComponent()]) 257 | } 258 | }, 259 | } as unknown as DefineComponent<IReactiveFieldProps>) 260 | ``` -------------------------------------------------------------------------------- /packages/path/src/tokenizer.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { 2 | Token, 3 | nameTok, 4 | colonTok, 5 | dotTok, 6 | starTok, 7 | dbStarTok, 8 | bangTok, 9 | bracketLTok, 10 | bracketRTok, 11 | bracketDRTok, 12 | expandTok, 13 | parenLTok, 14 | parenRTok, 15 | commaTok, 16 | eofTok, 17 | ignoreTok, 18 | braceLTok, 19 | braceRTok, 20 | bracketDLTok, 21 | } from './tokens' 22 | import { bracketDContext, Context } from './contexts' 23 | 24 | const nonASCIIWhitespace = /[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/ 25 | 26 | const fullCharCodeAtPos = (input: string, pos: number) => { 27 | if (String.fromCharCode) return input.codePointAt(pos) 28 | const code = input.charCodeAt(pos) 29 | if (code <= 0xd7ff || code >= 0xe000) return code 30 | 31 | const next = input.charCodeAt(pos + 1) 32 | return (code << 10) + next - 0x35fdc00 33 | } 34 | 35 | const isRewordCode = (code: number) => 36 | code === 42 || 37 | code === 46 || 38 | code === 33 || 39 | code === 91 || 40 | code === 93 || 41 | code === 40 || 42 | code === 41 || 43 | code === 44 || 44 | code === 58 || 45 | code === 126 || 46 | code === 123 || 47 | code === 125 48 | 49 | const getError = (message?: string, props?: any) => { 50 | const err = new Error(message) 51 | Object.assign(err, props) 52 | return err 53 | } 54 | 55 | const slice = (string: string, start: number, end: number) => { 56 | let str = '' 57 | for (let i = start; i < end; i++) { 58 | const ch = string.charAt(i) 59 | if (ch !== '\\') { 60 | str += ch 61 | } 62 | } 63 | return str 64 | } 65 | 66 | export class Tokenizer { 67 | public input: string 68 | public state: { 69 | context: Context[] 70 | type: Token 71 | pos: number 72 | value?: any 73 | } 74 | public type_: Token 75 | constructor(input: string) { 76 | this.input = input 77 | this.state = { 78 | context: [], 79 | type: null, 80 | pos: 0, 81 | } 82 | this.type_ = null 83 | } 84 | 85 | curContext() { 86 | return this.state.context[this.state.context.length - 1] 87 | } 88 | 89 | includesContext(context: Context) { 90 | for (let len = this.state.context.length - 1; len >= 0; len--) { 91 | if (this.state.context[len] === context) { 92 | return true 93 | } 94 | } 95 | return false 96 | } 97 | 98 | unexpect(type?: Token) { 99 | type = type || this.state.type 100 | return getError( 101 | `Unexpect token "${type.flag}" in ${this.state.pos} char.`, 102 | { 103 | pos: this.state.pos, 104 | } 105 | ) 106 | } 107 | 108 | expectNext(type?: Token, next?: Token) { 109 | if (type && type.expectNext) { 110 | if (next && !type.expectNext.call(this, next)) { 111 | throw getError( 112 | `Unexpect token "${next.flag}" token should not be behind "${type.flag}" token.(${this.state.pos}th char)`, 113 | { 114 | pos: this.state.pos, 115 | } 116 | ) 117 | } 118 | } 119 | } 120 | 121 | expectPrev(type?: Token, prev?: Token) { 122 | if (type && type.expectPrev) { 123 | if (prev && !type.expectPrev.call(this, prev)) { 124 | throw getError( 125 | `Unexpect token "${type.flag}" should not be behind "${prev.flag}"(${this.state.pos}th char).`, 126 | { 127 | pos: this.state.pos, 128 | } 129 | ) 130 | } 131 | } 132 | } 133 | 134 | match(type?: Token) { 135 | return this.state.type === type 136 | } 137 | 138 | skipSpace() { 139 | if (this.curContext() === bracketDContext) return 140 | loop: while (this.state.pos < this.input.length) { 141 | const ch = this.input.charCodeAt(this.state.pos) 142 | switch (ch) { 143 | case 32: 144 | case 160: 145 | ++this.state.pos 146 | break 147 | 148 | case 13: 149 | if (this.input.charCodeAt(this.state.pos + 1) === 10) { 150 | ++this.state.pos 151 | } 152 | 153 | case 10: 154 | case 8232: 155 | case 8233: 156 | ++this.state.pos 157 | break 158 | default: 159 | if ( 160 | (ch > 8 && ch < 14) || 161 | (ch >= 5760 && nonASCIIWhitespace.test(String.fromCharCode(ch))) 162 | ) { 163 | ++this.state.pos 164 | } else { 165 | break loop 166 | } 167 | } 168 | } 169 | } 170 | 171 | next() { 172 | this.type_ = this.state.type 173 | if (this.input.length <= this.state.pos) { 174 | return this.finishToken(eofTok) 175 | } 176 | this.skipSpace() 177 | this.readToken( 178 | this.getCode(), 179 | this.state.pos > 0 ? this.getCode(this.state.pos - 1) : -Infinity 180 | ) 181 | } 182 | 183 | getCode(pos = this.state.pos) { 184 | return fullCharCodeAtPos(this.input, pos) 185 | } 186 | 187 | eat(type) { 188 | if (this.match(type)) { 189 | this.next() 190 | return true 191 | } else { 192 | return false 193 | } 194 | } 195 | 196 | readKeyWord() { 197 | let startPos = this.state.pos, 198 | string = '' 199 | while (true) { 200 | const code = this.getCode() 201 | const prevCode = this.getCode(this.state.pos - 1) 202 | if (this.input.length === this.state.pos) { 203 | string = slice(this.input, startPos, this.state.pos + 1) 204 | break 205 | } 206 | if (!isRewordCode(code) || prevCode === 92) { 207 | if ( 208 | code === 32 || 209 | code === 160 || 210 | code === 10 || 211 | code === 8232 || 212 | code === 8233 213 | ) { 214 | string = slice(this.input, startPos, this.state.pos) 215 | break 216 | } 217 | if (code === 13 && this.input.charCodeAt(this.state.pos + 1) === 10) { 218 | string = slice(this.input, startPos, this.state.pos) 219 | break 220 | } 221 | if ( 222 | (code > 8 && code < 14) || 223 | (code >= 5760 && nonASCIIWhitespace.test(String.fromCharCode(code))) 224 | ) { 225 | string = slice(this.input, startPos, this.state.pos) 226 | break 227 | } 228 | this.state.pos++ 229 | } else { 230 | string = slice(this.input, startPos, this.state.pos) 231 | break 232 | } 233 | } 234 | 235 | this.finishToken(nameTok, string) 236 | } 237 | 238 | readIgnoreString() { 239 | let startPos = this.state.pos, 240 | prevCode, 241 | string = '' 242 | while (true) { 243 | const code = this.getCode() 244 | if (this.state.pos >= this.input.length) break 245 | if ((code === 91 || code === 93) && prevCode === 92) { 246 | this.state.pos++ 247 | prevCode = '' 248 | } else if (code == 93 && prevCode === 93) { 249 | string = this.input 250 | .slice(startPos, this.state.pos - 1) 251 | .replace(/\\([\[\]])/g, '$1') 252 | this.state.pos++ 253 | break 254 | } else { 255 | this.state.pos++ 256 | prevCode = code 257 | } 258 | } 259 | 260 | this.finishToken(ignoreTok, string) 261 | this.finishToken(bracketDRTok) 262 | } 263 | 264 | finishToken(type: Token, value?: any) { 265 | const preType = this.state.type 266 | this.state.type = type 267 | if (value !== undefined) this.state.value = value 268 | this.expectNext(preType, type) 269 | this.expectPrev(type, preType) 270 | if (type.updateContext) { 271 | type.updateContext.call(this, preType) 272 | } 273 | } 274 | 275 | readToken(code: number, prevCode: number) { 276 | if (prevCode === 92) { 277 | return this.readKeyWord() 278 | } 279 | if (this.input.length <= this.state.pos) { 280 | this.finishToken(eofTok) 281 | } else if (this.curContext() === bracketDContext) { 282 | this.readIgnoreString() 283 | } else if (code === 123) { 284 | this.state.pos++ 285 | this.finishToken(braceLTok) 286 | } else if (code === 125) { 287 | this.state.pos++ 288 | this.finishToken(braceRTok) 289 | } else if (code === 42) { 290 | this.state.pos++ 291 | if (this.getCode() === 42) { 292 | this.state.pos++ 293 | return this.finishToken(dbStarTok) 294 | } 295 | this.finishToken(starTok) 296 | } else if (code === 33) { 297 | this.state.pos++ 298 | this.finishToken(bangTok) 299 | } else if (code === 46) { 300 | this.state.pos++ 301 | this.finishToken(dotTok) 302 | } else if (code === 91) { 303 | this.state.pos++ 304 | if (this.getCode() === 91) { 305 | this.state.pos++ 306 | return this.finishToken(bracketDLTok) 307 | } 308 | this.finishToken(bracketLTok) 309 | } else if (code === 126) { 310 | this.state.pos++ 311 | this.finishToken(expandTok) 312 | } else if (code === 93) { 313 | this.state.pos++ 314 | this.finishToken(bracketRTok) 315 | } else if (code === 40) { 316 | this.state.pos++ 317 | this.finishToken(parenLTok) 318 | } else if (code === 41) { 319 | this.state.pos++ 320 | this.finishToken(parenRTok) 321 | } else if (code === 44) { 322 | this.state.pos++ 323 | this.finishToken(commaTok) 324 | } else if (code === 58) { 325 | this.state.pos++ 326 | this.finishToken(colonTok) 327 | } else { 328 | this.readKeyWord() 329 | } 330 | } 331 | } 332 | ``` -------------------------------------------------------------------------------- /packages/reactive/src/handlers.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { 2 | bindTargetKeyWithCurrentReaction, 3 | runReactionsFromTargetKey, 4 | } from './reaction' 5 | import { ProxyRaw, RawProxy } from './environment' 6 | import { isObservable, isSupportObservable } from './externals' 7 | import { createObservable } from './internals' 8 | 9 | const wellKnownSymbols = new Set( 10 | Object.getOwnPropertyNames(Symbol).reduce((buf: Symbol[], key) => { 11 | if (key === 'arguments' || key === 'caller') return buf 12 | const value = Symbol[key] 13 | if (typeof value === 'symbol') return buf.concat(value) 14 | return buf 15 | }, []) 16 | ) 17 | 18 | const hasOwnProperty = Object.prototype.hasOwnProperty 19 | 20 | function findObservable(target: any, key: PropertyKey, value: any) { 21 | const observableObj = RawProxy.get(value) 22 | if (observableObj) { 23 | return observableObj 24 | } 25 | if (!isObservable(value) && isSupportObservable(value)) { 26 | return createObservable(target, key, value) 27 | } 28 | return value 29 | } 30 | 31 | function patchIterator( 32 | target: any, 33 | key: PropertyKey, 34 | iterator: IterableIterator<any>, 35 | isEntries: boolean 36 | ) { 37 | const originalNext = iterator.next 38 | iterator.next = () => { 39 | let { done, value } = originalNext.call(iterator) 40 | if (!done) { 41 | if (isEntries) { 42 | value[1] = findObservable(target, key, value[1]) 43 | } else { 44 | value = findObservable(target, key, value) 45 | } 46 | } 47 | return { done, value } 48 | } 49 | return iterator 50 | } 51 | 52 | const instrumentations = { 53 | has(key: PropertyKey) { 54 | const target = ProxyRaw.get(this) 55 | const proto = Reflect.getPrototypeOf(this) as any 56 | bindTargetKeyWithCurrentReaction({ target, key, type: 'has' }) 57 | return proto.has.apply(target, arguments) 58 | }, 59 | get(key: PropertyKey) { 60 | const target = ProxyRaw.get(this) 61 | const proto = Reflect.getPrototypeOf(this) as any 62 | bindTargetKeyWithCurrentReaction({ target, key, type: 'get' }) 63 | return findObservable(target, key, proto.get.apply(target, arguments)) 64 | }, 65 | add(key: PropertyKey) { 66 | const target = ProxyRaw.get(this) 67 | const proto = Reflect.getPrototypeOf(this) as any 68 | const hadKey = proto.has.call(target, key) 69 | // forward the operation before queueing reactions 70 | const result = proto.add.apply(target, arguments) 71 | if (!hadKey) { 72 | runReactionsFromTargetKey({ target, key, value: key, type: 'add' }) 73 | } 74 | return result 75 | }, 76 | set(key: PropertyKey, value: any) { 77 | const target = ProxyRaw.get(this) 78 | const proto = Reflect.getPrototypeOf(this) as any 79 | const hadKey = proto.has.call(target, key) 80 | const oldValue = proto.get.call(target, key) 81 | // forward the operation before queueing reactions 82 | const result = proto.set.apply(target, arguments) 83 | if (!hadKey) { 84 | runReactionsFromTargetKey({ target, key, value, type: 'add' }) 85 | } else if (value !== oldValue) { 86 | runReactionsFromTargetKey({ target, key, value, oldValue, type: 'set' }) 87 | } 88 | return result 89 | }, 90 | delete(key: PropertyKey) { 91 | const target = ProxyRaw.get(this) 92 | const proto = Reflect.getPrototypeOf(this) as any 93 | const hadKey = proto.has.call(target, key) 94 | const oldValue = proto.get ? proto.get.call(target, key) : undefined 95 | // forward the operation before queueing reactions 96 | const result = proto.delete.apply(target, arguments) 97 | if (hadKey) { 98 | runReactionsFromTargetKey({ target, key, oldValue, type: 'delete' }) 99 | } 100 | return result 101 | }, 102 | clear() { 103 | const target = ProxyRaw.get(this) 104 | const proto = Reflect.getPrototypeOf(this) as any 105 | const hadItems = target.size !== 0 106 | const oldTarget = target instanceof Map ? new Map(target) : new Set(target) 107 | // forward the operation before queueing reactions 108 | const result = proto.clear.apply(target, arguments) 109 | if (hadItems) { 110 | runReactionsFromTargetKey({ target, oldTarget, type: 'clear' }) 111 | } 112 | return result 113 | }, 114 | forEach(cb: any, ...args: any[]) { 115 | const target = ProxyRaw.get(this) 116 | const proto = Reflect.getPrototypeOf(this) as any 117 | bindTargetKeyWithCurrentReaction({ target, type: 'iterate' }) 118 | // swap out the raw values with their observable pairs 119 | // before passing them to the callback 120 | const wrappedCb = (value: any, key: PropertyKey, ...args: any) => 121 | cb(findObservable(target, key, value), key, ...args) 122 | return proto.forEach.call(target, wrappedCb, ...args) 123 | }, 124 | keys() { 125 | const target = ProxyRaw.get(this) 126 | const proto = Reflect.getPrototypeOf(this) as any 127 | bindTargetKeyWithCurrentReaction({ target, type: 'iterate' }) 128 | return proto.keys.apply(target, arguments) 129 | }, 130 | values() { 131 | const target = ProxyRaw.get(this) 132 | const proto = Reflect.getPrototypeOf(this) as any 133 | bindTargetKeyWithCurrentReaction({ target, type: 'iterate' }) 134 | const iterator = proto.values.apply(target, arguments) 135 | return patchIterator(target, '', iterator, false) 136 | }, 137 | entries() { 138 | const target = ProxyRaw.get(this) 139 | const proto = Reflect.getPrototypeOf(this) as any 140 | bindTargetKeyWithCurrentReaction({ target, type: 'iterate' }) 141 | const iterator = proto.entries.apply(target, arguments) 142 | return patchIterator(target, '', iterator, true) 143 | }, 144 | [Symbol.iterator]() { 145 | const target = ProxyRaw.get(this) 146 | const proto = Reflect.getPrototypeOf(this) 147 | bindTargetKeyWithCurrentReaction({ target, type: 'iterate' }) 148 | const iterator = proto[Symbol.iterator].apply(target, arguments) 149 | return patchIterator(target, '', iterator, target instanceof Map) 150 | }, 151 | get size() { 152 | const target = ProxyRaw.get(this) 153 | const proto = Reflect.getPrototypeOf(this) 154 | bindTargetKeyWithCurrentReaction({ target, type: 'iterate' }) 155 | return Reflect.get(proto, 'size', target) 156 | }, 157 | } 158 | 159 | export const collectionHandlers = { 160 | get(target: any, key: PropertyKey, receiver: any) { 161 | // instrument methods and property accessors to be reactive 162 | target = hasOwnProperty.call(instrumentations, key) 163 | ? instrumentations 164 | : target 165 | return Reflect.get(target, key, receiver) 166 | }, 167 | } 168 | 169 | export const baseHandlers: ProxyHandler<any> = { 170 | get(target, key, receiver) { 171 | if (!key) return 172 | const result = target[key] // use Reflect.get is too slow 173 | if (typeof key === 'symbol' && wellKnownSymbols.has(key)) { 174 | return result 175 | } 176 | bindTargetKeyWithCurrentReaction({ target, key, receiver, type: 'get' }) 177 | const observableResult = RawProxy.get(result) 178 | if (observableResult) { 179 | return observableResult 180 | } 181 | if (!isObservable(result) && isSupportObservable(result)) { 182 | const descriptor = Reflect.getOwnPropertyDescriptor(target, key) 183 | if ( 184 | !descriptor || 185 | !(descriptor.writable === false && descriptor.configurable === false) 186 | ) { 187 | return createObservable(target, key, result) 188 | } 189 | } 190 | return result 191 | }, 192 | has(target, key) { 193 | const result = Reflect.has(target, key) 194 | bindTargetKeyWithCurrentReaction({ target, key, type: 'has' }) 195 | return result 196 | }, 197 | ownKeys(target) { 198 | const keys = Reflect.ownKeys(target) 199 | bindTargetKeyWithCurrentReaction({ target, type: 'iterate' }) 200 | return keys 201 | }, 202 | set(target, key, value, receiver) { 203 | // vue2中有对数组原型重写,因此需去除此处proxy 204 | if (key === '__proto__') { 205 | target[key] = value 206 | return true 207 | } 208 | const hadKey = hasOwnProperty.call(target, key) 209 | const newValue = createObservable(target, key, value) 210 | const oldValue = target[key] 211 | target[key] = newValue // use Reflect.set is too slow 212 | if (!hadKey) { 213 | runReactionsFromTargetKey({ 214 | target, 215 | key, 216 | value: newValue, 217 | oldValue, 218 | receiver, 219 | type: 'add', 220 | }) 221 | } else if (value !== oldValue) { 222 | runReactionsFromTargetKey({ 223 | target, 224 | key, 225 | value: newValue, 226 | oldValue, 227 | receiver, 228 | type: 'set', 229 | }) 230 | } 231 | return true 232 | }, 233 | deleteProperty(target, key) { 234 | const oldValue = target[key] 235 | delete target[key] 236 | runReactionsFromTargetKey({ 237 | target, 238 | key, 239 | oldValue, 240 | type: 'delete', 241 | }) 242 | return true 243 | }, 244 | } 245 | ``` -------------------------------------------------------------------------------- /packages/next/docs/components/Space.zh-CN.md: -------------------------------------------------------------------------------- ```markdown 1 | # Space 2 | 3 | > 超级便捷的 Flex 布局组件,可以帮助用户快速实现任何元素的并排紧挨布局 4 | 5 | ## Markup Schema 案例 6 | 7 | ```tsx 8 | import React from 'react' 9 | import { 10 | Input, 11 | FormItem, 12 | FormLayout, 13 | FormButtonGroup, 14 | Submit, 15 | Space, 16 | } from '@formily/next' 17 | import { createForm } from '@formily/core' 18 | import { FormProvider, createSchemaField } from '@formily/react' 19 | 20 | const SchemaField = createSchemaField({ 21 | components: { 22 | Input, 23 | FormItem, 24 | Space, 25 | }, 26 | }) 27 | 28 | const form = createForm() 29 | 30 | export default () => ( 31 | <FormProvider form={form}> 32 | <FormLayout labelCol={6} wrapperCol={16}> 33 | <SchemaField> 34 | <SchemaField.Void 35 | title="姓名" 36 | x-decorator="FormItem" 37 | x-decorator-props={{ 38 | asterisk: true, 39 | feedbackLayout: 'none', 40 | }} 41 | x-component="Space" 42 | > 43 | <SchemaField.String 44 | name="firstName" 45 | x-decorator="FormItem" 46 | x-component="Input" 47 | required 48 | /> 49 | <SchemaField.String 50 | name="lastName" 51 | x-decorator="FormItem" 52 | x-component="Input" 53 | required 54 | /> 55 | </SchemaField.Void> 56 | <SchemaField.Void 57 | title="文本串联" 58 | x-decorator="FormItem" 59 | x-decorator-props={{ 60 | asterisk: true, 61 | feedbackLayout: 'none', 62 | }} 63 | x-component="Space" 64 | > 65 | <SchemaField.String 66 | name="aa" 67 | x-decorator="FormItem" 68 | x-component="Input" 69 | x-decorator-props={{ 70 | addonAfter: '单位', 71 | }} 72 | required 73 | /> 74 | <SchemaField.String 75 | name="bb" 76 | x-decorator="FormItem" 77 | x-component="Input" 78 | x-decorator-props={{ 79 | addonAfter: '单位', 80 | }} 81 | required 82 | /> 83 | <SchemaField.String 84 | name="cc" 85 | x-decorator="FormItem" 86 | x-component="Input" 87 | x-decorator-props={{ 88 | addonAfter: '单位', 89 | }} 90 | required 91 | /> 92 | </SchemaField.Void> 93 | <SchemaField.String 94 | name="textarea" 95 | title="文本框" 96 | x-decorator="FormItem" 97 | required 98 | x-component="Input.TextArea" 99 | x-component-props={{ 100 | style: { 101 | width: 400, 102 | }, 103 | }} 104 | /> 105 | </SchemaField> 106 | <FormButtonGroup.FormItem> 107 | <Submit onSubmit={console.log}>提交</Submit> 108 | </FormButtonGroup.FormItem> 109 | </FormLayout> 110 | </FormProvider> 111 | ) 112 | ``` 113 | 114 | ## JSON Schema 案例 115 | 116 | ```tsx 117 | import React from 'react' 118 | import { 119 | Input, 120 | FormItem, 121 | FormLayout, 122 | FormButtonGroup, 123 | Submit, 124 | Space, 125 | } from '@formily/next' 126 | import { createForm } from '@formily/core' 127 | import { FormProvider, createSchemaField } from '@formily/react' 128 | 129 | const SchemaField = createSchemaField({ 130 | components: { 131 | Input, 132 | FormItem, 133 | Space, 134 | }, 135 | }) 136 | 137 | const form = createForm() 138 | 139 | const schema = { 140 | type: 'object', 141 | properties: { 142 | name: { 143 | type: 'void', 144 | title: '姓名', 145 | 'x-decorator': 'FormItem', 146 | 'x-decorator-props': { 147 | asterisk: true, 148 | feedbackLayout: 'none', 149 | }, 150 | 'x-component': 'Space', 151 | properties: { 152 | firstName: { 153 | type: 'string', 154 | 'x-decorator': 'FormItem', 155 | 'x-component': 'Input', 156 | required: true, 157 | }, 158 | lastName: { 159 | type: 'string', 160 | 'x-decorator': 'FormItem', 161 | 'x-component': 'Input', 162 | required: true, 163 | }, 164 | }, 165 | }, 166 | texts: { 167 | type: 'void', 168 | title: '文本串联', 169 | 'x-decorator': 'FormItem', 170 | 'x-decorator-props': { 171 | asterisk: true, 172 | feedbackLayout: 'none', 173 | }, 174 | 'x-component': 'Space', 175 | properties: { 176 | aa: { 177 | type: 'string', 178 | 'x-decorator': 'FormItem', 179 | 'x-decorator-props': { 180 | addonAfter: '单位', 181 | }, 182 | 'x-component': 'Input', 183 | required: true, 184 | }, 185 | bb: { 186 | type: 'string', 187 | 'x-decorator': 'FormItem', 188 | 'x-decorator-props': { 189 | addonAfter: '单位', 190 | }, 191 | 'x-component': 'Input', 192 | required: true, 193 | }, 194 | cc: { 195 | type: 'string', 196 | 'x-decorator': 'FormItem', 197 | 'x-decorator-props': { 198 | addonAfter: '单位', 199 | }, 200 | 'x-component': 'Input', 201 | required: true, 202 | }, 203 | }, 204 | }, 205 | 206 | textarea: { 207 | type: 'string', 208 | title: '文本框', 209 | 'x-decorator': 'FormItem', 210 | 'x-component': 'Input.TextArea', 211 | 'x-component-props': { 212 | style: { 213 | width: 400, 214 | }, 215 | }, 216 | required: true, 217 | }, 218 | }, 219 | } 220 | 221 | export default () => ( 222 | <FormProvider form={form}> 223 | <FormLayout labelCol={6} wrapperCol={16}> 224 | <SchemaField schema={schema} /> 225 | <FormButtonGroup.FormItem> 226 | <Submit onSubmit={console.log}>提交</Submit> 227 | </FormButtonGroup.FormItem> 228 | </FormLayout> 229 | </FormProvider> 230 | ) 231 | ``` 232 | 233 | ## 纯 JSX 案例 234 | 235 | ```tsx 236 | import React from 'react' 237 | import { 238 | Input, 239 | FormItem, 240 | FormLayout, 241 | FormButtonGroup, 242 | Submit, 243 | Space, 244 | } from '@formily/next' 245 | import { createForm } from '@formily/core' 246 | import { FormProvider, Field, VoidField } from '@formily/react' 247 | 248 | const form = createForm() 249 | 250 | export default () => ( 251 | <FormProvider form={form}> 252 | <FormLayout labelCol={6} wrapperCol={16}> 253 | <VoidField 254 | name="name" 255 | title="姓名" 256 | decorator={[ 257 | FormItem, 258 | { 259 | asterisk: true, 260 | feedbackLayout: 'none', 261 | }, 262 | ]} 263 | component={[Space]} 264 | > 265 | <Field 266 | name="firstName" 267 | decorator={[FormItem]} 268 | component={[Input]} 269 | required 270 | /> 271 | <Field 272 | name="lastName" 273 | decorator={[FormItem]} 274 | component={[Input]} 275 | required 276 | /> 277 | </VoidField> 278 | <VoidField 279 | name="texts" 280 | title="文本串联" 281 | decorator={[ 282 | FormItem, 283 | { 284 | asterisk: true, 285 | feedbackLayout: 'none', 286 | }, 287 | ]} 288 | component={[Space]} 289 | > 290 | <Field 291 | name="aa" 292 | decorator={[ 293 | FormItem, 294 | { 295 | addonAfter: '单位', 296 | }, 297 | ]} 298 | component={[Input]} 299 | required 300 | /> 301 | <Field 302 | name="bb" 303 | decorator={[ 304 | FormItem, 305 | { 306 | addonAfter: '单位', 307 | }, 308 | ]} 309 | component={[Input]} 310 | required 311 | /> 312 | <Field 313 | name="cc" 314 | decorator={[ 315 | FormItem, 316 | { 317 | addonAfter: '单位', 318 | }, 319 | ]} 320 | component={[Input]} 321 | required 322 | /> 323 | </VoidField> 324 | <Field 325 | name="textarea" 326 | title="文本框" 327 | decorator={[FormItem]} 328 | component={[ 329 | Input.TextArea, 330 | { 331 | style: { 332 | width: 400, 333 | }, 334 | }, 335 | ]} 336 | required 337 | /> 338 | <FormButtonGroup.FormItem> 339 | <Submit onSubmit={console.log}>提交</Submit> 340 | </FormButtonGroup.FormItem> 341 | </FormLayout> 342 | </FormProvider> 343 | ) 344 | ``` 345 | 346 | ## API 347 | 348 | | 属性名 | 类型 | 描述 | 默认值 | 349 | | --------- | -------------------------------------------- | -------- | --------- | 350 | | style | CSSProperties | 样式 | - | 351 | | className | string | 类名 | - | 352 | | prefix | string | 样式前缀 | true | 353 | | size | `number \| 'small' \| 'large' \| 'middle'` | 间隔尺寸 | 8px | 354 | | direction | `'horizontal' \| 'vertical'` | 方向 | - | 355 | | align | `'start' \| 'end' \| 'center' \| 'baseline'` | 对齐 | `'start'` | 356 | | wrap | boolean | 是否换行 | false | 357 | ``` -------------------------------------------------------------------------------- /packages/antd/docs/components/FormDrawer.md: -------------------------------------------------------------------------------- ```markdown 1 | # FormDrawer 2 | 3 | > Drawer form, mainly used in simple event to open form scene 4 | 5 | ## Markup Schema example 6 | 7 | ```tsx 8 | import React from 'react' 9 | import { 10 | FormDrawer, 11 | FormItem, 12 | FormLayout, 13 | Input, 14 | Submit, 15 | Reset, 16 | FormButtonGroup, 17 | } from '@formily/antd' 18 | import { createSchemaField } from '@formily/react' 19 | import { Button } from 'antd' 20 | 21 | const SchemaField = createSchemaField({ 22 | components: { 23 | FormItem, 24 | Input, 25 | }, 26 | }) 27 | 28 | export default () => { 29 | return ( 30 | <Button 31 | onClick={() => { 32 | FormDrawer('Drawer Form', () => { 33 | return ( 34 | <FormLayout labelCol={6} wrapperCol={10}> 35 | <SchemaField> 36 | <SchemaField.String 37 | name="aaa" 38 | required 39 | title="input box 1" 40 | x-decorator="FormItem" 41 | x-component="Input" 42 | /> 43 | <SchemaField.String 44 | name="bbb" 45 | required 46 | title="input box 2" 47 | x-decorator="FormItem" 48 | x-component="Input" 49 | /> 50 | <SchemaField.String 51 | name="ccc" 52 | required 53 | title="input box 3" 54 | x-decorator="FormItem" 55 | x-component="Input" 56 | /> 57 | <SchemaField.String 58 | name="ddd" 59 | required 60 | title="input box 4" 61 | x-decorator="FormItem" 62 | x-component="Input" 63 | /> 64 | </SchemaField> 65 | <FormDrawer.Extra> 66 | <FormButtonGroup align="right"> 67 | <Submit 68 | onSubmit={() => { 69 | return new Promise((resolve) => { 70 | setTimeout(resolve, 1000) 71 | }) 72 | }} 73 | > 74 | Submit 75 | </Submit> 76 | <Reset>Reset</Reset> 77 | </FormButtonGroup> 78 | </FormDrawer.Extra> 79 | </FormLayout> 80 | ) 81 | }) 82 | .forOpen((props, next) => { 83 | setTimeout(() => { 84 | next({ 85 | initialValues: { 86 | aaa: '123', 87 | }, 88 | }) 89 | }, 1000) 90 | }) 91 | .open() 92 | .then(console.log) 93 | }} 94 | > 95 | Click me to open the form 96 | </Button> 97 | ) 98 | } 99 | ``` 100 | 101 | ## JSON Schema case 102 | 103 | ```tsx 104 | import React from 'react' 105 | import { 106 | FormDrawer, 107 | FormItem, 108 | FormLayout, 109 | Input, 110 | Submit, 111 | Reset, 112 | FormButtonGroup, 113 | } from '@formily/antd' 114 | import { createSchemaField } from '@formily/react' 115 | import { Button } from 'antd' 116 | 117 | const SchemaField = createSchemaField({ 118 | components: { 119 | FormItem, 120 | Input, 121 | }, 122 | }) 123 | 124 | const schema = { 125 | type: 'object', 126 | properties: { 127 | aaa: { 128 | type: 'string', 129 | title: 'input box 1', 130 | required: true, 131 | 'x-decorator': 'FormItem', 132 | 'x-component': 'Input', 133 | }, 134 | bbb: { 135 | type: 'string', 136 | title: 'input box 2', 137 | required: true, 138 | 'x-decorator': 'FormItem', 139 | 'x-component': 'Input', 140 | }, 141 | ccc: { 142 | type: 'string', 143 | title: 'input box 3', 144 | required: true, 145 | 'x-decorator': 'FormItem', 146 | 'x-component': 'Input', 147 | }, 148 | ddd: { 149 | type: 'string', 150 | title: 'input box 4', 151 | required: true, 152 | 'x-decorator': 'FormItem', 153 | 'x-component': 'Input', 154 | }, 155 | }, 156 | } 157 | 158 | export default () => { 159 | return ( 160 | <Button 161 | onClick={() => { 162 | FormDrawer('Pop-up form', () => { 163 | return ( 164 | <FormLayout labelCol={6} wrapperCol={10}> 165 | <SchemaField schema={schema} /> 166 | <FormDrawer.Extra> 167 | <FormButtonGroup align="right"> 168 | <Submit 169 | onSubmit={() => { 170 | return new Promise((resolve) => { 171 | setTimeout(resolve, 1000) 172 | }) 173 | }} 174 | > 175 | Submit 176 | </Submit> 177 | <Reset>Reset</Reset> 178 | </FormButtonGroup> 179 | </FormDrawer.Extra> 180 | </FormLayout> 181 | ) 182 | }) 183 | .open({ 184 | initialValues: { 185 | aaa: '123', 186 | }, 187 | }) 188 | .then(console.log) 189 | }} 190 | > 191 | Click me to open the form 192 | </Button> 193 | ) 194 | } 195 | ``` 196 | 197 | ## Pure JSX case 198 | 199 | ```tsx 200 | import React from 'react' 201 | import { 202 | FormDrawer, 203 | FormItem, 204 | FormLayout, 205 | Input, 206 | Submit, 207 | Reset, 208 | FormButtonGroup, 209 | } from '@formily/antd' 210 | import { Field } from '@formily/react' 211 | import { Button } from 'antd' 212 | 213 | export default () => { 214 | return ( 215 | <Button 216 | onClick={() => { 217 | FormDrawer('Pop-up form', () => { 218 | return ( 219 | <FormLayout labelCol={6} wrapperCol={10}> 220 | <Field 221 | name="aaa" 222 | required 223 | title="input box 1" 224 | decorator={[FormItem]} 225 | component={[Input]} 226 | /> 227 | <Field 228 | name="bbb" 229 | required 230 | title="input box 2" 231 | decorator={[FormItem]} 232 | component={[Input]} 233 | /> 234 | <Field 235 | name="ccc" 236 | required 237 | title="input box 3" 238 | decorator={[FormItem]} 239 | component={[Input]} 240 | /> 241 | <Field 242 | name="ddd" 243 | required 244 | title="input box 4" 245 | decorator={[FormItem]} 246 | component={[Input]} 247 | /> 248 | <FormDrawer.Extra> 249 | <FormButtonGroup align="right"> 250 | <Submit 251 | onSubmit={() => { 252 | return new Promise((resolve) => { 253 | setTimeout(resolve, 1000) 254 | }) 255 | }} 256 | > 257 | Submit 258 | </Submit> 259 | <Reset>Reset</Reset> 260 | </FormButtonGroup> 261 | </FormDrawer.Extra> 262 | </FormLayout> 263 | ) 264 | }) 265 | .open({ 266 | initialValues: { 267 | aaa: '123', 268 | }, 269 | }) 270 | .then(console.log) 271 | }} 272 | > 273 | Click me to open the form 274 | </Button> 275 | ) 276 | } 277 | ``` 278 | 279 | ## API 280 | 281 | ### FormDrawer 282 | 283 | ```ts pure 284 | import { IFormProps, Form } from '@formily/core' 285 | 286 | type FormDrawerRenderer = 287 | | React.ReactElement 288 | | ((form: Form) => React.ReactElement) 289 | 290 | interface IFormDrawer { 291 | forOpen( 292 | middleware: ( 293 | props: IFormProps, 294 | next: (props?: IFormProps) => Promise<any> 295 | ) => any 296 | ): any //Middleware interceptor, can intercept Drawer to open 297 | //Open the pop-up window to receive form attributes, you can pass in initialValues/values/effects etc. 298 | open(props: IFormProps): Promise<any> //return form data 299 | //Close the pop-up window 300 | close(): void 301 | } 302 | 303 | export interface IDrawerProps extends DrawerProps { 304 | onClose?: (e: EventType) => void | boolean // return false can prevent onClose 305 | loadingText?: React.ReactNode 306 | } 307 | 308 | interface FormDrawer { 309 | (title: IDrawerProps, id: string, renderer: FormDrawerRenderer): IFormDrawer 310 | (title: IDrawerProps, renderer: FormDrawerRenderer): IFormDrawer 311 | (title: ModalTitle, id: string, renderer: FormDrawerRenderer): IFormDrawer 312 | (title: ModalTitle, renderer: FormDrawerRenderer): IFormDrawer 313 | } 314 | ``` 315 | 316 | `DrawerProps` type definition reference ant design [Drawer API](https://ant.design/components/drawer-cn/#API) 317 | 318 | ### FormDrawer.Extra 319 | 320 | No attributes, only child nodes are received 321 | 322 | ### FormDrawer.Footer 323 | 324 | No attributes, only child nodes are received 325 | 326 | ### FormDrawer.Portal 327 | 328 | Receive an optional id attribute, the default value is `form-drawer`, if there are multiple prefixCls in an application, and the prefixCls in the pop-up window of different regions are different, then it is recommended to specify the id as the region-level id 329 | ``` -------------------------------------------------------------------------------- /packages/next/src/preview-text/index.tsx: -------------------------------------------------------------------------------- ```typescript 1 | import React, { createContext, useContext } from 'react' 2 | import { isArr, isEmpty, isValid } from '@formily/shared' 3 | import { Field } from '@formily/core' 4 | import { useField, observer } from '@formily/react' 5 | import { InputProps } from '@alifd/next/lib/input' 6 | import { NumberPickerProps } from '@alifd/next/lib/number-picker' 7 | import { SelectProps } from '@alifd/next/lib/select' 8 | import { TreeSelectProps } from '@alifd/next/lib/tree-select' 9 | import { CascaderSelectProps } from '@alifd/next/lib/cascader-select' 10 | import { 11 | DatePickerProps, 12 | RangePickerProps as DateRangePickerProps, 13 | } from '@alifd/next/lib/date-picker' 14 | import { TimePickerProps } from '@alifd/next/lib/time-picker' 15 | import { 16 | TimePickerProps as TimePicker2Props, 17 | RangePickerProps as TimeRangePicker2Props, 18 | } from '@alifd/next/types/time-picker2' 19 | import { 20 | Tag, 21 | Input as NextInput, 22 | NumberPicker as NextNumberPicker, 23 | CascaderSelect as NextCascader, 24 | } from '@alifd/next' 25 | import cls from 'classnames' 26 | import { formatMomentValue, usePrefixCls } from '../__builtins__' 27 | 28 | const PlaceholderContext = createContext<React.ReactNode>('N/A') 29 | 30 | const Placeholder = PlaceholderContext.Provider 31 | 32 | const usePlaceholder = (value?: any) => { 33 | const placeholder = useContext(PlaceholderContext) || 'N/A' 34 | return !isEmpty(value) ? value : placeholder 35 | } 36 | 37 | const Input: React.FC<React.PropsWithChildren<InputProps>> = (props) => { 38 | return <NextInput {...props} isPreview /> 39 | } 40 | 41 | const NumberPicker: React.FC<React.PropsWithChildren<NumberPickerProps>> = ( 42 | props 43 | ) => { 44 | return <NextNumberPicker {...props} isPreview /> 45 | } 46 | 47 | const Select: React.FC<React.PropsWithChildren<SelectProps>> = observer( 48 | (props) => { 49 | const field = useField<Field>() 50 | const prefixCls = usePrefixCls('form-preview', props) 51 | const dataSource: any[] = field?.dataSource?.length 52 | ? field.dataSource 53 | : props?.dataSource?.length 54 | ? props.dataSource 55 | : [] 56 | const placeholder = usePlaceholder() 57 | const getSelected = () => { 58 | const value = props.value 59 | if (props.mode === 'multiple' || props.mode === 'tag') { 60 | if (props.useDetailValue) { 61 | return isArr(value) ? value : [] 62 | } else { 63 | return isArr(value) 64 | ? value.map((val) => ({ label: val, value: val })) 65 | : [] 66 | } 67 | } else { 68 | if (props.useDetailValue) { 69 | return isValid(value) ? [value] : [] 70 | } else { 71 | return isValid(value) ? [{ label: value, value }] : [] 72 | } 73 | } 74 | } 75 | 76 | const getLabel = (target: any) => { 77 | return ( 78 | dataSource?.find((item) => item.value == target?.value)?.label || 79 | target.label || 80 | placeholder 81 | ) 82 | } 83 | 84 | const getLabels = () => { 85 | const selected = getSelected() 86 | if (!selected.length) return placeholder 87 | if (selected.length === 1) return getLabel(selected[0]) 88 | return selected.map((item, key) => { 89 | return ( 90 | <Tag type="primary" size="small" key={key}> 91 | {getLabel(item)} 92 | </Tag> 93 | ) 94 | }) 95 | } 96 | 97 | return <div className={cls(prefixCls, props.className)}>{getLabels()}</div> 98 | } 99 | ) 100 | 101 | const TreeSelect: React.FC<React.PropsWithChildren<TreeSelectProps>> = observer( 102 | (props) => { 103 | const field = useField<Field>() 104 | const placeholder = usePlaceholder() 105 | const prefixCls = usePrefixCls('form-preview', props) 106 | const dataSource = field?.dataSource?.length 107 | ? field.dataSource 108 | : props?.dataSource?.length 109 | ? props.dataSource 110 | : [] 111 | const getSelected = () => { 112 | const value = props.value 113 | if (props.multiple) { 114 | if (props['useDetailValue']) { 115 | return isArr(value) ? value : [] 116 | } else { 117 | return isArr(value) 118 | ? value.map((val) => ({ label: val, value: val })) 119 | : [] 120 | } 121 | } else { 122 | if (props['useDetailValue']) { 123 | return value ? [value] : [] 124 | } else { 125 | return value ? [{ label: value, value }] : [] 126 | } 127 | } 128 | } 129 | 130 | const findLabel = (value: any, dataSource: any[]) => { 131 | for (let i = 0; i < dataSource?.length; i++) { 132 | const item = dataSource[i] 133 | if (item?.value === value) { 134 | return item?.label 135 | } else { 136 | const childLabel = findLabel(value, item?.children) 137 | if (childLabel) return childLabel 138 | } 139 | } 140 | } 141 | 142 | const getLabels = () => { 143 | const selected = getSelected() 144 | if (!selected?.length) 145 | return ( 146 | <Tag type="primary" size="small"> 147 | {placeholder} 148 | </Tag> 149 | ) 150 | return selected.map(({ value, label }, key) => { 151 | return ( 152 | <Tag type="primary" size="small" key={key}> 153 | {findLabel(value, dataSource) || label || placeholder} 154 | </Tag> 155 | ) 156 | }) 157 | } 158 | return <div className={cls(prefixCls, props.className)}>{getLabels()}</div> 159 | } 160 | ) 161 | 162 | const Cascader: React.FC<React.PropsWithChildren<CascaderSelectProps>> = 163 | observer((props) => { 164 | const field = useField<Field>() 165 | const prefixCls = usePrefixCls('form-preview', props) 166 | return ( 167 | <NextCascader 168 | {...props} 169 | className={prefixCls} 170 | dataSource={field.dataSource} 171 | isPreview 172 | /> 173 | ) 174 | }) 175 | 176 | const DatePicker: React.FC<React.PropsWithChildren<DatePickerProps>> = ( 177 | props 178 | ) => { 179 | const placeholder = usePlaceholder() 180 | const prefixCls = usePrefixCls('form-preview', props) 181 | const getLabels = () => { 182 | const labels = formatMomentValue(props.value, props.format, placeholder) 183 | return isArr(labels) ? labels.join('~') : labels 184 | } 185 | return <div className={cls(prefixCls, props.className)}>{getLabels()}</div> 186 | } 187 | 188 | const DateRangePicker: React.FC< 189 | React.PropsWithChildren<DateRangePickerProps> 190 | > = (props) => { 191 | const placeholder = usePlaceholder() 192 | const prefixCls = usePrefixCls('form-preview', props) 193 | const getLabels = () => { 194 | const labels = formatMomentValue(props.value, props.format, placeholder) 195 | return isArr(labels) ? labels.join('~') : labels 196 | } 197 | return <div className={cls(prefixCls, props.className)}>{getLabels()}</div> 198 | } 199 | 200 | const TimePicker: React.FC<React.PropsWithChildren<TimePickerProps>> = ( 201 | props 202 | ) => { 203 | const placeholder = usePlaceholder() 204 | const prefixCls = usePrefixCls('form-preview', props) 205 | const getLabels = () => { 206 | const labels = formatMomentValue(props.value, props.format, placeholder) 207 | return isArr(labels) ? labels.join('~') : labels 208 | } 209 | return <div className={cls(prefixCls, props.className)}>{getLabels()}</div> 210 | } 211 | 212 | const TimePicker2: React.FC<React.PropsWithChildren<TimePicker2Props>> = ( 213 | props 214 | ) => { 215 | const placeholder = usePlaceholder() 216 | const prefixCls = usePrefixCls('form-preview', props) 217 | const getLabels = () => { 218 | const labels = formatMomentValue(props.value, props.format, placeholder) 219 | return isArr(labels) ? labels.join('~') : labels 220 | } 221 | return <div className={cls(prefixCls, props.className)}>{getLabels()}</div> 222 | } 223 | 224 | const TimeRangePicker2: React.FC< 225 | React.PropsWithChildren<TimeRangePicker2Props> 226 | > = (props) => { 227 | const placeholder = usePlaceholder() 228 | const prefixCls = usePrefixCls('form-preview', props) 229 | const getLabels = () => { 230 | const labels = formatMomentValue(props.value, props.format, placeholder) 231 | return isArr(labels) ? labels.join('~') : labels 232 | } 233 | return <div className={cls(prefixCls, props.className)}>{getLabels()}</div> 234 | } 235 | 236 | const Text = (props: React.PropsWithChildren<any>) => { 237 | const prefixCls = usePrefixCls('form-preview', props) 238 | 239 | return ( 240 | <div className={cls(prefixCls, props.className)} style={props.style}> 241 | {usePlaceholder(props.value)} 242 | </div> 243 | ) 244 | } 245 | 246 | Text.Input = Input 247 | Text.NumberPicker = NumberPicker 248 | Text.Select = Select 249 | Text.TreeSelect = TreeSelect 250 | Text.Cascader = Cascader 251 | Text.DatePicker = DatePicker 252 | Text.DateRangePicker = DateRangePicker 253 | Text.TimePicker = TimePicker 254 | Text.TimePicker2 = TimePicker2 255 | Text.TimeRangePicker2 = TimeRangePicker2 256 | Text.Placeholder = Placeholder 257 | Text.usePlaceholder = usePlaceholder 258 | 259 | export const PreviewText = Text 260 | 261 | export default PreviewText 262 | ``` -------------------------------------------------------------------------------- /packages/reactive/src/__tests__/collections-set.spec.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { observable, autorun, raw } from '..' 2 | 3 | describe('Set', () => { 4 | test('should be a proper JS Set', () => { 5 | const set = observable(new Set()) 6 | expect(set).toBeInstanceOf(Set) 7 | expect(raw(set)).toBeInstanceOf(Set) 8 | }) 9 | 10 | test('should autorun mutations', () => { 11 | const handler = jest.fn() 12 | const set = observable(new Set()) 13 | autorun(() => handler(set.has('value'))) 14 | 15 | expect(handler).toBeCalledTimes(1) 16 | expect(handler).lastCalledWith(false) 17 | set.add('value') 18 | expect(handler).toBeCalledTimes(2) 19 | expect(handler).lastCalledWith(true) 20 | set.delete('value') 21 | expect(handler).toBeCalledTimes(3) 22 | expect(handler).lastCalledWith(false) 23 | }) 24 | 25 | test('should autorun size mutations', () => { 26 | const handler = jest.fn() 27 | const set = observable(new Set()) 28 | autorun(() => handler(set.size)) 29 | 30 | expect(handler).toBeCalledTimes(1) 31 | expect(handler).lastCalledWith(0) 32 | set.add('value') 33 | set.add('value2') 34 | expect(handler).toBeCalledTimes(3) 35 | expect(handler).lastCalledWith(2) 36 | set.delete('value') 37 | expect(handler).toBeCalledTimes(4) 38 | expect(handler).lastCalledWith(1) 39 | set.clear() 40 | expect(handler).toBeCalledTimes(5) 41 | expect(handler).lastCalledWith(0) 42 | }) 43 | 44 | test('should autorun for of iteration', () => { 45 | const handler = jest.fn() 46 | const set = observable(new Set<number>()) 47 | autorun(() => { 48 | let sum = 0 49 | // eslint-disable-next-line no-unused-vars 50 | for (let num of set) { 51 | sum += num 52 | } 53 | handler(sum) 54 | }) 55 | 56 | expect(handler).toBeCalledTimes(1) 57 | expect(handler).lastCalledWith(0) 58 | set.add(3) 59 | expect(handler).toBeCalledTimes(2) 60 | expect(handler).lastCalledWith(3) 61 | set.add(2) 62 | expect(handler).toBeCalledTimes(3) 63 | expect(handler).lastCalledWith(5) 64 | set.delete(3) 65 | expect(handler).toBeCalledTimes(4) 66 | expect(handler).lastCalledWith(2) 67 | set.clear() 68 | expect(handler).toBeCalledTimes(5) 69 | expect(handler).lastCalledWith(0) 70 | }) 71 | 72 | test('should autorun forEach iteration', () => { 73 | const handler = jest.fn() 74 | const set = observable(new Set<number>()) 75 | autorun(() => { 76 | let sum = 0 77 | set.forEach((num) => (sum += num)) 78 | handler(sum) 79 | }) 80 | 81 | expect(handler).toBeCalledTimes(1) 82 | expect(handler).lastCalledWith(0) 83 | set.add(3) 84 | expect(handler).toBeCalledTimes(2) 85 | expect(handler).lastCalledWith(3) 86 | set.add(2) 87 | expect(handler).toBeCalledTimes(3) 88 | expect(handler).lastCalledWith(5) 89 | set.delete(3) 90 | expect(handler).toBeCalledTimes(4) 91 | expect(handler).lastCalledWith(2) 92 | set.clear() 93 | expect(handler).toBeCalledTimes(5) 94 | expect(handler).lastCalledWith(0) 95 | }) 96 | 97 | test('should autorun keys iteration', () => { 98 | const handler = jest.fn() 99 | const set = observable(new Set<number>()) 100 | autorun(() => { 101 | let sum = 0 102 | for (let key of set.keys()) { 103 | sum += key 104 | } 105 | handler(sum) 106 | }) 107 | 108 | expect(handler).toBeCalledTimes(1) 109 | expect(handler).lastCalledWith(0) 110 | set.add(3) 111 | expect(handler).toBeCalledTimes(2) 112 | expect(handler).lastCalledWith(3) 113 | set.add(2) 114 | expect(handler).toBeCalledTimes(3) 115 | expect(handler).lastCalledWith(5) 116 | set.delete(3) 117 | expect(handler).toBeCalledTimes(4) 118 | expect(handler).lastCalledWith(2) 119 | set.clear() 120 | expect(handler).toBeCalledTimes(5) 121 | expect(handler).lastCalledWith(0) 122 | }) 123 | 124 | test('should autorun values iteration', () => { 125 | const handler = jest.fn() 126 | const set = observable(new Set<number>()) 127 | autorun(() => { 128 | let sum = 0 129 | for (let num of set.values()) { 130 | sum += num 131 | } 132 | handler(sum) 133 | }) 134 | 135 | expect(handler).toBeCalledTimes(1) 136 | expect(handler).lastCalledWith(0) 137 | set.add(3) 138 | expect(handler).toBeCalledTimes(2) 139 | expect(handler).lastCalledWith(3) 140 | set.add(2) 141 | expect(handler).toBeCalledTimes(3) 142 | expect(handler).lastCalledWith(5) 143 | set.delete(3) 144 | expect(handler).toBeCalledTimes(4) 145 | expect(handler).lastCalledWith(2) 146 | set.clear() 147 | expect(handler).toBeCalledTimes(5) 148 | expect(handler).lastCalledWith(0) 149 | }) 150 | 151 | test('should autorun entries iteration', () => { 152 | const handler = jest.fn() 153 | const set = observable(new Set<number>()) 154 | autorun(() => { 155 | let sum = 0 156 | // eslint-disable-next-line no-unused-vars 157 | for (let [, num] of set.entries()) { 158 | sum += num 159 | } 160 | handler(sum) 161 | }) 162 | 163 | expect(handler).toBeCalledTimes(1) 164 | expect(handler).lastCalledWith(0) 165 | set.add(3) 166 | expect(handler).toBeCalledTimes(2) 167 | expect(handler).lastCalledWith(3) 168 | set.add(2) 169 | expect(handler).toBeCalledTimes(3) 170 | expect(handler).lastCalledWith(5) 171 | set.delete(3) 172 | expect(handler).toBeCalledTimes(4) 173 | expect(handler).lastCalledWith(2) 174 | set.clear() 175 | expect(handler).toBeCalledTimes(5) 176 | expect(handler).lastCalledWith(0) 177 | }) 178 | 179 | test('should not autorun custom property mutations', () => { 180 | const handler = jest.fn() 181 | const set = observable(new Set()) 182 | autorun(() => handler(set['customProp'])) 183 | 184 | expect(handler).toBeCalledTimes(1) 185 | expect(handler).lastCalledWith(undefined) 186 | set['customProp'] = 'Hello World' 187 | expect(handler).toBeCalledTimes(1) 188 | }) 189 | 190 | test('should not autorun non value changing mutations', () => { 191 | const handler = jest.fn() 192 | const set = observable(new Set()) 193 | autorun(() => handler(set.has('value'))) 194 | 195 | expect(handler).toBeCalledTimes(1) 196 | expect(handler).lastCalledWith(false) 197 | set.add('value') 198 | expect(handler).toBeCalledTimes(2) 199 | expect(handler).lastCalledWith(true) 200 | set.add('value') 201 | expect(handler).toBeCalledTimes(2) 202 | set.delete('value') 203 | expect(handler).toBeCalledTimes(3) 204 | expect(handler).lastCalledWith(false) 205 | set.delete('value') 206 | expect(handler).toBeCalledTimes(3) 207 | set.clear() 208 | expect(handler).toBeCalledTimes(3) 209 | }) 210 | 211 | test('should not autorun raw data', () => { 212 | const handler = jest.fn() 213 | const set = observable(new Set()) 214 | autorun(() => handler(raw(set).has('value'))) 215 | 216 | expect(handler).toBeCalledTimes(1) 217 | expect(handler).lastCalledWith(false) 218 | set.add('value') 219 | expect(handler).toBeCalledTimes(1) 220 | set.delete('value') 221 | expect(handler).toBeCalledTimes(1) 222 | }) 223 | 224 | test('should not autorun raw iterations', () => { 225 | const handler = jest.fn() 226 | const set = observable(new Set<number>()) 227 | autorun(() => { 228 | let sum = 0 229 | // eslint-disable-next-line no-unused-vars 230 | for (let [, num] of raw(set).entries()) { 231 | sum += num 232 | } 233 | for (let key of raw(set).keys()) { 234 | sum += key 235 | } 236 | for (let num of raw(set).values()) { 237 | sum += num 238 | } 239 | raw(set).forEach((num) => { 240 | sum += num 241 | }) 242 | // eslint-disable-next-line no-unused-vars 243 | for (let num of raw(set)) { 244 | sum += num 245 | } 246 | handler(sum) 247 | }) 248 | 249 | expect(handler).toBeCalledTimes(1) 250 | expect(handler).lastCalledWith(0) 251 | set.add(2) 252 | set.add(3) 253 | expect(handler).toBeCalledTimes(1) 254 | set.delete(2) 255 | expect(handler).toBeCalledTimes(1) 256 | }) 257 | 258 | test('should not be triggered by raw mutations', () => { 259 | const handler = jest.fn() 260 | const set = observable(new Set()) 261 | autorun(() => handler(set.has('value'))) 262 | 263 | expect(handler).toBeCalledTimes(1) 264 | expect(handler).lastCalledWith(false) 265 | raw(set).add('value') 266 | expect(handler).toBeCalledTimes(1) 267 | raw(set).delete('value') 268 | expect(handler).toBeCalledTimes(1) 269 | raw(set).clear() 270 | expect(handler).toBeCalledTimes(1) 271 | }) 272 | 273 | test('should not autorun raw size mutations', () => { 274 | const handler = jest.fn() 275 | const set = observable(new Set()) 276 | autorun(() => handler(raw(set).size)) 277 | 278 | expect(handler).toBeCalledTimes(1) 279 | expect(handler).lastCalledWith(0) 280 | set.add('value') 281 | expect(handler).toBeCalledTimes(1) 282 | }) 283 | 284 | test('should not be triggered by raw size mutations', () => { 285 | const handler = jest.fn() 286 | const set = observable(new Set()) 287 | autorun(() => handler(set.size)) 288 | 289 | expect(handler).toBeCalledTimes(1) 290 | expect(handler).lastCalledWith(0) 291 | raw(set).add('value') 292 | expect(handler).toBeCalledTimes(1) 293 | }) 294 | }) 295 | ``` -------------------------------------------------------------------------------- /packages/core/src/__tests__/void.spec.ts: -------------------------------------------------------------------------------- ```typescript 1 | import { createForm } from '../' 2 | import { attach } from './shared' 3 | 4 | test('create void field', () => { 5 | const form = attach(createForm()) 6 | const field = attach( 7 | form.createVoidField({ 8 | name: 'void', 9 | }) 10 | ) 11 | field.destroy() 12 | }) 13 | 14 | test('create void field props', () => { 15 | const form = attach(createForm()) 16 | const field1 = attach( 17 | form.createVoidField({ 18 | name: 'field1', 19 | title: 'Field 1', 20 | description: 'This is Field 1', 21 | }) 22 | ) 23 | expect(field1.title).toEqual('Field 1') 24 | expect(field1.description).toEqual('This is Field 1') 25 | const field2 = attach( 26 | form.createVoidField({ 27 | name: 'field2', 28 | disabled: true, 29 | hidden: true, 30 | }) 31 | ) 32 | expect(field2.pattern).toEqual('disabled') 33 | expect(field2.disabled).toBeTruthy() 34 | expect(field2.display).toEqual('hidden') 35 | expect(field2.hidden).toBeTruthy() 36 | const field3 = attach( 37 | form.createVoidField({ 38 | name: 'field3', 39 | readOnly: true, 40 | visible: false, 41 | }) 42 | ) 43 | expect(field3.pattern).toEqual('readOnly') 44 | expect(field3.readOnly).toBeTruthy() 45 | expect(field3.display).toEqual('none') 46 | expect(field3.visible).toBeFalsy() 47 | }) 48 | 49 | test('setComponent/setComponentProps', () => { 50 | const form = attach(createForm()) 51 | const field = attach( 52 | form.createVoidField({ 53 | name: 'aa', 54 | }) 55 | ) 56 | const component = () => null 57 | field.setComponent(component, { props: 123 }) 58 | expect(field.component[0]).toEqual(component) 59 | expect(field.component[1]).toEqual({ props: 123 }) 60 | field.setComponentProps({ 61 | hello: 'world', 62 | }) 63 | expect(field.component[1]).toEqual({ props: 123, hello: 'world' }) 64 | }) 65 | 66 | test('setTitle/setDescription', () => { 67 | const form = attach(createForm()) 68 | const aa = attach( 69 | form.createVoidField({ 70 | name: 'aa', 71 | }) 72 | ) 73 | aa.setTitle('AAA') 74 | aa.setDescription('This is AAA') 75 | expect(aa.title).toEqual('AAA') 76 | expect(aa.description).toEqual('This is AAA') 77 | }) 78 | 79 | test('setComponent/setComponentProps', () => { 80 | const component = () => null 81 | const form = attach(createForm()) 82 | const field = attach( 83 | form.createVoidField({ 84 | name: 'aa', 85 | }) 86 | ) 87 | 88 | field.setComponent(undefined, { props: 123 }) 89 | field.setComponent(component) 90 | expect(field.component[0]).toEqual(component) 91 | expect(field.component[1]).toEqual({ props: 123 }) 92 | field.setComponentProps({ 93 | hello: 'world', 94 | }) 95 | expect(field.component[1]).toEqual({ props: 123, hello: 'world' }) 96 | }) 97 | 98 | test('setDecorator/setDecoratorProps', () => { 99 | const component = () => null 100 | const form = attach(createForm()) 101 | const field = attach( 102 | form.createVoidField({ 103 | name: 'aa', 104 | }) 105 | ) 106 | field.setDecorator(undefined, { props: 123 }) 107 | field.setDecorator(component) 108 | expect(field.decorator[0]).toEqual(component) 109 | expect(field.decorator[1]).toEqual({ props: 123 }) 110 | field.setDecoratorProps({ 111 | hello: 'world', 112 | }) 113 | expect(field.decorator[1]).toEqual({ props: 123, hello: 'world' }) 114 | }) 115 | 116 | test('setState/getState', () => { 117 | const form = attach(createForm()) 118 | const aa = attach( 119 | form.createVoidField({ 120 | name: 'aa', 121 | }) 122 | ) 123 | const state = aa.getState() 124 | aa.setState((state) => { 125 | state.title = 'AAA' 126 | }) 127 | expect(aa.title).toEqual('AAA') 128 | aa.setState(state) 129 | expect(aa.title).toBeUndefined() 130 | aa.setState((state) => { 131 | state.hidden = false 132 | }) 133 | expect(aa.display).toEqual('visible') 134 | aa.setState((state) => { 135 | state.visible = true 136 | }) 137 | expect(aa.display).toEqual('visible') 138 | aa.setState((state) => { 139 | state.readOnly = false 140 | }) 141 | expect(aa.pattern).toEqual('editable') 142 | aa.setState((state) => { 143 | state.disabled = false 144 | }) 145 | expect(aa.pattern).toEqual('editable') 146 | aa.setState((state) => { 147 | state.editable = true 148 | }) 149 | expect(aa.pattern).toEqual('editable') 150 | aa.setState((state) => { 151 | state.editable = false 152 | }) 153 | expect(aa.pattern).toEqual('readPretty') 154 | aa.setState((state) => { 155 | state.readPretty = true 156 | }) 157 | expect(aa.pattern).toEqual('readPretty') 158 | aa.setState((state) => { 159 | state.readPretty = false 160 | }) 161 | expect(aa.pattern).toEqual('editable') 162 | expect(aa.parent).toBeUndefined() 163 | }) 164 | 165 | test('nested display/pattern', () => { 166 | const form = attach(createForm()) 167 | attach( 168 | form.createObjectField({ 169 | name: 'object', 170 | }) 171 | ) 172 | const void_ = attach( 173 | form.createVoidField({ 174 | name: 'void', 175 | basePath: 'object', 176 | }) 177 | ) 178 | const void2_ = attach( 179 | form.createVoidField({ 180 | name: 'void', 181 | basePath: 'object.void.0', 182 | }) 183 | ) 184 | const aaa = attach( 185 | form.createField({ 186 | name: 'aaa', 187 | basePath: 'object.void', 188 | }) 189 | ) 190 | const bbb = attach( 191 | form.createField({ 192 | name: 'bbb', 193 | basePath: 'object.void', 194 | }) 195 | ) 196 | void_.setPattern('readPretty') 197 | expect(void_.pattern).toEqual('readPretty') 198 | expect(aaa.pattern).toEqual('readPretty') 199 | expect(bbb.pattern).toEqual('readPretty') 200 | void_.setPattern('readOnly') 201 | expect(void_.pattern).toEqual('readOnly') 202 | expect(aaa.pattern).toEqual('readOnly') 203 | expect(bbb.pattern).toEqual('readOnly') 204 | void_.setPattern('disabled') 205 | expect(void_.pattern).toEqual('disabled') 206 | expect(aaa.pattern).toEqual('disabled') 207 | expect(bbb.pattern).toEqual('disabled') 208 | void_.setPattern() 209 | expect(void_.pattern).toEqual('editable') 210 | expect(aaa.pattern).toEqual('editable') 211 | expect(bbb.pattern).toEqual('editable') 212 | 213 | void_.setDisplay('hidden') 214 | expect(void_.display).toEqual('hidden') 215 | expect(aaa.display).toEqual('hidden') 216 | expect(bbb.display).toEqual('hidden') 217 | void_.setDisplay('none') 218 | expect(void_.display).toEqual('none') 219 | expect(aaa.display).toEqual('none') 220 | expect(bbb.display).toEqual('none') 221 | void_.setDisplay() 222 | expect(void_.display).toEqual('visible') 223 | expect(aaa.display).toEqual('visible') 224 | expect(bbb.display).toEqual('visible') 225 | void_.onUnmount() 226 | expect(void2_.parent === void_).toBeTruthy() 227 | }) 228 | 229 | test('reactions', async () => { 230 | const form = attach(createForm()) 231 | const aa = attach( 232 | form.createField({ 233 | name: 'aa', 234 | }) 235 | ) 236 | const bb = attach( 237 | form.createVoidField({ 238 | name: 'bb', 239 | reactions: [ 240 | (field) => { 241 | const aa = field.query('aa') 242 | if (aa.get('value') === '123') { 243 | field.visible = false 244 | } else { 245 | field.visible = true 246 | } 247 | if (aa.get('inputValue') === '333') { 248 | field.editable = false 249 | } else if (aa.get('inputValue') === '444') { 250 | field.editable = true 251 | } 252 | if (aa.get('initialValue') === '555') { 253 | field.readOnly = true 254 | } else if (aa.get('initialValue') === '666') { 255 | field.readOnly = false 256 | } 257 | }, 258 | null, 259 | ], 260 | }) 261 | ) 262 | expect(bb.visible).toBeTruthy() 263 | aa.setValue('123') 264 | expect(bb.visible).toBeFalsy() 265 | await aa.onInput('333') 266 | expect(bb.editable).toBeFalsy() 267 | await aa.onInput('444') 268 | expect(bb.editable).toBeTruthy() 269 | aa.setInitialValue('555') 270 | expect(bb.readOnly).toBeTruthy() 271 | aa.setInitialValue('666') 272 | expect(bb.readOnly).toBeFalsy() 273 | form.onUnmount() 274 | }) 275 | 276 | test('fault tolerance', () => { 277 | const form = attach(createForm()) 278 | form.setDisplay(null) 279 | form.setPattern(null) 280 | const field = attach( 281 | form.createVoidField({ 282 | name: 'xxx', 283 | }) 284 | ) 285 | expect(field.display).toEqual('visible') 286 | expect(field.pattern).toEqual('editable') 287 | }) 288 | 289 | test('child field reactions', () => { 290 | const form = attach(createForm()) 291 | const voidField = attach(form.createVoidField({ name: 'void' })) 292 | const field1 = attach( 293 | form.createField({ 294 | name: 'field1', 295 | basePath: voidField.address, 296 | reactions: [ 297 | (field) => { 298 | field.value = field.query('field3').getIn('value') 299 | }, 300 | ], 301 | }) 302 | ) 303 | const field2 = attach( 304 | form.createField({ 305 | name: 'field2', 306 | basePath: voidField.address, 307 | reactions: [ 308 | (field) => { 309 | field.value = field.query('.field3').getIn('value') 310 | }, 311 | ], 312 | }) 313 | ) 314 | expect(field1.value).toBeUndefined() 315 | expect(field2.value).toBeUndefined() 316 | const field3 = attach( 317 | form.createField({ 318 | name: 'field3', 319 | basePath: voidField.address, 320 | value: 1, 321 | }) 322 | ) 323 | expect(field1.value).toBe(1) 324 | expect(field2.value).toBe(1) 325 | field3.value = 2 326 | expect(field1.value).toBe(2) 327 | expect(field2.value).toBe(2) 328 | }) 329 | ``` -------------------------------------------------------------------------------- /packages/core/docs/api/entry/FormChecker.md: -------------------------------------------------------------------------------- ```markdown 1 | --- 2 | order: 4 3 | --- 4 | 5 | # Form Checkers 6 | 7 | > The type checker is mainly used to determine the specific type of an object 8 | 9 | ## isForm 10 | 11 | #### Description 12 | 13 | Determine whether an object is a [Form](/api/models/form) object 14 | 15 | #### Signature 16 | 17 | ```ts 18 | interface isForm { 19 | (target: any): target is Form 20 | } 21 | ``` 22 | 23 | #### Example 24 | 25 | ```ts 26 | import { createForm, isForm } from '@formily/core' 27 | 28 | const form = createForm() 29 | 30 | console.log(isForm(form)) //true 31 | ``` 32 | 33 | ## isField 34 | 35 | #### Description 36 | 37 | Determine whether an object is a [Field](/api/models/field) object 38 | 39 | #### Signature 40 | 41 | ```ts 42 | interface isField { 43 | (target: any): target is Field 44 | } 45 | ``` 46 | 47 | #### Example 48 | 49 | ```ts 50 | import { createForm, isField } from '@formily/core' 51 | 52 | const form = createForm() 53 | 54 | const field = form.createField({ name: 'target' }) 55 | 56 | console.log(isField(field)) //true 57 | ``` 58 | 59 | ## isArrayField 60 | 61 | #### Description 62 | 63 | Determine whether an object is [ArrayField](/api/models/array-field) object 64 | 65 | #### Signature 66 | 67 | ```ts 68 | interface isArrayField { 69 | (target: any): target is ArrayField 70 | } 71 | ``` 72 | 73 | #### Example 74 | 75 | ```ts 76 | import { createForm, isArrayField } from '@formily/core' 77 | 78 | const form = createForm() 79 | 80 | const field = form.createArrayField({ name: 'target' }) 81 | 82 | console.log(isArrayField(field)) //true 83 | ``` 84 | 85 | ## isObjectField 86 | 87 | #### Description 88 | 89 | Determine whether an object is a [ObjectField](/api/models/object-field) object 90 | 91 | #### Signature 92 | 93 | ```ts 94 | interface isObjectField { 95 | (target: any): target is ObjectField 96 | } 97 | ``` 98 | 99 | #### Example 100 | 101 | ```ts 102 | import { createForm, isObjectField } from '@formily/core' 103 | 104 | const form = createForm() 105 | 106 | const field = form.createObjectField({ name: 'target' }) 107 | 108 | console.log(isObjectField(field)) //true 109 | ``` 110 | 111 | ## isVoidField 112 | 113 | #### Description 114 | 115 | Determine whether an object is a [VoidField](/api/models/void-field) object 116 | 117 | #### Signature 118 | 119 | ```ts 120 | interface isVoidField { 121 | (target: any): target is VoidField 122 | } 123 | ``` 124 | 125 | #### Example 126 | 127 | ```ts 128 | import { createForm, isVoidField } from '@formily/core' 129 | 130 | const form = createForm() 131 | 132 | const field = form.createVoidField({ name: 'target' }) 133 | 134 | console.log(isVoidField(field)) //true 135 | ``` 136 | 137 | ## isGeneralField 138 | 139 | #### Description 140 | 141 | Determine whether an object is a Field/ArrayField/ObjectField/VoidField object 142 | 143 | #### Signature 144 | 145 | ```ts 146 | interface isGeneralField { 147 | (target: any): target is Field | ArrayField | ObjectField | VoidField 148 | } 149 | ``` 150 | 151 | #### Example 152 | 153 | ```ts 154 | import { createForm, isGeneralField } from '@formily/core' 155 | 156 | const form = createForm() 157 | 158 | const field = form.createField({ name: 'target' }) 159 | const arr = form.createArrayField({ name: 'array' }) 160 | const obj = form.createObjectField({ name: 'object' }) 161 | const vod = form.createVoidField({ name: 'void' }) 162 | 163 | console.log(isGeneralField(field)) //true 164 | console.log(isGeneralField(arr)) //true 165 | console.log(isGeneralField(obj)) //true 166 | console.log(isGeneralField(vod)) //true 167 | console.log(isGeneralField({})) //false 168 | ``` 169 | 170 | ## isDataField 171 | 172 | #### Description 173 | 174 | Determine whether an object is a Field/ArrayField/ObjectField object 175 | 176 | #### Signature 177 | 178 | ```ts 179 | interface isDataField { 180 | (target: any): target is Field | ArrayField | ObjectField 181 | } 182 | ``` 183 | 184 | #### Example 185 | 186 | ```ts 187 | import { createForm, isDataField } from '@formily/core' 188 | 189 | const form = createForm() 190 | 191 | const field = form.createField({ name: 'target' }) 192 | const arr = form.createArrayField({ name: 'array' }) 193 | const obj = form.createObjectField({ name: 'object' }) 194 | const vod = form.createVoidField({ name: 'void' }) 195 | 196 | console.log(isDataField(field)) //true 197 | console.log(isDataField(arr)) //true 198 | console.log(isDataField(obj)) //true 199 | console.log(isDataField(vod)) //false 200 | console.log(isDataField({})) //false 201 | ``` 202 | 203 | ## isFormState 204 | 205 | #### Description 206 | 207 | Determine whether an object is [IFormState](/api/models/form#iformstate) object 208 | 209 | #### Signature 210 | 211 | ```ts 212 | interface isFormState { 213 | (target: any): target is IFormState 214 | } 215 | ``` 216 | 217 | #### Example 218 | 219 | ```ts 220 | import { createForm, isFormState } from '@formily/core' 221 | 222 | const form = createForm() 223 | 224 | console.log(isFormState(form)) //false 225 | console.log(isFormState(form.getState())) //true 226 | ``` 227 | 228 | ## isFieldState 229 | 230 | #### Description 231 | 232 | Determine whether an object is [IFieldState](/api/models/field#ifieldstate) object 233 | 234 | #### Signature 235 | 236 | ```ts 237 | interface isFieldState { 238 | (target: any): target is IFieldState 239 | } 240 | ``` 241 | 242 | #### Example 243 | 244 | ```ts 245 | import { createForm, isFieldState } from '@formily/core' 246 | 247 | const form = createForm() 248 | const field = form.createField({ 249 | name: 'target', 250 | }) 251 | 252 | console.log(isFieldState(field)) //false 253 | console.log(isFieldState(field.getState())) //true 254 | ``` 255 | 256 | ## isArrayFieldState 257 | 258 | #### Description 259 | 260 | Determine whether an object is [IArrayFieldState](/api/models/array-field#iarrayfieldstate) object 261 | 262 | #### Signature 263 | 264 | ```ts 265 | interface isArrayFieldState { 266 | (target: any): target is IArrayFieldState 267 | } 268 | ``` 269 | 270 | #### Example 271 | 272 | ```ts 273 | import { createForm, isArrayFieldState } from '@formily/core' 274 | 275 | const form = createForm() 276 | const field = form.createArrayField({ 277 | name: 'target', 278 | }) 279 | 280 | console.log(isArrayFieldState(field)) //false 281 | console.log(isArrayFieldState(field.getState())) //true 282 | ``` 283 | 284 | ## isObjectFieldState 285 | 286 | #### Description 287 | 288 | Determine whether an object is [IObjectFieldState](/api/models/object-field#iobjectfieldstate) object 289 | 290 | #### Signature 291 | 292 | ```ts 293 | interface isObjectFieldState { 294 | (target: any): target is IObjectFieldState 295 | } 296 | ``` 297 | 298 | #### Example 299 | 300 | ```ts 301 | import { createForm, isObjectFieldState } from '@formily/core' 302 | 303 | const form = createForm() 304 | const field = form.createObjectField({ 305 | name: 'target', 306 | }) 307 | 308 | console.log(isObjectFieldState(field)) //false 309 | console.log(isObjectFieldState(field.getState())) //true 310 | ``` 311 | 312 | ## isVoidFieldState 313 | 314 | #### Description 315 | 316 | Determine whether an object is [IVoidFieldState](/api/models/void-field#ivoidfieldstate) object 317 | 318 | #### Signature 319 | 320 | ```ts 321 | interface isVoidFieldState { 322 | (target: any): target is IVoidFieldState 323 | } 324 | ``` 325 | 326 | #### Example 327 | 328 | ```ts 329 | import { createForm, isVoidFieldState } from '@formily/core' 330 | 331 | const form = createForm() 332 | const field = form.createVoidField({ 333 | name: 'target', 334 | }) 335 | 336 | console.log(isVoidFieldState(field)) //false 337 | console.log(isVoidFieldState(field.getState())) //true 338 | ``` 339 | 340 | ## isGeneralFieldState 341 | 342 | #### Description 343 | 344 | Determine whether an object is an IFieldState/IArrayFieldState/IObjectFieldState/IVoidFieldState object 345 | 346 | #### Signature 347 | 348 | ```ts 349 | interface isGeneralFieldState { 350 | (target: any): target is 351 | | IFieldState 352 | | IArrayFieldState 353 | | IObjectFieldState 354 | | IVoidFieldState 355 | } 356 | ``` 357 | 358 | #### Example 359 | 360 | ```ts 361 | import { createForm, isGeneralFieldState } from '@formily/core' 362 | 363 | const form = createForm() 364 | 365 | const field = form.createField({ name: 'target' }) 366 | const arr = form.createArrayField({ name: 'array' }) 367 | const obj = form.createObjectField({ name: 'object' }) 368 | const vod = form.createVoidField({ name: 'void' }) 369 | 370 | console.log(isGeneralFieldState(field)) //false 371 | console.log(isGeneralFieldState(arr)) //false 372 | console.log(isGeneralFieldState(obj)) //false 373 | console.log(isGeneralFieldState(vod)) //false 374 | console.log(isGeneralFieldState(field.getState())) //true 375 | console.log(isGeneralFieldState(arr.getState())) //true 376 | console.log(isGeneralFieldState(obj.getState())) //true 377 | console.log(isGeneralFieldState(vod.getState())) //true 378 | console.log(isGeneralFieldState({})) //false 379 | ``` 380 | 381 | ## isDataFieldState 382 | 383 | #### Description 384 | 385 | Determine whether an object is an IFieldState/IArrayFieldState/IObjectFieldState object 386 | 387 | #### Signature 388 | 389 | ```ts 390 | interface isDataFieldState { 391 | (target: any): target is IFieldState | IArrayFieldState | IObjectFieldState 392 | } 393 | ``` 394 | 395 | #### Example 396 | 397 | ```ts 398 | import { createForm, isDataFieldState } from '@formily/core' 399 | 400 | const form = createForm() 401 | 402 | const field = form.createField({ name: 'target' }) 403 | const arr = form.createArrayField({ name: 'array' }) 404 | const obj = form.createObjectField({ name: 'object' }) 405 | const vod = form.createVoidField({ name: 'void' }) 406 | 407 | console.log(isDataFieldState(field)) //false 408 | console.log(isDataFieldState(arr)) //false 409 | console.log(isDataFieldState(obj)) //false 410 | console.log(isDataFieldState(vod)) //false 411 | console.log(isDataFieldState(field.getState())) //true 412 | console.log(isDataFieldState(arr.getState())) //true 413 | console.log(isDataFieldState(obj.getState())) //true 414 | console.log(isDataFieldState(vod.getState())) //false 415 | console.log(isDataFieldState({})) //false 416 | ``` 417 | 418 | ## isQuery 419 | 420 | #### Description 421 | 422 | Determine whether an object is a Query object 423 | 424 | #### Signature 425 | 426 | ```ts 427 | interface isQuery { 428 | (target: any): target is Query 429 | } 430 | ``` 431 | 432 | #### Example 433 | 434 | ```ts 435 | import { createForm, isQuery } from '@formily/core' 436 | 437 | const form = createForm() 438 | console.log(isQuery(form.query('target'))) //true 439 | ``` 440 | ``` -------------------------------------------------------------------------------- /packages/antd/docs/components/FormLayout.md: -------------------------------------------------------------------------------- ```markdown 1 | # FormLayout 2 | 3 | > Block-level layout batch control component, with the help of this component, we can easily control the layout mode of all FormItem components enclosed by FormLayout 4 | 5 | ## Markup Schema example 6 | 7 | ```tsx 8 | import React from 'react' 9 | import { Input, Select, FormItem, FormLayout } from '@formily/antd' 10 | import { createForm } from '@formily/core' 11 | import { FormProvider, createSchemaField } from '@formily/react' 12 | 13 | const SchemaField = createSchemaField({ 14 | components: { 15 | Input, 16 | Select, 17 | FormItem, 18 | FormLayout, 19 | }, 20 | }) 21 | 22 | const form = createForm() 23 | 24 | export default () => ( 25 | <FormProvider form={form}> 26 | <SchemaField> 27 | <SchemaField.Void 28 | x-component="FormLayout" 29 | x-component-props={{ 30 | labelCol: 6, 31 | wrapperCol: 10, 32 | }} 33 | > 34 | <SchemaField.String 35 | name="input" 36 | title="input box" 37 | x-decorator="FormItem" 38 | x-decorator-props={{ 39 | tooltip: <div>123</div>, 40 | }} 41 | x-component="Input" 42 | required 43 | /> 44 | <SchemaField.String 45 | name="select" 46 | title="select box" 47 | x-decorator="FormItem" 48 | x-component="Select" 49 | required 50 | /> 51 | </SchemaField.Void> 52 | </SchemaField> 53 | </FormProvider> 54 | ) 55 | ``` 56 | 57 | ## JSON Schema case 58 | 59 | ```tsx 60 | import React from 'react' 61 | import { Input, Select, FormItem, FormLayout } from '@formily/antd' 62 | import { createForm } from '@formily/core' 63 | import { FormProvider, createSchemaField } from '@formily/react' 64 | 65 | const SchemaField = createSchemaField({ 66 | components: { 67 | Input, 68 | Select, 69 | FormItem, 70 | FormLayout, 71 | }, 72 | }) 73 | 74 | const schema = { 75 | type: 'object', 76 | properties: { 77 | layout: { 78 | type: 'void', 79 | 'x-component': 'FormLayout', 80 | 'x-component-props': { 81 | labelCol: 6, 82 | wrapperCol: 10, 83 | layout: 'vertical', 84 | }, 85 | properties: { 86 | input: { 87 | type: 'string', 88 | title: 'input box', 89 | required: true, 90 | 'x-decorator': 'FormItem', 91 | 'x-decorator-props': { 92 | tooltip: <div>123</div>, 93 | }, 94 | 'x-component': 'Input', 95 | }, 96 | select: { 97 | type: 'string', 98 | title: 'Select box', 99 | required: true, 100 | 'x-decorator': 'FormItem', 101 | 'x-component': 'Select', 102 | }, 103 | }, 104 | }, 105 | }, 106 | } 107 | 108 | const form = createForm() 109 | 110 | export default () => ( 111 | <FormProvider form={form}> 112 | <SchemaField schema={schema} /> 113 | </FormProvider> 114 | ) 115 | ``` 116 | 117 | ## Pure JSX case 118 | 119 | ```tsx 120 | import React from 'react' 121 | import { 122 | Input, 123 | Select, 124 | FormItem, 125 | FormButtonGroup, 126 | Submit, 127 | FormLayout, 128 | } from '@formily/antd' 129 | import { createForm } from '@formily/core' 130 | import { FormProvider, Field } from '@formily/react' 131 | 132 | const form = createForm() 133 | 134 | export default () => ( 135 | <FormProvider form={form}> 136 | <FormLayout labelCol={6} wrapperCol={10}> 137 | <Field 138 | name="input" 139 | required 140 | title="input box" 141 | decorator={[FormItem]} 142 | component={[Input]} 143 | /> 144 | <Field 145 | name="select" 146 | required 147 | title="select box" 148 | decorator={[FormItem]} 149 | component={[Select]} 150 | /> 151 | <FormButtonGroup.FormItem> 152 | <Submit onSubmit={console.log}>Submit</Submit> 153 | </FormButtonGroup.FormItem> 154 | </FormLayout> 155 | </FormProvider> 156 | ) 157 | ``` 158 | 159 | ## API 160 | 161 | | Property name | Type | Description | Default value | 162 | | -------------- | -------------------------------------------------------------------------------------- | ----------------------------------------------------------- | ------------- | 163 | | style | CSSProperties | Style | - | 164 | | className | string | class name | - | 165 | | colon | boolean | Is there a colon | true | 166 | | requiredMark | boolean \| `"optional"` | Required mark style. Can use required mark or optional mark | true | 167 | | labelAlign | `'right' \| 'left' \| ('right' \| 'left')[]` | Label content alignment | - | 168 | | wrapperAlign | `'right' \| 'left' \| ('right' \| 'left')[]` | Component container content alignment | - | 169 | | labelWrap | boolean | Wrap label content | false | 170 | | labelWidth | number | Label width (px) | - | 171 | | wrapperWidth | number | Component container width (px) | - | 172 | | wrapperWrap | boolean | Component container wrap | false | 173 | | labelCol | `number \| number[]` | Label width (24 column) | - | 174 | | wrapperCol | `number \| number[]` | Component container width (24 column) | - | 175 | | fullness | boolean | Component container width 100% | false | 176 | | size | `'small' \|'default' \|'large'` | component size | default | 177 | | layout | `'vertical' \| 'horizontal' \| 'inline' \| ('vertical' \| 'horizontal' \| 'inline')[]` | layout mode | horizontal | 178 | | direction | `'rtl' \|'ltr'` | direction (not supported yet) | ltr | 179 | | inset | boolean | Inline layout | false | 180 | | shallow | boolean | shallow context transfer | true | 181 | | feedbackLayout | `'loose' \|'terse' \|'popover' \|'none'` | feedback layout | true | 182 | | tooltipLayout | `"icon" \| "text"` | Ask the prompt layout | `"icon"` | 183 | | tooltipIcon | ReactNode | Ask the prompt icon | - | 184 | | bordered | boolean | Is there a border | true | 185 | | breakpoints | number[] | Container size breakpoints | - | 186 | | gridColumnGap | number | Grid Column Gap | 8 | 187 | | gridRowGap | number | Grid Row Gap | 4 | 188 | | spaceGap | number | Space Gap | 8 | 189 | ```