This is page 20 of 52. Use http://codebase.md/alibaba/formily?lines=true&page={x} to view the full context.
# Directory Structure
```
├── .all-contributorsrc
├── .codecov.yml
├── .editorconfig
├── .eslintignore
├── .eslintrc
├── .github
│ ├── CONTRIBUTING.md
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE
│ │ └── config.yml
│ ├── PULL_REQUEST_TEMPLATE.md
│ └── workflows
│ ├── check-pr-title.yml
│ ├── ci.yml
│ ├── commitlint.yml
│ ├── issue-open-check.yml
│ ├── package-size.yml
│ └── pr-welcome.yml
├── .gitignore
├── .prettierrc.js
├── .umirc.js
├── .vscode
│ └── cspell.json
├── .yarnrc
├── CHANGELOG.md
├── commitlint.config.js
├── devtools
│ ├── .eslintrc
│ └── chrome-extension
│ ├── .npmignore
│ ├── assets
│ │ └── img
│ │ ├── loading.svg
│ │ └── logo
│ │ ├── 128x128.png
│ │ ├── 16x16.png
│ │ ├── 38x38.png
│ │ ├── 48x48.png
│ │ ├── error.png
│ │ ├── gray.png
│ │ └── scalable.png
│ ├── config
│ │ ├── webpack.base.ts
│ │ ├── webpack.dev.ts
│ │ └── webpack.prod.ts
│ ├── LICENSE.md
│ ├── package.json
│ ├── src
│ │ ├── app
│ │ │ ├── components
│ │ │ │ ├── FieldTree.tsx
│ │ │ │ ├── filter.ts
│ │ │ │ ├── LeftPanel.tsx
│ │ │ │ ├── RightPanel.tsx
│ │ │ │ ├── SearchBox.tsx
│ │ │ │ └── Tabs.tsx
│ │ │ ├── demo.tsx
│ │ │ └── index.tsx
│ │ └── extension
│ │ ├── backend.ts
│ │ ├── background.ts
│ │ ├── content.ts
│ │ ├── devpanel.tsx
│ │ ├── devtools.tsx
│ │ ├── inject.ts
│ │ ├── manifest.json
│ │ ├── popup.tsx
│ │ └── views
│ │ ├── devpanel.ejs
│ │ ├── devtools.ejs
│ │ └── popup.ejs
│ ├── tsconfig.build.json
│ └── tsconfig.json
├── docs
│ ├── functions
│ │ ├── contributors.ts
│ │ └── npm-search.ts
│ ├── guide
│ │ ├── advanced
│ │ │ ├── async.md
│ │ │ ├── async.zh-CN.md
│ │ │ ├── build.md
│ │ │ ├── build.zh-CN.md
│ │ │ ├── business-logic.md
│ │ │ ├── business-logic.zh-CN.md
│ │ │ ├── calculator.md
│ │ │ ├── calculator.zh-CN.md
│ │ │ ├── controlled.md
│ │ │ ├── controlled.zh-CN.md
│ │ │ ├── custom.md
│ │ │ ├── custom.zh-CN.md
│ │ │ ├── destructor.md
│ │ │ ├── destructor.zh-CN.md
│ │ │ ├── input.less
│ │ │ ├── layout.md
│ │ │ ├── layout.zh-CN.md
│ │ │ ├── linkages.md
│ │ │ ├── linkages.zh-CN.md
│ │ │ ├── validate.md
│ │ │ └── validate.zh-CN.md
│ │ ├── contribution.md
│ │ ├── contribution.zh-CN.md
│ │ ├── form-builder.md
│ │ ├── form-builder.zh-CN.md
│ │ ├── index.md
│ │ ├── index.zh-CN.md
│ │ ├── issue-helper.md
│ │ ├── issue-helper.zh-CN.md
│ │ ├── learn-formily.md
│ │ ├── learn-formily.zh-CN.md
│ │ ├── quick-start.md
│ │ ├── quick-start.zh-CN.md
│ │ ├── scenes
│ │ │ ├── dialog-drawer.md
│ │ │ ├── dialog-drawer.zh-CN.md
│ │ │ ├── edit-detail.md
│ │ │ ├── edit-detail.zh-CN.md
│ │ │ ├── index.less
│ │ │ ├── login-register.md
│ │ │ ├── login-register.zh-CN.md
│ │ │ ├── more.md
│ │ │ ├── more.zh-CN.md
│ │ │ ├── query-list.md
│ │ │ ├── query-list.zh-CN.md
│ │ │ ├── step-form.md
│ │ │ ├── step-form.zh-CN.md
│ │ │ ├── tab-form.md
│ │ │ ├── tab-form.zh-CN.md
│ │ │ └── VerifyCode.tsx
│ │ ├── upgrade.md
│ │ └── upgrade.zh-CN.md
│ ├── index.md
│ ├── index.zh-CN.md
│ └── site
│ ├── Contributors.less
│ ├── Contributors.tsx
│ ├── QrCode.less
│ ├── QrCode.tsx
│ ├── Section.less
│ ├── Section.tsx
│ └── styles.less
├── global.config.ts
├── jest.config.js
├── lerna.json
├── LICENSE.md
├── package.json
├── packages
│ ├── .eslintrc
│ ├── antd
│ │ ├── __tests__
│ │ │ ├── moment.spec.ts
│ │ │ └── sideEffects.spec.ts
│ │ ├── .npmignore
│ │ ├── .umirc.js
│ │ ├── build-style.ts
│ │ ├── create-style.ts
│ │ ├── docs
│ │ │ ├── components
│ │ │ │ ├── ArrayCards.md
│ │ │ │ ├── ArrayCards.zh-CN.md
│ │ │ │ ├── ArrayCollapse.md
│ │ │ │ ├── ArrayCollapse.zh-CN.md
│ │ │ │ ├── ArrayItems.md
│ │ │ │ ├── ArrayItems.zh-CN.md
│ │ │ │ ├── ArrayTable.md
│ │ │ │ ├── ArrayTable.zh-CN.md
│ │ │ │ ├── ArrayTabs.md
│ │ │ │ ├── ArrayTabs.zh-CN.md
│ │ │ │ ├── Cascader.md
│ │ │ │ ├── Cascader.zh-CN.md
│ │ │ │ ├── Checkbox.md
│ │ │ │ ├── Checkbox.zh-CN.md
│ │ │ │ ├── DatePicker.md
│ │ │ │ ├── DatePicker.zh-CN.md
│ │ │ │ ├── Editable.md
│ │ │ │ ├── Editable.zh-CN.md
│ │ │ │ ├── Form.md
│ │ │ │ ├── Form.zh-CN.md
│ │ │ │ ├── FormButtonGroup.md
│ │ │ │ ├── FormButtonGroup.zh-CN.md
│ │ │ │ ├── FormCollapse.md
│ │ │ │ ├── FormCollapse.zh-CN.md
│ │ │ │ ├── FormDialog.md
│ │ │ │ ├── FormDialog.zh-CN.md
│ │ │ │ ├── FormDrawer.md
│ │ │ │ ├── FormDrawer.zh-CN.md
│ │ │ │ ├── FormGrid.md
│ │ │ │ ├── FormGrid.zh-CN.md
│ │ │ │ ├── FormItem.md
│ │ │ │ ├── FormItem.zh-CN.md
│ │ │ │ ├── FormLayout.md
│ │ │ │ ├── FormLayout.zh-CN.md
│ │ │ │ ├── FormStep.md
│ │ │ │ ├── FormStep.zh-CN.md
│ │ │ │ ├── FormTab.md
│ │ │ │ ├── FormTab.zh-CN.md
│ │ │ │ ├── index.md
│ │ │ │ ├── index.zh-CN.md
│ │ │ │ ├── Input.md
│ │ │ │ ├── Input.zh-CN.md
│ │ │ │ ├── NumberPicker.md
│ │ │ │ ├── NumberPicker.zh-CN.md
│ │ │ │ ├── Password.md
│ │ │ │ ├── Password.zh-CN.md
│ │ │ │ ├── PreviewText.md
│ │ │ │ ├── PreviewText.zh-CN.md
│ │ │ │ ├── Radio.md
│ │ │ │ ├── Radio.zh-CN.md
│ │ │ │ ├── Reset.md
│ │ │ │ ├── Reset.zh-CN.md
│ │ │ │ ├── Select.md
│ │ │ │ ├── Select.zh-CN.md
│ │ │ │ ├── SelectTable.md
│ │ │ │ ├── SelectTable.zh-CN.md
│ │ │ │ ├── Space.md
│ │ │ │ ├── Space.zh-CN.md
│ │ │ │ ├── Submit.md
│ │ │ │ ├── Submit.zh-CN.md
│ │ │ │ ├── Switch.md
│ │ │ │ ├── Switch.zh-CN.md
│ │ │ │ ├── TimePicker.md
│ │ │ │ ├── TimePicker.zh-CN.md
│ │ │ │ ├── Transfer.md
│ │ │ │ ├── Transfer.zh-CN.md
│ │ │ │ ├── TreeSelect.md
│ │ │ │ ├── TreeSelect.zh-CN.md
│ │ │ │ ├── Upload.md
│ │ │ │ └── Upload.zh-CN.md
│ │ │ ├── index.md
│ │ │ └── index.zh-CN.md
│ │ ├── LICENSE.md
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── rollup.config.js
│ │ ├── src
│ │ │ ├── __builtins__
│ │ │ │ ├── hooks
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── useClickAway.ts
│ │ │ │ │ └── usePrefixCls.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── loading.ts
│ │ │ │ ├── moment.ts
│ │ │ │ ├── pickDataProps.ts
│ │ │ │ ├── portal.tsx
│ │ │ │ ├── render.ts
│ │ │ │ └── sort.tsx
│ │ │ ├── array-base
│ │ │ │ ├── index.tsx
│ │ │ │ ├── style.less
│ │ │ │ └── style.ts
│ │ │ ├── array-cards
│ │ │ │ ├── index.tsx
│ │ │ │ ├── style.less
│ │ │ │ └── style.ts
│ │ │ ├── array-collapse
│ │ │ │ ├── index.tsx
│ │ │ │ ├── style.less
│ │ │ │ └── style.ts
│ │ │ ├── array-items
│ │ │ │ ├── index.tsx
│ │ │ │ ├── style.less
│ │ │ │ └── style.ts
│ │ │ ├── array-table
│ │ │ │ ├── index.tsx
│ │ │ │ ├── style.less
│ │ │ │ └── style.ts
│ │ │ ├── array-tabs
│ │ │ │ ├── index.tsx
│ │ │ │ └── style.ts
│ │ │ ├── cascader
│ │ │ │ ├── index.tsx
│ │ │ │ └── style.ts
│ │ │ ├── checkbox
│ │ │ │ ├── index.tsx
│ │ │ │ └── style.ts
│ │ │ ├── date-picker
│ │ │ │ ├── index.tsx
│ │ │ │ └── style.ts
│ │ │ ├── editable
│ │ │ │ ├── index.tsx
│ │ │ │ ├── style.less
│ │ │ │ └── style.ts
│ │ │ ├── form
│ │ │ │ ├── index.tsx
│ │ │ │ ├── style.less
│ │ │ │ └── style.ts
│ │ │ ├── form-button-group
│ │ │ │ ├── index.tsx
│ │ │ │ ├── style.less
│ │ │ │ └── style.ts
│ │ │ ├── form-collapse
│ │ │ │ ├── index.tsx
│ │ │ │ └── style.ts
│ │ │ ├── form-dialog
│ │ │ │ ├── index.tsx
│ │ │ │ └── style.ts
│ │ │ ├── form-drawer
│ │ │ │ ├── index.tsx
│ │ │ │ └── style.ts
│ │ │ ├── form-grid
│ │ │ │ ├── index.tsx
│ │ │ │ ├── style.less
│ │ │ │ └── style.ts
│ │ │ ├── form-item
│ │ │ │ ├── animation.less
│ │ │ │ ├── grid.less
│ │ │ │ ├── index.tsx
│ │ │ │ ├── style.less
│ │ │ │ └── style.ts
│ │ │ ├── form-layout
│ │ │ │ ├── index.tsx
│ │ │ │ ├── style.less
│ │ │ │ ├── style.ts
│ │ │ │ └── useResponsiveFormLayout.ts
│ │ │ ├── form-step
│ │ │ │ ├── index.tsx
│ │ │ │ └── style.ts
│ │ │ ├── form-tab
│ │ │ │ ├── index.tsx
│ │ │ │ └── style.ts
│ │ │ ├── index.ts
│ │ │ ├── input
│ │ │ │ ├── index.tsx
│ │ │ │ └── style.ts
│ │ │ ├── number-picker
│ │ │ │ ├── index.tsx
│ │ │ │ └── style.ts
│ │ │ ├── password
│ │ │ │ ├── index.tsx
│ │ │ │ ├── PasswordStrength.tsx
│ │ │ │ └── style.ts
│ │ │ ├── preview-text
│ │ │ │ ├── index.tsx
│ │ │ │ ├── style.less
│ │ │ │ └── style.ts
│ │ │ ├── radio
│ │ │ │ ├── index.tsx
│ │ │ │ ├── style.less
│ │ │ │ └── style.ts
│ │ │ ├── reset
│ │ │ │ ├── index.tsx
│ │ │ │ └── style.ts
│ │ │ ├── select
│ │ │ │ ├── index.tsx
│ │ │ │ └── style.ts
│ │ │ ├── select-table
│ │ │ │ ├── index.tsx
│ │ │ │ ├── style.less
│ │ │ │ ├── style.ts
│ │ │ │ ├── useCheckSlackly.tsx
│ │ │ │ ├── useFilterOptions.tsx
│ │ │ │ ├── useFlatOptions.tsx
│ │ │ │ ├── useSize.tsx
│ │ │ │ ├── useTitleAddon.tsx
│ │ │ │ └── utils.ts
│ │ │ ├── space
│ │ │ │ ├── index.tsx
│ │ │ │ └── style.ts
│ │ │ ├── style.less
│ │ │ ├── style.ts
│ │ │ ├── submit
│ │ │ │ ├── index.tsx
│ │ │ │ └── style.ts
│ │ │ ├── switch
│ │ │ │ ├── index.tsx
│ │ │ │ └── style.ts
│ │ │ ├── time-picker
│ │ │ │ ├── index.tsx
│ │ │ │ └── style.ts
│ │ │ ├── transfer
│ │ │ │ ├── index.tsx
│ │ │ │ └── style.ts
│ │ │ ├── tree-select
│ │ │ │ ├── index.tsx
│ │ │ │ └── style.ts
│ │ │ └── upload
│ │ │ ├── index.tsx
│ │ │ ├── placeholder.ts
│ │ │ └── style.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── benchmark
│ │ ├── .npmignore
│ │ ├── .umirc.js
│ │ ├── LICENSE.md
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ └── index.tsx
│ │ ├── template.ejs
│ │ ├── tsconfig.build.json
│ │ ├── tsconfig.json
│ │ ├── webpack.base.ts
│ │ ├── webpack.dev.ts
│ │ └── webpack.prod.ts
│ ├── core
│ │ ├── .npmignore
│ │ ├── .umirc.js
│ │ ├── docs
│ │ │ ├── api
│ │ │ │ ├── entry
│ │ │ │ │ ├── ActionResponse.less
│ │ │ │ │ ├── ActionResponse.tsx
│ │ │ │ │ ├── createForm.md
│ │ │ │ │ ├── createForm.zh-CN.md
│ │ │ │ │ ├── FieldEffectHooks.md
│ │ │ │ │ ├── FieldEffectHooks.zh-CN.md
│ │ │ │ │ ├── FormChecker.md
│ │ │ │ │ ├── FormChecker.zh-CN.md
│ │ │ │ │ ├── FormEffectHooks.md
│ │ │ │ │ ├── FormEffectHooks.zh-CN.md
│ │ │ │ │ ├── FormHooksAPI.md
│ │ │ │ │ ├── FormHooksAPI.zh-CN.md
│ │ │ │ │ ├── FormPath.md
│ │ │ │ │ ├── FormPath.zh-CN.md
│ │ │ │ │ ├── FormValidatorRegistry.md
│ │ │ │ │ └── FormValidatorRegistry.zh-CN.md
│ │ │ │ └── models
│ │ │ │ ├── ArrayField.md
│ │ │ │ ├── ArrayField.zh-CN.md
│ │ │ │ ├── Field.md
│ │ │ │ ├── Field.zh-CN.md
│ │ │ │ ├── Form.md
│ │ │ │ ├── Form.zh-CN.md
│ │ │ │ ├── ObjectField.md
│ │ │ │ ├── ObjectField.zh-CN.md
│ │ │ │ ├── Query.md
│ │ │ │ ├── Query.zh-CN.md
│ │ │ │ ├── VoidField.md
│ │ │ │ └── VoidField.zh-CN.md
│ │ │ ├── guide
│ │ │ │ ├── architecture.md
│ │ │ │ ├── architecture.zh-CN.md
│ │ │ │ ├── field.md
│ │ │ │ ├── field.zh-CN.md
│ │ │ │ ├── form.md
│ │ │ │ ├── form.zh-CN.md
│ │ │ │ ├── index.md
│ │ │ │ ├── index.zh-CN.md
│ │ │ │ ├── mvvm.md
│ │ │ │ ├── mvvm.zh-CN.md
│ │ │ │ ├── values.md
│ │ │ │ └── values.zh-CN.md
│ │ │ ├── index.md
│ │ │ └── index.zh-CN.md
│ │ ├── LICENSE.md
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── rollup.config.js
│ │ ├── src
│ │ │ ├── __tests__
│ │ │ │ ├── array.spec.ts
│ │ │ │ ├── effects.spec.ts
│ │ │ │ ├── externals.spec.ts
│ │ │ │ ├── field.spec.ts
│ │ │ │ ├── form.spec.ts
│ │ │ │ ├── graph.spec.ts
│ │ │ │ ├── heart.spec.ts
│ │ │ │ ├── internals.spec.ts
│ │ │ │ ├── lifecycle.spec.ts
│ │ │ │ ├── object.spec.ts
│ │ │ │ ├── shared.ts
│ │ │ │ └── void.spec.ts
│ │ │ ├── effects
│ │ │ │ ├── index.ts
│ │ │ │ ├── onFieldEffects.ts
│ │ │ │ └── onFormEffects.ts
│ │ │ ├── global.d.ts
│ │ │ ├── index.ts
│ │ │ ├── models
│ │ │ │ ├── ArrayField.ts
│ │ │ │ ├── BaseField.ts
│ │ │ │ ├── Field.ts
│ │ │ │ ├── Form.ts
│ │ │ │ ├── Graph.ts
│ │ │ │ ├── Heart.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── LifeCycle.ts
│ │ │ │ ├── ObjectField.ts
│ │ │ │ ├── Query.ts
│ │ │ │ ├── types.ts
│ │ │ │ └── VoidField.ts
│ │ │ ├── shared
│ │ │ │ ├── checkers.ts
│ │ │ │ ├── constants.ts
│ │ │ │ ├── effective.ts
│ │ │ │ ├── externals.ts
│ │ │ │ └── internals.ts
│ │ │ └── types.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── element
│ │ ├── .npmignore
│ │ ├── build-style.ts
│ │ ├── create-style.ts
│ │ ├── docs
│ │ │ ├── .vuepress
│ │ │ │ ├── components
│ │ │ │ │ ├── createCodeSandBox.js
│ │ │ │ │ ├── dumi-previewer.vue
│ │ │ │ │ └── highlight.js
│ │ │ │ ├── config.js
│ │ │ │ ├── enhanceApp.js
│ │ │ │ ├── styles
│ │ │ │ │ └── index.styl
│ │ │ │ └── util.js
│ │ │ ├── demos
│ │ │ │ ├── guide
│ │ │ │ │ ├── array-cards
│ │ │ │ │ │ ├── effects-json-schema.vue
│ │ │ │ │ │ ├── effects-markup-schema.vue
│ │ │ │ │ │ ├── json-schema.vue
│ │ │ │ │ │ └── markup-schema.vue
│ │ │ │ │ ├── array-collapse
│ │ │ │ │ │ ├── effects-json-schema.vue
│ │ │ │ │ │ ├── effects-markup-schema.vue
│ │ │ │ │ │ ├── json-schema.vue
│ │ │ │ │ │ └── markup-schema.vue
│ │ │ │ │ ├── array-items
│ │ │ │ │ │ ├── json-schema.vue
│ │ │ │ │ │ └── markup-schema.vue
│ │ │ │ │ ├── array-table
│ │ │ │ │ │ ├── effects-json-schema.vue
│ │ │ │ │ │ ├── effects-markup-schema.vue
│ │ │ │ │ │ ├── json-schema.vue
│ │ │ │ │ │ └── markup-schema.vue
│ │ │ │ │ ├── array-tabs
│ │ │ │ │ │ ├── json-schema.vue
│ │ │ │ │ │ └── markup-schema.vue
│ │ │ │ │ ├── cascader
│ │ │ │ │ │ ├── json-schema.vue
│ │ │ │ │ │ ├── markup-schema.vue
│ │ │ │ │ │ └── template.vue
│ │ │ │ │ ├── checkbox
│ │ │ │ │ │ ├── json-schema.vue
│ │ │ │ │ │ ├── markup-schema.vue
│ │ │ │ │ │ └── template.vue
│ │ │ │ │ ├── date-picker
│ │ │ │ │ │ ├── json-schema.vue
│ │ │ │ │ │ ├── markup-schema.vue
│ │ │ │ │ │ └── template.vue
│ │ │ │ │ ├── editable
│ │ │ │ │ │ ├── json-schema.vue
│ │ │ │ │ │ ├── markup-schema.vue
│ │ │ │ │ │ └── template.vue
│ │ │ │ │ ├── form-button-group.vue
│ │ │ │ │ ├── form-collapse
│ │ │ │ │ │ ├── json-schema.vue
│ │ │ │ │ │ └── markup-schema.vue
│ │ │ │ │ ├── form-dialog
│ │ │ │ │ │ ├── json-schema.vue
│ │ │ │ │ │ ├── markup-schema.vue
│ │ │ │ │ │ └── template.vue
│ │ │ │ │ ├── form-drawer
│ │ │ │ │ │ ├── json-schema.vue
│ │ │ │ │ │ ├── markup-schema.vue
│ │ │ │ │ │ └── template.vue
│ │ │ │ │ ├── form-grid
│ │ │ │ │ │ ├── form.vue
│ │ │ │ │ │ ├── json-schema.vue
│ │ │ │ │ │ ├── markup-schema.vue
│ │ │ │ │ │ └── native.vue
│ │ │ │ │ ├── form-item
│ │ │ │ │ │ ├── bordered-none.vue
│ │ │ │ │ │ ├── common.vue
│ │ │ │ │ │ ├── feedback.vue
│ │ │ │ │ │ ├── inset.vue
│ │ │ │ │ │ ├── json-schema.vue
│ │ │ │ │ │ ├── markup-schema.vue
│ │ │ │ │ │ ├── size.vue
│ │ │ │ │ │ └── template.vue
│ │ │ │ │ ├── form-layout
│ │ │ │ │ │ ├── json-schema.vue
│ │ │ │ │ │ ├── markup-schema.vue
│ │ │ │ │ │ └── template.vue
│ │ │ │ │ ├── form-step
│ │ │ │ │ │ ├── json-schema.vue
│ │ │ │ │ │ └── markup-schema.vue
│ │ │ │ │ ├── form-tab
│ │ │ │ │ │ ├── json-schema.vue
│ │ │ │ │ │ └── markup-schema.vue
│ │ │ │ │ ├── form.vue
│ │ │ │ │ ├── input
│ │ │ │ │ │ ├── json-schema.vue
│ │ │ │ │ │ ├── markup-schema.vue
│ │ │ │ │ │ └── template.vue
│ │ │ │ │ ├── input-number
│ │ │ │ │ │ ├── json-schema.vue
│ │ │ │ │ │ ├── markup-schema.vue
│ │ │ │ │ │ └── template.vue
│ │ │ │ │ ├── password
│ │ │ │ │ │ ├── json-schema.vue
│ │ │ │ │ │ ├── markup-schema.vue
│ │ │ │ │ │ └── template.vue
│ │ │ │ │ ├── preview-text
│ │ │ │ │ │ ├── base.vue
│ │ │ │ │ │ └── extend.vue
│ │ │ │ │ ├── radio
│ │ │ │ │ │ ├── json-schema.vue
│ │ │ │ │ │ ├── markup-schema.vue
│ │ │ │ │ │ └── template.vue
│ │ │ │ │ ├── reset
│ │ │ │ │ │ ├── base.vue
│ │ │ │ │ │ ├── force.vue
│ │ │ │ │ │ └── validate.vue
│ │ │ │ │ ├── select
│ │ │ │ │ │ ├── json-schema-async.vue
│ │ │ │ │ │ ├── json-schema-sync.vue
│ │ │ │ │ │ ├── markup-schema-async-search.vue
│ │ │ │ │ │ ├── markup-schema-async.vue
│ │ │ │ │ │ ├── markup-schema-sync.vue
│ │ │ │ │ │ ├── template-async.vue
│ │ │ │ │ │ └── template-sync.vue
│ │ │ │ │ ├── space
│ │ │ │ │ │ ├── json-schema.vue
│ │ │ │ │ │ ├── markup-schema.vue
│ │ │ │ │ │ └── template.vue
│ │ │ │ │ ├── submit
│ │ │ │ │ │ ├── base.vue
│ │ │ │ │ │ └── loading.vue
│ │ │ │ │ ├── switch
│ │ │ │ │ │ ├── json-schema.vue
│ │ │ │ │ │ ├── markup-schema.vue
│ │ │ │ │ │ └── template.vue
│ │ │ │ │ ├── time-picker
│ │ │ │ │ │ ├── json-schema.vue
│ │ │ │ │ │ ├── markup-schema.vue
│ │ │ │ │ │ └── template.vue
│ │ │ │ │ ├── transfer
│ │ │ │ │ │ ├── json-schema.vue
│ │ │ │ │ │ ├── markup-schema.vue
│ │ │ │ │ │ └── template.vue
│ │ │ │ │ └── upload
│ │ │ │ │ ├── json-schema.vue
│ │ │ │ │ ├── markup-schema.vue
│ │ │ │ │ └── template.vue
│ │ │ │ └── index.vue
│ │ │ ├── guide
│ │ │ │ ├── array-cards.md
│ │ │ │ ├── array-collapse.md
│ │ │ │ ├── array-items.md
│ │ │ │ ├── array-table.md
│ │ │ │ ├── array-tabs.md
│ │ │ │ ├── cascader.md
│ │ │ │ ├── checkbox.md
│ │ │ │ ├── date-picker.md
│ │ │ │ ├── editable.md
│ │ │ │ ├── form-button-group.md
│ │ │ │ ├── form-collapse.md
│ │ │ │ ├── form-dialog.md
│ │ │ │ ├── form-drawer.md
│ │ │ │ ├── form-grid.md
│ │ │ │ ├── form-item.md
│ │ │ │ ├── form-layout.md
│ │ │ │ ├── form-step.md
│ │ │ │ ├── form-tab.md
│ │ │ │ ├── form.md
│ │ │ │ ├── index.md
│ │ │ │ ├── input-number.md
│ │ │ │ ├── input.md
│ │ │ │ ├── password.md
│ │ │ │ ├── preview-text.md
│ │ │ │ ├── radio.md
│ │ │ │ ├── reset.md
│ │ │ │ ├── select.md
│ │ │ │ ├── space.md
│ │ │ │ ├── submit.md
│ │ │ │ ├── switch.md
│ │ │ │ ├── time-picker.md
│ │ │ │ ├── transfer.md
│ │ │ │ └── upload.md
│ │ │ └── README.md
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── rollup.config.js
│ │ ├── src
│ │ │ ├── __builtins__
│ │ │ │ ├── configs
│ │ │ │ │ └── index.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── shared
│ │ │ │ │ ├── create-context.ts
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── loading.ts
│ │ │ │ │ ├── portal.ts
│ │ │ │ │ ├── resolve-component.ts
│ │ │ │ │ ├── transform-component.ts
│ │ │ │ │ ├── types.ts
│ │ │ │ │ └── utils.ts
│ │ │ │ └── styles
│ │ │ │ └── common.scss
│ │ │ ├── array-base
│ │ │ │ ├── index.ts
│ │ │ │ ├── style.scss
│ │ │ │ └── style.ts
│ │ │ ├── array-cards
│ │ │ │ ├── index.ts
│ │ │ │ ├── style.scss
│ │ │ │ └── style.ts
│ │ │ ├── array-collapse
│ │ │ │ ├── index.ts
│ │ │ │ ├── style.scss
│ │ │ │ └── style.ts
│ │ │ ├── array-items
│ │ │ │ ├── index.ts
│ │ │ │ ├── style.scss
│ │ │ │ └── style.ts
│ │ │ ├── array-table
│ │ │ │ ├── index.ts
│ │ │ │ ├── style.scss
│ │ │ │ └── style.ts
│ │ │ ├── array-tabs
│ │ │ │ ├── index.ts
│ │ │ │ ├── style.scss
│ │ │ │ └── style.ts
│ │ │ ├── cascader
│ │ │ │ ├── index.ts
│ │ │ │ └── style.ts
│ │ │ ├── checkbox
│ │ │ │ ├── index.ts
│ │ │ │ └── style.ts
│ │ │ ├── date-picker
│ │ │ │ ├── index.ts
│ │ │ │ └── style.ts
│ │ │ ├── editable
│ │ │ │ ├── index.ts
│ │ │ │ ├── style.scss
│ │ │ │ └── style.ts
│ │ │ ├── el-form
│ │ │ │ ├── index.ts
│ │ │ │ └── style.ts
│ │ │ ├── el-form-item
│ │ │ │ ├── index.ts
│ │ │ │ └── style.ts
│ │ │ ├── form
│ │ │ │ ├── index.ts
│ │ │ │ ├── style.scss
│ │ │ │ └── style.ts
│ │ │ ├── form-button-group
│ │ │ │ ├── index.ts
│ │ │ │ ├── style.scss
│ │ │ │ └── style.ts
│ │ │ ├── form-collapse
│ │ │ │ ├── index.ts
│ │ │ │ ├── style.scss
│ │ │ │ └── style.ts
│ │ │ ├── form-dialog
│ │ │ │ ├── index.ts
│ │ │ │ └── style.ts
│ │ │ ├── form-drawer
│ │ │ │ ├── index.ts
│ │ │ │ ├── style.scss
│ │ │ │ └── style.ts
│ │ │ ├── form-grid
│ │ │ │ ├── index.ts
│ │ │ │ ├── style.scss
│ │ │ │ └── style.ts
│ │ │ ├── form-item
│ │ │ │ ├── animation.scss
│ │ │ │ ├── grid.scss
│ │ │ │ ├── index.ts
│ │ │ │ ├── style.scss
│ │ │ │ ├── style.ts
│ │ │ │ └── var.scss
│ │ │ ├── form-layout
│ │ │ │ ├── index.ts
│ │ │ │ ├── style.scss
│ │ │ │ ├── style.ts
│ │ │ │ └── useResponsiveFormLayout.ts
│ │ │ ├── form-step
│ │ │ │ ├── index.ts
│ │ │ │ └── style.ts
│ │ │ ├── form-tab
│ │ │ │ ├── index.ts
│ │ │ │ ├── style.scss
│ │ │ │ └── style.ts
│ │ │ ├── index.ts
│ │ │ ├── input
│ │ │ │ ├── index.ts
│ │ │ │ └── style.ts
│ │ │ ├── input-number
│ │ │ │ ├── index.ts
│ │ │ │ └── style.ts
│ │ │ ├── password
│ │ │ │ ├── index.ts
│ │ │ │ └── style.ts
│ │ │ ├── preview-text
│ │ │ │ ├── index.ts
│ │ │ │ └── style.ts
│ │ │ ├── radio
│ │ │ │ ├── index.ts
│ │ │ │ └── style.ts
│ │ │ ├── reset
│ │ │ │ ├── index.ts
│ │ │ │ └── style.ts
│ │ │ ├── select
│ │ │ │ ├── index.ts
│ │ │ │ └── style.ts
│ │ │ ├── space
│ │ │ │ ├── index.ts
│ │ │ │ ├── style.scss
│ │ │ │ └── style.ts
│ │ │ ├── style.ts
│ │ │ ├── submit
│ │ │ │ ├── index.ts
│ │ │ │ └── style.ts
│ │ │ ├── switch
│ │ │ │ ├── index.ts
│ │ │ │ └── style.ts
│ │ │ ├── time-picker
│ │ │ │ ├── index.ts
│ │ │ │ └── style.ts
│ │ │ ├── transfer
│ │ │ │ ├── index.ts
│ │ │ │ └── style.ts
│ │ │ └── upload
│ │ │ ├── index.ts
│ │ │ └── style.ts
│ │ ├── transformer.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── grid
│ │ ├── .npmignore
│ │ ├── LICENSE.md
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── rollup.config.js
│ │ ├── src
│ │ │ ├── index.ts
│ │ │ └── observer.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── json-schema
│ │ ├── .npmignore
│ │ ├── LICENSE.md
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── rollup.config.js
│ │ ├── src
│ │ │ ├── __tests__
│ │ │ │ ├── __snapshots__
│ │ │ │ │ └── schema.spec.ts.snap
│ │ │ │ ├── compiler.spec.ts
│ │ │ │ ├── patches.spec.ts
│ │ │ │ ├── schema.spec.ts
│ │ │ │ ├── server-validate.spec.ts
│ │ │ │ ├── shared.spec.ts
│ │ │ │ ├── transformer.spec.ts
│ │ │ │ └── traverse.spec.ts
│ │ │ ├── compiler.ts
│ │ │ ├── global.d.ts
│ │ │ ├── index.ts
│ │ │ ├── patches.ts
│ │ │ ├── polyfills
│ │ │ │ ├── index.ts
│ │ │ │ └── SPECIFICATION_1_0.ts
│ │ │ ├── schema.ts
│ │ │ ├── shared.ts
│ │ │ ├── transformer.ts
│ │ │ └── types.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── next
│ │ ├── __tests__
│ │ │ ├── moment.spec.ts
│ │ │ └── sideEffects.spec.ts
│ │ ├── .npmignore
│ │ ├── .umirc.js
│ │ ├── build-style.ts
│ │ ├── create-style.ts
│ │ ├── docs
│ │ │ ├── components
│ │ │ │ ├── ArrayCards.md
│ │ │ │ ├── ArrayCards.zh-CN.md
│ │ │ │ ├── ArrayCollapse.md
│ │ │ │ ├── ArrayCollapse.zh-CN.md
│ │ │ │ ├── ArrayItems.md
│ │ │ │ ├── ArrayItems.zh-CN.md
│ │ │ │ ├── ArrayTable.md
│ │ │ │ ├── ArrayTable.zh-CN.md
│ │ │ │ ├── Cascader.md
│ │ │ │ ├── Cascader.zh-CN.md
│ │ │ │ ├── Checkbox.md
│ │ │ │ ├── Checkbox.zh-CN.md
│ │ │ │ ├── DatePicker.md
│ │ │ │ ├── DatePicker.zh-CN.md
│ │ │ │ ├── DatePicker2.md
│ │ │ │ ├── DatePicker2.zh-CN.md
│ │ │ │ ├── Editable.md
│ │ │ │ ├── Editable.zh-CN.md
│ │ │ │ ├── Form.md
│ │ │ │ ├── Form.zh-CN.md
│ │ │ │ ├── FormButtonGroup.md
│ │ │ │ ├── FormButtonGroup.zh-CN.md
│ │ │ │ ├── FormCollapse.md
│ │ │ │ ├── FormCollapse.zh-CN.md
│ │ │ │ ├── FormDialog.md
│ │ │ │ ├── FormDialog.zh-CN.md
│ │ │ │ ├── FormDrawer.md
│ │ │ │ ├── FormDrawer.zh-CN.md
│ │ │ │ ├── FormGrid.md
│ │ │ │ ├── FormGrid.zh-CN.md
│ │ │ │ ├── FormItem.md
│ │ │ │ ├── FormItem.zh-CN.md
│ │ │ │ ├── FormLayout.md
│ │ │ │ ├── FormLayout.zh-CN.md
│ │ │ │ ├── FormStep.md
│ │ │ │ ├── FormStep.zh-CN.md
│ │ │ │ ├── FormTab.md
│ │ │ │ ├── FormTab.zh-CN.md
│ │ │ │ ├── index.md
│ │ │ │ ├── index.zh-CN.md
│ │ │ │ ├── Input.md
│ │ │ │ ├── Input.zh-CN.md
│ │ │ │ ├── NumberPicker.md
│ │ │ │ ├── NumberPicker.zh-CN.md
│ │ │ │ ├── Password.md
│ │ │ │ ├── Password.zh-CN.md
│ │ │ │ ├── PreviewText.md
│ │ │ │ ├── PreviewText.zh-CN.md
│ │ │ │ ├── Radio.md
│ │ │ │ ├── Radio.zh-CN.md
│ │ │ │ ├── Reset.md
│ │ │ │ ├── Reset.zh-CN.md
│ │ │ │ ├── Select.md
│ │ │ │ ├── Select.zh-CN.md
│ │ │ │ ├── SelectTable.md
│ │ │ │ ├── SelectTable.zh-CN.md
│ │ │ │ ├── Space.md
│ │ │ │ ├── Space.zh-CN.md
│ │ │ │ ├── Submit.md
│ │ │ │ ├── Submit.zh-CN.md
│ │ │ │ ├── Switch.md
│ │ │ │ ├── Switch.zh-CN.md
│ │ │ │ ├── TimePicker.md
│ │ │ │ ├── TimePicker.zh-CN.md
│ │ │ │ ├── TimePicker2.md
│ │ │ │ ├── TimePicker2.zh-CN.md
│ │ │ │ ├── Transfer.md
│ │ │ │ ├── Transfer.zh-CN.md
│ │ │ │ ├── TreeSelect.md
│ │ │ │ ├── TreeSelect.zh-CN.md
│ │ │ │ ├── Upload.md
│ │ │ │ └── Upload.zh-CN.md
│ │ │ ├── index.md
│ │ │ └── index.zh-CN.md
│ │ ├── LESENCE.md
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── rollup.config.js
│ │ ├── src
│ │ │ ├── __builtins__
│ │ │ │ ├── empty.tsx
│ │ │ │ ├── hooks
│ │ │ │ │ ├── index.ts
│ │ │ │ │ ├── useClickAway.ts
│ │ │ │ │ └── usePrefixCls.ts
│ │ │ │ ├── icons.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── loading.ts
│ │ │ │ ├── mapSize.ts
│ │ │ │ ├── mapStatus.ts
│ │ │ │ ├── moment.ts
│ │ │ │ ├── pickDataProps.ts
│ │ │ │ ├── portal.tsx
│ │ │ │ ├── render.ts
│ │ │ │ └── toArray.ts
│ │ │ ├── array-base
│ │ │ │ ├── index.tsx
│ │ │ │ ├── main.scss
│ │ │ │ └── style.ts
│ │ │ ├── array-cards
│ │ │ │ ├── index.tsx
│ │ │ │ ├── main.scss
│ │ │ │ └── style.ts
│ │ │ ├── array-collapse
│ │ │ │ ├── index.tsx
│ │ │ │ ├── main.scss
│ │ │ │ └── style.ts
│ │ │ ├── array-items
│ │ │ │ ├── index.tsx
│ │ │ │ ├── main.scss
│ │ │ │ └── style.ts
│ │ │ ├── array-table
│ │ │ │ ├── index.tsx
│ │ │ │ ├── main.scss
│ │ │ │ └── style.ts
│ │ │ ├── cascader
│ │ │ │ ├── index.tsx
│ │ │ │ └── style.ts
│ │ │ ├── checkbox
│ │ │ │ ├── index.tsx
│ │ │ │ └── style.ts
│ │ │ ├── date-picker
│ │ │ │ ├── index.tsx
│ │ │ │ └── style.ts
│ │ │ ├── date-picker2
│ │ │ │ ├── index.tsx
│ │ │ │ ├── main.scss
│ │ │ │ └── style.ts
│ │ │ ├── editable
│ │ │ │ ├── index.tsx
│ │ │ │ ├── main.scss
│ │ │ │ └── style.ts
│ │ │ ├── form
│ │ │ │ ├── index.tsx
│ │ │ │ ├── main.scss
│ │ │ │ └── style.ts
│ │ │ ├── form-button-group
│ │ │ │ ├── index.tsx
│ │ │ │ ├── main.scss
│ │ │ │ └── style.ts
│ │ │ ├── form-collapse
│ │ │ │ ├── index.tsx
│ │ │ │ └── style.ts
│ │ │ ├── form-dialog
│ │ │ │ ├── index.tsx
│ │ │ │ └── style.ts
│ │ │ ├── form-drawer
│ │ │ │ ├── index.tsx
│ │ │ │ └── style.ts
│ │ │ ├── form-grid
│ │ │ │ ├── index.tsx
│ │ │ │ ├── main.scss
│ │ │ │ └── style.ts
│ │ │ ├── form-item
│ │ │ │ ├── animation.scss
│ │ │ │ ├── grid.scss
│ │ │ │ ├── index.tsx
│ │ │ │ ├── main.scss
│ │ │ │ ├── scss
│ │ │ │ │ └── variable.scss
│ │ │ │ └── style.ts
│ │ │ ├── form-layout
│ │ │ │ ├── index.tsx
│ │ │ │ ├── main.scss
│ │ │ │ ├── style.ts
│ │ │ │ └── useResponsiveFormLayout.ts
│ │ │ ├── form-step
│ │ │ │ ├── index.tsx
│ │ │ │ └── style.ts
│ │ │ ├── form-tab
│ │ │ │ ├── index.tsx
│ │ │ │ └── style.ts
│ │ │ ├── index.ts
│ │ │ ├── input
│ │ │ │ ├── index.tsx
│ │ │ │ └── style.ts
│ │ │ ├── main.scss
│ │ │ ├── number-picker
│ │ │ │ ├── index.tsx
│ │ │ │ └── style.ts
│ │ │ ├── password
│ │ │ │ ├── index.tsx
│ │ │ │ ├── PasswordStrength.tsx
│ │ │ │ └── style.ts
│ │ │ ├── preview-text
│ │ │ │ ├── index.tsx
│ │ │ │ ├── main.scss
│ │ │ │ └── style.ts
│ │ │ ├── radio
│ │ │ │ ├── index.tsx
│ │ │ │ └── style.ts
│ │ │ ├── reset
│ │ │ │ ├── index.tsx
│ │ │ │ └── style.ts
│ │ │ ├── select
│ │ │ │ ├── index.tsx
│ │ │ │ └── style.ts
│ │ │ ├── select-table
│ │ │ │ ├── index.tsx
│ │ │ │ ├── main.scss
│ │ │ │ ├── style.ts
│ │ │ │ ├── useCheckSlackly.tsx
│ │ │ │ ├── useFilterOptions.tsx
│ │ │ │ ├── useFlatOptions.tsx
│ │ │ │ ├── useSize.tsx
│ │ │ │ ├── useTitleAddon.tsx
│ │ │ │ └── utils.ts
│ │ │ ├── space
│ │ │ │ ├── index.tsx
│ │ │ │ ├── main.scss
│ │ │ │ └── style.ts
│ │ │ ├── style.ts
│ │ │ ├── submit
│ │ │ │ ├── index.tsx
│ │ │ │ └── style.ts
│ │ │ ├── switch
│ │ │ │ ├── index.tsx
│ │ │ │ └── style.ts
│ │ │ ├── time-picker
│ │ │ │ ├── index.tsx
│ │ │ │ └── style.ts
│ │ │ ├── time-picker2
│ │ │ │ ├── index.tsx
│ │ │ │ └── style.ts
│ │ │ ├── transfer
│ │ │ │ ├── index.tsx
│ │ │ │ └── style.ts
│ │ │ ├── tree-select
│ │ │ │ ├── index.tsx
│ │ │ │ └── style.ts
│ │ │ └── upload
│ │ │ ├── index.tsx
│ │ │ ├── main.scss
│ │ │ ├── placeholder.ts
│ │ │ └── style.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── path
│ │ ├── .npmignore
│ │ ├── benchmark.ts
│ │ ├── LICENSE.md
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── rollup.config.js
│ │ ├── src
│ │ │ ├── __tests__
│ │ │ │ ├── accessor.spec.ts
│ │ │ │ ├── basic.spec.ts
│ │ │ │ ├── match.spec.ts
│ │ │ │ ├── parser.spec.ts
│ │ │ │ └── share.spec.ts
│ │ │ ├── contexts.ts
│ │ │ ├── destructor.ts
│ │ │ ├── index.ts
│ │ │ ├── matcher.ts
│ │ │ ├── parser.ts
│ │ │ ├── shared.ts
│ │ │ ├── tokenizer.ts
│ │ │ ├── tokens.ts
│ │ │ └── types.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── react
│ │ ├── .npmignore
│ │ ├── .umirc.js
│ │ ├── docs
│ │ │ ├── api
│ │ │ │ ├── components
│ │ │ │ │ ├── ArrayField.md
│ │ │ │ │ ├── ArrayField.zh-CN.md
│ │ │ │ │ ├── ExpressionScope.md
│ │ │ │ │ ├── ExpressionScope.zh-CN.md
│ │ │ │ │ ├── Field.md
│ │ │ │ │ ├── Field.zh-CN.md
│ │ │ │ │ ├── FormConsumer.md
│ │ │ │ │ ├── FormConsumer.zh-CN.md
│ │ │ │ │ ├── FormProvider.md
│ │ │ │ │ ├── FormProvider.zh-CN.md
│ │ │ │ │ ├── ObjectField.md
│ │ │ │ │ ├── ObjectField.zh-CN.md
│ │ │ │ │ ├── RecordScope.md
│ │ │ │ │ ├── RecordScope.zh-CN.md
│ │ │ │ │ ├── RecordsScope.md
│ │ │ │ │ ├── RecordsScope.zh-CN.md
│ │ │ │ │ ├── RecursionField.md
│ │ │ │ │ ├── RecursionField.zh-CN.md
│ │ │ │ │ ├── SchemaField.md
│ │ │ │ │ ├── SchemaField.zh-CN.md
│ │ │ │ │ ├── VoidField.md
│ │ │ │ │ └── VoidField.zh-CN.md
│ │ │ │ ├── hooks
│ │ │ │ │ ├── useExpressionScope.md
│ │ │ │ │ ├── useExpressionScope.zh-CN.md
│ │ │ │ │ ├── useField.md
│ │ │ │ │ ├── useField.zh-CN.md
│ │ │ │ │ ├── useFieldSchema.md
│ │ │ │ │ ├── useFieldSchema.zh-CN.md
│ │ │ │ │ ├── useForm.md
│ │ │ │ │ ├── useForm.zh-CN.md
│ │ │ │ │ ├── useFormEffects.md
│ │ │ │ │ ├── useFormEffects.zh-CN.md
│ │ │ │ │ ├── useParentForm.md
│ │ │ │ │ └── useParentForm.zh-CN.md
│ │ │ │ └── shared
│ │ │ │ ├── connect.md
│ │ │ │ ├── connect.zh-CN.md
│ │ │ │ ├── context.md
│ │ │ │ ├── context.zh-CN.md
│ │ │ │ ├── mapProps.md
│ │ │ │ ├── mapProps.zh-CN.md
│ │ │ │ ├── mapReadPretty.md
│ │ │ │ ├── mapReadPretty.zh-CN.md
│ │ │ │ ├── observer.md
│ │ │ │ ├── observer.zh-CN.md
│ │ │ │ ├── Schema.md
│ │ │ │ └── Schema.zh-CN.md
│ │ │ ├── guide
│ │ │ │ ├── architecture.md
│ │ │ │ ├── architecture.zh-CN.md
│ │ │ │ ├── concept.md
│ │ │ │ ├── concept.zh-CN.md
│ │ │ │ ├── index.md
│ │ │ │ └── index.zh-CN.md
│ │ │ ├── index.md
│ │ │ └── index.zh-CN.md
│ │ ├── LICENSE.md
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── rollup.config.js
│ │ ├── src
│ │ │ ├── __tests__
│ │ │ │ ├── expression.spec.tsx
│ │ │ │ ├── field.spec.tsx
│ │ │ │ ├── form.spec.tsx
│ │ │ │ ├── schema.json.spec.tsx
│ │ │ │ ├── schema.markup.spec.tsx
│ │ │ │ └── shared.tsx
│ │ │ ├── components
│ │ │ │ ├── ArrayField.tsx
│ │ │ │ ├── ExpressionScope.tsx
│ │ │ │ ├── Field.tsx
│ │ │ │ ├── FormConsumer.tsx
│ │ │ │ ├── FormProvider.tsx
│ │ │ │ ├── index.ts
│ │ │ │ ├── ObjectField.tsx
│ │ │ │ ├── ReactiveField.tsx
│ │ │ │ ├── RecordScope.tsx
│ │ │ │ ├── RecordsScope.tsx
│ │ │ │ ├── RecursionField.tsx
│ │ │ │ ├── SchemaField.tsx
│ │ │ │ └── VoidField.tsx
│ │ │ ├── global.d.ts
│ │ │ ├── hooks
│ │ │ │ ├── index.ts
│ │ │ │ ├── useAttach.ts
│ │ │ │ ├── useExpressionScope.ts
│ │ │ │ ├── useField.ts
│ │ │ │ ├── useFieldSchema.ts
│ │ │ │ ├── useForm.ts
│ │ │ │ ├── useFormEffects.ts
│ │ │ │ └── useParentForm.ts
│ │ │ ├── index.ts
│ │ │ ├── shared
│ │ │ │ ├── connect.ts
│ │ │ │ ├── context.ts
│ │ │ │ ├── index.ts
│ │ │ │ └── render.ts
│ │ │ └── types.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── reactive
│ │ ├── .npmignore
│ │ ├── .umirc.js
│ │ ├── benchmark.ts
│ │ ├── docs
│ │ │ ├── api
│ │ │ │ ├── action.md
│ │ │ │ ├── action.zh-CN.md
│ │ │ │ ├── autorun.md
│ │ │ │ ├── autorun.zh-CN.md
│ │ │ │ ├── batch.md
│ │ │ │ ├── batch.zh-CN.md
│ │ │ │ ├── define.md
│ │ │ │ ├── define.zh-CN.md
│ │ │ │ ├── hasCollected.md
│ │ │ │ ├── hasCollected.zh-CN.md
│ │ │ │ ├── markObservable.md
│ │ │ │ ├── markObservable.zh-CN.md
│ │ │ │ ├── markRaw.md
│ │ │ │ ├── markRaw.zh-CN.md
│ │ │ │ ├── model.md
│ │ │ │ ├── model.zh-CN.md
│ │ │ │ ├── observable.md
│ │ │ │ ├── observable.zh-CN.md
│ │ │ │ ├── observe.md
│ │ │ │ ├── observe.zh-CN.md
│ │ │ │ ├── raw.md
│ │ │ │ ├── raw.zh-CN.md
│ │ │ │ ├── react
│ │ │ │ │ ├── observer.md
│ │ │ │ │ └── observer.zh-CN.md
│ │ │ │ ├── reaction.md
│ │ │ │ ├── reaction.zh-CN.md
│ │ │ │ ├── toJS.md
│ │ │ │ ├── toJS.zh-CN.md
│ │ │ │ ├── tracker.md
│ │ │ │ ├── tracker.zh-CN.md
│ │ │ │ ├── typeChecker.md
│ │ │ │ ├── typeChecker.zh-CN.md
│ │ │ │ ├── untracked.md
│ │ │ │ ├── untracked.zh-CN.md
│ │ │ │ └── vue
│ │ │ │ ├── observer.md
│ │ │ │ └── observer.zh-CN.md
│ │ │ ├── guide
│ │ │ │ ├── best-practice.md
│ │ │ │ ├── best-practice.zh-CN.md
│ │ │ │ ├── concept.md
│ │ │ │ ├── concept.zh-CN.md
│ │ │ │ ├── index.md
│ │ │ │ └── index.zh-CN.md
│ │ │ ├── index.md
│ │ │ └── index.zh-CN.md
│ │ ├── LICENSE.md
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── rollup.config.js
│ │ ├── src
│ │ │ ├── __tests__
│ │ │ │ ├── action.spec.ts
│ │ │ │ ├── annotations.spec.ts
│ │ │ │ ├── array.spec.ts
│ │ │ │ ├── autorun.spec.ts
│ │ │ │ ├── batch.spec.ts
│ │ │ │ ├── collections-map.spec.ts
│ │ │ │ ├── collections-set.spec.ts
│ │ │ │ ├── collections-weakmap.spec.ts
│ │ │ │ ├── collections-weakset.spec.ts
│ │ │ │ ├── define.spec.ts
│ │ │ │ ├── externals.spec.ts
│ │ │ │ ├── hasCollected.spec.ts
│ │ │ │ ├── observable.spec.ts
│ │ │ │ ├── observe.spec.ts
│ │ │ │ ├── tracker.spec.ts
│ │ │ │ └── untracked.spec.ts
│ │ │ ├── action.ts
│ │ │ ├── annotations
│ │ │ │ ├── box.ts
│ │ │ │ ├── computed.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── observable.ts
│ │ │ │ ├── ref.ts
│ │ │ │ └── shallow.ts
│ │ │ ├── array.ts
│ │ │ ├── autorun.ts
│ │ │ ├── batch.ts
│ │ │ ├── checkers.ts
│ │ │ ├── environment.ts
│ │ │ ├── externals.ts
│ │ │ ├── global.d.ts
│ │ │ ├── handlers.ts
│ │ │ ├── index.ts
│ │ │ ├── internals.ts
│ │ │ ├── model.ts
│ │ │ ├── observable.ts
│ │ │ ├── observe.ts
│ │ │ ├── reaction.ts
│ │ │ ├── tracker.ts
│ │ │ ├── tree.ts
│ │ │ ├── types.ts
│ │ │ └── untracked.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── reactive-react
│ │ ├── .npmignore
│ │ ├── .umirc.js
│ │ ├── LICENSE.md
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── rollup.config.js
│ │ ├── src
│ │ │ ├── hooks
│ │ │ │ ├── index.ts
│ │ │ │ ├── useCompatEffect.ts
│ │ │ │ ├── useCompatFactory.ts
│ │ │ │ ├── useDidUpdate.ts
│ │ │ │ ├── useForceUpdate.ts
│ │ │ │ ├── useLayoutEffect.ts
│ │ │ │ └── useObserver.ts
│ │ │ ├── index.ts
│ │ │ ├── observer.ts
│ │ │ ├── shared
│ │ │ │ ├── gc.ts
│ │ │ │ ├── global.ts
│ │ │ │ ├── immediate.ts
│ │ │ │ └── index.ts
│ │ │ └── types.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── reactive-test-cases-for-react18
│ │ ├── .npmignore
│ │ ├── .umirc.js
│ │ ├── LICENSE.md
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── src
│ │ │ ├── index.js
│ │ │ └── MySlowList.js
│ │ ├── template.ejs
│ │ ├── tsconfig.build.json
│ │ ├── tsconfig.json
│ │ ├── webpack.base.ts
│ │ ├── webpack.dev.ts
│ │ └── webpack.prod.ts
│ ├── reactive-vue
│ │ ├── .npmignore
│ │ ├── LICENSE.md
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── rollup.config.js
│ │ ├── src
│ │ │ ├── __tests__
│ │ │ │ └── observer.spec.ts
│ │ │ ├── hooks
│ │ │ │ ├── index.ts
│ │ │ │ └── useObserver.ts
│ │ │ ├── index.ts
│ │ │ ├── observer
│ │ │ │ ├── collectData.ts
│ │ │ │ ├── index.ts
│ │ │ │ ├── observerInVue2.ts
│ │ │ │ └── observerInVue3.ts
│ │ │ └── types.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── shared
│ │ ├── .npmignore
│ │ ├── LICENSE.md
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── rollup.config.js
│ │ ├── src
│ │ │ ├── __tests__
│ │ │ │ └── index.spec.ts
│ │ │ ├── array.ts
│ │ │ ├── case.ts
│ │ │ ├── checkers.ts
│ │ │ ├── clone.ts
│ │ │ ├── compare.ts
│ │ │ ├── defaults.ts
│ │ │ ├── deprecate.ts
│ │ │ ├── global.ts
│ │ │ ├── index.ts
│ │ │ ├── instanceof.ts
│ │ │ ├── isEmpty.ts
│ │ │ ├── merge.ts
│ │ │ ├── middleware.ts
│ │ │ ├── path.ts
│ │ │ ├── string.ts
│ │ │ ├── subscribable.ts
│ │ │ └── uid.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── validator
│ │ ├── .npmignore
│ │ ├── LICENSE.md
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── rollup.config.js
│ │ ├── src
│ │ │ ├── __tests__
│ │ │ │ ├── parser.spec.ts
│ │ │ │ ├── registry.spec.ts
│ │ │ │ └── validator.spec.ts
│ │ │ ├── formats.ts
│ │ │ ├── index.ts
│ │ │ ├── locale.ts
│ │ │ ├── parser.ts
│ │ │ ├── registry.ts
│ │ │ ├── rules.ts
│ │ │ ├── template.ts
│ │ │ ├── types.ts
│ │ │ └── validator.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ └── vue
│ ├── .npmignore
│ ├── bin
│ │ ├── formily-vue-fix.js
│ │ └── formily-vue-switch.js
│ ├── docs
│ │ ├── .vuepress
│ │ │ ├── components
│ │ │ │ ├── createCodeSandBox.js
│ │ │ │ ├── dumi-previewer.vue
│ │ │ │ └── highlight.js
│ │ │ ├── config.js
│ │ │ ├── enhanceApp.js
│ │ │ └── styles
│ │ │ └── index.styl
│ │ ├── api
│ │ │ ├── components
│ │ │ │ ├── array-field.md
│ │ │ │ ├── expression-scope.md
│ │ │ │ ├── field.md
│ │ │ │ ├── form-consumer.md
│ │ │ │ ├── form-provider.md
│ │ │ │ ├── object-field.md
│ │ │ │ ├── recursion-field-with-component.md
│ │ │ │ ├── recursion-field.md
│ │ │ │ ├── schema-field-with-schema.md
│ │ │ │ ├── schema-field.md
│ │ │ │ └── void-field.md
│ │ │ ├── hooks
│ │ │ │ ├── use-field-schema.md
│ │ │ │ ├── use-field.md
│ │ │ │ ├── use-form-effects.md
│ │ │ │ ├── use-form.md
│ │ │ │ └── use-parent-form.md
│ │ │ └── shared
│ │ │ ├── connect.md
│ │ │ ├── injections.md
│ │ │ ├── map-props.md
│ │ │ ├── map-read-pretty.md
│ │ │ ├── observer.md
│ │ │ └── schema.md
│ │ ├── demos
│ │ │ ├── api
│ │ │ │ ├── components
│ │ │ │ │ ├── array-field.vue
│ │ │ │ │ ├── expression-scope.vue
│ │ │ │ │ ├── field.vue
│ │ │ │ │ ├── form-consumer.vue
│ │ │ │ │ ├── form-provider.vue
│ │ │ │ │ ├── object-field.vue
│ │ │ │ │ ├── recursion-field-with-component.vue
│ │ │ │ │ ├── recursion-field.vue
│ │ │ │ │ ├── schema-field-with-schema.vue
│ │ │ │ │ ├── schema-field.vue
│ │ │ │ │ └── void-field.vue
│ │ │ │ ├── hooks
│ │ │ │ │ ├── use-field-schema.vue
│ │ │ │ │ ├── use-field.vue
│ │ │ │ │ ├── use-form-effects.vue
│ │ │ │ │ ├── use-form.vue
│ │ │ │ │ └── use-parent-form.vue
│ │ │ │ └── shared
│ │ │ │ ├── connect.vue
│ │ │ │ ├── map-props.vue
│ │ │ │ ├── map-read-pretty.vue
│ │ │ │ └── observer.vue
│ │ │ ├── index.vue
│ │ │ └── questions
│ │ │ ├── default-slot.vue
│ │ │ ├── events.vue
│ │ │ ├── named-slot.vue
│ │ │ └── scoped-slot.vue
│ │ ├── guide
│ │ │ ├── architecture.md
│ │ │ ├── concept.md
│ │ │ └── README.md
│ │ ├── questions
│ │ │ └── README.md
│ │ └── README.md
│ ├── package.json
│ ├── README.md
│ ├── rollup.config.js
│ ├── scripts
│ │ ├── postinstall.js
│ │ ├── switch-cli.js
│ │ └── utils.js
│ ├── src
│ │ ├── __tests__
│ │ │ ├── expression.scope.spec.ts
│ │ │ ├── field.spec.ts
│ │ │ ├── form.spec.ts
│ │ │ ├── schema.json.spec.ts
│ │ │ ├── schema.markup.spec.ts
│ │ │ ├── shared.spec.ts
│ │ │ └── utils.spec.ts
│ │ ├── components
│ │ │ ├── ArrayField.ts
│ │ │ ├── ExpressionScope.ts
│ │ │ ├── Field.ts
│ │ │ ├── FormConsumer.ts
│ │ │ ├── FormProvider.ts
│ │ │ ├── index.ts
│ │ │ ├── ObjectField.ts
│ │ │ ├── ReactiveField.ts
│ │ │ ├── RecursionField.ts
│ │ │ ├── SchemaField.ts
│ │ │ └── VoidField.ts
│ │ ├── global.d.ts
│ │ ├── hooks
│ │ │ ├── index.ts
│ │ │ ├── useAttach.ts
│ │ │ ├── useField.ts
│ │ │ ├── useFieldSchema.ts
│ │ │ ├── useForm.ts
│ │ │ ├── useFormEffects.ts
│ │ │ ├── useInjectionCleaner.ts
│ │ │ └── useParentForm.ts
│ │ ├── index.ts
│ │ ├── shared
│ │ │ ├── connect.ts
│ │ │ ├── context.ts
│ │ │ ├── createForm.ts
│ │ │ ├── fragment.ts
│ │ │ ├── h.ts
│ │ │ └── index.ts
│ │ ├── types
│ │ │ └── index.ts
│ │ ├── utils
│ │ │ ├── formatVNodeData.ts
│ │ │ ├── getFieldProps.ts
│ │ │ ├── getRawComponent.ts
│ │ │ └── resolveSchemaProps.ts
│ │ └── vue2-components.ts
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ └── tsconfig.types.json
├── README.md
├── README.zh-cn.md
├── scripts
│ ├── build-style
│ │ ├── buildAllStyles.ts
│ │ ├── copy.ts
│ │ ├── helper.ts
│ │ └── index.ts
│ └── rollup.base.js
├── tsconfig.build.json
├── tsconfig.jest.json
├── tsconfig.json
└── yarn.lock
```
# Files
--------------------------------------------------------------------------------
/packages/shared/src/array.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { isArr, isObj, isStr } from './checkers'
2 |
3 | type EachArrayIterator<T> = (currentValue: T, key: number) => void | boolean
4 | type EachStringIterator = (currentValue: string, key: number) => void | boolean
5 | type EachObjectIterator<T = any> = (
6 | currentValue: T,
7 | key: string
8 | ) => void | boolean
9 | type MapArrayIterator<TItem, TResult> = (
10 | currentValue: TItem,
11 | key: number
12 | ) => TResult
13 | type MapStringIterator<TResult> = (currentValue: string, key: number) => TResult
14 | type MapObjectIterator<TItem, TResult> = (
15 | currentValue: TItem,
16 | key: string
17 | ) => TResult
18 | type MemoArrayIterator<T, U> = (
19 | previousValue: U,
20 | currentValue: T,
21 | key: number
22 | ) => U
23 | type MemoStringIterator<T> = (
24 | previousValue: T,
25 | currentValue: string,
26 | key: number
27 | ) => T
28 | type MemoObjectIterator<TValue, TResult> = (
29 | previousValue: TResult,
30 | currentValue: TValue,
31 | key: string
32 | ) => TResult
33 |
34 | export const toArr = (val: any): any[] => (isArr(val) ? val : val ? [val] : [])
35 | export function each(
36 | val: string,
37 | iterator: EachStringIterator,
38 | revert?: boolean
39 | ): void
40 | export function each<T>(
41 | val: T[],
42 | iterator: EachArrayIterator<T>,
43 | revert?: boolean
44 | ): void
45 | export function each<T extends {}, TValue extends T[keyof T]>(
46 | val: T,
47 | iterator: EachObjectIterator<TValue>,
48 | revert?: boolean
49 | ): void
50 | export function each(val: any, iterator: any, revert?: boolean): void {
51 | if (isArr(val) || isStr(val)) {
52 | if (revert) {
53 | for (let i: number = val.length - 1; i >= 0; i--) {
54 | if (iterator(val[i], i) === false) {
55 | return
56 | }
57 | }
58 | } else {
59 | for (let i = 0; i < val.length; i++) {
60 | if (iterator(val[i], i) === false) {
61 | return
62 | }
63 | }
64 | }
65 | } else if (isObj(val)) {
66 | let key: string
67 | for (key in val) {
68 | if (Object.hasOwnProperty.call(val, key)) {
69 | if (iterator(val[key], key) === false) {
70 | return
71 | }
72 | }
73 | }
74 | }
75 | }
76 |
77 | export function map<T>(
78 | val: string,
79 | iterator: MapStringIterator<T>,
80 | revert?: boolean
81 | ): T[]
82 | export function map<TItem, TResult>(
83 | val: TItem[],
84 | iterator: MapArrayIterator<TItem, TResult>,
85 | revert?: boolean
86 | ): TResult[]
87 | export function map<T extends {}, TResult>(
88 | val: T,
89 | iterator: MapObjectIterator<T[keyof T], TResult>,
90 | revert?: boolean
91 | ): Record<keyof T, TResult>
92 | export function map(val: any, iterator: any, revert?: any): any {
93 | const res = isArr(val) || isStr(val) ? [] : {}
94 | each(
95 | val,
96 | (item, key) => {
97 | const value = iterator(item, key)
98 | if (isArr(res)) {
99 | ;(res as any).push(value)
100 | } else {
101 | res[key] = value
102 | }
103 | },
104 | revert
105 | )
106 | return res
107 | }
108 |
109 | export function reduce<T, U>(
110 | val: T[],
111 | iterator: MemoArrayIterator<T, U>,
112 | accumulator?: U,
113 | revert?: boolean
114 | ): U
115 | export function reduce<T>(
116 | val: string,
117 | iterator: MemoStringIterator<T>,
118 | accumulator?: T,
119 | revert?: boolean
120 | ): T
121 | export function reduce<T extends {}, TValue extends T[keyof T], TResult = any>(
122 | val: T,
123 | iterator: MemoObjectIterator<TValue, TResult>,
124 | accumulator?: TResult,
125 | revert?: boolean
126 | ): TResult
127 | export function reduce(
128 | val: any,
129 | iterator: any,
130 | accumulator?: any,
131 | revert?: boolean
132 | ): any {
133 | let result = accumulator
134 | each(
135 | val,
136 | (item, key) => {
137 | result = iterator(result, item, key)
138 | },
139 | revert
140 | )
141 | return result
142 | }
143 |
144 | export function every<T extends string>(
145 | val: T,
146 | iterator: EachStringIterator,
147 | revert?: boolean
148 | ): boolean
149 | export function every<T>(
150 | val: T[],
151 | iterator: EachArrayIterator<T>,
152 | revert?: boolean
153 | ): boolean
154 | export function every<T extends {}>(
155 | val: T,
156 | iterator: EachObjectIterator,
157 | revert?: boolean
158 | ): boolean
159 | export function every(val: any, iterator: any, revert?: boolean): boolean {
160 | let res = true
161 | each(
162 | val,
163 | (item, key) => {
164 | if (!iterator(item, key)) {
165 | res = false
166 | return false
167 | }
168 | },
169 | revert
170 | )
171 | return res
172 | }
173 |
174 | export function some<T extends string>(
175 | val: T,
176 | iterator: EachStringIterator,
177 | revert?: boolean
178 | ): boolean
179 | export function some<T>(
180 | val: T[],
181 | iterator: EachArrayIterator<T>,
182 | revert?: boolean
183 | ): boolean
184 | export function some<T extends {}>(
185 | val: T,
186 | iterator: EachObjectIterator,
187 | revert?: boolean
188 | ): boolean
189 | export function some(val: any, iterator: any, revert?: boolean): boolean {
190 | let res = false
191 | each(
192 | val,
193 | (item, key) => {
194 | if (iterator(item, key)) {
195 | res = true
196 | return false
197 | }
198 | },
199 | revert
200 | )
201 | return res
202 | }
203 |
204 | export function findIndex<T extends string>(
205 | val: T,
206 | iterator: EachStringIterator,
207 | revert?: boolean
208 | ): number
209 | export function findIndex<T>(
210 | val: T[],
211 | iterator: EachArrayIterator<T>,
212 | revert?: boolean
213 | ): number
214 | export function findIndex<T extends {}>(
215 | val: T,
216 | iterator: EachObjectIterator,
217 | revert?: boolean
218 | ): keyof T
219 | export function findIndex(
220 | val: any,
221 | iterator: any,
222 | revert?: boolean
223 | ): string | number {
224 | let res: number | string = -1
225 | each(
226 | val,
227 | (item, key) => {
228 | if (iterator(item, key)) {
229 | res = key
230 | return false
231 | }
232 | },
233 | revert
234 | )
235 | return res
236 | }
237 |
238 | export function find<T extends string>(
239 | val: T,
240 | iterator: EachStringIterator,
241 | revert?: boolean
242 | ): any
243 | export function find<T>(
244 | val: T[],
245 | iterator: EachArrayIterator<T>,
246 | revert?: boolean
247 | ): T
248 | export function find<T extends {}>(
249 | val: T,
250 | iterator: EachObjectIterator,
251 | revert?: boolean
252 | ): T[keyof T]
253 | export function find(val: any, iterator: any, revert?: boolean): any {
254 | let res: any
255 | each(
256 | val,
257 | (item, key) => {
258 | if (iterator(item, key)) {
259 | res = item
260 | return false
261 | }
262 | },
263 | revert
264 | )
265 | return res
266 | }
267 |
268 | export function includes<T extends string>(
269 | val: T,
270 | searchElement: string,
271 | revert?: boolean
272 | ): boolean
273 | export function includes<T>(
274 | val: T[],
275 | searchElement: T,
276 | revert?: boolean
277 | ): boolean
278 | export function includes(val: any, searchElement: any, revert?: boolean) {
279 | if (isStr(val)) return val.includes(searchElement)
280 | return some(val, (item) => item === searchElement, revert)
281 | }
282 |
283 | export function move<T extends any>(
284 | array: T[],
285 | fromIndex: number,
286 | toIndex: number
287 | ) {
288 | if (fromIndex === toIndex) return array
289 |
290 | if (
291 | toIndex < 0 ||
292 | fromIndex < 0 ||
293 | toIndex > array.length - 1 ||
294 | fromIndex > array.length - 1
295 | ) {
296 | return array
297 | }
298 |
299 | if (fromIndex < toIndex) {
300 | const fromItem = array[fromIndex]
301 | for (let index = fromIndex; index < toIndex; index++) {
302 | array[index] = array[index + 1]
303 | }
304 | array[toIndex] = fromItem
305 | } else {
306 | const fromItem = array[fromIndex]
307 | for (let index = fromIndex; index > toIndex; index--) {
308 | array[index] = array[index - 1]
309 | }
310 | array[toIndex] = fromItem
311 | }
312 | return array
313 | }
314 |
```
--------------------------------------------------------------------------------
/devtools/chrome-extension/src/app/components/FieldTree.tsx:
--------------------------------------------------------------------------------
```typescript
1 | import React, { useState, useEffect, useRef } from 'react'
2 | import styled from 'styled-components'
3 | import { FormPath, isObj } from '@formily/shared'
4 | import { Treebeard, decorators } from 'react-treebeard'
5 | import * as filters from './filter'
6 | import SearchBox from './SearchBox'
7 |
8 | const createTree = (dataSource: any, cursor?: any) => {
9 | const tree: any = {}
10 | const getParentPath = (key: string) => {
11 | let parentPath: FormPath = FormPath.parse(key)
12 | let i = 0
13 | while (true) {
14 | parentPath = parentPath.parent()
15 | if (dataSource[parentPath.toString()]) {
16 | return parentPath
17 | }
18 | if (i > parentPath.segments.length) return parentPath
19 | i++
20 | }
21 | }
22 | const findParent = (key: string): any => {
23 | const parentPath = getParentPath(key)
24 | const _findParent = (node: any) => {
25 | if (FormPath.parse(node.path).match(parentPath)) {
26 | return node
27 | } else {
28 | for (let i = 0; i < node?.children?.length; i++) {
29 | const parent = _findParent(node.children[i])
30 | if (parent) {
31 | return parent
32 | }
33 | }
34 | }
35 | }
36 | return _findParent(tree)
37 | }
38 | Object.keys(dataSource || {}).forEach((key) => {
39 | if (key == '') {
40 | tree.name = 'Form'
41 | tree.path = key
42 | tree.toggled = true
43 | tree.data = dataSource[key]
44 | if (cursor && cursor.current && cursor.current.path === key) {
45 | tree.active = true
46 | cursor.current = tree
47 | }
48 | } else {
49 | const node: any = {
50 | name: key,
51 | path: key,
52 | toggled: true,
53 | data: dataSource[key],
54 | }
55 | if (cursor && cursor.current && cursor.current.path === key) {
56 | node.active = true
57 | cursor.current = node
58 | }
59 | const parent = findParent(key)
60 | if (parent) {
61 | node.name = (node.path || '').slice(
62 | parent && parent.path ? parent.path.length + 1 : 0
63 | )
64 | parent.children = parent.children || []
65 | parent.children.push(node)
66 | }
67 | }
68 | })
69 | return tree
70 | }
71 |
72 | const theme = {
73 | tree: {
74 | base: {
75 | listStyle: 'none',
76 | margin: 0,
77 | padding: 0,
78 | color: '#9DA5AB',
79 | fontFamily: 'lucida grande ,tahoma,verdana,arial,sans-serif',
80 | fontSize: '8px',
81 | background: 'none',
82 | marginBottom: '50px',
83 | },
84 | node: {
85 | base: {
86 | position: 'relative',
87 | background: 'none',
88 | },
89 | link: {
90 | cursor: 'pointer',
91 | position: 'relative',
92 | padding: '0px 5px',
93 | display: 'block',
94 | },
95 | activeLink: {
96 | background: '#3D424A',
97 | },
98 | toggle: {
99 | base: {
100 | position: 'relative',
101 | display: 'inline-block',
102 | verticalAlign: 'top',
103 | marginLeft: '-5px',
104 | height: '22px',
105 | width: '20px',
106 | zIndex: 2,
107 | },
108 | wrapper: {
109 | position: 'absolute',
110 | top: '50%',
111 | left: '50%',
112 | transform: 'translate(-50%,-50%)',
113 | width: 4,
114 | height: 6,
115 | display: 'flex',
116 | alignItems: 'center',
117 | justifyContent: 'center',
118 | },
119 | height: 6,
120 | width: 4,
121 | arrow: {
122 | fill: '#9DA5AB',
123 | strokeWidth: 0,
124 | },
125 | },
126 | header: {
127 | base: {
128 | display: 'inline-block',
129 | verticalAlign: 'top',
130 | color: '#9DA5AB',
131 | },
132 | connector: {
133 | width: '2px',
134 | height: '12px',
135 | borderLeft: 'solid 2px black',
136 | borderBottom: 'solid 2px black',
137 | position: 'absolute',
138 | top: '0px',
139 | left: '-21px',
140 | },
141 | title: {
142 | lineHeight: '24px',
143 | verticalAlign: 'middle',
144 | },
145 | },
146 | subtree: {
147 | listStyle: 'none',
148 | paddingLeft: '19px',
149 | },
150 | loading: {
151 | color: '#E2C089',
152 | },
153 | },
154 | },
155 | }
156 |
157 | const Header = (props) => {
158 | const { node, style, customStyles } = props
159 | const title = node.data?.title ? node.data.title : ''
160 | return (
161 | <div
162 | className="node-header"
163 | style={style.base}
164 | onClick={() => {
165 | node.toggled = false
166 | }}
167 | >
168 | <div
169 | style={
170 | node.selected
171 | ? { ...style.title, ...customStyles.header.title }
172 | : style.title
173 | }
174 | >
175 | <span
176 | style={{
177 | zIndex: 1,
178 | position: 'relative',
179 | fontSize: 12,
180 | }}
181 | >
182 | {node.name}
183 | </span>
184 | <span style={{ zIndex: 1, position: 'absolute', right: 12 }}>
185 | {isObj(title) ? ((title as any).title ?? '') : title}
186 | </span>
187 | <div
188 | className={`highlight ${node.active ? 'active' : ''}`}
189 | style={{ transition: '.15s all ease-in' }}
190 | ></div>
191 | </div>
192 | </div>
193 | )
194 | }
195 |
196 | const ToolBar = styled.div`
197 | border-bottom: 1px solid #3d424a;
198 | height: 20px;
199 | padding: 10px 10px;
200 | padding: 5px;
201 | overflow: auto;
202 | position: sticky;
203 | top: 0;
204 | background: #282c34;
205 | z-index: 100;
206 | `
207 |
208 | export const FieldTree = styled(({ className, dataSource, onSelect }) => {
209 | const allDataRef = useRef(createTree(dataSource))
210 | const cursor = useRef(allDataRef.current)
211 | const [keyword, setKeyword] = useState('')
212 | const searchTimer = useRef(null)
213 | const [data, setData] = useState(allDataRef.current)
214 |
215 | const filterData = () => {
216 | if (!keyword) return data
217 | const finded = filters.filterTree(data, keyword)
218 | return filters.expandFilteredNodes(finded, keyword)
219 | }
220 |
221 | const onToggle = (node: any, toggled: boolean) => {
222 | cursor.current.active = false
223 | node.active = true
224 | if (node.children && node.children.length) {
225 | node.toggled = toggled
226 | }
227 | cursor.current = node
228 | setData(data)
229 | if (onSelect) {
230 | onSelect(node)
231 | }
232 | }
233 |
234 | const onSearch = ({ target: { value } }) => {
235 | clearTimeout(searchTimer.current)
236 | searchTimer.current = setTimeout(() => {
237 | setKeyword(value.trim())
238 | }, 100)
239 | }
240 |
241 | useEffect(() => {
242 | allDataRef.current = createTree(dataSource, cursor)
243 | setData(allDataRef.current)
244 | }, [dataSource])
245 |
246 | return (
247 | <div className={className}>
248 | <ToolBar>
249 | <SearchBox onSearch={onSearch} />
250 | </ToolBar>
251 |
252 | <Treebeard
253 | data={filterData()}
254 | onToggle={onToggle}
255 | decorators={{
256 | ...decorators,
257 | Header,
258 | }}
259 | style={theme}
260 | />
261 | </div>
262 | )
263 | })`
264 | position: relative;
265 | overflow: auto;
266 | height: calc(100% - 40px);
267 | user-select: none;
268 | .highlight {
269 | position: absolute;
270 | top: 0;
271 | right: 0;
272 | left: -100%;
273 | height: 100%;
274 | z-index: 0;
275 | &.active {
276 | background: #3d424a;
277 | }
278 | }
279 | .node-header:hover .highlight {
280 | background: #3d424a;
281 | }
282 | `
283 |
```
--------------------------------------------------------------------------------
/packages/next/docs/components/Editable.zh-CN.md:
--------------------------------------------------------------------------------
```markdown
1 | # Editable
2 |
3 | > 局部编辑器,对于一些空间要求较高的表单区域可以使用该组件
4 | >
5 | > Editable 组件相当于是 FormItem 组件的变体,所以通常放在 decorator 中
6 |
7 | ## Markup Schema 案例
8 |
9 | ```tsx
10 | import React from 'react'
11 | import {
12 | Input,
13 | DatePicker,
14 | Editable,
15 | FormItem,
16 | FormButtonGroup,
17 | Submit,
18 | } from '@formily/next'
19 | import { createForm } from '@formily/core'
20 | import { FormProvider, createSchemaField } from '@formily/react'
21 |
22 | const SchemaField = createSchemaField({
23 | components: {
24 | DatePicker,
25 | Editable,
26 | Input,
27 | FormItem,
28 | },
29 | })
30 |
31 | const form = createForm()
32 |
33 | export default () => (
34 | <FormProvider form={form}>
35 | <SchemaField>
36 | <SchemaField.String
37 | name="date"
38 | title="日期"
39 | x-decorator="Editable"
40 | x-component="DatePicker"
41 | />
42 | <SchemaField.String
43 | name="input"
44 | title="输入框"
45 | x-decorator="Editable"
46 | x-component="Input"
47 | />
48 | <SchemaField.Void
49 | name="void"
50 | title="虚拟节点容器"
51 | x-component="Editable.Popover"
52 | x-reactions={(field) => {
53 | field.title = field.query('.void.date2').get('value') || field.title
54 | }}
55 | >
56 | <SchemaField.String
57 | name="date2"
58 | title="日期"
59 | x-decorator="FormItem"
60 | x-component="DatePicker"
61 | x-component-props={{
62 | followTrigger: true,
63 | }}
64 | />
65 | <SchemaField.String
66 | name="input2"
67 | title="输入框"
68 | x-decorator="FormItem"
69 | x-component="Input"
70 | />
71 | </SchemaField.Void>
72 | <SchemaField.Object
73 | name="iobject"
74 | title="对象节点容器"
75 | x-component="Editable.Popover"
76 | x-reactions={(field) => {
77 | field.title = field.value?.date || field.title
78 | }}
79 | >
80 | <SchemaField.String
81 | name="date"
82 | title="日期"
83 | x-decorator="FormItem"
84 | x-component="DatePicker"
85 | x-component-props={{
86 | followTrigger: true,
87 | }}
88 | />
89 | <SchemaField.String
90 | name="input"
91 | title="输入框"
92 | x-decorator="FormItem"
93 | x-component="Input"
94 | />
95 | </SchemaField.Object>
96 | </SchemaField>
97 | <FormButtonGroup>
98 | <Submit onSubmit={console.log}>提交</Submit>
99 | </FormButtonGroup>
100 | </FormProvider>
101 | )
102 | ```
103 |
104 | ## JSON Schema 案例
105 |
106 | ```tsx
107 | import React from 'react'
108 | import {
109 | Input,
110 | DatePicker,
111 | Editable,
112 | FormItem,
113 | FormButtonGroup,
114 | Submit,
115 | } from '@formily/next'
116 | import { createForm } from '@formily/core'
117 | import { FormProvider, createSchemaField } from '@formily/react'
118 |
119 | const SchemaField = createSchemaField({
120 | components: {
121 | DatePicker,
122 | Editable,
123 | Input,
124 | FormItem,
125 | },
126 | })
127 |
128 | const form = createForm()
129 |
130 | const schema = {
131 | type: 'object',
132 | properties: {
133 | date: {
134 | type: 'string',
135 | title: '日期',
136 | 'x-decorator': 'Editable',
137 | 'x-component': 'DatePicker',
138 | },
139 | input: {
140 | type: 'string',
141 | title: '输入框',
142 | 'x-decorator': 'Editable',
143 | 'x-component': 'Input',
144 | },
145 | void: {
146 | type: 'void',
147 | title: '虚拟节点容器',
148 | 'x-component': 'Editable.Popover',
149 | 'x-reactions':
150 | "{{(field) => field.title = field.query('.void.date2').get('value') || field.title}}",
151 | properties: {
152 | date2: {
153 | type: 'string',
154 | title: '日期',
155 | 'x-decorator': 'FormItem',
156 | 'x-component': 'DatePicker',
157 | 'x-component-props': {
158 | followTrigger: true,
159 | },
160 | },
161 | input2: {
162 | type: 'string',
163 | title: '输入框',
164 | 'x-decorator': 'FormItem',
165 | 'x-component': 'Input',
166 | },
167 | },
168 | },
169 | iobject: {
170 | type: 'object',
171 | title: '对象节点容器',
172 | 'x-component': 'Editable.Popover',
173 | 'x-reactions':
174 | '{{(field) => field.title = field.value && field.value.date || field.title}}',
175 | properties: {
176 | date: {
177 | type: 'string',
178 | title: '日期',
179 | 'x-decorator': 'FormItem',
180 | 'x-component': 'DatePicker',
181 | 'x-component-props': {
182 | followTrigger: true,
183 | },
184 | },
185 | input: {
186 | type: 'string',
187 | title: '输入框',
188 | 'x-decorator': 'FormItem',
189 | 'x-component': 'Input',
190 | },
191 | },
192 | },
193 | },
194 | }
195 |
196 | export default () => (
197 | <FormProvider form={form}>
198 | <SchemaField schema={schema} />
199 | <FormButtonGroup>
200 | <Submit onSubmit={console.log}>提交</Submit>
201 | </FormButtonGroup>
202 | </FormProvider>
203 | )
204 | ```
205 |
206 | ## 纯 JSX 案例
207 |
208 | ```tsx
209 | import React from 'react'
210 | import {
211 | Input,
212 | DatePicker,
213 | Editable,
214 | FormItem,
215 | FormButtonGroup,
216 | Submit,
217 | } from '@formily/next'
218 | import { createForm } from '@formily/core'
219 | import { FormProvider, Field, VoidField, ObjectField } from '@formily/react'
220 |
221 | const form = createForm()
222 |
223 | export default () => (
224 | <FormProvider form={form}>
225 | <Field
226 | name="date"
227 | title="日期"
228 | decorator={[Editable]}
229 | component={[DatePicker]}
230 | />
231 | <Field
232 | name="input"
233 | title="输入框"
234 | decorator={[Editable]}
235 | component={[Input]}
236 | />
237 | <VoidField
238 | name="void"
239 | title="虚拟节点容器"
240 | reactions={(field) => {
241 | field.title = field.query('.void.date2').get('value') || field.title
242 | }}
243 | component={[Editable.Popover]}
244 | >
245 | <Field
246 | name="date2"
247 | title="日期"
248 | decorator={[FormItem]}
249 | component={[
250 | DatePicker,
251 | {
252 | followTrigger: true,
253 | },
254 | ]}
255 | />
256 | <Field
257 | name="input2"
258 | title="输入框"
259 | decorator={[FormItem]}
260 | component={[Input]}
261 | />
262 | </VoidField>
263 | <ObjectField
264 | name="iobject"
265 | title="对象节点容器"
266 | component={[
267 | Editable.Popover,
268 | {
269 | renderPreview: (field) => {
270 | return field.value?.date
271 | },
272 | },
273 | ]}
274 | >
275 | <Field
276 | name="date"
277 | title="日期"
278 | decorator={[FormItem]}
279 | component={[
280 | DatePicker,
281 | {
282 | followTrigger: true,
283 | },
284 | ]}
285 | />
286 | <Field
287 | name="input"
288 | title="输入框"
289 | decorator={[FormItem]}
290 | component={[Input]}
291 | />
292 | </ObjectField>
293 |
294 | <FormButtonGroup>
295 | <Submit onSubmit={console.log}>提交</Submit>
296 | </FormButtonGroup>
297 | </FormProvider>
298 | )
299 | ```
300 |
301 | ## API
302 |
303 | ### Editable
304 |
305 | > 内联编辑
306 |
307 | 参考 https://fusion.design/pc/component/basic/form 中的 FormItem 属性
308 |
309 | ### Editable.Popover
310 |
311 | > 浮层编辑
312 |
313 | | 属性名 | 类型 | 描述 | 默认值 |
314 | | ------------- | --------------------------------- | ---------- | ------ |
315 | | renderPreview | `(field:GeneralField)=>ReactNode` | 预览渲染器 | |
316 |
317 | 注意:如果在 Popover 内部有 Select/DatePicker 之类的浮层组件,需要在浮层组件上配置 followTrigger=true
318 |
319 | 其余参考 https://fusion.design/pc/component/basic/balloon
320 |
```
--------------------------------------------------------------------------------
/packages/shared/src/merge.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { isFn, isPlainObj } from './checkers'
2 | import { isEmpty, isValid } from './isEmpty'
3 |
4 | function defaultIsMergeableObject(value: any) {
5 | return isNonNullObject(value) && !isSpecial(value)
6 | }
7 |
8 | function isNonNullObject(value: any) {
9 | // TODO: value !== null && typeof value === 'object'
10 | return Boolean(value) && typeof value === 'object'
11 | }
12 |
13 | function isSpecial(value: any) {
14 | // TODO: use isComplexObject()
15 | if ('$$typeof' in value && '_owner' in value) {
16 | return true
17 | }
18 | if (value._isAMomentObject) {
19 | return true
20 | }
21 | if (value._isJSONSchemaObject) {
22 | return true
23 | }
24 | if (isFn(value.toJS)) {
25 | return true
26 | }
27 | if (isFn(value.toJSON)) {
28 | return true
29 | }
30 | return !isPlainObj(value)
31 | }
32 |
33 | function emptyTarget(val: any) {
34 | return Array.isArray(val) ? [] : {}
35 | }
36 | // @ts-ignore
37 | function cloneUnlessOtherwiseSpecified(value: any, options: Options) {
38 | if (options.clone !== false && options.isMergeableObject?.(value)) {
39 | return deepmerge(emptyTarget(value), value, options)
40 | }
41 | return value
42 | }
43 |
44 | function defaultArrayMerge(target: any, source: any, options: Options) {
45 | return target.concat(source).map(function (element: any) {
46 | return cloneUnlessOtherwiseSpecified(element, options)
47 | })
48 | }
49 |
50 | function getMergeFunction(key: string, options: Options) {
51 | if (!options.customMerge) {
52 | return deepmerge
53 | }
54 | const customMerge = options.customMerge(key)
55 | return typeof customMerge === 'function' ? customMerge : deepmerge
56 | }
57 |
58 | function getEnumerableOwnPropertySymbols(target: any): any {
59 | return Object.getOwnPropertySymbols
60 | ? Object.getOwnPropertySymbols(target).filter(function (symbol) {
61 | return target.propertyIsEnumerable(symbol)
62 | })
63 | : []
64 | }
65 |
66 | function getKeys(target: any) {
67 | if (!isValid(target)) return []
68 | return Object.keys(target).concat(getEnumerableOwnPropertySymbols(target))
69 | }
70 |
71 | function propertyIsOnObject(object: any, property: any) {
72 | /* istanbul ignore next */
73 | try {
74 | return property in object
75 | } catch (_) {
76 | return false
77 | }
78 | }
79 |
80 | // Protects from prototype poisoning and unexpected merging up the prototype chain.
81 | function propertyIsUnsafe(target: any, key: PropertyKey) {
82 | return (
83 | propertyIsOnObject(target, key) && // Properties are safe to merge if they don't exist in the target yet,
84 | !(
85 | Object.hasOwnProperty.call(target, key) && // unsafe if they exist up the prototype chain,
86 | Object.propertyIsEnumerable.call(target, key)
87 | )
88 | ) // and also unsafe if they're nonenumerable.
89 | }
90 |
91 | function mergeObject(target: any, source: any, options: Options) {
92 | const destination = options.assign ? target || {} : {}
93 | if (!options.isMergeableObject(target)) return target
94 | if (!options.assign) {
95 | getKeys(target).forEach(function (key) {
96 | destination[key] = cloneUnlessOtherwiseSpecified(target[key], options)
97 | })
98 | }
99 | getKeys(source).forEach(function (key) {
100 | /* istanbul ignore next */
101 | if (propertyIsUnsafe(target, key)) {
102 | return
103 | }
104 | if (isEmpty(target[key])) {
105 | destination[key] = source[key]
106 | } else if (
107 | propertyIsOnObject(target, key) &&
108 | // @ts-ignore
109 | options.isMergeableObject(source[key])
110 | ) {
111 | destination[key] = getMergeFunction(key, options)(
112 | target[key],
113 | source[key],
114 | options
115 | )
116 | } else {
117 | destination[key] = cloneUnlessOtherwiseSpecified(source[key], options)
118 | }
119 | })
120 | return destination
121 | }
122 |
123 | interface Options {
124 | arrayMerge?(target: any[], source: any[], options?: Options): any[]
125 | clone?: boolean
126 | assign?: boolean
127 | customMerge?: (
128 | key: string,
129 | options?: Options
130 | ) => ((x: any, y: any) => any) | undefined
131 | isMergeableObject?(value: object): boolean
132 | cloneUnlessOtherwiseSpecified?: (value: any, options: Options) => any
133 | }
134 |
135 | // @ts-ignore
136 | function deepmerge(target: any, source: any, options?: Options) {
137 | options = options || {}
138 | options.arrayMerge = options.arrayMerge || defaultArrayMerge
139 | options.isMergeableObject =
140 | options.isMergeableObject || defaultIsMergeableObject
141 | // cloneUnlessOtherwiseSpecified is added to `options` so that custom arrayMerge()
142 | // implementations can use it. The caller may not replace it.
143 | options.cloneUnlessOtherwiseSpecified = cloneUnlessOtherwiseSpecified
144 |
145 | const sourceIsArray = Array.isArray(source)
146 | const targetIsArray = Array.isArray(target)
147 | const sourceAndTargetTypesMatch = sourceIsArray === targetIsArray
148 |
149 | if (!sourceAndTargetTypesMatch) {
150 | return cloneUnlessOtherwiseSpecified(source, options)
151 | } else if (sourceIsArray) {
152 | return options.arrayMerge(target, source, options)
153 | } else {
154 | return mergeObject(target, source, options)
155 | }
156 | }
157 |
158 | export const lazyMerge = <T extends object | Function>(
159 | target: T,
160 | ...args: T[]
161 | ): any => {
162 | const _lazyMerge = <T extends object | Function>(
163 | target: T,
164 | source: T
165 | ): {} => {
166 | if (!isValid(source)) return target
167 | if (!isValid(target)) return source
168 | const isTargetObject = typeof target === 'object'
169 | const isSourceObject = typeof source === 'object'
170 | const isTargetFn = typeof target === 'function'
171 | const isSourceFn = typeof source === 'function'
172 | if (!isTargetObject && !isTargetFn) return source
173 | if (!isSourceObject && !isSourceFn) return target
174 | const getTarget = () => (isTargetFn ? target() : target)
175 | const getSource = () => (isSourceFn ? source() : source)
176 | const set = (_: object, key: PropertyKey, value: any) => {
177 | const source = getSource()
178 | const target = getTarget()
179 | if (key in source) {
180 | // @ts-ignore
181 | source[key] = value
182 | } else if (key in target) {
183 | // @ts-ignore
184 | target[key] = value
185 | } else {
186 | source[key] = value
187 | }
188 | return true
189 | }
190 | const get = (_: object, key: PropertyKey) => {
191 | const source = getSource()
192 | // @ts-ignore
193 | if (key in source) {
194 | return source[key]
195 | }
196 | // @ts-ignore
197 | return getTarget()[key]
198 | }
199 | const ownKeys = () => {
200 | const source = getSource()
201 | const target = getTarget()
202 | const keys = Object.keys(target)
203 | for (const key in source) {
204 | if (!(key in target)) {
205 | keys.push(key)
206 | }
207 | }
208 | return keys
209 | }
210 | const getOwnPropertyDescriptor = (_: object, key: PropertyKey) => ({
211 | value: get(_, key),
212 | enumerable: true,
213 | configurable: true,
214 | })
215 | const has = (_: object, key: PropertyKey) => {
216 | if (key in getSource() || key in getTarget()) return true
217 | return false
218 | }
219 | const getPrototypeOf = () => Object.getPrototypeOf({})
220 | return new Proxy(Object.create(null), {
221 | set,
222 | get,
223 | ownKeys,
224 | getPrototypeOf,
225 | getOwnPropertyDescriptor,
226 | has,
227 | }) as any
228 | }
229 | return args.reduce<{}>((buf, arg) => _lazyMerge(buf, arg), target)
230 | }
231 |
232 | export const merge = deepmerge
233 |
```
--------------------------------------------------------------------------------
/packages/antd/docs/components/FormCollapse.md:
--------------------------------------------------------------------------------
```markdown
1 | # FormCollapse
2 |
3 | > Folding panel, usually used in form scenes with high layout space requirements
4 | >
5 | > Note: Can only be used in Schema scenarios
6 |
7 | ## Markup Schema example
8 |
9 | ```tsx
10 | import React from 'react'
11 | import {
12 | FormCollapse,
13 | FormLayout,
14 | FormItem,
15 | Input,
16 | FormButtonGroup,
17 | Submit,
18 | } from '@formily/antd'
19 | import { createForm } from '@formily/core'
20 | import { FormProvider, createSchemaField } from '@formily/react'
21 | import { Button } from 'antd'
22 |
23 | const SchemaField = createSchemaField({
24 | components: {
25 | FormItem,
26 | FormCollapse,
27 | Input,
28 | },
29 | })
30 |
31 | const form = createForm()
32 | const formCollapse = FormCollapse.createFormCollapse()
33 | export default () => {
34 | return (
35 | <FormProvider form={form}>
36 | <FormLayout labelCol={6} wrapperCol={10}>
37 | <SchemaField>
38 | <SchemaField.Void
39 | title="Folding Panel"
40 | x-decorator="FormItem"
41 | x-component="FormCollapse"
42 | x-component-props={{
43 | formCollapse,
44 | }}
45 | >
46 | <SchemaField.Void
47 | name="panel1"
48 | x-component="FormCollapse.CollapsePanel"
49 | x-component-props={{ header: 'A1' }}
50 | >
51 | <SchemaField.String
52 | name="aaa"
53 | title="AAA"
54 | x-decorator="FormItem"
55 | required
56 | x-component="Input"
57 | />
58 | </SchemaField.Void>
59 | <SchemaField.Void
60 | name="panel2"
61 | x-component="FormCollapse.CollapsePanel"
62 | x-component-props={{ header: 'A2' }}
63 | >
64 | <SchemaField.String
65 | name="bbb"
66 | title="BBB"
67 | x-decorator="FormItem"
68 | required
69 | x-component="Input"
70 | />
71 | </SchemaField.Void>
72 | <SchemaField.Void
73 | name="panel3"
74 | x-component="FormCollapse.CollapsePanel"
75 | x-component-props={{ header: 'A3' }}
76 | >
77 | <SchemaField.String
78 | name="ccc"
79 | title="CCC"
80 | x-decorator="FormItem"
81 | required
82 | x-component="Input"
83 | />
84 | </SchemaField.Void>
85 | </SchemaField.Void>
86 | </SchemaField>
87 | <FormButtonGroup.FormItem>
88 | <Button
89 | onClick={() => {
90 | form.query('panel3').take((field) => {
91 | field.visible = !field.visible
92 | })
93 | }}
94 | >
95 | Show/hide the last tab
96 | </Button>
97 | <Button
98 | onClick={() => {
99 | formCollapse.toggleActiveKey('panel2')
100 | }}
101 | >
102 | Switch to the second Tab
103 | </Button>
104 | <Submit onSubmit={console.log}>Submit</Submit>
105 | </FormButtonGroup.FormItem>
106 | </FormLayout>
107 | </FormProvider>
108 | )
109 | }
110 | ```
111 |
112 | ## JSON Schema case
113 |
114 | ```tsx
115 | import React from 'react'
116 | import {
117 | FormCollapse,
118 | FormItem,
119 | FormLayout,
120 | Input,
121 | FormButtonGroup,
122 | Submit,
123 | } from '@formily/antd'
124 | import { createForm } from '@formily/core'
125 | import { FormProvider, createSchemaField } from '@formily/react'
126 | import { Button } from 'antd'
127 |
128 | const SchemaField = createSchemaField({
129 | components: {
130 | FormItem,
131 | FormCollapse,
132 | Input,
133 | },
134 | })
135 |
136 | const form = createForm()
137 | const formCollapse = FormCollapse.createFormCollapse()
138 | const schema = {
139 | type: 'object',
140 | properties: {
141 | collapse: {
142 | type: 'void',
143 | title: 'Folding Panel',
144 | 'x-decorator': 'FormItem',
145 | 'x-component': 'FormCollapse',
146 | 'x-component-props': {
147 | formCollapse: '{{formCollapse}}',
148 | },
149 | properties: {
150 | panel1: {
151 | type: 'void',
152 | 'x-component': 'FormCollapse.CollapsePanel',
153 | 'x-component-props': {
154 | header: 'A1',
155 | },
156 | properties: {
157 | aaa: {
158 | type: 'string',
159 | title: 'AAA',
160 | 'x-decorator': 'FormItem',
161 | required: true,
162 | 'x-component': 'Input',
163 | },
164 | },
165 | },
166 | panel2: {
167 | type: 'void',
168 | 'x-component': 'FormCollapse.CollapsePanel',
169 | 'x-component-props': {
170 | header: 'A2',
171 | },
172 | properties: {
173 | bbb: {
174 | type: 'string',
175 | title: 'BBB',
176 | 'x-decorator': 'FormItem',
177 | required: true,
178 | 'x-component': 'Input',
179 | },
180 | },
181 | },
182 | panel3: {
183 | type: 'void',
184 | 'x-component': 'FormCollapse.CollapsePanel',
185 | 'x-component-props': {
186 | header: 'A3',
187 | },
188 | properties: {
189 | ccc: {
190 | type: 'string',
191 | title: 'CCC',
192 | 'x-decorator': 'FormItem',
193 | required: true,
194 | 'x-component': 'Input',
195 | },
196 | },
197 | },
198 | },
199 | },
200 | },
201 | }
202 |
203 | export default () => {
204 | return (
205 | <FormProvider form={form}>
206 | <FormLayout labelCol={6} wrapperCol={10}>
207 | <SchemaField schema={schema} scope={{ formCollapse }} />
208 | <FormButtonGroup.FormItem>
209 | <Button
210 | onClick={() => {
211 | form.query('panel3').take((field) => {
212 | field.visible = !field.visible
213 | })
214 | }}
215 | >
216 | Show/hide the last tab
217 | </Button>
218 | <Button
219 | onClick={() => {
220 | formCollapse.toggleActiveKey('panel2')
221 | }}
222 | >
223 | Switch to the second Tab
224 | </Button>
225 | <Submit onSubmit={console.log}>Submit</Submit>
226 | </FormButtonGroup.FormItem>
227 | </FormLayout>
228 | </FormProvider>
229 | )
230 | }
231 | ```
232 |
233 | ## API
234 |
235 | ### FormCollapse
236 |
237 | | Property name | Type | Description | Default value |
238 | | ------------- | ------------- | --------------------------------------------------------------- | ------------- |
239 | | formCollapse | IFormCollapse | Pass in the model created by createFormCollapse/useFormCollapse | |
240 |
241 | Other references https://ant.design/components/collapse-cn/
242 |
243 | ### FormCollapse.CollapsePanel
244 |
245 | Reference https://ant.design/components/collapse-cn/
246 |
247 | ### FormCollapse.createFormCollapse
248 |
249 | ```ts pure
250 | type ActiveKey = string | number
251 | type ActiveKeys = string | number | Array<string | number>
252 |
253 | interface createFormCollapse {
254 | (defaultActiveKeys?: ActiveKeys): IFormCollpase
255 | }
256 |
257 | interface IFormCollapse {
258 | //Activate the primary key list
259 | activeKeys: ActiveKeys
260 | //Does the activation key exist?
261 | hasActiveKey(key: ActiveKey): boolean
262 | //Set the list of active primary keys
263 | setActiveKeys(keys: ActiveKeys): void
264 | //Add activation key
265 | addActiveKey(key: ActiveKey): void
266 | //Delete the active primary key
267 | removeActiveKey(key: ActiveKey): void
268 | //Switch to activate the main key
269 | toggleActiveKey(key: ActiveKey): void
270 | }
271 | ```
272 |
```
--------------------------------------------------------------------------------
/packages/next/docs/components/FormCollapse.md:
--------------------------------------------------------------------------------
```markdown
1 | # FormCollapse
2 |
3 | > Folding panel, usually used in form scenes with high layout space requirements
4 | >
5 | > Note: Can only be used in Schema scenarios
6 |
7 | ## Markup Schema example
8 |
9 | ```tsx
10 | import React from 'react'
11 | import {
12 | FormCollapse,
13 | FormItem,
14 | Input,
15 | FormButtonGroup,
16 | Submit,
17 | FormLayout,
18 | } from '@formily/next'
19 | import { createForm } from '@formily/core'
20 | import { FormProvider, createSchemaField } from '@formily/react'
21 | import { Button } from '@alifd/next'
22 |
23 | const SchemaField = createSchemaField({
24 | components: {
25 | FormItem,
26 | FormCollapse,
27 | Input,
28 | },
29 | })
30 |
31 | const form = createForm()
32 | const formCollapse = FormCollapse.createFormCollapse()
33 |
34 | export default () => {
35 | return (
36 | <FormProvider form={form}>
37 | <FormLayout labelCol={6} wrapperCol={10}>
38 | <SchemaField>
39 | <SchemaField.Void
40 | title="Folding Panel"
41 | x-decorator="FormItem"
42 | x-component="FormCollapse"
43 | x-component-props={{
44 | formCollapse,
45 | }}
46 | >
47 | <SchemaField.Void
48 | name="panel1"
49 | x-component="FormCollapse.CollapsePanel"
50 | x-component-props={{ title: 'A1' }}
51 | >
52 | <SchemaField.String
53 | name="aaa"
54 | title="AAA"
55 | x-decorator="FormItem"
56 | required
57 | x-component="Input"
58 | />
59 | </SchemaField.Void>
60 | <SchemaField.Void
61 | name="panel2"
62 | x-component="FormCollapse.CollapsePanel"
63 | x-component-props={{ title: 'A2' }}
64 | >
65 | <SchemaField.String
66 | name="bbb"
67 | title="BBB"
68 | x-decorator="FormItem"
69 | required
70 | x-component="Input"
71 | />
72 | </SchemaField.Void>
73 | <SchemaField.Void
74 | name="panel3"
75 | x-component="FormCollapse.CollapsePanel"
76 | x-component-props={{ title: 'A3' }}
77 | >
78 | <SchemaField.String
79 | name="ccc"
80 | title="CCC"
81 | x-decorator="FormItem"
82 | required
83 | x-component="Input"
84 | />
85 | </SchemaField.Void>
86 | </SchemaField.Void>
87 | </SchemaField>
88 | <FormButtonGroup.FormItem>
89 | <Button
90 | onClick={() => {
91 | form.query('panel3').take((field) => {
92 | field.visible = !field.visible
93 | })
94 | }}
95 | >
96 | Show/hide the last tab
97 | </Button>
98 | <Button
99 | onClick={() => {
100 | formCollapse.toggleActiveKey('panel2')
101 | }}
102 | >
103 | Switch to the second Tab
104 | </Button>
105 | <Submit onSubmit={console.log}>Submit</Submit>
106 | </FormButtonGroup.FormItem>
107 | </FormLayout>
108 | </FormProvider>
109 | )
110 | }
111 | ```
112 |
113 | ## JSON Schema case
114 |
115 | ```tsx
116 | import React from 'react'
117 | import {
118 | FormCollapse,
119 | FormItem,
120 | Input,
121 | FormButtonGroup,
122 | Submit,
123 | FormLayout,
124 | } from '@formily/next'
125 | import { createForm } from '@formily/core'
126 | import { FormProvider, createSchemaField } from '@formily/react'
127 | import { Button } from '@alifd/next'
128 |
129 | const SchemaField = createSchemaField({
130 | components: {
131 | FormItem,
132 | FormCollapse,
133 | Input,
134 | },
135 | })
136 |
137 | const form = createForm()
138 | const formCollapse = FormCollapse.createFormCollapse()
139 |
140 | const schema = {
141 | type: 'object',
142 | properties: {
143 | collapse: {
144 | type: 'void',
145 | title: 'Folding Panel',
146 | 'x-decorator': 'FormItem',
147 | 'x-component': 'FormCollapse',
148 | 'x-component-props': {
149 | formCollapse: '{{formCollapse}}',
150 | },
151 | properties: {
152 | panel1: {
153 | type: 'void',
154 | 'x-component': 'FormCollapse.CollapsePanel',
155 | 'x-component-props': {
156 | title: 'A1',
157 | },
158 | properties: {
159 | aaa: {
160 | type: 'string',
161 | title: 'AAA',
162 | 'x-decorator': 'FormItem',
163 | required: true,
164 | 'x-component': 'Input',
165 | },
166 | },
167 | },
168 | panel2: {
169 | type: 'void',
170 | 'x-component': 'FormCollapse.CollapsePanel',
171 | 'x-component-props': {
172 | title: 'A2',
173 | },
174 | properties: {
175 | bbb: {
176 | type: 'string',
177 | title: 'BBB',
178 | 'x-decorator': 'FormItem',
179 | required: true,
180 | 'x-component': 'Input',
181 | },
182 | },
183 | },
184 | panel3: {
185 | type: 'void',
186 | 'x-component': 'FormCollapse.CollapsePanel',
187 | 'x-component-props': {
188 | title: 'A3',
189 | },
190 | properties: {
191 | ccc: {
192 | type: 'string',
193 | title: 'CCC',
194 | 'x-decorator': 'FormItem',
195 | required: true,
196 | 'x-component': 'Input',
197 | },
198 | },
199 | },
200 | },
201 | },
202 | },
203 | }
204 |
205 | export default () => {
206 | return (
207 | <FormProvider form={form}>
208 | <FormLayout labelCol={6} wrapperCol={10}>
209 | <SchemaField schema={schema} scope={{ formCollapse }} />
210 | <FormButtonGroup.FormItem>
211 | <Button
212 | onClick={() => {
213 | form.query('panel3').take((field) => {
214 | field.visible = !field.visible
215 | })
216 | }}
217 | >
218 | Show/hide the last tab
219 | </Button>
220 | <Button
221 | onClick={() => {
222 | formCollapse.toggleActiveKey('panel2')
223 | }}
224 | >
225 | Switch to the second Tab
226 | </Button>
227 | <Submit onSubmit={console.log}>Submit</Submit>
228 | </FormButtonGroup.FormItem>
229 | </FormLayout>
230 | </FormProvider>
231 | )
232 | }
233 | ```
234 |
235 | ## API
236 |
237 | ### FormCollapse
238 |
239 | | Property name | Type | Description | Default value |
240 | | ------------- | ------------- | --------------------------------------------------------------- | ------------- |
241 | | formCollapse | IFormCollapse | Pass in the model created by createFormCollapse/useFormCollapse | |
242 |
243 | Other references https://fusion.design/pc/component/basic/collapse
244 |
245 | ### FormCollapse.CollapsePanel
246 |
247 | Reference https://fusion.design/pc/component/basic/collapse
248 |
249 | ### FormCollapse.createFormCollapse
250 |
251 | ```ts pure
252 | type ActiveKey = string | number
253 | type ActiveKeys = string | number | Array<string | number>
254 |
255 | interface createFormCollapse {
256 | (defaultActiveKeys?: ActiveKeys): IFormCollpase
257 | }
258 |
259 | interface IFormCollapse {
260 | //Activate the primary key list
261 | activeKeys: ActiveKeys
262 | //Does the activation key exist?
263 | hasActiveKey(key: ActiveKey): boolean
264 | //Set the list of active primary keys
265 | setActiveKeys(keys: ActiveKeys): void
266 | //Add activation key
267 | addActiveKey(key: ActiveKey): void
268 | //Delete the active primary key
269 | removeActiveKey(key: ActiveKey): void
270 | //Switch to activate the main key
271 | toggleActiveKey(key: ActiveKey): void
272 | }
273 | ```
274 |
```
--------------------------------------------------------------------------------
/packages/next/docs/components/FormLayout.zh-CN.md:
--------------------------------------------------------------------------------
```markdown
1 | # FormLayout
2 |
3 | > 区块级布局批量控制组件,借助该组件,我们可以轻松的控制被 FormLayout 圈住的所有 FormItem 组件的布局模式
4 |
5 | ## Markup Schema 案例
6 |
7 | ```tsx
8 | import React from 'react'
9 | import { Input, Select, FormItem, FormLayout } from '@formily/next'
10 | import { createForm } from '@formily/core'
11 | import { FormProvider, createSchemaField } from '@formily/react'
12 |
13 | const SchemaField = createSchemaField({
14 | components: {
15 | Input,
16 | Select,
17 | FormItem,
18 | FormLayout,
19 | },
20 | })
21 |
22 | const form = createForm()
23 |
24 | export default () => (
25 | <FormProvider form={form}>
26 | <SchemaField>
27 | <SchemaField.Void
28 | x-component="FormLayout"
29 | x-component-props={{
30 | labelCol: 6,
31 | wrapperCol: 10,
32 | }}
33 | >
34 | <SchemaField.String
35 | name="input"
36 | title="输入框"
37 | x-decorator="FormItem"
38 | x-decorator-props={{
39 | tooltip: <div>123</div>,
40 | }}
41 | x-component="Input"
42 | required
43 | />
44 | <SchemaField.String
45 | name="select"
46 | title="选择框"
47 | x-decorator="FormItem"
48 | x-component="Select"
49 | required
50 | />
51 | </SchemaField.Void>
52 | </SchemaField>
53 | </FormProvider>
54 | )
55 | ```
56 |
57 | ## JSON Schema 案例
58 |
59 | ```tsx
60 | import React from 'react'
61 | import { Input, Select, FormItem, FormLayout } from '@formily/next'
62 | import { createForm } from '@formily/core'
63 | import { FormProvider, createSchemaField } from '@formily/react'
64 |
65 | const SchemaField = createSchemaField({
66 | components: {
67 | Input,
68 | Select,
69 | FormItem,
70 | FormLayout,
71 | },
72 | })
73 |
74 | const schema = {
75 | type: 'object',
76 | properties: {
77 | layout: {
78 | type: 'void',
79 | 'x-component': 'FormLayout',
80 | 'x-component-props': {
81 | labelCol: 6,
82 | wrapperCol: 10,
83 | layout: 'vertical',
84 | },
85 | properties: {
86 | input: {
87 | type: 'string',
88 | title: '输入框',
89 | required: true,
90 | 'x-decorator': 'FormItem',
91 | 'x-decorator-props': {
92 | tooltip: <div>123</div>,
93 | },
94 | 'x-component': 'Input',
95 | },
96 | select: {
97 | type: 'string',
98 | title: '选择框',
99 | required: true,
100 | 'x-decorator': 'FormItem',
101 | 'x-component': 'Select',
102 | },
103 | },
104 | },
105 | },
106 | }
107 |
108 | const form = createForm()
109 |
110 | export default () => (
111 | <FormProvider form={form}>
112 | <SchemaField schema={schema} />
113 | </FormProvider>
114 | )
115 | ```
116 |
117 | ## 纯 JSX 案例
118 |
119 | ```tsx
120 | import React from 'react'
121 | import {
122 | Input,
123 | Select,
124 | FormItem,
125 | FormButtonGroup,
126 | Submit,
127 | FormLayout,
128 | } from '@formily/next'
129 | import { createForm } from '@formily/core'
130 | import { FormProvider, Field } from '@formily/react'
131 |
132 | const form = createForm()
133 |
134 | export default () => (
135 | <FormProvider form={form}>
136 | <FormLayout
137 | breakpoints={[680]}
138 | layout={['vertical', 'horizontal']}
139 | labelAlign={['left', 'right']}
140 | labelCol={[24, 6]}
141 | wrapperCol={[24, 10]}
142 | >
143 | <Field
144 | name="input"
145 | required
146 | title="输入框"
147 | decorator={[FormItem]}
148 | component={[Input]}
149 | />
150 | <Field
151 | name="select"
152 | required
153 | title="选择框"
154 | decorator={[FormItem]}
155 | component={[Select]}
156 | />
157 | <FormButtonGroup.FormItem>
158 | <Submit onSubmit={console.log}>提交</Submit>
159 | </FormButtonGroup.FormItem>
160 | </FormLayout>
161 | </FormProvider>
162 | )
163 | ```
164 |
165 | ## API
166 |
167 | | 属性名 | 类型 | 描述 | 默认值 |
168 | | -------------- | ------------------------------------------------------------------------------------- | ----------------------- | ---------- |
169 | | style | CSSProperties | 样式 | - |
170 | | className | string | 类名 | - |
171 | | colon | boolean | 是否有冒号 | true |
172 | | labelAlign | `'right' \| 'left' \| ('right' \| 'left')[]` | 标签内容对齐 | - |
173 | | wrapperAlign | `'right' \| 'left' \| ('right' \| 'left')[]` | 组件容器内容对齐 | - |
174 | | labelWrap | boolean | 标签内容换行 | false |
175 | | labelWidth | number | 标签宽度(px) | - |
176 | | wrapperWidth | number | 组件容器宽度(px) | - |
177 | | wrapperWrap | boolean | 组件容器换行 | false |
178 | | labelCol | `number \| number[]` | 标签宽度(24 column) | - |
179 | | wrapperCol | `number \| number[]` | 组件容器宽度(24 column) | - |
180 | | fullness | boolean | 组件容器宽度 100% | false |
181 | | size | `'small' \| 'default' \| 'large'` | 组件尺寸 | default |
182 | | layout | `'vertical' \| 'horizontal' \| 'inline' \|('vertical' \| 'horizontal' \| 'inline')[]` | 布局模式 | horizontal |
183 | | direction | `'rtl' \| 'ltr'` | 方向(暂不支持) | ltr |
184 | | inset | boolean | 内联布局 | false |
185 | | shallow | boolean | 上下文浅层传递 | true |
186 | | feedbackLayout | `'loose' \| 'terse' \| 'popover' \| 'none'` | 反馈布局 | true |
187 | | tooltipLayout | `"icon" \| "text"` | 问号提示布局 | `"icon"` |
188 | | tooltipIcon | ReactNode | 问号提示图标 | - |
189 | | bordered | boolean | 是否有边框 | true |
190 | | breakpoints | number[] | 容器尺寸断点 | - |
191 | | gridColumnGap | number | 网格布局列间距 | 8 |
192 | | gridRowGap | number | 网格布局行间距 | 4 |
193 | | spaceGap | number | 弹性间距 | 8 |
194 |
```
--------------------------------------------------------------------------------
/packages/element/src/array-cards/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | import { ArrayField } from '@formily/core'
2 | import { ISchema } from '@formily/json-schema'
3 | import { observer } from '@formily/reactive-vue'
4 | import { h, RecursionField, useField, useFieldSchema } from '@formily/vue'
5 | import type { Card as CardProps } from 'element-ui'
6 | import { Card, Empty, Row } from 'element-ui'
7 | import { defineComponent } from 'vue-demi'
8 | import { ArrayBase } from '../array-base'
9 | import { stylePrefix } from '../__builtins__/configs'
10 | import { composeExport } from '../__builtins__/shared'
11 |
12 | const isAdditionComponent = (schema: ISchema) => {
13 | return schema['x-component']?.indexOf?.('Addition') > -1
14 | }
15 |
16 | const isIndexComponent = (schema: ISchema) => {
17 | return schema['x-component']?.indexOf?.('Index') > -1
18 | }
19 |
20 | const isRemoveComponent = (schema: ISchema) => {
21 | return schema['x-component']?.indexOf?.('Remove') > -1
22 | }
23 |
24 | const isMoveUpComponent = (schema: ISchema) => {
25 | return schema['x-component']?.indexOf?.('MoveUp') > -1
26 | }
27 |
28 | const isMoveDownComponent = (schema: ISchema) => {
29 | return schema['x-component']?.indexOf?.('MoveDown') > -1
30 | }
31 |
32 | const isOperationComponent = (schema: ISchema) => {
33 | return (
34 | isAdditionComponent(schema) ||
35 | isRemoveComponent(schema) ||
36 | isMoveDownComponent(schema) ||
37 | isMoveUpComponent(schema)
38 | )
39 | }
40 | const ArrayCardsInner = observer(
41 | defineComponent<CardProps>({
42 | name: 'FArrayCards',
43 | props: [],
44 | setup(props, { attrs }) {
45 | const fieldRef = useField<ArrayField>()
46 | const schemaRef = useFieldSchema()
47 | const prefixCls = `${stylePrefix}-array-cards`
48 | const { getKey, keyMap } = ArrayBase.useKey(schemaRef.value)
49 |
50 | return () => {
51 | const field = fieldRef.value
52 | const schema = schemaRef.value
53 | const dataSource = Array.isArray(field.value) ? field.value : []
54 | if (!schema) throw new Error('can not found schema object')
55 |
56 | const renderItems = () => {
57 | return dataSource?.map((item, index) => {
58 | const items = Array.isArray(schema.items)
59 | ? schema.items[index] || schema.items[0]
60 | : schema.items
61 |
62 | const title = h(
63 | 'span',
64 | {},
65 | {
66 | default: () => [
67 | h(
68 | RecursionField,
69 | {
70 | props: {
71 | schema: items,
72 | name: index,
73 | filterProperties: (schema) => {
74 | if (!isIndexComponent(schema)) return false
75 | return true
76 | },
77 | onlyRenderProperties: true,
78 | },
79 | },
80 | {}
81 | ),
82 | attrs.title || field.title,
83 | ],
84 | }
85 | )
86 | const extra = h(
87 | 'span',
88 | {},
89 | {
90 | default: () => [
91 | h(
92 | RecursionField,
93 | {
94 | props: {
95 | schema: items,
96 | name: index,
97 | filterProperties: (schema) => {
98 | if (!isOperationComponent(schema)) return false
99 | return true
100 | },
101 | onlyRenderProperties: true,
102 | },
103 | },
104 | {}
105 | ),
106 | attrs.extra,
107 | ],
108 | }
109 | )
110 | const content = h(
111 | RecursionField,
112 | {
113 | props: {
114 | schema: items,
115 | name: index,
116 | filterProperties: (schema) => {
117 | if (isIndexComponent(schema)) return false
118 | if (isOperationComponent(schema)) return false
119 | return true
120 | },
121 | },
122 | },
123 | {}
124 | )
125 |
126 | return h(
127 | ArrayBase.Item,
128 | {
129 | key: getKey(item, index),
130 | props: {
131 | index,
132 | record: item,
133 | },
134 | },
135 | {
136 | default: () =>
137 | h(
138 | Card,
139 | {
140 | class: [`${prefixCls}-item`],
141 | attrs: {
142 | shadow: 'never',
143 | ...attrs,
144 | },
145 | },
146 | {
147 | default: () => [content],
148 | header: () =>
149 | h(
150 | Row,
151 | {
152 | props: {
153 | type: 'flex',
154 | justify: 'space-between',
155 | },
156 | },
157 | {
158 | default: () => [title, extra],
159 | }
160 | ),
161 | }
162 | ),
163 | }
164 | )
165 | })
166 | }
167 | const renderAddition = () => {
168 | return schema.reduceProperties((addition, schema) => {
169 | if (isAdditionComponent(schema)) {
170 | return h(
171 | RecursionField,
172 | {
173 | props: {
174 | schema,
175 | name: 'addition',
176 | },
177 | },
178 | {}
179 | )
180 | }
181 | return addition
182 | }, null)
183 | }
184 | const renderEmpty = () => {
185 | if (dataSource?.length) return
186 | return h(
187 | Card,
188 | {
189 | class: [`${prefixCls}-item`],
190 | attrs: {
191 | shadow: 'never',
192 | ...attrs,
193 | header: attrs.title || field.title,
194 | },
195 | },
196 | {
197 | default: () =>
198 | h(
199 | Empty,
200 | { props: { description: 'No Data', imageSize: 100 } },
201 | {}
202 | ),
203 | }
204 | )
205 | }
206 |
207 | return h(
208 | 'div',
209 | {
210 | class: [prefixCls],
211 | },
212 | {
213 | default: () =>
214 | h(
215 | ArrayBase,
216 | {
217 | props: {
218 | keyMap,
219 | },
220 | },
221 | {
222 | default: () => [
223 | renderEmpty(),
224 | renderItems(),
225 | renderAddition(),
226 | ],
227 | }
228 | ),
229 | }
230 | )
231 | }
232 | },
233 | })
234 | )
235 |
236 | export const ArrayCards = composeExport(ArrayCardsInner, {
237 | Index: ArrayBase.Index,
238 | SortHandle: ArrayBase.SortHandle,
239 | Addition: ArrayBase.Addition,
240 | Remove: ArrayBase.Remove,
241 | MoveDown: ArrayBase.MoveDown,
242 | MoveUp: ArrayBase.MoveUp,
243 | useArray: ArrayBase.useArray,
244 | useIndex: ArrayBase.useIndex,
245 | useRecord: ArrayBase.useRecord,
246 | })
247 |
248 | export default ArrayCards
249 |
```
--------------------------------------------------------------------------------
/packages/antd/docs/components/FormButtonGroup.zh-CN.md:
--------------------------------------------------------------------------------
```markdown
1 | # FormButtonGroup
2 |
3 | > 表单按钮组布局组件
4 |
5 | ## 普通案例
6 |
7 | ```tsx
8 | import React from 'react'
9 | import {
10 | FormButtonGroup,
11 | Submit,
12 | Reset,
13 | FormItem,
14 | Input,
15 | FormLayout,
16 | } from '@formily/antd'
17 | import { createForm } from '@formily/core'
18 | import { FormProvider, createSchemaField } from '@formily/react'
19 | const SchemaField = createSchemaField({
20 | components: {
21 | FormItem,
22 | Input,
23 | },
24 | })
25 |
26 | const form = createForm()
27 |
28 | export default () => {
29 | return (
30 | <FormProvider form={form}>
31 | <FormLayout labelCol={6} wrapperCol={10}>
32 | <SchemaField>
33 | <SchemaField.String
34 | title="输入框"
35 | x-decorator="FormItem"
36 | required
37 | x-component="Input"
38 | />
39 | <SchemaField.String
40 | title="输入框"
41 | x-decorator="FormItem"
42 | required
43 | x-component="Input"
44 | />
45 | <SchemaField.String
46 | title="输入框"
47 | x-decorator="FormItem"
48 | required
49 | x-component="Input"
50 | />
51 | <SchemaField.String
52 | title="输入框"
53 | x-decorator="FormItem"
54 | required
55 | x-component="Input"
56 | />
57 | <SchemaField.String
58 | title="输入框"
59 | x-decorator="FormItem"
60 | required
61 | x-component="Input"
62 | />
63 | <SchemaField.String
64 | title="输入框"
65 | x-decorator="FormItem"
66 | required
67 | x-component="Input"
68 | />
69 | <SchemaField.String
70 | title="输入框"
71 | x-decorator="FormItem"
72 | required
73 | x-component="Input"
74 | />
75 | <SchemaField.String
76 | title="输入框"
77 | x-decorator="FormItem"
78 | required
79 | x-component="Input"
80 | />
81 | <SchemaField.String
82 | title="输入框"
83 | x-decorator="FormItem"
84 | required
85 | x-component="Input"
86 | />
87 | </SchemaField>
88 | <FormButtonGroup.FormItem>
89 | <Submit onSubmit={console.log}>提交</Submit>
90 | <Reset>重置</Reset>
91 | </FormButtonGroup.FormItem>
92 | </FormLayout>
93 | </FormProvider>
94 | )
95 | }
96 | ```
97 |
98 | ## 吸底案例
99 |
100 | ```tsx
101 | import React from 'react'
102 | import {
103 | FormButtonGroup,
104 | Submit,
105 | Reset,
106 | FormItem,
107 | FormLayout,
108 | Input,
109 | } from '@formily/antd'
110 | import { createForm } from '@formily/core'
111 | import { FormProvider, createSchemaField } from '@formily/react'
112 |
113 | const SchemaField = createSchemaField({
114 | components: {
115 | FormItem,
116 | Input,
117 | },
118 | })
119 |
120 | const form = createForm()
121 |
122 | export default () => {
123 | return (
124 | <FormProvider form={form}>
125 | <FormLayout labelCol={6} wrapperCol={10}>
126 | <SchemaField>
127 | <SchemaField.String
128 | title="输入框"
129 | x-decorator="FormItem"
130 | required
131 | x-component="Input"
132 | />
133 | <SchemaField.String
134 | title="输入框"
135 | x-decorator="FormItem"
136 | required
137 | x-component="Input"
138 | />
139 | <SchemaField.String
140 | title="输入框"
141 | x-decorator="FormItem"
142 | required
143 | x-component="Input"
144 | />
145 | <SchemaField.String
146 | title="输入框"
147 | x-decorator="FormItem"
148 | required
149 | x-component="Input"
150 | />
151 | <SchemaField.String
152 | title="输入框"
153 | x-decorator="FormItem"
154 | required
155 | x-component="Input"
156 | />
157 | <SchemaField.String
158 | title="输入框"
159 | x-decorator="FormItem"
160 | required
161 | x-component="Input"
162 | />
163 | <SchemaField.String
164 | title="输入框"
165 | x-decorator="FormItem"
166 | required
167 | x-component="Input"
168 | />
169 | <SchemaField.String
170 | title="输入框"
171 | x-decorator="FormItem"
172 | required
173 | x-component="Input"
174 | />
175 | <SchemaField.String
176 | title="输入框"
177 | x-decorator="FormItem"
178 | required
179 | x-component="Input"
180 | />
181 | </SchemaField>
182 | <FormButtonGroup.Sticky>
183 | <FormButtonGroup.FormItem>
184 | <Submit onSubmit={console.log}>提交</Submit>
185 | <Reset>重置</Reset>
186 | </FormButtonGroup.FormItem>
187 | </FormButtonGroup.Sticky>
188 | </FormLayout>
189 | </FormProvider>
190 | )
191 | }
192 | ```
193 |
194 | ## 吸底居中案例
195 |
196 | ```tsx
197 | import React from 'react'
198 | import {
199 | FormButtonGroup,
200 | Submit,
201 | Reset,
202 | FormItem,
203 | FormLayout,
204 | Input,
205 | } from '@formily/antd'
206 | import { createForm } from '@formily/core'
207 | import { FormProvider, createSchemaField } from '@formily/react'
208 |
209 | const SchemaField = createSchemaField({
210 | components: {
211 | FormItem,
212 | Input,
213 | },
214 | })
215 |
216 | const form = createForm()
217 |
218 | export default () => {
219 | return (
220 | <FormProvider form={form}>
221 | <FormLayout labelCol={6} wrapperCol={10}>
222 | <SchemaField>
223 | <SchemaField.String
224 | title="输入框"
225 | x-decorator="FormItem"
226 | required
227 | x-component="Input"
228 | />
229 | <SchemaField.String
230 | title="输入框"
231 | x-decorator="FormItem"
232 | required
233 | x-component="Input"
234 | />
235 | <SchemaField.String
236 | title="输入框"
237 | x-decorator="FormItem"
238 | required
239 | x-component="Input"
240 | />
241 | <SchemaField.String
242 | title="输入框"
243 | x-decorator="FormItem"
244 | required
245 | x-component="Input"
246 | />
247 | <SchemaField.String
248 | title="输入框"
249 | x-decorator="FormItem"
250 | required
251 | x-component="Input"
252 | />
253 | <SchemaField.String
254 | title="输入框"
255 | x-decorator="FormItem"
256 | required
257 | x-component="Input"
258 | />
259 | <SchemaField.String
260 | title="输入框"
261 | x-decorator="FormItem"
262 | required
263 | x-component="Input"
264 | />
265 | <SchemaField.String
266 | title="输入框"
267 | x-decorator="FormItem"
268 | required
269 | x-component="Input"
270 | />
271 | <SchemaField.String
272 | title="输入框"
273 | x-decorator="FormItem"
274 | required
275 | x-component="Input"
276 | />
277 | </SchemaField>
278 | <FormButtonGroup.Sticky align="center">
279 | <FormButtonGroup>
280 | <Submit onSubmit={console.log}>提交</Submit>
281 | <Reset>重置</Reset>
282 | </FormButtonGroup>
283 | </FormButtonGroup.Sticky>
284 | </FormLayout>
285 | </FormProvider>
286 | )
287 | }
288 | ```
289 |
290 | ## API
291 |
292 | ### FormButtonGroup
293 |
294 | > 该组件主要用来处理按钮组间隙
295 |
296 | | 属性名 | 类型 | 描述 | 默认值 |
297 | | ------ | --------------------------- | -------- | -------- |
298 | | gutter | number | 间隙大小 | 8px |
299 | | align | `'left'\|'center'\|'right'` | 对齐方式 | `'left'` |
300 |
301 | ### FormButtonGroup.FormItem
302 |
303 | > 该组件主要用来处理按钮组与主表单 FormItem 对齐问题
304 |
305 | 参考 [FormItem](/components/form-item) 属性
306 |
307 | ### FormButtonGroup.Sticky
308 |
309 | > 该组件主要用来处理按钮组浮动定位问题
310 |
311 | | 属性名 | 类型 | 描述 | 默认值 |
312 | | ------ | --------------------------- | -------- | -------- |
313 | | align | `'left'\|'center'\|'right'` | 对齐方式 | `'left'` |
314 |
```
--------------------------------------------------------------------------------
/packages/next/docs/components/FormButtonGroup.zh-CN.md:
--------------------------------------------------------------------------------
```markdown
1 | # FormButtonGroup
2 |
3 | > 表单按钮组布局组件
4 |
5 | ## 普通案例
6 |
7 | ```tsx
8 | import React from 'react'
9 | import {
10 | FormButtonGroup,
11 | Submit,
12 | Reset,
13 | FormItem,
14 | Input,
15 | FormLayout,
16 | } from '@formily/next'
17 | import { createForm } from '@formily/core'
18 | import { FormProvider, createSchemaField } from '@formily/react'
19 |
20 | const SchemaField = createSchemaField({
21 | components: {
22 | FormItem,
23 | Input,
24 | },
25 | })
26 |
27 | const form = createForm()
28 |
29 | export default () => {
30 | return (
31 | <FormProvider form={form}>
32 | <FormLayout labelCol={6} wrapperCol={10}>
33 | <SchemaField>
34 | <SchemaField.String
35 | title="输入框"
36 | x-decorator="FormItem"
37 | required
38 | x-component="Input"
39 | />
40 | <SchemaField.String
41 | title="输入框"
42 | x-decorator="FormItem"
43 | required
44 | x-component="Input"
45 | />
46 | <SchemaField.String
47 | title="输入框"
48 | x-decorator="FormItem"
49 | required
50 | x-component="Input"
51 | />
52 | <SchemaField.String
53 | title="输入框"
54 | x-decorator="FormItem"
55 | required
56 | x-component="Input"
57 | />
58 | <SchemaField.String
59 | title="输入框"
60 | x-decorator="FormItem"
61 | required
62 | x-component="Input"
63 | />
64 | <SchemaField.String
65 | title="输入框"
66 | x-decorator="FormItem"
67 | required
68 | x-component="Input"
69 | />
70 | <SchemaField.String
71 | title="输入框"
72 | x-decorator="FormItem"
73 | required
74 | x-component="Input"
75 | />
76 | <SchemaField.String
77 | title="输入框"
78 | x-decorator="FormItem"
79 | required
80 | x-component="Input"
81 | />
82 | <SchemaField.String
83 | title="输入框"
84 | x-decorator="FormItem"
85 | required
86 | x-component="Input"
87 | />
88 | </SchemaField>
89 | <FormButtonGroup.FormItem>
90 | <Submit onSubmit={console.log}>提交</Submit>
91 | <Reset>重置</Reset>
92 | </FormButtonGroup.FormItem>
93 | </FormLayout>
94 | </FormProvider>
95 | )
96 | }
97 | ```
98 |
99 | ## 吸底案例
100 |
101 | ```tsx
102 | import React from 'react'
103 | import {
104 | FormButtonGroup,
105 | Submit,
106 | Reset,
107 | FormItem,
108 | FormLayout,
109 | Input,
110 | } from '@formily/next'
111 | import { createForm } from '@formily/core'
112 | import { FormProvider, createSchemaField } from '@formily/react'
113 |
114 | const SchemaField = createSchemaField({
115 | components: {
116 | FormItem,
117 | Input,
118 | },
119 | })
120 |
121 | const form = createForm()
122 |
123 | export default () => {
124 | return (
125 | <FormProvider form={form}>
126 | <FormLayout labelCol={6} wrapperCol={10}>
127 | <SchemaField>
128 | <SchemaField.String
129 | title="输入框"
130 | x-decorator="FormItem"
131 | required
132 | x-component="Input"
133 | />
134 | <SchemaField.String
135 | title="输入框"
136 | x-decorator="FormItem"
137 | required
138 | x-component="Input"
139 | />
140 | <SchemaField.String
141 | title="输入框"
142 | x-decorator="FormItem"
143 | required
144 | x-component="Input"
145 | />
146 | <SchemaField.String
147 | title="输入框"
148 | x-decorator="FormItem"
149 | required
150 | x-component="Input"
151 | />
152 | <SchemaField.String
153 | title="输入框"
154 | x-decorator="FormItem"
155 | required
156 | x-component="Input"
157 | />
158 | <SchemaField.String
159 | title="输入框"
160 | x-decorator="FormItem"
161 | required
162 | x-component="Input"
163 | />
164 | <SchemaField.String
165 | title="输入框"
166 | x-decorator="FormItem"
167 | required
168 | x-component="Input"
169 | />
170 | <SchemaField.String
171 | title="输入框"
172 | x-decorator="FormItem"
173 | required
174 | x-component="Input"
175 | />
176 | <SchemaField.String
177 | title="输入框"
178 | x-decorator="FormItem"
179 | required
180 | x-component="Input"
181 | />
182 | </SchemaField>
183 | <FormButtonGroup.Sticky>
184 | <FormButtonGroup.FormItem>
185 | <Submit onSubmit={console.log}>提交</Submit>
186 | <Reset>重置</Reset>
187 | </FormButtonGroup.FormItem>
188 | </FormButtonGroup.Sticky>
189 | </FormLayout>
190 | </FormProvider>
191 | )
192 | }
193 | ```
194 |
195 | ## 吸底居中案例
196 |
197 | ```tsx
198 | import React from 'react'
199 | import {
200 | FormButtonGroup,
201 | Submit,
202 | Reset,
203 | FormItem,
204 | FormLayout,
205 | Input,
206 | } from '@formily/next'
207 | import { createForm } from '@formily/core'
208 | import { FormProvider, createSchemaField } from '@formily/react'
209 |
210 | const SchemaField = createSchemaField({
211 | components: {
212 | FormItem,
213 | Input,
214 | },
215 | })
216 |
217 | const form = createForm()
218 |
219 | export default () => {
220 | return (
221 | <FormProvider form={form}>
222 | <FormLayout labelCol={6} wrapperCol={10}>
223 | <SchemaField>
224 | <SchemaField.String
225 | title="输入框"
226 | x-decorator="FormItem"
227 | required
228 | x-component="Input"
229 | />
230 | <SchemaField.String
231 | title="输入框"
232 | x-decorator="FormItem"
233 | required
234 | x-component="Input"
235 | />
236 | <SchemaField.String
237 | title="输入框"
238 | x-decorator="FormItem"
239 | required
240 | x-component="Input"
241 | />
242 | <SchemaField.String
243 | title="输入框"
244 | x-decorator="FormItem"
245 | required
246 | x-component="Input"
247 | />
248 | <SchemaField.String
249 | title="输入框"
250 | x-decorator="FormItem"
251 | required
252 | x-component="Input"
253 | />
254 | <SchemaField.String
255 | title="输入框"
256 | x-decorator="FormItem"
257 | required
258 | x-component="Input"
259 | />
260 | <SchemaField.String
261 | title="输入框"
262 | x-decorator="FormItem"
263 | required
264 | x-component="Input"
265 | />
266 | <SchemaField.String
267 | title="输入框"
268 | x-decorator="FormItem"
269 | required
270 | x-component="Input"
271 | />
272 | <SchemaField.String
273 | title="输入框"
274 | x-decorator="FormItem"
275 | required
276 | x-component="Input"
277 | />
278 | </SchemaField>
279 | <FormButtonGroup.Sticky align="center">
280 | <FormButtonGroup>
281 | <Submit onSubmit={console.log}>提交</Submit>
282 | <Reset>重置</Reset>
283 | </FormButtonGroup>
284 | </FormButtonGroup.Sticky>
285 | </FormLayout>
286 | </FormProvider>
287 | )
288 | }
289 | ```
290 |
291 | ## API
292 |
293 | ### FormButtonGroup
294 |
295 | > 该组件主要用来处理按钮组间隙
296 |
297 | | 属性名 | 类型 | 描述 | 默认值 |
298 | | ------ | --------------------------- | -------- | -------- |
299 | | gutter | number | 间隙大小 | 8px |
300 | | align | `'left'\|'center'\|'right'` | 对齐方式 | `'left'` |
301 |
302 | ### FormButtonGroup.FormItem
303 |
304 | > 该组件主要用来处理按钮组与主表单 FormItem 对齐问题
305 |
306 | 参考 [FormItem](/components/form-item) 属性
307 |
308 | ### FormButtonGroup.Sticky
309 |
310 | > 该组件主要用来处理按钮组浮动定位问题
311 |
312 | | 属性名 | 类型 | 描述 | 默认值 |
313 | | ------ | --------------------------- | -------- | -------- |
314 | | align | `'left'\|'center'\|'right'` | 对齐方式 | `'left'` |
315 |
```
--------------------------------------------------------------------------------
/packages/next/src/array-cards/index.tsx:
--------------------------------------------------------------------------------
```typescript
1 | import React from 'react'
2 | import { Card } from '@alifd/next'
3 | import { CardProps } from '@alifd/next/lib/card'
4 | import { ArrayField } from '@formily/core'
5 | import {
6 | useField,
7 | observer,
8 | useFieldSchema,
9 | RecursionField,
10 | } from '@formily/react'
11 | import { ISchema } from '@formily/json-schema'
12 | import { usePrefixCls } from '../__builtins__'
13 | import { ArrayBase, ArrayBaseMixins, IArrayBaseProps } from '../array-base'
14 | import cls from 'classnames'
15 |
16 | type ComposedArrayCards = React.FC<
17 | React.PropsWithChildren<CardProps & IArrayBaseProps>
18 | > &
19 | ArrayBaseMixins
20 |
21 | const isAdditionComponent = (schema: ISchema) => {
22 | return schema['x-component']?.indexOf?.('Addition') > -1
23 | }
24 |
25 | const isIndexComponent = (schema: ISchema) => {
26 | return schema['x-component']?.indexOf?.('Index') > -1
27 | }
28 |
29 | const isRemoveComponent = (schema: ISchema) => {
30 | return schema['x-component']?.indexOf?.('Remove') > -1
31 | }
32 |
33 | const isCopyComponent = (schema: ISchema) => {
34 | return schema['x-component']?.indexOf?.('Copy') > -1
35 | }
36 |
37 | const isMoveUpComponent = (schema: ISchema) => {
38 | return schema['x-component']?.indexOf?.('MoveUp') > -1
39 | }
40 |
41 | const isMoveDownComponent = (schema: ISchema) => {
42 | return schema['x-component']?.indexOf?.('MoveDown') > -1
43 | }
44 |
45 | const isOperationComponent = (schema: ISchema) => {
46 | return (
47 | isAdditionComponent(schema) ||
48 | isRemoveComponent(schema) ||
49 | isCopyComponent(schema) ||
50 | isMoveDownComponent(schema) ||
51 | isMoveUpComponent(schema)
52 | )
53 | }
54 |
55 | const Empty = () => {
56 | return (
57 | <div className="next-empty">
58 | <div className="next-empty-image">
59 | <svg
60 | className="ant-empty-img-default"
61 | width="184"
62 | height="152"
63 | viewBox="0 0 184 152"
64 | xmlns="http://www.w3.org/2000/svg"
65 | >
66 | <g fill="none" fillRule="evenodd">
67 | <g transform="translate(24 31.67)">
68 | <ellipse
69 | className="ant-empty-img-default-ellipse"
70 | cx="67.797"
71 | cy="106.89"
72 | rx="67.797"
73 | ry="12.668"
74 | ></ellipse>
75 | <path
76 | className="ant-empty-img-default-path-1"
77 | d="M122.034 69.674L98.109 40.229c-1.148-1.386-2.826-2.225-4.593-2.225h-51.44c-1.766 0-3.444.839-4.592 2.225L13.56 69.674v15.383h108.475V69.674z"
78 | ></path>
79 | <path
80 | className="ant-empty-img-default-path-2"
81 | d="M101.537 86.214L80.63 61.102c-1.001-1.207-2.507-1.867-4.048-1.867H31.724c-1.54 0-3.047.66-4.048 1.867L6.769 86.214v13.792h94.768V86.214z"
82 | transform="translate(13.56)"
83 | ></path>
84 | <path
85 | className="ant-empty-img-default-path-3"
86 | d="M33.83 0h67.933a4 4 0 0 1 4 4v93.344a4 4 0 0 1-4 4H33.83a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4z"
87 | ></path>
88 | <path
89 | className="ant-empty-img-default-path-4"
90 | d="M42.678 9.953h50.237a2 2 0 0 1 2 2V36.91a2 2 0 0 1-2 2H42.678a2 2 0 0 1-2-2V11.953a2 2 0 0 1 2-2zM42.94 49.767h49.713a2.262 2.262 0 1 1 0 4.524H42.94a2.262 2.262 0 0 1 0-4.524zM42.94 61.53h49.713a2.262 2.262 0 1 1 0 4.525H42.94a2.262 2.262 0 0 1 0-4.525zM121.813 105.032c-.775 3.071-3.497 5.36-6.735 5.36H20.515c-3.238 0-5.96-2.29-6.734-5.36a7.309 7.309 0 0 1-.222-1.79V69.675h26.318c2.907 0 5.25 2.448 5.25 5.42v.04c0 2.971 2.37 5.37 5.277 5.37h34.785c2.907 0 5.277-2.421 5.277-5.393V75.1c0-2.972 2.343-5.426 5.25-5.426h26.318v33.569c0 .617-.077 1.216-.221 1.789z"
91 | ></path>
92 | </g>
93 | <path
94 | className="ant-empty-img-default-path-5"
95 | d="M149.121 33.292l-6.83 2.65a1 1 0 0 1-1.317-1.23l1.937-6.207c-2.589-2.944-4.109-6.534-4.109-10.408C138.802 8.102 148.92 0 161.402 0 173.881 0 184 8.102 184 18.097c0 9.995-10.118 18.097-22.599 18.097-4.528 0-8.744-1.066-12.28-2.902z"
96 | ></path>
97 | <g
98 | className="ant-empty-img-default-g"
99 | transform="translate(149.65 15.383)"
100 | >
101 | <ellipse cx="20.654" cy="3.167" rx="2.849" ry="2.815"></ellipse>
102 | <path d="M5.698 5.63H0L2.898.704zM9.259.704h4.985V5.63H9.259z"></path>
103 | </g>
104 | </g>
105 | </svg>
106 | </div>
107 | </div>
108 | )
109 | }
110 |
111 | export const ArrayCards: ComposedArrayCards = observer((props) => {
112 | const field = useField<ArrayField>()
113 | const schema = useFieldSchema()
114 | const dataSource = Array.isArray(field.value) ? field.value : []
115 | const prefixCls = usePrefixCls('formily-array-cards', props)
116 | const { onAdd, onCopy, onRemove, onMoveDown, onMoveUp } = props
117 | const renderItems = () => {
118 | return dataSource?.map((item, index) => {
119 | const items = Array.isArray(schema.items)
120 | ? schema.items[index] || schema.items[0]
121 | : schema.items
122 | const title = (
123 | <span>
124 | <RecursionField
125 | schema={items}
126 | name={index}
127 | filterProperties={(schema) => {
128 | if (!isIndexComponent(schema)) return false
129 | return true
130 | }}
131 | onlyRenderProperties
132 | />
133 | {props.title || field.title}
134 | </span>
135 | )
136 | const extra = (
137 | <span>
138 | <RecursionField
139 | schema={items}
140 | name={index}
141 | filterProperties={(schema) => {
142 | if (!isOperationComponent(schema)) return false
143 | return true
144 | }}
145 | onlyRenderProperties
146 | />
147 | {props.extra}
148 | </span>
149 | )
150 | const content = (
151 | <RecursionField
152 | schema={items}
153 | name={index}
154 | filterProperties={(schema) => {
155 | if (isIndexComponent(schema)) return false
156 | if (isOperationComponent(schema)) return false
157 | return true
158 | }}
159 | />
160 | )
161 | return (
162 | <ArrayBase.Item
163 | key={index}
164 | index={index}
165 | record={() => field.value?.[index]}
166 | >
167 | <Card
168 | contentHeight="auto"
169 | {...props}
170 | onChange={() => {}}
171 | className={cls(`${prefixCls}-item`, props.className)}
172 | title={title}
173 | extra={extra}
174 | >
175 | {content}
176 | </Card>
177 | </ArrayBase.Item>
178 | )
179 | })
180 | }
181 |
182 | const renderAddition = () => {
183 | return schema.reduceProperties((addition, schema, key) => {
184 | if (isAdditionComponent(schema)) {
185 | return <RecursionField schema={schema} name={key} />
186 | }
187 | return addition
188 | }, null)
189 | }
190 |
191 | const renderEmpty = () => {
192 | if (dataSource?.length) return
193 | return (
194 | <Card
195 | contentHeight="auto"
196 | {...props}
197 | className={cls(`${prefixCls}-item`, props.className)}
198 | title={props.title || field.title}
199 | onChange={() => {}}
200 | >
201 | <Empty />
202 | </Card>
203 | )
204 | }
205 |
206 | return (
207 | <ArrayBase
208 | onAdd={onAdd}
209 | onCopy={onCopy}
210 | onRemove={onRemove}
211 | onMoveUp={onMoveUp}
212 | onMoveDown={onMoveDown}
213 | >
214 | {renderEmpty()}
215 | {renderItems()}
216 | {renderAddition()}
217 | </ArrayBase>
218 | )
219 | })
220 |
221 | ArrayCards.displayName = 'ArrayCards'
222 |
223 | ArrayBase.mixin(ArrayCards)
224 |
225 | export default ArrayCards
226 |
```
--------------------------------------------------------------------------------
/packages/next/docs/components/Editable.md:
--------------------------------------------------------------------------------
```markdown
1 | # Editable
2 |
3 | > Partial editor, you can use this component for some form areas with high space requirements
4 | >
5 | > Editable component is equivalent to a variant of FormItem component, so it is usually placed in decorator
6 |
7 | ## Markup Schema example
8 |
9 | ```tsx
10 | import React from 'react'
11 | import {
12 | Input,
13 | DatePicker,
14 | Editable,
15 | FormItem,
16 | FormButtonGroup,
17 | Submit,
18 | } from '@formily/next'
19 | import { createForm } from '@formily/core'
20 | import { FormProvider, createSchemaField } from '@formily/react'
21 |
22 | const SchemaField = createSchemaField({
23 | components: {
24 | DatePicker,
25 | Editable,
26 | Input,
27 | FormItem,
28 | },
29 | })
30 |
31 | const form = createForm()
32 |
33 | export default () => (
34 | <FormProvider form={form}>
35 | <SchemaField>
36 | <SchemaField.String
37 | name="date"
38 | title="date"
39 | x-decorator="Editable"
40 | x-component="DatePicker"
41 | />
42 | <SchemaField.String
43 | name="input"
44 | title="input box"
45 | x-decorator="Editable"
46 | x-component="Input"
47 | />
48 | <SchemaField.Void
49 | name="void"
50 | title="Virtual Node Container"
51 | x-component="Editable.Popover"
52 | x-reactions={(field) => {
53 | field.title = field.query('.void.date2').get('value') || field.title
54 | }}
55 | >
56 | <SchemaField.String
57 | name="date2"
58 | title="date"
59 | x-decorator="FormItem"
60 | x-component="DatePicker"
61 | x-component-props={{
62 | followTrigger: true,
63 | }}
64 | />
65 | <SchemaField.String
66 | name="input2"
67 | title="input box"
68 | x-decorator="FormItem"
69 | x-component="Input"
70 | />
71 | </SchemaField.Void>
72 | <SchemaField.Object
73 | name="iobject"
74 | title="Object node container"
75 | x-component="Editable.Popover"
76 | x-reactions={(field) => {
77 | field.title = field.value?.date || field.title
78 | }}
79 | >
80 | <SchemaField.String
81 | name="date"
82 | title="date"
83 | x-decorator="FormItem"
84 | x-component="DatePicker"
85 | x-component-props={{
86 | followTrigger: true,
87 | }}
88 | />
89 | <SchemaField.String
90 | name="input"
91 | title="input box"
92 | x-decorator="FormItem"
93 | x-component="Input"
94 | />
95 | </SchemaField.Object>
96 | </SchemaField>
97 | <FormButtonGroup>
98 | <Submit onSubmit={console.log}>Submit</Submit>
99 | </FormButtonGroup>
100 | </FormProvider>
101 | )
102 | ```
103 |
104 | ## JSON Schema case
105 |
106 | ```tsx
107 | import React from 'react'
108 | import {
109 | Input,
110 | DatePicker,
111 | Editable,
112 | FormItem,
113 | FormButtonGroup,
114 | Submit,
115 | } from '@formily/next'
116 | import { createForm } from '@formily/core'
117 | import { FormProvider, createSchemaField } from '@formily/react'
118 |
119 | const SchemaField = createSchemaField({
120 | components: {
121 | DatePicker,
122 | Editable,
123 | Input,
124 | FormItem,
125 | },
126 | })
127 |
128 | const form = createForm()
129 |
130 | const schema = {
131 | type: 'object',
132 | properties: {
133 | date: {
134 | type: 'string',
135 | title: 'Date',
136 | 'x-decorator': 'Editable',
137 | 'x-component': 'DatePicker',
138 | },
139 | input: {
140 | type: 'string',
141 | title: 'input box',
142 | 'x-decorator': 'Editable',
143 | 'x-component': 'Input',
144 | },
145 | void: {
146 | type: 'void',
147 | title: 'Virtual Node Container',
148 | 'x-component': 'Editable.Popover',
149 | 'x-reactions':
150 | "{{(field) => field.title = field.query('.void.date2').get('value') || field.title}}",
151 | properties: {
152 | date2: {
153 | type: 'string',
154 | title: 'Date',
155 | 'x-decorator': 'FormItem',
156 | 'x-component': 'DatePicker',
157 | 'x-component-props': {
158 | followTrigger: true,
159 | },
160 | },
161 | input2: {
162 | type: 'string',
163 | title: 'input box',
164 | 'x-decorator': 'FormItem',
165 | 'x-component': 'Input',
166 | },
167 | },
168 | },
169 | iobject: {
170 | type: 'object',
171 | title: 'Object node container',
172 | 'x-component': 'Editable.Popover',
173 | 'x-reactions':
174 | '{{(field) => field.title = field.value && field.value.date || field.title}}',
175 | properties: {
176 | date: {
177 | type: 'string',
178 | title: 'Date',
179 | 'x-decorator': 'FormItem',
180 | 'x-component': 'DatePicker',
181 | 'x-component-props': {
182 | followTrigger: true,
183 | },
184 | },
185 | input: {
186 | type: 'string',
187 | title: 'input box',
188 | 'x-decorator': 'FormItem',
189 | 'x-component': 'Input',
190 | },
191 | },
192 | },
193 | },
194 | }
195 |
196 | export default () => (
197 | <FormProvider form={form}>
198 | <SchemaField schema={schema} />
199 | <FormButtonGroup>
200 | <Submit onSubmit={console.log}>Submit</Submit>
201 | </FormButtonGroup>
202 | </FormProvider>
203 | )
204 | ```
205 |
206 | ## Pure JSX case
207 |
208 | ```tsx
209 | import React from 'react'
210 | import {
211 | Input,
212 | DatePicker,
213 | Editable,
214 | FormItem,
215 | FormButtonGroup,
216 | Submit,
217 | } from '@formily/next'
218 | import { createForm } from '@formily/core'
219 | import { FormProvider, Field, VoidField, ObjectField } from '@formily/react'
220 |
221 | const form = createForm()
222 |
223 | export default () => (
224 | <FormProvider form={form}>
225 | <Field
226 | name="date"
227 | title="date"
228 | decorator={[Editable]}
229 | component={[DatePicker]}
230 | />
231 | <Field
232 | name="input"
233 | title="input box"
234 | decorator={[Editable]}
235 | component={[Input]}
236 | />
237 | <VoidField
238 | name="void"
239 | title="Virtual Node Container"
240 | reactions={(field) => {
241 | field.title = field.query('.void.date2').get('value') || field.title
242 | }}
243 | component={[Editable.Popover]}
244 | >
245 | <Field
246 | name="date2"
247 | title="date"
248 | decorator={[FormItem]}
249 | component={[
250 | DatePicker,
251 | {
252 | followTrigger: true,
253 | },
254 | ]}
255 | />
256 | <Field
257 | name="input2"
258 | title="input box"
259 | decorator={[FormItem]}
260 | component={[Input]}
261 | />
262 | </VoidField>
263 | <ObjectField
264 | name="iobject"
265 | title="Object node container"
266 | component={[
267 | Editable.Popover,
268 | {
269 | renderPreview: (field) => {
270 | return field.value?.date
271 | },
272 | },
273 | ]}
274 | >
275 | <Field
276 | name="date"
277 | title="date"
278 | decorator={[FormItem]}
279 | component={[
280 | DatePicker,
281 | {
282 | followTrigger: true,
283 | },
284 | ]}
285 | />
286 | <Field
287 | name="input"
288 | title="input box"
289 | decorator={[FormItem]}
290 | component={[Input]}
291 | />
292 | </ObjectField>
293 |
294 | <FormButtonGroup>
295 | <Submit onSubmit={console.log}>Submit</Submit>
296 | </FormButtonGroup>
297 | </FormProvider>
298 | )
299 | ```
300 |
301 | ## API
302 |
303 | ### Editable
304 |
305 | > Inline editing
306 |
307 | Refer to the FormItem property in https://fusion.design/pc/component/basic/form
308 |
309 | ### Editable.Popover
310 |
311 | > Floating layer editing
312 |
313 | | Property name | Type | Description | Default value |
314 | | ------------- | --------------------------------- | ---------------- | ------------- |
315 | | renderPreview | `(field:GeneralField)=>ReactNode` | Preview renderer | |
316 |
317 | Note: If there is a floating layer component such as Select/DatePicker inside the Popover, followTrigger=true needs to be configured on the floating layer component
318 |
319 | Other references https://fusion.design/pc/component/basic/balloon
320 |
```