Skip to content

Props hoc #3084

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 22 additions & 9 deletions examples/sites/demos/pc/app/button/basic-usage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
<tiny-layout>
<p>基本按钮</p>
<tiny-row>
<tiny-button type="primary"> 主要按钮 </tiny-button>
<tiny-button> 次要按钮 </tiny-button>
<tiny-button type="success"> 成功按钮 </tiny-button>
<tiny-button type="info"> 信息按钮 </tiny-button>
<tiny-button type="warning"> 警告按钮 </tiny-button>
<tiny-button type="danger"> 危险按钮 </tiny-button>
<config-provider :design="design">
<tiny-button type="primary"> 主要按钮 </tiny-button>
<tiny-button> 次要按钮 </tiny-button>
<tiny-button type="success"> 成功按钮 </tiny-button>
<tiny-button type="info"> 信息按钮 </tiny-button>
<tiny-button type="warning"> 警告按钮 </tiny-button>
<tiny-button type="danger"> 危险按钮 </tiny-button>
</config-provider>
</tiny-row>
<p>朴素按钮</p>
<tiny-row>
Expand Down Expand Up @@ -40,14 +42,15 @@
</template>

<script>
import { TinyButton, TinyLayout, TinyRow } from '@opentiny/vue'
import { TinyButton, TinyLayout, TinyRow, ConfigProvider } from '@opentiny/vue'
import { IconDeleteL, IconYes, IconEditor, IconMail, IconStarO, IconSearch } from '@opentiny/vue-icon'

export default {
components: {
TinyButton,
TinyLayout,
TinyRow
TinyRow,
ConfigProvider
},
data() {
return {
Expand All @@ -56,7 +59,17 @@ export default {
IconEditor: IconEditor(),
IconMail: IconMail(),
IconStarO: IconStarO(),
IconSearch: IconSearch()
IconSearch: IconSearch(),
design: {
components: {
Button: {
props: {
resetTime: 0,
size: 'mini'
}
}
}
}
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions examples/vue3/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"build": "vite build",
"preview": "vite preview",
"test:e2e": "playwright test",
"test:unit": "vitest",
"test:unit": "vitest --ui",
"install:browser": "playwright install",
"codegen": "playwright codegen localhost:3101"
},
Expand All @@ -37,7 +37,7 @@
"@unocss/transformer-directives": "^0.48.0",
"@vitejs/plugin-vue": "4.1.0",
"@vitejs/plugin-vue-jsx": "^3.0.0",
"@vitest/ui": "^0.31.0",
"@vitest/ui": "^2.1.9",
"@vue/runtime-core": "3.2.31",
"@vue/test-utils": "^2.2.7",
"jsdom": "^21.0.0",
Expand All @@ -58,4 +58,4 @@
"vue": "^3.3.9",
"vue-i18n": "^9.1.7"
}
}
}
73 changes: 73 additions & 0 deletions packages/vue-common/src/adapter/vue2.7/hoc.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import Vue from 'vue'
import { design, $prefix } from '../../index'

export default function DesignConfigPropsHOC(BaseComponent) {
return Vue.extend({
name: 'DesignConfigHOC',
functional: false,
inject: {
globalDesignConfig: {
from: design.configKey,
default: () => ({})
}
},
created() {
// 暴露实例方法
this.$watch(
() => this.innerRef,
(val) => {
if (val) {
Object.keys(val).forEach((key) => {
if (!(key in this)) {
this[key] = val[key]
}
})
}
}
)
},
data() {
return {
innerRef: null
}
},
computed: {
componentName() {
const rawName = BaseComponent.name || BaseComponent.options.name
return rawName.replace($prefix, '')
},
mergedProps() {
// 处理响应式配置
const globalConfig = this.unwrap(this.globalDesignConfig)
const componentConfig = globalConfig.components?.[this.componentName] || {}
return {
...componentConfig.props,
...this.$attrs
}
}
},
methods: {
// 解包可能存在的响应式对象
unwrap(config) {
if (config && typeof config === 'object' && 'value' in config) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure that the unwrap method correctly handles all possible types of reactive objects to avoid unexpected behavior.

return config.value
}
return config || {}
}
},
render(h) {
return h(
BaseComponent,
{
ref: (el) => {
this.innerRef = el
},
attrs: this.mergedProps,
on: this.$listeners,
scopedSlots: this.$scopedSlots
},
this.$slots.default
)
}
})
}
10 changes: 10 additions & 0 deletions packages/vue-common/src/adapter/vue2.7/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import Vue from 'vue'
import * as hooks from 'vue'
import { emitter, bindFilter, getElementCssClass, getElementStatusClass } from '../utils'
import teleport from '../teleport'
import DesignConfigPropsHOC from './hoc'

