mirror of
https://github.com/tusen-ai/naive-ui.git
synced 2024-11-21 01:13:16 +08:00
feat(marquee): new component
This commit is contained in:
parent
9a23d7eed4
commit
acced75df6
@ -10,6 +10,7 @@
|
||||
|
||||
### Features
|
||||
|
||||
- 🌟 Adds `n-marquee` component.
|
||||
- `n-image` adds `error` slot, closes [#5649](https://github.com/tusen-ai/naive-ui/issues/5649)
|
||||
- `n-date-picker` adds `date-format` prop.
|
||||
- `n-progress`'s `color` prop supports gradient config.
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
### Features
|
||||
|
||||
- 🌟 新增 `n-marquee` 组件
|
||||
- `n-image` 新增 `error` 插槽,关闭 [#5649](https://github.com/tusen-ai/naive-ui/issues/5649)
|
||||
- `n-date-picker` 新增 `date-format` 属性
|
||||
- `n-progress` 的 `color` 属性支持渐变色配置
|
||||
|
@ -590,6 +590,10 @@ export const enComponentRoutes = [
|
||||
path: 'highlight',
|
||||
component: () =>
|
||||
import('../../src/highlight/demos/enUS/index.demo-entry.md')
|
||||
},
|
||||
{
|
||||
path: 'marquee',
|
||||
component: () => import('../../src/marquee/demos/enUS/index.demo-entry.md')
|
||||
}
|
||||
]
|
||||
|
||||
@ -999,6 +1003,10 @@ export const zhComponentRoutes = [
|
||||
path: 'highlight',
|
||||
component: () =>
|
||||
import('../../src/highlight/demos/zhCN/index.demo-entry.md')
|
||||
},
|
||||
{
|
||||
path: 'marquee',
|
||||
component: () => import('../../src/marquee/demos/zhCN/index.demo-entry.md')
|
||||
}
|
||||
]
|
||||
|
||||
|
@ -663,6 +663,13 @@ export function createComponentMenuOptions({ lang, theme }) {
|
||||
enSuffix: true,
|
||||
path: '/drawer'
|
||||
},
|
||||
{
|
||||
en: 'Marquee',
|
||||
zh: '跑马灯',
|
||||
enSuffix: true,
|
||||
path: '/marquee',
|
||||
isNew: true
|
||||
},
|
||||
{
|
||||
en: 'Message',
|
||||
zh: '信息',
|
||||
|
@ -10,6 +10,7 @@ bordered.vue
|
||||
closable.vue
|
||||
icon.vue
|
||||
no-icon.vue
|
||||
marquee.vue
|
||||
```
|
||||
|
||||
## API
|
||||
|
15
src/alert/demos/enUS/marquee.demo.vue
Normal file
15
src/alert/demos/enUS/marquee.demo.vue
Normal file
@ -0,0 +1,15 @@
|
||||
<markdown>
|
||||
# Marquee
|
||||
|
||||
You can use `n-marquee` to achieve marquee effect.
|
||||
</markdown>
|
||||
|
||||
<template>
|
||||
<n-alert type="error" title="Warning">
|
||||
<n-marquee>
|
||||
<div style="margin-right: 64px">
|
||||
Test environment is offline again.
|
||||
</div>
|
||||
</n-marquee>
|
||||
</n-alert>
|
||||
</template>
|
@ -12,6 +12,7 @@ bordered.vue
|
||||
closable.vue
|
||||
icon.vue
|
||||
no-icon.vue
|
||||
marquee.vue
|
||||
rtl-debug.vue
|
||||
empty-debug.vue
|
||||
```
|
||||
|
15
src/alert/demos/zhCN/marquee.demo.vue
Normal file
15
src/alert/demos/zhCN/marquee.demo.vue
Normal file
@ -0,0 +1,15 @@
|
||||
<markdown>
|
||||
# 跑马灯
|
||||
|
||||
你可以配合 `n-marquee` 实现轮播的效果。
|
||||
</markdown>
|
||||
|
||||
<template>
|
||||
<n-alert type="error" title="呵呵">
|
||||
<n-marquee>
|
||||
<div style="margin-right: 64px">
|
||||
测试环境又挂了。
|
||||
</div>
|
||||
</n-marquee>
|
||||
</n-alert>
|
||||
</template>
|
@ -53,6 +53,7 @@ export * from './list'
|
||||
export * from './loading-bar'
|
||||
export * from './log'
|
||||
export * from './infinite-scroll'
|
||||
export * from './marquee'
|
||||
export * from './menu'
|
||||
export * from './mention'
|
||||
export * from './message'
|
||||
|
@ -100,6 +100,7 @@ import type { RowTheme } from '../../legacy-grid/styles'
|
||||
import type { SplitTheme } from '../../split/styles'
|
||||
import type { FlexTheme } from '../../flex/styles'
|
||||
import type { FloatButtonGroupTheme } from '../../float-button-group/styles'
|
||||
import type { MarqueeTheme } from '../../marquee/styles'
|
||||
import type { Katex } from './katex'
|
||||
import type { GlobalTheme, GlobalThemeOverrides } from './interface'
|
||||
|
||||
@ -190,6 +191,7 @@ export interface GlobalThemeWithoutCommon {
|
||||
Watermark?: WatermarkTheme
|
||||
Split?: SplitTheme
|
||||
Row?: RowTheme
|
||||
Marquee?: MarqueeTheme
|
||||
// internal
|
||||
InternalSelectMenu?: InternalSelectMenuTheme
|
||||
InternalSelection?: InternalSelectionTheme
|
||||
|
16
src/marquee/demos/enUS/auto-fill.demo.vue
Normal file
16
src/marquee/demos/enUS/auto-fill.demo.vue
Normal file
@ -0,0 +1,16 @@
|
||||
<markdown>
|
||||
# Auto fill
|
||||
|
||||
Use `auto-fill` prop to fill all the blank space that left.
|
||||
</markdown>
|
||||
|
||||
<template>
|
||||
<n-marquee auto-fill>
|
||||
<n-image
|
||||
width="80"
|
||||
height="80"
|
||||
src="https://07akioni.oss-cn-beijing.aliyuncs.com/07akioni.jpeg"
|
||||
style="margin-right: 24px"
|
||||
/>
|
||||
</n-marquee>
|
||||
</template>
|
15
src/marquee/demos/enUS/basic.demo.vue
Normal file
15
src/marquee/demos/enUS/basic.demo.vue
Normal file
@ -0,0 +1,15 @@
|
||||
<markdown>
|
||||
# Basic
|
||||
|
||||
Put text into marquee:
|
||||
</markdown>
|
||||
|
||||
<template>
|
||||
<n-marquee>
|
||||
<div style="margin-right: 64px">
|
||||
In 2020 Noel returned to the legendary Rockfield Studios in Wales for the
|
||||
first time since the band recorded the album, looking back at his memories
|
||||
and reflecting on the album’s legacy.
|
||||
</div>
|
||||
</n-marquee>
|
||||
</template>
|
16
src/marquee/demos/enUS/image.demo.vue
Normal file
16
src/marquee/demos/enUS/image.demo.vue
Normal file
@ -0,0 +1,16 @@
|
||||
<markdown>
|
||||
# Image
|
||||
|
||||
You can put any content inside marquee.
|
||||
</markdown>
|
||||
|
||||
<template>
|
||||
<n-marquee>
|
||||
<n-image
|
||||
width="80"
|
||||
height="80"
|
||||
src="https://07akioni.oss-cn-beijing.aliyuncs.com/07akioni.jpeg"
|
||||
style="margin-right: 24px"
|
||||
/>
|
||||
</n-marquee>
|
||||
</template>
|
28
src/marquee/demos/enUS/index.demo-entry.md
Normal file
28
src/marquee/demos/enUS/index.demo-entry.md
Normal file
@ -0,0 +1,28 @@
|
||||
# Marquee
|
||||
|
||||
A trivia: There's a deprecated HTML Element called `marquee`.
|
||||
|
||||
Available since `NEXT_VERSION`.
|
||||
|
||||
## Demos
|
||||
|
||||
```demo
|
||||
basic.vue
|
||||
image.vue
|
||||
auto-fill.vue
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### Marquee Props
|
||||
|
||||
| Name | Type | Default | Description | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| auto-fill | `boolean` | `false` | Whether to fill the blank of the container using its content repeatly. | NEXT_VERSION |
|
||||
| speed | `number` | `48` | The speed calculated as pixels/second. | NEXT_VERSION |
|
||||
|
||||
### Marquee Slots
|
||||
|
||||
| Name | Parameters | Description | Version |
|
||||
| ------- | ---------- | ----------- | ------------ |
|
||||
| default | `()` | Content. | NEXT_VERSION |
|
16
src/marquee/demos/zhCN/auto-fill.demo.vue
Normal file
16
src/marquee/demos/zhCN/auto-fill.demo.vue
Normal file
@ -0,0 +1,16 @@
|
||||
<markdown>
|
||||
# 自动填充
|
||||
|
||||
使用 `auto-fill` 属性让内容铺满空白空间。
|
||||
</markdown>
|
||||
|
||||
<template>
|
||||
<n-marquee auto-fill>
|
||||
<n-image
|
||||
width="80"
|
||||
height="80"
|
||||
src="https://07akioni.oss-cn-beijing.aliyuncs.com/07akioni.jpeg"
|
||||
style="margin-right: 24px"
|
||||
/>
|
||||
</n-marquee>
|
||||
</template>
|
11
src/marquee/demos/zhCN/basic.demo.vue
Normal file
11
src/marquee/demos/zhCN/basic.demo.vue
Normal file
@ -0,0 +1,11 @@
|
||||
<markdown>
|
||||
# 基础用法
|
||||
|
||||
在跑马灯中输入文字:
|
||||
</markdown>
|
||||
|
||||
<template>
|
||||
<n-marquee>
|
||||
谁用运气换呼吸 谁用灵魂换稻米 谁用运气换呼吸 谁用灵魂换稻米
|
||||
</n-marquee>
|
||||
</template>
|
16
src/marquee/demos/zhCN/image.demo.vue
Normal file
16
src/marquee/demos/zhCN/image.demo.vue
Normal file
@ -0,0 +1,16 @@
|
||||
<markdown>
|
||||
# 图片
|
||||
|
||||
你可以将任何内容放入跑马灯中。
|
||||
</markdown>
|
||||
|
||||
<template>
|
||||
<n-marquee>
|
||||
<n-image
|
||||
width="80"
|
||||
height="80"
|
||||
src="https://07akioni.oss-cn-beijing.aliyuncs.com/07akioni.jpeg"
|
||||
style="margin-right: 24px"
|
||||
/>
|
||||
</n-marquee>
|
||||
</template>
|
28
src/marquee/demos/zhCN/index.demo-entry.md
Normal file
28
src/marquee/demos/zhCN/index.demo-entry.md
Normal file
@ -0,0 +1,28 @@
|
||||
# 跑马灯 Marquee
|
||||
|
||||
我有一个高中同学,当时他的口头禅是:“滚滚滚。”
|
||||
|
||||
自 `NEXT_VERSION` 开始提供。
|
||||
|
||||
## 演示
|
||||
|
||||
```demo
|
||||
basic.vue
|
||||
image.vue
|
||||
auto-fill.vue
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### Marquee Props
|
||||
|
||||
| 名称 | 类型 | 默认值 | 说明 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| auto-fill | `boolean` | `false` | 是否重复的用内容铺满容器的空白 | NEXT_VERSION |
|
||||
| speed | `number` | `48` | 移动的速度,单位是像素每秒 | NEXT_VERSION |
|
||||
|
||||
### Marquee Slots
|
||||
|
||||
| 名称 | 参数 | 说明 | 版本 |
|
||||
| ------- | ---- | ---- | ------------ |
|
||||
| default | `()` | 内容 | NEXT_VERSION |
|
2
src/marquee/index.ts
Normal file
2
src/marquee/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export { default as NMarqueue } from './src/Marquee'
|
||||
export type * from './src/public-types'
|
148
src/marquee/src/Marquee.tsx
Normal file
148
src/marquee/src/Marquee.tsx
Normal file
@ -0,0 +1,148 @@
|
||||
import { computed, defineComponent, h, nextTick, ref } from 'vue'
|
||||
import { VResizeObserver } from 'vueuc'
|
||||
import { repeat } from 'seemly'
|
||||
import { useConfig, useTheme } from '../../_mixins'
|
||||
import { marqueeLight } from '../styles'
|
||||
import style from './styles/index.cssr'
|
||||
import { marqueeProps } from './props'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Marquee',
|
||||
props: marqueeProps,
|
||||
setup(props) {
|
||||
const { mergedClsPrefixRef } = useConfig(props)
|
||||
useTheme(
|
||||
'Marquee',
|
||||
'-marquee',
|
||||
style,
|
||||
marqueeLight,
|
||||
props,
|
||||
mergedClsPrefixRef
|
||||
)
|
||||
|
||||
const containerElRef = ref<HTMLDivElement | null>(null)
|
||||
|
||||
const contentWidthRef = ref(-1)
|
||||
const containerWidthRef = ref(-1)
|
||||
|
||||
const playStateRef = ref<'paused' | 'running'>('running')
|
||||
|
||||
const repeatCountInOneGroupRef = computed(() => {
|
||||
if (!props.autoFill)
|
||||
return 1
|
||||
const { value: contentWidth } = contentWidthRef
|
||||
const { value: containerWidth } = containerWidthRef
|
||||
if (contentWidth === -1 || containerWidth === -1)
|
||||
return 1
|
||||
return Math.ceil(containerWidthRef.value / contentWidth)
|
||||
})
|
||||
|
||||
const durationRef = computed(() => {
|
||||
const { value: contentWidth } = contentWidthRef
|
||||
if (contentWidth === -1)
|
||||
return 0
|
||||
return (contentWidth * repeatCountInOneGroupRef.value) / props.speed
|
||||
})
|
||||
|
||||
const animationCssVarsRef = computed(() => {
|
||||
return {
|
||||
'--n-play': playStateRef.value,
|
||||
'--n-direction': 'normal',
|
||||
'--n-duration': `${durationRef.value}s`,
|
||||
'--n-delay': '0s',
|
||||
'--n-iteration-count': 'infinite',
|
||||
'--n-min-width': 'auto'
|
||||
}
|
||||
})
|
||||
|
||||
function resetScrollState() {
|
||||
playStateRef.value = 'paused'
|
||||
nextTick().then(() => {
|
||||
void containerElRef.value?.offsetTop
|
||||
playStateRef.value = 'running'
|
||||
})
|
||||
}
|
||||
|
||||
function handleContainerResize(entry: ResizeObserverEntry) {
|
||||
containerWidthRef.value = entry.contentRect.width
|
||||
}
|
||||
|
||||
function handleContentResize(entry: ResizeObserverEntry) {
|
||||
contentWidthRef.value = entry.contentRect.width
|
||||
}
|
||||
|
||||
function handleAnimationIteration() {
|
||||
resetScrollState()
|
||||
}
|
||||
|
||||
return {
|
||||
mergedClsPrefix: mergedClsPrefixRef,
|
||||
animationCssVars: animationCssVarsRef,
|
||||
containerElRef,
|
||||
repeatCountInOneGroup: repeatCountInOneGroupRef,
|
||||
handleContainerResize,
|
||||
handleContentResize,
|
||||
handleAnimationIteration
|
||||
}
|
||||
},
|
||||
render() {
|
||||
const {
|
||||
$slots,
|
||||
mergedClsPrefix,
|
||||
animationCssVars,
|
||||
repeatCountInOneGroup,
|
||||
handleAnimationIteration
|
||||
} = this
|
||||
const originalNode = (
|
||||
<VResizeObserver onResize={this.handleContentResize}>
|
||||
<div
|
||||
class={`${mergedClsPrefix}-marquee__item ${mergedClsPrefix}-marquee__original-item`}
|
||||
>
|
||||
{$slots}
|
||||
</div>
|
||||
</VResizeObserver>
|
||||
)
|
||||
const mirrorNode = (
|
||||
<div class={`${mergedClsPrefix}-marquee__item`}>{$slots}</div>
|
||||
)
|
||||
if (this.autoFill) {
|
||||
return (
|
||||
<VResizeObserver onResize={this.handleContainerResize}>
|
||||
<div
|
||||
class={`${mergedClsPrefix}-marquee ${mergedClsPrefix}-marquee--auto-fill`}
|
||||
ref="containerElRef"
|
||||
style={animationCssVars}
|
||||
>
|
||||
<div
|
||||
class={`${mergedClsPrefix}-marquee__group`}
|
||||
onAnimationiteration={handleAnimationIteration}
|
||||
>
|
||||
{originalNode}
|
||||
{repeat(repeatCountInOneGroup - 1, mirrorNode)}
|
||||
</div>
|
||||
<div class={`${mergedClsPrefix}-marquee__group`}>
|
||||
{repeat(repeatCountInOneGroup, mirrorNode)}
|
||||
</div>
|
||||
</div>
|
||||
</VResizeObserver>
|
||||
)
|
||||
}
|
||||
else {
|
||||
return (
|
||||
<div
|
||||
class={[`${mergedClsPrefix}-marquee`]}
|
||||
ref="containerElRef"
|
||||
style={animationCssVars}
|
||||
>
|
||||
<div
|
||||
class={`${mergedClsPrefix}-marquee__group`}
|
||||
onAnimationiteration={handleAnimationIteration}
|
||||
>
|
||||
{originalNode}
|
||||
</div>
|
||||
<div class={`${mergedClsPrefix}-marquee__group`}>{mirrorNode}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
15
src/marquee/src/props.ts
Normal file
15
src/marquee/src/props.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import type { ThemeProps } from '../../_mixins'
|
||||
import { useTheme } from '../../_mixins'
|
||||
import type { ExtractPublicPropTypes } from '../../_utils'
|
||||
import type { MarqueeTheme } from '../styles'
|
||||
|
||||
export const marqueeProps = {
|
||||
...(useTheme.props as ThemeProps<MarqueeTheme>),
|
||||
autoFill: Boolean,
|
||||
speed: {
|
||||
type: Number,
|
||||
default: 48
|
||||
}
|
||||
}
|
||||
|
||||
export type MarqueeProps = ExtractPublicPropTypes<typeof marqueeProps>
|
1
src/marquee/src/public-types.ts
Normal file
1
src/marquee/src/public-types.ts
Normal file
@ -0,0 +1 @@
|
||||
export type { MarqueeProps } from './props'
|
40
src/marquee/src/styles/index.cssr.ts
Normal file
40
src/marquee/src/styles/index.cssr.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import { c, cB, cE, cNotM } from '../../../_utils/cssr'
|
||||
|
||||
// vars:
|
||||
// --n-play
|
||||
// --n-direction
|
||||
// --n-duration
|
||||
// --n-delay
|
||||
// --n-iteration-count
|
||||
// --n-min-width
|
||||
export default c([
|
||||
cB('marquee', `
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
`, [
|
||||
cE('group', `
|
||||
flex: 0 0 auto;
|
||||
min-width: var(--n-min-width);
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
animation: n-marquee var(--n-duration) linear var(--n-delay) var(--n-iteration-count);
|
||||
animation-play-state: var(--n-play);
|
||||
animation-delay: var(--n-delay);
|
||||
animation-direction: var(--n-direction);
|
||||
`),
|
||||
cNotM('auto-fill', [
|
||||
cE('group', `min-width: 100%;`),
|
||||
cE('item', `min-width: 100%;`)
|
||||
])
|
||||
]),
|
||||
c('@keyframes n-marquee', {
|
||||
from: {
|
||||
transform: 'translateX(0)'
|
||||
},
|
||||
to: {
|
||||
transform: 'translateX(-100%)'
|
||||
}
|
||||
})
|
||||
])
|
11
src/marquee/styles/dark.ts
Normal file
11
src/marquee/styles/dark.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { commonDark } from '../../_styles/common'
|
||||
import type { MarqueeTheme } from './light'
|
||||
import { self } from './light'
|
||||
|
||||
const marqueeDark: MarqueeTheme = {
|
||||
name: 'Marquee',
|
||||
common: commonDark,
|
||||
self
|
||||
}
|
||||
|
||||
export default marqueeDark
|
3
src/marquee/styles/index.ts
Normal file
3
src/marquee/styles/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export { default as marqueeDark } from './dark'
|
||||
export { default as marqueeLight } from './light'
|
||||
export type { MarqueeTheme, MarqueeThemeVars } from './light'
|
17
src/marquee/styles/light.ts
Normal file
17
src/marquee/styles/light.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { commonLight } from '../../_styles/common'
|
||||
import type { Theme } from '../../_mixins'
|
||||
|
||||
export function self() {
|
||||
return {}
|
||||
}
|
||||
|
||||
export type MarqueeThemeVars = ReturnType<typeof self>
|
||||
|
||||
const marqueeLight: Theme<'Marquee', MarqueeThemeVars> = {
|
||||
name: 'Marquee',
|
||||
common: commonLight,
|
||||
self
|
||||
}
|
||||
|
||||
export default marqueeLight
|
||||
export type MarqueeTheme = typeof marqueeLight
|
8
src/marquee/tests/Marquee.spec.ts
Normal file
8
src/marquee/tests/Marquee.spec.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { NMarqueue } from '../index'
|
||||
|
||||
describe('n-marquee', () => {
|
||||
it('should work with import on demand', () => {
|
||||
mount(NMarqueue)
|
||||
})
|
||||
})
|
20
src/marquee/tests/server.spec.tsx
Normal file
20
src/marquee/tests/server.spec.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @jest-environment node
|
||||
*/
|
||||
import { createSSRApp, h } from 'vue'
|
||||
import { renderToString } from '@vue/server-renderer'
|
||||
import { setup } from '@css-render/vue3-ssr'
|
||||
import { NEmpty } from '../..'
|
||||
|
||||
describe('sSR', () => {
|
||||
it('works', async () => {
|
||||
const app = createSSRApp(() => <NEmpty />)
|
||||
setup(app)
|
||||
try {
|
||||
await renderToString(app)
|
||||
}
|
||||
catch (e) {
|
||||
expect(e).not.toBeTruthy()
|
||||
}
|
||||
})
|
||||
})
|
@ -85,6 +85,7 @@ import { watermarkDark } from '../watermark/styles'
|
||||
import { splitDark } from '../split/styles'
|
||||
import { flexDark } from '../styles'
|
||||
import { floatButtonGroupDark } from '../float-button-group/styles'
|
||||
import { marqueeDark } from '../marquee/styles'
|
||||
import type { BuiltInGlobalTheme } from './interface'
|
||||
|
||||
export const darkTheme: BuiltInGlobalTheme = {
|
||||
@ -175,5 +176,6 @@ export const darkTheme: BuiltInGlobalTheme = {
|
||||
Watermark: watermarkDark,
|
||||
Split: splitDark,
|
||||
FloatButton: floatButtonDark,
|
||||
FloatButtonGroup: floatButtonGroupDark
|
||||
FloatButtonGroup: floatButtonGroupDark,
|
||||
Marquee: marqueeDark
|
||||
}
|
||||
|
@ -87,6 +87,7 @@ import { watermarkLight } from '../watermark/styles'
|
||||
import { splitLight } from '../split/styles'
|
||||
import { flexLight } from '../flex/styles'
|
||||
import { floatButtonGroupLight } from '../float-button-group/styles'
|
||||
import { marqueeLight } from '../marquee/styles'
|
||||
import type { BuiltInGlobalTheme } from './interface'
|
||||
|
||||
export const lightTheme: BuiltInGlobalTheme = {
|
||||
@ -177,5 +178,6 @@ export const lightTheme: BuiltInGlobalTheme = {
|
||||
Watermark: watermarkLight,
|
||||
Split: splitLight,
|
||||
FloatButton: floatButtonLight,
|
||||
FloatButtonGroup: floatButtonGroupLight
|
||||
FloatButtonGroup: floatButtonGroupLight,
|
||||
Marquee: marqueeLight
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user