mirror of
https://github.com/tusen-ai/naive-ui.git
synced 2024-11-27 04:09:51 +08:00
feat(marquee): new component
This commit is contained in:
parent
9a23d7eed4
commit
acced75df6
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
|
- 🌟 Adds `n-marquee` component.
|
||||||
- `n-image` adds `error` slot, closes [#5649](https://github.com/tusen-ai/naive-ui/issues/5649)
|
- `n-image` adds `error` slot, closes [#5649](https://github.com/tusen-ai/naive-ui/issues/5649)
|
||||||
- `n-date-picker` adds `date-format` prop.
|
- `n-date-picker` adds `date-format` prop.
|
||||||
- `n-progress`'s `color` prop supports gradient config.
|
- `n-progress`'s `color` prop supports gradient config.
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
|
- 🌟 新增 `n-marquee` 组件
|
||||||
- `n-image` 新增 `error` 插槽,关闭 [#5649](https://github.com/tusen-ai/naive-ui/issues/5649)
|
- `n-image` 新增 `error` 插槽,关闭 [#5649](https://github.com/tusen-ai/naive-ui/issues/5649)
|
||||||
- `n-date-picker` 新增 `date-format` 属性
|
- `n-date-picker` 新增 `date-format` 属性
|
||||||
- `n-progress` 的 `color` 属性支持渐变色配置
|
- `n-progress` 的 `color` 属性支持渐变色配置
|
||||||
|
@ -590,6 +590,10 @@ export const enComponentRoutes = [
|
|||||||
path: 'highlight',
|
path: 'highlight',
|
||||||
component: () =>
|
component: () =>
|
||||||
import('../../src/highlight/demos/enUS/index.demo-entry.md')
|
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',
|
path: 'highlight',
|
||||||
component: () =>
|
component: () =>
|
||||||
import('../../src/highlight/demos/zhCN/index.demo-entry.md')
|
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,
|
enSuffix: true,
|
||||||
path: '/drawer'
|
path: '/drawer'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
en: 'Marquee',
|
||||||
|
zh: '跑马灯',
|
||||||
|
enSuffix: true,
|
||||||
|
path: '/marquee',
|
||||||
|
isNew: true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
en: 'Message',
|
en: 'Message',
|
||||||
zh: '信息',
|
zh: '信息',
|
||||||
|
@ -10,6 +10,7 @@ bordered.vue
|
|||||||
closable.vue
|
closable.vue
|
||||||
icon.vue
|
icon.vue
|
||||||
no-icon.vue
|
no-icon.vue
|
||||||
|
marquee.vue
|
||||||
```
|
```
|
||||||
|
|
||||||
## API
|
## 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
|
closable.vue
|
||||||
icon.vue
|
icon.vue
|
||||||
no-icon.vue
|
no-icon.vue
|
||||||
|
marquee.vue
|
||||||
rtl-debug.vue
|
rtl-debug.vue
|
||||||
empty-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 './loading-bar'
|
||||||
export * from './log'
|
export * from './log'
|
||||||
export * from './infinite-scroll'
|
export * from './infinite-scroll'
|
||||||
|
export * from './marquee'
|
||||||
export * from './menu'
|
export * from './menu'
|
||||||
export * from './mention'
|
export * from './mention'
|
||||||
export * from './message'
|
export * from './message'
|
||||||
|
@ -100,6 +100,7 @@ import type { RowTheme } from '../../legacy-grid/styles'
|
|||||||
import type { SplitTheme } from '../../split/styles'
|
import type { SplitTheme } from '../../split/styles'
|
||||||
import type { FlexTheme } from '../../flex/styles'
|
import type { FlexTheme } from '../../flex/styles'
|
||||||
import type { FloatButtonGroupTheme } from '../../float-button-group/styles'
|
import type { FloatButtonGroupTheme } from '../../float-button-group/styles'
|
||||||
|
import type { MarqueeTheme } from '../../marquee/styles'
|
||||||
import type { Katex } from './katex'
|
import type { Katex } from './katex'
|
||||||
import type { GlobalTheme, GlobalThemeOverrides } from './interface'
|
import type { GlobalTheme, GlobalThemeOverrides } from './interface'
|
||||||
|
|
||||||
@ -190,6 +191,7 @@ export interface GlobalThemeWithoutCommon {
|
|||||||
Watermark?: WatermarkTheme
|
Watermark?: WatermarkTheme
|
||||||
Split?: SplitTheme
|
Split?: SplitTheme
|
||||||
Row?: RowTheme
|
Row?: RowTheme
|
||||||
|
Marquee?: MarqueeTheme
|
||||||
// internal
|
// internal
|
||||||
InternalSelectMenu?: InternalSelectMenuTheme
|
InternalSelectMenu?: InternalSelectMenuTheme
|
||||||
InternalSelection?: InternalSelectionTheme
|
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 { splitDark } from '../split/styles'
|
||||||
import { flexDark } from '../styles'
|
import { flexDark } from '../styles'
|
||||||
import { floatButtonGroupDark } from '../float-button-group/styles'
|
import { floatButtonGroupDark } from '../float-button-group/styles'
|
||||||
|
import { marqueeDark } from '../marquee/styles'
|
||||||
import type { BuiltInGlobalTheme } from './interface'
|
import type { BuiltInGlobalTheme } from './interface'
|
||||||
|
|
||||||
export const darkTheme: BuiltInGlobalTheme = {
|
export const darkTheme: BuiltInGlobalTheme = {
|
||||||
@ -175,5 +176,6 @@ export const darkTheme: BuiltInGlobalTheme = {
|
|||||||
Watermark: watermarkDark,
|
Watermark: watermarkDark,
|
||||||
Split: splitDark,
|
Split: splitDark,
|
||||||
FloatButton: floatButtonDark,
|
FloatButton: floatButtonDark,
|
||||||
FloatButtonGroup: floatButtonGroupDark
|
FloatButtonGroup: floatButtonGroupDark,
|
||||||
|
Marquee: marqueeDark
|
||||||
}
|
}
|
||||||
|
@ -87,6 +87,7 @@ import { watermarkLight } from '../watermark/styles'
|
|||||||
import { splitLight } from '../split/styles'
|
import { splitLight } from '../split/styles'
|
||||||
import { flexLight } from '../flex/styles'
|
import { flexLight } from '../flex/styles'
|
||||||
import { floatButtonGroupLight } from '../float-button-group/styles'
|
import { floatButtonGroupLight } from '../float-button-group/styles'
|
||||||
|
import { marqueeLight } from '../marquee/styles'
|
||||||
import type { BuiltInGlobalTheme } from './interface'
|
import type { BuiltInGlobalTheme } from './interface'
|
||||||
|
|
||||||
export const lightTheme: BuiltInGlobalTheme = {
|
export const lightTheme: BuiltInGlobalTheme = {
|
||||||
@ -177,5 +178,6 @@ export const lightTheme: BuiltInGlobalTheme = {
|
|||||||
Watermark: watermarkLight,
|
Watermark: watermarkLight,
|
||||||
Split: splitLight,
|
Split: splitLight,
|
||||||
FloatButton: floatButtonLight,
|
FloatButton: floatButtonLight,
|
||||||
FloatButtonGroup: floatButtonGroupLight
|
FloatButtonGroup: floatButtonGroupLight,
|
||||||
|
Marquee: marqueeLight
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user