const Teleport = teleport(hooks)

Expand Down Expand Up @@ -341,6 +342,15 @@ export const createComponentFn = (design) => {

export const defineComponent = hooks.defineComponent

// 简便用法
// export default WithDesignConfigPropsDefineComponent({
// name: 'xxx',
// setup() {}
// })
export function WithDesignConfigPropsDefineComponent(BaseComponent: any) {
return DesignConfigPropsHOC(hooks.defineComponent(BaseComponent))
}

export default hooks

export const isVue2 = true
Expand Down
73 changes: 73 additions & 0 deletions packages/vue-common/src/adapter/vue2/hoc.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import Vue from 'vue'
import { design, $prefix } from '../../index'

export default function DesignConfigPropsHOC(BaseComponent) {
return Vue.extend({
name: 'DesignConfigHOC',
functional: false,
inject: {
globalDesignConfig: {
from: design.configKey,
default: () => ({})
}
},
created() {
// 暴露实例方法
this.$watch(
() => this.innerRef,
(val) => {
if (val) {
Object.keys(val).forEach((key) => {
if (!(key in this)) {
this[key] = val[key]
}
})
}
}
)
},
data() {
return {
innerRef: null
}
},
computed: {
componentName() {
const rawName = BaseComponent.name || BaseComponent.options.name
return rawName.replace($prefix, '')
},
mergedProps() {
// 处理响应式配置
const globalConfig = this.unwrap(this.globalDesignConfig)
const componentConfig = globalConfig.components?.[this.componentName] || {}
return {
...componentConfig.props,
...this.$attrs
}
}
},
methods: {
// 解包可能存在的响应式对象
unwrap(config) {
if (config && typeof config === 'object' && 'value' in config) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure that the unwrap method correctly handles all possible types of reactive objects to avoid unexpected behavior.

return config.value
}
return config || {}
}
},
render(h) {
return h(
BaseComponent,
{
ref: (el) => {
this.innerRef = el
},
attrs: this.mergedProps,
on: this.$listeners,
scopedSlots: this.$scopedSlots
},
this.$slots.default
)
}
})
}
10 changes: 10 additions & 0 deletions packages/vue-common/src/adapter/vue2/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as compositionHooks from '@vue/composition-api'
import * as vueHooks from 'vue'
import { bindFilter, emitter, getElementCssClass, getElementStatusClass } from '../utils'
import teleport from '../teleport'
import DesignConfigPropsHOC from './hoc'

