From e9dec48fe43b755d985ecd5dfef4be9c3a85848a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=88=E8=90=BD=E9=9F=B3=E9=98=91?= <424532913@qq.com> Date: Tue, 29 Jun 2021 20:17:03 +0800 Subject: [PATCH] feat(vue): add components prop for schema-field (#1686) * feat(vue): add components prop for schema-field * feat(vue): update types * feat(vue): support object style of component "style" * feat(vue): add test cases for x-content --- packages/vue/docs/api/shared/schema.md | 2 +- .../vue/docs/demos/api/shared/observer.vue | 31 +-- packages/vue/package.json | 1 + packages/vue/src/__tests__/field.spec.ts | 10 +- packages/vue/src/__tests__/form.spec.ts | 3 +- .../vue/src/__tests__/schema.json.spec.ts | 213 +++++++++++++++++- .../vue/src/__tests__/schema.markup.spec.ts | 8 +- packages/vue/src/__tests__/shared.spec.ts | 7 + packages/vue/src/components/ArrayField.ts | 11 +- packages/vue/src/components/Field.ts | 20 +- packages/vue/src/components/FormConsumer.ts | 4 +- packages/vue/src/components/FormProvider.ts | 4 +- packages/vue/src/components/ObjectField.ts | 12 +- packages/vue/src/components/ReactiveField.ts | 34 +-- packages/vue/src/components/RecursionField.ts | 39 +--- packages/vue/src/components/SchemaField.ts | 100 +++----- packages/vue/src/components/VoidField.ts | 12 +- packages/vue/src/global.d.ts | 8 + packages/vue/src/index.ts | 2 +- packages/vue/src/shared/connect.ts | 11 +- packages/vue/src/shared/context.ts | 5 +- packages/vue/src/types/index.ts | 45 ++-- packages/vue/src/types/vue-demi.d.ts | 5 - packages/vue/src/types/vue2.ts | 9 +- packages/vue/src/vue2-components.ts | 74 ++++++ yarn.lock | 61 ++--- 26 files changed, 501 insertions(+), 230 deletions(-) create mode 100644 packages/vue/src/__tests__/shared.spec.ts create mode 100644 packages/vue/src/global.d.ts delete mode 100644 packages/vue/src/types/vue-demi.d.ts create mode 100644 packages/vue/src/vue2-components.ts diff --git a/packages/vue/docs/api/shared/schema.md b/packages/vue/docs/api/shared/schema.md index 04e5ffcdf3d..94837dc1beb 100644 --- a/packages/vue/docs/api/shared/schema.md +++ b/packages/vue/docs/api/shared/schema.md @@ -461,7 +461,7 @@ Schema.registerVoidComponents(['card', 'tab', 'step']) ``` - 注意,该 api 需要配合 enablePolyfills(['1.0']) 使用 +

注意,该 api 需要配合 enablePolyfills(['1.0']) 使用

### registerTypeDefaultComponents diff --git a/packages/vue/docs/demos/api/shared/observer.vue b/packages/vue/docs/demos/api/shared/observer.vue index 9556662b2a4..62b95623548 100644 --- a/packages/vue/docs/demos/api/shared/observer.vue +++ b/packages/vue/docs/demos/api/shared/observer.vue @@ -15,34 +15,37 @@ diff --git a/packages/vue/package.json b/packages/vue/package.json index 07ef1d9e7d4..0ab0a935e53 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -42,6 +42,7 @@ "@formily/shared": "2.0.0-beta.72", "@formily/validator": "2.0.0-beta.72", "@type-helper/vue3": "npm:vue@3", + "@type-helper/vue2": "npm:vue@2", "vue-demi": "^0.9.0", "vue-frag": "^1.1.4" }, diff --git a/packages/vue/src/__tests__/field.spec.ts b/packages/vue/src/__tests__/field.spec.ts index 7d360037318..58caa0d9b29 100644 --- a/packages/vue/src/__tests__/field.spec.ts +++ b/packages/vue/src/__tests__/field.spec.ts @@ -2,18 +2,14 @@ import Vue, { FunctionalComponentOptions } from 'vue' import { render, fireEvent, waitFor } from '@testing-library/vue' import { defineComponent, h } from '@vue/composition-api' import { createForm } from '@formily/core' +import { useField, useFormEffects, connect, mapProps, mapReadPretty } from '../' import { FormProvider, ArrayField, ObjectField, VoidField, Field, - useField, - useFormEffects, - connect, - mapProps, - mapReadPretty, -} from '../' +} from '../vue2-components' import ReactiveField from '../components/ReactiveField' // import { expectThrowError } from './shared' import { isField, isVoidField, onFieldChange } from '@formily/core' @@ -23,7 +19,7 @@ Vue.component('ArrayField', ArrayField) Vue.component('ObjectField', ObjectField) Vue.component('VoidField', VoidField) Vue.component('Field', Field) -Vue.component('ReactiveField', ReactiveField) +Vue.component('ReactiveField', ReactiveField as unknown as Vue) const Decorator: FunctionalComponentOptions = { functional: true, diff --git a/packages/vue/src/__tests__/form.spec.ts b/packages/vue/src/__tests__/form.spec.ts index a3c9d505172..c4d76ba46e0 100644 --- a/packages/vue/src/__tests__/form.spec.ts +++ b/packages/vue/src/__tests__/form.spec.ts @@ -1,8 +1,7 @@ import Vue from 'vue' import { render } from '@testing-library/vue' import { createForm } from '@formily/core' -import { FormProvider } from '../' -import { FormConsumer } from '../components' +import { FormProvider, FormConsumer } from '../vue2-components' Vue.component('FormProvider', FormProvider) Vue.component('FormConsumer', FormConsumer) diff --git a/packages/vue/src/__tests__/schema.json.spec.ts b/packages/vue/src/__tests__/schema.json.spec.ts index 32f64eb512a..d76cf0bba6d 100644 --- a/packages/vue/src/__tests__/schema.json.spec.ts +++ b/packages/vue/src/__tests__/schema.json.spec.ts @@ -1,8 +1,8 @@ import { createForm } from '@formily/core' -import { FormProvider, createSchemaField } from '../index' import { Schema } from '@formily/json-schema' import { render } from '@testing-library/vue' import Vue, { FunctionalComponentOptions } from 'vue' +import { FormProvider, createSchemaField } from '../vue2-components' Vue.component('FormProvider', FormProvider) @@ -21,6 +21,36 @@ const Input: FunctionalComponentOptions = { }, } +const Previewer: FunctionalComponentOptions = { + functional: true, + render(h, context) { + return h( + 'div', + { + attrs: { + 'data-testid': 'previewer', + }, + }, + context.children + ) + }, +} + +const Previewer2: FunctionalComponentOptions = { + functional: true, + render(h, context) { + return h( + 'div', + { + attrs: { + 'data-testid': 'previewer2', + }, + }, + [context.scopedSlots.content({})] + ) + }, +} + describe('json schema field', () => { test('string field', () => { const form = createForm() @@ -85,3 +115,184 @@ describe('json schema field', () => { expect(queryByTestId('input')).toBeVisible() }) }) + +describe('x-content', () => { + test('default slot', () => { + const form = createForm() + const { SchemaField } = createSchemaField({ + components: { + Previewer, + }, + }) + const { queryByTestId } = render({ + components: { SchemaField }, + data() { + return { + form, + schema: new Schema({ + type: 'string', + 'x-component': 'Previewer', + 'x-content': '123', + }), + } + }, + template: ` + + `, + }) + expect(queryByTestId('previewer')).toBeVisible() + expect(queryByTestId('previewer').textContent).toEqual('123') + }) + + test('default slot with component', () => { + const form = createForm() + const Content = { + render(h) { + return h('span', '123') + }, + } + const { SchemaField } = createSchemaField({ + components: { + Previewer, + }, + }) + const { queryByTestId } = render({ + components: { SchemaField }, + data() { + return { + form, + schema: new Schema({ + type: 'string', + 'x-component': 'Previewer', + 'x-content': Content, + }), + } + }, + template: ` + + `, + }) + expect(queryByTestId('previewer')).toBeVisible() + expect(queryByTestId('previewer').textContent).toEqual('123') + }) + + test('default slot with name default', () => { + const form = createForm() + const Content = { + render(h) { + return h('span', '123') + }, + } + const { SchemaField } = createSchemaField({ + components: { + Previewer, + }, + }) + const { queryByTestId } = render({ + components: { SchemaField }, + data() { + return { + form, + schema: new Schema({ + type: 'string', + 'x-component': 'Previewer', + 'x-content': { + default: Content, + }, + }), + } + }, + template: ` + + `, + }) + expect(queryByTestId('previewer')).toBeVisible() + expect(queryByTestId('previewer').textContent).toEqual('123') + }) + + test('named slot', () => { + const form = createForm() + const Content = { + render(h) { + return h('span', '123') + }, + } + const { SchemaField } = createSchemaField({ + components: { + Previewer2, + }, + }) + const { queryByTestId } = render({ + components: { SchemaField }, + data() { + return { + form, + schema: new Schema({ + type: 'string', + 'x-component': 'Previewer2', + 'x-content': { + content: Content, + }, + }), + } + }, + template: ` + + `, + }) + expect(queryByTestId('previewer2')).toBeVisible() + expect(queryByTestId('previewer2').textContent).toEqual('123') + }) + + test('named slot with scope', () => { + const form = createForm() + const Content = { + render(h) { + return h('span', '123') + }, + } + const { SchemaField } = createSchemaField({ + components: { + Previewer2, + }, + scope: { + Content, + }, + }) + const { queryByTestId } = render({ + components: { SchemaField }, + data() { + return { + form, + schema: new Schema({ + type: 'string', + 'x-component': 'Previewer2', + 'x-content': { + content: '{{Content}}', + }, + }), + } + }, + template: ` + + `, + }) + expect(queryByTestId('previewer2')).toBeVisible() + expect(queryByTestId('previewer2').textContent).toEqual('123') + }) +}) diff --git a/packages/vue/src/__tests__/schema.markup.spec.ts b/packages/vue/src/__tests__/schema.markup.spec.ts index d04f474f4ba..574e031a4a6 100644 --- a/packages/vue/src/__tests__/schema.markup.spec.ts +++ b/packages/vue/src/__tests__/schema.markup.spec.ts @@ -1,12 +1,10 @@ import { createForm } from '@formily/core' +import { useFieldSchema, useField, Schema } from '../' import { FormProvider, - createSchemaField, - useFieldSchema, - useField, RecursionField, - Schema, -} from '../index' + createSchemaField, +} from '../vue2-components' import { render } from '@testing-library/vue' import { mount, createLocalVue } from '@vue/test-utils' import Vue, { CreateElement } from 'vue' diff --git a/packages/vue/src/__tests__/shared.spec.ts b/packages/vue/src/__tests__/shared.spec.ts new file mode 100644 index 00000000000..2cc7e9a18c9 --- /dev/null +++ b/packages/vue/src/__tests__/shared.spec.ts @@ -0,0 +1,7 @@ +import { createForm } from '../' +import { isRaw } from '@vue/composition-api' + +test('createForm returns an un reactive form instance.', () => { + const form = createForm() + expect(isRaw(form)).toBeTruthy() +}) diff --git a/packages/vue/src/components/ArrayField.ts b/packages/vue/src/components/ArrayField.ts index 203ba50fdbd..702da60e700 100644 --- a/packages/vue/src/components/ArrayField.ts +++ b/packages/vue/src/components/ArrayField.ts @@ -1,17 +1,16 @@ -import { provide, defineComponent, DefineComponent } from 'vue-demi' +import { provide, defineComponent } from 'vue-demi' import { useField, useForm } from '../hooks' import { useAttach } from '../hooks/useAttach' -import { VueComponent, IFieldProps } from '../types' import ReactiveField from './ReactiveField' import { FieldSymbol } from '../shared/context' import h from '../shared/h' import { getRawComponent } from '../utils/getRawComponent' import { observer } from '@formily/reactive-vue' -type ArrayFieldProps = IFieldProps +import type { IArrayFieldProps, DefineComponent } from '../types' export default observer( - defineComponent({ + defineComponent({ name: 'ArrayField', /* eslint-disable vue/require-prop-types */ /* eslint-disable vue/require-default-prop */ @@ -62,7 +61,7 @@ export default observer( validator: {}, reactions: [Array, Function], }, - setup(props: ArrayFieldProps, { slots }) { + setup(props: IArrayFieldProps, { slots }) { const formRef = useForm() const parentRef = useField() const basePath = @@ -101,5 +100,5 @@ export default observer( return h(ReactiveField, componentData, children) } }, - }) as unknown as DefineComponent + }) as unknown as DefineComponent ) diff --git a/packages/vue/src/components/Field.ts b/packages/vue/src/components/Field.ts index 476e1364ef1..c8d079b721d 100644 --- a/packages/vue/src/components/Field.ts +++ b/packages/vue/src/components/Field.ts @@ -1,13 +1,14 @@ -import { provide, defineComponent, DefineComponent } from 'vue-demi' +import { provide, defineComponent, computed } from 'vue-demi' import { useField, useForm } from '../hooks' import { useAttach } from '../hooks/useAttach' import { FieldSymbol } from '../shared/context' -import { VueComponent, IFieldProps } from '../types' import ReactiveField from './ReactiveField' import h from '../shared/h' import { getRawComponent } from '../utils/getRawComponent' -export default defineComponent>({ +import type { IFieldProps, DefineComponent } from '../types' + +export default defineComponent({ name: 'Field', /* eslint-disable vue/require-prop-types */ /* eslint-disable vue/require-default-prop */ @@ -58,20 +59,21 @@ export default defineComponent>({ validator: {}, reactions: [Array, Function], }, - setup(props: IFieldProps, { slots }) { - // const { track } = useObserver() + setup(props: IFieldProps, { slots }) { const formRef = useForm() const parentRef = useField() - const basePath = + const basePath = computed(() => props.basePath !== undefined ? props.basePath : parentRef?.value?.address + ) + const fieldRef = useAttach( () => formRef.value.createField({ ...props, - basePath, + basePath: basePath.value, ...getRawComponent(props), }), - [() => props.name, formRef] + [() => props.name, basePath, formRef] ) provide(FieldSymbol, fieldRef) @@ -96,4 +98,4 @@ export default defineComponent>({ return h(ReactiveField, componentData, children) } }, -}) as unknown as DefineComponent> +}) as unknown as DefineComponent diff --git a/packages/vue/src/components/FormConsumer.ts b/packages/vue/src/components/FormConsumer.ts index 4a412bc586f..7f9ee25244b 100644 --- a/packages/vue/src/components/FormConsumer.ts +++ b/packages/vue/src/components/FormConsumer.ts @@ -1,9 +1,11 @@ -import { defineComponent, DefineComponent } from 'vue-demi' +import { defineComponent } from 'vue-demi' import { observer } from '@formily/reactive-vue' import { useForm } from '../hooks' import h from '../shared/h' import { Fragment } from '../shared/fragment' +import type { DefineComponent } from '../types' + export default observer( defineComponent({ name: 'FormConsumer', diff --git a/packages/vue/src/components/FormProvider.ts b/packages/vue/src/components/FormProvider.ts index 4914c1179c2..1848eff8904 100644 --- a/packages/vue/src/components/FormProvider.ts +++ b/packages/vue/src/components/FormProvider.ts @@ -1,10 +1,12 @@ -import { provide, defineComponent, toRaw, DefineComponent } from 'vue-demi' +import { provide, defineComponent, toRaw } from 'vue-demi' import { FormSymbol } from '../shared/context' import { IProviderProps } from '../types' import { useAttach } from '../hooks/useAttach' import h from '../shared/h' import { Fragment } from '../shared/fragment' +import type { DefineComponent } from '../types' + export default defineComponent({ name: 'FormProvider', inheritAttrs: false, diff --git a/packages/vue/src/components/ObjectField.ts b/packages/vue/src/components/ObjectField.ts index ca8c1e43522..0e6daba6c3a 100644 --- a/packages/vue/src/components/ObjectField.ts +++ b/packages/vue/src/components/ObjectField.ts @@ -1,17 +1,16 @@ -import { provide, defineComponent, DefineComponent } from 'vue-demi' +import { provide, defineComponent } from 'vue-demi' import { useField, useForm } from '../hooks' import { useAttach } from '../hooks/useAttach' -import { VueComponent, IFieldProps } from '../types' import ReactiveField from './ReactiveField' import { observer } from '@formily/reactive-vue' import { FieldSymbol } from '../shared/context' import h from '../shared/h' import { getRawComponent } from '../utils/getRawComponent' -type ObjectFieldProps = IFieldProps +import type { IObjectFieldProps, DefineComponent } from '../types' export default observer( - defineComponent({ + defineComponent({ name: 'ObjectField', /* eslint-disable vue/require-prop-types */ /* eslint-disable vue/require-default-prop */ @@ -62,8 +61,7 @@ export default observer( validator: {}, reactions: [Array, Function], }, - setup(props: ObjectFieldProps, { slots }) { - // const { track } = useObserver() + setup(props: IObjectFieldProps, { slots }) { const formRef = useForm() const parentRef = useField() const basePath = @@ -102,5 +100,5 @@ export default observer( return h(ReactiveField, componentData, children) } }, - }) as unknown as DefineComponent + }) as unknown as DefineComponent ) diff --git a/packages/vue/src/components/ReactiveField.ts b/packages/vue/src/components/ReactiveField.ts index 5a788990ca8..e437da2a458 100644 --- a/packages/vue/src/components/ReactiveField.ts +++ b/packages/vue/src/components/ReactiveField.ts @@ -1,5 +1,4 @@ -import { VueComponent } from '../types' -import { defineComponent, DefineComponent } from 'vue-demi' +import { defineComponent } from 'vue-demi' import { isVoidField } from '@formily/core' import { clone } from '@formily/shared' import { observer } from '@formily/reactive-vue' @@ -8,9 +7,11 @@ import { toJS } from '@formily/reactive' import h from '../shared/h' import { Fragment } from '../shared/fragment' -interface IReactiveFieldProps { - field: Formily.Core.Types.GeneralField -} +import type { + IReactiveFieldProps, + VueComponent, + DefineComponent, +} from '../types' export default observer( defineComponent({ @@ -18,7 +19,6 @@ export default observer( // eslint-disable-next-line vue/require-prop-types props: ['field'], setup(props: IReactiveFieldProps, { slots }) { - // const { track } = useObserver() const key = Math.floor(Date.now() * Math.random()).toString(16) return () => { const field = props.field @@ -39,11 +39,16 @@ export default observer( } else { const decorator = field.decorator[0] as VueComponent const decoratorData = clone(field.decorator[1]) || {} + const style = decoratorData?.style + delete decoratorData.style return { default: () => h( decorator, - { attrs: decoratorData }, + { + style, + attrs: decoratorData, + }, { default: () => childNodes, } @@ -59,8 +64,7 @@ export default observer( {}, { default: () => - slots.default && - slots.default({ + slots.default?.({ field: props.field, form: props.field.form, }), @@ -104,6 +108,9 @@ export default observer( if (!isVoidField(field)) field.onBlur(...args) originBlur?.(...args) } + + const style = originData?.style + delete originData?.style const attrs = { disabled: !isVoidField(field) ? field.pattern === 'disabled' || field.pattern === 'readPretty' @@ -116,21 +123,22 @@ export default observer( value: !isVoidField(field) ? toJS(field.value) : undefined, } const componentData = { - attrs: attrs, + attrs, + style, on: events, } - const children = { + const componentChildren = { ...slots, } if (slots.default) { - children.default = () => + componentChildren.default = () => slots.default({ field: props.field, form: props.field.form, }) } - return h(component, componentData, children) + return h(component, componentData, componentChildren) } children = renderDecorator([renderComponent()]) diff --git a/packages/vue/src/components/RecursionField.ts b/packages/vue/src/components/RecursionField.ts index 598062fb62e..57dc9c1306f 100644 --- a/packages/vue/src/components/RecursionField.ts +++ b/packages/vue/src/components/RecursionField.ts @@ -1,11 +1,4 @@ -import { - inject, - provide, - watch, - defineComponent, - shallowRef, - DefineComponent, -} from 'vue-demi' +import { inject, provide, watch, defineComponent, shallowRef } from 'vue-demi' import { isFn, isValid } from '@formily/shared' import { Schema } from '@formily/json-schema' import { observer } from '@formily/reactive-vue' @@ -14,7 +7,6 @@ import { SchemaOptionsSymbol, SchemaExpressionScopeSymbol, } from '../shared' -import { IRecursionFieldProps } from '../types' import { useField } from '../hooks' import ObjectField from './ObjectField' import ArrayField from './ArrayField' @@ -23,6 +15,8 @@ import VoidField from './VoidField' import { h } from '../shared/h' import { Fragment } from '../shared/fragment' +import type { IRecursionFieldProps, DefineComponent } from '../types' + function isVueOptions(options: any) { if (!options) { return false @@ -58,26 +52,26 @@ const RecursionField = observer( }, setup(props: IRecursionFieldProps) { const parentRef = useField() - const options = inject(SchemaOptionsSymbol) + const optionsRef = inject(SchemaOptionsSymbol) const scopeRef = inject(SchemaExpressionScopeSymbol) const createSchema = (schemaProp: IRecursionFieldProps['schema']) => new Schema(schemaProp) const createFieldSchema = (schema: Schema) => schema.compile?.({ - ...options.scope, + ...optionsRef.value.scope, ...scopeRef.value, }) const schemaRef = shallowRef(createSchema(props.schema)) const fieldSchemaRef = shallowRef(createFieldSchema(schemaRef.value)) - watch([() => props.schema, scopeRef], () => { + watch([() => props.schema, scopeRef, optionsRef], () => { schemaRef.value = createSchema(props.schema) fieldSchemaRef.value = createFieldSchema(schemaRef.value) }) const getPropsFromSchema = (schema: Schema) => - schema?.toFieldProps?.(options) + schema?.toFieldProps?.(optionsRef.value) const fieldPropsRef = shallowRef(getPropsFromSchema(fieldSchemaRef.value)) - watch(fieldSchemaRef, () => { + watch([fieldSchemaRef, optionsRef], () => { fieldPropsRef.value = getPropsFromSchema(fieldSchemaRef.value) }) @@ -226,23 +220,6 @@ const RecursionField = observer( }) } - if (typeof xContent === 'string') { - slots['default'] = () => [xContent] - } else if (isVueOptions(xContent) || typeof xContent === 'function') { - // is vue component or functional component - slots['default'] = () => [h(xContent, {}, {})] - } else if (xContent && typeof xContent === 'object') { - // for named slots - Object.keys(xContent).forEach((key) => { - const child = xContent[key] - if (typeof child === 'string') { - slots[key] = () => [child] - } else if (isVueOptions(child) || typeof child === 'function') { - slots[key] = () => [h(child, {}, {})] - } - }) - } - return h( Field, { diff --git a/packages/vue/src/components/SchemaField.ts b/packages/vue/src/components/SchemaField.ts index a8d744040a7..9b6c714cc0e 100644 --- a/packages/vue/src/components/SchemaField.ts +++ b/packages/vue/src/components/SchemaField.ts @@ -6,7 +6,6 @@ import { shallowRef, watch, } from 'vue-demi' -import type { DefineComponent } from 'vue-demi' import { ISchema, Schema, SchemaTypes } from '@formily/json-schema' import { RecursionField } from '../components' import { @@ -21,11 +20,14 @@ import { SchemaComponents, ISchemaFieldProps, ISchemaMarkupFieldProps, + ISchemaTypeFieldProps, } from '../types' import { resolveSchemaProps } from '../utils/resolveSchemaProps' import { h } from '../shared/h' import { Fragment } from '../shared/fragment' +import type { DefineComponent } from '../types' + const env = { nonameId: 0, } @@ -112,78 +114,22 @@ const markupProps = { }, } -type SchemaFieldComponents = { - SchemaField: DefineComponent> - SchemaMarkupField: DefineComponent< - ISchemaMarkupFieldProps< - Components, - ComponentPath, - ComponentPath - > - > - SchemaStringField: DefineComponent< - ISchemaMarkupFieldProps< - Components, - ComponentPath, - ComponentPath - > - > - SchemaObjectField: DefineComponent< - ISchemaMarkupFieldProps< - Components, - ComponentPath, - ComponentPath - > - > - SchemaArrayField: DefineComponent< - ISchemaMarkupFieldProps< - Components, - ComponentPath, - ComponentPath - > - > - SchemaBooleanField: DefineComponent< - ISchemaMarkupFieldProps< - Components, - ComponentPath, - ComponentPath - > - > - SchemaDateField: DefineComponent< - ISchemaMarkupFieldProps< - Components, - ComponentPath, - ComponentPath - > - > - SchemaDateTimeField: DefineComponent< - ISchemaMarkupFieldProps< - Components, - ComponentPath, - ComponentPath - > - > - SchemaVoidField: DefineComponent< - ISchemaMarkupFieldProps< - Components, - ComponentPath, - ComponentPath - > - > - SchemaNumberField: DefineComponent< - ISchemaMarkupFieldProps< - Components, - ComponentPath, - ComponentPath - > - > +type SchemaFieldComponents = { + SchemaField: DefineComponent + SchemaMarkupField: DefineComponent + SchemaStringField: DefineComponent + SchemaObjectField: DefineComponent + SchemaArrayField: DefineComponent + SchemaBooleanField: DefineComponent + SchemaDateField: DefineComponent + SchemaDateTimeField: DefineComponent + SchemaVoidField: DefineComponent + SchemaNumberField: DefineComponent } export function createSchemaField< Components extends SchemaComponents = SchemaComponents ->( - options: ISchemaFieldFactoryOptions -): SchemaFieldComponents { +>(options: ISchemaFieldFactoryOptions): SchemaFieldComponents { const SchemaField = defineComponent< ISchemaFieldProps >({ @@ -192,6 +138,7 @@ export function createSchemaField< props: { schema: {}, scope: {}, + components: {}, basePath: {}, title: {}, description: {}, @@ -248,10 +195,21 @@ export function createSchemaField< }) ) - const scopeRef = computed(() => props.scope) + const scopeRef = computed(() => ({ + ...options.scope, + ...props.scope, + })) + + const optionsRef = computed(() => ({ + ...options, + components: { + ...options.components, + ...props.components, + }, + })) provide(SchemaMarkupSymbol, schemaRef) - provide(SchemaOptionsSymbol, options) + provide(SchemaOptionsSymbol, optionsRef) provide(SchemaExpressionScopeSymbol, scopeRef) return () => { diff --git a/packages/vue/src/components/VoidField.ts b/packages/vue/src/components/VoidField.ts index 22b5e5edb86..a035fb38131 100644 --- a/packages/vue/src/components/VoidField.ts +++ b/packages/vue/src/components/VoidField.ts @@ -1,13 +1,14 @@ -import { provide, defineComponent, DefineComponent } from 'vue-demi' +import { provide, defineComponent } from 'vue-demi' import { useField, useForm } from '../hooks' import { useAttach } from '../hooks/useAttach' -import { VueComponent, IVoidFieldProps } from '../types' import ReactiveField from './ReactiveField' import { FieldSymbol } from '../shared/context' import h from '../shared/h' import { getRawComponent } from '../utils/getRawComponent' -export default defineComponent>({ +import type { IVoidFieldProps, DefineComponent } from '../types' + +export default defineComponent({ name: 'VoidField', /* eslint-disable vue/require-prop-types */ /* eslint-disable vue/require-default-prop */ @@ -46,8 +47,7 @@ export default defineComponent>({ }, reactions: [Array, Function], }, - setup(props: IVoidFieldProps, { slots }) { - // const { track } = useObserver() + setup(props: IVoidFieldProps, { slots }) { const formRef = useForm() const parentRef = useField() const basePath = @@ -84,4 +84,4 @@ export default defineComponent>({ return h(ReactiveField, componentData, children) } }, -}) as unknown as DefineComponent> +}) as unknown as DefineComponent diff --git a/packages/vue/src/global.d.ts b/packages/vue/src/global.d.ts new file mode 100644 index 00000000000..af5b00ea1b9 --- /dev/null +++ b/packages/vue/src/global.d.ts @@ -0,0 +1,8 @@ +/// +/// +import * as Types from './types' +declare global { + namespace Formily.Vue { + export { Types } + } +} diff --git a/packages/vue/src/index.ts b/packages/vue/src/index.ts index f3a3b9f1db2..415063e5940 100644 --- a/packages/vue/src/index.ts +++ b/packages/vue/src/index.ts @@ -1,5 +1,5 @@ export * from '@formily/json-schema' -export * from '@formily/reactive-vue' export * from './components' export * from './shared' export * from './hooks' +export * as Vue2Components from './vue2-components' diff --git a/packages/vue/src/shared/connect.ts b/packages/vue/src/shared/connect.ts index ba23a6efa7e..02ed10dec29 100644 --- a/packages/vue/src/shared/connect.ts +++ b/packages/vue/src/shared/connect.ts @@ -1,17 +1,20 @@ /* eslint-disable vue/one-component-per-file */ import { Vue2Component } from '../types/vue2' -import { isVue2, markRaw, defineComponent, DefineComponent } from 'vue-demi' +import { isVue2, markRaw, defineComponent } from 'vue-demi' import { isFn, isStr, FormPath, each } from '@formily/shared' import { isVoidField } from '@formily/core' import { observer } from '@formily/reactive-vue' -import { + +import { useField } from '../hooks/useField' +import h from './h' + +import type { VueComponent, IComponentMapper, IStateMapper, VueComponentProps, + DefineComponent, } from '../types' -import { useField } from '../hooks/useField' -import h from './h' export function mapProps( ...args: IStateMapper>[] diff --git a/packages/vue/src/shared/context.ts b/packages/vue/src/shared/context.ts index 171e5666842..a6ba5f0637f 100644 --- a/packages/vue/src/shared/context.ts +++ b/packages/vue/src/shared/context.ts @@ -11,5 +11,6 @@ export const SchemaSymbol: InjectionKey> = Symbol('schema') export const SchemaExpressionScopeSymbol: InjectionKey< Ref> > = Symbol('schemaExpression') -export const SchemaOptionsSymbol: InjectionKey = - Symbol('schemaOptions') +export const SchemaOptionsSymbol: InjectionKey< + Ref +> = Symbol('schemaOptions') diff --git a/packages/vue/src/types/index.ts b/packages/vue/src/types/index.ts index 4930a4df61e..6af2143089f 100644 --- a/packages/vue/src/types/index.ts +++ b/packages/vue/src/types/index.ts @@ -2,9 +2,13 @@ import type { Vue2Component } from './vue2' import type { Vue3Component } from './vue3' import type { FormPathPattern } from '@formily/shared' import type { ISchema, Schema, SchemaKey } from '@formily/json-schema' +import type { DefineComponent as DefineVue3Component } from '@type-helper/vue3' + +export type DefineComponent> = + DefineVue3Component export type VueComponent> = - | Vue2Component + | Vue2Component | Vue3Component | Props export type VueComponentOptionsWithProps = { @@ -17,11 +21,22 @@ export interface IProviderProps { form: Formily.Core.Models.Form } -export type IFieldProps = - Formily.Core.Types.IFieldFactoryProps +export type IFieldProps< + D extends VueComponent = VueComponent, + C extends VueComponent = VueComponent +> = Formily.Core.Types.IFieldFactoryProps + +export type IVoidFieldProps< + D extends VueComponent = VueComponent, + C extends VueComponent = VueComponent +> = Formily.Core.Types.IVoidFieldFactoryProps -export type IVoidFieldProps = - Formily.Core.Types.IVoidFieldFactoryProps +export type IArrayFieldProps = IFieldProps +export type IObjectFieldProps = IFieldProps + +export interface IReactiveFieldProps { + field: Formily.Core.Types.GeneralField +} export interface IComponentMapper { (target: T): VueComponent @@ -43,14 +58,17 @@ export interface ISchemaFieldFactoryOptions< } export interface ISchemaFieldProps< - Decorator extends VueComponent = any, - Component extends VueComponent = any, + Decorator extends VueComponent = VueComponent, + Component extends VueComponent = VueComponent, InnerField = Formily.Core.Models.ObjectField > extends Omit< Formily.Core.Types.IFieldFactoryProps, 'name' > { schema?: ISchema + components?: { + [key: string]: VueComponent + } scope?: any name?: SchemaKey } @@ -68,6 +86,7 @@ export interface ISchemaMapper { export interface ISchemaFilter { (schema: Schema, name: SchemaKey): boolean } + export interface IRecursionFieldProps { schema: Schema name?: SchemaKey @@ -93,9 +112,9 @@ export type ComponentPropsByPathValue< > = P extends keyof T ? VueComponentProps : never export type ISchemaMarkupFieldProps< - Components extends SchemaComponents, - Decorator extends ComponentPath, - Component extends ComponentPath + Components extends SchemaComponents = SchemaComponents, + Decorator extends ComponentPath = ComponentPath, + Component extends ComponentPath = ComponentPath > = ISchema< Decorator, Component, @@ -109,9 +128,9 @@ export type ISchemaMarkupFieldProps< > export type ISchemaTypeFieldProps< - Components extends SchemaComponents, - Decorator extends ComponentPath, - Component extends ComponentPath + Components extends SchemaComponents = SchemaComponents, + Decorator extends ComponentPath = ComponentPath, + Component extends ComponentPath = ComponentPath > = Omit, 'type'> export interface ISchemaTransformerOptions extends ISchemaFieldFactoryOptions { diff --git a/packages/vue/src/types/vue-demi.d.ts b/packages/vue/src/types/vue-demi.d.ts deleted file mode 100644 index 27dcda60b08..00000000000 --- a/packages/vue/src/types/vue-demi.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -import type { DefineComponent as Vue3DefineComponent } from '@type-helper/vue3' - -declare module 'vue-demi' { - type DefineComponent> = Vue3DefineComponent -} diff --git a/packages/vue/src/types/vue2.ts b/packages/vue/src/types/vue2.ts index 7ce868cad4f..87c46dbb43b 100644 --- a/packages/vue/src/types/vue2.ts +++ b/packages/vue/src/types/vue2.ts @@ -1,3 +1,8 @@ -import type { Component, ComponentOptions } from 'vue' +import type { Component, ComponentOptions } from '@type-helper/vue2' export type Vue2ComponentOptions = ComponentOptions -export type Vue2Component = Component +export type Vue2Component> = Component< + any, + any, + any, + Props +> diff --git a/packages/vue/src/vue2-components.ts b/packages/vue/src/vue2-components.ts new file mode 100644 index 00000000000..114b988b93d --- /dev/null +++ b/packages/vue/src/vue2-components.ts @@ -0,0 +1,74 @@ +import * as components from './components' + +import type Vue from 'vue' +import type { VueConstructor } from 'vue' +import type { + IVoidFieldProps, + IArrayFieldProps, + IObjectFieldProps, + IFieldProps, + IRecursionFieldProps, + IProviderProps, + ISchemaMarkupFieldProps, + ISchemaFieldProps, + ISchemaFieldFactoryOptions, + ISchemaTypeFieldProps, + SchemaComponents, +} from './types' + +const { + Field: _Field, + ArrayField: _ArrayField, + FormConsumer: _FormConsumer, + FormProvider: _FormProvider, + ObjectField: _ObjectField, + RecursionField: _RecursionField, + VoidField: _VoidField, + createSchemaField: _createSchemaField, +} = components + +type DefineComponent = Vue & VueConstructor & Props + +type SchemaFieldComponents = { + SchemaField: DefineComponent> + SchemaMarkupField: DefineComponent + SchemaStringField: DefineComponent + SchemaObjectField: DefineComponent + SchemaArrayField: DefineComponent + SchemaBooleanField: DefineComponent + SchemaDateField: DefineComponent + SchemaDateTimeField: DefineComponent + SchemaVoidField: DefineComponent + SchemaNumberField: DefineComponent +} + +type CreateSchemaField = + (options: ISchemaFieldFactoryOptions) => SchemaFieldComponents + +const Field = _Field as unknown as DefineComponent> +const ArrayField = _ArrayField as unknown as DefineComponent< + Omit +> +const ObjectField = _ObjectField as unknown as DefineComponent< + Omit +> +const VoidField = _VoidField as unknown as DefineComponent< + Omit +> +const RecursionField = _RecursionField as unknown as DefineComponent< + Omit +> +const FormConsumer = _FormConsumer as unknown as Vue +const FormProvider = _FormProvider as unknown as DefineComponent +const createSchemaField = _createSchemaField as unknown as CreateSchemaField + +export { + Field, + ArrayField, + ObjectField, + VoidField, + RecursionField, + FormConsumer, + FormProvider, + createSchemaField, +} diff --git a/yarn.lock b/yarn.lock index 2ab1dd1b7a7..e0fca3d1b1b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1162,12 +1162,12 @@ resolved "https://registry.yarnpkg.com/@ctrl/tinycolor/-/tinycolor-3.4.0.tgz#c3c5ae543c897caa9c2a68630bed355be5f9990f" integrity sha512-JZButFdZ1+/xAfpguQHoabIXkcqRRKpMrWKBkpEZZyxfY9C1DpADFB8PEqGSTeFr135SaTRfKqGKx5xSCLI7ZQ== -"@designable/core@0.3.14", "@designable/core@^0.3.14": - version "0.3.14" - resolved "https://registry.yarnpkg.com/@designable/core/-/core-0.3.14.tgz#c842c74a8a303a00f25f03bf81f1264c4e17a3f7" - integrity sha512-i9vTRhE8p4HeweRH/AZZ61eT+96i/cV8bEffjFnLVOpcly9AsGP6HJ5k50Dl5ViBQZ+Q25XroqmjGzlr0cH1gw== +"@designable/core@0.3.15", "@designable/core@^0.3.15": + version "0.3.15" + resolved "https://registry.yarnpkg.com/@designable/core/-/core-0.3.15.tgz#9b5e7ca0c9bc9f12292133f758a92b42e2a0fedd" + integrity sha512-62Gsk4FgsDmvOI0dKhr67X0XgFBHe9z5+zDP/pInEAjjDJvkCuxGpBMYx03IQCLYYyER5CbCOOjQwUdKwwOngQ== dependencies: - "@designable/shared" "0.3.14" + "@designable/shared" "0.3.15" "@formily/json-schema" "^2.0.0-beta.68" "@formily/path" "^2.0.0-beta.68" "@formily/reactive" "^2.0.0-beta.68" @@ -1183,24 +1183,24 @@ "@formily/reactive" "^2.0.0-beta.50" "@juggle/resize-observer" "^3.3.1" -"@designable/formily@^0.3.14": - version "0.3.14" - resolved "https://registry.yarnpkg.com/@designable/formily/-/formily-0.3.14.tgz#b6e019e96186e6391407de9c093b2798b16002ec" - integrity sha512-dz6lTZpeyerQPDiyEQF92JrBTKm2EHcgyKVk0Toi4nJ8VkAE9Jpi6bIX6zDP0WDlBW8X5aaN+XWsbV5ZMfrlaw== +"@designable/formily@^0.3.15": + version "0.3.15" + resolved "https://registry.yarnpkg.com/@designable/formily/-/formily-0.3.15.tgz#ade47e54a40afc2c2ab91c681547f3bfe2db745b" + integrity sha512-EWHzCPw70r1VK4pRUrG8j2xWNMqwXAHRhMUJM06v7OBJGpbDjGNucFsNWI9JSeBTFMsHRx9f2HzQwqnS00jBvA== dependencies: - "@designable/core" "0.3.14" - "@designable/shared" "0.3.14" + "@designable/core" "0.3.15" + "@designable/shared" "0.3.15" "@formily/core" "^2.0.0-beta.68" "@formily/json-schema" "^2.0.0-beta.68" -"@designable/react-settings-form@^0.3.14": - version "0.3.14" - resolved "https://registry.yarnpkg.com/@designable/react-settings-form/-/react-settings-form-0.3.14.tgz#bf73b23717e8a65f982087b7effc1fa8f2005415" - integrity sha512-kjudvAZiROW5Gvpwn6KZ9LlvpmncMlIY+WqQvzHHySKt9+we6vCdmSY8Foa9IGPaZWdCd5y9kkYX9wj4b8zbWA== +"@designable/react-settings-form@^0.3.15": + version "0.3.15" + resolved "https://registry.yarnpkg.com/@designable/react-settings-form/-/react-settings-form-0.3.15.tgz#c2fcf11aba59728d931f44bbcaccda6b6cf0503b" + integrity sha512-6CHfwldyXtZzyp66IptCLLqiQtkbkAl3C178s3o8pac26Unzywd63saTsiGEY+6ZVVLunPG0KOqt/JWR3pOBNg== dependencies: - "@designable/core" "0.3.14" - "@designable/react" "0.3.14" - "@designable/shared" "0.3.14" + "@designable/core" "0.3.15" + "@designable/react" "0.3.15" + "@designable/shared" "0.3.15" "@formily/antd" "^2.0.0-beta.68" "@formily/core" "^2.0.0-beta.68" "@formily/react" "^2.0.0-beta.68" @@ -1208,13 +1208,13 @@ "@formily/reactive-react" "^2.0.0-beta.68" react-color "^2.19.3" -"@designable/react@0.3.14", "@designable/react@^0.3.14": - version "0.3.14" - resolved "https://registry.yarnpkg.com/@designable/react/-/react-0.3.14.tgz#5bff5c937859dd57161bf4f5596253814c214a4b" - integrity sha512-2fU8DqKrxFoZ12/FMpM0SLcttjDUEaL0w4ZcIjHMFnNwiPqOL6x11dlyUAXh9IM6vtTyrtAKllPw1us7G0i7cQ== +"@designable/react@0.3.15", "@designable/react@^0.3.15": + version "0.3.15" + resolved "https://registry.yarnpkg.com/@designable/react/-/react-0.3.15.tgz#d3ce06fc06ca916f7941d837ef74d5c601337309" + integrity sha512-XICF6H3SYHugx/85gYMYd4x3meZ8OVbOPjyqt4bq4n3LrjOOA2PqSPR1CAQwesSHt4md1B0aa+l+mfZecqDgvQ== dependencies: - "@designable/core" "0.3.14" - "@designable/shared" "0.3.14" + "@designable/core" "0.3.15" + "@designable/shared" "0.3.15" "@formily/reactive" "^2.0.0-beta.68" "@formily/reactive-react" "^2.0.0-beta.68" "@juggle/resize-observer" "^3.3.1" @@ -1226,10 +1226,10 @@ dependencies: requestidlecallback "^0.3.0" -"@designable/shared@0.3.14": - version "0.3.14" - resolved "https://registry.yarnpkg.com/@designable/shared/-/shared-0.3.14.tgz#7086631ce208df0db7c4cf81217b60c14fd9af86" - integrity sha512-gsdVLwsTBO/PwLk9sZX9MofNfR1rYQ0Pn40fSdb7gG/DRd3EbCdyWl7d4VWzNgAGa6/iTXV8dITMtgFZeGkCxg== +"@designable/shared@0.3.15": + version "0.3.15" + resolved "https://registry.yarnpkg.com/@designable/shared/-/shared-0.3.15.tgz#94668262e115182014cb1b6dd4a3c2a67d4b296a" + integrity sha512-r9yWDyR45Uc/BFj5YKZKhg6Jh5DvgwVaCvkTxk9z/U22mJUKqA2imfZS32WDPkPdAuZ/ZpcSk4dE79CW2NVv7w== dependencies: requestidlecallback "^0.3.0" @@ -2559,6 +2559,11 @@ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== +"@type-helper/vue2@npm:vue@2": + version "2.6.14" + resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.14.tgz#e51aa5250250d569a3fbad3a8a5a687d6036e235" + integrity sha512-x2284lgYvjOMj3Za7kqzRcUSxBboHqtgRE2zlos1qWaOye5yUmHn42LB1250NJBLRwEcdrB0JRwyPTEPhfQjiQ== + "@type-helper/vue3@npm:vue@3": version "3.0.11" resolved "https://registry.yarnpkg.com/vue/-/vue-3.0.11.tgz#c82f9594cbf4dcc869241d4c8dd3e08d9a8f4b5f"