feat(spin): add delay prop (#4781)

* feat(spin): add delay support

* feat(spin): add demo for spin delay

* test(spin): add tests for spinning

* doc: add change log
This commit is contained in:
Kevin Law 2023-06-14 00:08:54 +08:00 committed by GitHub
parent 396eb816fd
commit 9c84e32994
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 142 additions and 4 deletions

View File

@ -51,11 +51,11 @@
- `n-data-table` adds `titleAlign` prop, closes [#3954](https://github.com/tusen-ai/naive-ui/issues/3954).
- `n-rate` exposes `index` in the default slot, closes [#4413](https://github.com/tusen-ai/naive-ui/issues/4413).
- `n-scrollbar` adds `size` prop, closes [#3896](https://github.com/tusen-ai/naive-ui/issues/3896).
- `n-select` adds `keyboard` prop, closes [#4340](https://github.com/tusen-ai/naive-ui/issues/4340).
- `n-data-table`'s `render-expand-icon` add `expanded` param, closes [#4439](https://github.com/tusen-ai/naive-ui/issues/4439).
- `n-tabs` adds `pane-wrapper-class` `pane-wrapper-style` prop.
- `n-collapse` adds `titlePadding` theme variable, closes [#4728](https://github.com/tusen-ai/naive-ui/issues/4728).
- `n-tabs` adds `placement` prop.
- `n-spin` adds `delay` prop.
### i18n

View File

@ -57,6 +57,7 @@
- `n-tabs` 新增 `pane-wrapper-class` `pane-wrapper-style` 属性
- `n-collapse` 新增 `titlePadding` 主题变量,关闭 [#4728](https://github.com/tusen-ai/naive-ui/issues/4728)
- `n-tabs` 新增 `placement` 属性
- `n-spin` 新增 `delay` 属性,
### i18n

View File

@ -0,0 +1,30 @@
<markdown>
# Delay
You can specify a delay for displaying spin. If spinning ends during delay, spin won't appear.
</markdown>
<template>
<n-space vertical>
<n-spin :show="show" :delay="1000">
<n-alert title="La La La" type="success">
Leave it till tomorrow to unpack my case. Honey disconnect the phone.
</n-alert>
</n-spin>
<n-button @click="show = !show">
{{ show ? 'Click to stop Spin' : 'Click to Spin' }}
</n-button>
</n-space>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue'
export default defineComponent({
setup () {
return {
show: ref(false)
}
}
})
</script>

View File

@ -9,6 +9,7 @@ basic.vue
wrap.vue
description.vue
customize-icon.vue
delay.vue
```
## API
@ -23,6 +24,7 @@ customize-icon.vue
| show | `boolean` | `true` | Specify whether spin is active when spin has content inside. It won't work if you just use spin itself. |
| stroke-width | `number` | `undefined` | Relative width of spin's stroke, assuming the outer radius of spin is 100. |
| stroke | `string` | `undefined` | Color of the spin. |
| delay | `number` | `undefined` | Specifies a delay in milliseconds for loading state (prevent flush). |
### Spin Slots

View File

@ -0,0 +1,30 @@
<markdown>
# 延迟
你可以设置一个显示延迟时间在延迟时间到达前结束Spin 将不会显示
</markdown>
<template>
<n-space vertical>
<n-spin :show="show" :delay="1000">
<n-alert title="La La La" type="success">
明天再打开行李箱宝贝挂电话啦
</n-alert>
</n-spin>
<n-button @click="show = !show">
{{ show ? '不要再转啦' : '点击来转圈' }}
</n-button>
</n-space>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue'
export default defineComponent({
setup () {
return {
show: ref(false)
}
}
})
</script>

View File

@ -9,6 +9,7 @@ basic.vue
wrap.vue
description.vue
customize-icon.vue
delay.vue
```
## API
@ -23,6 +24,7 @@ customize-icon.vue
| show | `boolean` | `true` | 在填入内容的情况下 Spin 是否激活,直接使用 Spin 时不生效 |
| stroke-width | `number` | `undefined` | Spin 边缘的相对宽度,假定 Spin 的外侧半径是 100 |
| stroke | `string` | `undefined` | Spin 的颜色 |
| delay | `number` | `undefined` | 延迟显示加载效果的时间, 单位为毫秒(避免闪烁) |
### Spin Slots

View File

@ -5,6 +5,7 @@ import {
Transition,
type PropType,
type CSSProperties,
ref,
watchEffect
} from 'vue'
import { useCompitable } from 'vooks'
@ -16,6 +17,7 @@ import { createKey, type ExtractPublicPropTypes, warnOnce } from '../../_utils'
import { spinLight } from '../styles'
import type { SpinTheme } from '../styles'
import style from './styles/index.cssr'
import { debounce, isNumber } from 'lodash-es'
const STROKE_WIDTH = {
small: 20,
@ -46,6 +48,10 @@ export const spinProps = {
return true
},
default: undefined
},
delay: {
type: Number,
default: undefined
}
}
@ -104,9 +110,29 @@ export default defineComponent({
props
)
: undefined
const shouldDelay = computed(
() => isNumber(props.delay) && props.delay !== 0
)
const compitableShow = useCompitable(props, ['spinning', 'show'])
const spanning = ref(false)
watchEffect((onCleanup) => {
if (compitableShow.value && shouldDelay.value) {
const debouncedShow = debounce(
() => (spanning.value = true),
props.delay
)
debouncedShow()
onCleanup(() => debouncedShow.cancel())
} else {
spanning.value = compitableShow.value
}
})
return {
mergedClsPrefix: mergedClsPrefixRef,
compitableShow: useCompitable(props, ['spinning', 'show']),
spanning,
mergedStrokeWidth: computed(() => {
const { strokeWidth } = props
if (strokeWidth !== undefined) return strokeWidth
@ -160,14 +186,14 @@ export default defineComponent({
<div
class={[
`${mergedClsPrefix}-spin-content`,
this.compitableShow && `${mergedClsPrefix}-spin-content--spinning`
this.spanning && `${mergedClsPrefix}-spin-content--spinning`
]}
>
{$slots}
</div>
<Transition name="fade-in-transition">
{{
default: () => (this.compitableShow ? icon : null)
default: () => (this.spanning ? icon : null)
}}
</Transition>
</div>

View File

@ -90,4 +90,51 @@ describe('n-spin', () => {
expect(wrapper.find('circle').attributes('stroke-width')).toEqual('40')
wrapper.unmount()
})
it('should work with `delay` prop', async () => {
const wrapper = mount(NSpin, {
props: {
show: true,
delay: 1000
},
slots: {
default: () => 'test'
}
})
expect(wrapper.find('.n-spin-content').classes()).not.toContain(
'n-spin-content--spinning'
)
await new Promise<void>((resolve) => setTimeout(() => resolve(), 1000))
expect(wrapper.find('.n-spin-content').classes()).toContain(
'n-spin-content--spinning'
)
})
it('should `delay` prop not delay close spin', async () => {
const wrapper = mount(NSpin, {
props: {
show: true,
delay: 1000
},
slots: {
default: () => 'test'
}
})
expect(wrapper.find('.n-spin-content').classes()).not.toContain(
'n-spin-content--spinning'
)
await new Promise<void>((resolve) => setTimeout(() => resolve(), 1000))
expect(wrapper.find('.n-spin-content').classes()).toContain(
'n-spin-content--spinning'
)
await wrapper.setProps({
show: false
})
expect(wrapper.find('.n-spin-content').classes()).not.toContain(
'n-spin-content--spinning'
)
})
})