// vue2.7有version字段
const isVueHooks = Boolean(Vue.version?.includes('2.7'))
Expand Down Expand Up @@ -342,6 +343,15 @@ export const createComponentFn = (design) => {

export const defineComponent = hooks.defineComponent

// 简便用法
// export default WithDesignConfigPropsDefineComponent({
// name: 'xxx',
// setup() {}
// })
export function WithDesignConfigPropsDefineComponent(BaseComponent: any) {
return DesignConfigPropsHOC(hooks.defineComponent(BaseComponent))
}

export default hooks

export const isVue2 = true
Expand Down
43 changes: 43 additions & 0 deletions packages/vue-common/src/adapter/vue3/hoc.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { type SetupContext } from 'vue'
import { design, hooks, $prefix } from '../../index'
import { getComponentName } from '../index'

// 修改组件 props ,注入 Design Config
export default function DesignConfigPropsHOC(BaseComponent: any) {
return {
...BaseComponent,
props: {},
setup(props, { attrs, slots, expose }: SetupContext) {
const innerRef = hooks.ref()
// 获取组件级配置和全局配置(inject需要带有默认值,否则控制台会报警告)
let globalDesignConfig = hooks.inject(design.configKey, {})
// globalDesignConfig 可能是响应式对象,比如 computed
globalDesignConfig = globalDesignConfig?.value || globalDesignConfig || {}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure that the globalDesignConfig is correctly unwrapped to handle all possible types of reactive objects to avoid unexpected behavior.

const designConfig = globalDesignConfig?.components?.[getComponentName().replace($prefix, '')]
const designConfigProps = designConfig?.props || {}
const mergedProps = { ...designConfigProps, ...attrs }

expose(
new Proxy(
{},
{
get(_target, key) {
return innerRef.value?.[key]
},
has(_target, key) {
return innerRef.value?.[key]
}
}
)
)

return () => {
return (
<BaseComponent {...mergedProps} ref={innerRef}>
{slots}
</BaseComponent>
)
}
}
}
}
10 changes: 10 additions & 0 deletions packages/vue-common/src/adapter/vue3/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import * as hooks from 'vue'

import { camelize, capitalize, hyphenate } from '@opentiny/utils'
import { bindFilter, emitter, getElementCssClass, getElementStatusClass } from '../utils'
import DesignConfigPropsHOC from './hoc'

const Teleport = hooks.Teleport

Expand Down Expand Up @@ -477,6 +478,15 @@ export const createComponentFn = (design) => {

export const defineComponent = hooks.defineComponent

// 简便用法
// export default WithDesignConfigPropsDefineComponent({
// name: 'xxx',
// setup() {}
// })
export function WithDesignConfigPropsDefineComponent(BaseComponent: any) {
return DesignConfigPropsHOC(hooks.defineComponent(BaseComponent))
}

export default hooks

export const isVue2 = false
Expand Down
4 changes: 2 additions & 2 deletions packages/vue-common/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
import { t } from '@opentiny/vue-locale'
import { stringifyCssClass, stringifyCssClassObject, stringifyCssClassArray, deduplicateCssClass } from './csscls'
import '@opentiny/vue-theme/base/index.less'
import { defineComponent, isVue2, isVue3 } from './adapter'
import { defineComponent, isVue2, isVue3, WithDesignConfigPropsDefineComponent } from './adapter'
import { useBreakpoint } from './breakpoint'
import { useDefer } from './usedefer'
import { GRADIENT_ICONS_LIST, generateIcon } from './generateIcon'
Expand All @@ -37,7 +37,7 @@ export { useBreakpoint, useDefer }

export { version } from '../package.json'

export { defineComponent, isVue2, isVue3, appProperties }
export { defineComponent, isVue2, isVue3, appProperties, WithDesignConfigPropsDefineComponent }

export const $prefix = 'Tiny'

Expand Down
2 changes: 1 addition & 1 deletion packages/vue/src/button/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
*
*/
import { $props, $prefix, $setup, defineComponent } from '@opentiny/vue-common'
import { $props, $prefix, $setup, WithDesignConfigPropsDefineComponent as defineComponent } from '@opentiny/vue-common'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

如果这样修改可能会对现有业务造成影响,你这操作相当于包了一层组件转发下props,能不能通过某一个全局配置可以让用户自己选择是否开启这个功能,不要对存量业务造成影响

import template from 'virtual-template?pc|mobile-first'

export const buttonProps = {
Expand Down
Loading