mirror of
https://github.com/tusen-ai/naive-ui.git
synced 2025-01-30 12:52:43 +08:00
feat(spin): icon slot, rotate prop (#289)
* feat(spin): add icon slot * doc(changelog * fix(spin): icon slot logic * test(spin): add icon slot test * fix(spin): refact code * feat(spin): add rotate prop Co-authored-by: wanli.song@tusimple.ai <wanli.song@tusimple.ai> Co-authored-by: 07akioni <07akioni2@gmail.com>
This commit is contained in:
parent
1a354081ba
commit
21698de271
@ -13,6 +13,8 @@
|
||||
- `n-upload` add `before-upload` prop.
|
||||
- `n-image` add `alt` prop.
|
||||
- Support the enter key on the numeric keypad.
|
||||
- `n-spin` support `icon` slot for icon customizing, closes[#260](https://github.com/TuSimple/naive-ui/issues/260).
|
||||
- `n-spin` add `rotate` prop fro slot icon to rotate.
|
||||
- `n-form` export `FormItemRule` & `FormRules` type.
|
||||
|
||||
### Fixes
|
||||
|
@ -13,6 +13,8 @@
|
||||
- `n-upload` 新增 `before-upload` 属性
|
||||
- `n-image` 新增 `alt` 属性.
|
||||
- 支持小键盘的 enter 键
|
||||
- `n-spin` 支持 `icon` 插槽为了自定义加载图标,closes[#260](https://github.com/TuSimple/naive-ui/issues/260)
|
||||
- `n-spin` 新增 `rotate` 属性控制自定义加载图标是否有旋转动画
|
||||
- `n-form` 导出 `FormItemRule` & `FormRules` 类型
|
||||
|
||||
### Fixes
|
||||
|
@ -33,13 +33,17 @@ export default defineComponent({
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
rotate: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
setup (props) {
|
||||
useStyle('BaseLoading', style, toRef(props, 'clsPrefix'))
|
||||
},
|
||||
render () {
|
||||
const { clsPrefix, radius, strokeWidth, stroke, scale } = this
|
||||
const { clsPrefix, radius, strokeWidth, stroke, scale, $slots } = this
|
||||
const scaledRadius = radius / scale
|
||||
return (
|
||||
<div class={`${clsPrefix}-base-loading`} role="img" aria-label="loading">
|
||||
@ -47,6 +51,13 @@ export default defineComponent({
|
||||
{{
|
||||
default: () =>
|
||||
this.show ? (
|
||||
$slots.icon ?
|
||||
<div class={[
|
||||
`${clsPrefix}-base-loading__icon-slot`,
|
||||
this.rotate && `${clsPrefix}-base-loading__icon-slot--rotate`
|
||||
]}>
|
||||
{$slots.icon()}
|
||||
</div> :
|
||||
<svg
|
||||
class={`${clsPrefix}-base-loading__icon`}
|
||||
viewBox={`0 0 ${2 * scaledRadius} ${2 * scaledRadius}`}
|
||||
|
@ -1,28 +1,44 @@
|
||||
import { cB, cE } from '../../../../_utils/cssr'
|
||||
import { c, cB, cE, cM } from '../../../../_utils/cssr'
|
||||
import iconSwitchTransition from '../../../../_styles/transitions/icon-switch.cssr'
|
||||
|
||||
export default cB('base-loading', `
|
||||
position: relative;
|
||||
line-height: 0;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
`, [
|
||||
cE('placeholder', {
|
||||
position: 'absolute',
|
||||
left: '50%',
|
||||
top: '50%',
|
||||
transform: 'translateX(-50%) translateY(-50%)'
|
||||
}, [
|
||||
iconSwitchTransition({
|
||||
export default c([
|
||||
c('@keyframes icon-rotate-animation', `
|
||||
0% {
|
||||
transform:rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform:rotate(360deg);
|
||||
}
|
||||
`),
|
||||
cB('base-loading', `
|
||||
position: relative;
|
||||
line-height: 0;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
`, [
|
||||
cE('placeholder', {
|
||||
position: 'absolute',
|
||||
left: '50%',
|
||||
top: '50%',
|
||||
originalTransform: 'translateX(-50%) translateY(-50%)'
|
||||
})
|
||||
]),
|
||||
cE('icon', `
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
`, [
|
||||
iconSwitchTransition()
|
||||
transform: 'translateX(-50%) translateY(-50%)'
|
||||
}, [
|
||||
iconSwitchTransition({
|
||||
left: '50%',
|
||||
top: '50%',
|
||||
originalTransform: 'translateX(-50%) translateY(-50%)'
|
||||
})
|
||||
]),
|
||||
cE('icon', `
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
`, [
|
||||
iconSwitchTransition()
|
||||
]),
|
||||
cE('icon-slot', [
|
||||
cM('rotate', `
|
||||
display: inline-block;
|
||||
animation: icon-rotate-animation 1s linear infinite;
|
||||
`)
|
||||
])
|
||||
])
|
||||
])
|
||||
|
40
src/spin/demos/enUS/customize-icon.demo.md
Normal file
40
src/spin/demos/enUS/customize-icon.demo.md
Normal file
@ -0,0 +1,40 @@
|
||||
# Customize Icon
|
||||
|
||||
```html
|
||||
<n-space vertical>
|
||||
<n-spin size="small">
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<Reload />
|
||||
</n-icon>
|
||||
</template>
|
||||
</n-spin>
|
||||
<n-spin :show="show">
|
||||
<n-alert title="La La La" type="success">
|
||||
Leave it till tomorrow to unpack my case. Honey disconnect the phone.
|
||||
</n-alert>
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<Reload />
|
||||
</n-icon>
|
||||
</template>
|
||||
</n-spin>
|
||||
<n-button @click="show = !show">Click to Spin</n-button>
|
||||
</n-space>
|
||||
```
|
||||
|
||||
```js
|
||||
import { ref, defineComponent } from 'vue'
|
||||
import { Reload } from '@vicons/ionicons5'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
Reload
|
||||
},
|
||||
setup () {
|
||||
return {
|
||||
show: ref(false)
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
@ -7,12 +7,14 @@ It can be called loading, but why it's called loading? Because there is another
|
||||
```demo
|
||||
basic
|
||||
wrap
|
||||
customize-icon
|
||||
```
|
||||
|
||||
## Props
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| rotate | `boolean` | `true` | If slot icon is rotate. |
|
||||
| size | `'small' \| 'medium' \| 'large' \| number` | `'medium'` | |
|
||||
| show | `boolean` | `true` | If spin is active. |
|
||||
| stroke-width | `number` | `undefined` | Relative width of spin's stroke, you can assume the outer radius of spin is 100. |
|
||||
@ -23,3 +25,4 @@ wrap
|
||||
| Name | Parameters | Description |
|
||||
| ------- | ---------- | ---------------------------------- |
|
||||
| default | `()` | If set, spin will wrap the content |
|
||||
| icon | `()` | Customize the spin icon |
|
||||
|
40
src/spin/demos/zhCN/customize-icon.demo.md
Normal file
40
src/spin/demos/zhCN/customize-icon.demo.md
Normal file
@ -0,0 +1,40 @@
|
||||
# 自定义图标
|
||||
|
||||
```html
|
||||
<n-space vertical>
|
||||
<n-spin size="small" >
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<Reload />
|
||||
</n-icon>
|
||||
</template>
|
||||
</n-spin>
|
||||
<n-spin :show="show">
|
||||
<n-alert title="La La La" type="success">
|
||||
明天再打开行李箱。宝贝,挂电话啦。
|
||||
</n-alert>
|
||||
<template #icon>
|
||||
<n-icon>
|
||||
<Reload />
|
||||
</n-icon>
|
||||
</template>
|
||||
</n-spin>
|
||||
<n-button @click="show = !show">点出来加载</n-button>
|
||||
</n-space>
|
||||
```
|
||||
|
||||
```js
|
||||
import { ref, defineComponent } from 'vue'
|
||||
import { Reload } from '@vicons/ionicons5'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
Reload
|
||||
},
|
||||
setup () {
|
||||
return {
|
||||
show: ref(false)
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
@ -7,12 +7,14 @@
|
||||
```demo
|
||||
basic
|
||||
wrap
|
||||
customize-icon
|
||||
```
|
||||
|
||||
## Props
|
||||
|
||||
| 名称 | 类型 | 默认值 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| rotate | `boolean` | `true` | 自定义加载图标是否有旋转动画 |
|
||||
| size | `'small' \| 'medium' \| 'large' \| number` | `'medium'` | |
|
||||
| show | `boolean` | `true` | Spin 在填入内容的状态是否激活 |
|
||||
| stroke-width | `number` | `undefined` | Spin 边缘的相对宽度,假定 Spin 的外侧半径是 100 |
|
||||
@ -23,3 +25,4 @@ wrap
|
||||
| 名称 | 参数 | 说明 |
|
||||
| ------- | ---- | ------------------------------- |
|
||||
| default | `()` | 如果填入,Spin 会包裹填入的内容 |
|
||||
| icon | `()` | 自定义加载图标 |
|
||||
|
@ -47,6 +47,10 @@ const spinProps = {
|
||||
return true
|
||||
},
|
||||
default: undefined
|
||||
},
|
||||
rotate: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
}
|
||||
|
||||
@ -117,8 +121,11 @@ export default defineComponent({
|
||||
clsPrefix={mergedClsPrefix}
|
||||
stroke={this.stroke}
|
||||
strokeWidth={this.mergedStrokeWidth}
|
||||
rotate={this.rotate}
|
||||
class={`${mergedClsPrefix}-spin`}
|
||||
/>
|
||||
>
|
||||
{{ icon: $slots.icon }}
|
||||
</NBaseLoading>
|
||||
) : null
|
||||
}}
|
||||
</Transition>
|
||||
@ -129,8 +136,11 @@ export default defineComponent({
|
||||
style={this.cssVars as CSSProperties}
|
||||
stroke={this.stroke}
|
||||
stroke-width={this.mergedStrokeWidth}
|
||||
rotate={this.rotate}
|
||||
class={`${mergedClsPrefix}-spin`}
|
||||
/>
|
||||
>
|
||||
{{ icon: $slots.icon }}
|
||||
</NBaseLoading>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
@ -1,8 +1,37 @@
|
||||
import { h } from '@vue/runtime-core'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { NSpin } from '../index'
|
||||
import { Reload } from '@vicons/ionicons5'
|
||||
import { NIcon } from '../../icon'
|
||||
|
||||
describe('n-spin', () => {
|
||||
it('should work with import on demand', () => {
|
||||
mount(NSpin)
|
||||
})
|
||||
it('should work with icon slot', () => {
|
||||
const wrapper = mount(NSpin, {
|
||||
slots: {
|
||||
icon: () => h(NIcon, null, {
|
||||
default: () => h(Reload)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
expect(wrapper.findComponent(NIcon).exists()).toBe(true)
|
||||
expect(wrapper.findComponent(Reload).exists()).toBe(true)
|
||||
})
|
||||
it('rotate should work on icon slot', async () => {
|
||||
const wrapper = mount(NSpin, {
|
||||
slots: {
|
||||
icon: () => h(NIcon, null, {
|
||||
default: () => h(Reload)
|
||||
})
|
||||
}
|
||||
})
|
||||
expect(wrapper.find('.n-base-loading__icon-slot--rotate').exists()).toBe(true)
|
||||
await wrapper.setProps({
|
||||
rotate: false
|
||||
})
|
||||
expect(wrapper.find('.n-base-loading__icon-slot--rotate').exists()).toBe(false)
|
||||
})
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user