This is page 13 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/core/docs/index.md:
--------------------------------------------------------------------------------
```markdown
---
title: Formily-Alibaba unified front-end form solution
order: 10
hero:
title: Core Library
desc: Alibaba Unified Form Solution
actions:
- text: Home Site
link: //formilyjs.org
- text: Document
link: /guide
features:
- icon: https://img.alicdn.com/imgextra/i1/O1CN01bHdrZJ1rEOESvXEi5_!!6000000005599-55-tps-800-800.svg
title: High Performance
desc: Efficient update, Demand rendering
- icon: https://img.alicdn.com/imgextra/i3/O1CN0194OqFF1ui6mMT4g7O_!!6000000006070-55-tps-800-800.svg
title: Excellent Reusability
desc: Independent side effects, Pluggable
- icon: https://img.alicdn.com/imgextra/i2/O1CN01QnfYS71E44I1ZpxU9_!!6000000000297-55-tps-800-800.svg
title: Elegant Linkage Writing
desc: Flexible, Complete, Elegant
- icon: https://img.alicdn.com/imgextra/i2/O1CN01YqmcpN1tDalwgyHBH_!!6000000005868-55-tps-800-800.svg
title: Complete domain model
desc: Pure Core, No UI, No Framework
- icon: https://img.alicdn.com/imgextra/i4/O1CN018vDmpl2186xdLu6KI_!!6000000006939-55-tps-800-800.svg
title: Friendly debugging
desc: Natural docking with Formily DevTools
- icon: https://img.alicdn.com/imgextra/i4/O1CN01u6jHgs1ZMwXpjAYnh_!!6000000003181-55-tps-800-800.svg
title: Smart Tips
desc: Embrace Typescript
footer: Open-source MIT Licensed | Copyright © 2019-present<br />Powered by self
---
## Installation
```bash
$ npm install --save @formily/core
```
## Quick start
> The following case is to teach you step by step to implement a form from scratch
>
> @formily/core brings you the following capabilities:
>
> 1. Responsive computing capabilities
> 2. Verification capability, verification internationalization capability
> 3. Value Management Ability
> 4. Linkage management capabilities
> 5. Development tool debugging capabilities, [download Formily Devtools](https://chrome.google.com/webstore/detail/formily-devtools/kkocalmbfnplecdmbadaapgapdioecfm?hl=zh-CN)
```tsx
/**
* defaultShowCode: true
*/
import React, { createContext, useMemo, useContext, useEffect } from 'react'
import { createForm, setValidateLanguage } from '@formily/core'
import { observer } from '@formily/reactive-react'
//Create a context to facilitate Field consumption
const FormContext = createContext()
//Create a context to facilitate the consumption of FormItem
const FieldContext = createContext()
//State bridge component
const Field = observer((props) => {
const form = useContext(FormContext)
//Create a field
const field = form.createField(props)
useEffect(() => {
//Mount field
field.onMount()
return () => {
//Unload field
field.onUnmount()
}
})
if (!field.visible || field.hidden) return null
//Render the field, associate the field state with the UI component
const component = React.createElement(field.component[0], {
...field.component[1],
value: field.value,
onChange: field.onInput,
})
//Render field wrapper
const decorator = React.createElement(
field.decorator[0],
field.decorator[1],
component
)
return (
<FieldContext.Provider value={field}>{decorator}</FieldContext.Provider>
)
})
// FormItem UI component
const FormItem = observer(({ children }) => {
const field = useContext(FieldContext)
return (
<div>
<div style={{ height: 20 }}>{field.title}:</div>
{children}
<div style={{ height: 20, fontSize: 12, color: 'red' }}>
{field.selfErrors.join(',')}
</div>
</div>
)
})
// Input UI component
const Input = (props) => {
return (
<input
{...props}
value={props.value || ''}
style={{
...props.style,
border: '2px solid rgb(186 203 255)',
borderRadius: 6,
width: '100%',
height: 28,
padding: '0 5px',
}}
/>
)
}
//Form management entrance
const FormProvider = (props) => {
useEffect(() => {
//Mount form
props.form?.onMount()
return () => {
//Uninstall the form
props.form?.onUnmount()
}
})
return (
<FormContext.Provider value={props.form}>
{props.children}
</FormContext.Provider>
)
}
//Form response monitor
const FormConsumer = observer((props) => {
const form = useContext(FormContext)
return <div>{props.children(form)}</div>
})
/*
* The above logic has been implemented in @formily/react or @formily/vue, and there is no need to rewrite it in actual use
*/
//Switch the built-in check internationalization copy to English
setValidateLanguage('en')
export default () => {
const form = useMemo(() => createForm({ validateFirst: true }))
const createPasswordEqualValidate = (equalName) => (field) => {
if (
form.values.confirm_password &&
field.value &&
form.values[equalName] !== field.value
) {
field.selfErrors = ['Password does not match Confirm Password.']
} else {
field.selfErrors = []
}
}
return (
<FormProvider form={form}>
<Field
name="name"
title="Name"
required
decorator={[FormItem]}
component={[Input, { placeholder: 'Please Input' }]}
/>
<Field
name="password"
title="Password"
required
decorator={[FormItem]}
component={[Input, { type: 'password', placeholder: 'Please Input' }]}
reactions={createPasswordEqualValidate('confirm_password')}
/>
<Field
name="confirm_password"
title="Confirm Password"
required
decorator={[FormItem]}
component={[Input, { type: 'password', placeholder: 'Please Input' }]}
reactions={createPasswordEqualValidate('password')}
/>
<code>
<pre>
<FormConsumer>
{(form) => JSON.stringify(form.values, null, 2)}
</FormConsumer>
</pre>
</code>
</FormProvider>
)
}
```
```
--------------------------------------------------------------------------------
/packages/element/src/form-collapse/index.ts:
--------------------------------------------------------------------------------
```typescript
import { Collapse, CollapseItem, Badge } from 'element-ui'
import { model } from '@formily/reactive'
import type {
Collapse as CollapseProps,
CollapseItem as CollapseItemProps,
} from 'element-ui'
import {
useField,
useFieldSchema,
RecursionField,
h,
Fragment,
} from '@formily/vue'
import { observer } from '@formily/reactive-vue'
import { Schema, SchemaKey } from '@formily/json-schema'
import { composeExport, stylePrefix } from '../__builtins__'
import { toArr } from '@formily/shared'
import { computed, defineComponent, PropType } from 'vue-demi'
import { GeneralField } from '@formily/core'
type ActiveKeys = string | number | Array<string | number>
type ActiveKey = string | number
type Panels = { name: SchemaKey; props: any; schema: Schema }[]
export interface IFormCollapse {
activeKeys: ActiveKeys
hasActiveKey(key: ActiveKey): boolean
setActiveKeys(key: ActiveKeys): void
addActiveKey(key: ActiveKey): void
removeActiveKey(key: ActiveKey): void
toggleActiveKey(key: ActiveKey): void
}
export interface IFormCollapseProps extends CollapseProps {
formCollapse?: IFormCollapse
activeKey?: ActiveKey
}
const usePanels = (collapseField: GeneralField, schema: Schema) => {
const panels: Panels = []
schema.mapProperties((schema, name) => {
const field = collapseField.query(collapseField.address.concat(name)).take()
if (field?.display === 'none' || field?.display === 'hidden') return
if (schema['x-component']?.indexOf('FormCollapse.Item') > -1) {
panels.push({
name,
props: {
...schema?.['x-component-props'],
key: schema?.['x-component-props']?.key || name,
},
schema,
})
}
})
return panels
}
const createFormCollapse = (defaultActiveKeys?: ActiveKeys) => {
const formCollapse = model({
activeKeys: defaultActiveKeys,
setActiveKeys(keys: ActiveKeys) {
formCollapse.activeKeys = keys
},
hasActiveKey(key: ActiveKey) {
if (Array.isArray(formCollapse.activeKeys)) {
if (formCollapse.activeKeys.includes(key)) {
return true
}
} else if (formCollapse.activeKeys == key) {
return true
}
return false
},
addActiveKey(key: ActiveKey) {
if (formCollapse.hasActiveKey(key)) return
formCollapse.activeKeys = toArr(formCollapse.activeKeys).concat(key)
},
removeActiveKey(key: ActiveKey) {
if (Array.isArray(formCollapse.activeKeys)) {
formCollapse.activeKeys = formCollapse.activeKeys.filter(
(item) => item != key
)
} else {
formCollapse.activeKeys = ''
}
},
toggleActiveKey(key: ActiveKey) {
if (formCollapse.hasActiveKey(key)) {
formCollapse.removeActiveKey(key)
} else {
formCollapse.addActiveKey(key)
}
},
})
return formCollapse
}
const FormCollapse = observer(
defineComponent({
inheritAttrs: false,
props: {
formCollapse: { type: Object as PropType<IFormCollapse> },
activeKey: {
type: [String, Number],
},
},
setup(props, { attrs, emit }) {
const field = useField()
const schema = useFieldSchema()
const prefixCls = `${stylePrefix}-form-collapse`
const _formCollapse = computed(
() => props.formCollapse ?? createFormCollapse()
)
const takeActiveKeys = (panels: Panels) => {
if (props.activeKey) return props.activeKey
if (_formCollapse.value?.activeKeys)
return _formCollapse.value?.activeKeys
if (attrs.accordion) return panels[0]?.name
return panels.map((item) => item.name)
}
const badgedHeader = (key: SchemaKey, props: any) => {
const errors = field.value.form.queryFeedbacks({
type: 'error',
address: `${field.value.address.concat(key)}.*`,
})
if (errors.length) {
return h(
Badge,
{
class: [`${prefixCls}-errors-badge`],
props: {
value: errors.length,
},
},
{ default: () => props.title }
)
}
return props.title
}
return () => {
const panels = usePanels(field.value, schema.value)
const activeKey = takeActiveKeys(panels)
return h(
Collapse,
{
class: prefixCls,
props: {
value: activeKey,
},
on: {
change: (key: string | string[]) => {
emit('input', key)
_formCollapse.value.setActiveKeys(key)
},
},
},
{
default: () => {
return panels.map(({ props, schema, name }, index) => {
return h(
CollapseItem,
{
key: index,
props: {
...props,
name,
},
},
{
default: () => [
h(RecursionField, { props: { schema, name } }, {}),
],
title: () =>
h(
'span',
{},
{ default: () => badgedHeader(name, props) }
),
}
)
})
},
}
)
}
},
})
)
export const FormCollapseItem = defineComponent<CollapseItemProps>({
name: 'FFormCollapseItem',
setup(_props, { slots }) {
return () => h(Fragment, {}, slots)
},
})
const composeFormCollapse = composeExport(FormCollapse, {
Item: FormCollapseItem,
createFormCollapse,
})
export { composeFormCollapse as FormCollapse }
export default composeFormCollapse
```
--------------------------------------------------------------------------------
/packages/antd/docs/components/Editable.zh-CN.md:
--------------------------------------------------------------------------------
```markdown
# Editable
> 局部编辑器,对于一些空间要求较高的表单区域可以使用该组件
>
> Editable 组件相当于是 FormItem 组件的变体,所以通常放在 decorator 中
## Markup Schema 案例
```tsx
import React from 'react'
import {
Input,
DatePicker,
Editable,
FormItem,
FormButtonGroup,
Submit,
} from '@formily/antd'
import { createForm } from '@formily/core'
import { FormProvider, createSchemaField } from '@formily/react'
const SchemaField = createSchemaField({
components: {
DatePicker,
Editable,
Input,
FormItem,
},
})
const form = createForm()
export default () => (
<FormProvider form={form}>
<SchemaField>
<SchemaField.String
name="date"
title="日期"
x-decorator="Editable"
x-component="DatePicker"
/>
<SchemaField.String
name="input"
title="输入框"
x-decorator="Editable"
x-component="Input"
/>
<SchemaField.Void
name="void"
title="虚拟节点容器"
x-component="Editable.Popover"
x-reactions={(field) => {
field.title = field.query('.void.date2').get('value') || field.title
}}
>
<SchemaField.String
name="date2"
title="日期"
x-decorator="FormItem"
x-component="DatePicker"
/>
<SchemaField.String
name="input2"
title="输入框"
x-decorator="FormItem"
x-component="Input"
/>
</SchemaField.Void>
<SchemaField.Object
name="iobject"
title="对象节点容器"
x-component="Editable.Popover"
x-reactions={(field) => {
field.title = field.value?.date || field.title
}}
>
<SchemaField.String
name="date"
title="日期"
x-decorator="FormItem"
x-component="DatePicker"
/>
<SchemaField.String
name="input"
title="输入框"
x-decorator="FormItem"
x-component="Input"
/>
</SchemaField.Object>
</SchemaField>
<FormButtonGroup>
<Submit onSubmit={console.log}>提交</Submit>
</FormButtonGroup>
</FormProvider>
)
```
## JSON Schema 案例
```tsx
import React from 'react'
import {
Input,
DatePicker,
Editable,
FormItem,
FormButtonGroup,
Submit,
} from '@formily/antd'
import { createForm } from '@formily/core'
import { FormProvider, createSchemaField } from '@formily/react'
const SchemaField = createSchemaField({
components: {
DatePicker,
Editable,
Input,
FormItem,
},
})
const form = createForm()
const schema = {
type: 'object',
properties: {
date: {
type: 'string',
title: '日期',
'x-decorator': 'Editable',
'x-component': 'DatePicker',
},
input: {
type: 'string',
title: '输入框',
'x-decorator': 'Editable',
'x-component': 'Input',
},
void: {
type: 'void',
title: '虚拟节点容器',
'x-component': 'Editable.Popover',
'x-reactions':
"{{(field) => field.title = field.query('.void.date2').get('value') || field.title}}",
properties: {
date2: {
type: 'string',
title: '日期',
'x-decorator': 'FormItem',
'x-component': 'DatePicker',
},
input2: {
type: 'string',
title: '输入框',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
iobject: {
type: 'object',
title: '对象节点容器',
'x-component': 'Editable.Popover',
'x-reactions':
'{{(field) => field.title = field.value && field.value.date || field.title}}',
properties: {
date: {
type: 'string',
title: '日期',
'x-decorator': 'FormItem',
'x-component': 'DatePicker',
},
input: {
type: 'string',
title: '输入框',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
},
}
export default () => (
<FormProvider form={form}>
<SchemaField schema={schema} />
<FormButtonGroup>
<Submit onSubmit={console.log}>提交</Submit>
</FormButtonGroup>
</FormProvider>
)
```
## 纯 JSX 案例
```tsx
import React from 'react'
import {
Input,
DatePicker,
Editable,
FormItem,
FormButtonGroup,
Submit,
} from '@formily/antd'
import { createForm } from '@formily/core'
import { FormProvider, Field, VoidField, ObjectField } from '@formily/react'
const form = createForm()
export default () => (
<FormProvider form={form}>
<Field
name="date"
title="日期"
decorator={[Editable]}
component={[DatePicker]}
/>
<Field
name="input"
title="输入框"
decorator={[Editable]}
component={[Input]}
/>
<VoidField
name="void"
title="虚拟节点容器"
reactions={(field) => {
field.title = field.query('.void.date2').get('value') || field.title
}}
component={[Editable.Popover]}
>
<Field
name="date2"
title="日期"
decorator={[FormItem]}
component={[DatePicker]}
/>
<Field
name="input2"
title="输入框"
decorator={[FormItem]}
component={[Input]}
/>
</VoidField>
<ObjectField
name="iobject"
title="对象节点容器"
reactions={(field) => {
field.title = field.value?.date || field.title
}}
component={[Editable.Popover]}
>
<Field
name="date"
title="日期"
decorator={[FormItem]}
component={[DatePicker]}
/>
<Field
name="input"
title="输入框"
decorator={[FormItem]}
component={[Input]}
/>
</ObjectField>
<FormButtonGroup>
<Submit onSubmit={console.log}>提交</Submit>
</FormButtonGroup>
</FormProvider>
)
```
## API
### Editable
> 内联编辑
参考 https://ant.design/components/form-cn/ 中的 FormItem 属性
### Editable.Popover
> 浮层编辑
参考 https://ant.design/components/popover-cn/
```
--------------------------------------------------------------------------------
/packages/react/src/__tests__/schema.json.spec.tsx:
--------------------------------------------------------------------------------
```typescript
import React from 'react'
import { createForm } from '@formily/core'
import { FormProvider, createSchemaField } from '../index'
import { Schema } from '@formily/json-schema'
import { render } from '@testing-library/react'
const Input = ({ value, onChange }) => {
return <input data-testid="input" value={value || ''} onChange={onChange} />
}
import { Button, Rate } from 'antd'
import { SearchOutlined, DollarOutlined } from '@ant-design/icons'
describe('json schema field', () => {
test('string field', () => {
const form = createForm()
const SchemaField = createSchemaField({
components: {
Input,
},
})
const { queryByTestId } = render(
<FormProvider form={form}>
<SchemaField
name="string"
schema={
new Schema({
type: 'string',
default: '123',
'x-component': 'Input',
})
}
/>
</FormProvider>
)
expect(queryByTestId('input')).toBeVisible()
expect(queryByTestId('input')?.getAttribute('value')).toEqual('123')
})
test('object field', () => {
const form = createForm()
const SchemaField = createSchemaField({
components: {
Input,
},
})
const { queryByTestId } = render(
<FormProvider form={form}>
<SchemaField
name="object"
schema={{
type: 'object',
properties: {
string: {
type: 'string',
'x-component': 'Input',
},
},
}}
/>
</FormProvider>
)
expect(queryByTestId('input')).toBeVisible()
})
test('x-component-props children', () => {
const form = createForm()
const Text: React.FC = ({ children }) => {
return <div data-testid="children-test">{children}</div>
}
const SchemaField = createSchemaField({
components: {
Text,
},
})
const { queryByTestId } = render(
<FormProvider form={form}>
<SchemaField
name="object"
schema={{
type: 'object',
properties: {
string: {
type: 'string',
'x-component': 'Text',
'x-component-props': {
children: 'children',
},
},
},
}}
/>
</FormProvider>
)
expect(queryByTestId('children-test')).toBeVisible()
expect(queryByTestId('children-test')?.innerHTML).toEqual('children')
})
test('x-content', async () => {
const form = createForm()
const Text: React.FC = ({ children }) => {
return <div data-testid="content-test">{children}</div>
}
const SchemaField = createSchemaField({
components: {
Text,
},
})
const { queryByTestId } = render(
<FormProvider form={form}>
<SchemaField
name="object"
schema={{
type: 'object',
properties: {
string: {
type: 'string',
'x-component': 'Text',
'x-content': 'content',
},
},
}}
/>
</FormProvider>
)
expect(queryByTestId('content-test')).toBeVisible()
expect(queryByTestId('content-test')?.innerHTML).toEqual('content')
})
test('x-slot-node', () => {
const form = createForm()
const SchemaField = createSchemaField({
components: {
SearchOutlined,
Button,
},
})
const { queryByTestId } = render(
<FormProvider form={form}>
<SchemaField
name="object"
schema={{
type: 'object',
properties: {
icon: {
'x-slot-node': {
target: 'button.x-component-props.icon',
},
'x-component': 'SearchOutlined',
'x-component-props': {
'data-testid': 'icon',
},
},
button: {
type: 'string',
'x-component': 'Button',
'x-component-props': {
'data-testid': 'button',
},
},
},
}}
/>
</FormProvider>
)
const button = queryByTestId('button')
const icon = queryByTestId('icon')
expect(button).toContainElement(icon)
})
test('x-slot-node render prop', async () => {
const form = createForm()
const SchemaField = createSchemaField({
components: {
Rate,
DollarOutlined,
},
})
const { queryByRole, queryAllByTestId } = render(
<FormProvider form={form}>
<SchemaField
name="object"
schema={{
type: 'object',
properties: {
icon: {
'x-slot-node': {
target: 'rate.x-component-props.character',
isRenderProp: true,
},
'x-component': 'DollarOutlined',
'x-component-props': {
'data-testid': 'icon',
rotate: '{{$slotArgs[0].value * 45}}',
style: {
fontSize: '{{`${$slotArgs[0].value * 10}px`}}',
},
},
},
rate: {
'x-component': 'Rate',
'x-component-props': {
defaultValue: 2,
},
},
},
}}
/>
</FormProvider>
)
const rate = queryByRole('radiogroup')
expect(rate).toBeVisible()
const icons = queryAllByTestId('icon')
expect(icons).toHaveLength(10)
icons.forEach((icon) => {
expect(rate).toContainElement(icon)
})
const style = window.getComputedStyle(icons[0])
const fontSize = style.fontSize
expect(fontSize).toBe('20px')
})
})
```
--------------------------------------------------------------------------------
/packages/antd/src/form-dialog/index.tsx:
--------------------------------------------------------------------------------
```typescript
import React, { Fragment, useRef, useLayoutEffect, useState } from 'react'
import { createPortal } from 'react-dom'
import { createForm, IFormProps, Form } from '@formily/core'
import { toJS } from '@formily/reactive'
import { FormProvider, Observer, observer, ReactFC } from '@formily/react'
import {
isNum,
isStr,
isBool,
isFn,
applyMiddleware,
IMiddleware,
} from '@formily/shared'
import { Modal, ModalProps } from 'antd'
import {
usePrefixCls,
loading,
createPortalProvider,
createPortalRoot,
} from '../__builtins__'
type FormDialogRenderer =
| React.ReactElement
| ((form: Form) => React.ReactElement)
type ModalTitle = string | number | React.ReactElement
const isModalTitle = (props: any): props is ModalTitle => {
return (
isNum(props) || isStr(props) || isBool(props) || React.isValidElement(props)
)
}
const getModelProps = (props: any): IModalProps => {
if (isModalTitle(props)) {
return {
title: props,
}
} else {
return props
}
}
export interface IFormDialog {
forOpen(middleware: IMiddleware<IFormProps>): IFormDialog
forConfirm(middleware: IMiddleware<Form>): IFormDialog
forCancel(middleware: IMiddleware<Form>): IFormDialog
open(props?: IFormProps): Promise<any>
close(): void
}
export interface IModalProps extends ModalProps {
onOk?: (event: React.MouseEvent<HTMLElement>) => void | boolean
onCancel?: (event: React.MouseEvent<HTMLElement>) => void | boolean
loadingText?: React.ReactNode
}
export function FormDialog(
title: IModalProps,
id: string,
renderer: FormDialogRenderer
): IFormDialog
export function FormDialog(
title: IModalProps,
renderer: FormDialogRenderer
): IFormDialog
export function FormDialog(
title: ModalTitle,
id: string,
renderer: FormDialogRenderer
): IFormDialog
export function FormDialog(
title: ModalTitle,
renderer: FormDialogRenderer
): IFormDialog
export function FormDialog(title: any, id: any, renderer?: any): IFormDialog {
if (isFn(id) || React.isValidElement(id)) {
renderer = id
id = 'form-dialog'
}
const env = {
host: document.createElement('div'),
form: null,
promise: null,
openMiddlewares: [],
confirmMiddlewares: [],
cancelMiddlewares: [],
}
const root = createPortalRoot(env.host, id)
const props = getModelProps(title)
const modal = {
...props,
afterClose: () => {
props?.afterClose?.()
root.unmount()
},
}
const DialogContent = observer(() => {
return <Fragment>{isFn(renderer) ? renderer(env.form) : renderer}</Fragment>
})
const renderDialog = (
visible = true,
resolve?: () => any,
reject?: () => any
) => {
return (
<Observer>
{() => (
<Modal
{...modal}
visible={visible}
confirmLoading={env.form.submitting}
onCancel={(e) => {
if (modal?.onCancel?.(e) !== false) {
reject()
}
}}
onOk={async (e) => {
if (modal?.onOk?.(e) !== false) {
resolve()
}
}}
>
<FormProvider form={env.form}>
<DialogContent />
</FormProvider>
</Modal>
)}
</Observer>
)
}
document.body.appendChild(env.host)
const formDialog = {
forOpen: (middleware: IMiddleware<IFormProps>) => {
if (isFn(middleware)) {
env.openMiddlewares.push(middleware)
}
return formDialog
},
forConfirm: (middleware: IMiddleware<Form>) => {
if (isFn(middleware)) {
env.confirmMiddlewares.push(middleware)
}
return formDialog
},
forCancel: (middleware: IMiddleware<Form>) => {
if (isFn(middleware)) {
env.cancelMiddlewares.push(middleware)
}
return formDialog
},
open: async (props: IFormProps) => {
if (env.promise) return env.promise
env.promise = new Promise(async (resolve, reject) => {
try {
props = await loading(modal.loadingText, () =>
applyMiddleware(props, env.openMiddlewares)
)
env.form = env.form || createForm(props)
} catch (e) {
reject(e)
}
root.render(() =>
renderDialog(
true,
() => {
env.form
.submit(async () => {
await applyMiddleware(env.form, env.confirmMiddlewares)
resolve(toJS(env.form.values))
formDialog.close()
})
.catch(() => {})
},
async () => {
await loading(modal.loadingText, () =>
applyMiddleware(env.form, env.cancelMiddlewares)
)
formDialog.close()
}
)
)
})
return env.promise
},
close: () => {
if (!env.host) return
root.render(() => renderDialog(false))
},
}
return formDialog
}
const DialogFooter: ReactFC = (props) => {
const ref = useRef<HTMLDivElement>()
const [footer, setFooter] = useState<HTMLDivElement>()
const footerRef = useRef<HTMLDivElement>()
const prefixCls = usePrefixCls('modal')
useLayoutEffect(() => {
const content = ref.current?.closest(`.${prefixCls}-content`)
if (content) {
if (!footerRef.current) {
footerRef.current = content.querySelector(`.${prefixCls}-footer`)
if (!footerRef.current) {
footerRef.current = document.createElement('div')
footerRef.current.classList.add(`${prefixCls}-footer`)
content.appendChild(footerRef.current)
}
}
setFooter(footerRef.current)
}
})
footerRef.current = footer
return (
<div ref={ref} style={{ display: 'none' }}>
{footer && createPortal(props.children, footer)}
</div>
)
}
FormDialog.Footer = DialogFooter
FormDialog.Portal = createPortalProvider('form-dialog')
export default FormDialog
```
--------------------------------------------------------------------------------
/packages/antd/src/form-drawer/index.tsx:
--------------------------------------------------------------------------------
```typescript
import React, { Fragment, useLayoutEffect, useRef, useState } from 'react'
import { createPortal } from 'react-dom'
import {
createForm,
IFormProps,
Form,
onFormSubmitSuccess,
} from '@formily/core'
import { toJS } from '@formily/reactive'
import { FormProvider, observer, ReactFC } from '@formily/react'
import {
isNum,
isStr,
isBool,
isFn,
applyMiddleware,
IMiddleware,
} from '@formily/shared'
import { Drawer, DrawerProps } from 'antd'
import {
usePrefixCls,
createPortalProvider,
createPortalRoot,
loading,
} from '../__builtins__'
type FormDrawerRenderer =
| React.ReactElement
| ((form: Form) => React.ReactElement)
type DrawerTitle = string | number | React.ReactElement
type EventType =
| React.KeyboardEvent<HTMLDivElement>
| React.MouseEvent<HTMLDivElement | HTMLButtonElement>
const isDrawerTitle = (props: any): props is DrawerTitle => {
return (
isNum(props) || isStr(props) || isBool(props) || React.isValidElement(props)
)
}
const getDrawerProps = (props: any): IDrawerProps => {
if (isDrawerTitle(props)) {
return {
title: props,
}
} else {
return props
}
}
export interface IFormDrawer {
forOpen(middleware: IMiddleware<IFormProps>): IFormDrawer
open(props?: IFormProps): Promise<any>
close(): void
}
export interface IDrawerProps extends DrawerProps {
onClose?: (e: EventType) => void | boolean
loadingText?: React.ReactNode
}
export function FormDrawer(
title: IDrawerProps,
id: string,
renderer: FormDrawerRenderer
): IFormDrawer
export function FormDrawer(
title: IDrawerProps,
id: FormDrawerRenderer
): IFormDrawer
export function FormDrawer(
title: DrawerTitle,
id: string,
renderer: FormDrawerRenderer
): IFormDrawer
export function FormDrawer(
title: DrawerTitle,
id: FormDrawerRenderer
): IFormDrawer
export function FormDrawer(title: any, id: any, renderer?: any): IFormDrawer {
if (isFn(id) || React.isValidElement(id)) {
renderer = id
id = 'form-drawer'
}
const env = {
host: document.createElement('div'),
openMiddlewares: [],
form: null,
promise: null,
}
const root = createPortalRoot(env.host, id)
const props = getDrawerProps(title)
const drawer = {
width: '40%',
...props,
onClose: (e: any) => {
if (props?.onClose?.(e) !== false) {
formDrawer.close()
}
},
afterVisibleChange: (visible: boolean) => {
props?.afterVisibleChange?.(visible)
if (visible) return
root.unmount()
},
}
const DrawerContent = observer(() => {
return <Fragment>{isFn(renderer) ? renderer(env.form) : renderer}</Fragment>
})
const renderDrawer = (visible = true) => {
return (
<Drawer {...drawer} visible={visible}>
<FormProvider form={env.form}>
<DrawerContent />
</FormProvider>
</Drawer>
)
}
document.body.appendChild(env.host)
const formDrawer = {
forOpen: (middleware: IMiddleware<IFormProps>) => {
if (isFn(middleware)) {
env.openMiddlewares.push(middleware)
}
return formDrawer
},
open: (props: IFormProps) => {
if (env.promise) return env.promise
env.promise = new Promise(async (resolve, reject) => {
try {
props = await loading(drawer.loadingText, () =>
applyMiddleware(props, env.openMiddlewares)
)
env.form =
env.form ||
createForm({
...props,
effects(form) {
onFormSubmitSuccess(() => {
resolve(toJS(form.values))
formDrawer.close()
})
props?.effects?.(form)
},
})
} catch (e) {
reject(e)
}
root.render(() => renderDrawer(false))
setTimeout(() => {
root.render(() => renderDrawer(true))
}, 16)
})
return env.promise
},
close: () => {
if (!env.host) return
root.render(() => renderDrawer(false))
},
}
return formDrawer
}
const DrawerExtra: ReactFC = (props) => {
const ref = useRef<HTMLDivElement>()
const [extra, setExtra] = useState<HTMLDivElement>()
const extraRef = useRef<HTMLDivElement>()
const prefixCls = usePrefixCls('drawer')
useLayoutEffect(() => {
const content = ref.current
?.closest(`.${prefixCls}-wrapper-body`)
?.querySelector(`.${prefixCls}-header`)
if (content) {
if (!extraRef.current) {
extraRef.current = content.querySelector(`.${prefixCls}-extra`)
if (!extraRef.current) {
extraRef.current = document.createElement('div')
extraRef.current.classList.add(`${prefixCls}-extra`)
content.appendChild(extraRef.current)
}
}
setExtra(extraRef.current)
}
})
extraRef.current = extra
return (
<div ref={ref} style={{ display: 'none' }}>
{extra && createPortal(props.children, extra)}
</div>
)
}
const DrawerFooter: ReactFC = (props) => {
const ref = useRef<HTMLDivElement>()
const [footer, setFooter] = useState<HTMLDivElement>()
const footerRef = useRef<HTMLDivElement>()
const prefixCls = usePrefixCls('drawer')
useLayoutEffect(() => {
const content = ref.current?.closest(`.${prefixCls}-wrapper-body`)
if (content) {
if (!footerRef.current) {
footerRef.current = content.querySelector(`.${prefixCls}-footer`)
if (!footerRef.current) {
footerRef.current = document.createElement('div')
footerRef.current.classList.add(`${prefixCls}-footer`)
content.appendChild(footerRef.current)
}
}
setFooter(footerRef.current)
}
})
footerRef.current = footer
return (
<div ref={ref} style={{ display: 'none' }}>
{footer && createPortal(props.children, footer)}
</div>
)
}
FormDrawer.Extra = DrawerExtra
FormDrawer.Footer = DrawerFooter
FormDrawer.Portal = createPortalProvider('form-drawer')
export default FormDrawer
```
--------------------------------------------------------------------------------
/docs/guide/advanced/destructor.md:
--------------------------------------------------------------------------------
```markdown
# Compatible solution for front-end and back-end data differences
Many times, we always encounter scenarios where the front-end data structure does not match the back-end data structure. The seemingly simple problem is actually very uncomfortable to solve. The most common problems are:
The output of the front-end date range component is an array structure, but the format required by the back-end is to split a flat data structure. This problem is largely limited by the back-end domain model. Because from the perspective of back-end model design, splitting the flat structure is the best solution;
But from the perspective of front-end componentization, the array structure is the best;
So each side has its truth, but unfortunately, it can only cancel such an unequal treaty at the front end every time. However, with Formily, you don’t need to feel uncomfortable for such an embarrassing situation. **Formily provides the ability to deconstruct the path, which can help users quickly solve such problems.** Let's take a look at an example
## Markup Schema Case
```tsx
import React from 'react'
import {
Form,
FormItem,
DatePicker,
FormButtonGroup,
Radio,
Submit,
} from '@formily/antd'
import { createForm, onFieldValueChange } from '@formily/core'
import { createSchemaField, FormConsumer } from '@formily/react'
const SchemaField = createSchemaField({
components: {
FormItem,
DatePicker,
Radio,
},
})
const form = createForm({
effects() {
onFieldValueChange('visible_destructor', (field) => {
form.setFieldState('[startDate,endDate]', (state) => {
state.visible = !!field.value
})
})
},
})
export default () => {
return (
<Form form={form} layout="vertical">
<SchemaField>
<SchemaField.Boolean
name="visible_destructor"
title="Whether to display deconstructed fields"
default={true}
enum={[
{ label: 'yes', value: true },
{ label: 'no', value: false },
]}
x-decorator="FormItem"
x-component="Radio.Group"
/>
<SchemaField.String
name="undestructor"
title="before deconstruction"
x-decorator="FormItem"
x-component="DatePicker.RangePicker"
/>
<SchemaField.String
name="[startDate,endDate]"
title="after deconstruction"
default={['2020-11-20', '2021-12-30']}
x-decorator="FormItem"
x-component="DatePicker.RangePicker"
/>
</SchemaField>
<code>
<pre>
<FormConsumer>
{(form) => JSON.stringify(form.values, null, 2)}
</FormConsumer>
</pre>
</code>
<FormButtonGroup>
<Submit onSubmit={console.log}>submit</Submit>
</FormButtonGroup>
</Form>
)
}
```
## JSON Schema Cases
```tsx
import React from 'react'
import {
Form,
FormItem,
DatePicker,
FormButtonGroup,
Radio,
Submit,
} from '@formily/antd'
import { createForm } from '@formily/core'
import { createSchemaField, FormConsumer } from '@formily/react'
const SchemaField = createSchemaField({
components: {
FormItem,
DatePicker,
Radio,
},
})
const form = createForm()
const schema = {
type: 'object',
properties: {
visible_destructor: {
type: 'boolean',
title: 'Whether to display deconstructed fields',
default: true,
enum: [
{ label: 'yes', value: true },
{ label: 'no', value: false },
],
'x-decorator': 'FormItem',
'x-component': 'Radio.Group',
},
undestructor: {
type: 'string',
title: 'before deconstruction',
'x-decorator': 'FormItem',
'x-component': 'DatePicker.RangePicker',
},
'[startDate,endDate]': {
type: 'string',
title: 'after deconstruction',
default: ['2020-11-20', '2021-12-30'],
'x-decorator': 'FormItem',
'x-component': 'DatePicker.RangePicker',
'x-reactions': {
dependencies: ['visible_destructor'],
fulfill: {
state: {
visible: '{{!!$deps[0]}}',
},
},
},
},
},
}
export default () => {
return (
<Form form={form} layout="vertical">
<SchemaField schema={schema} />
<code>
<pre>
<FormConsumer>
{(form) => JSON.stringify(form.values, null, 2)}
</FormConsumer>
</pre>
</code>
<FormButtonGroup>
<Submit onSubmit={console.log}>submit</Submit>
</FormButtonGroup>
</Form>
)
}
```
## Pure JSX Case
```tsx
import React from 'react'
import {
Form,
FormItem,
DatePicker,
FormButtonGroup,
Radio,
Submit,
} from '@formily/antd'
import { createForm } from '@formily/core'
import { Field, FormConsumer } from '@formily/react'
const form = createForm()
export default () => {
return (
<Form form={form} layout="vertical">
<Field
name="visible_destructor"
title="Whether to display deconstructed fields"
initialValue={true}
dataSource={[
{ label: 'yes', value: true },
{ label: 'no', value: false },
]}
decorator={[FormItem]}
component={[Radio.Group]}
/>
<Field
name="undestructor"
title="before deconstruction"
decorator={[FormItem]}
component={[DatePicker.RangePicker]}
/>
<Field
name="[startDate,endDate]"
title="after deconstruction"
initialValue={['2020-11-20', '2021-12-30']}
decorator={[FormItem]}
component={[DatePicker.RangePicker]}
reactions={(field) => {
field.visible = !!field.query('visible_destructor').value()
}}
/>
<code>
<pre>
<FormConsumer>
{(form) => JSON.stringify(form.values, null, 2)}
</FormConsumer>
</pre>
</code>
<FormButtonGroup>
<Submit onSubmit={console.log}>submit</Submit>
</FormButtonGroup>
</Form>
)
}
```
```
--------------------------------------------------------------------------------
/packages/vue/docs/.vuepress/components/dumi-previewer.vue:
--------------------------------------------------------------------------------
```vue
<template>
<client-only>
<section class="dumi-previewer">
<div class="dumi-previewer-demo">
<template v-if="demoPath && demo">
<component :is="demo" />
</template>
<template v-else>
<slot name="demo"></slot>
</template>
</div>
<div class="dumi-previewer-actions">
<div>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
class="dumi-previewer-actions__icon"
viewBox="0 0 256 296"
@click="openCodeSandBox"
>
<path
d="M115.498 261.088v-106.61L23.814 101.73v60.773l41.996 24.347v45.7l49.688 28.54zm23.814.627l50.605-29.151V185.78l42.269-24.495v-60.011l-92.874 53.621v106.82zm80.66-180.887l-48.817-28.289l-42.863 24.872l-43.188-24.897l-49.252 28.667l91.914 52.882l92.206-53.235zM0 222.212V74.495L127.987 0L256 74.182v147.797l-128.016 73.744L0 222.212z"
fill="#000"
></path>
</svg>
</div>
<div>
<svg
v-if="copied"
class="dumi-previewer-actions__icon"
style="fill: green"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24"
>
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M10 15.172l9.192-9.193 1.415 1.414L10 18l-6.364-6.364 1.414-1.414z"
/>
</svg>
<svg
v-else
class="dumi-previewer-actions__icon"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24"
@click="handleCopy"
>
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M7 6V3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v14a1 1 0 0 1-1 1h-3v3c0 .552-.45 1-1.007 1H4.007A1.001 1.001 0 0 1 3 21l.003-14c0-.552.45-1 1.007-1H7zM5.003 8L5 20h10V8H5.003zM9 6h8v10h2V4H9v2z"
/>
</svg>
<svg
class="dumi-previewer-actions__icon"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24"
@click="handleCollapse"
>
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M24 12l-5.657 5.657-1.414-1.414L21.172 12l-4.243-4.243 1.414-1.414L24 12zM2.828 12l4.243 4.243-1.414 1.414L0 12l5.657-5.657L7.07 7.757 2.828 12zm6.96 9H7.66l6.552-18h2.128L9.788 21z"
/>
</svg>
</div>
</div>
<div v-show="!collapsed" class="dumi-previewer-source">
<div v-html="highlightCode" class="language-vue extra-class" />
</div>
</section>
</client-only>
</template>
<script>
import copy from 'copy-to-clipboard'
import highlight from './highlight'
import { createCodeSandBox } from './createCodeSandBox'
export default {
name: 'dumi-previewer',
props: {
code: {
type: String,
default: '',
},
demoPath: {
type: String,
default: '',
},
},
data() {
return {
collapsed: false,
copied: false,
timerId: null,
demoStr: '',
/**
* take over VuePress render
* 接管VuePress渲染
*/
demo: null,
}
},
computed: {
decodedCode() {
return decodeURIComponent(this.code || this.demoStr)
},
highlightCode() {
return highlight(this.decodedCode, 'vue')
},
},
created() {
if (this.demoPath) {
import(
/* webpackPrefetch: true */ `../../demos/${this.demoPath}.vue`
).then((module) => {
this.demo = module.default
})
import(
/* webpackPrefetch: true */ `!raw-loader!../../demos/${this.demoPath}.vue`
).then((module) => {
this.demoStr = module.default
})
}
},
beforeDestroy() {
clearTimeout(this.timerId)
},
methods: {
handleCollapse() {
this.collapsed = !this.collapsed
},
handleCopy() {
this.copied = true
copy(this.decodedCode)
clearTimeout(this.timer)
this.timerId = setTimeout(() => {
this.copied = false
}, 2000)
},
openCodeSandBox() {
createCodeSandBox(this.demoStr)
},
},
}
</script>
<style lang="stylus">
.dumi-previewer {
background-color: #fff;
border: 1px solid #ebedf1;
border-radius: 1px;
.dumi-previewer-demo {
padding: 40px 24px;
}
.dumi-previewer-actions {
display: flex;
align-items: center;
justify-content: space-between;
border-top: 1px dashed #ebedf1;
height: 40px;
padding: 0 1em;
.dumi-previewer-actions__icon {
width: 16px;
height: 16px;
padding: 8px 4px;
opacity: 0.4;
cursor: pointer;
transition: opacity .3s;
&:hover {
opacity: 0.6;
}
}
}
.dumi-previewer-source {
border-top: 1px dashed #ebedf1;
div[class*="language-"] {
background-color: #f9fafb;
border-radius: 0;
}
pre[class*="language-"] {
margin: 0 !important;
}
pre[class*="language-"] code {
color: #000 !important;
}
.token.cdata,.token.comment,.token.doctype,.token.prolog {
color: #708090
}
.token.punctuation {
color: #999
}
.token.namespace {
opacity: .7
}
.token.boolean,.token.constant,.token.deleted,.token.number,.token.property,.token.symbol,.token.tag {
color: #905
}
.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string {
color: #690
}
.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url {
color: #9a6e3a;
background: hsla(0,0%,100%,.5)
}
.token.atrule,.token.attr-value,.token.keyword {
color: #07a
}
.token.class-name,.token.function {
color: #dd4a68
}
.token.important,.token.regex,.token.variable {
color: #e90
}
}
}
</style>
```
--------------------------------------------------------------------------------
/packages/antd/docs/components/FormStep.zh-CN.md:
--------------------------------------------------------------------------------
```markdown
# FormStep
> 分步表单组件
>
> 注意:该组件只能用在 Schema 场景
## Markup Schema 案例
```tsx
import React from 'react'
import { FormStep, FormItem, Input, FormButtonGroup } from '@formily/antd'
import { createForm } from '@formily/core'
import { FormProvider, FormConsumer, createSchemaField } from '@formily/react'
import { Button } from 'antd'
const SchemaField = createSchemaField({
components: {
FormItem,
FormStep,
Input,
},
})
const form = createForm()
const formStep = FormStep.createFormStep()
export default () => {
return (
<FormProvider form={form}>
<SchemaField>
<SchemaField.Void
x-component="FormStep"
x-component-props={{ formStep }}
>
<SchemaField.Void
x-component="FormStep.StepPane"
x-component-props={{ title: '第一步' }}
>
<SchemaField.String
name="aaa"
x-decorator="FormItem"
required
x-component="Input"
/>
</SchemaField.Void>
<SchemaField.Void
x-component="FormStep.StepPane"
x-component-props={{ title: '第二步' }}
>
<SchemaField.String
name="bbb"
x-decorator="FormItem"
required
x-component="Input"
/>
</SchemaField.Void>
<SchemaField.Void
type="void"
x-component="FormStep.StepPane"
x-component-props={{ title: '第三步' }}
>
<SchemaField.String
name="ccc"
x-decorator="FormItem"
required
x-component="Input"
/>
</SchemaField.Void>
</SchemaField.Void>
</SchemaField>
<FormConsumer>
{() => (
<FormButtonGroup>
<Button
disabled={!formStep.allowBack}
onClick={() => {
formStep.back()
}}
>
上一步
</Button>
<Button
disabled={!formStep.allowNext}
onClick={() => {
formStep.next()
}}
>
下一步
</Button>
<Button
disabled={formStep.allowNext}
onClick={() => {
formStep.submit(console.log)
}}
>
提交
</Button>
</FormButtonGroup>
)}
</FormConsumer>
</FormProvider>
)
}
```
## JSON Schema 案例
```tsx
import React from 'react'
import { FormStep, FormItem, Input, FormButtonGroup } from '@formily/antd'
import { createForm } from '@formily/core'
import { FormProvider, FormConsumer, createSchemaField } from '@formily/react'
import { Button } from 'antd'
const SchemaField = createSchemaField({
components: {
FormItem,
FormStep,
Input,
},
})
const form = createForm()
const formStep = FormStep.createFormStep()
const schema = {
type: 'object',
properties: {
step: {
type: 'void',
'x-component': 'FormStep',
'x-component-props': {
formStep: '{{formStep}}',
},
properties: {
step1: {
type: 'void',
'x-component': 'FormStep.StepPane',
'x-component-props': {
title: '第一步',
},
properties: {
aaa: {
type: 'string',
title: 'AAA',
required: true,
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
step2: {
type: 'void',
'x-component': 'FormStep.StepPane',
'x-component-props': {
title: '第二步',
},
properties: {
bbb: {
type: 'string',
title: 'AAA',
required: true,
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
step3: {
type: 'void',
'x-component': 'FormStep.StepPane',
'x-component-props': {
title: '第三步',
},
properties: {
ccc: {
type: 'string',
title: 'AAA',
required: true,
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
},
},
},
}
export default () => {
return (
<FormProvider form={form}>
<SchemaField schema={schema} scope={{ formStep }} />
<FormConsumer>
{() => (
<FormButtonGroup>
<Button
disabled={!formStep.allowBack}
onClick={() => {
formStep.back()
}}
>
上一步
</Button>
<Button
disabled={!formStep.allowNext}
onClick={() => {
formStep.next()
}}
>
下一步
</Button>
<Button
disabled={formStep.allowNext}
onClick={() => {
formStep.submit(console.log)
}}
>
提交
</Button>
</FormButtonGroup>
)}
</FormConsumer>
</FormProvider>
)
}
```
## API
### FormStep
| 属性名 | 类型 | 描述 | 默认值 |
| -------- | --------- | -------------------------------------------------- | ------ |
| formStep | IFormStep | 传入通过 createFormStep/useFormStep 创建出来的模型 | |
其余参考 https://ant.design/components/steps-cn/
### FormStep.StepPane
参考 https://ant.design/components/steps-cn/ Steps.Step 属性
### FormStep.createFormStep
```ts pure
import { Form } from '@formily/core'
interface createFormStep {
(current?: number): IFormStep
}
interface IFormTab {
//当前索引
current: number
//是否允许向后
allowNext: boolean
//是否允许向前
allowBack: boolean
//设置当前索引
setCurrent(key: number): void
//提交表单
submit: Form['submit']
//向后
next(): void
//向前
back(): void
}
```
```
--------------------------------------------------------------------------------
/packages/next/docs/components/FormStep.zh-CN.md:
--------------------------------------------------------------------------------
```markdown
# FormStep
> 分步表单组件
>
> 注意:该组件只能用在 Schema 场景
## Markup Schema 案例
```tsx
import React from 'react'
import { FormStep, FormItem, Input, FormButtonGroup } from '@formily/next'
import { createForm } from '@formily/core'
import { FormProvider, FormConsumer, createSchemaField } from '@formily/react'
import { Button } from '@alifd/next'
const SchemaField = createSchemaField({
components: {
FormItem,
FormStep,
Input,
},
})
const form = createForm()
const formStep = FormStep.createFormStep()
export default () => {
return (
<FormProvider form={form}>
<SchemaField>
<SchemaField.Void
x-component="FormStep"
x-component-props={{ formStep }}
>
<SchemaField.Void
x-component="FormStep.StepPane"
x-component-props={{ title: '第一步' }}
>
<SchemaField.String
name="aaa"
x-decorator="FormItem"
required
x-component="Input"
/>
</SchemaField.Void>
<SchemaField.Void
x-component="FormStep.StepPane"
x-component-props={{ title: '第二步' }}
>
<SchemaField.String
name="bbb"
x-decorator="FormItem"
required
x-component="Input"
/>
</SchemaField.Void>
<SchemaField.Void
type="void"
x-component="FormStep.StepPane"
x-component-props={{ title: '第三步' }}
>
<SchemaField.String
name="ccc"
x-decorator="FormItem"
required
x-component="Input"
/>
</SchemaField.Void>
</SchemaField.Void>
</SchemaField>
<FormConsumer>
{() => (
<FormButtonGroup>
<Button
disabled={!formStep.allowBack}
onClick={() => {
formStep.back()
}}
>
上一步
</Button>
<Button
disabled={!formStep.allowNext}
onClick={() => {
formStep.next()
}}
>
下一步
</Button>
<Button
disabled={formStep.allowNext}
onClick={() => {
formStep.submit(console.log)
}}
>
提交
</Button>
</FormButtonGroup>
)}
</FormConsumer>
</FormProvider>
)
}
```
## JSON Schema 案例
```tsx
import React from 'react'
import { FormStep, FormItem, Input, FormButtonGroup } from '@formily/next'
import { createForm } from '@formily/core'
import { FormProvider, FormConsumer, createSchemaField } from '@formily/react'
import { Button } from '@alifd/next'
const SchemaField = createSchemaField({
components: {
FormItem,
FormStep,
Input,
},
})
const form = createForm()
const formStep = FormStep.createFormStep()
const schema = {
type: 'object',
properties: {
step: {
type: 'void',
'x-component': 'FormStep',
'x-component-props': {
formStep: '{{formStep}}',
},
properties: {
step1: {
type: 'void',
'x-component': 'FormStep.StepPane',
'x-component-props': {
title: '第一步',
},
properties: {
aaa: {
type: 'string',
title: 'AAA',
required: true,
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
step2: {
type: 'void',
'x-component': 'FormStep.StepPane',
'x-component-props': {
title: '第二步',
},
properties: {
bbb: {
type: 'string',
title: 'AAA',
required: true,
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
step3: {
type: 'void',
'x-component': 'FormStep.StepPane',
'x-component-props': {
title: '第三步',
},
properties: {
ccc: {
type: 'string',
title: 'AAA',
required: true,
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
},
},
},
}
export default () => {
return (
<FormProvider form={form}>
<SchemaField schema={schema} scope={{ formStep }} />
<FormConsumer>
{() => (
<FormButtonGroup>
<Button
disabled={!formStep.allowBack}
onClick={() => {
formStep.back()
}}
>
上一步
</Button>
<Button
disabled={!formStep.allowNext}
onClick={() => {
formStep.next()
}}
>
下一步
</Button>
<Button
disabled={formStep.allowNext}
onClick={() => {
formStep.submit(console.log)
}}
>
提交
</Button>
</FormButtonGroup>
)}
</FormConsumer>
</FormProvider>
)
}
```
## API
### FormStep
| 属性名 | 类型 | 描述 | 默认值 |
| -------- | --------- | -------------------------------------------------- | ------ |
| formStep | IFormStep | 传入通过 createFormStep/useFormStep 创建出来的模型 | |
其余参考 https://fusion.design/pc/component/basic/step
### FormStep.StepPane
参考 https://fusion.design/pc/component/basic/step Steps.Step 属性
### FormStep.createFormStep
```ts pure
import { Form } from '@formily/core'
interface createFormStep {
(current?: number): IFormStep
}
interface IFormTab {
//当前索引
current: number
//是否允许向后
allowNext: boolean
//是否允许向前
allowBack: boolean
//设置当前索引
setCurrent(key: number): void
//提交表单
submit: Form['submit']
//向后
next(): void
//向前
back(): void
}
```
```
--------------------------------------------------------------------------------
/packages/element/docs/.vuepress/components/dumi-previewer.vue:
--------------------------------------------------------------------------------
```vue
<template>
<client-only>
<section class="dumi-previewer">
<div class="dumi-previewer-demo">
<template v-if="demoPath && demo">
<component :is="demo" />
</template>
<template v-else>
<slot name="demo"></slot>
</template>
</div>
<div class="dumi-previewer-actions">
<div>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
class="dumi-previewer-actions__icon"
viewBox="0 0 256 296"
@click="openCodeSandBox"
>
<path
d="M115.498 261.088v-106.61L23.814 101.73v60.773l41.996 24.347v45.7l49.688 28.54zm23.814.627l50.605-29.151V185.78l42.269-24.495v-60.011l-92.874 53.621v106.82zm80.66-180.887l-48.817-28.289l-42.863 24.872l-43.188-24.897l-49.252 28.667l91.914 52.882l92.206-53.235zM0 222.212V74.495L127.987 0L256 74.182v147.797l-128.016 73.744L0 222.212z"
fill="#000"
></path>
</svg>
</div>
<div>
<svg
v-if="copied"
class="dumi-previewer-actions__icon"
style="fill: green"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24"
>
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M10 15.172l9.192-9.193 1.415 1.414L10 18l-6.364-6.364 1.414-1.414z"
/>
</svg>
<svg
v-else
class="dumi-previewer-actions__icon"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24"
@click="handleCopy"
>
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M7 6V3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v14a1 1 0 0 1-1 1h-3v3c0 .552-.45 1-1.007 1H4.007A1.001 1.001 0 0 1 3 21l.003-14c0-.552.45-1 1.007-1H7zM5.003 8L5 20h10V8H5.003zM9 6h8v10h2V4H9v2z"
/>
</svg>
<svg
class="dumi-previewer-actions__icon"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24"
@click="handleCollapse"
>
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M24 12l-5.657 5.657-1.414-1.414L21.172 12l-4.243-4.243 1.414-1.414L24 12zM2.828 12l4.243 4.243-1.414 1.414L0 12l5.657-5.657L7.07 7.757 2.828 12zm6.96 9H7.66l6.552-18h2.128L9.788 21z"
/>
</svg>
</div>
</div>
<div v-show="!innerCollapsed" class="dumi-previewer-source">
<div v-html="highlightCode" class="language-vue extra-class" />
</div>
</section>
</client-only>
</template>
<script>
import copy from 'copy-to-clipboard'
import highlight from './highlight'
import { createCodeSandBox } from './createCodeSandBox'
export default {
name: 'dumi-previewer',
props: {
code: {
type: String,
default: '',
},
demoPath: {
type: String,
default: '',
},
collapsed: {
type: Boolean,
default: true,
},
},
data() {
return {
innerCollapsed: this.collapsed,
copied: false,
timerId: null,
demoStr: '',
/**
* take over VuePress render
* 接管VuePress渲染
*/
demo: null,
}
},
computed: {
decodedCode() {
return this.code || this.demoStr
},
highlightCode() {
return highlight(this.decodedCode, 'vue')
},
},
created() {
if (this.demoPath) {
import(
/* webpackPrefetch: true */ `../../demos/${this.demoPath}.vue`
).then((module) => {
this.demo = module.default
})
import(
/* webpackPrefetch: true */ `!raw-loader!../../demos/${this.demoPath}.vue`
).then((module) => {
this.demoStr = module.default
})
}
},
beforeDestroy() {
clearTimeout(this.timerId)
},
methods: {
handleCollapse() {
this.innerCollapsed = !this.innerCollapsed
},
handleCopy() {
this.copied = true
copy(this.decodedCode)
clearTimeout(this.timer)
this.timerId = setTimeout(() => {
this.copied = false
}, 2000)
},
openCodeSandBox() {
createCodeSandBox(this.demoStr)
},
},
}
</script>
<style lang="stylus">
.dumi-previewer {
background-color: #fff;
border: 1px solid #ebedf1;
border-radius: 1px;
.dumi-previewer-demo {
padding: 40px 24px;
}
.dumi-previewer-actions {
display: flex;
align-items: center;
justify-content: space-between;
border-top: 1px dashed #ebedf1;
height: 40px;
padding: 0 1em;
.dumi-previewer-actions__icon {
width: 16px;
height: 16px;
padding: 8px 4px;
opacity: 0.4;
cursor: pointer;
transition: opacity .3s;
&:hover {
opacity: 0.6;
}
}
}
.dumi-previewer-source {
border-top: 1px dashed #ebedf1;
div[class*="language-"] {
background-color: #f9fafb;
border-radius: 0;
}
pre[class*="language-"] {
margin: 0 !important;
}
pre[class*="language-"] code {
color: #000 !important;
}
.token.cdata,.token.comment,.token.doctype,.token.prolog {
color: #708090
}
.token.punctuation {
color: #999
}
.token.namespace {
opacity: .7
}
.token.boolean,.token.constant,.token.deleted,.token.number,.token.property,.token.symbol,.token.tag {
color: #905
}
.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string {
color: #690
}
.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url {
color: #9a6e3a;
background: hsla(0,0%,100%,.5)
}
.token.atrule,.token.attr-value,.token.keyword {
color: #07a
}
.token.class-name,.token.function {
color: #dd4a68
}
.token.important,.token.regex,.token.variable {
color: #e90
}
}
}
</style>
```
--------------------------------------------------------------------------------
/packages/react/src/components/SchemaField.tsx:
--------------------------------------------------------------------------------
```typescript
import React, { useContext, Fragment } from 'react'
import { ISchema, Schema } from '@formily/json-schema'
import { RecursionField } from './RecursionField'
import { render } from '../shared/render'
import {
SchemaMarkupContext,
SchemaOptionsContext,
SchemaComponentsContext,
} from '../shared'
import {
ReactComponentPath,
JSXComponent,
ISchemaFieldReactFactoryOptions,
SchemaReactComponents,
ISchemaFieldProps,
ISchemaMarkupFieldProps,
ISchemaTypeFieldProps,
} from '../types'
import { lazyMerge } from '@formily/shared'
import { ExpressionScope } from './ExpressionScope'
const env = {
nonameId: 0,
}
const getRandomName = () => {
return `NO_NAME_FIELD_$${env.nonameId++}`
}
export function createSchemaField<Components extends SchemaReactComponents>(
options: ISchemaFieldReactFactoryOptions<Components> = {}
) {
function SchemaField<
Decorator extends JSXComponent,
Component extends JSXComponent
>(props: ISchemaFieldProps<Decorator, Component>) {
const schema = Schema.isSchemaInstance(props.schema)
? props.schema
: new Schema({
type: 'object',
...props.schema,
})
const renderMarkup = () => {
env.nonameId = 0
if (props.schema) return null
return render(
<SchemaMarkupContext.Provider value={schema}>
{props.children}
</SchemaMarkupContext.Provider>
)
}
const renderChildren = () => {
return <RecursionField {...props} schema={schema} />
}
return (
<SchemaOptionsContext.Provider value={options}>
<SchemaComponentsContext.Provider
value={lazyMerge(options.components, props.components)}
>
<ExpressionScope value={lazyMerge(options.scope, props.scope)}>
{renderMarkup()}
{renderChildren()}
</ExpressionScope>
</SchemaComponentsContext.Provider>
</SchemaOptionsContext.Provider>
)
}
SchemaField.displayName = 'SchemaField'
function MarkupRender(props: any) {
const parent = useContext(SchemaMarkupContext)
if (!parent) return <Fragment />
const renderChildren = () => {
return <React.Fragment>{props.children}</React.Fragment>
}
const appendArraySchema = (schema: ISchema) => {
const items = parent.items as Schema
if (items && items.name !== props.name) {
return parent.addProperty(props.name, schema)
} else {
return parent.setItems(schema)
}
}
if (parent.type === 'object' || parent.type === 'void') {
const schema = parent.addProperty(props.name, props)
return (
<SchemaMarkupContext.Provider value={schema}>
{renderChildren()}
</SchemaMarkupContext.Provider>
)
} else if (parent.type === 'array') {
const schema = appendArraySchema(props)
return (
<SchemaMarkupContext.Provider
value={Array.isArray(schema) ? schema[0] : schema}
>
{props.children}
</SchemaMarkupContext.Provider>
)
} else {
return renderChildren()
}
}
function MarkupField<
Decorator extends ReactComponentPath<Components>,
Component extends ReactComponentPath<Components>
>(props: ISchemaMarkupFieldProps<Components, Component, Decorator>) {
return <MarkupRender {...props} name={props.name || getRandomName()} />
}
MarkupField.displayName = 'MarkupField'
function StringField<
Decorator extends ReactComponentPath<Components>,
Component extends ReactComponentPath<Components>
>(props: ISchemaTypeFieldProps<Components, Component, Decorator>) {
return <MarkupField {...props} type="string" />
}
StringField.displayName = 'StringField'
function ObjectField<
Decorator extends ReactComponentPath<Components>,
Component extends ReactComponentPath<Components>
>(props: ISchemaTypeFieldProps<Components, Component, Decorator>) {
return <MarkupField {...props} type="object" />
}
ObjectField.displayName = 'ObjectField'
function ArrayField<
Decorator extends ReactComponentPath<Components>,
Component extends ReactComponentPath<Components>
>(props: ISchemaTypeFieldProps<Components, Component, Decorator>) {
return <MarkupField {...props} type="array" />
}
ArrayField.displayName = 'ArrayField'
function BooleanField<
Decorator extends ReactComponentPath<Components>,
Component extends ReactComponentPath<Components>
>(props: ISchemaTypeFieldProps<Components, Component, Decorator>) {
return <MarkupField {...props} type="boolean" />
}
BooleanField.displayName = 'BooleanField'
function NumberField<
Decorator extends ReactComponentPath<Components>,
Component extends ReactComponentPath<Components>
>(props: ISchemaTypeFieldProps<Components, Component, Decorator>) {
return <MarkupField {...props} type="number" />
}
NumberField.displayName = 'NumberField'
function DateField<
Decorator extends ReactComponentPath<Components>,
Component extends ReactComponentPath<Components>
>(props: ISchemaTypeFieldProps<Components, Component, Decorator>) {
return <MarkupField {...props} type="date" />
}
DateField.displayName = 'DateField'
function DateTimeField<
Decorator extends ReactComponentPath<Components>,
Component extends ReactComponentPath<Components>
>(props: ISchemaTypeFieldProps<Components, Component, Decorator>) {
return <MarkupField {...props} type="datetime" />
}
DateTimeField.displayName = 'DateTimeField'
function VoidField<
Decorator extends ReactComponentPath<Components>,
Component extends ReactComponentPath<Components>
>(props: ISchemaTypeFieldProps<Components, Component, Decorator>) {
return <MarkupField {...props} type="void" />
}
VoidField.displayName = 'VoidField'
SchemaField.Markup = MarkupField
SchemaField.String = StringField
SchemaField.Object = ObjectField
SchemaField.Array = ArrayField
SchemaField.Boolean = BooleanField
SchemaField.Date = DateField
SchemaField.DateTime = DateTimeField
SchemaField.Void = VoidField
SchemaField.Number = NumberField
return SchemaField
}
```
--------------------------------------------------------------------------------
/packages/antd/docs/components/PreviewText.zh-CN.md:
--------------------------------------------------------------------------------
```markdown
# PreviewText
> 阅读态组件,主要用来实现类 Input,类 DatePicker 这些组件的阅读态
## 简单用例
```tsx
import React from 'react'
import { PreviewText, FormItem, FormLayout } from '@formily/antd'
import { createForm } from '@formily/core'
import { FormProvider, createSchemaField } from '@formily/react'
const SchemaField = createSchemaField({
components: {
FormItem,
PreviewText,
},
})
const form = createForm()
export default () => {
return (
<FormLayout labelCol={6} wrapperCol={10}>
<FormProvider form={form}>
<SchemaField>
<SchemaField.String
x-decorator="FormItem"
title="文本预览"
x-component="PreviewText.Input"
default={'Hello world'}
/>
<SchemaField.String
x-decorator="FormItem"
title="选择项预览"
x-component="PreviewText.Select"
x-component-props={{
mode: 'multiple',
}}
default={['123', '222']}
enum={[
{ label: 'A111', value: '123' },
{ label: 'A222', value: '222' },
]}
/>
<SchemaField.String
x-decorator="FormItem"
title="树选择预览"
x-component="PreviewText.TreeSelect"
x-component-props={{
multiple: true,
}}
default={['123', '222']}
enum={[
{ label: 'A111', value: '123' },
{ label: 'A222', value: '222' },
]}
/>
<SchemaField.String
x-decorator="FormItem"
title="树选择(treeData)预览"
x-component="PreviewText.TreeSelect"
x-component-props={{
multiple: true,
treeNodeLabelProp: 'name',
treeData: [
{ name: 'A111', value: '123' },
{ name: 'A222', value: '222' },
],
}}
default={['123', '222']}
/>
<SchemaField.String
x-decorator="FormItem"
title="日期预览"
x-component="PreviewText.DatePicker"
default={'2020-11-23 22:15:20'}
/>
<SchemaField.String
x-decorator="FormItem"
title="Cascader预览"
x-component="PreviewText.Cascader"
default={'yuhang'}
enum={[
{
label: '杭州',
value: 'hangzhou',
children: [
{
label: '余杭',
value: 'yuhang',
},
],
},
]}
/>
</SchemaField>
</FormProvider>
</FormLayout>
)
}
```
## 扩展阅读态
```tsx
import React from 'react'
import {
PreviewText,
FormItem,
FormLayout,
FormButtonGroup,
} from '@formily/antd'
import { createForm } from '@formily/core'
import {
FormProvider,
mapReadPretty,
connect,
createSchemaField,
} from '@formily/react'
import { Button, Input as AntdInput } from 'antd'
const Input = connect(AntdInput, mapReadPretty(PreviewText.Input))
const SchemaField = createSchemaField({
components: {
Input,
FormItem,
PreviewText,
},
})
const form = createForm()
export default () => {
return (
<PreviewText.Placeholder value="暂无数据">
<FormLayout labelCol={6} wrapperCol={10}>
<FormProvider form={form}>
<SchemaField>
<SchemaField.Markup
type="string"
x-decorator="FormItem"
title="文本预览"
required
x-component="Input"
default={'Hello world'}
/>
<SchemaField.Markup
type="string"
x-decorator="FormItem"
title="选择项预览"
x-component="PreviewText.Select"
x-component-props={{
mode: 'multiple',
}}
default={['123']}
enum={[
{ label: 'A111', value: '123' },
{ label: 'A222', value: '222' },
]}
/>
<SchemaField.Markup
type="string"
x-decorator="FormItem"
title="日期预览"
x-component="PreviewText.DatePicker"
/>
<SchemaField.Markup
type="string"
x-decorator="FormItem"
title="Cascader预览"
x-component="PreviewText.Cascader"
default={'yuhang'}
enum={[
{
label: '杭州',
value: 'hangzhou',
children: [
{
label: '余杭',
value: 'yuhang',
},
],
},
]}
/>
</SchemaField>
<FormButtonGroup.FormItem>
<Button
onClick={() => {
form.setState((state) => {
state.editable = !state.editable
})
}}
>
切换阅读态
</Button>
</FormButtonGroup.FormItem>
</FormProvider>
</FormLayout>
</PreviewText.Placeholder>
)
}
```
## API
### PreviewText.Input
参考 https://ant.design/components/input-cn/
### PreviewText.Select
参考 https://ant.design/components/select-cn/
### PreviewText.TreeSelect
参考 https://ant.design/components/tree-select-cn/
### PreviewText.Cascader
参考 https://ant.design/components/cascader-cn/
### PreviewText.DatePicker
参考 https://ant.design/components/date-picker-cn/
### PreviewText.DateRangePicker
参考 https://ant.design/components/date-picker-cn/
### PreviewText.TimePicker
参考 https://ant.design/components/time-picker-cn/
### PreviewText.TimeRangePicker
参考 https://ant.design/components/time-picker-cn/
### PreviewText.NumberPicker
参考 https://ant.design/components/input-number-cn/
### PreviewText.Placeholder
| 属性名 | 类型 | 描述 | 默认值 |
| ------ | ------ | ---------- | ------ |
| value | stirng | 缺省占位符 | N/A |
### PreviewText.usePlaceholder
```ts pure
interface usePlaceholder {
(): string
}
```
```
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
```json
{
"name": "root",
"private": true,
"devEngines": {
"node": "8.x || 9.x || 10.x || 11.x"
},
"workspaces": [
"packages/*",
"devtools/*"
],
"scripts": {
"build": "rimraf -rf packages/*/{lib,dist,esm} && lerna run build",
"build:docs": "dumi build",
"start": "dumi dev",
"test": "jest --coverage",
"test:reactive": "jest packages/reactive/",
"test:validator": "jest packages/validator/",
"test:core": "jest packages/core/",
"test:core:watch": "npm run test:core --- --watch",
"test:schema": "jest packages/json-schema/",
"test:schema:watch": "npm run test:schema --- --watch",
"test:react": "jest packages/react/",
"test:shared": "jest packages/shared/",
"test:path": "jest packages/path/",
"test:react:watch": "npm run test:react --- --watch",
"test:vue": "jest packages/vue/",
"test:vue:watch": "npm run test:vue --- --watch",
"test:reactive-vue": "jest packages/reactive-vue/",
"test:reactive-vue:watch": "npm run test:reactive-vue --- --watch",
"test:antd": "jest packages/antd/",
"test:next": "jest packages/next/",
"test:watch": "jest --watch",
"test:prod": "jest --coverage --silent",
"preversion": "yarn install --ignore-engines && git add -A && npm run build && npm run lint && npm run test",
"version:alpha": "lerna version prerelease --preid alpha",
"version:beta": "lerna version prerelease --preid beta",
"version:rc": "lerna version prerelease --preid rc",
"version:patch": "lerna version patch",
"version:minor": "lerna version minor",
"version:preminor": "lerna version preminor --preid beta",
"version:major": "lerna version major",
"release:force": "lerna publish from-package --yes",
"lint": "eslint ."
},
"resolutions": {
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"@mapbox/hast-util-to-jsx": "~1.0.0",
"yargs": "^16.x",
"commander": "^6.x",
"ttypescript": "1.5.15"
},
"devDependencies": {
"@alifd/next": "^1.19.1",
"@commitlint/cli": "^14.1.0",
"@commitlint/config-conventional": "^14.1.0",
"@commitlint/prompt-cli": "^14.1.0",
"@netlify/functions": "^0.7.2",
"@rollup/plugin-commonjs": "^17.0.0",
"@testing-library/jest-dom": "^5.0.0",
"@testing-library/react": "^11.2.3",
"@testing-library/vue": "^5.6.2",
"@types/fs-extra": "^8.1.0",
"@types/hoist-non-react-statics": "^3.3.1",
"@types/jest": "^24.0.18",
"@types/node": "^12.6.8",
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"@types/react-is": "^18.3.0",
"@typescript-eslint/eslint-plugin": "^4.9.1",
"@typescript-eslint/parser": "^4.8.2",
"@umijs/plugin-sass": "^1.1.1",
"@vue/test-utils": "1.0.0-beta.22",
"antd": "^4.0.0",
"axios": "^1.6.0",
"chalk": "^2.4.2",
"chokidar": "^2.1.2",
"concurrently": "^4.1.0",
"conventional-commit-types": "^2.2.0",
"cool-path": "^1.0.6",
"cross-env": "^5.2.1",
"css-loader": "^5.0.0",
"cz-conventional-changelog": "^2.1.0",
"dumi": "^1.1.53",
"escape-string-regexp": "^4.0.0",
"eslint": "^7.14.0",
"eslint-config-prettier": "^7.0.0",
"eslint-plugin-import": "^2.13.0",
"eslint-plugin-markdown": "^2.0.1",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^3.1.0",
"eslint-plugin-promise": "^4.0.0",
"eslint-plugin-react": "^7.14.2",
"eslint-plugin-react-hooks": "^4.2.0",
"eslint-plugin-vue": "^7.0.1",
"execa": "^5.0.0",
"file-loader": "^5.0.2",
"findup": "^0.1.5",
"fs-extra": "^7.0.1",
"ghooks": "^2.0.4",
"glob": "^7.1.3",
"html-webpack-plugin": "^3.2.0",
"immutable": "^4.0.0-rc.12",
"istanbul-api": "^2.1.1",
"istanbul-lib-coverage": "^2.0.3",
"jest": "^26.0.0",
"jest-codemods": "^0.19.1",
"jest-dom": "^3.1.2",
"jest-localstorage-mock": "^2.3.0",
"jest-styled-components": "6.3.3",
"jest-watch-lerna-packages": "^1.1.0",
"lerna": "^4.0.0",
"less": "^4.1.1",
"less-loader": "^5.0.0",
"less-plugin-npm-import": "^2.1.0",
"lint-staged": "^8.2.1",
"mfetch": "^0.2.27",
"mobx": "^6.0.4",
"mobx-react-lite": "^3.1.6",
"onchange": "^5.2.0",
"opencollective": "^1.0.3",
"opencollective-postinstall": "^2.0.2",
"param-case": "^3.0.4",
"postcss": "^8.0.0",
"prettier": "^2.2.1",
"pretty-format": "^24.0.0",
"pretty-quick": "^3.1.0",
"querystring": "^0.2.1",
"raw-loader": "^4.0.0",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react-mde": "^11.5.0",
"react-test-renderer": "^16.11.0",
"rimraf": "^3.0.0",
"rollup": "^2.37.1",
"rollup-plugin-dts": "^2.0.0",
"rollup-plugin-external-globals": "^0.6.1",
"rollup-plugin-inject-process-env": "^1.3.1",
"rollup-plugin-node-resolve": "^5.2.0",
"rollup-plugin-postcss": "^4.0.0",
"rollup-plugin-terser": "^7.0.2",
"rollup-plugin-typescript2": "^0.35.0",
"semver": "^7.3.5",
"semver-regex": "^3.1.3",
"showdown": "^1.9.1",
"staged-git-files": "^1.1.2",
"string-similarity": "^4.0.4",
"style-loader": "^1.1.3",
"styled-components": "^5.0.0",
"ts-import-plugin": "1.6.1",
"ts-jest": "^26.0.0",
"ts-loader": "^7.0.4",
"ts-node": "^9.1.1",
"typescript": "^4.1.5",
"vue-eslint-parser": "^7.1.1",
"webpack": "^4.41.5",
"webpack-cli": "^3.3.10",
"webpack-dev-server": "^3.10.1",
"yup": "^1.4.0"
},
"repository": {
"type": "git",
"url": "git+https://github.com/alibaba/formily.git"
},
"config": {
"ghooks": {
"pre-commit": "lint-staged",
"commit-msg": "commitlint --edit"
}
},
"lint-staged": {
"*.{ts,tsx,js}": [
"eslint --ext .ts,.tsx,.js",
"pretty-quick --staged",
"git add"
],
"*.md": [
"pretty-quick --staged",
"git add"
]
},
"collective": {
"type": "opencollective",
"url": "https://opencollective.com/formily"
},
"dependencies": {
"@ant-design/icons": "^4.0.2"
},
"packageManager": "[email protected]+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
}
```
--------------------------------------------------------------------------------
/packages/antd/docs/components/Editable.md:
--------------------------------------------------------------------------------
```markdown
# Editable
> Partial editor, you can use this component for some form areas with high space requirements
>
> Editable component is equivalent to a variant of FormItem component, so it is usually placed in decorator
## Markup Schema example
```tsx
import React from 'react'
import {
Input,
DatePicker,
Editable,
FormItem,
FormButtonGroup,
Submit,
} from '@formily/antd'
import { createForm } from '@formily/core'
import { FormProvider, createSchemaField } from '@formily/react'
const SchemaField = createSchemaField({
components: {
DatePicker,
Editable,
Input,
FormItem,
},
})
const form = createForm()
export default () => (
<FormProvider form={form}>
<SchemaField>
<SchemaField.String
name="date"
title="date"
x-decorator="Editable"
x-component="DatePicker"
/>
<SchemaField.String
name="input"
title="input box"
x-decorator="Editable"
x-component="Input"
/>
<SchemaField.Void
name="void"
title="Virtual Node Container"
x-component="Editable.Popover"
x-reactions={(field) => {
field.title = field.query('.void.date2').get('value') || field.title
}}
>
<SchemaField.String
name="date2"
title="date"
x-decorator="FormItem"
x-component="DatePicker"
/>
<SchemaField.String
name="input2"
title="input box"
x-decorator="FormItem"
x-component="Input"
/>
</SchemaField.Void>
<SchemaField.Object
name="iobject"
title="Object node container"
x-component="Editable.Popover"
x-reactions={(field) => {
field.title = field.value?.date || field.title
}}
>
<SchemaField.String
name="date"
title="date"
x-decorator="FormItem"
x-component="DatePicker"
/>
<SchemaField.String
name="input"
title="input box"
x-decorator="FormItem"
x-component="Input"
/>
</SchemaField.Object>
</SchemaField>
<FormButtonGroup>
<Submit onSubmit={console.log}>Submit</Submit>
</FormButtonGroup>
</FormProvider>
)
```
## JSON Schema case
```tsx
import React from 'react'
import {
Input,
DatePicker,
Editable,
FormItem,
FormButtonGroup,
Submit,
} from '@formily/antd'
import { createForm } from '@formily/core'
import { FormProvider, createSchemaField } from '@formily/react'
const SchemaField = createSchemaField({
components: {
DatePicker,
Editable,
Input,
FormItem,
},
})
const form = createForm()
const schema = {
type: 'object',
properties: {
date: {
type: 'string',
title: 'Date',
'x-decorator': 'Editable',
'x-component': 'DatePicker',
},
input: {
type: 'string',
title: 'input box',
'x-decorator': 'Editable',
'x-component': 'Input',
},
void: {
type: 'void',
title: 'Virtual Node Container',
'x-component': 'Editable.Popover',
'x-reactions':
"{{(field) => field.title = field.query('.void.date2').get('value') || field.title}}",
properties: {
date2: {
type: 'string',
title: 'Date',
'x-decorator': 'FormItem',
'x-component': 'DatePicker',
},
input2: {
type: 'string',
title: 'input box',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
iobject: {
type: 'object',
title: 'Object node container',
'x-component': 'Editable.Popover',
'x-reactions':
'{{(field) => field.title = field.value && field.value.date || field.title}}',
properties: {
date: {
type: 'string',
title: 'Date',
'x-decorator': 'FormItem',
'x-component': 'DatePicker',
},
input: {
type: 'string',
title: 'input box',
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
},
}
export default () => (
<FormProvider form={form}>
<SchemaField schema={schema} />
<FormButtonGroup>
<Submit onSubmit={console.log}>Submit</Submit>
</FormButtonGroup>
</FormProvider>
)
```
## Pure JSX case
```tsx
import React from 'react'
import {
Input,
DatePicker,
Editable,
FormItem,
FormButtonGroup,
Submit,
} from '@formily/antd'
import { createForm } from '@formily/core'
import { FormProvider, Field, VoidField, ObjectField } from '@formily/react'
const form = createForm()
export default () => (
<FormProvider form={form}>
<Field
name="date"
title="date"
decorator={[Editable]}
component={[DatePicker]}
/>
<Field
name="input"
title="input box"
decorator={[Editable]}
component={[Input]}
/>
<VoidField
name="void"
title="Virtual Node Container"
reactions={(field) => {
field.title = field.query('.void.date2').get('value') || field.title
}}
component={[Editable.Popover]}
>
<Field
name="date2"
title="date"
decorator={[FormItem]}
component={[DatePicker]}
/>
<Field
name="input2"
title="input box"
decorator={[FormItem]}
component={[Input]}
/>
</VoidField>
<ObjectField
name="iobject"
title="Object node container"
reactions={(field) => {
field.title = field.value?.date || field.title
}}
component={[Editable.Popover]}
>
<Field
name="date"
title="date"
decorator={[FormItem]}
component={[DatePicker]}
/>
<Field
name="input"
title="input box"
decorator={[FormItem]}
component={[Input]}
/>
</ObjectField>
<FormButtonGroup>
<Submit onSubmit={console.log}>Submit</Submit>
</FormButtonGroup>
</FormProvider>
)
```
## API
### Editable
> Inline editing
Refer to the FormItem property in https://ant.design/components/form-cn/
### Editable.Popover
> Floating layer editing
Reference https://ant.design/components/popover-cn/
```
--------------------------------------------------------------------------------
/packages/path/src/tokens.ts:
--------------------------------------------------------------------------------
```typescript
import {
bracketContext,
parenContext,
bracketArrayContext,
bracketDContext,
braceContext,
destructorContext,
} from './contexts'
interface ITokenProps {
expectNext?: (next?: Token) => boolean
expectPrev?: (prev?: Token) => boolean
updateContext?: (prev?: Token) => void
}
export type Token = ITokenProps & {
flag: string
}
const TokenType = (flag: string, props?: ITokenProps): Token => {
return {
flag,
...props,
}
}
export const nameTok = TokenType('name', {
expectNext(next) {
if (this.includesContext(destructorContext)) {
return (
next === nameTok ||
next === commaTok ||
next === bracketRTok ||
next === braceRTok ||
next === colonTok
)
}
return (
next === dotTok ||
next === commaTok ||
next === eofTok ||
next === bracketRTok ||
next === parenRTok ||
next === colonTok ||
next === expandTok ||
next === bracketLTok
)
},
})
export const starTok = TokenType('*', {
expectNext(next) {
return (
next === dotTok ||
next === parenLTok ||
next === bracketLTok ||
next === eofTok ||
next === commaTok ||
next === parenRTok
)
},
})
export const dbStarTok = TokenType('**', {
expectNext(next) {
return (
next === dotTok ||
next === bracketLTok ||
next === eofTok ||
next === commaTok ||
next === parenRTok
)
},
})
export const dotTok = TokenType('.', {
expectNext(next) {
return (
next === dotTok ||
next === nameTok ||
next === bracketDLTok ||
next === starTok ||
next === dbStarTok ||
next === bracketLTok ||
next === braceLTok ||
next === eofTok
)
},
expectPrev(prev) {
return (
prev === dotTok ||
prev === nameTok ||
prev === bracketDRTok ||
prev === starTok ||
prev === parenRTok ||
prev === bracketRTok ||
prev === expandTok ||
prev === braceRTok
)
},
})
export const bangTok = TokenType('!', {
expectNext(next) {
return next === nameTok || next === bracketDLTok
},
})
export const colonTok = TokenType(':', {
expectNext(next) {
if (this.includesContext(destructorContext)) {
return next === nameTok || next === braceLTok || next === bracketLTok
}
return next === nameTok || next === bracketDLTok || next === bracketRTok
},
})
export const braceLTok = TokenType('{', {
expectNext(next) {
return next === nameTok
},
expectPrev(prev) {
if (this.includesContext(destructorContext)) {
return prev === colonTok || prev === commaTok || prev === bracketLTok
}
return prev === dotTok || prev === colonTok || prev === parenLTok
},
updateContext() {
this.state.context.push(braceContext)
},
})
export const braceRTok = TokenType('}', {
expectNext(next) {
if (this.includesContext(destructorContext)) {
return (
next === commaTok ||
next === braceRTok ||
next === eofTok ||
next === bracketRTok
)
}
return next === dotTok || next === eofTok || next === commaTok
},
expectPrev(prev) {
return prev === nameTok || prev === braceRTok || prev === bracketRTok
},
updateContext() {
this.state.context.pop(braceContext)
},
})
export const bracketLTok = TokenType('[', {
expectNext(next) {
if (this.includesContext(destructorContext)) {
return (
next === nameTok ||
next === bracketLTok ||
next === braceLTok ||
next === bracketRTok
)
}
return (
next === nameTok ||
next === bracketDLTok ||
next === colonTok ||
next === bracketLTok ||
next === ignoreTok ||
next === bracketRTok
)
},
expectPrev(prev) {
if (this.includesContext(destructorContext)) {
return prev === colonTok || prev === commaTok || prev === bracketLTok
}
return (
prev === starTok ||
prev === bracketLTok ||
prev === dotTok ||
prev === nameTok ||
prev === parenLTok ||
// never reach
prev == commaTok
)
},
updateContext() {
this.state.context.push(bracketContext)
},
})
export const bracketRTok = TokenType(']', {
expectNext(next) {
if (this.includesContext(destructorContext)) {
return (
next === commaTok ||
next === braceRTok ||
next === bracketRTok ||
next === eofTok
)
}
return (
next === dotTok ||
next === eofTok ||
next === commaTok ||
next === parenRTok ||
next === bracketRTok
)
},
updateContext() {
if (this.includesContext(bracketArrayContext)) return
if (!this.includesContext(bracketContext)) throw this.unexpect()
this.state.context.pop()
},
})
export const bracketDLTok = TokenType('[[', {
updateContext() {
this.state.context.push(bracketDContext)
},
})
export const bracketDRTok = TokenType(']]', {
updateContext() {
if (this.curContext() !== bracketDContext) throw this.unexpect()
this.state.context.pop()
},
})
export const parenLTok = TokenType('(', {
expectNext(next) {
return (
next === nameTok ||
next === bracketDLTok ||
next === braceLTok ||
next === bangTok ||
next === bracketLTok
)
},
expectPrev(prev) {
return prev === starTok
},
updateContext() {
this.state.context.push(parenContext)
},
})
export const parenRTok = TokenType(')', {
expectNext(next) {
return (
next === dotTok ||
next === eofTok ||
next === commaTok ||
next === parenRTok
)
},
updateContext() {
if (this.curContext() !== parenContext) throw this.unexpect()
this.state.context.pop()
},
})
export const commaTok = TokenType(',', {
expectNext(next) {
return (
next === nameTok ||
next === bracketDLTok ||
next === bracketLTok ||
next === braceLTok
)
},
})
export const ignoreTok = TokenType('ignore', {
expectNext(next) {
return next === bracketDRTok
},
expectPrev(prev) {
return prev == bracketDLTok
},
})
export const expandTok = TokenType('expandTok', {
expectNext(next) {
return (
next === dotTok ||
next === eofTok ||
next === commaTok ||
next === parenRTok
)
},
})
export const eofTok = TokenType('eof')
```
--------------------------------------------------------------------------------
/packages/antd/docs/components/FormStep.md:
--------------------------------------------------------------------------------
```markdown
# FormStep
> Step-by-step form components
>
> Note: This component can only be used in Schema scenarios
## Markup Schema example
```tsx
import React from 'react'
import { FormStep, FormItem, Input, FormButtonGroup } from '@formily/antd'
import { createForm } from '@formily/core'
import { FormProvider, FormConsumer, createSchemaField } from '@formily/react'
import { Button } from 'antd'
const SchemaField = createSchemaField({
components: {
FormItem,
FormStep,
Input,
},
})
const form = createForm()
const formStep = FormStep.createFormStep()
export default () => {
return (
<FormProvider form={form}>
<SchemaField>
<SchemaField.Void
x-component="FormStep"
x-component-props={{ formStep }}
>
<SchemaField.Void
x-component="FormStep.StepPane"
x-component-props={{ title: 'First Step' }}
>
<SchemaField.String
name="aaa"
x-decorator="FormItem"
required
x-component="Input"
/>
</SchemaField.Void>
<SchemaField.Void
x-component="FormStep.StepPane"
x-component-props={{ title: 'Second Step' }}
>
<SchemaField.String
name="bbb"
x-decorator="FormItem"
required
x-component="Input"
/>
</SchemaField.Void>
<SchemaField.Void
type="void"
x-component="FormStep.StepPane"
x-component-props={{ title: 'Step 3' }}
>
<SchemaField.String
name="ccc"
x-decorator="FormItem"
required
x-component="Input"
/>
</SchemaField.Void>
</SchemaField.Void>
</SchemaField>
<FormConsumer>
{() => (
<FormButtonGroup>
<Button
disabled={!formStep.allowBack}
onClick={() => {
formStep.back()
}}
>
Previous
</Button>
<Button
disabled={!formStep.allowNext}
onClick={() => {
formStep.next()
}}
>
Next step
</Button>
<Button
disabled={formStep.allowNext}
onClick={() => {
formStep.submit(console.log)
}}
>
submit
</Button>
</FormButtonGroup>
)}
</FormConsumer>
</FormProvider>
)
}
```
## JSON Schema case
```tsx
import React from 'react'
import { FormStep, FormItem, Input, FormButtonGroup } from '@formily/antd'
import { createForm } from '@formily/core'
import { FormProvider, FormConsumer, createSchemaField } from '@formily/react'
import { Button } from 'antd'
const SchemaField = createSchemaField({
components: {
FormItem,
FormStep,
Input,
},
})
const form = createForm()
const formStep = FormStep.createFormStep()
const schema = {
type: 'object',
properties: {
step: {
type: 'void',
'x-component': 'FormStep',
'x-component-props': {
formStep: '{{formStep}}',
},
properties: {
step1: {
type: 'void',
'x-component': 'FormStep.StepPane',
'x-component-props': {
title: 'First Step',
},
properties: {
aaa: {
type: 'string',
title: 'AAA',
required: true,
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
step2: {
type: 'void',
'x-component': 'FormStep.StepPane',
'x-component-props': {
title: 'Second Step',
},
properties: {
bbb: {
type: 'string',
title: 'AAA',
required: true,
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
step3: {
type: 'void',
'x-component': 'FormStep.StepPane',
'x-component-props': {
title: 'The third step',
},
properties: {
ccc: {
type: 'string',
title: 'AAA',
required: true,
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
},
},
},
}
export default () => {
return (
<FormProvider form={form}>
<SchemaField schema={schema} scope={{ formStep }} />
<FormConsumer>
{() => (
<FormButtonGroup>
<Button
disabled={!formStep.allowBack}
onClick={() => {
formStep.back()
}}
>
Previous
</Button>
<Button
disabled={!formStep.allowNext}
onClick={() => {
formStep.next()
}}
>
Next step
</Button>
<Button
disabled={formStep.allowNext}
onClick={() => {
formStep.submit(console.log)
}}
>
submit
</Button>
</FormButtonGroup>
)}
</FormConsumer>
</FormProvider>
)
}
```
## API
### FormStep
| Property name | Type | Description | Default value |
| ------------- | --------- | ------------------------------------------------------- | ------------- |
| formStep | IFormStep | Pass in the model created by createFormStep/useFormStep | |
Other references https://ant.design/components/steps-cn/
### FormStep.StepPane
Refer to https://ant.design/components/steps-cn/ Steps.Step properties
### FormStep.createFormStep
```ts pure
import { Form } from '@formily/core'
interface createFormStep {
(current?: number): IFormStep
}
interface IFormTab {
//Current index
current: number
//Whether to allow backwards
allowNext: boolean
//Whether to allow forward
allowBack: boolean
//Set the current index
setCurrent(key: number): void
//submit Form
submit: Form['submit']
//backward
next(): void
//forward
back(): void
}
```
```
--------------------------------------------------------------------------------
/packages/react/docs/api/components/SchemaField.md:
--------------------------------------------------------------------------------
```markdown
---
order: 4
---
# SchemaField
## Description
The SchemaField component is a component specially used to parse [JSON-Schema](/api/shared/schema) dynamically rendering forms.
When using the SchemaField component, you need to create a SchemaField component through the createSchemaField factory function.
## Signature
```ts
//SchemaField component and its static properties
type ComposeSchemaField = React.FC<
React.PropsWithChildren<ISchemaFieldProps>
> & {
Markup: React.FC<React.PropsWithChildren<ISchema>>
String: React.FC<React.PropsWithChildren<Omit<ISchema, 'type'>>>
Object: React.FC<React.PropsWithChildren<Omit<ISchema, 'type'>>>
Array: React.FC<React.PropsWithChildren<Omit<ISchema, 'type'>>>
Date: React.FC<React.PropsWithChildren<Omit<ISchema, 'type'>>>
DateTime: React.FC<React.PropsWithChildren<Omit<ISchema, 'type'>>>
Boolean: React.FC<React.PropsWithChildren<Omit<ISchema, 'type'>>>
Number: React.FC<React.PropsWithChildren<Omit<ISchema, 'type'>>>
Void: React.FC<React.PropsWithChildren<Omit<ISchema, 'type'>>>
}
//Factory function parameter attributes
interface ISchemaFieldFactoryProps {
components?: {
[key: string]: React.FC //Component list
}
scope?: any //Global scope, used to implement protocol expression variable injection
}
//SchemaField attribute
interface ISchemaFieldProps extends IFieldFactoryProps {
schema?: ISchema //Field schema
scope?: any //Protocol expression scope
name?: string //Field name
components?: {
[key: string]: React.FC //Partial component list, note: the components passed here cannot enjoy smart prompts
}
}
//Factory function
interface createSchemaField {
(props: ISchemaFieldFactoryProps): ComposeSchemaField
}
```
IFieldFactoryProps reference [IFieldFactoryProps](https://core.formilyjs.org/api/models/form#ifieldfactoryprops)
ISchema Reference [ISchema](/api/shared/schema#ischema)
## Markup Schema Use Case
```tsx
import React from 'react'
import { createForm } from '@formily/core'
import { FormProvider, createSchemaField } from '@formily/react'
import { Input, Select } from 'antd'
const form = createForm()
const SchemaField = createSchemaField({
components: {
Input,
},
})
export default () => (
<FormProvider form={form}>
<SchemaField
components={{
Select,
}}
>
<SchemaField.String name="input" x-component="Input" />
<SchemaField.String
name="select"
x-component="Select"
x-component-props={{
style: {
width: 200,
marginTop: 20,
},
}}
/>
</SchemaField>
</FormProvider>
)
```
## JSON Schema Use Case
```tsx
import React from 'react'
import { createForm } from '@formily/core'
import { FormProvider, createSchemaField } from '@formily/react'
import { Input, Select } from 'antd'
const form = createForm()
const SchemaField = createSchemaField({
components: {
Input,
},
})
export default () => (
<FormProvider form={form}>
<SchemaField
components={{
Select,
}}
schema={{
type: 'object',
properties: {
input: {
type: 'string',
'x-component': 'Input',
},
select: {
type: 'string',
'x-component': 'Select',
'x-component-props': {
style: {
width: 200,
marginTop: 20,
},
},
},
},
}}
></SchemaField>
</FormProvider>
)
```
## JSON Schema ReactNode Prop Use Case (x-slot-node)
Reference [Slot](https://react.formilyjs.org/api/shared/schema#slot)
```tsx
import React from 'react'
import { createForm } from '@formily/core'
import { FormProvider, createSchemaField } from '@formily/react'
import { Input, Tag } from 'antd'
import { CheckCircleTwoTone, CloseCircleOutlined } from '@ant-design/icons'
const form = createForm()
const SchemaField = createSchemaField({
components: {
Input,
CheckCircleTwoTone,
CloseCircleOutlined,
},
})
export default () => (
<FormProvider form={form}>
<SchemaField
components={{
Tag,
}}
schema={{
type: 'object',
properties: {
tag: {
'x-slot-node': {
target: 'input.x-component-props.prefix',
},
'x-component': 'Tag',
'x-component-props': {
children: 'www.',
},
},
tag2: {
'x-slot-node': {
target: 'input.x-component-props.suffix',
},
'x-component': 'Tag',
'x-component-props': {
children: '.com',
},
},
icon: {
'x-slot-node': {
target: 'input.x-component-props.addonAfter',
},
'x-component':
'{{$form.values.input?.length > 5 ? "CheckCircleTwoTone" : "CloseCircleOutlined"}}',
},
input: {
type: 'string',
'x-component': 'Input',
'x-component-props': {},
},
},
}}
></SchemaField>
</FormProvider>
)
```
## JSON Schema Render Prop Use Case (x-slot-node & isRenderProp)
Reference [Slot](https://react.formilyjs.org/api/shared/schema#slot)
```tsx
import React from 'react'
import { createForm } from '@formily/core'
import { FormProvider, createSchemaField } from '@formily/react'
import { Rate } from 'antd'
import { DollarOutlined } from '@ant-design/icons'
const form = createForm()
const SchemaField = createSchemaField({
components: {
DollarOutlined,
},
})
export default () => (
<FormProvider form={form}>
<SchemaField
components={{
Rate,
}}
schema={{
type: 'object',
properties: {
icon: {
'x-slot-node': {
target: 'rate.x-component-props.character',
isRenderProp: true,
},
'x-component': 'DollarOutlined',
'x-component-props': {
rotate: '{{ $slotArgs[0].value * 45 }}',
style: {
fontSize: '50px',
},
},
},
rate: {
'x-component': 'Rate',
'x-component-props': {
defaultValue: 3,
},
},
},
}}
></SchemaField>
</FormProvider>
)
```
```
--------------------------------------------------------------------------------
/packages/next/docs/components/FormStep.md:
--------------------------------------------------------------------------------
```markdown
# FormStep
> Step-by-step form components
>
> Note: This component can only be used in Schema scenarios
## Markup Schema example
```tsx
import React from 'react'
import { FormStep, FormItem, Input, FormButtonGroup } from '@formily/next'
import { createForm } from '@formily/core'
import { FormProvider, FormConsumer, createSchemaField } from '@formily/react'
import { Button } from '@alifd/next'
const SchemaField = createSchemaField({
components: {
FormItem,
FormStep,
Input,
},
})
const form = createForm()
const formStep = FormStep.createFormStep()
export default () => {
return (
<FormProvider form={form}>
<SchemaField>
<SchemaField.Void
x-component="FormStep"
x-component-props={{ formStep }}
>
<SchemaField.Void
x-component="FormStep.StepPane"
x-component-props={{ title: 'First Step' }}
>
<SchemaField.String
name="aaa"
x-decorator="FormItem"
required
x-component="Input"
/>
</SchemaField.Void>
<SchemaField.Void
x-component="FormStep.StepPane"
x-component-props={{ title: 'Second Step' }}
>
<SchemaField.String
name="bbb"
x-decorator="FormItem"
required
x-component="Input"
/>
</SchemaField.Void>
<SchemaField.Void
type="void"
x-component="FormStep.StepPane"
x-component-props={{ title: 'Step 3' }}
>
<SchemaField.String
name="ccc"
x-decorator="FormItem"
required
x-component="Input"
/>
</SchemaField.Void>
</SchemaField.Void>
</SchemaField>
<FormConsumer>
{() => (
<FormButtonGroup>
<Button
disabled={!formStep.allowBack}
onClick={() => {
formStep.back()
}}
>
Previous
</Button>
<Button
disabled={!formStep.allowNext}
onClick={() => {
formStep.next()
}}
>
Next step
</Button>
<Button
disabled={formStep.allowNext}
onClick={() => {
formStep.submit(console.log)
}}
>
submit
</Button>
</FormButtonGroup>
)}
</FormConsumer>
</FormProvider>
)
}
```
## JSON Schema case
```tsx
import React from 'react'
import { FormStep, FormItem, Input, FormButtonGroup } from '@formily/next'
import { createForm } from '@formily/core'
import { FormProvider, FormConsumer, createSchemaField } from '@formily/react'
import { Button } from '@alifd/next'
const SchemaField = createSchemaField({
components: {
FormItem,
FormStep,
Input,
},
})
const form = createForm()
const formStep = FormStep.createFormStep()
const schema = {
type: 'object',
properties: {
step: {
type: 'void',
'x-component': 'FormStep',
'x-component-props': {
formStep: '{{formStep}}',
},
properties: {
step1: {
type: 'void',
'x-component': 'FormStep.StepPane',
'x-component-props': {
title: 'First Step',
},
properties: {
aaa: {
type: 'string',
title: 'AAA',
required: true,
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
step2: {
type: 'void',
'x-component': 'FormStep.StepPane',
'x-component-props': {
title: 'Second Step',
},
properties: {
bbb: {
type: 'string',
title: 'AAA',
required: true,
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
step3: {
type: 'void',
'x-component': 'FormStep.StepPane',
'x-component-props': {
title: 'The third step',
},
properties: {
ccc: {
type: 'string',
title: 'AAA',
required: true,
'x-decorator': 'FormItem',
'x-component': 'Input',
},
},
},
},
},
},
}
export default () => {
return (
<FormProvider form={form}>
<SchemaField schema={schema} scope={{ formStep }} />
<FormConsumer>
{() => (
<FormButtonGroup>
<Button
disabled={!formStep.allowBack}
onClick={() => {
formStep.back()
}}
>
Previous
</Button>
<Button
disabled={!formStep.allowNext}
onClick={() => {
formStep.next()
}}
>
Next step
</Button>
<Button
disabled={formStep.allowNext}
onClick={() => {
formStep.submit(console.log)
}}
>
submit
</Button>
</FormButtonGroup>
)}
</FormConsumer>
</FormProvider>
)
}
```
## API
### FormStep
| Property name | Type | Description | Default value |
| ------------- | --------- | ------------------------------------------------------- | ------------- |
| formStep | IFormStep | Pass in the model created by createFormStep/useFormStep | |
Other references https://fusion.design/pc/component/basic/step
### FormStep.StepPane
Refer to https://fusion.design/pc/component/basic/step Steps.Step properties
### FormStep.createFormStep
```ts pure
import { Form } from '@formily/core'
interface createFormStep {
(current?: number): IFormStep
}
interface IFormTab {
//Current index
current: number
//Whether to allow backwards
allowNext: boolean
//Whether to allow forward
allowBack: boolean
//Set the current index
setCurrent(key: number): void
//submit Form
submit: Form['submit']
//backward
next(): void
//forward
back(): void
}
```
```