diff --git a/packages/element/src/__builtins__/shared/get-component-by-tag.ts b/packages/element/src/__builtins__/shared/get-component-by-tag.ts deleted file mode 100644 index cb3b2f50de4..00000000000 --- a/packages/element/src/__builtins__/shared/get-component-by-tag.ts +++ /dev/null @@ -1,55 +0,0 @@ -import type { Component } from 'vue' -import { merge } from '@formily/shared' -import { h } from '@formily/vue' -import { isVue2 } from 'vue-demi' - -type ListenersTransformRules = Record - -export const getComponentByTag = >( - tag: any, - transformRules?: ListenersTransformRules, - defaultProps?: Partial -): Component | any => { - if (isVue2) { - return { - functional: true, - render(h, context) { - const data = context.data - if (transformRules) { - const listeners = transformRules - Object.keys(listeners).forEach((extract) => { - if (data.on !== undefined) { - data.on[listeners[extract]] = context.listeners[extract] - } - }) - } - if (defaultProps) { - data.props = merge(defaultProps, data.props) - } - return h(tag, data, context.children) - }, - } - } else { - return { - setup(props, { attrs, slots }) { - return () => { - let data = { - ...attrs, - } - if (transformRules) { - const listeners = transformRules - Object.keys(listeners).forEach((extract) => { - const event = listeners[extract] - data[`on${event[0].toUpperCase()}${event.slice(1)}`] = - attrs[`on${extract[0].toUpperCase()}${extract.slice(1)}`] - }) - } - if (defaultProps) { - data = merge(defaultProps, data) - } - return h(tag, attrs, slots) - } - }, - } - } -} diff --git a/packages/element/src/__builtins__/shared/index.ts b/packages/element/src/__builtins__/shared/index.ts index d7b9d7e4ddd..25aee28cd35 100644 --- a/packages/element/src/__builtins__/shared/index.ts +++ b/packages/element/src/__builtins__/shared/index.ts @@ -1,4 +1,4 @@ -export * from './get-component-by-tag' +export * from './transform-component' export * from './resolve-component' export * from './create-context' export * from './utils' diff --git a/packages/element/src/__builtins__/shared/resolve-component.ts b/packages/element/src/__builtins__/shared/resolve-component.ts index 9eec7b3b0b4..b9ff6615582 100644 --- a/packages/element/src/__builtins__/shared/resolve-component.ts +++ b/packages/element/src/__builtins__/shared/resolve-component.ts @@ -1,26 +1,21 @@ +import { Component } from 'vue' import { h, toRaw } from '@vue/composition-api' -import { Component, VNode } from 'vue' +import { SlotTypes } from '.' +import { isVnode } from './utils' export const resolveComponent = ( - child?: - | Component - | string - | number - | boolean - | ((...args: any[]) => VNode[] | VNode), + child?: SlotTypes, props?: Record ) => { if (child) { - if ( - typeof child === 'string' || - typeof child === 'number' || - typeof child === 'boolean' - ) { + if (typeof child === 'string' || typeof child === 'number') { return child } else if (typeof child === 'function') { return (child as Function)(props) + } else if (isVnode(child)) { + return child } else { - return h(toRaw(child), { props }) + return h(toRaw(child as Component), { props }) } } diff --git a/packages/element/src/__builtins__/shared/transform-component.ts b/packages/element/src/__builtins__/shared/transform-component.ts new file mode 100644 index 00000000000..4c8af267117 --- /dev/null +++ b/packages/element/src/__builtins__/shared/transform-component.ts @@ -0,0 +1,65 @@ +import type { Component } from 'vue' +import { merge } from '@formily/shared' +import { h } from '@formily/vue' +import { isVue2, h as hInVue2, defineComponent } from 'vue-demi' + +type ListenersTransformRules = Record + +export const transformComponent = >( + tag: any, + transformRules?: ListenersTransformRules, + defaultProps?: Partial +): Component | any => { + if (isVue2) { + return defineComponent({ + setup(props, { attrs, slots, listeners }) { + return () => { + const data = { + attrs: { + ...attrs, + }, + on: { + ...listeners, + }, + } + + if (transformRules) { + const transformListeners = transformRules + Object.keys(transformListeners).forEach((extract) => { + if (data.on !== undefined) { + data.on[transformListeners[extract]] = listeners[extract] + } + }) + } + if (defaultProps) { + data.attrs = merge(defaultProps, data.attrs) + } + + return h(tag, data, slots) + } + }, + }) + } else { + return defineComponent({ + setup(props, { attrs, slots }) { + return () => { + let data = { + ...attrs, + } + if (transformRules) { + const listeners = transformRules + Object.keys(listeners).forEach((extract) => { + const event = listeners[extract] + data[`on${event[0].toUpperCase()}${event.slice(1)}`] = + attrs[`on${extract[0].toUpperCase()}${extract.slice(1)}`] + }) + } + if (defaultProps) { + data = merge(defaultProps, data) + } + return h(tag, data, slots) + } + }, + }) + } +} diff --git a/packages/element/src/__builtins__/shared/utils.ts b/packages/element/src/__builtins__/shared/utils.ts index 3d2fc2d205a..e98703446ca 100644 --- a/packages/element/src/__builtins__/shared/utils.ts +++ b/packages/element/src/__builtins__/shared/utils.ts @@ -9,6 +9,16 @@ export function isValidElement(element) { ) // remove text node } +export function isVnode(element: any): boolean { + return ( + element && + typeof element === 'object' && + 'componentOptions' in element && + 'context' in element && + element.tag !== undefined + ) +} + export function isVueOptions(options) { return ( options && diff --git a/packages/element/src/checkbox/index.ts b/packages/element/src/checkbox/index.ts index da22fa269de..d0144701f7e 100644 --- a/packages/element/src/checkbox/index.ts +++ b/packages/element/src/checkbox/index.ts @@ -2,7 +2,7 @@ import { connect, mapProps, h, mapReadPretty } from '@formily/vue' import { defineComponent, PropType } from '@vue/composition-api' import { composeExport, - getComponentByTag, + transformComponent, resolveComponent, SlotTypes, } from '../__builtins__/shared' @@ -83,7 +83,7 @@ export type CheckboxGroupProps = ElCheckboxGroupProps & { optionType: 'default' | 'button' } -const TransformElCheckboxGroup = getComponentByTag(ElCheckboxGroup, { +const TransformElCheckboxGroup = transformComponent(ElCheckboxGroup, { change: 'input', }) diff --git a/packages/element/src/date-picker/index.ts b/packages/element/src/date-picker/index.ts index cd5a705e52c..eaab986b073 100644 --- a/packages/element/src/date-picker/index.ts +++ b/packages/element/src/date-picker/index.ts @@ -1,4 +1,4 @@ -import { getComponentByTag } from '../__builtins__/shared' +import { transformComponent } from '../__builtins__/shared' import { connect, mapProps, mapReadPretty } from '@formily/vue' import type { DatePicker as ElDatePickerProps } from 'element-ui' @@ -7,9 +7,12 @@ import { PreviewText } from '../preview-text' export type DatePickerProps = ElDatePickerProps -const TransformElDatePicker = getComponentByTag(ElDatePicker, { - change: 'input', -}) +const TransformElDatePicker = transformComponent( + ElDatePicker, + { + change: 'input', + } +) const getDefaultFormat = (props, formatType = 'format') => { const type = props.type diff --git a/packages/element/src/input-number/index.ts b/packages/element/src/input-number/index.ts index 5683acf3154..fb4c325e2bd 100644 --- a/packages/element/src/input-number/index.ts +++ b/packages/element/src/input-number/index.ts @@ -1,4 +1,4 @@ -import { getComponentByTag } from '../__builtins__/shared' +import { transformComponent } from '../__builtins__/shared' import { connect, mapProps, mapReadPretty } from '@formily/vue' import type { InputNumber as _ElInputNumberProps } from 'element-ui' @@ -7,7 +7,7 @@ import { PreviewText } from '../preview-text' export type InputNumberProps = _ElInputNumberProps -const TransformElInputNumber = getComponentByTag( +const TransformElInputNumber = transformComponent( ElInputNumber, { change: 'input', diff --git a/packages/element/src/input/index.ts b/packages/element/src/input/index.ts index 2407aa4422c..2c5c07b1767 100644 --- a/packages/element/src/input/index.ts +++ b/packages/element/src/input/index.ts @@ -1,4 +1,4 @@ -import { composeExport, getComponentByTag } from '../__builtins__/shared' +import { composeExport, transformComponent } from '../__builtins__/shared' import { connect, mapProps, mapReadPretty } from '@formily/vue' import { PreviewText } from '../preview-text' import type { Input as ElInputProps } from 'element-ui' @@ -6,7 +6,7 @@ import { Input as ElInput } from 'element-ui' export type InputProps = ElInputProps -const TransformElInput = getComponentByTag(ElInput, { +const TransformElInput = transformComponent(ElInput, { change: 'input', }) diff --git a/packages/element/src/radio/index.ts b/packages/element/src/radio/index.ts index fcf498ca2e2..384ffbfb639 100644 --- a/packages/element/src/radio/index.ts +++ b/packages/element/src/radio/index.ts @@ -2,7 +2,7 @@ import { connect, mapProps, h, mapReadPretty } from '@formily/vue' import { defineComponent, PropType } from '@vue/composition-api' import { composeExport, - getComponentByTag, + transformComponent, resolveComponent, SlotTypes, } from '../__builtins__/shared' @@ -31,7 +31,7 @@ export type RadioGroupProps = ElRadioGroupProps & { export type RadioProps = ElRadioProps -const TransformElRadioGroup = getComponentByTag(ElRadioGroup, { +const TransformElRadioGroup = transformComponent(ElRadioGroup, { change: 'input', }) diff --git a/packages/element/src/select/index.ts b/packages/element/src/select/index.ts index 53d08efbb53..b2d4f778bd3 100644 --- a/packages/element/src/select/index.ts +++ b/packages/element/src/select/index.ts @@ -7,9 +7,14 @@ import type { Option as ElOptionProps, } from 'element-ui' import { Select as ElSelect, Option as ElOption } from 'element-ui' +import { resolveComponent, SlotTypes } from '../__builtins__' export type SelectProps = ElSelectProps & { - options?: Array + options?: Array< + Omit & { + label: SlotTypes + } + > } const SelectOption = defineComponent({ @@ -26,11 +31,30 @@ const SelectOption = defineComponent({ if (typeof option === 'string') { return h( ElOption, - { props: { label: option, value: option } }, - {} + { props: { value: option } }, + { + default: () => [ + resolveComponent(slots?.option ?? option, { option }), + ], + } ) } else { - return h(ElOption, { props: option }, {}) + return h( + ElOption, + { + props: { + ...option, + label: '', + }, + }, + { + default: () => [ + resolveComponent(slots?.option ?? option.label, { + option, + }), + ], + } + ) } }), } diff --git a/packages/element/src/time-picker/index.ts b/packages/element/src/time-picker/index.ts index 4a74f655e17..cc40bdc0a26 100644 --- a/packages/element/src/time-picker/index.ts +++ b/packages/element/src/time-picker/index.ts @@ -1,4 +1,4 @@ -import { getComponentByTag } from '../__builtins__/shared' +import { transformComponent } from '../__builtins__/shared' import { connect, mapProps, mapReadPretty } from '@formily/vue' import { PreviewText } from '../preview-text' import type { TimePicker as ElTimePickerProps } from 'element-ui' @@ -6,9 +6,12 @@ import { TimePicker as ElTimePicker } from 'element-ui' export type TimePickerProps = ElTimePickerProps -const TransformElTimePicker = getComponentByTag(ElTimePicker, { - change: 'input', -}) +const TransformElTimePicker = transformComponent( + ElTimePicker, + { + change: 'input', + } +) export const TimePicker = connect( TransformElTimePicker, diff --git a/yarn.lock b/yarn.lock index ad143ff149f..5b1d40af2b4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -19619,6 +19619,11 @@ vue-frag@^1.1.4: resolved "https://registry.yarnpkg.com/vue-frag/-/vue-frag-1.1.5.tgz#1158f92361dff2c312e00b8ca85334c329e566e7" integrity sha512-g+PP9pjW1gnrmPjy2Sa5jK/bfxYHK8Hgt1sVs/Y/7KT+srGJUwtGNwXNTUvWv/zJ2yGcvJVEH98eIrICyZX9Ng== +vue-hoc@^0.4.7: + version "0.4.7" + resolved "https://registry.nlark.com/vue-hoc/download/vue-hoc-0.4.7.tgz#4d3322ba89b8b0e42b19045ef536c21d948a4fac" + integrity sha1-TTMiuom4sOQrGQRe9TbCHZSKT6w= + vue-hot-reload-api@^2.3.0: version "2.3.4" resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz#532955cc1eb208a3d990b3a9f9a70574657e08f2"