Skip to content

feat(config-provider): config-provider adds theme configuration function #3353

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

Merged
merged 1 commit into from
Apr 25, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
14 changes: 14 additions & 0 deletions examples/sites/demos/apis/config-provider.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,20 @@ export default {
},
mode: ['pc'],
pcDemo: 'tag'
},
{
name: 'theme',
type: 'object',
defaultValue: '--',
meta: {
stable: '3.23.0'
},
desc: {
'zh-CN': '自定义主题色,格式:{data:{"tv-base-color-brand":"#595959",....}}',
'en-US': 'Customized theme color, in {data:"tv-base-color-brand":"#595959",....} format.'
},
mode: ['pc'],
pcDemo: 'theme'
}
],
events: [],
Expand Down
11 changes: 11 additions & 0 deletions examples/sites/demos/apis/tabs.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,17 @@ export default {
pcDemo: 'size',
mfDemo: ''
},
{
name: 'swipeable',
type: 'boolean',
defaultValue: 'false',
desc: {
'zh-CN': '是否开启滑动内容切换标签页',
'en-US': 'Do you want to enable the sliding content switching tab function'
},
mode: ['mobile-first'],
mfDemo: 'swipeable'
},
{
name: 'stretch',
type: 'boolean',
Expand Down
50 changes: 50 additions & 0 deletions examples/sites/demos/mobile-first/app/tabs/swipeable.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<template>
<tiny-tabs v-model="activeName" class="select-none" :optimized="false" swipeable>
<tiny-tab-item title="表单组件" name="first">
<div class="flex w-fit">
<div class="bg-color-bg-1 w-96 h-60 mr-4">
表单组件,具有与用户交互,并可完成数据采集功能的控件。--tab 1 item 1
</div>
<div class="bg-color-bg-2 w-96 h-60 mr-4">
表单组件,具有与用户交互,并可完成数据采集功能的控件。--tab 1 item 2
</div>
<div class="bg-color-bg-3 w-96 h-60 mr-4">
表单组件,具有与用户交互,并可完成数据采集功能的控件。--tab 1 item 3
</div>
<div class="bg-color-bg-4 w-96 h-60 mr-4">
表单组件,具有与用户交互,并可完成数据采集功能的控件。--tab 1 item 4
</div>
<div class="bg-color-bg-5 w-96 h-60 mr-4">
表单组件,具有与用户交互,并可完成数据采集功能的控件。--tab 1 item 5
</div>
</div>
</tiny-tab-item>
<tiny-tab-item title="数据组件" name="second">
<div class="bg-color-bg-6 w-full h-60 mr-4">
数据组件,提供了非常强大数据表格功能,在Grid可以展示数据列表,可以对数据列表进行选择、编辑等。--tab 2
</div>
</tiny-tab-item>
<tiny-tab-item title="导航组件" name="third">
<div class="bg-color-bg-7 w-full h-60 mr-4">导航组件,帮助网站访问者浏览站点的组件。--tab 3</div>
</tiny-tab-item>
<tiny-tab-item title="业务组件" name="fourth">
<div class="bg-color-bg-8 w-full h-60 mr-4">业务组件,与业务紧密相关实现某种业务功能的组件集。--tab 4</div>
</tiny-tab-item>
</tiny-tabs>
</template>

<script>
import { TinyTabs, TinyTabItem } from '@opentiny/vue'

export default {
components: {
TinyTabs,
TinyTabItem
},
data() {
return {
activeName: 'first'
}
}
}
</script>
13 changes: 13 additions & 0 deletions examples/sites/demos/mobile-first/app/tabs/webdoc/tabs.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,19 @@ export default {
},
codeFiles: ['align-center.vue']
},
{
demoId: 'swipeable',
name: {
'zh-CN': '滑动切换内容',
'en-US': 'Slide to switch content'
},
desc: {
'zh-CN':
'<p>在关闭优化渲染并且所有标签项都不开启延时加载时,可通过 <code>:swipeable=true</code> 开启滑动内容切换标签页。在此模式下,标签项内容区可以是一个水平滚动列表,参考示例中 `表单组件` 标签项。</p>',
'en-US': `<p>When optimizing rendering is turned off and all label items do not enable lazy loading, you can use<code>: swift=true</code>to enable sliding content to switch tabs. In this mode, the tag item content area can be a horizontally scrolling list, as shown in the example of the 'form component' tag item.</p>`
},
codeFiles: ['swipeable.vue']
},
{
demoId: 'tab-style-card',
name: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<template>
<div>
<tiny-button @click="changeTheme" style="margin-bottom: 20px">改变主题颜色</tiny-button>
<tiny-button type="success" @click="resetTheme" style="margin-bottom: 20px">重置主题颜色</tiny-button>
<tiny-config-provider :theme="theme">
<tiny-button type="primary">主题色按钮</tiny-button>
</tiny-config-provider>
</div>
</template>

<script setup>
import { ref } from 'vue'
import { TinyConfigProvider, TinyButton } from '@opentiny/vue'

const theme = ref({
data: {}
})

function changeTheme() {
theme.value = {
data: {
'tv-base-color-brand': 'green'
}
}
}

function resetTheme() {
theme.value = {
data: {
'tv-base-color-brand': '#191919'
}
}
}
</script>

<style scoped>
.tiny-config-provider {
padding: 1em;
border: 1px solid #ccc;
border-radius: 1em;
}
</style>
51 changes: 51 additions & 0 deletions examples/sites/demos/pc/app/config-provider/theme.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<template>
<div>
<tiny-button @click="changeTheme" style="margin-bottom: 20px">改变主题颜色</tiny-button>
<tiny-button type="success" @click="resetTheme" style="margin-bottom: 20px">重置主题颜色</tiny-button>
<tiny-config-provider :theme="theme">
<tiny-button type="primary">主题色按钮</tiny-button>
</tiny-config-provider>
</div>
</template>

<script>
import { TinyConfigProvider, TinyButton } from '@opentiny/vue'

export default {
components: {
TinyConfigProvider,
TinyButton
},
data() {
return {
theme: {
data: {}
}
}
},
methods: {
changeTheme() {
this.theme = {
data: {
'tv-base-color-brand': 'green'
}
}
},
resetTheme() {
this.theme = {
data: {
'tv-base-color-brand': '#191919'
}
}
}
}
}
</script>

<style scoped>
.tiny-config-provider {
padding: 1em;
border: 1px solid #ccc;
border-radius: 1em;
}
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,18 @@ export default {
'en-US': 'Container labels can be customized through<code>tag</code>.'
},
codeFiles: ['tag.vue']
},
{
demoId: 'theme',
name: {
'zh-CN': '自定义主题色',
'en-US': 'Custom Colors'
},
desc: {
'zh-CN': '可通过<code>theme</code>属性设置自定义主题色常量。',
'en-US': 'You can use the <code>theme</code> property to set a custom theme color constant.'
},
codeFiles: ['theme.vue']
}
],
features: [
Expand Down
19 changes: 4 additions & 15 deletions packages/theme-saas/src/svgs/go-back.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions packages/utils/src/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -308,3 +308,27 @@ export const CASCADER: Record<string, any> = {
PropsHover: 'hoverThreshold',
MenuConnector: 'cascader-menu-'
}

/**
* 检查对象是否具有任何一个指定的键
* @param obj 需要检查的对象
* @param keys 需要检查的键的数组
* @return 如果对象具有任何一个指定的键,返回true,否则返回false
*/
export const hasAnyKey = (obj: any, keys: string[]): boolean => {
if (obj == null) {
return false
}

if (keys.length === 0) {
return false
}

for (const key of keys) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
return true
}
}

