This is page 3 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/reactive-vue/src/observer/collectData.ts: -------------------------------------------------------------------------------- ```typescript // https://github.com/mobxjs/mobx-vue/blob/master/src/collectData.ts /** * @author Kuitos * @homepage https://github.com/kuitos/ * @since 2018-06-08 10:16 */ import { isObservable } from '@formily/reactive' export default function collectData(vm: any, data?: any) { const dataDefinition = typeof data === 'function' ? data.call(vm, vm) : data || {} const filteredData = Object.keys(dataDefinition).reduce( (result: any, field) => { const value = dataDefinition[field] if (isObservable(value)) { Object.defineProperty(vm, field, { configurable: true, get() { return value }, }) } else { result[field] = value } return result }, {} ) return filteredData } ``` -------------------------------------------------------------------------------- /packages/vue/src/components/FormConsumer.ts: -------------------------------------------------------------------------------- ```typescript import { defineComponent } from 'vue-demi' import { observer } from '@formily/reactive-vue' import { useForm } from '../hooks' import h from '../shared/h' export default observer( defineComponent({ name: 'FormConsumer', inheritAttrs: false, setup(props, { slots }) { const formRef = useForm() return () => { // just like <Fragment> return h( 'div', { style: { display: 'contents' } }, { default: () => slots.default?.({ form: formRef.value, }), } ) } }, }), { // make sure observables updated <cannot be tracked by tests> scheduler: /* istanbul ignore next */ (update) => Promise.resolve().then(update), } ) ``` -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- ```yaml name: Node CI on: push: branches: - formily_next pull_request: branches: - formily_next jobs: build: runs-on: ubuntu-latest if: contains(github.event.head_commit.message, 'chore(versions)') == false steps: - uses: actions/checkout@v1 - name: Use Node.js uses: actions/setup-node@v1 with: node-version: 16 - run: yarn -v - run: yarn --ignore-engines - name: ESlint uses: reviewdog/action-eslint@v1 with: reporter: github-check eslint_flags: '.' - run: yarn build - run: yarn test:prod env: CI: true HEADLESS: false PROGRESS: none NODE_ENV: test NODE_OPTIONS: --max_old_space_size=4096 ``` -------------------------------------------------------------------------------- /packages/benchmark/webpack.prod.ts: -------------------------------------------------------------------------------- ```typescript import baseConfig from './webpack.base' import HtmlWebpackPlugin from 'html-webpack-plugin' import MiniCssExtractPlugin from 'mini-css-extract-plugin' import path from 'path' const createPages = (pages) => { return pages.map(({ filename, template, chunk }) => { return new HtmlWebpackPlugin({ filename, template, inject: 'body', chunks: chunk, }) }) } export default { ...baseConfig, mode: 'production', plugins: [ new MiniCssExtractPlugin({ filename: '[name].[hash].css', chunkFilename: '[id].[hash].css', }), ...createPages([ { filename: 'index.html', template: path.resolve(__dirname, './template.ejs'), chunk: ['index'], }, ]), ], optimization: { minimize: true, }, } ``` -------------------------------------------------------------------------------- /packages/reactive-test-cases-for-react18/webpack.prod.ts: -------------------------------------------------------------------------------- ```typescript import baseConfig from './webpack.base' import HtmlWebpackPlugin from 'html-webpack-plugin' import MiniCssExtractPlugin from 'mini-css-extract-plugin' import path from 'path' const createPages = (pages) => { return pages.map(({ filename, template, chunk }) => { return new HtmlWebpackPlugin({ filename, template, inject: 'body', chunks: chunk, }) }) } export default { ...baseConfig, mode: 'production', plugins: [ new MiniCssExtractPlugin({ filename: '[name].[hash].css', chunkFilename: '[id].[hash].css', }), ...createPages([ { filename: 'index.html', template: path.resolve(__dirname, './template.ejs'), chunk: ['index'], }, ]), ], optimization: { minimize: true, }, } ``` -------------------------------------------------------------------------------- /packages/next/src/checkbox/index.tsx: -------------------------------------------------------------------------------- ```typescript import { connect, mapProps, mapReadPretty } from '@formily/react' import { Checkbox as NextCheckbox } from '@alifd/next' import { CheckboxProps, GroupProps as CheckboxGroupProps, } from '@alifd/next/lib/checkbox' import { PreviewText } from '../preview-text' import { mapSize } from '../__builtins__' type ComposedCheckbox = React.FC<React.PropsWithChildren<CheckboxProps>> & { Group?: React.FC<React.PropsWithChildren<CheckboxGroupProps>> } export const Checkbox: ComposedCheckbox = connect( NextCheckbox, mapProps( { value: 'checked', }, mapSize ) ) Checkbox.Group = connect( NextCheckbox.Group, mapProps( { dataSource: true, }, mapSize ), mapReadPretty(PreviewText.Select, { mode: 'multiple', }) ) export default Checkbox ``` -------------------------------------------------------------------------------- /packages/element/src/input/index.ts: -------------------------------------------------------------------------------- ```typescript import { composeExport, transformComponent } from '../__builtins__/shared' import { connect, mapProps, mapReadPretty } from '@formily/vue' import { PreviewText } from '../preview-text' import type { Input as ElInputProps } from 'element-ui' import { Input as ElInput } from 'element-ui' export type InputProps = ElInputProps const TransformElInput = transformComponent<InputProps>(ElInput, { change: 'input', }) const InnerInput = connect( TransformElInput, mapProps({ readOnly: 'readonly' }), mapReadPretty(PreviewText.Input) ) const TextArea = connect( InnerInput, mapProps((props) => { return { ...props, type: 'textarea', } }), mapReadPretty(PreviewText.Input) ) export const Input = composeExport(InnerInput, { TextArea, }) export default Input ``` -------------------------------------------------------------------------------- /packages/react/docs/api/hooks/useParentForm.md: -------------------------------------------------------------------------------- ```markdown # useParentForm ## Description Used to read the most recent Form or ObjectField instance, which is mainly convenient for calling the submit/validate of the subform ## Signature ```ts interface useParentForm { (): Form | ObjectField } ``` ## Example ```tsx import React from 'react' import { createForm } from '@formily/core' import { FormProvider, ObjectField, VoidField, observer, useParentForm, } from '@formily/react' const form = createForm() const Custom = observer(() => { const form = useParentForm() return <div>{form.displayName}</div> }) export default () => ( <FormProvider form={form}> <ObjectField name="object"> <Custom /> </ObjectField> <Custom /> <VoidField name="void"> <Custom /> </VoidField> </FormProvider> ) ``` ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/transfer/template.vue: -------------------------------------------------------------------------------- ```vue <template> <FormProvider :form="form"> <Field name="input" title="单选" :decorator="[FormItem]" :component="[Transfer]" :dataSource="[ { label: '选项1', key: 1, }, { label: '选项2', key: 2, }, ]" /> <Submit @submit="log">提交</Submit> </FormProvider> </template> <script> import { createForm } from '@formily/core' import { FormProvider, Field } from '@formily/vue' import { FormItem, Transfer, Submit } from '@formily/element' const form = createForm() export default { components: { FormProvider, Field, Submit }, data() { return { FormItem, Transfer, form, } }, methods: { log(value) { console.log(value) }, }, } </script> ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/radio/template.vue: -------------------------------------------------------------------------------- ```vue <template> <FormProvider :form="form"> <Field name="input" title="单选" :decorator="[FormItem]" :component="[Radio.Group]" :dataSource="[ { label: '选项1', value: 1, }, { label: '选项2', value: 2, }, ]" /> <Submit @submit="log">提交</Submit> </FormProvider> </template> <script> import { createForm } from '@formily/core' import { FormProvider, Field } from '@formily/vue' import { FormItem, Radio, Submit } from '@formily/element' const form = createForm() export default { components: { FormProvider, Field, Submit }, data() { return { FormItem, Radio, form, } }, methods: { log(value) { console.log(value) }, }, } </script> ``` -------------------------------------------------------------------------------- /packages/react/src/components/ObjectField.tsx: -------------------------------------------------------------------------------- ```typescript import React from 'react' import { ObjectField as ObjectFieldType } from '@formily/core' import { useForm, useField } from '../hooks' import { useAttach } from '../hooks/useAttach' import { ReactiveField } from './ReactiveField' import { FieldContext } from '../shared' import { JSXComponent, IFieldProps } from '../types' export const ObjectField = <D extends JSXComponent, C extends JSXComponent>( props: IFieldProps<D, C, ObjectFieldType> ) => { const form = useForm() const parent = useField() const field = useAttach( form.createObjectField({ basePath: parent?.address, ...props }) ) return ( <FieldContext.Provider value={field}> <ReactiveField field={field}>{props.children}</ReactiveField> </FieldContext.Provider> ) } ObjectField.displayName = 'ObjectField' ``` -------------------------------------------------------------------------------- /packages/react/docs/guide/architecture.md: -------------------------------------------------------------------------------- ```markdown # Core Architecture The architecture of @formily/react is not complicated compared to @formily/core. First look at the architecture diagram:  From this architecture diagram, we can see that @formily/react supports two types of users, one is pure source code development users, they only need to use the Field/ArrayField/ObjectField/VoidField component. The other type is users who do dynamic development based on JSON-Schema. They mainly rely on the SchemaField component. However, both types of users need to use a FormProvider component to uniformly deliver the context. Then there is the SchemaField component, which is actually the dependent Field/ArrayField/ObjectField/VoidField component inside. ``` -------------------------------------------------------------------------------- /packages/react/src/components/ArrayField.tsx: -------------------------------------------------------------------------------- ```typescript import React from 'react' import { ArrayField as ArrayFieldType } from '@formily/core' import { useForm, useField } from '../hooks' import { useAttach } from '../hooks/useAttach' import { FieldContext } from '../shared' import { JSXComponent, IFieldProps } from '../types' import { ReactiveField } from './ReactiveField' export const ArrayField = <D extends JSXComponent, C extends JSXComponent>( props: IFieldProps<D, C, ArrayFieldType> ) => { const form = useForm() const parent = useField() const field = useAttach( form.createArrayField({ basePath: parent?.address, ...props, }) ) return ( <FieldContext.Provider value={field}> <ReactiveField field={field}>{props.children}</ReactiveField> </FieldContext.Provider> ) } ArrayField.displayName = 'ArrayField' ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/form-item/json-schema.vue: -------------------------------------------------------------------------------- ```vue <template> <Form :form="form"> <SchemaField :schema="schema" /> <Submit @submit="onSubmit">提交</Submit> </Form> </template> <script> import { createForm } from '@formily/core' import { createSchemaField } from '@formily/vue' import { Form, FormItem, Input, Submit } from '@formily/element' const schema = { type: 'object', properties: { input: { type: 'string', title: '输入框', 'x-decorator': 'FormItem', 'x-component': 'Input', }, }, } const form = createForm() const { SchemaField } = createSchemaField({ components: { FormItem, Input, }, }) export default { components: { Form, SchemaField, Submit }, data() { return { form, schema, } }, methods: { onSubmit(value) { console.log(value) }, }, } </script> ``` -------------------------------------------------------------------------------- /packages/core/src/__tests__/object.spec.ts: -------------------------------------------------------------------------------- ```typescript import { createForm } from '../' import { attach } from './shared' test('create object field', () => { const form = attach(createForm()) const object = attach( form.createObjectField({ name: 'object', }) ) expect(object.value).toEqual({}) expect(object.addProperty).toBeDefined() expect(object.removeProperty).toBeDefined() expect(object.existProperty).toBeDefined() }) test('create object field methods', () => { const form = attach(createForm()) const object = attach( form.createObjectField({ name: 'object', value: {}, }) ) expect(object.value).toEqual({}) object.addProperty('aaa', 123) expect(object.value).toEqual({ aaa: 123 }) object.removeProperty('aaa') expect(object.value).toEqual({}) expect(object.existProperty('aaa')).toBeFalsy() }) ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/switch/json-schema.vue: -------------------------------------------------------------------------------- ```vue <template> <Form :form="form"> <SchemaField :schema="schema" /> <Submit @submit="onSubmit">提交</Submit> </Form> </template> <script> import { createForm } from '@formily/core' import { createSchemaField } from '@formily/vue' import { Form, FormItem, Switch, Submit } from '@formily/element' const schema = { type: 'object', properties: { switch: { type: 'boolean', title: '开关', 'x-decorator': 'FormItem', 'x-component': 'Switch', }, }, } const form = createForm() const { SchemaField } = createSchemaField({ components: { FormItem, Switch, }, }) export default { components: { Form, SchemaField, Submit }, data() { return { form, schema, } }, methods: { onSubmit(value) { console.log(value) }, }, } </script> ``` -------------------------------------------------------------------------------- /packages/element/src/input-number/index.ts: -------------------------------------------------------------------------------- ```typescript import { transformComponent } from '../__builtins__/shared' import { connect, mapProps, mapReadPretty } from '@formily/vue' import type { InputNumber as _ElInputNumberProps } from 'element-ui' import { InputNumber as ElInputNumber } from 'element-ui' import { PreviewText } from '../preview-text' export type InputNumberProps = _ElInputNumberProps const TransformElInputNumber = transformComponent<InputNumberProps>( ElInputNumber, { change: 'input', } ) export const InputNumber = connect( TransformElInputNumber, mapProps({ readOnly: 'readonly' }, (props) => { let controlsPosition = 'right' if (props.controlsPosition) { controlsPosition = props.controlsPosition } return { controlsPosition, } }), mapReadPretty(PreviewText.Input) ) export default InputNumber ``` -------------------------------------------------------------------------------- /packages/antd/src/select-table/useSize.tsx: -------------------------------------------------------------------------------- ```typescript interface ISize { ( fieldSize: 'large' | 'default' | 'small', searchSize: 'large' | 'middle' | 'small', tableSize: 'large' | 'middle' | 'small' ): { searchSize: 'large' | 'middle' | 'small' tableSize: 'large' | 'middle' | 'small' } } const useSize: ISize = (fieldSize = 'default', searchSize, tableSize) => { const fieldSizeMap: any = { small: { searchSize: 'small', tableSize: 'small', }, default: { searchSize: 'middle', tableSize: 'middle', }, large: { searchSize: 'large', tableSize: 'default', }, } const { searchSize: fieldSearchSize, tableSize: fieldTableSize } = fieldSizeMap[fieldSize] return { searchSize: searchSize || fieldSearchSize, tableSize: tableSize || fieldTableSize, } } export { useSize } ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/password/json-schema.vue: -------------------------------------------------------------------------------- ```vue <template> <Form :form="form"> <SchemaField :schema="schema" /> <Submit @submit="onSubmit">提交</Submit> </Form> </template> <script> import { createForm } from '@formily/core' import { createSchemaField } from '@formily/vue' import { Form, FormItem, Password, Submit } from '@formily/element' const schema = { type: 'object', properties: { password: { type: 'string', title: '密码框', 'x-decorator': 'FormItem', 'x-component': 'Password', }, }, } const form = createForm() const { SchemaField } = createSchemaField({ components: { FormItem, Password, }, }) export default { components: { Form, SchemaField, Submit }, data() { return { form, schema, } }, methods: { onSubmit(value) { console.log(value) }, }, } </script> ``` -------------------------------------------------------------------------------- /packages/validator/src/__tests__/registry.spec.ts: -------------------------------------------------------------------------------- ```typescript import { getValidateLocaleIOSCode, getLocaleByPath, getValidateLocale, setValidateLanguage, } from '../' import locale from '../locale' test('getValidateLocaleIOSCode', () => { expect(getValidateLocaleIOSCode('zh-CN')).toEqual('zh-CN') expect(getValidateLocaleIOSCode('zh')).toEqual('zh') expect(getValidateLocaleIOSCode('ZH')).toEqual('zh') expect(getValidateLocaleIOSCode('cn')).toEqual('zh-CN') expect(getValidateLocaleIOSCode('en')).toEqual('en') expect(getValidateLocaleIOSCode('TW')).toEqual('zh-TW') }) test('getLocaleByPath', () => { expect(getLocaleByPath('pattern', 'vi')).toEqual(locale.en.pattern) expect(getLocaleByPath('pattern')).toEqual(locale.en.pattern) }) test('getValidateLocale', () => { setValidateLanguage('vi') expect(getValidateLocale('pattern')).toEqual(locale.en.pattern) }) ``` -------------------------------------------------------------------------------- /devtools/chrome-extension/src/app/components/LeftPanel.tsx: -------------------------------------------------------------------------------- ```typescript import React, { useState } from 'react' import { Tabs } from './Tabs' import { FieldTree } from './FieldTree' import styled from 'styled-components' export const LeftPanel = styled(({ className, dataSource, onSelect }) => { const [current, setCurrent] = useState(0) return ( <div className={className}> <Tabs dataSource={dataSource} current={current} onChange={(index) => { setCurrent(index) onSelect({ current: index, key: '', }) }} /> <FieldTree dataSource={dataSource[current]} onSelect={(node) => { if (onSelect) { onSelect({ current, key: node.path, }) } }} /> </div> ) })` width: 50%; min-width: 50%; ` ``` -------------------------------------------------------------------------------- /packages/vue/docs/demos/questions/named-slot.vue: -------------------------------------------------------------------------------- ```vue <template> <FormProvider :form="form"> <SchemaField :schema="schema" /> </FormProvider> </template> <script> import { Input, Icon } from 'ant-design-vue' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/vue' import 'ant-design-vue/dist/antd.css' const { SchemaField } = createSchemaField({ components: { Input, }, }) const PrefixIcon = { functional: true, render(h) { return h(Icon, { props: { type: 'user' } }) }, } const schema = { type: 'object', properties: { input: { type: 'string', 'x-component': 'Input', 'x-content': { prefix: PrefixIcon, }, }, }, } export default { components: { FormProvider, SchemaField }, data() { return { form: createForm(), schema, } }, } </script> ``` -------------------------------------------------------------------------------- /packages/react/docs/api/hooks/useForm.md: -------------------------------------------------------------------------------- ```markdown # useForm ## Description Mainly read the current [Form](https://core.formilyjs.org/api/models/form) instance in the custom component to implement some side-effect dependencies, such as relying on the errors information of the Form, for Implement some more complex scenario-based components ## Signature ```ts interface useForm { (): Form } ``` ## Example ```tsx import React from 'react' import { createForm } from '@formily/core' import { FormProvider, Field, useForm, observer } from '@formily/react' import { Input } from 'antd' const form = createForm() const Custom = observer(() => { const form = useForm() return <div>{form.values.input}</div> }) export default () => ( <FormProvider form={form}> <Field name="input" component={[Input]} /> <Field name="custom" component={[Custom]} /> </FormProvider> ) ``` ``` -------------------------------------------------------------------------------- /packages/core/docs/guide/architecture.md: -------------------------------------------------------------------------------- ```markdown # Core Architecture ## Domain Model Formily's kernel architecture is very complicated, because it is necessary to solve a domain-level problem, rather than a single point of specific problem, first go to the architecture diagram:  ## Description From the above figure, we can see that the Formily kernel is actually a @formily/reactive domain model. The actual consumption domain model mainly relies on the @formily/reactive responder mechanism for dependency tracking to consume. We can consume any attribute in the Form/Field/ArrayField/ObjectField/VoidField model in the responder (Reactions). When the dependent attribute changes, the responder will execute repeatedly. So as to realize the Reactive programming model at the form level. ``` -------------------------------------------------------------------------------- /scripts/build-style/helper.ts: -------------------------------------------------------------------------------- ```typescript /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ import { OutputOptions, rollup, RollupOptions } from 'rollup' import postcss from 'rollup-plugin-postcss' import NpmImport from 'less-plugin-npm-import' import resolve from 'rollup-plugin-node-resolve' export const getRollupBasePlugin = () => [ resolve(), postcss({ extract: true, minimize: true, sourceMap: true, // extensions: ['.css', '.less', '.sass'], use: { less: { plugins: [new NpmImport({ prefix: '~' })], javascriptEnabled: true, }, sass: {}, stylus: {}, }, }), ] export const build = async ( rollupConfig: Omit<RollupOptions, 'output'> & { output: OutputOptions } ) => { const { output, ...input } = rollupConfig const bundle = await rollup(input) return bundle.write(output as OutputOptions) } ``` -------------------------------------------------------------------------------- /packages/element/docs/guide/checkbox.md: -------------------------------------------------------------------------------- ```markdown # Checkbox > 复选框 ## Markup Schema 案例 <dumi-previewer demoPath="guide/checkbox/markup-schema" /> ## JSON Schema 案例 <dumi-previewer demoPath="guide/checkbox/json-schema" /> ## Template 案例 <dumi-previewer demoPath="guide/checkbox/template" /> ## API 参考 [https://element.eleme.io/#/zh-CN/component/checkbox](https://element.eleme.io/#/zh-CN/component/checkbox) ### 扩展属性 | 属性名 | 类型 | 描述 | 默认值 | | ---------- | ------------------------------------------------------------------------------------------ | -------- | ------- | | options | [CheckboxProps](https://element.eleme.io/#/zh-CN/component/checkbox#checkbox-attributes)[] | 选项 | [] | | optionType | default/button | 样式类型 | default | ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/input-number/markup-schema.vue: -------------------------------------------------------------------------------- ```vue <template> <FormProvider :form="form"> <SchemaField> <SchemaStringField name="input" title="输入框" x-decorator="FormItem" x-component="InputNumber" :x-component-props="{ style: { width: '240px', }, }" /> </SchemaField> <Submit @submit="log">提交</Submit> </FormProvider> </template> <script> import { createForm } from '@formily/core' import { createSchemaField, FormProvider } from '@formily/vue' import { FormItem, InputNumber, Submit } from '@formily/element' const form = createForm() const fields = createSchemaField({ components: { FormItem, InputNumber, }, }) export default { components: { FormProvider, ...fields, Submit }, data() { return { form, } }, methods: { log(value) { console.log(value) }, }, } </script> ``` -------------------------------------------------------------------------------- /packages/react/docs/api/components/FormConsumer.md: -------------------------------------------------------------------------------- ```markdown --- order: 7 --- # FormConsumer ## Description The form response consumer is specifically used to monitor the data changes of the form model to implement various UI response components. The use method is render props. When the dependent data in the callback function changes, the callback function will be re-rendered ## Signature ```ts type FormConsumer = React.FC<{ children: (form: Form) => React.ReactNode }> ``` Form reference [Form](https://core.formilyjs.org/api/models/form) ## Example ```tsx import React from 'react' import { createForm } from '@formily/core' import { FormProvider, FormConsumer, Field } from '@formily/react' import { Input } from 'antd' const form = createForm() export default () => ( <FormProvider form={form}> <Field name="input" component={[Input]} /> <FormConsumer>{(form) => form.values.input}</FormConsumer> </FormProvider> ) ``` ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/select/template-sync.vue: -------------------------------------------------------------------------------- ```vue <template> <FormProvider :form="form"> <Field name="select" title="选择框" :decorator="[FormItem]" :component="[ Select, { style: { width: '240px', }, }, ]" :dataSource="[ { label: '选项1', value: 1, }, { label: '选项2', value: 2, }, ]" /> <Submit @submit="log">提交</Submit> </FormProvider> </template> <script> import { createForm } from '@formily/core' import { FormProvider, Field } from '@formily/vue' import { FormItem, Select, Submit } from '@formily/element' const form = createForm() export default { components: { FormProvider, Field, Submit }, data() { return { FormItem, Select, form, } }, methods: { log(value) { console.log(value) }, }, } </script> ``` -------------------------------------------------------------------------------- /packages/element/src/__builtins__/shared/portal.ts: -------------------------------------------------------------------------------- ```typescript import { Fragment, h } from '@formily/vue' import { defineComponent, onBeforeUnmount } from 'vue-demi' export interface IPortalProps { id?: string | symbol } const PortalMap = new Map<string | symbol, any>() export const createPortalProvider = (id: string | symbol) => { const Portal = defineComponent({ name: 'ProtalProvider', props: { id: { type: [String, Symbol], default: id, }, }, setup(props) { onBeforeUnmount(() => { const { id } = props if (id && PortalMap.has(id)) { PortalMap.delete(id) } }) }, render() { const { id } = this if (id && !PortalMap.has(id)) { PortalMap.set(id, this) } return h(Fragment, {}, this.$scopedSlots) }, }) return Portal } export function getProtalContext(id: string | symbol) { return PortalMap.get(id) } ``` -------------------------------------------------------------------------------- /packages/antd/src/input/index.tsx: -------------------------------------------------------------------------------- ```typescript import React from 'react' import { connect, mapProps, mapReadPretty } from '@formily/react' import { Input as AntdInput } from 'antd' import { InputProps, TextAreaProps } from 'antd/lib/input' import { PreviewText } from '../preview-text' import { LoadingOutlined } from '@ant-design/icons' type ComposedInput = React.FC<React.PropsWithChildren<InputProps>> & { TextArea?: React.FC<React.PropsWithChildren<TextAreaProps>> } export const Input: ComposedInput = connect( AntdInput, mapProps((props, field) => { return { ...props, suffix: ( <span> {field?.['loading'] || field?.['validating'] ? ( <LoadingOutlined /> ) : ( props.suffix )} </span> ), } }), mapReadPretty(PreviewText.Input) ) Input.TextArea = connect(AntdInput.TextArea, mapReadPretty(PreviewText.Input)) export default Input ``` -------------------------------------------------------------------------------- /packages/react/docs/api/components/Field.md: -------------------------------------------------------------------------------- ```markdown --- order: 0 --- # Field ## Description As @formily/core's [createField](https://core.formilyjs.org/api/models/form#createfield) React implementation, it is a bridge component specifically used to bind ViewModel and input controls, the Field component Property reference [IFieldFactoryProps](https://core.formilyjs.org/api/models/form#ifieldfactoryprops) <Alert> When we use the Field component, we must remember to pass the name attribute. </Alert> ## Signature ```ts type Field = React.FC<React.PropsWithChildren<IFieldFactoryProps>> ``` ## Example ```tsx import React from 'react' import { createForm } from '@formily/core' import { FormProvider, Field } from '@formily/react' import { Input } from 'antd' const form = createForm() export default () => ( <FormProvider form={form}> <Field name="input" component={[Input, { placeholder: 'Please Input' }]} /> </FormProvider> ) ``` ``` -------------------------------------------------------------------------------- /packages/reactive-react/src/shared/gc.ts: -------------------------------------------------------------------------------- ```typescript import { globalThisPolyfill } from './global' const registry: FinalizationRegistry<any> = globalThisPolyfill['FinalizationRegistry'] && new globalThisPolyfill['FinalizationRegistry']((token: any) => token?.clean?.() ) type Token = { clean: () => void } export class GarbageCollector<T extends object = any> { private expireTime: number private request?: ReturnType<typeof setTimeout>; private token: Token constructor(clean?: () => void, expireTime = 10_000) { this.token = { clean, } this.expireTime = expireTime } open(target: T) { if (registry) { registry.register(target, this.token, this.token) } else { this.request = setTimeout(() => { this.token?.clean?.() }, this.expireTime) } } close() { if (registry) { registry.unregister(this.token) } else { clearTimeout(this.request) } } } ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/input/markup-schema.vue: -------------------------------------------------------------------------------- ```vue <template> <FormProvider :form="form"> <SchemaField> <SchemaStringField name="input" title="输入框" x-decorator="FormItem" x-component="Input" /> <SchemaStringField name="textarea" title="文本框" x-decorator="FormItem" x-component="Input.TextArea" /> </SchemaField> <Submit @submit="log">提交</Submit> </FormProvider> </template> <script> import { createForm } from '@formily/core' import { createSchemaField, FormProvider } from '@formily/vue' import { FormItem, Input, Submit } from '@formily/element' const form = createForm() const fields = createSchemaField({ components: { FormItem, Input, }, }) export default { components: { FormProvider, ...fields, Submit }, data() { return { form, } }, methods: { log(value) { console.log(value) }, }, } </script> ``` -------------------------------------------------------------------------------- /packages/reactive/src/checkers.ts: -------------------------------------------------------------------------------- ```typescript const toString = Object.prototype.toString export const isMap = (val: any): val is Map<any, any> => val && val instanceof Map export const isSet = (val: any): val is Set<any> => val && val instanceof Set export const isWeakMap = (val: any): val is WeakMap<any, any> => val && val instanceof WeakMap export const isWeakSet = (val: any): val is WeakSet<any> => val && val instanceof WeakSet export const isFn = (val: any): val is Function => typeof val === 'function' export const isArr = Array.isArray export const isPlainObj = (val: any): val is object => toString.call(val) === '[object Object]' export const isValid = (val: any) => val !== null && val !== undefined export const isCollectionType = (target: any) => { return ( isMap(target) || isWeakMap(target) || isSet(target) || isWeakSet(target) ) } export const isNormalType = (target: any) => { return isPlainObj(target) || isArr(target) } ``` -------------------------------------------------------------------------------- /packages/vue/docs/demos/api/hooks/use-parent-form.vue: -------------------------------------------------------------------------------- ```vue <template> <FormProvider :form="form"> <ObjectField name="object"> <Custom></Custom> </ObjectField> <Custom></Custom> <VoidField name="void"> <Custom></Custom> </VoidField> </FormProvider> </template> <script> import { defineComponent, h } from '@vue/composition-api' import { createForm } from '@formily/core' import { FormProvider, ObjectField, VoidField, useParentForm, } from '@formily/vue' import { observer } from '@formily/reactive-vue' const Custom = observer( defineComponent({ setup() { const formRef = useParentForm() return () => { const form = formRef.value return h('div', {}, [form.displayName]) } }, }) ) export default { components: { FormProvider, ObjectField, VoidField, VoidField, Custom, }, data() { const form = createForm() return { form, } }, } </script> ``` -------------------------------------------------------------------------------- /packages/vue/src/components/FormProvider.ts: -------------------------------------------------------------------------------- ```typescript import { provide, defineComponent, toRef } from 'vue-demi' import { FormSymbol, FieldSymbol, SchemaMarkupSymbol, SchemaSymbol, SchemaExpressionScopeSymbol, SchemaOptionsSymbol, } from '../shared/context' import { IProviderProps, DefineComponent } from '../types' import { useAttach } from '../hooks/useAttach' import { useInjectionCleaner } from '../hooks/useInjectionCleaner' import h from '../shared/h' import { Fragment } from '../shared/fragment' export default defineComponent({ name: 'FormProvider', inheritAttrs: false, props: ['form'], setup(props: IProviderProps, { slots }) { const formRef = useAttach(toRef(props, 'form')) provide(FormSymbol, formRef) useInjectionCleaner([ FieldSymbol, SchemaMarkupSymbol, SchemaSymbol, SchemaExpressionScopeSymbol, SchemaOptionsSymbol, ]) return () => h(Fragment, {}, slots) }, }) as DefineComponent<IProviderProps> ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/transfer/markup-schema.vue: -------------------------------------------------------------------------------- ```vue <template> <FormProvider :form="form"> <SchemaField> <SchemaStringField name="input" title="单选" x-decorator="FormItem" x-component="Transfer" :enum="[ { label: '选项1', key: 1, }, { label: '选项2', key: 2, }, ]" /> </SchemaField> <Submit @submit="log">提交</Submit> </FormProvider> </template> <script> import { createForm } from '@formily/core' import { createSchemaField, FormProvider } from '@formily/vue' import { FormItem, Transfer, Submit } from '@formily/element' const form = createForm() const fields = createSchemaField({ components: { FormItem, Transfer, }, }) export default { components: { FormProvider, ...fields, Submit }, data() { return { form, } }, methods: { log(value) { console.log(value) }, }, } </script> ``` -------------------------------------------------------------------------------- /packages/vue/docs/demos/api/hooks/use-form.vue: -------------------------------------------------------------------------------- ```vue <template> <FormProvider :form="form"> <Space> <Field name="input" :component="[Input]" /> <Field name="custom" :component="[Custom]" /> </Space> </FormProvider> </template> <script> import { defineComponent, h } from '@vue/composition-api' import { createForm } from '@formily/core' import { FormProvider, Field, useForm } from '@formily/vue' import { observer } from '@formily/reactive-vue' import { Input, Space } from 'ant-design-vue' import 'ant-design-vue/dist/antd.css' const Custom = observer( defineComponent({ setup() { const formRef = useForm() return () => { const form = formRef.value return h('div', {}, [form.values.input]) } }, }) ) export default { components: { FormProvider, Field, Space, }, data() { const form = createForm({ validateFirst: true }) return { Input, Custom, form, } }, } </script> ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/radio/markup-schema.vue: -------------------------------------------------------------------------------- ```vue <template> <FormProvider :form="form"> <SchemaField> <SchemaStringField name="input" title="单选" x-decorator="FormItem" x-component="Radio.Group" :enum="[ { label: '选项1', value: 1, }, { label: '选项2', value: 2, }, ]" /> </SchemaField> <Submit @submit="log">提交</Submit> </FormProvider> </template> <script> import { createForm } from '@formily/core' import { createSchemaField, FormProvider } from '@formily/vue' import { FormItem, Radio, Submit } from '@formily/element' const form = createForm() const fields = createSchemaField({ components: { FormItem, Radio, }, }) export default { components: { FormProvider, ...fields, Submit }, data() { return { form, } }, methods: { log(value) { console.log(value) }, }, } </script> ``` -------------------------------------------------------------------------------- /.github/workflows/commitlint.yml: -------------------------------------------------------------------------------- ```yaml # This is a basic workflow to help you get started with Actions name: Check Commit spec # Controls when the action will run. on: # Triggers the workflow on push or pull request events but only for the formily_next branch push: branches: [formily_next] pull_request: branches: [formily_next] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: # A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: # This workflow contains a single job called "build" commitlint: # The type of runner that the job will run on runs-on: ubuntu-latest # Steps represent a sequence of tasks that will be executed as part of the job steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - uses: actions/checkout@v2 with: fetch-depth: 0= - uses: wagoid/commitlint-github-action@v3 ``` -------------------------------------------------------------------------------- /packages/json-schema/src/patches.ts: -------------------------------------------------------------------------------- ```typescript import { isFn, isArr } from '@formily/shared' import { SchemaPatch } from './types' const patches: SchemaPatch[] = [] const polyfills: Record<string, SchemaPatch[]> = {} export const reducePatches = (schema: any) => { return patches.reduce( (buf, patch) => { return patch(buf) }, { ...schema } ) } export const registerPatches = (...args: SchemaPatch[]) => { args.forEach((patch) => { if (isFn(patch)) { patches.push(patch) } }) } export const registerPolyfills = (version: string, patch: SchemaPatch) => { if (version && isFn(patch)) { polyfills[version] = polyfills[version] || [] polyfills[version].push(patch) } } export const enablePolyfills = (versions?: string[]) => { if (isArr(versions)) { versions.forEach((version) => { if (isArr(polyfills[version])) { polyfills[version].forEach((patch) => { registerPatches(patch) }) } }) } } ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/input/json-schema.vue: -------------------------------------------------------------------------------- ```vue <template> <Form :form="form"> <SchemaField :schema="schema" /> <Submit @submit="onSubmit">提交</Submit> </Form> </template> <script> import { createForm } from '@formily/core' import { createSchemaField } from '@formily/vue' import { Form, FormItem, Input, Submit } from '@formily/element' const schema = { type: 'object', properties: { input: { type: 'string', title: '输入框', 'x-decorator': 'FormItem', 'x-component': 'Input', }, textarea: { type: 'string', title: '文本框', 'x-decorator': 'FormItem', 'x-component': 'Input.TextArea', }, }, } const form = createForm() const { SchemaField } = createSchemaField({ components: { FormItem, Input, }, }) export default { components: { Form, SchemaField, Submit }, data() { return { form, schema, } }, methods: { onSubmit(value) { console.log(value) }, }, } </script> ``` -------------------------------------------------------------------------------- /packages/reactive-test-cases-for-react18/src/MySlowList.js: -------------------------------------------------------------------------------- ```javascript import React from 'react' import { observer } from '@formily/reactive-react' // Note: this file is exactly the same in both examples. function ListItem({ children }) { let now = performance.now() while (performance.now() - now < 10) { // Note: this is an INTENTIONALLY EMPTY loop that // DOES NOTHING for 3 milliseconds for EACH ITEM. // // It's meant to emulate what happens in a deep // component tree with calculations and other // work performed inside components that can't // trivially be optimized or removed. } return <div className="ListItem">{children}</div> } export default observer(function MySlowList({ text }) { let items = [] for (let i = 0; i < 50; i++) { items.push( <ListItem key={i}>{'Result ' + i + ' for ' + text.text}</ListItem> ) } return ( <> <p> <b>Results for "{text.text}":</b> </p> <ul className="List">{items}</ul> </> ) }) ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/transfer/json-schema.vue: -------------------------------------------------------------------------------- ```vue <template> <Form :form="form" label-align="left" :label-width="160"> <SchemaField :schema="schema" /> <Submit @submit="onSubmit">提交</Submit> </Form> </template> <script> import { createForm } from '@formily/core' import { createSchemaField } from '@formily/vue' import { Form, FormItem, Transfer, Submit } from '@formily/element' const schema = { type: 'object', properties: { transfer: { type: 'array', title: '穿梭框', enum: [ { label: '选项1', key: 1 }, { label: '选项2', key: 2 }, ], 'x-decorator': 'FormItem', 'x-component': 'Transfer', }, }, } const form = createForm() const { SchemaField } = createSchemaField({ components: { FormItem, Transfer, }, }) export default { components: { Form, SchemaField, Submit }, data() { return { form, schema, } }, methods: { onSubmit(value) { console.log(value) }, }, } </script> ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/input-number/json-schema.vue: -------------------------------------------------------------------------------- ```vue <template> <FormProvider :form="form"> <SchemaField :schema="schema" /> <Submit @submit="onSubmit">提交</Submit> </FormProvider> </template> <script> import { createForm } from '@formily/core' import { createSchemaField, FormProvider } from '@formily/vue' import { FormItem, InputNumber, Submit } from '@formily/element' const schema = { type: 'object', properties: { inputNumber: { type: 'number', title: '输入框', 'x-decorator': 'FormItem', 'x-component': 'InputNumber', 'x-component-props': { style: { width: '240px', }, }, }, }, } const form = createForm() const { SchemaField } = createSchemaField({ components: { FormItem, InputNumber, }, }) export default { components: { FormProvider, SchemaField, Submit }, data() { return { form, schema, } }, methods: { onSubmit(value) { console.log(value) }, }, } </script> ``` -------------------------------------------------------------------------------- /packages/reactive/src/__tests__/array.spec.ts: -------------------------------------------------------------------------------- ```typescript import { toArray, ArraySet } from '../array' test('toArray', () => { expect(toArray([])).toEqual([]) expect(toArray(null)).toEqual([]) expect(toArray(undefined)).toEqual([]) expect(toArray(0)).toEqual([0]) }) test('ArraySet', () => { const set = new ArraySet() set.add(11) set.add(11) set.add(22) expect(set.value).toEqual([11, 22]) const handler = jest.fn() set.forEach(handler) expect(handler).toBeCalledTimes(2) expect(handler).nthCalledWith(1, 11) expect(handler).nthCalledWith(2, 22) expect(set.value).toEqual([11, 22]) expect(set.has(11)).toBe(true) set.delete(11) expect(set.has(11)).toBe(false) expect(set.value).toEqual([22]) set.clear() expect(set.value).toEqual([]) const handler1 = jest.fn() set.add(11) set.add(22) set.batchDelete(handler1) expect(handler1).toBeCalledTimes(2) expect(handler1).nthCalledWith(1, 11) expect(handler1).nthCalledWith(2, 22) expect(set.value).toEqual([]) }) ``` -------------------------------------------------------------------------------- /packages/react/src/components/RecordScope.tsx: -------------------------------------------------------------------------------- ```typescript import React from 'react' import { lazyMerge } from '@formily/shared' import { ExpressionScope } from './ExpressionScope' import { ReactFC, IRecordScopeProps } from '../types' import { useExpressionScope } from '../hooks' export const RecordScope: ReactFC<IRecordScopeProps> = (props) => { const scope = useExpressionScope() return ( <ExpressionScope value={{ get $lookup() { return scope?.$record }, get $record() { const record = props.getRecord?.() if (typeof record === 'object') { return lazyMerge(record, { get $lookup() { return scope?.$record }, get $index() { return props.getIndex?.() }, }) } return record }, get $index() { return props.getIndex?.() }, }} > {props.children} </ExpressionScope> ) } ``` -------------------------------------------------------------------------------- /packages/json-schema/src/__tests__/shared.spec.ts: -------------------------------------------------------------------------------- ```typescript import { isNoNeedCompileObject, createDataSource } from '../shared' import { observable } from '@formily/reactive' import { Schema } from '../schema' test('isNoNeedCompileObject', () => { expect(isNoNeedCompileObject({})).toBeFalsy() expect(isNoNeedCompileObject({ $$typeof: null, _owner: null })).toBeTruthy() expect(isNoNeedCompileObject({ _isAMomentObject: true })).toBeTruthy() expect( isNoNeedCompileObject({ [Symbol.for('__REVA_ACTIONS')]: true }) ).toBeTruthy() expect(isNoNeedCompileObject({ toJSON: () => {} })).toBeTruthy() expect(isNoNeedCompileObject({ toJS: () => {} })).toBeTruthy() expect(isNoNeedCompileObject(observable({}))).toBeTruthy() expect(isNoNeedCompileObject(new Schema({}))).toBeTruthy() }) test('createDataSource', () => { expect(createDataSource(['111'])).toEqual([{ label: '111', value: '111' }]) expect(createDataSource([{ label: '111', value: '111' }])).toEqual([ { label: '111', value: '111' }, ]) }) ``` -------------------------------------------------------------------------------- /packages/reactive/docs/guide/index.md: -------------------------------------------------------------------------------- ```markdown # Introduction The core idea of @formily/reactive is to refer to [Mobx](https://mobx.js.org/), so why reinvent the wheel? There are four main reasons: - mobx does not support dependency collection within actions - The observable function of mobx does not support filtering special objects such as react node, moment, and immutable - mobx's observable function will automatically turn the function into action - The observer of mobx-react-lite does not support React concurrent rendering For the above reasons, Formily had to reinvent the wheel, but the wheel strongly relies on Proxy, that is, it does not support IE browser. Of course, reinventing the wheel also has its advantages: - More controllability, you can do more in-depth optimization and customization for formily scenes - Regardless of the historical burden of Mobx, the code can be cleaner - If the Mobx version is Break Change or there are security vulnerabilities, it will have no impact on Formily ``` -------------------------------------------------------------------------------- /devtools/chrome-extension/config/webpack.prod.ts: -------------------------------------------------------------------------------- ```typescript import baseConfig from './webpack.base' import HtmlWebpackPlugin from 'html-webpack-plugin' import path from 'path' const createPages = (pages) => { return pages.map(({ filename, template, chunk }) => { return new HtmlWebpackPlugin({ filename, template, inject: 'body', chunks: [chunk], }) }) } module.exports = { ...baseConfig, mode: 'production', plugins: [ ...createPages([ { filename: 'popup.html', template: path.resolve(__dirname, '../src/extension/views/popup.ejs'), chunk: 'popup', }, { filename: 'devtools.html', template: path.resolve( __dirname, '../src/extension/views/devtools.ejs' ), chunk: 'devtools', }, { filename: 'devpanel.html', template: path.resolve( __dirname, '../src/extension/views/devpanel.ejs' ), chunk: 'devpanel', }, ]), ], } ``` -------------------------------------------------------------------------------- /packages/element/src/editable/style.scss: -------------------------------------------------------------------------------- ```scss @import '../__builtins__/styles/common.scss'; $editable-prefix-cls: '#{$formily-prefix}-editable'; .#{$editable-prefix-cls} { cursor: pointer; display: inline-block !important; .#{$formily-prefix}-form-text { .#{$formily-prefix}-tag { transition: none !important; } .#{$formily-prefix}-tag:last-child { margin-right: 0 !important; } } &-content { display: flex; align-items: center; > * { margin-right: 3px; &:last-child { margin-right: 0; } } } .#{$editable-prefix-cls}-edit-btn, .#{$editable-prefix-cls}-close-btn { transition: all 0.25s ease-in-out; &:hover { color: $--color-primary; } } .#{$formily-prefix}-form-text { display: flex; align-items: center; } .#{$editable-prefix-cls}-preview { white-space: nowrap; text-overflow: ellipsis; overflow: hidden; word-break: break-all; max-width: 100px; display: block; } } ``` -------------------------------------------------------------------------------- /docs/site/Contributors.tsx: -------------------------------------------------------------------------------- ```typescript import React, { useEffect, useState } from 'react' import './Contributors.less' export const Contributors: React.FC = () => { const [contributors, setContributors] = useState([]) useEffect(() => { fetch('//formilyjs.org/.netlify/functions/contributors') .then((res) => res.json()) .then(({ data }) => { setContributors(data) }) }, []) return ( <div className="contri-list"> {contributors.map((user, key) => ( <div className="contri-user" key={key}> <a className="contri-user-avatar" href={user.html_url} target="_blank" rel="noreferrer" > <img src={user.avatar_url} /> </a> <div className="contri-user-info"> <a href={user.html_url} target="_blank" rel="noreferrer"> <div className="contri-user-name">{user.login}</div> </a> </div> </div> ))} </div> ) } ``` -------------------------------------------------------------------------------- /packages/next/src/select/index.tsx: -------------------------------------------------------------------------------- ```typescript import { connect, mapReadPretty, mapProps } from '@formily/react' import { isVoidField } from '@formily/core' import { Select as NextSelect } from '@alifd/next' import { PreviewText } from '../preview-text' import { mapSize, mapStatus } from '../__builtins__' const patchDataSource = (dataSource: any = []) => { const removeEmptyChildren = (data: any) => { const result = { ...data } if (!result.children || result.children.length === 0) { delete result.children } else { result.children = result.children.map(removeEmptyChildren) } return result } return dataSource.map(removeEmptyChildren) } export const Select = connect( NextSelect, mapProps( (props, field) => { if (isVoidField(field)) { return props } return { ...props, dataSource: patchDataSource(props.dataSource ?? field?.dataSource), } }, mapSize, mapStatus ), mapReadPretty(PreviewText.Select) ) export default Select ``` -------------------------------------------------------------------------------- /packages/next/src/time-picker/index.tsx: -------------------------------------------------------------------------------- ```typescript import moment from 'moment' import { connect, mapProps, mapReadPretty } from '@formily/react' import { TimePicker as NextTimePicker } from '@alifd/next' import { TimePickerProps } from '@alifd/next/lib/time-picker' import { PreviewText } from '../preview-text' import { formatMomentValue, momentable, mapSize, mapStatus, } from '../__builtins__' const mapTimeFormat = function () { return (props: any) => { const format = props['format'] || 'HH:mm:ss' const onChange = props.onChange return { ...props, format, value: momentable(props.value, format), onChange: (value: moment.Moment | moment.Moment[]) => { if (onChange) { onChange(formatMomentValue(value, format)) } }, } } } export const TimePicker: React.FC<React.PropsWithChildren<TimePickerProps>> = connect( NextTimePicker, mapProps(mapTimeFormat(), mapSize, mapStatus), mapReadPretty(PreviewText.TimePicker) ) export default TimePicker ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/checkbox/markup-schema.vue: -------------------------------------------------------------------------------- ```vue <template> <Form :form="form"> <SchemaField> <SchemaBooleanField name="single" title="是否确认" x-decorator="FormItem" x-component="Checkbox" /> <SchemaArrayField name="multiple" title="复选" :enum="[ { label: '选项1', value: 1 }, { label: '选项2', value: 2 }, ]" x-decorator="FormItem" x-component="Checkbox.Group" /> </SchemaField> <Submit @submit="onSubmit">提交</Submit> </Form> </template> <script> import { createForm } from '@formily/core' import { createSchemaField } from '@formily/vue' import { Form, FormItem, Checkbox, Submit } from '@formily/element' const form = createForm() const fields = createSchemaField({ components: { FormItem, Checkbox, }, }) export default { components: { Form, ...fields, Submit }, data() { return { form, } }, methods: { onSubmit(value) { console.log(value) }, }, } </script> ``` -------------------------------------------------------------------------------- /packages/element/src/index.ts: -------------------------------------------------------------------------------- ```typescript import './style' export * from './array-base' export * from './array-table' export * from './cascader' export * from './checkbox' export * from './date-picker' export * from './el-form' export * from './el-form-item' export * from './form' export * from './form-button-group' export * from './form-grid' export * from './form-item' export * from './form-layout' export * from './input' export * from './input-number' export * from './password' export * from './radio' export * from './reset' export * from './select' export * from './space' export * from './submit' export * from './switch' export * from './time-picker' export * from './transfer' export * from './upload' export * from './preview-text' export * from './form-collapse' export * from './form-tab' export * from './form-step' export * from './array-cards' export * from './array-collapse' export * from './array-items' export * from './array-tabs' export * from './editable' export * from './form-dialog' export * from './form-drawer' ``` -------------------------------------------------------------------------------- /packages/reactive-react/src/hooks/useCompatEffect.ts: -------------------------------------------------------------------------------- ```typescript import { useEffect, useRef, EffectCallback, DependencyList } from 'react' import { immediate } from '../shared' const isArr = Array.isArray const isEqualDeps = (target: any, source: any) => { const arrA = isArr(target) const arrB = isArr(source) if (arrA !== arrB) return false if (arrA) { if (target.length !== source.length) return false return target.every((val, index) => val === source[index]) } return target === source } export const useCompatEffect = ( effect: EffectCallback, deps?: DependencyList ) => { const depsRef = useRef<DependencyList>(null) const mountedRef = useRef(false) useEffect(() => { mountedRef.current = true const dispose = effect() return () => { mountedRef.current = false if (!isEqualDeps(depsRef.current, deps)) { if (dispose) dispose() return } immediate(() => { if (mountedRef.current) return if (dispose) dispose() }) } }, deps) depsRef.current = deps } ``` -------------------------------------------------------------------------------- /packages/antd/src/index.ts: -------------------------------------------------------------------------------- ```typescript import './style' export * from './array-base' export * from './array-table' export * from './array-tabs' export * from './array-cards' export * from './array-collapse' export * from './array-items' export * from './form-dialog' export * from './form-drawer' export * from './form' export * from './form-item' export * from './form-layout' export * from './form-step' export * from './form-grid' export * from './form-tab' export * from './form-collapse' export * from './form-button-group' export * from './input' export * from './password' export * from './cascader' export * from './space' export * from './preview-text' export * from './radio' export * from './checkbox' export * from './select' export * from './tree-select' export * from './transfer' export * from './date-picker' export * from './time-picker' export * from './number-picker' export * from './switch' export * from './upload' export * from './submit' export * from './reset' export * from './editable' export * from './select-table' ``` -------------------------------------------------------------------------------- /devtools/chrome-extension/config/webpack.dev.ts: -------------------------------------------------------------------------------- ```typescript import baseConfig from './webpack.base' import HtmlWebpackPlugin from 'html-webpack-plugin' import webpack from 'webpack' import path from 'path' const PORT = 3000 const createPages = (pages) => { return pages.map(({ filename, template, chunk }) => { return new HtmlWebpackPlugin({ filename, template, inject: 'body', chunks: [chunk], }) }) } for (let key in baseConfig.entry) { if (Array.isArray(baseConfig.entry[key])) { baseConfig.entry[key].push( require.resolve('webpack/hot/dev-server'), `${require.resolve('webpack-dev-server/client')}?http://localhost:${PORT}` ) } } module.exports = { ...baseConfig, plugins: [ ...createPages([ { filename: 'index.html', template: path.resolve( __dirname, '../src/extension/views/devtools.ejs' ), chunk: 'demo', }, ]), new webpack.HotModuleReplacementPlugin(), ], devServer: { open: true, port: PORT, }, } ``` -------------------------------------------------------------------------------- /packages/core/docs/api/models/ObjectField.md: -------------------------------------------------------------------------------- ```markdown --- order: 3 --- # ObjectField Call the ObjectField model returned by [createObjectField](/api/models/form#createobjectfield). Because ObjectField is inherited from the [Field](/api/models/field) model, most APIs can refer to the Field model. This document only explains the extension method ## Method ### addProperty #### Description Add attributes to the object and trigger onInput #### Signature ```ts interface addProperty { (key: FormPathPattern, value: any): Promise<void> } ``` ### removeProperty #### Description Remove object properties and trigger onInput #### Signature ```ts interface removeProperty { (key: FormPathPattern): Promise<void> } ``` ### existProperty #### Description Determine whether the attribute exists #### Signature ```ts interface existProperty { (key: FormPathPattern): boolean } ``` ## Types of ### IObjectFieldState The main attributes refer to [IFieldState](/api/models/field#ifieldstate), but the data type of value is required to be an object ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/time-picker/template.vue: -------------------------------------------------------------------------------- ```vue <template> <FormProvider :form="form"> <Field name="time" title="时间" required :decorator="[FormItem]" :component="[ TimePicker, { style: { width: '240px', }, }, ]" /> <Field name="[startTime,endTime]" title="时间范围" :decorator="[FormItem]" :component="[ TimePicker, { isRange: true, style: { width: '240px', }, }, ]" /> <Submit @submit="log">提交</Submit> </FormProvider> </template> <script> import { createForm } from '@formily/core' import { FormProvider, Field } from '@formily/vue' import { FormItem, TimePicker, Submit } from '@formily/element' const form = createForm() export default { components: { FormProvider, Field, Submit }, data() { return { FormItem, TimePicker, form, } }, methods: { log(value) { console.log(value) }, }, } </script> ``` -------------------------------------------------------------------------------- /packages/reactive/docs/api/observe.md: -------------------------------------------------------------------------------- ```markdown # observe ## Description Very different from autorun/reaction/Tracker, using observe will monitor all operations of observable objects, support deep monitoring and shallow monitoring <Alert> Note: The read operation will not be monitored </Alert> ## Signature ```ts type PropertyKey = string | number | symbol type ObservablePath = Array<string | number> type OperationType = | 'add' | 'delete' | 'clear' | 'set' | 'get' | 'iterate' | 'has' interface IChange { key?: PropertyKey path?: ObservablePath object?: object value?: any oldValue?: any type?: OperationType } interface IDispose { (): void } interface observe { ( target: object, observer?: (change: IChange) => void, deep?: boolean //default is true ): IDispose //Release the monitor } ``` ## Example ```ts import { observable, observe } from '@formily/reactive' const obs = observable({ aa: 11, }) const dispose = observe(obs, (change) => { console.log(change) }) obs.aa = 22 dispose() ``` ``` -------------------------------------------------------------------------------- /docs/guide/advanced/async.md: -------------------------------------------------------------------------------- ```markdown # Asynchronous Data Sources Asynchronous data source management, the core is reflected in the dataSource property of the [Field](https://core.formilyjs.org/api/models/field) model. We can modify the dataSource of the Field in effects, or modify the dataSource property in reactions. If the field component (such as Select) has a consumer dataSource property, when the dataSource changes, the corresponding component will automatically re-render. <Alert> Note: If it is a business custom component, please manually map the dataSource to the custom component, you can use <a href="https://react.formilyjs.org/api/shared/connect">connect</a> or <a href="https://react.formilyjs.org/api/shared/observer">observer</a> + <a href="https://react.formilyjs.org/api/hooks/use-field">useField</a> </Alert> Specific cases can refer to: - [Select](https://antd.formilyjs.org/components/select) - [TreeSelect](https://antd.formilyjs.org/components/tree-select) - [Cascader](https://antd.formilyjs.org/components/cascader) ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/checkbox/template.vue: -------------------------------------------------------------------------------- ```vue <template> <Form :form="form"> <Field name="single" title="是否确认" :decorator="[FormItem]" :component="[Checkbox]" /> <ArrayField name="multiple" title="复选" :dataSource="[ { label: '选项1', value: 1 }, { label: '选项2', value: 2 }, ]" :decorator="[FormItem]" :component="[Checkbox.Group, { optionType: 'button' }]" > <template v-slot:option="{ option }"> <div>{{ option.label }}</div> </template> </ArrayField> <Submit @submit="onSubmit">提交</Submit> </Form> </template> <script> import { createForm } from '@formily/core' import { Field, ArrayField } from '@formily/vue' import { Form, FormItem, Checkbox, Submit } from '@formily/element' const form = createForm() export default { components: { Form, Field, ArrayField, Submit }, data() { return { FormItem, Checkbox, form, } }, methods: { onSubmit(value) { console.log(value) }, }, } </script> ``` -------------------------------------------------------------------------------- /packages/reactive/src/environment.ts: -------------------------------------------------------------------------------- ```typescript import { ObservableListener, Reaction, ReactionsMap } from './types' import { ArraySet } from './array' import { DataNode } from './tree' export const ProxyRaw = new WeakMap() export const RawProxy = new WeakMap() export const RawShallowProxy = new WeakMap() export const RawNode = new WeakMap<object, DataNode>() export const RawReactionsMap = new WeakMap<object, ReactionsMap>() export const ReactionStack: Reaction[] = [] export const BatchCount = { value: 0 } export const UntrackCount = { value: 0 } export const BatchScope = { value: false } export const DependencyCollected = { value: false } export const PendingReactions = new ArraySet<Reaction>() export const PendingScopeReactions = new ArraySet<Reaction>() export const BatchEndpoints = new ArraySet<() => void>() export const ObserverListeners = new ArraySet<ObservableListener>() export const MakeObModelSymbol = Symbol('MakeObModelSymbol') export const ObModelSymbol = Symbol('ObModelSymbol') export const ObModelNodeSymbol = Symbol('ObModelNodeSymbol') ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/reset/base.vue: -------------------------------------------------------------------------------- ```vue <template> <FormProvider :form="form"> <SchemaField> <SchemaStringField required name="input1" title="输入框" x-decorator="FormItem" x-component="Input" /> <SchemaStringField required title="输入框" name="input2" x-decorator="FormItem" x-component="Input" default="123" /> </SchemaField> <FormButtonGroup align-form-item> <Reset>重置</Reset> </FormButtonGroup> </FormProvider> </template> <script> import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/vue' import { FormLayout, Reset, FormButtonGroup, FormItem, Input, } from '@formily/element' const fields = createSchemaField({ components: { FormItem, Input, }, }) export default { components: { FormProvider, FormLayout, Reset, FormButtonGroup, ...fields, }, data() { const form = createForm() return { form, } }, } </script> ``` -------------------------------------------------------------------------------- /packages/antd/src/editable/style.less: -------------------------------------------------------------------------------- ``` @root-entry-name: 'default'; @import (reference) '~antd/es/style/themes/index.less'; @editable-prefix-cls: ~'@{ant-prefix}-formily-editable'; .@{editable-prefix-cls} { cursor: pointer; display: inline-block !important; .@{ant-prefix}-form-text { .@{ant-prefix}-tag { transition: none !important; } .@{ant-prefix}-tag:last-child { margin-right: 0 !important; } } &-content { display: flex; align-items: center; > * { margin-right: 3px; &:last-child { margin-right: 0; } } } .@{editable-prefix-cls}-edit-btn, .@{editable-prefix-cls}-close-btn { transition: all 0.25s ease-in-out; color: #aaa; font-size: 12px; &:hover { color: @primary-5; } } .@{ant-prefix}-form-text { display: flex; align-items: center; } .@{editable-prefix-cls}-preview { white-space: nowrap; text-overflow: ellipsis; overflow: hidden; word-break: break-all; max-width: 100px; display: block; } } ``` -------------------------------------------------------------------------------- /packages/react/docs/api/components/ExpressionScope.zh-CN.md: -------------------------------------------------------------------------------- ```markdown --- order: 8 --- # ExpressionScope ## 描述 用于自定义组件内部给 json-schema 表达式传递局部作用域 ## 签名 ```ts interface IExpressionScopeProps { value?: any } type ExpressionScope = React.FC<React.PropsWithChildren<IExpressionScopeProps>> ``` ## 用例 ```tsx import React from 'react' import { createForm } from '@formily/core' import { FormProvider, createSchemaField, ExpressionScope, } from '@formily/react' import { Input } from 'antd' const form = createForm() const Container = (props) => { return ( <ExpressionScope value={{ $innerScope: 'this inner scope value' }}> {props.children} </ExpressionScope> ) } const SchemaField = createSchemaField({ components: { Container, Input, }, }) export default () => ( <FormProvider form={form}> <SchemaField> <SchemaField.Void x-component="Container"> <SchemaField.String name="input" x-component="Input" x-value="{{$innerScope}}" /> </SchemaField.Void> </SchemaField> </FormProvider> ) ``` ``` -------------------------------------------------------------------------------- /packages/validator/src/validator.ts: -------------------------------------------------------------------------------- ```typescript import { parseValidator } from './parser' import { IValidateResults, Validator, IValidatorOptions } from './types' import { registerValidateFormats, registerValidateLocale, registerValidateRules, } from './registry' import locales from './locale' import formats from './formats' import rules from './rules' registerValidateRules(rules) registerValidateLocale(locales) registerValidateFormats(formats) export const validate = async <Context = any>( value: any, validator: Validator<Context>, options?: IValidatorOptions<Context> ): Promise<IValidateResults> => { const validates = parseValidator(validator, options) const results: IValidateResults = { error: [], success: [], warning: [], } for (let i = 0; i < validates.length; i++) { const result = await validates[i](value, options?.context) const { type, message } = result results[type] = results[type] || [] if (message) { results[type].push(message) if (options?.validateFirst) break } } return results } ``` -------------------------------------------------------------------------------- /devtools/chrome-extension/package.json: -------------------------------------------------------------------------------- ```json { "name": "@formily/chrome-extension", "version": "2.3.7", "private": true, "license": "MIT", "repository": { "type": "git", "url": "git+https://github.com/alibaba/formily.git" }, "types": "esm/index.d.ts", "bugs": { "url": "https://github.com/alibaba/formily/issues" }, "homepage": "https://github.com/alibaba/formily#readme", "engines": { "npm": ">=3.0.0" }, "scripts": { "build:devtools": "cross-env NODE_OPTIONS=--openssl-legacy-provider webpack-cli --config config/webpack.prod.ts", "build:zip": "rimraf package.zip && zip -r package.zip package", "start": "cross-env NODE_OPTIONS=--openssl-legacy-provider webpack-dev-server --config config/webpack.dev.ts" }, "dependencies": { "@formily/core": "2.3.7", "@formily/shared": "2.3.7", "react": "^18.0.0", "react-dom": "^18.0.0", "react-json-view": "^1.19.1", "react-treebeard": "^3.2.4" }, "publishConfig": { "access": "public" }, "gitHead": "2c44ae410a73f02735c63c6430e021a50e21f3ec" } ``` -------------------------------------------------------------------------------- /packages/reactive/docs/api/reaction.md: -------------------------------------------------------------------------------- ```markdown # reaction ## Description Receive a tracker function and a callback response function. If there is observable data in the tracker, the tracker function will be executed repeatedly when the data changes, but the callback execution must be executed when the tracker function return value changes. ## Signature ```ts interface IReactionOptions<T> { name?: string equals?: (oldValue: T, newValue: T) => boolean //Dirty check fireImmediately?: boolean //Is it triggered by default for the first time, bypassing the dirty check } interface reaction<T> { ( tracker: () => T, subscriber?: (newValue: T, oldValue: T) => void, options?: IReactionOptions<T> ): void } ``` ## Example ```ts import { observable, reaction, batch } from '@formily/reactive' const obs = observable({ aa: 1, bb: 2, }) const dispose = reaction(() => { return obs.aa + obs.bb }, console.log) batch(() => { //Won't trigger because the value of obs.aa + obs.bb has not changed obs.aa = 2 obs.bb = 1 }) obs.aa = 4 dispose() ``` ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/reset/force.vue: -------------------------------------------------------------------------------- ```vue <template> <FormProvider :form="form"> <SchemaField> <SchemaStringField required name="input1" title="输入框" x-decorator="FormItem" x-component="Input" /> <SchemaStringField required title="输入框" name="input2" x-decorator="FormItem" x-component="Input" default="123" /> </SchemaField> <FormButtonGroup align-form-item> <Reset forceClear>重置</Reset> </FormButtonGroup> </FormProvider> </template> <script> import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/vue' import { FormLayout, Reset, FormButtonGroup, FormItem, Input, } from '@formily/element' const fields = createSchemaField({ components: { FormItem, Input, }, }) export default { components: { FormProvider, FormLayout, Reset, FormButtonGroup, ...fields, }, data() { const form = createForm() return { form, } }, } </script> ``` -------------------------------------------------------------------------------- /packages/next/src/index.ts: -------------------------------------------------------------------------------- ```typescript import './style' export * from './array-base' export * from './array-table' export * from './array-cards' export * from './array-collapse' export * from './array-items' export * from './form-dialog' export * from './form-drawer' export * from './form-grid' export * from './form-item' export * from './form' export * from './form-layout' export * from './form-step' export * from './form-tab' export * from './form-collapse' export * from './form-button-group' export * from './input' export * from './password' export * from './preview-text' export * from './radio' export * from './checkbox' export * from './select' export * from './cascader' export * from './space' export * from './tree-select' export * from './transfer' export * from './date-picker' export * from './date-picker2' export * from './time-picker' export * from './time-picker2' export * from './number-picker' export * from './switch' export * from './upload' export * from './submit' export * from './reset' export * from './editable' export * from './select-table' ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/select/markup-schema-sync.vue: -------------------------------------------------------------------------------- ```vue <template> <FormProvider :form="form"> <SchemaField> <SchemaStringField name="select" title="选择框" x-decorator="FormItem" x-component="Select" :x-component-props="{ style: { width: '240px', }, }" :enum="[ { label: '选项1', value: 1, }, { label: '选项2', value: 2, }, ]" /> </SchemaField> <Submit @submit="log">提交</Submit> </FormProvider> </template> <script> import { createForm } from '@formily/core' import { createSchemaField, FormProvider } from '@formily/vue' import { FormItem, Select, Submit } from '@formily/element' const form = createForm() const fields = createSchemaField({ components: { FormItem, Select, }, }) export default { components: { FormProvider, ...fields, Submit }, data() { return { form, } }, methods: { log(value) { console.log(value) }, }, } </script> ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/form-layout/template.vue: -------------------------------------------------------------------------------- ```vue <template> <FormProvider :form="form"> <FormLayout :breakpoints="[680]" :layout="['vertical', 'horizontal']" :labelAlign="['left', 'right']" :labelCol="[24, 6]" :wrapperCol="[24, 10]" > <Field name="input" title="输入框" :decorator="[ FormItem, { tooltip: '123', }, ]" :component="[Input]" :required="true" /> <Field name="select" title="选择框" :decorator="[FormItem]" :component="[Select]" :required="true" /> </FormLayout> </FormProvider> </template> <script> import { createForm } from '@formily/core' import { FormProvider, Field } from '@formily/vue' import { FormLayout, FormItem, Input, Select } from '@formily/element' export default { components: { FormProvider, Field, FormLayout }, data() { const form = createForm() return { FormLayout, FormItem, Input, Select, form, } }, } </script> ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/reset/validate.vue: -------------------------------------------------------------------------------- ```vue <template> <FormProvider :form="form"> <SchemaField> <SchemaStringField required name="input1" title="输入框" x-decorator="FormItem" x-component="Input" /> <SchemaStringField required title="输入框" name="input2" x-decorator="FormItem" x-component="Input" default="123" /> </SchemaField> <FormButtonGroup align-form-item> <Reset validate forceClear>重置</Reset> </FormButtonGroup> </FormProvider> </template> <script> import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/vue' import { FormLayout, Reset, FormButtonGroup, FormItem, Input, } from '@formily/element' const fields = createSchemaField({ components: { FormItem, Input, }, }) export default { components: { FormProvider, FormLayout, Reset, FormButtonGroup, ...fields, }, data() { const form = createForm() return { form, } }, } </script> ``` -------------------------------------------------------------------------------- /packages/vue/docs/demos/api/shared/observer.vue: -------------------------------------------------------------------------------- ```vue <template> <FormProvider :form="form"> <Space> <Field name="name" title="Name" required :component="[Input, { placeholder: 'Please Input' }]" /> <FormPreviewer /> </Space> </FormProvider> </template> <script> import { defineComponent, h } from '@vue/composition-api' import { createForm } from '@formily/core' import { FormProvider, Field, useForm } from '@formily/vue' import { observer } from '@formily/reactive-vue' import { Input, Space } from 'ant-design-vue' import 'ant-design-vue/dist/antd.css' const FormPreviewer = observer( defineComponent({ name: 'FormPreviewer', setup() { const formRef = useForm() return () => { const form = formRef.value return h('div', [JSON.stringify(form.values)]) } }, }) ) export default { components: { FormProvider, Field, FormPreviewer, Space, }, data() { const form = createForm({ validateFirst: true }) return { Input, form, } }, } </script> ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/select/json-schema-sync.vue: -------------------------------------------------------------------------------- ```vue <template> <Form :form="form"> <SchemaField :schema="schema" /> <Submit @submit="onSubmit">提交</Submit> </Form> </template> <script> import { createForm } from '@formily/core' import { createSchemaField } from '@formily/vue' import { Form, FormItem, Select, Submit, Reset } from '@formily/element' const schema = { type: 'object', properties: { select: { type: 'string', title: '选择框', enum: [ { label: '选项1', value: 1, }, { label: '选项2', value: 2, }, ], 'x-decorator': 'FormItem', 'x-component': 'Select', 'x-component-props': { style: 'width: 240px;', }, }, }, } const form = createForm() const { SchemaField } = createSchemaField({ components: { FormItem, Select, }, }) export default { components: { Form, SchemaField, Submit, Reset }, data() { return { form, schema, } }, methods: { onSubmit(value) { console.log(value) }, }, } </script> ``` -------------------------------------------------------------------------------- /packages/reactive-react/src/hooks/useCompatFactory.ts: -------------------------------------------------------------------------------- ```typescript import React from 'react' import { GarbageCollector } from '../shared' import { useCompatEffect } from './useCompatEffect' class ObjectToBeRetainedByReact {} function objectToBeRetainedByReactFactory() { return new ObjectToBeRetainedByReact() } export const useCompatFactory = <T extends { dispose: () => void }>( factory: () => T ): T => { const instRef = React.useRef<T>(null) const gcRef = React.useRef<GarbageCollector>() const [objectRetainedByReact] = React.useState( objectToBeRetainedByReactFactory ) if (!instRef.current) { instRef.current = factory() } //StrictMode/ConcurrentMode会导致组件无法正确触发UnMount,所以只能自己做垃圾回收 if (!gcRef.current) { gcRef.current = new GarbageCollector(() => { if (instRef.current) { instRef.current.dispose() } }) gcRef.current.open(objectRetainedByReact) } useCompatEffect(() => { gcRef.current.close() return () => { if (instRef.current) { instRef.current.dispose() instRef.current = null } } }, []) return instRef.current } ``` -------------------------------------------------------------------------------- /packages/validator/package.json: -------------------------------------------------------------------------------- ```json { "name": "@formily/validator", "version": "2.3.7", "license": "MIT", "main": "lib", "module": "esm", "umd:main": "dist/formily.validator.umd.production.js", "unpkg": "dist/formily.validator.umd.production.js", "jsdelivr": "dist/formily.validator.umd.production.js", "jsnext:main": "esm", "repository": { "type": "git", "url": "git+https://github.com/alibaba/formily.git" }, "types": "esm/index.d.ts", "bugs": { "url": "https://github.com/alibaba/formily/issues" }, "homepage": "https://github.com/alibaba/formily#readme", "engines": { "npm": ">=3.0.0" }, "scripts": { "build": "rimraf -rf lib esm dist lib && npm run build:cjs && npm run build:esm && npm run build:umd", "build:cjs": "tsc --project tsconfig.build.json", "build:esm": "tsc --project tsconfig.build.json --module es2015 --outDir esm", "build:umd": "rollup --config" }, "dependencies": { "@formily/shared": "2.3.7" }, "publishConfig": { "access": "public" }, "gitHead": "ac79c196ae9324889aca5e0501146f9b37b04283" } ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/submit/base.vue: -------------------------------------------------------------------------------- ```vue <template> <FormProvider :form="form"> <SchemaField> <SchemaStringField required name="input1" title="输入框" x-decorator="FormItem" x-component="Input" /> <SchemaStringField required title="输入框" name="input2" x-decorator="FormItem" x-component="Input" /> </SchemaField> <FormButtonGroup align-form-item> <Submit :onSubmit="log">提交</Submit> </FormButtonGroup> </FormProvider> </template> <script> import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/vue' import { FormLayout, Submit, FormButtonGroup, FormItem, Input, } from '@formily/element' const fields = createSchemaField({ components: { FormItem, Input } }) export default { components: { FormProvider, FormLayout, Submit, FormButtonGroup, ...fields, }, data() { const form = createForm() return { form, } }, methods: { log(v) { console.log(v) }, }, } </script> ``` -------------------------------------------------------------------------------- /packages/antd/src/reset/index.tsx: -------------------------------------------------------------------------------- ```typescript import React from 'react' import { Button } from 'antd' import { ButtonProps } from 'antd/lib/button' import { IFieldResetOptions, IFormFeedback } from '@formily/core' import { useParentForm } from '@formily/react' export interface IResetProps extends IFieldResetOptions, ButtonProps { onClick?: (e: React.MouseEvent<Element, MouseEvent>) => any onResetValidateSuccess?: (payload: any) => void onResetValidateFailed?: (feedbacks: IFormFeedback[]) => void } export const Reset: React.FC<React.PropsWithChildren<IResetProps>> = ({ forceClear, validate, onResetValidateSuccess, onResetValidateFailed, ...props }) => { const form = useParentForm() return ( <Button {...props} onClick={(e) => { if (props.onClick) { if (props.onClick(e) === false) return } form .reset('*', { forceClear, validate, }) .then(onResetValidateSuccess) .catch(onResetValidateFailed) }} > {props.children} </Button> ) } export default Reset ``` -------------------------------------------------------------------------------- /packages/vue/src/__tests__/utils.spec.ts: -------------------------------------------------------------------------------- ```typescript import { formatVue3VNodeData } from '../utils/formatVNodeData' test('valid formatVNodeData', () => { const onClick = () => {} const ondblclick = () => {} const vNodeData = { class: [{ bar: false }, { 'test-component': true }], style: { border: '4px solid red', padding: '20px', borderRadius: '10px', }, attrs: { id: 'foo', }, props: { value: 'leader', user: { name: '张三', age: 18, sex: 1, }, }, domProps: { innerHTML: 'innerHTML - baz', }, on: { click: onClick, }, nativeOn: { dblclick: ondblclick, }, } const vue3VNodeData = { class: [{ bar: false }, { 'test-component': true }], style: { border: '4px solid red', padding: '20px', borderRadius: '10px', }, id: 'foo', value: 'leader', user: { name: '张三', age: 18, sex: 1, }, innerHTML: 'innerHTML - baz', onClick, ondblclick, } expect(formatVue3VNodeData(vNodeData)).toEqual(vue3VNodeData) }) ``` -------------------------------------------------------------------------------- /packages/core/src/shared/constants.ts: -------------------------------------------------------------------------------- ```typescript export const ReservedProperties = { form: true, parent: true, props: true, caches: true, requests: true, disposers: true, heart: true, graph: true, indexes: true, fields: true, lifecycles: true, componentType: true, componentProps: true, decoratorType: true, decoratorProps: true, } export const ReadOnlyProperties = { address: true, path: true, valid: true, invalid: true, selfValid: true, selfInvalid: true, errors: true, successes: true, warnings: true, validateStatus: true, } const SELF_DISPLAY = 'selfDisplay' const SELF_PATTERN = 'selfPattern' export const MutuallyExclusiveProperties = { pattern: SELF_PATTERN, editable: SELF_PATTERN, readOnly: SELF_PATTERN, readPretty: SELF_PATTERN, disabled: SELF_PATTERN, display: SELF_DISPLAY, hidden: SELF_DISPLAY, visible: SELF_DISPLAY, } export const RESPONSE_REQUEST_DURATION = 100 export const GlobalState = { lifecycles: [], context: [], effectStart: false, effectEnd: false, initializing: false, } export const NumberIndexReg = /^\.(\d+)/ ``` -------------------------------------------------------------------------------- /devtools/chrome-extension/src/app/components/Tabs.tsx: -------------------------------------------------------------------------------- ```typescript import React from 'react' import styled from 'styled-components' import { toArr } from '@formily/shared' export const Tabs = styled(({ className, dataSource, current, onChange }) => { current = current || 0 return ( <div className={className}> {toArr(dataSource).map((item, index) => { return ( <div className={`tab-item ${current == index ? 'active' : ''}`} key={index} onClick={() => { if (onChange) { onChange(index) } }} > <span>Form#{index + 1}</span> </div> ) })} </div> ) })` height: 36px; border-bottom: 1px solid #3d424a; display: flex; line-height: 36px; width: 100%; overflow: scroll; &::-webkit-scrollbar { display: none; } .tab-item { cursor: pointer; transition: 0.15s all ease-in-out; border-right: 1px solid #3d424a; padding: 0 10px; font-size: 12px; &:hover { background: #1d1f25; } &.active { background: #1d1f25; } } ` ``` -------------------------------------------------------------------------------- /packages/react/docs/api/components/ExpressionScope.md: -------------------------------------------------------------------------------- ```markdown --- order: 8 --- # ExpressionScope ## Description Used to pass local scopes to json-schema expressions inside custom components ## Signature ```ts interface IExpressionScopeProps { value?: any } type ExpressionScope = React.FC<React.PropsWithChildren<IExpressionScopeProps>> ``` ## Example ```tsx import React from 'react' import { createForm } from '@formily/core' import { FormProvider, createSchemaField, ExpressionScope, } from '@formily/react' import { Input } from 'antd' const form = createForm() const Container = (props) => { return ( <ExpressionScope value={{ $innerScope: 'this inner scope value' }}> {props.children} </ExpressionScope> ) } const SchemaField = createSchemaField({ components: { Container, Input, }, }) export default () => ( <FormProvider form={form}> <SchemaField> <SchemaField.Void x-component="Container"> <SchemaField.String name="input" x-component="Input" x-value="{{$innerScope}}" /> </SchemaField.Void> </SchemaField> </FormProvider> ) ``` ``` -------------------------------------------------------------------------------- /packages/next/src/reset/index.tsx: -------------------------------------------------------------------------------- ```typescript import React from 'react' import { Button } from '@alifd/next' import { ButtonProps } from '@alifd/next/lib/button' import { IFormFeedback, IFieldResetOptions } from '@formily/core' import { useParentForm } from '@formily/react' export interface IResetProps extends IFieldResetOptions, ButtonProps { onClick?: (e: React.MouseEvent<Element, MouseEvent>) => any onResetValidateSuccess?: (payload: any) => void onResetValidateFailed?: (feedbacks: IFormFeedback[]) => void } export const Reset: React.FC<React.PropsWithChildren<IResetProps>> = ({ forceClear, validate, onResetValidateFailed, onResetValidateSuccess, ...props }: IResetProps) => { const form = useParentForm() return ( <Button {...props} onClick={(e) => { if (props.onClick) { if (props.onClick(e) === false) return } form .reset('*', { forceClear, validate, }) .then(onResetValidateSuccess) .catch(onResetValidateFailed) }} > {props.children} </Button> ) } export default Reset ``` -------------------------------------------------------------------------------- /packages/antd/src/__builtins__/moment.ts: -------------------------------------------------------------------------------- ```typescript import { isArr, isFn, isEmpty } from '@formily/shared' import moment from 'moment' export const momentable = (value: any, format?: string) => { return Array.isArray(value) ? value.map((val) => moment(val, format)) : value ? moment(value, format) : value } export const formatMomentValue = ( value: any, format: any, placeholder?: string ): string | string[] => { const formatDate = (date: any, format: any, i = 0) => { if (!date) return placeholder const TIME_REG = /^(?:[01]\d|2[0-3]):[0-5]\d(:[0-5]\d)?$/ let _format = format if (isArr(format)) { _format = format[i] } if (isFn(_format)) { return _format(date) } if (isEmpty(_format)) { return date } // moment '19:55:22' 下需要传入第二个参数 if (TIME_REG.test(date)) { return moment(date, _format).format(_format) } return moment(date).format(_format) } if (isArr(value)) { return value.map((val, index) => { return formatDate(val, format, index) }) } else { return value ? formatDate(value, format) : value || placeholder } } ``` -------------------------------------------------------------------------------- /packages/reactive/src/annotations/shallow.ts: -------------------------------------------------------------------------------- ```typescript import { createAnnotation, createObservable } from '../internals' import { bindTargetKeyWithCurrentReaction, runReactionsFromTargetKey, } from '../reaction' import { IObservable } from './observable' export const shallow: IObservable = createAnnotation( ({ target, key, value }) => { const store = { value: createObservable(target, key, target ? target[key] : value, true), } function get() { bindTargetKeyWithCurrentReaction({ target: target, key: key, type: 'get', }) return store.value } function set(value: any) { const oldValue = store.value value = createObservable(target, key, value, true) store.value = value if (oldValue === value) return runReactionsFromTargetKey({ target: target, key: key, type: 'set', oldValue, value, }) } if (target) { Object.defineProperty(target, key, { set, get, enumerable: true, configurable: false, }) return target } return store.value } ) ``` -------------------------------------------------------------------------------- /packages/reactive/src/annotations/observable.ts: -------------------------------------------------------------------------------- ```typescript import { createAnnotation, createObservable } from '../internals' import { bindTargetKeyWithCurrentReaction, runReactionsFromTargetKey, } from '../reaction' export interface IObservable { <T>(target: T): T } export const observable: IObservable = createAnnotation( ({ target, key, value }) => { const store = { value: createObservable(target, key, target ? target[key] : value), } function get() { bindTargetKeyWithCurrentReaction({ target: target, key: key, type: 'get', }) return store.value } function set(value: any) { const oldValue = store.value value = createObservable(target, key, value) store.value = value if (oldValue === value) return runReactionsFromTargetKey({ target: target, key: key, type: 'set', oldValue, value, }) } if (target) { Object.defineProperty(target, key, { set, get, enumerable: true, configurable: false, }) return target } return store.value } ) ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/checkbox/json-schema.vue: -------------------------------------------------------------------------------- ```vue <template> <Form :form="form"> <SchemaField :schema="schema" /> <Submit @submit="onSubmit">提交</Submit> </Form> </template> <script> import { createForm } from '@formily/core' import { createSchemaField } from '@formily/vue' import { Form, FormItem, Checkbox, Submit } from '@formily/element' const schema = { type: 'object', properties: { checkbox: { type: 'number', title: '是否确认', 'x-decorator': 'FormItem', 'x-component': 'Checkbox', }, checkboxGroup: { type: 'array', title: '复选', 'x-decorator': 'FormItem', 'x-component': 'Checkbox.Group', enum: [ { label: '选项1', value: 1, }, { label: '选项2', value: 2, }, ], }, }, } const form = createForm() const { SchemaField } = createSchemaField({ components: { FormItem, Checkbox, }, }) export default { components: { Form, SchemaField, Submit }, data() { return { form, schema, } }, methods: { onSubmit(value) { console.log(value) }, }, } </script> ``` -------------------------------------------------------------------------------- /packages/next/LESENCE.md: -------------------------------------------------------------------------------- ```markdown The MIT License (MIT) Copyright (c) 2015-present, Alibaba Group Holding Limited. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/form-layout/markup-schema.vue: -------------------------------------------------------------------------------- ```vue <template> <FormProvider :form="form"> <SchemaField> <SchemaVoidField x-component="FormLayout" :x-component-props="{ labelCol: 6, wrapperCol: 10, }" > <SchemaStringField name="input" title="输入框" x-decorator="FormItem" :x-decorator-props="{ tooltip: '123', }" x-component="Input" :required="true" /> <SchemaStringField name="select" title="选择框" x-decorator="FormItem" x-component="Select" :required="true" /> </SchemaVoidField> </SchemaField> </FormProvider> </template> <script> import { createForm } from '@formily/core' import { createSchemaField, FormProvider } from '@formily/vue' import { FormLayout, FormItem, Input, Select } from '@formily/element' const fields = createSchemaField({ components: { FormLayout, FormItem, Input, Select }, }) export default { components: { FormProvider, ...fields }, data() { const form = createForm() return { form, } }, } </script> ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/radio/json-schema.vue: -------------------------------------------------------------------------------- ```vue <template> <Form :form="form"> <SchemaField :schema="schema" /> <Submit @submit="onSubmit">提交</Submit> </Form> </template> <script> import { createForm } from '@formily/core' import { createSchemaField } from '@formily/vue' import { Form, FormItem, Radio, Submit } from '@formily/element' const { SchemaField } = createSchemaField({ components: { FormItem, Radio, }, }) export default { components: { Form, SchemaField, Submit }, data() { const schema = { type: 'object', properties: { radio: { type: 'boolean', title: '单选', enum: [ { label: '选项1', value: 1, }, { label: '选项2', value: 2, }, ], 'x-decorator': 'FormItem', 'x-component': 'Radio.Group', 'x-component-props': { optionType: 'button', }, }, }, } const form = createForm() return { form, schema, } }, methods: { onSubmit(value) { console.log(value) }, }, } </script> ``` -------------------------------------------------------------------------------- /packages/react/src/shared/context.ts: -------------------------------------------------------------------------------- ```typescript import React, { createContext } from 'react' import { Form, GeneralField } from '@formily/core' import { Schema } from '@formily/json-schema' import { ISchemaFieldReactFactoryOptions, SchemaReactComponents, } from '../types' const createContextCleaner = <T>(...contexts: React.Context<T>[]) => { return ({ children }) => { return contexts.reduce((buf, ctx) => { return React.createElement(ctx.Provider, { value: undefined }, buf) }, children) } } export const FormContext = createContext<Form>(null) export const FieldContext = createContext<GeneralField>(null) export const SchemaMarkupContext = createContext<Schema>(null) export const SchemaContext = createContext<Schema>(null) export const SchemaExpressionScopeContext = createContext<any>(null) export const SchemaComponentsContext = createContext<SchemaReactComponents>(null) export const SchemaOptionsContext = createContext<ISchemaFieldReactFactoryOptions>(null) export const ContextCleaner = createContextCleaner( FieldContext, SchemaMarkupContext, SchemaContext, SchemaExpressionScopeContext, SchemaComponentsContext, SchemaOptionsContext ) ``` -------------------------------------------------------------------------------- /packages/react/src/shared/render.ts: -------------------------------------------------------------------------------- ```typescript import React, { ReactNode, ReactPortal } from 'react' import { globalThisPolyfill } from '@formily/shared' interface Env { portalDOM?: HTMLDivElement createPortal?: (children: ReactNode, container: Element) => ReactPortal } const env: Env = { portalDOM: globalThisPolyfill?.document?.createElement?.('div'), createPortal: globalThisPolyfill?.['ReactDOM']?.createPortal, } /* istanbul ignore next */ const loadCreatePortal = () => { if (!env.createPortal) { try { // eslint-disable-next-line @typescript-eslint/no-var-requires env.createPortal ??= require('react-dom')?.createPortal } catch {} } if (!env.createPortal) { try { // @ts-ignore import('react-dom') .then((module) => (env.createPortal ??= module?.createPortal)) .catch() } catch {} } } export const render = (element: React.ReactElement) => { if (globalThisPolyfill.navigator?.product === 'ReactNative') return null if (env.portalDOM && env.createPortal) { return env.createPortal(element, env.portalDOM) } else { return React.createElement('template', {}, element) } } loadCreatePortal() ``` -------------------------------------------------------------------------------- /packages/reactive/src/tracker.ts: -------------------------------------------------------------------------------- ```typescript import { ReactionStack } from './environment' import { isFn } from './checkers' import { Reaction } from './types' import { batchEnd, batchStart, disposeBindingReactions, releaseBindingReactions, } from './reaction' export class Tracker { private results: any constructor( scheduler?: (reaction: Reaction) => void, name = 'TrackerReaction' ) { this.track._scheduler = (callback) => { if (this.track._boundary === 0) this.dispose() if (isFn(callback)) scheduler(callback) } this.track._name = name this.track._boundary = 0 } track: Reaction = (tracker: Reaction) => { if (!isFn(tracker)) return this.results if (this.track._boundary > 0) return if (ReactionStack.indexOf(this.track) === -1) { releaseBindingReactions(this.track) try { batchStart() ReactionStack.push(this.track) this.results = tracker() } finally { ReactionStack.pop() this.track._boundary++ batchEnd() this.track._boundary = 0 } } return this.results } dispose = () => { disposeBindingReactions(this.track) } } ``` -------------------------------------------------------------------------------- /packages/grid/package.json: -------------------------------------------------------------------------------- ```json { "name": "@formily/grid", "version": "2.3.7", "license": "MIT", "main": "lib", "module": "esm", "umd:main": "dist/formily.grid.umd.production.js", "unpkg": "dist/formily.grid.umd.production.js", "jsdelivr": "dist/formily.grid.umd.production.js", "jsnext:main": "esm", "repository": { "type": "git", "url": "git+https://github.com/alibaba/formily.git" }, "types": "esm/index.d.ts", "bugs": { "url": "https://github.com/alibaba/formily/issues" }, "homepage": "https://github.com/alibaba/formily#readme", "engines": { "npm": ">=3.0.0" }, "scripts": { "build": "rimraf -rf lib esm dist && npm run build:cjs && npm run build:esm && npm run build:umd", "build:cjs": "tsc --project tsconfig.build.json", "build:esm": "tsc --project tsconfig.build.json --module es2015 --outDir esm", "build:umd": "rollup --config" }, "peerDependencies": { "typescript": "4.x || 5.x" }, "dependencies": { "@formily/reactive": "2.3.7", "@juggle/resize-observer": "^3.3.1" }, "publishConfig": { "access": "public" }, "gitHead": "ac79c196ae9324889aca5e0501146f9b37b04283" } ``` -------------------------------------------------------------------------------- /packages/element/src/__builtins__/shared/create-context.ts: -------------------------------------------------------------------------------- ```typescript import type { Component } from 'vue' import { defineComponent, inject, InjectionKey, provide, readonly, ref, Ref, toRef, } from 'vue-demi' export type CreateContext<T> = { Provider: Component Consumer: Component injectKey: InjectionKey<Ref<T>> } export const createContext = <T>(defaultValue?: T): CreateContext<T> => { const injectKey: InjectionKey<Ref<T>> = Symbol() return { Provider: defineComponent({ name: 'ContextProvider', props: { value: { type: null, default() { return defaultValue ?? null }, }, }, setup(props, { slots }) { const value = toRef(props, 'value') provide(injectKey, readonly(value)) return () => slots?.default?.() }, }), Consumer: defineComponent({ name: 'ContextConsumer', setup(_props, { slots }) { const value = inject(injectKey) return () => slots?.default?.(value) }, }), injectKey, } } export const useContext = <T>(context: CreateContext<T>) => { const key = context.injectKey return inject(key, ref(null)) } ``` -------------------------------------------------------------------------------- /packages/shared/src/defaults.ts: -------------------------------------------------------------------------------- ```typescript import { each } from './array' import { isEmpty, isValid } from './isEmpty' import { getType, isArr, isPlainObj } from './checkers' const isUnNormalObject = (value: any) => { if (value?._owner && value?.$$typeof) { return true } if (value?._isAMomentObject || value?._isJSONSchemaObject) { return true } if (value?.toJS || value?.toJSON) { return true } } const isEnumerableObject = (val: any) => { if (isUnNormalObject(val)) { return false } return typeof val === 'object' } /** * * @param defaults * @param targets */ export const defaults = (defaults_: any, targets: any) => { if ( getType(defaults_) !== getType(targets) || !isEnumerableObject(defaults_) || !isEnumerableObject(targets) ) { return !isEmpty(targets) ? targets : defaults_ } else { const results = isArr(defaults_) ? [] : isPlainObj(defaults_) ? {} : defaults_ each(targets, (value, key) => { results[key] = defaults(defaults_[key], value) }) each(defaults_, (value, key) => { if (!isValid(results[key])) { results[key] = value } }) return results } } ``` -------------------------------------------------------------------------------- /packages/path/package.json: -------------------------------------------------------------------------------- ```json { "name": "@formily/path", "version": "2.3.7", "license": "MIT", "main": "lib", "module": "esm", "umd:main": "dist/formily.path.umd.production.js", "unpkg": "dist/formily.path.umd.production.js", "jsdelivr": "dist/formily.path.umd.production.js", "jsnext:main": "esm", "repository": { "type": "git", "url": "git+https://github.com/alibaba/formily.git" }, "types": "esm/index.d.ts", "bugs": { "url": "https://github.com/alibaba/formily/issues" }, "homepage": "https://github.com/alibaba/formily#readme", "engines": { "npm": ">=3.0.0" }, "scripts": { "start": "dumi dev", "build": "rimraf -rf lib esm dist && npm run build:cjs && npm run build:esm && npm run build:umd", "build:cjs": "tsc --project tsconfig.build.json", "build:esm": "tsc --project tsconfig.build.json --module es2015 --outDir esm", "build:umd": "rollup --config", "build:docs": "dumi build", "benchmark": "ts-node ./benchmark" }, "devDependencies": { "benny": "^3.6.15", "dumi": "^1.1.0-rc.8" }, "publishConfig": { "access": "public" }, "gitHead": "ac79c196ae9324889aca5e0501146f9b37b04283" } ``` -------------------------------------------------------------------------------- /packages/element/docs/.vuepress/components/highlight.js: -------------------------------------------------------------------------------- ```javascript const prism = require('prismjs') const escapeHtml = require('escape-html') const loadLanguages = require('prismjs/components/index') function wrap(code, lang) { if (lang === 'text') { code = escapeHtml(code) } return `<pre v-pre class="language-${lang}"><code>${code}</code></pre>` } function getLangCodeFromExtension(extension) { const extensionMap = { vue: 'markup', html: 'markup', md: 'markdown', rb: 'ruby', ts: 'typescript', py: 'python', sh: 'bash', yml: 'yaml', styl: 'stylus', kt: 'kotlin', rs: 'rust', } return extensionMap[extension] || extension } module.exports = (str, lang) => { if (!lang) { return wrap(str, 'text') } lang = lang.toLowerCase() const rawLang = lang lang = getLangCodeFromExtension(lang) if (!prism.languages[lang]) { try { loadLanguages([lang]) } catch (e) { console.warn( `[vuepress] Syntax highlight for language "${lang}" is not supported.` ) } } if (prism.languages[lang]) { const code = prism.highlight(str, prism.languages[lang], lang) return wrap(code, rawLang) } return wrap(str, 'text') } ``` -------------------------------------------------------------------------------- /packages/reactive-test-cases-for-react18/package.json: -------------------------------------------------------------------------------- ```json { "name": "@formily/reactive-test-cases-for-react18", "version": "2.3.7", "license": "MIT", "private": true, "repository": { "type": "git", "url": "git+https://github.com/alibaba/formily.git" }, "types": "esm/index.d.ts", "bugs": { "url": "https://github.com/alibaba/formily/issues" }, "homepage": "https://github.com/alibaba/formily#readme", "engines": { "npm": ">=3.0.0" }, "scripts": { "start": "webpack-dev-server --config webpack.dev.ts" }, "devDependencies": { "file-loader": "^5.0.2", "html-webpack-plugin": "^3.2.0", "mini-css-extract-plugin": "^1.6.0", "raw-loader": "^4.0.0", "style-loader": "^1.1.3", "ts-loader": "^7.0.4", "webpack": "^4.41.5", "webpack-bundle-analyzer": "^3.9.0", "webpack-cli": "^3.3.10", "webpack-dev-server": "^3.10.1" }, "resolutions": { "react": "next", "react-dom": "next", "react-is": "next" }, "dependencies": { "@formily/reactive": "2.3.7", "@formily/reactive-react": "2.3.7", "react": "next", "react-dom": "next", "react-is": "next" }, "gitHead": "ac79c196ae9324889aca5e0501146f9b37b04283" } ``` -------------------------------------------------------------------------------- /packages/vue/docs/.vuepress/components/highlight.js: -------------------------------------------------------------------------------- ```javascript const prism = require('prismjs') const escapeHtml = require('escape-html') const loadLanguages = require('prismjs/components/index') function wrap(code, lang) { if (lang === 'text') { code = escapeHtml(code) } return `<pre v-pre class="language-${lang}"><code>${code}</code></pre>` } function getLangCodeFromExtension(extension) { const extensionMap = { vue: 'markup', html: 'markup', md: 'markdown', rb: 'ruby', ts: 'typescript', py: 'python', sh: 'bash', yml: 'yaml', styl: 'stylus', kt: 'kotlin', rs: 'rust', } return extensionMap[extension] || extension } module.exports = (str, lang) => { if (!lang) { return wrap(str, 'text') } lang = lang.toLowerCase() const rawLang = lang lang = getLangCodeFromExtension(lang) if (!prism.languages[lang]) { try { loadLanguages([lang]) } catch (e) { console.warn( `[vuepress] Syntax highlight for language "${lang}" is not supported.` ) } } if (prism.languages[lang]) { const code = prism.highlight(str, prism.languages[lang], lang) return wrap(code, rawLang) } return wrap(str, 'text') } ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/time-picker/markup-schema.vue: -------------------------------------------------------------------------------- ```vue <template> <FormProvider :form="form"> <SchemaField> <SchemaStringField name="time" title="时间" required x-decorator="FormItem" x-component="TimePicker" :x-component-props="{ style: { width: '240px', }, }" /> <SchemaStringField name="[startTime, endTime]" title="时间范围" x-decorator="FormItem" x-component="TimePicker" :x-component-props="{ isRange: true, style: { width: '240px', }, }" /> </SchemaField> <Submit @submit="log">提交</Submit> </FormProvider> </template> <script> import { createForm } from '@formily/core' import { createSchemaField, FormProvider } from '@formily/vue' import { FormItem, TimePicker, Submit } from '@formily/element' const form = createForm() const fields = createSchemaField({ components: { FormItem, TimePicker, }, }) export default { components: { FormProvider, ...fields, Submit }, data() { return { form, } }, methods: { log(value) { console.log(value) }, }, } </script> ``` -------------------------------------------------------------------------------- /packages/vue/docs/demos/api/components/recursion-field.vue: -------------------------------------------------------------------------------- ```vue <template> <FormProvider :form="form"> <SchemaField> <SchemaObjectField name="custom" x-component="Custom" :x-component-props="{ schema: { type: 'object', properties: { input: { type: 'string', 'x-component': 'Input', }, }, }, }" /> </SchemaField> </FormProvider> </template> <script> import { Input } from 'ant-design-vue' import { createForm } from '@formily/core' import { FormProvider, createSchemaField, RecursionField } from '@formily/vue' import 'ant-design-vue/dist/antd.css' // functional component in vue2 const Custom = { functional: true, render(h, { props }) { return h(RecursionField, { props: { name: props.name, schema: props.schema, onlyRenderProperties: true, }, }) }, } const { SchemaField, SchemaObjectField } = createSchemaField({ components: { Custom, Input, }, }) export default { components: { FormProvider, SchemaField, SchemaObjectField }, data() { return { form: createForm(), } }, } </script> ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/time-picker/json-schema.vue: -------------------------------------------------------------------------------- ```vue <template> <Form :form="form"> <SchemaField :schema="schema" /> <Submit @submit="onSubmit">提交</Submit> </Form> </template> <script> import { createForm } from '@formily/core' import { createSchemaField } from '@formily/vue' import { Form, FormItem, TimePicker, Submit } from '@formily/element' const schema = { type: 'object', properties: { time: { type: 'string', title: '时间', 'x-decorator': 'FormItem', 'x-component': 'TimePicker', 'x-component-props': { style: { width: '240px', }, }, }, '[startTime,endTime]': { title: '时间范围', 'x-decorator': 'FormItem', 'x-component': 'TimePicker', 'x-component-props': { isRange: true, style: { width: '240px', }, }, type: 'string', }, }, } const form = createForm() const { SchemaField } = createSchemaField({ components: { FormItem, TimePicker, }, }) export default { components: { Form, SchemaField, Submit }, data() { return { form, schema, } }, methods: { onSubmit(value) { console.log(value) }, }, } </script> ``` -------------------------------------------------------------------------------- /packages/benchmark/webpack.dev.ts: -------------------------------------------------------------------------------- ```typescript import baseConfig from './webpack.base' import HtmlWebpackPlugin from 'html-webpack-plugin' import MiniCssExtractPlugin from 'mini-css-extract-plugin' import webpack from 'webpack' import path from 'path' const PORT = 3000 const createPages = (pages) => { return pages.map(({ filename, template, chunk }) => { return new HtmlWebpackPlugin({ filename, template, inject: 'body', chunks: chunk, }) }) } for (const key in baseConfig.entry) { if (Array.isArray(baseConfig.entry[key])) { baseConfig.entry[key].push( require.resolve('webpack/hot/dev-server'), `${require.resolve('webpack-dev-server/client')}?http://localhost:${PORT}` ) } } export default { ...baseConfig, plugins: [ new MiniCssExtractPlugin({ filename: '[name].[hash].css', chunkFilename: '[id].[hash].css', }), ...createPages([ { filename: 'index.html', template: path.resolve(__dirname, './template.ejs'), chunk: ['index'], }, ]), new webpack.HotModuleReplacementPlugin(), // new BundleAnalyzerPlugin() ], devServer: { host: '127.0.0.1', open: true, port: PORT, }, } ``` -------------------------------------------------------------------------------- /packages/element/docs/demos/guide/form-button-group.vue: -------------------------------------------------------------------------------- ```vue <template> <FormProvider :form="form"> <FormLayout :labelCol="6" :wrapperCol="10"> <SchemaField> <SchemaStringField required title="输入框" x-decorator="FormItem" x-component="Input" /> <SchemaStringField required title="输入框" x-decorator="FormItem" x-component="Input" /> </SchemaField> <FormButtonGroup align-form-item> <Submit @submit="log">提交</Submit> <Reset>重置</Reset> </FormButtonGroup> </FormLayout> </FormProvider> </template> <script> import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/vue' import { FormLayout, Submit, Reset, FormButtonGroup, FormItem, Input, Select, } from '@formily/element' const fields = createSchemaField({ components: { FormItem, Input, Select } }) export default { components: { FormProvider, FormLayout, Submit, Reset, FormButtonGroup, ...fields, }, data() { const form = createForm() return { form, } }, methods: { log(v) { console.log(v) }, }, } </script> ``` -------------------------------------------------------------------------------- /packages/reactive-test-cases-for-react18/webpack.dev.ts: -------------------------------------------------------------------------------- ```typescript import baseConfig from './webpack.base' import HtmlWebpackPlugin from 'html-webpack-plugin' import MiniCssExtractPlugin from 'mini-css-extract-plugin' import webpack from 'webpack' import path from 'path' const PORT = 3000 const createPages = (pages) => { return pages.map(({ filename, template, chunk }) => { return new HtmlWebpackPlugin({ filename, template, inject: 'body', chunks: chunk, }) }) } for (const key in baseConfig.entry) { if (Array.isArray(baseConfig.entry[key])) { baseConfig.entry[key].push( require.resolve('webpack/hot/dev-server'), `${require.resolve('webpack-dev-server/client')}?http://localhost:${PORT}` ) } } export default { ...baseConfig, plugins: [ new MiniCssExtractPlugin({ filename: '[name].[hash].css', chunkFilename: '[id].[hash].css', }), ...createPages([ { filename: 'index.html', template: path.resolve(__dirname, './template.ejs'), chunk: ['index'], }, ]), new webpack.HotModuleReplacementPlugin(), // new BundleAnalyzerPlugin() ], devServer: { host: '127.0.0.1', open: true, port: PORT, }, } ```