This is page 16 of 35. Use http://codebase.md/alibaba/formily?lines=false&page={x} to view the full context. # Directory Structure ``` ├── .all-contributorsrc ├── .codecov.yml ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .github │ ├── CONTRIBUTING.md │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE │ │ └── config.yml │ ├── PULL_REQUEST_TEMPLATE.md │ └── workflows │ ├── check-pr-title.yml │ ├── ci.yml │ ├── commitlint.yml │ ├── issue-open-check.yml │ ├── package-size.yml │ └── pr-welcome.yml ├── .gitignore ├── .prettierrc.js ├── .umirc.js ├── .vscode │ └── cspell.json ├── .yarnrc ├── CHANGELOG.md ├── commitlint.config.js ├── devtools │ ├── .eslintrc │ └── chrome-extension │ ├── .npmignore │ ├── assets │ │ └── img │ │ ├── loading.svg │ │ └── logo │ │ ├── 128x128.png │ │ ├── 16x16.png │ │ ├── 38x38.png │ │ ├── 48x48.png │ │ ├── error.png │ │ ├── gray.png │ │ └── scalable.png │ ├── config │ │ ├── webpack.base.ts │ │ ├── webpack.dev.ts │ │ └── webpack.prod.ts │ ├── LICENSE.md │ ├── package.json │ ├── src │ │ ├── app │ │ │ ├── components │ │ │ │ ├── FieldTree.tsx │ │ │ │ ├── filter.ts │ │ │ │ ├── LeftPanel.tsx │ │ │ │ ├── RightPanel.tsx │ │ │ │ ├── SearchBox.tsx │ │ │ │ └── Tabs.tsx │ │ │ ├── demo.tsx │ │ │ └── index.tsx │ │ └── extension │ │ ├── backend.ts │ │ ├── background.ts │ │ ├── content.ts │ │ ├── devpanel.tsx │ │ ├── devtools.tsx │ │ ├── inject.ts │ │ ├── manifest.json │ │ ├── popup.tsx │ │ └── views │ │ ├── devpanel.ejs │ │ ├── devtools.ejs │ │ └── popup.ejs │ ├── tsconfig.build.json │ └── tsconfig.json ├── docs │ ├── functions │ │ ├── contributors.ts │ │ └── npm-search.ts │ ├── guide │ │ ├── advanced │ │ │ ├── async.md │ │ │ ├── async.zh-CN.md │ │ │ ├── build.md │ │ │ ├── build.zh-CN.md │ │ │ ├── business-logic.md │ │ │ ├── business-logic.zh-CN.md │ │ │ ├── calculator.md │ │ │ ├── calculator.zh-CN.md │ │ │ ├── controlled.md │ │ │ ├── controlled.zh-CN.md │ │ │ ├── custom.md │ │ │ ├── custom.zh-CN.md │ │ │ ├── destructor.md │ │ │ ├── destructor.zh-CN.md │ │ │ ├── input.less │ │ │ ├── layout.md │ │ │ ├── layout.zh-CN.md │ │ │ ├── linkages.md │ │ │ ├── linkages.zh-CN.md │ │ │ ├── validate.md │ │ │ └── validate.zh-CN.md │ │ ├── contribution.md │ │ ├── contribution.zh-CN.md │ │ ├── form-builder.md │ │ ├── form-builder.zh-CN.md │ │ ├── index.md │ │ ├── index.zh-CN.md │ │ ├── issue-helper.md │ │ ├── issue-helper.zh-CN.md │ │ ├── learn-formily.md │ │ ├── learn-formily.zh-CN.md │ │ ├── quick-start.md │ │ ├── quick-start.zh-CN.md │ │ ├── scenes │ │ │ ├── dialog-drawer.md │ │ │ ├── dialog-drawer.zh-CN.md │ │ │ ├── edit-detail.md │ │ │ ├── edit-detail.zh-CN.md │ │ │ ├── index.less │ │ │ ├── login-register.md │ │ │ ├── login-register.zh-CN.md │ │ │ ├── more.md │ │ │ ├── more.zh-CN.md │ │ │ ├── query-list.md │ │ │ ├── query-list.zh-CN.md │ │ │ ├── step-form.md │ │ │ ├── step-form.zh-CN.md │ │ │ ├── tab-form.md │ │ │ ├── tab-form.zh-CN.md │ │ │ └── VerifyCode.tsx │ │ ├── upgrade.md │ │ └── upgrade.zh-CN.md │ ├── index.md │ ├── index.zh-CN.md │ └── site │ ├── Contributors.less │ ├── Contributors.tsx │ ├── QrCode.less │ ├── QrCode.tsx │ ├── Section.less │ ├── Section.tsx │ └── styles.less ├── global.config.ts ├── jest.config.js ├── lerna.json ├── LICENSE.md ├── package.json ├── packages │ ├── .eslintrc │ ├── antd │ │ ├── __tests__ │ │ │ ├── moment.spec.ts │ │ │ └── sideEffects.spec.ts │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── build-style.ts │ │ ├── create-style.ts │ │ ├── docs │ │ │ ├── components │ │ │ │ ├── ArrayCards.md │ │ │ │ ├── ArrayCards.zh-CN.md │ │ │ │ ├── ArrayCollapse.md │ │ │ │ ├── ArrayCollapse.zh-CN.md │ │ │ │ ├── ArrayItems.md │ │ │ │ ├── ArrayItems.zh-CN.md │ │ │ │ ├── ArrayTable.md │ │ │ │ ├── ArrayTable.zh-CN.md │ │ │ │ ├── ArrayTabs.md │ │ │ │ ├── ArrayTabs.zh-CN.md │ │ │ │ ├── Cascader.md │ │ │ │ ├── Cascader.zh-CN.md │ │ │ │ ├── Checkbox.md │ │ │ │ ├── Checkbox.zh-CN.md │ │ │ │ ├── DatePicker.md │ │ │ │ ├── DatePicker.zh-CN.md │ │ │ │ ├── Editable.md │ │ │ │ ├── Editable.zh-CN.md │ │ │ │ ├── Form.md │ │ │ │ ├── Form.zh-CN.md │ │ │ │ ├── FormButtonGroup.md │ │ │ │ ├── FormButtonGroup.zh-CN.md │ │ │ │ ├── FormCollapse.md │ │ │ │ ├── FormCollapse.zh-CN.md │ │ │ │ ├── FormDialog.md │ │ │ │ ├── FormDialog.zh-CN.md │ │ │ │ ├── FormDrawer.md │ │ │ │ ├── FormDrawer.zh-CN.md │ │ │ │ ├── FormGrid.md │ │ │ │ ├── FormGrid.zh-CN.md │ │ │ │ ├── FormItem.md │ │ │ │ ├── FormItem.zh-CN.md │ │ │ │ ├── FormLayout.md │ │ │ │ ├── FormLayout.zh-CN.md │ │ │ │ ├── FormStep.md │ │ │ │ ├── FormStep.zh-CN.md │ │ │ │ ├── FormTab.md │ │ │ │ ├── FormTab.zh-CN.md │ │ │ │ ├── index.md │ │ │ │ ├── index.zh-CN.md │ │ │ │ ├── Input.md │ │ │ │ ├── Input.zh-CN.md │ │ │ │ ├── NumberPicker.md │ │ │ │ ├── NumberPicker.zh-CN.md │ │ │ │ ├── Password.md │ │ │ │ ├── Password.zh-CN.md │ │ │ │ ├── PreviewText.md │ │ │ │ ├── PreviewText.zh-CN.md │ │ │ │ ├── Radio.md │ │ │ │ ├── Radio.zh-CN.md │ │ │ │ ├── Reset.md │ │ │ │ ├── Reset.zh-CN.md │ │ │ │ ├── Select.md │ │ │ │ ├── Select.zh-CN.md │ │ │ │ ├── SelectTable.md │ │ │ │ ├── SelectTable.zh-CN.md │ │ │ │ ├── Space.md │ │ │ │ ├── Space.zh-CN.md │ │ │ │ ├── Submit.md │ │ │ │ ├── Submit.zh-CN.md │ │ │ │ ├── Switch.md │ │ │ │ ├── Switch.zh-CN.md │ │ │ │ ├── TimePicker.md │ │ │ │ ├── TimePicker.zh-CN.md │ │ │ │ ├── Transfer.md │ │ │ │ ├── Transfer.zh-CN.md │ │ │ │ ├── TreeSelect.md │ │ │ │ ├── TreeSelect.zh-CN.md │ │ │ │ ├── Upload.md │ │ │ │ └── Upload.zh-CN.md │ │ │ ├── index.md │ │ │ └── index.zh-CN.md │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __builtins__ │ │ │ │ ├── hooks │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── useClickAway.ts │ │ │ │ │ └── usePrefixCls.ts │ │ │ │ ├── index.ts │ │ │ │ ├── loading.ts │ │ │ │ ├── moment.ts │ │ │ │ ├── pickDataProps.ts │ │ │ │ ├── portal.tsx │ │ │ │ ├── render.ts │ │ │ │ └── sort.tsx │ │ │ ├── array-base │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── array-cards │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── array-collapse │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── array-items │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── array-table │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── array-tabs │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── cascader │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── checkbox │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── date-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── editable │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── form │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── form-button-group │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── form-collapse │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-dialog │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-drawer │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-grid │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── form-item │ │ │ │ ├── animation.less │ │ │ │ ├── grid.less │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── form-layout │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ ├── style.ts │ │ │ │ └── useResponsiveFormLayout.ts │ │ │ ├── form-step │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-tab │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── index.ts │ │ │ ├── input │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── number-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── password │ │ │ │ ├── index.tsx │ │ │ │ ├── PasswordStrength.tsx │ │ │ │ └── style.ts │ │ │ ├── preview-text │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── radio │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ └── style.ts │ │ │ ├── reset │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── select │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── select-table │ │ │ │ ├── index.tsx │ │ │ │ ├── style.less │ │ │ │ ├── style.ts │ │ │ │ ├── useCheckSlackly.tsx │ │ │ │ ├── useFilterOptions.tsx │ │ │ │ ├── useFlatOptions.tsx │ │ │ │ ├── useSize.tsx │ │ │ │ ├── useTitleAddon.tsx │ │ │ │ └── utils.ts │ │ │ ├── space │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── style.less │ │ │ ├── style.ts │ │ │ ├── submit │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── switch │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── time-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── transfer │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── tree-select │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ └── upload │ │ │ ├── index.tsx │ │ │ ├── placeholder.ts │ │ │ └── style.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── benchmark │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── src │ │ │ └── index.tsx │ │ ├── template.ejs │ │ ├── tsconfig.build.json │ │ ├── tsconfig.json │ │ ├── webpack.base.ts │ │ ├── webpack.dev.ts │ │ └── webpack.prod.ts │ ├── core │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── docs │ │ │ ├── api │ │ │ │ ├── entry │ │ │ │ │ ├── ActionResponse.less │ │ │ │ │ ├── ActionResponse.tsx │ │ │ │ │ ├── createForm.md │ │ │ │ │ ├── createForm.zh-CN.md │ │ │ │ │ ├── FieldEffectHooks.md │ │ │ │ │ ├── FieldEffectHooks.zh-CN.md │ │ │ │ │ ├── FormChecker.md │ │ │ │ │ ├── FormChecker.zh-CN.md │ │ │ │ │ ├── FormEffectHooks.md │ │ │ │ │ ├── FormEffectHooks.zh-CN.md │ │ │ │ │ ├── FormHooksAPI.md │ │ │ │ │ ├── FormHooksAPI.zh-CN.md │ │ │ │ │ ├── FormPath.md │ │ │ │ │ ├── FormPath.zh-CN.md │ │ │ │ │ ├── FormValidatorRegistry.md │ │ │ │ │ └── FormValidatorRegistry.zh-CN.md │ │ │ │ └── models │ │ │ │ ├── ArrayField.md │ │ │ │ ├── ArrayField.zh-CN.md │ │ │ │ ├── Field.md │ │ │ │ ├── Field.zh-CN.md │ │ │ │ ├── Form.md │ │ │ │ ├── Form.zh-CN.md │ │ │ │ ├── ObjectField.md │ │ │ │ ├── ObjectField.zh-CN.md │ │ │ │ ├── Query.md │ │ │ │ ├── Query.zh-CN.md │ │ │ │ ├── VoidField.md │ │ │ │ └── VoidField.zh-CN.md │ │ │ ├── guide │ │ │ │ ├── architecture.md │ │ │ │ ├── architecture.zh-CN.md │ │ │ │ ├── field.md │ │ │ │ ├── field.zh-CN.md │ │ │ │ ├── form.md │ │ │ │ ├── form.zh-CN.md │ │ │ │ ├── index.md │ │ │ │ ├── index.zh-CN.md │ │ │ │ ├── mvvm.md │ │ │ │ ├── mvvm.zh-CN.md │ │ │ │ ├── values.md │ │ │ │ └── values.zh-CN.md │ │ │ ├── index.md │ │ │ └── index.zh-CN.md │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── array.spec.ts │ │ │ │ ├── effects.spec.ts │ │ │ │ ├── externals.spec.ts │ │ │ │ ├── field.spec.ts │ │ │ │ ├── form.spec.ts │ │ │ │ ├── graph.spec.ts │ │ │ │ ├── heart.spec.ts │ │ │ │ ├── internals.spec.ts │ │ │ │ ├── lifecycle.spec.ts │ │ │ │ ├── object.spec.ts │ │ │ │ ├── shared.ts │ │ │ │ └── void.spec.ts │ │ │ ├── effects │ │ │ │ ├── index.ts │ │ │ │ ├── onFieldEffects.ts │ │ │ │ └── onFormEffects.ts │ │ │ ├── global.d.ts │ │ │ ├── index.ts │ │ │ ├── models │ │ │ │ ├── ArrayField.ts │ │ │ │ ├── BaseField.ts │ │ │ │ ├── Field.ts │ │ │ │ ├── Form.ts │ │ │ │ ├── Graph.ts │ │ │ │ ├── Heart.ts │ │ │ │ ├── index.ts │ │ │ │ ├── LifeCycle.ts │ │ │ │ ├── ObjectField.ts │ │ │ │ ├── Query.ts │ │ │ │ ├── types.ts │ │ │ │ └── VoidField.ts │ │ │ ├── shared │ │ │ │ ├── checkers.ts │ │ │ │ ├── constants.ts │ │ │ │ ├── effective.ts │ │ │ │ ├── externals.ts │ │ │ │ └── internals.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── element │ │ ├── .npmignore │ │ ├── build-style.ts │ │ ├── create-style.ts │ │ ├── docs │ │ │ ├── .vuepress │ │ │ │ ├── components │ │ │ │ │ ├── createCodeSandBox.js │ │ │ │ │ ├── dumi-previewer.vue │ │ │ │ │ └── highlight.js │ │ │ │ ├── config.js │ │ │ │ ├── enhanceApp.js │ │ │ │ ├── styles │ │ │ │ │ └── index.styl │ │ │ │ └── util.js │ │ │ ├── demos │ │ │ │ ├── guide │ │ │ │ │ ├── array-cards │ │ │ │ │ │ ├── effects-json-schema.vue │ │ │ │ │ │ ├── effects-markup-schema.vue │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── array-collapse │ │ │ │ │ │ ├── effects-json-schema.vue │ │ │ │ │ │ ├── effects-markup-schema.vue │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── array-items │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── array-table │ │ │ │ │ │ ├── effects-json-schema.vue │ │ │ │ │ │ ├── effects-markup-schema.vue │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── array-tabs │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── cascader │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── checkbox │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── date-picker │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── editable │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── form-button-group.vue │ │ │ │ │ ├── form-collapse │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── form-dialog │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── form-drawer │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── form-grid │ │ │ │ │ │ ├── form.vue │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── native.vue │ │ │ │ │ ├── form-item │ │ │ │ │ │ ├── bordered-none.vue │ │ │ │ │ │ ├── common.vue │ │ │ │ │ │ ├── feedback.vue │ │ │ │ │ │ ├── inset.vue │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ ├── size.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── form-layout │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── form-step │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── form-tab │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ └── markup-schema.vue │ │ │ │ │ ├── form.vue │ │ │ │ │ ├── input │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── input-number │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── password │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── preview-text │ │ │ │ │ │ ├── base.vue │ │ │ │ │ │ └── extend.vue │ │ │ │ │ ├── radio │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── reset │ │ │ │ │ │ ├── base.vue │ │ │ │ │ │ ├── force.vue │ │ │ │ │ │ └── validate.vue │ │ │ │ │ ├── select │ │ │ │ │ │ ├── json-schema-async.vue │ │ │ │ │ │ ├── json-schema-sync.vue │ │ │ │ │ │ ├── markup-schema-async-search.vue │ │ │ │ │ │ ├── markup-schema-async.vue │ │ │ │ │ │ ├── markup-schema-sync.vue │ │ │ │ │ │ ├── template-async.vue │ │ │ │ │ │ └── template-sync.vue │ │ │ │ │ ├── space │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── submit │ │ │ │ │ │ ├── base.vue │ │ │ │ │ │ └── loading.vue │ │ │ │ │ ├── switch │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── time-picker │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ ├── transfer │ │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ │ └── template.vue │ │ │ │ │ └── upload │ │ │ │ │ ├── json-schema.vue │ │ │ │ │ ├── markup-schema.vue │ │ │ │ │ └── template.vue │ │ │ │ └── index.vue │ │ │ ├── guide │ │ │ │ ├── array-cards.md │ │ │ │ ├── array-collapse.md │ │ │ │ ├── array-items.md │ │ │ │ ├── array-table.md │ │ │ │ ├── array-tabs.md │ │ │ │ ├── cascader.md │ │ │ │ ├── checkbox.md │ │ │ │ ├── date-picker.md │ │ │ │ ├── editable.md │ │ │ │ ├── form-button-group.md │ │ │ │ ├── form-collapse.md │ │ │ │ ├── form-dialog.md │ │ │ │ ├── form-drawer.md │ │ │ │ ├── form-grid.md │ │ │ │ ├── form-item.md │ │ │ │ ├── form-layout.md │ │ │ │ ├── form-step.md │ │ │ │ ├── form-tab.md │ │ │ │ ├── form.md │ │ │ │ ├── index.md │ │ │ │ ├── input-number.md │ │ │ │ ├── input.md │ │ │ │ ├── password.md │ │ │ │ ├── preview-text.md │ │ │ │ ├── radio.md │ │ │ │ ├── reset.md │ │ │ │ ├── select.md │ │ │ │ ├── space.md │ │ │ │ ├── submit.md │ │ │ │ ├── switch.md │ │ │ │ ├── time-picker.md │ │ │ │ ├── transfer.md │ │ │ │ └── upload.md │ │ │ └── README.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __builtins__ │ │ │ │ ├── configs │ │ │ │ │ └── index.ts │ │ │ │ ├── index.ts │ │ │ │ ├── shared │ │ │ │ │ ├── create-context.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── loading.ts │ │ │ │ │ ├── portal.ts │ │ │ │ │ ├── resolve-component.ts │ │ │ │ │ ├── transform-component.ts │ │ │ │ │ ├── types.ts │ │ │ │ │ └── utils.ts │ │ │ │ └── styles │ │ │ │ └── common.scss │ │ │ ├── array-base │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── array-cards │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── array-collapse │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── array-items │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── array-table │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── array-tabs │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── cascader │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── checkbox │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── date-picker │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── editable │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── el-form │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── el-form-item │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── form │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── form-button-group │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── form-collapse │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── form-dialog │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── form-drawer │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── form-grid │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── form-item │ │ │ │ ├── animation.scss │ │ │ │ ├── grid.scss │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ ├── style.ts │ │ │ │ └── var.scss │ │ │ ├── form-layout │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ ├── style.ts │ │ │ │ └── useResponsiveFormLayout.ts │ │ │ ├── form-step │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── form-tab │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── index.ts │ │ │ ├── input │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── input-number │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── password │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── preview-text │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── radio │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── reset │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── select │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── space │ │ │ │ ├── index.ts │ │ │ │ ├── style.scss │ │ │ │ └── style.ts │ │ │ ├── style.ts │ │ │ ├── submit │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── switch │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── time-picker │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ ├── transfer │ │ │ │ ├── index.ts │ │ │ │ └── style.ts │ │ │ └── upload │ │ │ ├── index.ts │ │ │ └── style.ts │ │ ├── transformer.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── grid │ │ ├── .npmignore │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── index.ts │ │ │ └── observer.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── json-schema │ │ ├── .npmignore │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── __snapshots__ │ │ │ │ │ └── schema.spec.ts.snap │ │ │ │ ├── compiler.spec.ts │ │ │ │ ├── patches.spec.ts │ │ │ │ ├── schema.spec.ts │ │ │ │ ├── server-validate.spec.ts │ │ │ │ ├── shared.spec.ts │ │ │ │ ├── transformer.spec.ts │ │ │ │ └── traverse.spec.ts │ │ │ ├── compiler.ts │ │ │ ├── global.d.ts │ │ │ ├── index.ts │ │ │ ├── patches.ts │ │ │ ├── polyfills │ │ │ │ ├── index.ts │ │ │ │ └── SPECIFICATION_1_0.ts │ │ │ ├── schema.ts │ │ │ ├── shared.ts │ │ │ ├── transformer.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── next │ │ ├── __tests__ │ │ │ ├── moment.spec.ts │ │ │ └── sideEffects.spec.ts │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── build-style.ts │ │ ├── create-style.ts │ │ ├── docs │ │ │ ├── components │ │ │ │ ├── ArrayCards.md │ │ │ │ ├── ArrayCards.zh-CN.md │ │ │ │ ├── ArrayCollapse.md │ │ │ │ ├── ArrayCollapse.zh-CN.md │ │ │ │ ├── ArrayItems.md │ │ │ │ ├── ArrayItems.zh-CN.md │ │ │ │ ├── ArrayTable.md │ │ │ │ ├── ArrayTable.zh-CN.md │ │ │ │ ├── Cascader.md │ │ │ │ ├── Cascader.zh-CN.md │ │ │ │ ├── Checkbox.md │ │ │ │ ├── Checkbox.zh-CN.md │ │ │ │ ├── DatePicker.md │ │ │ │ ├── DatePicker.zh-CN.md │ │ │ │ ├── DatePicker2.md │ │ │ │ ├── DatePicker2.zh-CN.md │ │ │ │ ├── Editable.md │ │ │ │ ├── Editable.zh-CN.md │ │ │ │ ├── Form.md │ │ │ │ ├── Form.zh-CN.md │ │ │ │ ├── FormButtonGroup.md │ │ │ │ ├── FormButtonGroup.zh-CN.md │ │ │ │ ├── FormCollapse.md │ │ │ │ ├── FormCollapse.zh-CN.md │ │ │ │ ├── FormDialog.md │ │ │ │ ├── FormDialog.zh-CN.md │ │ │ │ ├── FormDrawer.md │ │ │ │ ├── FormDrawer.zh-CN.md │ │ │ │ ├── FormGrid.md │ │ │ │ ├── FormGrid.zh-CN.md │ │ │ │ ├── FormItem.md │ │ │ │ ├── FormItem.zh-CN.md │ │ │ │ ├── FormLayout.md │ │ │ │ ├── FormLayout.zh-CN.md │ │ │ │ ├── FormStep.md │ │ │ │ ├── FormStep.zh-CN.md │ │ │ │ ├── FormTab.md │ │ │ │ ├── FormTab.zh-CN.md │ │ │ │ ├── index.md │ │ │ │ ├── index.zh-CN.md │ │ │ │ ├── Input.md │ │ │ │ ├── Input.zh-CN.md │ │ │ │ ├── NumberPicker.md │ │ │ │ ├── NumberPicker.zh-CN.md │ │ │ │ ├── Password.md │ │ │ │ ├── Password.zh-CN.md │ │ │ │ ├── PreviewText.md │ │ │ │ ├── PreviewText.zh-CN.md │ │ │ │ ├── Radio.md │ │ │ │ ├── Radio.zh-CN.md │ │ │ │ ├── Reset.md │ │ │ │ ├── Reset.zh-CN.md │ │ │ │ ├── Select.md │ │ │ │ ├── Select.zh-CN.md │ │ │ │ ├── SelectTable.md │ │ │ │ ├── SelectTable.zh-CN.md │ │ │ │ ├── Space.md │ │ │ │ ├── Space.zh-CN.md │ │ │ │ ├── Submit.md │ │ │ │ ├── Submit.zh-CN.md │ │ │ │ ├── Switch.md │ │ │ │ ├── Switch.zh-CN.md │ │ │ │ ├── TimePicker.md │ │ │ │ ├── TimePicker.zh-CN.md │ │ │ │ ├── TimePicker2.md │ │ │ │ ├── TimePicker2.zh-CN.md │ │ │ │ ├── Transfer.md │ │ │ │ ├── Transfer.zh-CN.md │ │ │ │ ├── TreeSelect.md │ │ │ │ ├── TreeSelect.zh-CN.md │ │ │ │ ├── Upload.md │ │ │ │ └── Upload.zh-CN.md │ │ │ ├── index.md │ │ │ └── index.zh-CN.md │ │ ├── LESENCE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __builtins__ │ │ │ │ ├── empty.tsx │ │ │ │ ├── hooks │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── useClickAway.ts │ │ │ │ │ └── usePrefixCls.ts │ │ │ │ ├── icons.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── loading.ts │ │ │ │ ├── mapSize.ts │ │ │ │ ├── mapStatus.ts │ │ │ │ ├── moment.ts │ │ │ │ ├── pickDataProps.ts │ │ │ │ ├── portal.tsx │ │ │ │ ├── render.ts │ │ │ │ └── toArray.ts │ │ │ ├── array-base │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── array-cards │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── array-collapse │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── array-items │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── array-table │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── cascader │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── checkbox │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── date-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── date-picker2 │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── editable │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── form │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── form-button-group │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── form-collapse │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-dialog │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-drawer │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-grid │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── form-item │ │ │ │ ├── animation.scss │ │ │ │ ├── grid.scss │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ ├── scss │ │ │ │ │ └── variable.scss │ │ │ │ └── style.ts │ │ │ ├── form-layout │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ ├── style.ts │ │ │ │ └── useResponsiveFormLayout.ts │ │ │ ├── form-step │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── form-tab │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── index.ts │ │ │ ├── input │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── main.scss │ │ │ ├── number-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── password │ │ │ │ ├── index.tsx │ │ │ │ ├── PasswordStrength.tsx │ │ │ │ └── style.ts │ │ │ ├── preview-text │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── radio │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── reset │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── select │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── select-table │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ ├── style.ts │ │ │ │ ├── useCheckSlackly.tsx │ │ │ │ ├── useFilterOptions.tsx │ │ │ │ ├── useFlatOptions.tsx │ │ │ │ ├── useSize.tsx │ │ │ │ ├── useTitleAddon.tsx │ │ │ │ └── utils.ts │ │ │ ├── space │ │ │ │ ├── index.tsx │ │ │ │ ├── main.scss │ │ │ │ └── style.ts │ │ │ ├── style.ts │ │ │ ├── submit │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── switch │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── time-picker │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── time-picker2 │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── transfer │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ ├── tree-select │ │ │ │ ├── index.tsx │ │ │ │ └── style.ts │ │ │ └── upload │ │ │ ├── index.tsx │ │ │ ├── main.scss │ │ │ ├── placeholder.ts │ │ │ └── style.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── path │ │ ├── .npmignore │ │ ├── benchmark.ts │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── accessor.spec.ts │ │ │ │ ├── basic.spec.ts │ │ │ │ ├── match.spec.ts │ │ │ │ ├── parser.spec.ts │ │ │ │ └── share.spec.ts │ │ │ ├── contexts.ts │ │ │ ├── destructor.ts │ │ │ ├── index.ts │ │ │ ├── matcher.ts │ │ │ ├── parser.ts │ │ │ ├── shared.ts │ │ │ ├── tokenizer.ts │ │ │ ├── tokens.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── react │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── docs │ │ │ ├── api │ │ │ │ ├── components │ │ │ │ │ ├── ArrayField.md │ │ │ │ │ ├── ArrayField.zh-CN.md │ │ │ │ │ ├── ExpressionScope.md │ │ │ │ │ ├── ExpressionScope.zh-CN.md │ │ │ │ │ ├── Field.md │ │ │ │ │ ├── Field.zh-CN.md │ │ │ │ │ ├── FormConsumer.md │ │ │ │ │ ├── FormConsumer.zh-CN.md │ │ │ │ │ ├── FormProvider.md │ │ │ │ │ ├── FormProvider.zh-CN.md │ │ │ │ │ ├── ObjectField.md │ │ │ │ │ ├── ObjectField.zh-CN.md │ │ │ │ │ ├── RecordScope.md │ │ │ │ │ ├── RecordScope.zh-CN.md │ │ │ │ │ ├── RecordsScope.md │ │ │ │ │ ├── RecordsScope.zh-CN.md │ │ │ │ │ ├── RecursionField.md │ │ │ │ │ ├── RecursionField.zh-CN.md │ │ │ │ │ ├── SchemaField.md │ │ │ │ │ ├── SchemaField.zh-CN.md │ │ │ │ │ ├── VoidField.md │ │ │ │ │ └── VoidField.zh-CN.md │ │ │ │ ├── hooks │ │ │ │ │ ├── useExpressionScope.md │ │ │ │ │ ├── useExpressionScope.zh-CN.md │ │ │ │ │ ├── useField.md │ │ │ │ │ ├── useField.zh-CN.md │ │ │ │ │ ├── useFieldSchema.md │ │ │ │ │ ├── useFieldSchema.zh-CN.md │ │ │ │ │ ├── useForm.md │ │ │ │ │ ├── useForm.zh-CN.md │ │ │ │ │ ├── useFormEffects.md │ │ │ │ │ ├── useFormEffects.zh-CN.md │ │ │ │ │ ├── useParentForm.md │ │ │ │ │ └── useParentForm.zh-CN.md │ │ │ │ └── shared │ │ │ │ ├── connect.md │ │ │ │ ├── connect.zh-CN.md │ │ │ │ ├── context.md │ │ │ │ ├── context.zh-CN.md │ │ │ │ ├── mapProps.md │ │ │ │ ├── mapProps.zh-CN.md │ │ │ │ ├── mapReadPretty.md │ │ │ │ ├── mapReadPretty.zh-CN.md │ │ │ │ ├── observer.md │ │ │ │ ├── observer.zh-CN.md │ │ │ │ ├── Schema.md │ │ │ │ └── Schema.zh-CN.md │ │ │ ├── guide │ │ │ │ ├── architecture.md │ │ │ │ ├── architecture.zh-CN.md │ │ │ │ ├── concept.md │ │ │ │ ├── concept.zh-CN.md │ │ │ │ ├── index.md │ │ │ │ └── index.zh-CN.md │ │ │ ├── index.md │ │ │ └── index.zh-CN.md │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── expression.spec.tsx │ │ │ │ ├── field.spec.tsx │ │ │ │ ├── form.spec.tsx │ │ │ │ ├── schema.json.spec.tsx │ │ │ │ ├── schema.markup.spec.tsx │ │ │ │ └── shared.tsx │ │ │ ├── components │ │ │ │ ├── ArrayField.tsx │ │ │ │ ├── ExpressionScope.tsx │ │ │ │ ├── Field.tsx │ │ │ │ ├── FormConsumer.tsx │ │ │ │ ├── FormProvider.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── ObjectField.tsx │ │ │ │ ├── ReactiveField.tsx │ │ │ │ ├── RecordScope.tsx │ │ │ │ ├── RecordsScope.tsx │ │ │ │ ├── RecursionField.tsx │ │ │ │ ├── SchemaField.tsx │ │ │ │ └── VoidField.tsx │ │ │ ├── global.d.ts │ │ │ ├── hooks │ │ │ │ ├── index.ts │ │ │ │ ├── useAttach.ts │ │ │ │ ├── useExpressionScope.ts │ │ │ │ ├── useField.ts │ │ │ │ ├── useFieldSchema.ts │ │ │ │ ├── useForm.ts │ │ │ │ ├── useFormEffects.ts │ │ │ │ └── useParentForm.ts │ │ │ ├── index.ts │ │ │ ├── shared │ │ │ │ ├── connect.ts │ │ │ │ ├── context.ts │ │ │ │ ├── index.ts │ │ │ │ └── render.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── reactive │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── benchmark.ts │ │ ├── docs │ │ │ ├── api │ │ │ │ ├── action.md │ │ │ │ ├── action.zh-CN.md │ │ │ │ ├── autorun.md │ │ │ │ ├── autorun.zh-CN.md │ │ │ │ ├── batch.md │ │ │ │ ├── batch.zh-CN.md │ │ │ │ ├── define.md │ │ │ │ ├── define.zh-CN.md │ │ │ │ ├── hasCollected.md │ │ │ │ ├── hasCollected.zh-CN.md │ │ │ │ ├── markObservable.md │ │ │ │ ├── markObservable.zh-CN.md │ │ │ │ ├── markRaw.md │ │ │ │ ├── markRaw.zh-CN.md │ │ │ │ ├── model.md │ │ │ │ ├── model.zh-CN.md │ │ │ │ ├── observable.md │ │ │ │ ├── observable.zh-CN.md │ │ │ │ ├── observe.md │ │ │ │ ├── observe.zh-CN.md │ │ │ │ ├── raw.md │ │ │ │ ├── raw.zh-CN.md │ │ │ │ ├── react │ │ │ │ │ ├── observer.md │ │ │ │ │ └── observer.zh-CN.md │ │ │ │ ├── reaction.md │ │ │ │ ├── reaction.zh-CN.md │ │ │ │ ├── toJS.md │ │ │ │ ├── toJS.zh-CN.md │ │ │ │ ├── tracker.md │ │ │ │ ├── tracker.zh-CN.md │ │ │ │ ├── typeChecker.md │ │ │ │ ├── typeChecker.zh-CN.md │ │ │ │ ├── untracked.md │ │ │ │ ├── untracked.zh-CN.md │ │ │ │ └── vue │ │ │ │ ├── observer.md │ │ │ │ └── observer.zh-CN.md │ │ │ ├── guide │ │ │ │ ├── best-practice.md │ │ │ │ ├── best-practice.zh-CN.md │ │ │ │ ├── concept.md │ │ │ │ ├── concept.zh-CN.md │ │ │ │ ├── index.md │ │ │ │ └── index.zh-CN.md │ │ │ ├── index.md │ │ │ └── index.zh-CN.md │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── action.spec.ts │ │ │ │ ├── annotations.spec.ts │ │ │ │ ├── array.spec.ts │ │ │ │ ├── autorun.spec.ts │ │ │ │ ├── batch.spec.ts │ │ │ │ ├── collections-map.spec.ts │ │ │ │ ├── collections-set.spec.ts │ │ │ │ ├── collections-weakmap.spec.ts │ │ │ │ ├── collections-weakset.spec.ts │ │ │ │ ├── define.spec.ts │ │ │ │ ├── externals.spec.ts │ │ │ │ ├── hasCollected.spec.ts │ │ │ │ ├── observable.spec.ts │ │ │ │ ├── observe.spec.ts │ │ │ │ ├── tracker.spec.ts │ │ │ │ └── untracked.spec.ts │ │ │ ├── action.ts │ │ │ ├── annotations │ │ │ │ ├── box.ts │ │ │ │ ├── computed.ts │ │ │ │ ├── index.ts │ │ │ │ ├── observable.ts │ │ │ │ ├── ref.ts │ │ │ │ └── shallow.ts │ │ │ ├── array.ts │ │ │ ├── autorun.ts │ │ │ ├── batch.ts │ │ │ ├── checkers.ts │ │ │ ├── environment.ts │ │ │ ├── externals.ts │ │ │ ├── global.d.ts │ │ │ ├── handlers.ts │ │ │ ├── index.ts │ │ │ ├── internals.ts │ │ │ ├── model.ts │ │ │ ├── observable.ts │ │ │ ├── observe.ts │ │ │ ├── reaction.ts │ │ │ ├── tracker.ts │ │ │ ├── tree.ts │ │ │ ├── types.ts │ │ │ └── untracked.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── reactive-react │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── hooks │ │ │ │ ├── index.ts │ │ │ │ ├── useCompatEffect.ts │ │ │ │ ├── useCompatFactory.ts │ │ │ │ ├── useDidUpdate.ts │ │ │ │ ├── useForceUpdate.ts │ │ │ │ ├── useLayoutEffect.ts │ │ │ │ └── useObserver.ts │ │ │ ├── index.ts │ │ │ ├── observer.ts │ │ │ ├── shared │ │ │ │ ├── gc.ts │ │ │ │ ├── global.ts │ │ │ │ ├── immediate.ts │ │ │ │ └── index.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── reactive-test-cases-for-react18 │ │ ├── .npmignore │ │ ├── .umirc.js │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── src │ │ │ ├── index.js │ │ │ └── MySlowList.js │ │ ├── template.ejs │ │ ├── tsconfig.build.json │ │ ├── tsconfig.json │ │ ├── webpack.base.ts │ │ ├── webpack.dev.ts │ │ └── webpack.prod.ts │ ├── reactive-vue │ │ ├── .npmignore │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ └── observer.spec.ts │ │ │ ├── hooks │ │ │ │ ├── index.ts │ │ │ │ └── useObserver.ts │ │ │ ├── index.ts │ │ │ ├── observer │ │ │ │ ├── collectData.ts │ │ │ │ ├── index.ts │ │ │ │ ├── observerInVue2.ts │ │ │ │ └── observerInVue3.ts │ │ │ └── types.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── shared │ │ ├── .npmignore │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ └── index.spec.ts │ │ │ ├── array.ts │ │ │ ├── case.ts │ │ │ ├── checkers.ts │ │ │ ├── clone.ts │ │ │ ├── compare.ts │ │ │ ├── defaults.ts │ │ │ ├── deprecate.ts │ │ │ ├── global.ts │ │ │ ├── index.ts │ │ │ ├── instanceof.ts │ │ │ ├── isEmpty.ts │ │ │ ├── merge.ts │ │ │ ├── middleware.ts │ │ │ ├── path.ts │ │ │ ├── string.ts │ │ │ ├── subscribable.ts │ │ │ └── uid.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ ├── validator │ │ ├── .npmignore │ │ ├── LICENSE.md │ │ ├── package.json │ │ ├── README.md │ │ ├── rollup.config.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── parser.spec.ts │ │ │ │ ├── registry.spec.ts │ │ │ │ └── validator.spec.ts │ │ │ ├── formats.ts │ │ │ ├── index.ts │ │ │ ├── locale.ts │ │ │ ├── parser.ts │ │ │ ├── registry.ts │ │ │ ├── rules.ts │ │ │ ├── template.ts │ │ │ ├── types.ts │ │ │ └── validator.ts │ │ ├── tsconfig.build.json │ │ └── tsconfig.json │ └── vue │ ├── .npmignore │ ├── bin │ │ ├── formily-vue-fix.js │ │ └── formily-vue-switch.js │ ├── docs │ │ ├── .vuepress │ │ │ ├── components │ │ │ │ ├── createCodeSandBox.js │ │ │ │ ├── dumi-previewer.vue │ │ │ │ └── highlight.js │ │ │ ├── config.js │ │ │ ├── enhanceApp.js │ │ │ └── styles │ │ │ └── index.styl │ │ ├── api │ │ │ ├── components │ │ │ │ ├── array-field.md │ │ │ │ ├── expression-scope.md │ │ │ │ ├── field.md │ │ │ │ ├── form-consumer.md │ │ │ │ ├── form-provider.md │ │ │ │ ├── object-field.md │ │ │ │ ├── recursion-field-with-component.md │ │ │ │ ├── recursion-field.md │ │ │ │ ├── schema-field-with-schema.md │ │ │ │ ├── schema-field.md │ │ │ │ └── void-field.md │ │ │ ├── hooks │ │ │ │ ├── use-field-schema.md │ │ │ │ ├── use-field.md │ │ │ │ ├── use-form-effects.md │ │ │ │ ├── use-form.md │ │ │ │ └── use-parent-form.md │ │ │ └── shared │ │ │ ├── connect.md │ │ │ ├── injections.md │ │ │ ├── map-props.md │ │ │ ├── map-read-pretty.md │ │ │ ├── observer.md │ │ │ └── schema.md │ │ ├── demos │ │ │ ├── api │ │ │ │ ├── components │ │ │ │ │ ├── array-field.vue │ │ │ │ │ ├── expression-scope.vue │ │ │ │ │ ├── field.vue │ │ │ │ │ ├── form-consumer.vue │ │ │ │ │ ├── form-provider.vue │ │ │ │ │ ├── object-field.vue │ │ │ │ │ ├── recursion-field-with-component.vue │ │ │ │ │ ├── recursion-field.vue │ │ │ │ │ ├── schema-field-with-schema.vue │ │ │ │ │ ├── schema-field.vue │ │ │ │ │ └── void-field.vue │ │ │ │ ├── hooks │ │ │ │ │ ├── use-field-schema.vue │ │ │ │ │ ├── use-field.vue │ │ │ │ │ ├── use-form-effects.vue │ │ │ │ │ ├── use-form.vue │ │ │ │ │ └── use-parent-form.vue │ │ │ │ └── shared │ │ │ │ ├── connect.vue │ │ │ │ ├── map-props.vue │ │ │ │ ├── map-read-pretty.vue │ │ │ │ └── observer.vue │ │ │ ├── index.vue │ │ │ └── questions │ │ │ ├── default-slot.vue │ │ │ ├── events.vue │ │ │ ├── named-slot.vue │ │ │ └── scoped-slot.vue │ │ ├── guide │ │ │ ├── architecture.md │ │ │ ├── concept.md │ │ │ └── README.md │ │ ├── questions │ │ │ └── README.md │ │ └── README.md │ ├── package.json │ ├── README.md │ ├── rollup.config.js │ ├── scripts │ │ ├── postinstall.js │ │ ├── switch-cli.js │ │ └── utils.js │ ├── src │ │ ├── __tests__ │ │ │ ├── expression.scope.spec.ts │ │ │ ├── field.spec.ts │ │ │ ├── form.spec.ts │ │ │ ├── schema.json.spec.ts │ │ │ ├── schema.markup.spec.ts │ │ │ ├── shared.spec.ts │ │ │ └── utils.spec.ts │ │ ├── components │ │ │ ├── ArrayField.ts │ │ │ ├── ExpressionScope.ts │ │ │ ├── Field.ts │ │ │ ├── FormConsumer.ts │ │ │ ├── FormProvider.ts │ │ │ ├── index.ts │ │ │ ├── ObjectField.ts │ │ │ ├── ReactiveField.ts │ │ │ ├── RecursionField.ts │ │ │ ├── SchemaField.ts │ │ │ └── VoidField.ts │ │ ├── global.d.ts │ │ ├── hooks │ │ │ ├── index.ts │ │ │ ├── useAttach.ts │ │ │ ├── useField.ts │ │ │ ├── useFieldSchema.ts │ │ │ ├── useForm.ts │ │ │ ├── useFormEffects.ts │ │ │ ├── useInjectionCleaner.ts │ │ │ └── useParentForm.ts │ │ ├── index.ts │ │ ├── shared │ │ │ ├── connect.ts │ │ │ ├── context.ts │ │ │ ├── createForm.ts │ │ │ ├── fragment.ts │ │ │ ├── h.ts │ │ │ └── index.ts │ │ ├── types │ │ │ └── index.ts │ │ ├── utils │ │ │ ├── formatVNodeData.ts │ │ │ ├── getFieldProps.ts │ │ │ ├── getRawComponent.ts │ │ │ └── resolveSchemaProps.ts │ │ └── vue2-components.ts │ ├── tsconfig.build.json │ ├── tsconfig.json │ └── tsconfig.types.json ├── README.md ├── README.zh-cn.md ├── scripts │ ├── build-style │ │ ├── buildAllStyles.ts │ │ ├── copy.ts │ │ ├── helper.ts │ │ └── index.ts │ └── rollup.base.js ├── tsconfig.build.json ├── tsconfig.jest.json ├── tsconfig.json └── yarn.lock ``` # Files -------------------------------------------------------------------------------- /packages/next/src/__builtins__/icons.tsx: -------------------------------------------------------------------------------- ```typescript import React from 'react' import cls from 'classnames' import { usePrefixCls } from './hooks/usePrefixCls' export type IconProps = React.HTMLAttributes<SVGSVGElement> & { ref?: React.ForwardedRef<SVGSVGElement> } export type IconType = React.ForwardRefExoticComponent<IconProps> export const Icon: IconType = React.forwardRef((props, ref) => { const prefix = usePrefixCls('formily-icon') return ( <svg {...props} ref={ref} className={cls(prefix, props.className)} style={{ ...props.style, cursor: props.onClick ? 'pointer' : '', display: 'inline-block', verticalAlign: 'middle', }} viewBox="64 64 896 896" fill="currentColor" width="1em" height="1em" focusable="false" aria-hidden="true" > {props.children} </svg> ) }) export const MenuOutlinedIcon: IconType = React.forwardRef((props, ref) => ( <Icon {...props} ref={ref}> <path d="M904 160H120c-4.4 0-8 3.6-8 8v64c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-64c0-4.4-3.6-8-8-8zm0 624H120c-4.4 0-8 3.6-8 8v64c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-64c0-4.4-3.6-8-8-8zm0-312H120c-4.4 0-8 3.6-8 8v64c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-64c0-4.4-3.6-8-8-8z"></path> </Icon> )) export const PlusOutlinedIcon: IconType = React.forwardRef((props, ref) => ( <Icon {...props} ref={ref}> <path d="M482 152h60q8 0 8 8v704q0 8-8 8h-60q-8 0-8-8V160q0-8 8-8z"></path> <path d="M176 474h672q8 0 8 8v60q0 8-8 8H176q-8 0-8-8v-60q0-8 8-8z"></path> </Icon> )) export const UpOutlinedIcon: IconType = React.forwardRef((props, ref) => ( <Icon {...props} ref={ref}> <path d="M890.5 755.3L537.9 269.2c-12.8-17.6-39-17.6-51.7 0L133.5 755.3A8 8 0 00140 768h75c5.1 0 9.9-2.5 12.9-6.6L512 369.8l284.1 391.6c3 4.1 7.8 6.6 12.9 6.6h75c6.5 0 10.3-7.4 6.5-12.7z"></path> </Icon> )) export const DownOutlinedIcon: IconType = React.forwardRef((props, ref) => ( <Icon {...props} ref={ref}> <path d="M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z"></path> </Icon> )) export const DeleteOutlinedIcon: IconType = React.forwardRef((props, ref) => ( <Icon {...props} ref={ref}> <path d="M360 184h-8c4.4 0 8-3.6 8-8v8h304v-8c0 4.4 3.6 8 8 8h-8v72h72v-80c0-35.3-28.7-64-64-64H352c-35.3 0-64 28.7-64 64v80h72v-72zm504 72H160c-17.7 0-32 14.3-32 32v32c0 4.4 3.6 8 8 8h60.4l24.7 523c1.6 34.1 29.8 61 63.9 61h454c34.2 0 62.3-26.8 63.9-61l24.7-523H888c4.4 0 8-3.6 8-8v-32c0-17.7-14.3-32-32-32zM731.3 840H292.7l-24.2-512h487l-24.2 512z"></path> </Icon> )) export const CopyOutlinedIcon: IconType = React.forwardRef((props, ref) => ( <Icon {...props} ref={ref}> <path d="M832 64H296c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h496v688c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8V96c0-17.7-14.3-32-32-32zM704 192H192c-17.7 0-32 14.3-32 32v530.7c0 8.5 3.4 16.6 9.4 22.6l173.3 173.3c2.2 2.2 4.7 4 7.4 5.5v1.9h4.2c3.5 1.3 7.2 2 11 2H704c17.7 0 32-14.3 32-32V224c0-17.7-14.3-32-32-32zM350 856.2L263.9 770H350v86.2zM664 888H414V746c0-22.1-17.9-40-40-40H232V264h432v624z"></path> </Icon> )) export const QuestionCircleOutlinedIcon: IconType = React.forwardRef( (props, ref) => ( <Icon {...props} ref={ref}> <path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"></path> <path d="M623.6 316.7C593.6 290.4 554 276 512 276s-81.6 14.5-111.6 40.7C369.2 344 352 380.7 352 420v7.6c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8V420c0-44.1 43.1-80 96-80s96 35.9 96 80c0 31.1-22 59.6-56.1 72.7-21.2 8.1-39.2 22.3-52.1 40.9-13.1 19-19.9 41.8-19.9 64.9V620c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8v-22.7a48.3 48.3 0 0130.9-44.8c59-22.7 97.1-74.7 97.1-132.5.1-39.3-17.1-76-48.3-103.3zM472 732a40 40 0 1080 0 40 40 0 10-80 0z"></path> </Icon> ) ) export const CloseCircleOutlinedIcon: IconType = React.forwardRef( (props, ref) => ( <Icon {...props} ref={ref}> <path d="M685.4 354.8c0-4.4-3.6-8-8-8l-66 .3L512 465.6l-99.3-118.4-66.1-.3c-4.4 0-8 3.5-8 8 0 1.9.7 3.7 1.9 5.2l130.1 155L340.5 670a8.32 8.32 0 00-1.9 5.2c0 4.4 3.6 8 8 8l66.1-.3L512 564.4l99.3 118.4 66 .3c4.4 0 8-3.5 8-8 0-1.9-.7-3.7-1.9-5.2L553.5 515l130.1-155c1.2-1.4 1.8-3.3 1.8-5.2z"></path> <path d="M512 65C264.6 65 64 265.6 64 513s200.6 448 448 448 448-200.6 448-448S759.4 65 512 65zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"></path> </Icon> ) ) export const CheckCircleOutlinedIcon: IconType = React.forwardRef( (props, ref) => ( <Icon {...props} ref={ref}> <path d="M699 353h-46.9c-10.2 0-19.9 4.9-25.9 13.3L469 584.3l-71.2-98.8c-6-8.3-15.6-13.3-25.9-13.3H325c-6.5 0-10.3 7.4-6.5 12.7l124.6 172.8a31.8 31.8 0 0051.7 0l210.6-292c3.9-5.3.1-12.7-6.4-12.7z"></path> <path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"></path> </Icon> ) ) export const ExclamationCircleOutlinedIcon: IconType = React.forwardRef( (props, ref) => ( <Icon {...props} ref={ref}> <path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm0 820c-205.4 0-372-166.6-372-372s166.6-372 372-372 372 166.6 372 372-166.6 372-372 372z"></path> <path d="M464 688a48 48 0 1096 0 48 48 0 10-96 0zm24-112h48c4.4 0 8-3.6 8-8V296c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v272c0 4.4 3.6 8 8 8z"></path> </Icon> ) ) export const EditOutlinedIcon: IconType = React.forwardRef((props, ref) => ( <Icon {...props} ref={ref}> <path d="M257.7 752c2 0 4-.2 6-.5L431.9 722c2-.4 3.9-1.3 5.3-2.8l423.9-423.9a9.96 9.96 0 000-14.1L694.9 114.9c-1.9-1.9-4.4-2.9-7.1-2.9s-5.2 1-7.1 2.9L256.8 538.8c-1.5 1.5-2.4 3.3-2.8 5.3l-29.5 168.2a33.5 33.5 0 009.4 29.8c6.6 6.4 14.9 9.9 23.8 9.9zm67.4-174.4L687.8 215l73.3 73.3-362.7 362.6-88.9 15.7 15.6-89zM880 836H144c-17.7 0-32 14.3-32 32v36c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-36c0-17.7-14.3-32-32-32z"></path>{' '} </Icon> )) export const CloseOutlinedIcon: IconType = React.forwardRef((props, ref) => ( <Icon {...props} ref={ref}> <path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path> </Icon> )) export const MessageOutlinedIcon: IconType = React.forwardRef((props, ref) => ( <Icon {...props} ref={ref}> <path d="M464 512a48 48 0 1096 0 48 48 0 10-96 0zm200 0a48 48 0 1096 0 48 48 0 10-96 0zm-400 0a48 48 0 1096 0 48 48 0 10-96 0zm661.2-173.6c-22.6-53.7-55-101.9-96.3-143.3a444.35 444.35 0 00-143.3-96.3C630.6 75.7 572.2 64 512 64h-2c-60.6.3-119.3 12.3-174.5 35.9a445.35 445.35 0 00-142 96.5c-40.9 41.3-73 89.3-95.2 142.8-23 55.4-34.6 114.3-34.3 174.9A449.4 449.4 0 00112 714v152a46 46 0 0046 46h152.1A449.4 449.4 0 00510 960h2.1c59.9 0 118-11.6 172.7-34.3a444.48 444.48 0 00142.8-95.2c41.3-40.9 73.8-88.7 96.5-142 23.6-55.2 35.6-113.9 35.9-174.5.3-60.9-11.5-120-34.8-175.6zm-151.1 438C704 845.8 611 884 512 884h-1.7c-60.3-.3-120.2-15.3-173.1-43.5l-8.4-4.5H188V695.2l-4.5-8.4C155.3 633.9 140.3 574 140 513.7c-.4-99.7 37.7-193.3 107.6-263.8 69.8-70.5 163.1-109.5 262.8-109.9h1.7c50 0 98.5 9.7 144.2 28.9 44.6 18.7 84.6 45.6 119 80 34.3 34.3 61.3 74.4 80 119 19.4 46.2 29.1 95.2 28.9 145.8-.6 99.6-39.7 192.9-110.1 262.7z"></path> </Icon> )) ``` -------------------------------------------------------------------------------- /packages/antd/docs/components/FormLayout.zh-CN.md: -------------------------------------------------------------------------------- ```markdown # FormLayout > 区块级布局批量控制组件,借助该组件,我们可以轻松的控制被 FormLayout 圈住的所有 FormItem 组件的布局模式 ## Markup Schema 案例 ```tsx import React from 'react' import { Input, Select, FormItem, FormLayout } from '@formily/antd' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { Input, Select, FormItem, FormLayout, }, }) const form = createForm() export default () => ( <FormProvider form={form}> <SchemaField> <SchemaField.Void x-component="FormLayout" x-component-props={{ labelCol: 6, wrapperCol: 10, }} > <SchemaField.String name="input" title="输入框" x-decorator="FormItem" x-decorator-props={{ tooltip: <div>123</div>, }} x-component="Input" required /> <SchemaField.String name="select" title="选择框" x-decorator="FormItem" x-component="Select" required /> </SchemaField.Void> </SchemaField> </FormProvider> ) ``` ## JSON Schema 案例 ```tsx import React from 'react' import { Input, Select, FormItem, FormLayout } from '@formily/antd' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { Input, Select, FormItem, FormLayout, }, }) const schema = { type: 'object', properties: { layout: { type: 'void', 'x-component': 'FormLayout', 'x-component-props': { labelCol: 6, wrapperCol: 10, layout: 'vertical', }, properties: { input: { type: 'string', title: '输入框', required: true, 'x-decorator': 'FormItem', 'x-decorator-props': { tooltip: <div>123</div>, }, 'x-component': 'Input', }, select: { type: 'string', title: '选择框', required: true, 'x-decorator': 'FormItem', 'x-component': 'Select', }, }, }, }, } const form = createForm() export default () => ( <FormProvider form={form}> <SchemaField schema={schema} /> </FormProvider> ) ``` ## 纯 JSX 案例 ```tsx import React from 'react' import { Input, Select, FormItem, FormButtonGroup, Submit, FormLayout, } from '@formily/antd' import { createForm } from '@formily/core' import { FormProvider, Field } from '@formily/react' const form = createForm() export default () => ( <FormProvider form={form}> <FormLayout breakpoints={[680]} layout={['vertical', 'horizontal']} labelAlign={['left', 'right']} labelCol={[24, 6]} wrapperCol={[24, 10]} > <Field name="input" required title="输入框" decorator={[FormItem]} component={[Input]} /> <Field name="select" required title="选择框" decorator={[FormItem]} component={[Select]} /> <FormButtonGroup.FormItem> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup.FormItem> </FormLayout> </FormProvider> ) ``` ## API | 属性名 | 类型 | 描述 | 默认值 | | -------------- | -------------------------------------------------------------------------------------- | ---------------------------------------- | ---------- | | style | CSSProperties | 样式 | - | | className | string | 类名 | - | | colon | boolean | 是否有冒号 | true | | requiredMark | boolean \| `"optional"` | 必选样式,可以切换为必选或者可选展示样式 | true | | labelAlign | `'right' \| 'left' \| ('right' \| 'left')[]` | 标签内容对齐 | - | | wrapperAlign | `'right' \| 'left' \| ('right' \| 'left')[]` | 组件容器内容对齐 | - | | labelWrap | boolean | 标签内容换行 | false | | labelWidth | number | 标签宽度(px) | - | | wrapperWidth | number | 组件容器宽度(px) | - | | wrapperWrap | boolean | 组件容器换行 | false | | labelCol | `number \| number[]` | 标签宽度(24 column) | - | | wrapperCol | `number \| number[]` | 组件容器宽度(24 column) | - | | fullness | boolean | 组件容器宽度 100% | false | | size | `'small' \| 'default' \| 'large'` | 组件尺寸 | default | | layout | `'vertical' \| 'horizontal' \| 'inline' \| ('vertical' \| 'horizontal' \| 'inline')[]` | 布局模式 | horizontal | | direction | `'rtl' \| 'ltr'` | 方向(暂不支持) | ltr | | inset | boolean | 内联布局 | false | | shallow | boolean | 上下文浅层传递 | true | | feedbackLayout | `'loose' \| 'terse' \| 'popover' \| 'none'` | 反馈布局 | true | | tooltipLayout | `"icon" \| "text"` | 问号提示布局 | `"icon"` | | tooltipIcon | ReactNode | 问号提示图标 | - | | bordered | boolean | 是否有边框 | true | | breakpoints | number[] | 容器尺寸断点 | - | | gridColumnGap | number | 网格布局列间距 | 8 | | gridRowGap | number | 网格布局行间距 | 4 | | spaceGap | number | 弹性间距 | 8 | ``` -------------------------------------------------------------------------------- /packages/core/src/models/BaseField.ts: -------------------------------------------------------------------------------- ```typescript import { FormPath, FormPathPattern, isValid, toArr, each, isFn, } from '@formily/shared' import { JSXComponent, LifeCycleTypes, FieldDisplayTypes, FieldPatternTypes, FieldDecorator, FieldComponent, IFieldActions, } from '../types' import { locateNode, destroy, initFieldUpdate, getArrayParent, getObjectParent, } from '../shared/internals' import { Form } from './Form' import { Query } from './Query' export class BaseField<Decorator = any, Component = any, TextType = any> { title: TextType description: TextType selfDisplay: FieldDisplayTypes selfPattern: FieldPatternTypes initialized: boolean mounted: boolean unmounted: boolean content: any data: any decoratorType: Decorator decoratorProps: Record<string, any> componentType: Component componentProps: Record<string, any> designable: boolean address: FormPath path: FormPath form: Form disposers: (() => void)[] = [] actions: IFieldActions = {} locate(address: FormPathPattern) { this.form.fields[address.toString()] = this as any locateNode(this as any, address) } get indexes(): number[] { return this.path.transform(/^\d+$/, (...args) => args.map((index) => Number(index)) ) as number[] } get index() { return this.indexes[this.indexes.length - 1] ?? -1 } get records() { const array = getArrayParent(this) return array?.value } get record() { const obj = getObjectParent(this) if (obj) { return obj.value } const index = this.index const array = getArrayParent(this, index) if (array) { return array.value?.[index] } return this.form.values } get component() { return [this.componentType, this.componentProps] } set component(value: FieldComponent<Component>) { const component = toArr(value) this.componentType = component[0] this.componentProps = component[1] || {} } get decorator() { return [this.decoratorType, this.decoratorProps] } set decorator(value: FieldDecorator<Decorator>) { const decorator = toArr(value) this.decoratorType = decorator[0] this.decoratorProps = decorator[1] || {} } get parent() { let parent = this.address.parent() let identifier = parent.toString() while (!this.form.fields[identifier]) { parent = parent.parent() identifier = parent.toString() if (!identifier) return } return this.form.fields[identifier] } get display(): FieldDisplayTypes { const parentDisplay = (this.parent as any)?.display if (parentDisplay && parentDisplay !== 'visible') { if (this.selfDisplay && this.selfDisplay !== 'visible') return this.selfDisplay return parentDisplay } if (isValid(this.selfDisplay)) return this.selfDisplay return parentDisplay || this.form.display || 'visible' } get pattern(): FieldPatternTypes { const parentPattern: FieldPatternTypes = (this.parent as any)?.pattern || this.form.pattern || 'editable' const selfPattern = this.selfPattern if (isValid(selfPattern)) { if (parentPattern === 'readPretty' && selfPattern !== 'editable') { return parentPattern } return selfPattern } return parentPattern } get editable() { return this.pattern === 'editable' } get disabled() { return this.pattern === 'disabled' } get readOnly() { return this.pattern === 'readOnly' } get readPretty() { return this.pattern === 'readPretty' } get hidden() { return this.display === 'hidden' } get visible() { return this.display === 'visible' } get destroyed() { return !this.form.fields[this.address.toString()] } set hidden(hidden: boolean) { if (!isValid(hidden)) return if (hidden) { this.display = 'hidden' } else { this.display = 'visible' } } set visible(visible: boolean) { if (!isValid(visible)) return if (visible) { this.display = 'visible' } else { this.display = 'none' } } set editable(editable: boolean) { if (!isValid(editable)) return if (editable) { this.pattern = 'editable' } else { this.pattern = 'readPretty' } } set readOnly(readOnly: boolean) { if (!isValid(readOnly)) return if (readOnly) { this.pattern = 'readOnly' } else { this.pattern = 'editable' } } set disabled(disabled: boolean) { if (!isValid(disabled)) return if (disabled) { this.pattern = 'disabled' } else { this.pattern = 'editable' } } set readPretty(readPretty: boolean) { if (!isValid(readPretty)) return if (readPretty) { this.pattern = 'readPretty' } else { this.pattern = 'editable' } } set pattern(pattern: FieldPatternTypes) { this.selfPattern = pattern } set display(display: FieldDisplayTypes) { this.selfDisplay = display } setTitle = (title?: TextType) => { this.title = title } setDescription = (description?: TextType) => { this.description = description } setDisplay = (type?: FieldDisplayTypes) => { this.display = type } setPattern = (type?: FieldPatternTypes) => { this.pattern = type } setComponent = <C extends JSXComponent, ComponentProps extends object = {}>( component?: C, props?: ComponentProps ) => { if (component) { this.componentType = component as any } if (props) { this.componentProps = this.componentProps || {} Object.assign(this.componentProps, props) } } setComponentProps = <ComponentProps extends object = {}>( props?: ComponentProps ) => { if (props) { this.componentProps = this.componentProps || {} Object.assign(this.componentProps, props) } } setDecorator = <D extends JSXComponent, ComponentProps extends object = {}>( component?: D, props?: ComponentProps ) => { if (component) { this.decoratorType = component as any } if (props) { this.decoratorProps = this.decoratorProps || {} Object.assign(this.decoratorProps, props) } } setDecoratorProps = <ComponentProps extends object = {}>( props?: ComponentProps ) => { if (props) { this.decoratorProps = this.decoratorProps || {} Object.assign(this.decoratorProps, props) } } setData = (data: any) => { this.data = data } setContent = (content: any) => { this.content = content } onInit = () => { this.initialized = true initFieldUpdate(this as any) this.notify(LifeCycleTypes.ON_FIELD_INIT) } onMount = () => { this.mounted = true this.unmounted = false this.notify(LifeCycleTypes.ON_FIELD_MOUNT) } onUnmount = () => { this.mounted = false this.unmounted = true this.notify(LifeCycleTypes.ON_FIELD_UNMOUNT) } query = (pattern: FormPathPattern | RegExp) => { return new Query({ pattern, base: this.address, form: this.form, }) } notify = (type: LifeCycleTypes, payload?: any) => { return this.form.notify(type, payload ?? this) } dispose = () => { this.disposers.forEach((dispose) => { dispose() }) this.form.removeEffects(this) } destroy = (forceClear = true) => { destroy(this.form.fields, this.address.toString(), forceClear) } match = (pattern: FormPathPattern) => { return FormPath.parse(pattern).matchAliasGroup(this.address, this.path) } inject = (actions: IFieldActions) => { each(actions, (action, key) => { if (isFn(action)) { this.actions[key] = action } }) } invoke = (name: string, ...args: any[]) => { return this.actions[name]?.(...args) } } ``` -------------------------------------------------------------------------------- /packages/next/docs/components/FormLayout.md: -------------------------------------------------------------------------------- ```markdown # FormLayout > 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 ## Markup Schema example ```tsx import React from 'react' import { Input, Select, FormItem, FormLayout } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { Input, Select, FormItem, FormLayout, }, }) const form = createForm() export default () => ( <FormProvider form={form}> <SchemaField> <SchemaField.Void x-component="FormLayout" x-component-props={{ labelCol: 6, wrapperCol: 10, }} > <SchemaField.String name="input" title="input box" x-decorator="FormItem" x-decorator-props={{ tooltip: <div>123</div>, }} x-component="Input" required /> <SchemaField.String name="select" title="select box" x-decorator="FormItem" x-component="Select" required /> </SchemaField.Void> </SchemaField> </FormProvider> ) ``` ## JSON Schema case ```tsx import React from 'react' import { Input, Select, FormItem, FormLayout } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { Input, Select, FormItem, FormLayout, }, }) const schema = { type: 'object', properties: { layout: { type: 'void', 'x-component': 'FormLayout', 'x-component-props': { labelCol: 6, wrapperCol: 10, layout: 'vertical', }, properties: { input: { type: 'string', title: 'input box', required: true, 'x-decorator': 'FormItem', 'x-decorator-props': { tooltip: <div>123</div>, }, 'x-component': 'Input', }, select: { type: 'string', title: 'Select box', required: true, 'x-decorator': 'FormItem', 'x-component': 'Select', }, }, }, }, } const form = createForm() export default () => ( <FormProvider form={form}> <SchemaField schema={schema} /> </FormProvider> ) ``` ## Pure JSX case ```tsx import React from 'react' import { Input, Select, FormItem, FormButtonGroup, Submit, FormLayout, } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, Field } from '@formily/react' const form = createForm() export default () => ( <FormProvider form={form}> <FormLayout labelCol={6} wrapperCol={10}> <Field name="input" required title="input box" decorator={[FormItem]} component={[Input]} /> <Field name="select" required title="select box" decorator={[FormItem]} component={[Select]} /> <FormButtonGroup.FormItem> <Submit onSubmit={console.log}>Submit</Submit> </FormButtonGroup.FormItem> </FormLayout> </FormProvider> ) ``` ## API | Property name | Type | Description | Default value | | -------------- | -------------------------------------------------------------------------------------- | ------------------------------------- | ------------- | | style | CSSProperties | Style | - | | className | string | class name | - | | colon | boolean | Is there a colon | true | | labelAlign | `'right' \| 'left' \| ('right' \| 'left')[]` | Label content alignment | - | | wrapperAlign | `'right' \| 'left' \| ('right' \| 'left')[]` | Component container content alignment | - | | labelWrap | boolean | Wrap label content | false | | labelWidth | number | Label width (px) | - | | wrapperWidth | number | Component container width (px) | - | | wrapperWrap | boolean | Component container wrap | false | | labelCol | `number \| number[]` | Label width (24 column) | - | | wrapperCol | `number \| number[]` | Component container width (24 column) | - | | fullness | boolean | Component container width 100% | false | | size | `'small' \|'default' \|'large'` | component size | default | | layout | `'vertical' \| 'horizontal' \| 'inline' \| ('vertical' \| 'horizontal' \| 'inline')[]` | layout mode | horizontal | | direction | `'rtl' \|'ltr'` | direction (not supported yet) | ltr | | inset | boolean | Inline layout | false | | shallow | boolean | shallow context transfer | true | | feedbackLayout | `'loose' \|'terse' \|'popover' \|'none'` | feedback layout | true | | tooltipLayout | `"icon" \| "text"` | Ask the prompt layout | `"icon"` | | tooltipIcon | ReactNode | Ask the prompt icon | - | | bordered | boolean | Is there a border | true | | breakpoints | number[] | Container size breakpoints | - | | gridColumnGap | number | Grid Column Gap | 8 | | gridRowGap | number | Grid Row Gap | 4 | | spaceGap | number | Space Gap | 8 | ``` -------------------------------------------------------------------------------- /packages/antd/docs/components/FormButtonGroup.md: -------------------------------------------------------------------------------- ```markdown # FormButtonGroup > Form button group layout component ## Common case ```tsx import React from 'react' import { FormButtonGroup, Submit, Reset, FormItem, Input, FormLayout, } from '@formily/antd' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { FormItem, Input, }, }) const form = createForm() export default () => { return ( <FormProvider form={form}> <FormLayout labelCol={6} wrapperCol={10}> <SchemaField> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> </SchemaField> <FormButtonGroup.FormItem> <Submit onSubmit={console.log}>Submit</Submit> <Reset>Reset</Reset> </FormButtonGroup.FormItem> </FormLayout> </FormProvider> ) } ``` ## Suction bottom case ```tsx import React from 'react' import { FormButtonGroup, Submit, Reset, FormItem, FormLayout, Input, } from '@formily/antd' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { FormItem, Input, }, }) const form = createForm() export default () => { return ( <FormProvider form={form}> <FormLayout labelCol={6} wrapperCol={10}> <SchemaField> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> </SchemaField> <FormButtonGroup.Sticky> <FormButtonGroup.FormItem> <Submit onSubmit={console.log}>Submit</Submit> <Reset>Reset</Reset> </FormButtonGroup.FormItem> </FormButtonGroup.Sticky> </FormLayout> </FormProvider> ) } ``` ## Suction bottom centering case ```tsx import React from 'react' import { FormButtonGroup, Submit, Reset, FormItem, FormLayout, Input, } from '@formily/antd' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { FormItem, Input, }, }) const form = createForm() export default () => { return ( <FormProvider form={form}> <FormLayout labelCol={6} wrapperCol={10}> <SchemaField> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> </SchemaField> <FormButtonGroup.Sticky align="center"> <FormButtonGroup> <Submit onSubmit={console.log}>Submit</Submit> <Reset>Reset</Reset> </FormButtonGroup> </FormButtonGroup.Sticky> </FormLayout> </FormProvider> ) } ``` ## API ### FormButtonGroup > This component is mainly used to handle the button group gap | Property name | Type | Description | Default value | | ------------- | --------------------------- | ----------- | ------------- | | gutter | number | Gap size | 8px | | align | `'left'\|'center'\|'right'` | Alignment | `'left'` | ### FormButtonGroup.FormItem > This component is mainly used to deal with the alignment of the button group and the main form FormItem Refer to [FormItem](/components/form-item) property ### FormButtonGroup.Sticky > This component is mainly used to deal with the floating positioning problem of the button group | Property name | Type | Description | Default value | | ------------- | --------------------------- | ----------- | ------------- | | align | `'left'\|'center'\|'right'` | Alignment | `'left'` | ``` -------------------------------------------------------------------------------- /packages/next/docs/components/Upload.md: -------------------------------------------------------------------------------- ```markdown # Upload > Upload components > > Note: Using the upload component, it is recommended that users perform secondary packaging. Users do not need to care about the data communication between the upload component and Formily, only the style and basic upload configuration are required. ## Markup Schema example ```tsx import React from 'react' import { Upload, FormItem, FormButtonGroup, Submit, FormLayout, } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' import { Button } from '@alifd/next' import { UploadOutlined, InboxOutlined } from '@ant-design/icons' const NormalUpload = (props) => { return ( <Upload {...props} withCredentials={false} action="https://www.mocky.io/v2/5cc8019d300000980a055e76" headers={{ authorization: 'authorization-text', }} > <Button> <UploadOutlined /> upload files </Button> </Upload> ) } const CardUpload = (props) => { return ( <Upload.Card {...props} action="https://www.mocky.io/v2/5cc8019d300000980a055e76" withCredentials={false} headers={{ authorization: 'authorization-text', }} /> ) } const DraggerUpload = (props) => { return ( <Upload.Dragger {...props} withCredentials={false} action="https://www.mocky.io/v2/5cc8019d300000980a055e76" > <div className="next-upload-drag"> <p className="next-upload-drag-icon" style={{ fontSize: 50 }}> <InboxOutlined /> </p> <p className="next-upload-drag-text"> click to <Button text>download template</Button> or drag file here </p> <p className="next-upload-drag-hint">supports docx, xls, PDF </p> </div> </Upload.Dragger> ) } const SchemaField = createSchemaField({ components: { NormalUpload, CardUpload, DraggerUpload, FormItem, }, }) const form = createForm() export default () => ( <FormProvider form={form}> <FormLayout labelCol={6} wrapperCol={14}> <SchemaField> <SchemaField.Array name="upload" title="Upload" x-decorator="FormItem" x-component="NormalUpload" required /> <SchemaField.Array name="upload2" title="Card upload" x-decorator="FormItem" x-component="CardUpload" required /> <SchemaField.Array name="upload3" title="Drag and drop upload" x-decorator="FormItem" x-component="DraggerUpload" required /> </SchemaField> <FormButtonGroup.FormItem> <Submit onSubmit={console.log}>Submit</Submit> </FormButtonGroup.FormItem> </FormLayout> </FormProvider> ) ``` ## JSON Schema case ```tsx import React from 'react' import { Upload, FormItem, FormButtonGroup, Submit, FormLayout, } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' import { Button } from '@alifd/next' import { UploadOutlined, InboxOutlined } from '@ant-design/icons' const NormalUpload = (props) => { return ( <Upload {...props} withCredentials={false} action="https://www.mocky.io/v2/5cc8019d300000980a055e76" headers={{ authorization: 'authorization-text', }} > <Button> <UploadOutlined /> upload files </Button> </Upload> ) } const CardUpload = (props) => { return ( <Upload.Card {...props} withCredentials={false} action="https://www.mocky.io/v2/5cc8019d300000980a055e76" headers={{ authorization: 'authorization-text', }} /> ) } const DraggerUpload = (props) => { return ( <Upload.Dragger {...props} withCredentials={false} action="https://www.mocky.io/v2/5cc8019d300000980a055e76" > <div className="next-upload-drag"> <p className="next-upload-drag-icon" style={{ fontSize: 50 }}> <InboxOutlined /> </p> <p className="next-upload-drag-text"> click to <Button text>download template</Button> or drag file here </p> <p className="next-upload-drag-hint">supports docx, xls, PDF </p> </div> </Upload.Dragger> ) } const SchemaField = createSchemaField({ components: { NormalUpload, CardUpload, DraggerUpload, FormItem, }, }) const form = createForm() const schema = { type: 'object', properties: { upload: { type: 'array', title: 'Upload', required: true, 'x-decorator': 'FormItem', 'x-component': 'NormalUpload', }, upload2: { type: 'array', title: 'Card upload', required: true, 'x-decorator': 'FormItem', 'x-component': 'CardUpload', }, upload3: { type: 'array', title: 'Drag and drop upload', required: true, 'x-decorator': 'FormItem', 'x-component': 'DraggerUpload', }, }, } export default () => ( <FormProvider form={form}> <FormLayout labelCol={6} wrapperCol={14}> <SchemaField schema={schema} /> <FormButtonGroup.FormItem> <Submit onSubmit={console.log}>Submit</Submit> </FormButtonGroup.FormItem> </FormLayout> </FormProvider> ) ``` ## Pure JSX case ```tsx import React from 'react' import { Upload, FormItem, FormButtonGroup, Submit, FormLayout, } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, Field } from '@formily/react' import { Button } from '@alifd/next' import { UploadOutlined, InboxOutlined } from '@ant-design/icons' const NormalUpload = (props) => { return ( <Upload {...props} withCredentials={false} action="https://www.mocky.io/v2/5cc8019d300000980a055e76" headers={{ authorization: 'authorization-text', }} > <Button> <UploadOutlined /> upload files </Button> </Upload> ) } const CardUpload = (props) => { return ( <Upload.Card {...props} withCredentials={false} action="https://www.mocky.io/v2/5cc8019d300000980a055e76" headers={{ authorization: 'authorization-text', }} /> ) } const DraggerUpload = (props) => { return ( <Upload.Dragger {...props} withCredentials={false} action="https://www.mocky.io/v2/5cc8019d300000980a055e76" > <div className="next-upload-drag"> <p className="next-upload-drag-icon" style={{ fontSize: 50 }}> <InboxOutlined /> </p> <p className="next-upload-drag-text"> click to <Button text>download template</Button> or drag file here </p> <p className="next-upload-drag-hint">supports docx, xls, PDF </p> </div> </Upload.Dragger> ) } const form = createForm() export default () => ( <FormProvider form={form}> <FormLayout labelCol={6} wrapperCol={14}> <Field name="upload" title="Upload" required decorator={[FormItem]} component={[NormalUpload]} /> <Field name="upload2" title="Card upload" required decorator={[FormItem]} component={[CardUpload]} /> <Field name="upload3" title="Drag and drop upload" required decorator={[FormItem]} component={[DraggerUpload]} /> <FormButtonGroup.FormItem> <Submit onSubmit={console.log}>Submit</Submit> </FormButtonGroup.FormItem> </FormLayout> </FormProvider> ) ``` ## API Reference https://fusion.design/pc/component/basic/upload ``` -------------------------------------------------------------------------------- /packages/next/docs/components/FormButtonGroup.md: -------------------------------------------------------------------------------- ```markdown # FormButtonGroup > Form button group layout component ## Common case ```tsx import React from 'react' import { FormButtonGroup, Submit, Reset, FormItem, Input, FormLayout, } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { FormItem, Input, }, }) const form = createForm() export default () => { return ( <FormProvider form={form}> <FormLayout labelCol={6} wrapperCol={10}> <SchemaField> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> </SchemaField> <FormButtonGroup.FormItem> <Submit onSubmit={console.log}>Submit</Submit> <Reset>Reset</Reset> </FormButtonGroup.FormItem> </FormLayout> </FormProvider> ) } ``` ## Suction bottom case ```tsx import React from 'react' import { FormButtonGroup, Submit, Reset, FormItem, FormLayout, Input, } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { FormItem, Input, }, }) const form = createForm() export default () => { return ( <FormProvider form={form}> <FormLayout labelCol={6} wrapperCol={10}> <SchemaField> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> </SchemaField> <FormButtonGroup.Sticky> <FormButtonGroup.FormItem> <Submit onSubmit={console.log}>Submit</Submit> <Reset>Reset</Reset> </FormButtonGroup.FormItem> </FormButtonGroup.Sticky> </FormLayout> </FormProvider> ) } ``` ## Suction bottom centering case ```tsx import React from 'react' import { FormButtonGroup, Submit, Reset, FormItem, FormLayout, Input, } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { FormItem, Input, }, }) const form = createForm() export default () => { return ( <FormProvider form={form}> <FormLayout labelCol={6} wrapperCol={10}> <SchemaField> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> <SchemaField.String title="input box" x-decorator="FormItem" required x-component="Input" /> </SchemaField> <FormButtonGroup.Sticky align="center"> <FormButtonGroup> <Submit onSubmit={console.log}>Submit</Submit> <Reset>Reset</Reset> </FormButtonGroup> </FormButtonGroup.Sticky> </FormLayout> </FormProvider> ) } ``` ## API ### FormButtonGroup > This component is mainly used to handle the button group gap | Property name | Type | Description | Default value | | ------------- | --------------------------- | ----------- | ------------- | | gutter | number | Gap size | 8px | | align | `'left'\|'center'\|'right'` | Alignment | `'left'` | ### FormButtonGroup.FormItem > This component is mainly used to deal with the alignment of the button group and the main form FormItem Refer to [FormItem](/components/form-item) property ### FormButtonGroup.Sticky > This component is mainly used to deal with the floating positioning problem of the button group | Property name | Type | Description | Default value | | ------------- | --------------------------- | ----------- | ------------- | | align | `'left'\|'center'\|'right'` | Alignment | `'left'` | ``` -------------------------------------------------------------------------------- /packages/antd/docs/components/DatePicker.md: -------------------------------------------------------------------------------- ```markdown # DatePicker > Date Picker ## Markup Schema example ```tsx import React from 'react' import { DatePicker, FormItem, FormButtonGroup, Submit } from '@formily/antd' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { DatePicker, FormItem, }, }) const form = createForm() export default () => ( <FormProvider form={form}> <SchemaField> <SchemaField.String name="date" required title="normal date" x-decorator="FormItem" x-component="DatePicker" /> <SchemaField.String name="week" title="Week Selection" x-decorator="FormItem" x-component="DatePicker" x-component-props={{ picker: 'week', }} /> <SchemaField.String name="month" title="Month Selection" x-decorator="FormItem" x-component="DatePicker" x-component-props={{ picker: 'month', }} /> <SchemaField.String name="quarter" title="Financial Year Selection" x-decorator="FormItem" x-component="DatePicker" x-component-props={{ picker: 'quarter', }} /> <SchemaField.String name="year" title="Year selection" x-decorator="FormItem" x-component="DatePicker" x-component-props={{ picker: 'year', }} /> <SchemaField.String name="[startDate,endDate]" title="Date Range" x-decorator="FormItem" x-component="DatePicker.RangePicker" x-component-props={{ showTime: true, }} /> <SchemaField.String name="range_week" title="Week range selection" x-decorator="FormItem" x-component="DatePicker.RangePicker" x-component-props={{ picker: 'week', }} /> <SchemaField.String name="range_month" title="Month Range Selection" x-decorator="FormItem" x-component="DatePicker.RangePicker" x-component-props={{ picker: 'month', }} /> <SchemaField.String name="range_quarter" title="Financial Year Range Selection" x-decorator="FormItem" x-component="DatePicker.RangePicker" x-component-props={{ picker: 'quarter', }} /> <SchemaField.String name="range_year" title="Year range selection" x-decorator="FormItem" x-component="DatePicker.RangePicker" x-component-props={{ picker: 'year', }} /> </SchemaField> <FormButtonGroup> <Submit onSubmit={console.log}>Submit</Submit> </FormButtonGroup> </FormProvider> ) ``` ## JSON Schema case ```tsx import React from 'react' import { DatePicker, FormItem, FormButtonGroup, Submit } from '@formily/antd' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { DatePicker, FormItem, }, }) const form = createForm() const schema = { type: 'object', properties: { date: { title: 'Normal date', 'x-decorator': 'FormItem', 'x-component': 'DatePicker', type: 'string', }, week: { title: 'Week Selection', 'x-decorator': 'FormItem', 'x-component': 'DatePicker', 'x-component-props': { picker: 'week', }, type: 'string', }, month: { title: 'Month Selection', 'x-decorator': 'FormItem', 'x-component': 'DatePicker', 'x-component-props': { picker: 'month', }, type: 'string', }, quarter: { title: 'Fiscal Year Selection', 'x-decorator': 'FormItem', 'x-component': 'DatePicker', 'x-component-props': { picker: 'quarter', }, type: 'string', }, year: { title: 'Year selection', 'x-decorator': 'FormItem', 'x-component': 'DatePicker', 'x-component-props': { picker: 'year', }, type: 'string', }, '[startDate,endDate]': { title: 'Date range', 'x-decorator': 'FormItem', 'x-component': 'DatePicker.RangePicker', 'x-component-props': { showTime: true, }, type: 'string', }, range_week: { title: 'Week range selection', 'x-decorator': 'FormItem', 'x-component': 'DatePicker.RangePicker', 'x-component-props': { picker: 'week', }, type: 'string', }, range_month: { title: 'Month Range Selection', 'x-decorator': 'FormItem', 'x-component': 'DatePicker.RangePicker', 'x-component-props': { picker: 'month', }, type: 'string', }, range_quarter: { title: 'Financial year range selection', 'x-decorator': 'FormItem', 'x-component': 'DatePicker.RangePicker', 'x-component-props': { picker: 'quarter', }, type: 'string', }, range_year: { name: 'range_year', title: 'Year range selection', 'x-decorator': 'FormItem', 'x-component': 'DatePicker.RangePicker', 'x-component-props': { picker: 'year', }, type: 'string', }, }, } export default () => ( <FormProvider form={form}> <SchemaField schema={schema} /> <FormButtonGroup> <Submit onSubmit={console.log}>Submit</Submit> </FormButtonGroup> </FormProvider> ) ``` ## Pure JSX case ```tsx import React from 'react' import { DatePicker, FormItem, FormButtonGroup, Submit } from '@formily/antd' import { createForm } from '@formily/core' import { FormProvider, Field } from '@formily/react' const form = createForm() export default () => ( <FormProvider form={form}> <Field name="date" title="date selection" decorator={[FormItem]} component={[DatePicker]} /> <Field name="week" title="Week Selection" decorator={[FormItem]} component={[ DatePicker, { picker: 'week', }, ]} /> <Field name="quarter" title="Financial Year Selection" decorator={[FormItem]} component={[ DatePicker, { picker: 'month', }, ]} /> <Field name="year" title="Year selection" decorator={[FormItem]} component={[ DatePicker, { picker: 'year', }, ]} /> <Field name="[startDate,endDate]" title="Date range selection" decorator={[FormItem]} component={[DatePicker.RangePicker]} /> <Field name="range_week" title="Week range selection" decorator={[FormItem]} component={[ DatePicker.RangePicker, { picker: 'week', }, ]} /> <Field name="range_month" title="Month Range Selection" decorator={[FormItem]} component={[ DatePicker.RangePicker, { picker: 'month', }, ]} /> <Field name="range_quarter" title="Financial Year Range Selection" decorator={[FormItem]} component={[ DatePicker.RangePicker, { picker: 'quarter', }, ]} /> <Field name="range_year" title="Year range selection" decorator={[FormItem]} component={[ DatePicker.RangePicker, { picker: 'year', }, ]} /> <FormButtonGroup> <Submit onSubmit={console.log}>Submit</Submit> </FormButtonGroup> </FormProvider> ) ``` ## API Reference https://ant.design/components/date-picker-cn/ ``` -------------------------------------------------------------------------------- /packages/vue/src/components/ReactiveField.ts: -------------------------------------------------------------------------------- ```typescript import { inject, provide, Ref, ref, shallowRef, watch, isVue2 } from 'vue-demi' import { GeneralField, isVoidField } from '@formily/core' import { each, FormPath } from '@formily/shared' import { observer } from '@formily/reactive-vue' import { toJS, reaction } from '@formily/reactive' import { SchemaOptionsSymbol, FieldSymbol, h, Fragment } from '../shared' import { useAttach } from '../hooks/useAttach' import { useField, useForm } from '../hooks' import type { IReactiveFieldProps, VueComponentProps, DefineComponent, } from '../types' import type { VNode } from 'vue' function isVueOptions(options: Record<string, unknown>) { return ( typeof options.template === 'string' || typeof options.render === 'function' || typeof options.setup === 'function' ) } const wrapFragment = (childNodes: VNode[] | VNode): VNode => { if (!Array.isArray(childNodes)) { return childNodes } if (childNodes.length > 1) { return h(Fragment, {}, { default: () => childNodes }) } return childNodes[0] } const resolveComponent = (render: () => unknown[], extra?: any) => { if (extra === undefined || extra === null) { return render } if (typeof extra === 'string') { return () => [...render(), extra] } // not component if (!isVueOptions(extra) && typeof extra !== 'function') { return render } // for scoped slot if (extra.length > 1 || extra?.render?.length > 1) { return (scopedProps: VueComponentProps<any>) => [ ...render(), h(extra, { props: scopedProps }, {}), ] } return () => [...render(), h(extra, {}, {})] } const mergeSlots = ( field: GeneralField, slots: Record<string, any>, content: any ): Record<string, (...args: any) => any[]> => { const slotNames = Object.keys(slots) if (!slotNames.length) { if (!content) { return {} } if (typeof content === 'string') { return { default: resolveComponent(() => [], content), } } } const patchSlot = (slotName: string) => (...originArgs) => slots[slotName]?.({ field, form: field.form, ...originArgs[0] }) ?? [] const patchedSlots: Record<string, (...args: any) => unknown[]> = {} slotNames.forEach((name) => { patchedSlots[name] = patchSlot(name) }) // for named slots if (content && typeof content === 'object' && !isVueOptions(content)) { Object.keys(content).forEach((key) => { const child = content[key] const slot = patchedSlots[key] ?? (() => []) patchedSlots[key] = resolveComponent(slot, child) }) return patchedSlots } // maybe default slot is empty patchedSlots['default'] = resolveComponent( patchedSlots['default'] ?? (() => []), content ) return patchedSlots } const createFieldInVue2 = (innerCreateField) => { return () => { let res: GeneralField const disposer = reaction(() => { res = innerCreateField() }) disposer() return res } } export default observer({ name: 'ReactiveField', props: { fieldType: { type: String, default: 'Field', }, fieldProps: { type: Object, default: () => ({}), }, }, setup(props: IReactiveFieldProps, { slots }) { const formRef = useForm() const parentRef = useField() const optionsRef = inject(SchemaOptionsSymbol, ref(null)) let createField = () => formRef?.value?.[`create${props.fieldType}`]?.({ ...props.fieldProps, basePath: props.fieldProps?.basePath ?? parentRef.value?.address, }) if (isVue2) { createField = createFieldInVue2(createField) } const fieldRef = shallowRef(createField()) as Ref<GeneralField> watch( () => props.fieldProps, () => (fieldRef.value = createField()) ) useAttach(fieldRef) provide(FieldSymbol, fieldRef) return () => { const field = fieldRef.value const options = optionsRef.value if (!field) { return slots.default?.() } if (field.display !== 'visible') { return h('template', {}, {}) } const mergedSlots = mergeSlots(field, slots, field.content) const renderDecorator = (childNodes: any[]) => { if (!field.decoratorType) { return wrapFragment(childNodes) } const finalComponent = FormPath.getIn(options?.components, field.decoratorType as string) ?? field.decoratorType const componentAttrs = toJS(field.decorator[1]) || {} const events: Record<string, any> = {} each(componentAttrs, (value, eventKey) => { const onEvent = eventKey.startsWith('on') const atEvent = eventKey.startsWith('@') if (!onEvent && !atEvent) return if (onEvent) { const eventName = `${eventKey[2].toLowerCase()}${eventKey.slice(3)}` // '@xxx' has higher priority events[eventName] = events[eventName] || value } else if (atEvent) { const eventName = eventKey.slice(1) events[eventName] = value delete componentAttrs[eventKey] } }) const componentData = { attrs: componentAttrs, style: componentAttrs?.style, class: componentAttrs?.class, on: events, } delete componentData.attrs.style delete componentData.attrs.class return h(finalComponent, componentData, { default: () => childNodes, }) } const renderComponent = () => { if (!field.componentType) return wrapFragment(mergedSlots?.default?.()) const component = FormPath.getIn(options?.components, field.componentType as string) ?? field.componentType const originData = toJS(field.component[1]) || {} const events = {} as Record<string, any> const originChange = originData['@change'] || originData['onChange'] const originFocus = originData['@focus'] || originData['onFocus'] const originBlur = originData['@blur'] || originData['onBlur'] each(originData, (value, eventKey) => { const onEvent = eventKey.startsWith('on') const atEvent = eventKey.startsWith('@') if (!onEvent && !atEvent) return if (onEvent) { const eventName = `${eventKey[2].toLowerCase()}${eventKey.slice(3)}` // '@xxx' has higher priority events[eventName] = events[eventName] || value } else if (atEvent) { const eventName = eventKey.slice(1) events[eventName] = value delete originData[eventKey] } }) events.change = (...args: any[]) => { if (!isVoidField(field)) field.onInput(...args) originChange?.(...args) } events.focus = (...args: any[]) => { if (!isVoidField(field)) field.onFocus(...args) originFocus?.(...args) } events.blur = (...args: any[]) => { if (!isVoidField(field)) field.onBlur(...args) originBlur?.(...args) } const componentData = { attrs: { disabled: !isVoidField(field) ? field.pattern === 'disabled' || field.pattern === 'readPretty' : undefined, readOnly: !isVoidField(field) ? field.pattern === 'readOnly' : undefined, ...originData, value: !isVoidField(field) ? field.value : undefined, }, style: originData?.style, class: originData?.class, on: events, } delete componentData.attrs.style delete componentData.attrs.class return h(component, componentData, mergedSlots) } return renderDecorator([renderComponent()]) } }, } as unknown as DefineComponent<IReactiveFieldProps>) ``` -------------------------------------------------------------------------------- /packages/path/src/tokenizer.ts: -------------------------------------------------------------------------------- ```typescript import { Token, nameTok, colonTok, dotTok, starTok, dbStarTok, bangTok, bracketLTok, bracketRTok, bracketDRTok, expandTok, parenLTok, parenRTok, commaTok, eofTok, ignoreTok, braceLTok, braceRTok, bracketDLTok, } from './tokens' import { bracketDContext, Context } from './contexts' const nonASCIIWhitespace = /[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/ const fullCharCodeAtPos = (input: string, pos: number) => { if (String.fromCharCode) return input.codePointAt(pos) const code = input.charCodeAt(pos) if (code <= 0xd7ff || code >= 0xe000) return code const next = input.charCodeAt(pos + 1) return (code << 10) + next - 0x35fdc00 } const isRewordCode = (code: number) => code === 42 || code === 46 || code === 33 || code === 91 || code === 93 || code === 40 || code === 41 || code === 44 || code === 58 || code === 126 || code === 123 || code === 125 const getError = (message?: string, props?: any) => { const err = new Error(message) Object.assign(err, props) return err } const slice = (string: string, start: number, end: number) => { let str = '' for (let i = start; i < end; i++) { const ch = string.charAt(i) if (ch !== '\\') { str += ch } } return str } export class Tokenizer { public input: string public state: { context: Context[] type: Token pos: number value?: any } public type_: Token constructor(input: string) { this.input = input this.state = { context: [], type: null, pos: 0, } this.type_ = null } curContext() { return this.state.context[this.state.context.length - 1] } includesContext(context: Context) { for (let len = this.state.context.length - 1; len >= 0; len--) { if (this.state.context[len] === context) { return true } } return false } unexpect(type?: Token) { type = type || this.state.type return getError( `Unexpect token "${type.flag}" in ${this.state.pos} char.`, { pos: this.state.pos, } ) } expectNext(type?: Token, next?: Token) { if (type && type.expectNext) { if (next && !type.expectNext.call(this, next)) { throw getError( `Unexpect token "${next.flag}" token should not be behind "${type.flag}" token.(${this.state.pos}th char)`, { pos: this.state.pos, } ) } } } expectPrev(type?: Token, prev?: Token) { if (type && type.expectPrev) { if (prev && !type.expectPrev.call(this, prev)) { throw getError( `Unexpect token "${type.flag}" should not be behind "${prev.flag}"(${this.state.pos}th char).`, { pos: this.state.pos, } ) } } } match(type?: Token) { return this.state.type === type } skipSpace() { if (this.curContext() === bracketDContext) return loop: while (this.state.pos < this.input.length) { const ch = this.input.charCodeAt(this.state.pos) switch (ch) { case 32: case 160: ++this.state.pos break case 13: if (this.input.charCodeAt(this.state.pos + 1) === 10) { ++this.state.pos } case 10: case 8232: case 8233: ++this.state.pos break default: if ( (ch > 8 && ch < 14) || (ch >= 5760 && nonASCIIWhitespace.test(String.fromCharCode(ch))) ) { ++this.state.pos } else { break loop } } } } next() { this.type_ = this.state.type if (this.input.length <= this.state.pos) { return this.finishToken(eofTok) } this.skipSpace() this.readToken( this.getCode(), this.state.pos > 0 ? this.getCode(this.state.pos - 1) : -Infinity ) } getCode(pos = this.state.pos) { return fullCharCodeAtPos(this.input, pos) } eat(type) { if (this.match(type)) { this.next() return true } else { return false } } readKeyWord() { let startPos = this.state.pos, string = '' while (true) { const code = this.getCode() const prevCode = this.getCode(this.state.pos - 1) if (this.input.length === this.state.pos) { string = slice(this.input, startPos, this.state.pos + 1) break } if (!isRewordCode(code) || prevCode === 92) { if ( code === 32 || code === 160 || code === 10 || code === 8232 || code === 8233 ) { string = slice(this.input, startPos, this.state.pos) break } if (code === 13 && this.input.charCodeAt(this.state.pos + 1) === 10) { string = slice(this.input, startPos, this.state.pos) break } if ( (code > 8 && code < 14) || (code >= 5760 && nonASCIIWhitespace.test(String.fromCharCode(code))) ) { string = slice(this.input, startPos, this.state.pos) break } this.state.pos++ } else { string = slice(this.input, startPos, this.state.pos) break } } this.finishToken(nameTok, string) } readIgnoreString() { let startPos = this.state.pos, prevCode, string = '' while (true) { const code = this.getCode() if (this.state.pos >= this.input.length) break if ((code === 91 || code === 93) && prevCode === 92) { this.state.pos++ prevCode = '' } else if (code == 93 && prevCode === 93) { string = this.input .slice(startPos, this.state.pos - 1) .replace(/\\([\[\]])/g, '$1') this.state.pos++ break } else { this.state.pos++ prevCode = code } } this.finishToken(ignoreTok, string) this.finishToken(bracketDRTok) } finishToken(type: Token, value?: any) { const preType = this.state.type this.state.type = type if (value !== undefined) this.state.value = value this.expectNext(preType, type) this.expectPrev(type, preType) if (type.updateContext) { type.updateContext.call(this, preType) } } readToken(code: number, prevCode: number) { if (prevCode === 92) { return this.readKeyWord() } if (this.input.length <= this.state.pos) { this.finishToken(eofTok) } else if (this.curContext() === bracketDContext) { this.readIgnoreString() } else if (code === 123) { this.state.pos++ this.finishToken(braceLTok) } else if (code === 125) { this.state.pos++ this.finishToken(braceRTok) } else if (code === 42) { this.state.pos++ if (this.getCode() === 42) { this.state.pos++ return this.finishToken(dbStarTok) } this.finishToken(starTok) } else if (code === 33) { this.state.pos++ this.finishToken(bangTok) } else if (code === 46) { this.state.pos++ this.finishToken(dotTok) } else if (code === 91) { this.state.pos++ if (this.getCode() === 91) { this.state.pos++ return this.finishToken(bracketDLTok) } this.finishToken(bracketLTok) } else if (code === 126) { this.state.pos++ this.finishToken(expandTok) } else if (code === 93) { this.state.pos++ this.finishToken(bracketRTok) } else if (code === 40) { this.state.pos++ this.finishToken(parenLTok) } else if (code === 41) { this.state.pos++ this.finishToken(parenRTok) } else if (code === 44) { this.state.pos++ this.finishToken(commaTok) } else if (code === 58) { this.state.pos++ this.finishToken(colonTok) } else { this.readKeyWord() } } } ``` -------------------------------------------------------------------------------- /packages/reactive/src/handlers.ts: -------------------------------------------------------------------------------- ```typescript import { bindTargetKeyWithCurrentReaction, runReactionsFromTargetKey, } from './reaction' import { ProxyRaw, RawProxy } from './environment' import { isObservable, isSupportObservable } from './externals' import { createObservable } from './internals' const wellKnownSymbols = new Set( Object.getOwnPropertyNames(Symbol).reduce((buf: Symbol[], key) => { if (key === 'arguments' || key === 'caller') return buf const value = Symbol[key] if (typeof value === 'symbol') return buf.concat(value) return buf }, []) ) const hasOwnProperty = Object.prototype.hasOwnProperty function findObservable(target: any, key: PropertyKey, value: any) { const observableObj = RawProxy.get(value) if (observableObj) { return observableObj } if (!isObservable(value) && isSupportObservable(value)) { return createObservable(target, key, value) } return value } function patchIterator( target: any, key: PropertyKey, iterator: IterableIterator<any>, isEntries: boolean ) { const originalNext = iterator.next iterator.next = () => { let { done, value } = originalNext.call(iterator) if (!done) { if (isEntries) { value[1] = findObservable(target, key, value[1]) } else { value = findObservable(target, key, value) } } return { done, value } } return iterator } const instrumentations = { has(key: PropertyKey) { const target = ProxyRaw.get(this) const proto = Reflect.getPrototypeOf(this) as any bindTargetKeyWithCurrentReaction({ target, key, type: 'has' }) return proto.has.apply(target, arguments) }, get(key: PropertyKey) { const target = ProxyRaw.get(this) const proto = Reflect.getPrototypeOf(this) as any bindTargetKeyWithCurrentReaction({ target, key, type: 'get' }) return findObservable(target, key, proto.get.apply(target, arguments)) }, add(key: PropertyKey) { const target = ProxyRaw.get(this) const proto = Reflect.getPrototypeOf(this) as any const hadKey = proto.has.call(target, key) // forward the operation before queueing reactions const result = proto.add.apply(target, arguments) if (!hadKey) { runReactionsFromTargetKey({ target, key, value: key, type: 'add' }) } return result }, set(key: PropertyKey, value: any) { const target = ProxyRaw.get(this) const proto = Reflect.getPrototypeOf(this) as any const hadKey = proto.has.call(target, key) const oldValue = proto.get.call(target, key) // forward the operation before queueing reactions const result = proto.set.apply(target, arguments) if (!hadKey) { runReactionsFromTargetKey({ target, key, value, type: 'add' }) } else if (value !== oldValue) { runReactionsFromTargetKey({ target, key, value, oldValue, type: 'set' }) } return result }, delete(key: PropertyKey) { const target = ProxyRaw.get(this) const proto = Reflect.getPrototypeOf(this) as any const hadKey = proto.has.call(target, key) const oldValue = proto.get ? proto.get.call(target, key) : undefined // forward the operation before queueing reactions const result = proto.delete.apply(target, arguments) if (hadKey) { runReactionsFromTargetKey({ target, key, oldValue, type: 'delete' }) } return result }, clear() { const target = ProxyRaw.get(this) const proto = Reflect.getPrototypeOf(this) as any const hadItems = target.size !== 0 const oldTarget = target instanceof Map ? new Map(target) : new Set(target) // forward the operation before queueing reactions const result = proto.clear.apply(target, arguments) if (hadItems) { runReactionsFromTargetKey({ target, oldTarget, type: 'clear' }) } return result }, forEach(cb: any, ...args: any[]) { const target = ProxyRaw.get(this) const proto = Reflect.getPrototypeOf(this) as any bindTargetKeyWithCurrentReaction({ target, type: 'iterate' }) // swap out the raw values with their observable pairs // before passing them to the callback const wrappedCb = (value: any, key: PropertyKey, ...args: any) => cb(findObservable(target, key, value), key, ...args) return proto.forEach.call(target, wrappedCb, ...args) }, keys() { const target = ProxyRaw.get(this) const proto = Reflect.getPrototypeOf(this) as any bindTargetKeyWithCurrentReaction({ target, type: 'iterate' }) return proto.keys.apply(target, arguments) }, values() { const target = ProxyRaw.get(this) const proto = Reflect.getPrototypeOf(this) as any bindTargetKeyWithCurrentReaction({ target, type: 'iterate' }) const iterator = proto.values.apply(target, arguments) return patchIterator(target, '', iterator, false) }, entries() { const target = ProxyRaw.get(this) const proto = Reflect.getPrototypeOf(this) as any bindTargetKeyWithCurrentReaction({ target, type: 'iterate' }) const iterator = proto.entries.apply(target, arguments) return patchIterator(target, '', iterator, true) }, [Symbol.iterator]() { const target = ProxyRaw.get(this) const proto = Reflect.getPrototypeOf(this) bindTargetKeyWithCurrentReaction({ target, type: 'iterate' }) const iterator = proto[Symbol.iterator].apply(target, arguments) return patchIterator(target, '', iterator, target instanceof Map) }, get size() { const target = ProxyRaw.get(this) const proto = Reflect.getPrototypeOf(this) bindTargetKeyWithCurrentReaction({ target, type: 'iterate' }) return Reflect.get(proto, 'size', target) }, } export const collectionHandlers = { get(target: any, key: PropertyKey, receiver: any) { // instrument methods and property accessors to be reactive target = hasOwnProperty.call(instrumentations, key) ? instrumentations : target return Reflect.get(target, key, receiver) }, } export const baseHandlers: ProxyHandler<any> = { get(target, key, receiver) { if (!key) return const result = target[key] // use Reflect.get is too slow if (typeof key === 'symbol' && wellKnownSymbols.has(key)) { return result } bindTargetKeyWithCurrentReaction({ target, key, receiver, type: 'get' }) const observableResult = RawProxy.get(result) if (observableResult) { return observableResult } if (!isObservable(result) && isSupportObservable(result)) { const descriptor = Reflect.getOwnPropertyDescriptor(target, key) if ( !descriptor || !(descriptor.writable === false && descriptor.configurable === false) ) { return createObservable(target, key, result) } } return result }, has(target, key) { const result = Reflect.has(target, key) bindTargetKeyWithCurrentReaction({ target, key, type: 'has' }) return result }, ownKeys(target) { const keys = Reflect.ownKeys(target) bindTargetKeyWithCurrentReaction({ target, type: 'iterate' }) return keys }, set(target, key, value, receiver) { // vue2中有对数组原型重写,因此需去除此处proxy if (key === '__proto__') { target[key] = value return true } const hadKey = hasOwnProperty.call(target, key) const newValue = createObservable(target, key, value) const oldValue = target[key] target[key] = newValue // use Reflect.set is too slow if (!hadKey) { runReactionsFromTargetKey({ target, key, value: newValue, oldValue, receiver, type: 'add', }) } else if (value !== oldValue) { runReactionsFromTargetKey({ target, key, value: newValue, oldValue, receiver, type: 'set', }) } return true }, deleteProperty(target, key) { const oldValue = target[key] delete target[key] runReactionsFromTargetKey({ target, key, oldValue, type: 'delete', }) return true }, } ``` -------------------------------------------------------------------------------- /packages/next/docs/components/Space.zh-CN.md: -------------------------------------------------------------------------------- ```markdown # Space > 超级便捷的 Flex 布局组件,可以帮助用户快速实现任何元素的并排紧挨布局 ## Markup Schema 案例 ```tsx import React from 'react' import { Input, FormItem, FormLayout, FormButtonGroup, Submit, Space, } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { Input, FormItem, Space, }, }) const form = createForm() export default () => ( <FormProvider form={form}> <FormLayout labelCol={6} wrapperCol={16}> <SchemaField> <SchemaField.Void title="姓名" x-decorator="FormItem" x-decorator-props={{ asterisk: true, feedbackLayout: 'none', }} x-component="Space" > <SchemaField.String name="firstName" x-decorator="FormItem" x-component="Input" required /> <SchemaField.String name="lastName" x-decorator="FormItem" x-component="Input" required /> </SchemaField.Void> <SchemaField.Void title="文本串联" x-decorator="FormItem" x-decorator-props={{ asterisk: true, feedbackLayout: 'none', }} x-component="Space" > <SchemaField.String name="aa" x-decorator="FormItem" x-component="Input" x-decorator-props={{ addonAfter: '单位', }} required /> <SchemaField.String name="bb" x-decorator="FormItem" x-component="Input" x-decorator-props={{ addonAfter: '单位', }} required /> <SchemaField.String name="cc" x-decorator="FormItem" x-component="Input" x-decorator-props={{ addonAfter: '单位', }} required /> </SchemaField.Void> <SchemaField.String name="textarea" title="文本框" x-decorator="FormItem" required x-component="Input.TextArea" x-component-props={{ style: { width: 400, }, }} /> </SchemaField> <FormButtonGroup.FormItem> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup.FormItem> </FormLayout> </FormProvider> ) ``` ## JSON Schema 案例 ```tsx import React from 'react' import { Input, FormItem, FormLayout, FormButtonGroup, Submit, Space, } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' const SchemaField = createSchemaField({ components: { Input, FormItem, Space, }, }) const form = createForm() const schema = { type: 'object', properties: { name: { type: 'void', title: '姓名', 'x-decorator': 'FormItem', 'x-decorator-props': { asterisk: true, feedbackLayout: 'none', }, 'x-component': 'Space', properties: { firstName: { type: 'string', 'x-decorator': 'FormItem', 'x-component': 'Input', required: true, }, lastName: { type: 'string', 'x-decorator': 'FormItem', 'x-component': 'Input', required: true, }, }, }, texts: { type: 'void', title: '文本串联', 'x-decorator': 'FormItem', 'x-decorator-props': { asterisk: true, feedbackLayout: 'none', }, 'x-component': 'Space', properties: { aa: { type: 'string', 'x-decorator': 'FormItem', 'x-decorator-props': { addonAfter: '单位', }, 'x-component': 'Input', required: true, }, bb: { type: 'string', 'x-decorator': 'FormItem', 'x-decorator-props': { addonAfter: '单位', }, 'x-component': 'Input', required: true, }, cc: { type: 'string', 'x-decorator': 'FormItem', 'x-decorator-props': { addonAfter: '单位', }, 'x-component': 'Input', required: true, }, }, }, textarea: { type: 'string', title: '文本框', 'x-decorator': 'FormItem', 'x-component': 'Input.TextArea', 'x-component-props': { style: { width: 400, }, }, required: true, }, }, } export default () => ( <FormProvider form={form}> <FormLayout labelCol={6} wrapperCol={16}> <SchemaField schema={schema} /> <FormButtonGroup.FormItem> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup.FormItem> </FormLayout> </FormProvider> ) ``` ## 纯 JSX 案例 ```tsx import React from 'react' import { Input, FormItem, FormLayout, FormButtonGroup, Submit, Space, } from '@formily/next' import { createForm } from '@formily/core' import { FormProvider, Field, VoidField } from '@formily/react' const form = createForm() export default () => ( <FormProvider form={form}> <FormLayout labelCol={6} wrapperCol={16}> <VoidField name="name" title="姓名" decorator={[ FormItem, { asterisk: true, feedbackLayout: 'none', }, ]} component={[Space]} > <Field name="firstName" decorator={[FormItem]} component={[Input]} required /> <Field name="lastName" decorator={[FormItem]} component={[Input]} required /> </VoidField> <VoidField name="texts" title="文本串联" decorator={[ FormItem, { asterisk: true, feedbackLayout: 'none', }, ]} component={[Space]} > <Field name="aa" decorator={[ FormItem, { addonAfter: '单位', }, ]} component={[Input]} required /> <Field name="bb" decorator={[ FormItem, { addonAfter: '单位', }, ]} component={[Input]} required /> <Field name="cc" decorator={[ FormItem, { addonAfter: '单位', }, ]} component={[Input]} required /> </VoidField> <Field name="textarea" title="文本框" decorator={[FormItem]} component={[ Input.TextArea, { style: { width: 400, }, }, ]} required /> <FormButtonGroup.FormItem> <Submit onSubmit={console.log}>提交</Submit> </FormButtonGroup.FormItem> </FormLayout> </FormProvider> ) ``` ## API | 属性名 | 类型 | 描述 | 默认值 | | --------- | -------------------------------------------- | -------- | --------- | | style | CSSProperties | 样式 | - | | className | string | 类名 | - | | prefix | string | 样式前缀 | true | | size | `number \| 'small' \| 'large' \| 'middle'` | 间隔尺寸 | 8px | | direction | `'horizontal' \| 'vertical'` | 方向 | - | | align | `'start' \| 'end' \| 'center' \| 'baseline'` | 对齐 | `'start'` | | wrap | boolean | 是否换行 | false | ``` -------------------------------------------------------------------------------- /packages/antd/docs/components/FormDrawer.md: -------------------------------------------------------------------------------- ```markdown # FormDrawer > Drawer form, mainly used in simple event to open form scene ## Markup Schema example ```tsx import React from 'react' import { FormDrawer, FormItem, FormLayout, Input, Submit, Reset, FormButtonGroup, } from '@formily/antd' import { createSchemaField } from '@formily/react' import { Button } from 'antd' const SchemaField = createSchemaField({ components: { FormItem, Input, }, }) export default () => { return ( <Button onClick={() => { FormDrawer('Drawer Form', () => { return ( <FormLayout labelCol={6} wrapperCol={10}> <SchemaField> <SchemaField.String name="aaa" required title="input box 1" x-decorator="FormItem" x-component="Input" /> <SchemaField.String name="bbb" required title="input box 2" x-decorator="FormItem" x-component="Input" /> <SchemaField.String name="ccc" required title="input box 3" x-decorator="FormItem" x-component="Input" /> <SchemaField.String name="ddd" required title="input box 4" x-decorator="FormItem" x-component="Input" /> </SchemaField> <FormDrawer.Extra> <FormButtonGroup align="right"> <Submit onSubmit={() => { return new Promise((resolve) => { setTimeout(resolve, 1000) }) }} > Submit </Submit> <Reset>Reset</Reset> </FormButtonGroup> </FormDrawer.Extra> </FormLayout> ) }) .forOpen((props, next) => { setTimeout(() => { next({ initialValues: { aaa: '123', }, }) }, 1000) }) .open() .then(console.log) }} > Click me to open the form </Button> ) } ``` ## JSON Schema case ```tsx import React from 'react' import { FormDrawer, FormItem, FormLayout, Input, Submit, Reset, FormButtonGroup, } from '@formily/antd' import { createSchemaField } from '@formily/react' import { Button } from 'antd' const SchemaField = createSchemaField({ components: { FormItem, Input, }, }) const schema = { type: 'object', properties: { aaa: { type: 'string', title: 'input box 1', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', }, bbb: { type: 'string', title: 'input box 2', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', }, ccc: { type: 'string', title: 'input box 3', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', }, ddd: { type: 'string', title: 'input box 4', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', }, }, } export default () => { return ( <Button onClick={() => { FormDrawer('Pop-up form', () => { return ( <FormLayout labelCol={6} wrapperCol={10}> <SchemaField schema={schema} /> <FormDrawer.Extra> <FormButtonGroup align="right"> <Submit onSubmit={() => { return new Promise((resolve) => { setTimeout(resolve, 1000) }) }} > Submit </Submit> <Reset>Reset</Reset> </FormButtonGroup> </FormDrawer.Extra> </FormLayout> ) }) .open({ initialValues: { aaa: '123', }, }) .then(console.log) }} > Click me to open the form </Button> ) } ``` ## Pure JSX case ```tsx import React from 'react' import { FormDrawer, FormItem, FormLayout, Input, Submit, Reset, FormButtonGroup, } from '@formily/antd' import { Field } from '@formily/react' import { Button } from 'antd' export default () => { return ( <Button onClick={() => { FormDrawer('Pop-up form', () => { return ( <FormLayout labelCol={6} wrapperCol={10}> <Field name="aaa" required title="input box 1" decorator={[FormItem]} component={[Input]} /> <Field name="bbb" required title="input box 2" decorator={[FormItem]} component={[Input]} /> <Field name="ccc" required title="input box 3" decorator={[FormItem]} component={[Input]} /> <Field name="ddd" required title="input box 4" decorator={[FormItem]} component={[Input]} /> <FormDrawer.Extra> <FormButtonGroup align="right"> <Submit onSubmit={() => { return new Promise((resolve) => { setTimeout(resolve, 1000) }) }} > Submit </Submit> <Reset>Reset</Reset> </FormButtonGroup> </FormDrawer.Extra> </FormLayout> ) }) .open({ initialValues: { aaa: '123', }, }) .then(console.log) }} > Click me to open the form </Button> ) } ``` ## API ### FormDrawer ```ts pure import { IFormProps, Form } from '@formily/core' type FormDrawerRenderer = | React.ReactElement | ((form: Form) => React.ReactElement) interface IFormDrawer { forOpen( middleware: ( props: IFormProps, next: (props?: IFormProps) => Promise<any> ) => any ): any //Middleware interceptor, can intercept Drawer to open //Open the pop-up window to receive form attributes, you can pass in initialValues/values/effects etc. open(props: IFormProps): Promise<any> //return form data //Close the pop-up window close(): void } export interface IDrawerProps extends DrawerProps { onClose?: (e: EventType) => void | boolean // return false can prevent onClose loadingText?: React.ReactNode } interface FormDrawer { (title: IDrawerProps, id: string, renderer: FormDrawerRenderer): IFormDrawer (title: IDrawerProps, renderer: FormDrawerRenderer): IFormDrawer (title: ModalTitle, id: string, renderer: FormDrawerRenderer): IFormDrawer (title: ModalTitle, renderer: FormDrawerRenderer): IFormDrawer } ``` `DrawerProps` type definition reference ant design [Drawer API](https://ant.design/components/drawer-cn/#API) ### FormDrawer.Extra No attributes, only child nodes are received ### FormDrawer.Footer No attributes, only child nodes are received ### FormDrawer.Portal 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 ``` -------------------------------------------------------------------------------- /packages/next/src/preview-text/index.tsx: -------------------------------------------------------------------------------- ```typescript import React, { createContext, useContext } from 'react' import { isArr, isEmpty, isValid } from '@formily/shared' import { Field } from '@formily/core' import { useField, observer } from '@formily/react' import { InputProps } from '@alifd/next/lib/input' import { NumberPickerProps } from '@alifd/next/lib/number-picker' import { SelectProps } from '@alifd/next/lib/select' import { TreeSelectProps } from '@alifd/next/lib/tree-select' import { CascaderSelectProps } from '@alifd/next/lib/cascader-select' import { DatePickerProps, RangePickerProps as DateRangePickerProps, } from '@alifd/next/lib/date-picker' import { TimePickerProps } from '@alifd/next/lib/time-picker' import { TimePickerProps as TimePicker2Props, RangePickerProps as TimeRangePicker2Props, } from '@alifd/next/types/time-picker2' import { Tag, Input as NextInput, NumberPicker as NextNumberPicker, CascaderSelect as NextCascader, } from '@alifd/next' import cls from 'classnames' import { formatMomentValue, usePrefixCls } from '../__builtins__' const PlaceholderContext = createContext<React.ReactNode>('N/A') const Placeholder = PlaceholderContext.Provider const usePlaceholder = (value?: any) => { const placeholder = useContext(PlaceholderContext) || 'N/A' return !isEmpty(value) ? value : placeholder } const Input: React.FC<React.PropsWithChildren<InputProps>> = (props) => { return <NextInput {...props} isPreview /> } const NumberPicker: React.FC<React.PropsWithChildren<NumberPickerProps>> = ( props ) => { return <NextNumberPicker {...props} isPreview /> } const Select: React.FC<React.PropsWithChildren<SelectProps>> = observer( (props) => { const field = useField<Field>() const prefixCls = usePrefixCls('form-preview', props) const dataSource: any[] = field?.dataSource?.length ? field.dataSource : props?.dataSource?.length ? props.dataSource : [] const placeholder = usePlaceholder() const getSelected = () => { const value = props.value if (props.mode === 'multiple' || props.mode === 'tag') { if (props.useDetailValue) { return isArr(value) ? value : [] } else { return isArr(value) ? value.map((val) => ({ label: val, value: val })) : [] } } else { if (props.useDetailValue) { return isValid(value) ? [value] : [] } else { return isValid(value) ? [{ label: value, value }] : [] } } } const getLabel = (target: any) => { return ( dataSource?.find((item) => item.value == target?.value)?.label || target.label || placeholder ) } const getLabels = () => { const selected = getSelected() if (!selected.length) return placeholder if (selected.length === 1) return getLabel(selected[0]) return selected.map((item, key) => { return ( <Tag type="primary" size="small" key={key}> {getLabel(item)} </Tag> ) }) } return <div className={cls(prefixCls, props.className)}>{getLabels()}</div> } ) const TreeSelect: React.FC<React.PropsWithChildren<TreeSelectProps>> = observer( (props) => { const field = useField<Field>() const placeholder = usePlaceholder() const prefixCls = usePrefixCls('form-preview', props) const dataSource = field?.dataSource?.length ? field.dataSource : props?.dataSource?.length ? props.dataSource : [] const getSelected = () => { const value = props.value if (props.multiple) { if (props['useDetailValue']) { return isArr(value) ? value : [] } else { return isArr(value) ? value.map((val) => ({ label: val, value: val })) : [] } } else { if (props['useDetailValue']) { return value ? [value] : [] } else { return value ? [{ label: value, value }] : [] } } } const findLabel = (value: any, dataSource: any[]) => { for (let i = 0; i < dataSource?.length; i++) { const item = dataSource[i] if (item?.value === value) { return item?.label } else { const childLabel = findLabel(value, item?.children) if (childLabel) return childLabel } } } const getLabels = () => { const selected = getSelected() if (!selected?.length) return ( <Tag type="primary" size="small"> {placeholder} </Tag> ) return selected.map(({ value, label }, key) => { return ( <Tag type="primary" size="small" key={key}> {findLabel(value, dataSource) || label || placeholder} </Tag> ) }) } return <div className={cls(prefixCls, props.className)}>{getLabels()}</div> } ) const Cascader: React.FC<React.PropsWithChildren<CascaderSelectProps>> = observer((props) => { const field = useField<Field>() const prefixCls = usePrefixCls('form-preview', props) return ( <NextCascader {...props} className={prefixCls} dataSource={field.dataSource} isPreview /> ) }) const DatePicker: React.FC<React.PropsWithChildren<DatePickerProps>> = ( props ) => { const placeholder = usePlaceholder() const prefixCls = usePrefixCls('form-preview', props) const getLabels = () => { const labels = formatMomentValue(props.value, props.format, placeholder) return isArr(labels) ? labels.join('~') : labels } return <div className={cls(prefixCls, props.className)}>{getLabels()}</div> } const DateRangePicker: React.FC< React.PropsWithChildren<DateRangePickerProps> > = (props) => { const placeholder = usePlaceholder() const prefixCls = usePrefixCls('form-preview', props) const getLabels = () => { const labels = formatMomentValue(props.value, props.format, placeholder) return isArr(labels) ? labels.join('~') : labels } return <div className={cls(prefixCls, props.className)}>{getLabels()}</div> } const TimePicker: React.FC<React.PropsWithChildren<TimePickerProps>> = ( props ) => { const placeholder = usePlaceholder() const prefixCls = usePrefixCls('form-preview', props) const getLabels = () => { const labels = formatMomentValue(props.value, props.format, placeholder) return isArr(labels) ? labels.join('~') : labels } return <div className={cls(prefixCls, props.className)}>{getLabels()}</div> } const TimePicker2: React.FC<React.PropsWithChildren<TimePicker2Props>> = ( props ) => { const placeholder = usePlaceholder() const prefixCls = usePrefixCls('form-preview', props) const getLabels = () => { const labels = formatMomentValue(props.value, props.format, placeholder) return isArr(labels) ? labels.join('~') : labels } return <div className={cls(prefixCls, props.className)}>{getLabels()}</div> } const TimeRangePicker2: React.FC< React.PropsWithChildren<TimeRangePicker2Props> > = (props) => { const placeholder = usePlaceholder() const prefixCls = usePrefixCls('form-preview', props) const getLabels = () => { const labels = formatMomentValue(props.value, props.format, placeholder) return isArr(labels) ? labels.join('~') : labels } return <div className={cls(prefixCls, props.className)}>{getLabels()}</div> } const Text = (props: React.PropsWithChildren<any>) => { const prefixCls = usePrefixCls('form-preview', props) return ( <div className={cls(prefixCls, props.className)} style={props.style}> {usePlaceholder(props.value)} </div> ) } Text.Input = Input Text.NumberPicker = NumberPicker Text.Select = Select Text.TreeSelect = TreeSelect Text.Cascader = Cascader Text.DatePicker = DatePicker Text.DateRangePicker = DateRangePicker Text.TimePicker = TimePicker Text.TimePicker2 = TimePicker2 Text.TimeRangePicker2 = TimeRangePicker2 Text.Placeholder = Placeholder Text.usePlaceholder = usePlaceholder export const PreviewText = Text export default PreviewText ``` -------------------------------------------------------------------------------- /packages/reactive/src/__tests__/collections-set.spec.ts: -------------------------------------------------------------------------------- ```typescript import { observable, autorun, raw } from '..' describe('Set', () => { test('should be a proper JS Set', () => { const set = observable(new Set()) expect(set).toBeInstanceOf(Set) expect(raw(set)).toBeInstanceOf(Set) }) test('should autorun mutations', () => { const handler = jest.fn() const set = observable(new Set()) autorun(() => handler(set.has('value'))) expect(handler).toBeCalledTimes(1) expect(handler).lastCalledWith(false) set.add('value') expect(handler).toBeCalledTimes(2) expect(handler).lastCalledWith(true) set.delete('value') expect(handler).toBeCalledTimes(3) expect(handler).lastCalledWith(false) }) test('should autorun size mutations', () => { const handler = jest.fn() const set = observable(new Set()) autorun(() => handler(set.size)) expect(handler).toBeCalledTimes(1) expect(handler).lastCalledWith(0) set.add('value') set.add('value2') expect(handler).toBeCalledTimes(3) expect(handler).lastCalledWith(2) set.delete('value') expect(handler).toBeCalledTimes(4) expect(handler).lastCalledWith(1) set.clear() expect(handler).toBeCalledTimes(5) expect(handler).lastCalledWith(0) }) test('should autorun for of iteration', () => { const handler = jest.fn() const set = observable(new Set<number>()) autorun(() => { let sum = 0 // eslint-disable-next-line no-unused-vars for (let num of set) { sum += num } handler(sum) }) expect(handler).toBeCalledTimes(1) expect(handler).lastCalledWith(0) set.add(3) expect(handler).toBeCalledTimes(2) expect(handler).lastCalledWith(3) set.add(2) expect(handler).toBeCalledTimes(3) expect(handler).lastCalledWith(5) set.delete(3) expect(handler).toBeCalledTimes(4) expect(handler).lastCalledWith(2) set.clear() expect(handler).toBeCalledTimes(5) expect(handler).lastCalledWith(0) }) test('should autorun forEach iteration', () => { const handler = jest.fn() const set = observable(new Set<number>()) autorun(() => { let sum = 0 set.forEach((num) => (sum += num)) handler(sum) }) expect(handler).toBeCalledTimes(1) expect(handler).lastCalledWith(0) set.add(3) expect(handler).toBeCalledTimes(2) expect(handler).lastCalledWith(3) set.add(2) expect(handler).toBeCalledTimes(3) expect(handler).lastCalledWith(5) set.delete(3) expect(handler).toBeCalledTimes(4) expect(handler).lastCalledWith(2) set.clear() expect(handler).toBeCalledTimes(5) expect(handler).lastCalledWith(0) }) test('should autorun keys iteration', () => { const handler = jest.fn() const set = observable(new Set<number>()) autorun(() => { let sum = 0 for (let key of set.keys()) { sum += key } handler(sum) }) expect(handler).toBeCalledTimes(1) expect(handler).lastCalledWith(0) set.add(3) expect(handler).toBeCalledTimes(2) expect(handler).lastCalledWith(3) set.add(2) expect(handler).toBeCalledTimes(3) expect(handler).lastCalledWith(5) set.delete(3) expect(handler).toBeCalledTimes(4) expect(handler).lastCalledWith(2) set.clear() expect(handler).toBeCalledTimes(5) expect(handler).lastCalledWith(0) }) test('should autorun values iteration', () => { const handler = jest.fn() const set = observable(new Set<number>()) autorun(() => { let sum = 0 for (let num of set.values()) { sum += num } handler(sum) }) expect(handler).toBeCalledTimes(1) expect(handler).lastCalledWith(0) set.add(3) expect(handler).toBeCalledTimes(2) expect(handler).lastCalledWith(3) set.add(2) expect(handler).toBeCalledTimes(3) expect(handler).lastCalledWith(5) set.delete(3) expect(handler).toBeCalledTimes(4) expect(handler).lastCalledWith(2) set.clear() expect(handler).toBeCalledTimes(5) expect(handler).lastCalledWith(0) }) test('should autorun entries iteration', () => { const handler = jest.fn() const set = observable(new Set<number>()) autorun(() => { let sum = 0 // eslint-disable-next-line no-unused-vars for (let [, num] of set.entries()) { sum += num } handler(sum) }) expect(handler).toBeCalledTimes(1) expect(handler).lastCalledWith(0) set.add(3) expect(handler).toBeCalledTimes(2) expect(handler).lastCalledWith(3) set.add(2) expect(handler).toBeCalledTimes(3) expect(handler).lastCalledWith(5) set.delete(3) expect(handler).toBeCalledTimes(4) expect(handler).lastCalledWith(2) set.clear() expect(handler).toBeCalledTimes(5) expect(handler).lastCalledWith(0) }) test('should not autorun custom property mutations', () => { const handler = jest.fn() const set = observable(new Set()) autorun(() => handler(set['customProp'])) expect(handler).toBeCalledTimes(1) expect(handler).lastCalledWith(undefined) set['customProp'] = 'Hello World' expect(handler).toBeCalledTimes(1) }) test('should not autorun non value changing mutations', () => { const handler = jest.fn() const set = observable(new Set()) autorun(() => handler(set.has('value'))) expect(handler).toBeCalledTimes(1) expect(handler).lastCalledWith(false) set.add('value') expect(handler).toBeCalledTimes(2) expect(handler).lastCalledWith(true) set.add('value') expect(handler).toBeCalledTimes(2) set.delete('value') expect(handler).toBeCalledTimes(3) expect(handler).lastCalledWith(false) set.delete('value') expect(handler).toBeCalledTimes(3) set.clear() expect(handler).toBeCalledTimes(3) }) test('should not autorun raw data', () => { const handler = jest.fn() const set = observable(new Set()) autorun(() => handler(raw(set).has('value'))) expect(handler).toBeCalledTimes(1) expect(handler).lastCalledWith(false) set.add('value') expect(handler).toBeCalledTimes(1) set.delete('value') expect(handler).toBeCalledTimes(1) }) test('should not autorun raw iterations', () => { const handler = jest.fn() const set = observable(new Set<number>()) autorun(() => { let sum = 0 // eslint-disable-next-line no-unused-vars for (let [, num] of raw(set).entries()) { sum += num } for (let key of raw(set).keys()) { sum += key } for (let num of raw(set).values()) { sum += num } raw(set).forEach((num) => { sum += num }) // eslint-disable-next-line no-unused-vars for (let num of raw(set)) { sum += num } handler(sum) }) expect(handler).toBeCalledTimes(1) expect(handler).lastCalledWith(0) set.add(2) set.add(3) expect(handler).toBeCalledTimes(1) set.delete(2) expect(handler).toBeCalledTimes(1) }) test('should not be triggered by raw mutations', () => { const handler = jest.fn() const set = observable(new Set()) autorun(() => handler(set.has('value'))) expect(handler).toBeCalledTimes(1) expect(handler).lastCalledWith(false) raw(set).add('value') expect(handler).toBeCalledTimes(1) raw(set).delete('value') expect(handler).toBeCalledTimes(1) raw(set).clear() expect(handler).toBeCalledTimes(1) }) test('should not autorun raw size mutations', () => { const handler = jest.fn() const set = observable(new Set()) autorun(() => handler(raw(set).size)) expect(handler).toBeCalledTimes(1) expect(handler).lastCalledWith(0) set.add('value') expect(handler).toBeCalledTimes(1) }) test('should not be triggered by raw size mutations', () => { const handler = jest.fn() const set = observable(new Set()) autorun(() => handler(set.size)) expect(handler).toBeCalledTimes(1) expect(handler).lastCalledWith(0) raw(set).add('value') expect(handler).toBeCalledTimes(1) }) }) ```