return false
}
3 changes: 2 additions & 1 deletion packages/vue/src/config-provider/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
},
"dependencies": {
"@opentiny/vue-common": "workspace:~",
"@opentiny/vue-theme": "workspace:~"
"@opentiny/vue-theme": "workspace:~",
"@opentiny/utils": "workspace:~"
},
"license": "MIT"
}
54 changes: 53 additions & 1 deletion packages/vue/src/config-provider/src/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,32 @@ import type { PropType } from '@opentiny/vue-common'
import type { Tag, TextDirection, breakPoint } from './props'
import { configProviderContextKey } from '../index'
import '@opentiny/vue-theme/config-provider/index.less'
import TinyThemeTool from '@opentiny/vue-theme/theme-tool'
import { isObject } from '@opentiny/utils'

/**
* 检查对象是否具有任何一个指定的键
* @param obj 需要检查的对象
* @param keys 需要检查的键的数组
* @return 如果对象具有任何一个指定的键,返回true,否则返回false
*/
const hasAnyKey = (obj: any, keys: string[]): boolean => {
if (obj == null) {
return false
}

if (keys.length === 0) {
return false
}

for (const key of keys) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
return true
}
}

return false
}

export default defineComponent({
name: $prefix + 'ConfigProvider',
Expand Down Expand Up @@ -42,6 +68,12 @@ export default defineComponent({
}
}
},
theme: {
type: Object,
default: () => {
return null
}
},
..._props
.map((item) => {
return {
Expand All @@ -58,8 +90,28 @@ export default defineComponent({
})
},
setup(props, { slots }) {
const { direction, design } = hooks.toRefs(props)
const { direction, design, theme } = hooks.toRefs(props)
provideDesignConfig(design)
hooks.watch(
() => theme.value,
() => {
if (isObject(theme.value) && hasAnyKey(theme.value, ['data'])) {
const themeTool = new TinyThemeTool()
themeTool.changeTheme(theme.value)
}

if (isObject(theme.value) && !hasAnyKey(theme.value, ['data'])) {
console.warn(`configProvider组件的theme属性对象请配置data属性。e.g { data: {'tv-base-color-brand': '#000'}}`)

Choose a reason for hiding this comment

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

The warning message here should be more descriptive to guide the user on how to properly configure the theme property. Consider providing a more detailed example or linking to documentation.

}

if (theme.value && !isObject(theme.value)) {
console.warn(`configProvider组件的theme属性请配置对象格式数据`)
}
},
{
immediate: true
}
)
const isRTL = hooks.computed(() => direction.value === 'rtl')
const cssVar = hooks.computed(() => {
return {
Expand Down
1 change: 1 addition & 0 deletions packages/vue/src/config-provider/src/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ export interface ConfigProviderProps {
enable?: boolean
name?: string
}
theme?: Object | null
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid using Object as a type and define a specific interface instead.

Using Object as a type is overly broad and provides little type safety. Consider creating a specific interface that defines the expected structure of the theme object, which would provide better documentation and type checking.

+ export interface ThemeConfig {
+   data: Record<string, string>;
+   // Add any other expected properties
+ }

export interface ConfigProviderProps {
  breakPoints: breakPoint
  direction: 'rtl' | 'ltr'
  globalPrefix?: string
  tag: {
    enable?: boolean
    name?: string
  }
-  theme?: Object | null
+  theme?: ThemeConfig | null
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
theme?: Object | null
export interface ThemeConfig {
data: Record<string, string>
// Add any other expected properties
}
export interface ConfigProviderProps {
breakPoints: breakPoint
direction: 'rtl' | 'ltr'
globalPrefix?: string
tag: {
enable?: boolean
name?: string
}
theme?: ThemeConfig | null
}
🧰 Tools
🪛 Biome (1.9.4)

[error] 23-23: Don't use 'Object' as a type.

Prefer explicitly define the object shape. This type means "any non-nullable value", which is slightly better than 'unknown', but it's still a broad type.

(lint/complexity/noBannedTypes)

}
Loading