feat: add skeleton

This commit is contained in:
wangxueliang 2018-12-10 11:34:51 +08:00
parent a04bdc6acf
commit 1859364a8a
23 changed files with 1039 additions and 1 deletions

View File

@ -122,6 +122,8 @@ import { default as version } from './version'
import { default as Drawer } from './drawer'
import { default as Skeleton } from './skeleton'
const components = [
Affix,
Anchor,
@ -174,6 +176,7 @@ const components = [
Tooltip,
Upload,
Drawer,
Skeleton,
]
const install = function (Vue) {
@ -251,6 +254,7 @@ export {
Tooltip,
Upload,
Drawer,
Skeleton,
}
export default {

View File

@ -0,0 +1,37 @@
import classNames from 'classnames'
import PropTypes from '../_util/vue-types'
import { initDefaultProps } from '../_util/props-util'
const skeletonAvatarProps = {
prefixCls: PropTypes.string,
size: PropTypes.oneOf(['large', 'small', 'default']),
shape: PropTypes.oneOf(['circle', 'square']),
}
export const SkeletonAvatarProps = PropTypes.shape(skeletonAvatarProps).loose
const Avatar = {
props: initDefaultProps(skeletonAvatarProps, {
prefixCls: 'ant-skeleton-avatar',
size: 'large',
}),
render () {
const { prefixCls, size, shape } = this.$props
const sizeCls = classNames({
[`${prefixCls}-lg`]: size === 'large',
[`${prefixCls}-sm`]: size === 'small',
})
const shapeCls = classNames({
[`${prefixCls}-circle`]: shape === 'circle',
[`${prefixCls}-square`]: shape === 'square',
})
return (
<span class={classNames(prefixCls, sizeCls, shapeCls)} />
)
},
}
export default Avatar

View File

@ -0,0 +1,53 @@
import PropTypes from '../_util/vue-types'
import { initDefaultProps } from '../_util/props-util'
const widthUnit = PropTypes.oneOfType([
PropTypes.number,
PropTypes.string,
])
const skeletonParagraphProps = {
prefixCls: PropTypes.string,
width: PropTypes.oneOfType([
widthUnit,
PropTypes.arrayOf(widthUnit),
]),
rows: PropTypes.number,
}
export const SkeletonParagraphProps = PropTypes.shape(skeletonParagraphProps)
const Paragraph = {
props: initDefaultProps(skeletonParagraphProps, {
prefixCls: 'ant-skeleton-paragraph',
}),
methods: {
getWidth (index) {
const { width, rows = 2 } = this
if (Array.isArray(width)) {
return width[index]
}
// last paragraph
if (rows - 1 === index) {
return width
}
return undefined
},
},
render () {
const { prefixCls, rows } = this.$props
const rowList = [...Array(rows)].map((_, index) => {
const width = this.getWidth(index)
return <li key={index} style={{ width: typeof width === 'number' ? `${width}px` : width }} />
})
return (
<ul
class={prefixCls}
>
{rowList}
</ul>
)
},
}
export default Paragraph

View File

@ -0,0 +1,30 @@
import PropTypes from '../_util/vue-types'
import { initDefaultProps } from '../_util/props-util'
const skeletonTitleProps = {
prefixCls: PropTypes.string,
width: PropTypes.oneOfType([
PropTypes.number,
PropTypes.string,
]),
}
export const SkeletonTitleProps = PropTypes.shape(skeletonTitleProps)
const Title = {
props: initDefaultProps(skeletonTitleProps, {
prefixCls: 'ant-skeleton-title',
}),
render () {
const { prefixCls, width } = this.$props
const zWidth = typeof width === 'number' ? `${width}px` : width
return (
<h3
class={prefixCls}
style={{ width: zWidth }}
/>
)
},
}
export default Title

View File

@ -0,0 +1,104 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders ./components/skeleton/demo/active.md correctly 1`] = `
<div class="ant-skeleton ant-skeleton-active">
<div class="ant-skeleton-content">
<h3 class="ant-skeleton-title" style="width: 38%;"></h3>
<ul class="ant-skeleton-paragraph">
<li></li>
<li></li>
<li style="width: 61%;"></li>
</ul>
</div>
</div>
`;
exports[`renders ./components/skeleton/demo/basic.md correctly 1`] = `
<div class="ant-skeleton">
<div class="ant-skeleton-content">
<h3 class="ant-skeleton-title" style="width: 38%;"></h3>
<ul class="ant-skeleton-paragraph">
<li></li>
<li></li>
<li style="width: 61%;"></li>
</ul>
</div>
</div>
`;
exports[`renders ./components/skeleton/demo/children.md correctly 1`] = `
<div class="article">
<div>
<h4>Ant Design Vue, a design language</h4>
<p>We supply a series of design principles, practical patterns and high quality design resources (Sketch and Axure), to help people create their product prototypes beautifully and efficiently.</p>
</div> <button type="button" class="ant-btn ant-btn-default"><span>Show Skeleton</span></button>
</div>
`;
exports[`renders ./components/skeleton/demo/complex.md correctly 1`] = `
<div class="ant-skeleton ant-skeleton-with-avatar">
<div class="ant-skeleton-header"><span class="ant-skeleton-avatar ant-skeleton-avatar-lg ant-skeleton-avatar-circle"></span></div>
<div class="ant-skeleton-content">
<h3 class="ant-skeleton-title" style="width: 50%;"></h3>
<ul class="ant-skeleton-paragraph">
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
</div>
</div>
`;
exports[`renders ./components/skeleton/demo/list.md correctly 1`] = `
<div><span tabindex="0" class="ant-switch"><span class="ant-switch-inner"></span></span>
<div class="ant-list ant-list-vertical ant-list-lg ant-list-split">
<div class="ant-spin-nested-loading">
<div class="ant-spin-container">
<div class="ant-list-item">
<div class="ant-list-item-content ant-list-item-content-single">
<div class="ant-skeleton ant-skeleton-with-avatar ant-skeleton-active">
<div class="ant-skeleton-header"><span class="ant-skeleton-avatar ant-skeleton-avatar-lg ant-skeleton-avatar-circle"></span></div>
<div class="ant-skeleton-content">
<h3 class="ant-skeleton-title" style="width: 50%;"></h3>
<ul class="ant-skeleton-paragraph">
<li></li>
<li></li>
</ul>
</div>
</div>
</div>
</div>
<div class="ant-list-item">
<div class="ant-list-item-content ant-list-item-content-single">
<div class="ant-skeleton ant-skeleton-with-avatar ant-skeleton-active">
<div class="ant-skeleton-header"><span class="ant-skeleton-avatar ant-skeleton-avatar-lg ant-skeleton-avatar-circle"></span></div>
<div class="ant-skeleton-content">
<h3 class="ant-skeleton-title" style="width: 50%;"></h3>
<ul class="ant-skeleton-paragraph">
<li></li>
<li></li>
</ul>
</div>
</div>
</div>
</div>
<div class="ant-list-item">
<div class="ant-list-item-content ant-list-item-content-single">
<div class="ant-skeleton ant-skeleton-with-avatar ant-skeleton-active">
<div class="ant-skeleton-header"><span class="ant-skeleton-avatar ant-skeleton-avatar-lg ant-skeleton-avatar-circle"></span></div>
<div class="ant-skeleton-content">
<h3 class="ant-skeleton-title" style="width: 50%;"></h3>
<ul class="ant-skeleton-paragraph">
<li></li>
<li></li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
`;

View File

@ -0,0 +1,120 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Skeleton avatar shape 1`] = `
<div class="ant-skeleton ant-skeleton-with-avatar">
<div class="ant-skeleton-header"><span class="ant-skeleton-avatar ant-skeleton-avatar-lg ant-skeleton-avatar-circle"></span></div>
<div class="ant-skeleton-content">
<h3 class="ant-skeleton-title" style="width: 50%;"></h3>
<ul class="ant-skeleton-paragraph">
<li></li>
<li></li>
</ul>
</div>
</div>
`;
exports[`Skeleton avatar shape 2`] = `
<div class="ant-skeleton ant-skeleton-with-avatar">
<div class="ant-skeleton-header"><span class="ant-skeleton-avatar ant-skeleton-avatar-lg ant-skeleton-avatar-square"></span></div>
<div class="ant-skeleton-content">
<h3 class="ant-skeleton-title" style="width: 50%;"></h3>
<ul class="ant-skeleton-paragraph">
<li></li>
<li></li>
</ul>
</div>
</div>
`;
exports[`Skeleton avatar size 1`] = `
<div class="ant-skeleton ant-skeleton-with-avatar">
<div class="ant-skeleton-header"><span class="ant-skeleton-avatar ant-skeleton-avatar-sm ant-skeleton-avatar-circle"></span></div>
<div class="ant-skeleton-content">
<h3 class="ant-skeleton-title" style="width: 50%;"></h3>
<ul class="ant-skeleton-paragraph">
<li></li>
<li></li>
</ul>
</div>
</div>
`;
exports[`Skeleton avatar size 2`] = `
<div class="ant-skeleton ant-skeleton-with-avatar">
<div class="ant-skeleton-header"><span class="ant-skeleton-avatar ant-skeleton-avatar-circle"></span></div>
<div class="ant-skeleton-content">
<h3 class="ant-skeleton-title" style="width: 50%;"></h3>
<ul class="ant-skeleton-paragraph">
<li></li>
<li></li>
</ul>
</div>
</div>
`;
exports[`Skeleton avatar size 3`] = `
<div class="ant-skeleton ant-skeleton-with-avatar">
<div class="ant-skeleton-header"><span class="ant-skeleton-avatar ant-skeleton-avatar-lg ant-skeleton-avatar-circle"></span></div>
<div class="ant-skeleton-content">
<h3 class="ant-skeleton-title" style="width: 50%;"></h3>
<ul class="ant-skeleton-paragraph">
<li></li>
<li></li>
</ul>
</div>
</div>
`;
exports[`Skeleton paragraph rows 1`] = `
<div class="ant-skeleton">
<div class="ant-skeleton-content">
<h3 class="ant-skeleton-title" style="width: 38%;"></h3>
<ul class="ant-skeleton-paragraph">
<li></li>
<li></li>
<li></li>
<li></li>
<li style="width: 61%;"></li>
</ul>
</div>
</div>
`;
exports[`Skeleton paragraph width 1`] = `
<div class="ant-skeleton">
<div class="ant-skeleton-content">
<h3 class="ant-skeleton-title" style="width: 38%;"></h3>
<ul class="ant-skeleton-paragraph">
<li></li>
<li></li>
<li style="width: 93%;"></li>
</ul>
</div>
</div>
`;
exports[`Skeleton paragraph width 2`] = `
<div class="ant-skeleton">
<div class="ant-skeleton-content">
<h3 class="ant-skeleton-title" style="width: 38%;"></h3>
<ul class="ant-skeleton-paragraph">
<li style="width: 28%;"></li>
<li style="width: 93%;"></li>
<li></li>
</ul>
</div>
</div>
`;
exports[`Skeleton title width 1`] = `
<div class="ant-skeleton">
<div class="ant-skeleton-content">
<h3 class="ant-skeleton-title" style="width: 93%;"></h3>
<ul class="ant-skeleton-paragraph">
<li></li>
<li></li>
<li style="width: 61%;"></li>
</ul>
</div>
</div>
`;

View File

@ -0,0 +1,3 @@
import demoTest from '../../../tests/shared/demoTest'
demoTest('skeleton')

View File

@ -0,0 +1,82 @@
import { mount } from '@vue/test-utils'
import { asyncExpect } from '@/tests/utils'
import Skeleton from '..'
describe('Skeleton', () => {
const genSkeleton = props => {
const skeletonProps = {
propsData: {
loading: true,
...props,
},
slots: {
default: 'Bamboo',
},
sync: false,
}
return mount(Skeleton, skeletonProps)
}
describe('avatar', () => {
it('size', async () => {
const wrapperSmall = genSkeleton({ avatar: { size: 'small' }})
await asyncExpect(() => {
expect(wrapperSmall.html()).toMatchSnapshot()
})
const wrapperDefault = genSkeleton({ avatar: { size: 'default' }})
await asyncExpect(() => {
expect(wrapperDefault.html()).toMatchSnapshot()
})
const wrapperLarge = genSkeleton({ avatar: { size: 'large' }})
await asyncExpect(() => {
expect(wrapperLarge.html()).toMatchSnapshot()
})
})
it('shape', async () => {
const wrapperCircle = genSkeleton({ avatar: { shape: 'circle' }})
await asyncExpect(() => {
expect(wrapperCircle.html()).toMatchSnapshot()
})
const wrapperSquare = genSkeleton({ avatar: { shape: 'square' }})
await asyncExpect(() => {
expect(wrapperSquare.html()).toMatchSnapshot()
})
})
})
describe('title', () => {
it('width', async () => {
const wrapper = genSkeleton({ title: { width: '93%' }})
await asyncExpect(() => {
expect(wrapper.html()).toMatchSnapshot()
})
})
})
describe('paragraph', () => {
it('rows', async () => {
const wrapper = genSkeleton({ paragraph: { rows: 5 }})
await asyncExpect(() => {
expect(wrapper.html()).toMatchSnapshot()
})
})
it('width', async () => {
const wrapperPure = genSkeleton({ paragraph: { width: '93%' }})
await asyncExpect(() => {
expect(wrapperPure.html()).toMatchSnapshot()
})
const wrapperList = genSkeleton({ paragraph: { width: ['28%', '93%'] }})
await asyncExpect(() => {
expect(wrapperList.html()).toMatchSnapshot()
})
})
})
})

View File

@ -0,0 +1,16 @@
<cn>
#### 动画效果
显示动画效果。
</cn>
<us>
#### Active Animation
Display active animation.
</us>
```html
<template>
<a-skeleton active />
</template>
```

View File

@ -0,0 +1,15 @@
<cn>
#### 基本
最简单的占位效果。
</cn>
<us>
#### Basic
Simplest Skeleton usage.
</us>
```html
<template>
<a-skeleton />
</template>
```

View File

@ -0,0 +1,51 @@
<cn>
#### 包含子组件
加载占位图包含子组件。
</cn>
<us>
#### Contains sub component
Skeleton contains sub component.
</us>
```html
<template>
<div class="article">
<a-skeleton :loading="loading">
<div>
<h4>Ant Design Vue, a design language</h4>
<p>We supply a series of design principles, practical patterns and high quality design resources (Sketch and Axure), to help people create their product prototypes beautifully and efficiently.</p>
</div>
</a-skeleton>
<a-button @click="showSkeleton" :disabled="loading">
Show Skeleton
</a-button>
</div>
</template>
<script>
export default {
data() {
return {
loading: false,
}
},
methods: {
showSkeleton() {
this.loading = true
setTimeout(() => {
this.loading = false
}, 3000);
}
}
}
</script>
<style>
.article h4 {
margin-bottom: 16px;
}
.article button {
margin-top: 16px;
}
</style>
```

View File

@ -0,0 +1,16 @@
<cn>
#### 复杂的组合
更复杂的组合。
</cn>
<us>
#### Complex combination
Complex combination with avatar and multiple paragraphs.
</us>
```html
<template>
<a-skeleton avatar :paragraph="{rows: 4}" />
</template>
```

View File

@ -0,0 +1,62 @@
<script>
import Basic from './basic.md'
import Active from './active.md'
import Children from './children.md'
import Complex from './complex.md'
import List from './list.md'
import CN from '../index.zh-CN.md'
import US from '../index.en-US.md'
const md = {
cn: `# 加载占位图
在需要等待加载内容的位置提供一个占位图
## 何时使用
- 网络较慢需要长时间等待加载处理的情况下
- 图文信息内容较多的列表/卡片中
## 代码演示`,
us: `# Skeleton
Provide a placeholder at the place which need waiting for loading.
# When To Use
- When resource needs long time to load, like low network speed.
- The component contains much information. Such as List or Card.
## Examples
`,
}
export default {
category: 'Components',
subtitle: '加载占位图',
type: 'Feedback',
title: 'Skeleton',
cols: 1,
render () {
return (
<div>
<md cn={md.cn} us={md.us}/>
<br/>
<Basic/>
<br />
<Complex />
<br />
<Active />
<br />
<Children />
<br />
<List />
<br/>
<api>
<template slot='cn'>
<CN/>
</template>
<US/>
</api>
</div>
)
},
}
</script>

View File

@ -0,0 +1,79 @@
<cn>
#### 列表
在列表组件中使用加载占位符。
</cn>
<us>
#### List
Use skeleton in list component.
</us>
```html
<template>
<div>
<a-switch :checked="!loading" @change="onChange" />
<a-list
itemLayout="vertical"
size="large"
:dataSource="listData"
>
<a-list-item slot="renderItem" slot-scope="item, index" key="item.title">
<template v-if="!loading" slot="actions" v-for="{type, text} in actions">
<span :key="type">
<a-icon :type="type" style="margin-right: 8px" />
{{text}}
</span>
</template>
<img v-if="!loading" slot="extra" width="272" alt="logo" src="https://gw.alipayobjects.com/zos/rmsportal/mqaQswcyDLcXyDKnZfES.png" />
<a-skeleton :loading="loading" active avatar>
<a-list-item-meta
:description="item.description"
>
<a slot="title" :href="item.href">{{item.title}}</a>
<a-avatar slot="avatar" :src="item.avatar" />
</a-list-item-meta>
{{item.content}}
</a-skeleton>
</a-list-item>
</a-list>
</div>
</template>
<script>
const listData = [];
for (let i = 0; i < 3; i++) {
listData.push({
href: 'https://vuecomponent.github.io/ant-design-vue/',
title: `ant design vue part ${i}`,
avatar: 'https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png',
description: 'Ant Design, a design language for background applications, is refined by Ant UED Team.',
content: 'We supply a series of design principles, practical patterns and high quality design resources (Sketch and Axure), to help people create their product prototypes beautifully and efficiently.',
});
}
export default {
data() {
return {
loading: true,
listData,
actions: [
{ type: 'star-o', text: '156' },
{ type: 'like-o', text: '156' },
{ type: 'message', text: '2' },
],
}
},
methods: {
onChange(checked) {
this.loading = !checked
}
}
}
</script>
<style>
.skeleton-demo {
border: 1px solid #f4f4f4;
}
</style>
```

View File

@ -0,0 +1,31 @@
## API
### Skeleton
| Property | Description | Type | Default |
| --- | --- | --- | --- |
| active | Show animation effect | boolean | false |
| avatar | Show avatar placeholder | boolean \| [SkeletonAvatarProps](#SkeletonAvatarProps) | false |
| loading | Display the skeleton when `true` | boolean | - |
| paragraph | Show paragraph placeholder | boolean \| [SkeletonParagraphProps](#SkeletonParagraphProps) | true |
| title | Show title placeholder | boolean \| [SkeletonTitleProps](#SkeletonTitleProps) | true |
### SkeletonAvatarProps
| Property | Description | Type | Default |
| --- | --- | --- | --- |
| size | Set the size of avatar | Enum{ 'large', 'small', 'default' } | - |
| shape | Set the shape of avatar | Enum{ 'circle', 'square' } | - |
### SkeletonTitleProps
| Property | Description | Type | Default |
| --- | --- | --- | --- |
| width | Set the width of title | number \| string | - |
### SkeletonParagraphProps
| Property | Description | Type | Default |
| --- | --- | --- | --- |
| rows | Set the row count of paragraph | number | - |
| width | Set the width of paragraph. When width is an Array, it can set the width of each row. Otherwise only set the last row width | number \| string \| Array<number \| string> | - |

View File

@ -0,0 +1,171 @@
import classNames from 'classnames'
import PropTypes from '../_util/vue-types'
import { initDefaultProps, hasProp } from '../_util/props-util'
import Avatar, { SkeletonAvatarProps } from './Avatar'
import Title, { SkeletonTitleProps } from './Title'
import Paragraph, { SkeletonParagraphProps } from './Paragraph'
export const SkeletonProps = {
active: PropTypes.bool,
loading: PropTypes.bool,
prefixCls: PropTypes.string,
children: PropTypes.any,
avatar: PropTypes.oneOfType([
PropTypes.string,
SkeletonAvatarProps,
PropTypes.bool,
]),
title: PropTypes.oneOfType([
PropTypes.bool,
PropTypes.string,
SkeletonTitleProps,
]),
paragraph: PropTypes.oneOfType([
PropTypes.bool,
PropTypes.string,
SkeletonParagraphProps,
]),
}
function getComponentProps (prop) {
if (prop && typeof prop === 'object') {
return prop
}
return {}
}
function getAvatarBasicProps (hasTitle, hasParagraph) {
if (hasTitle && !hasParagraph) {
return { shape: 'square' }
}
return { shape: 'circle' }
}
function getTitleBasicProps (hasAvatar, hasParagraph) {
if (!hasAvatar && hasParagraph) {
return { width: '38%' }
}
if (hasAvatar && hasParagraph) {
return { width: '50%' }
}
return {}
}
function getParagraphBasicProps (hasAvatar, hasTitle) {
const basicProps = {}
// Width
if (!hasAvatar || !hasTitle) {
basicProps.width = '61%'
}
// Rows
if (!hasAvatar && hasTitle) {
basicProps.rows = 3
} else {
basicProps.rows = 2
}
return basicProps
}
const Skeleton = {
name: 'ASkeleton',
props: initDefaultProps(SkeletonProps, {
prefixCls: 'ant-skeleton',
avatar: false,
title: true,
paragraph: true,
}),
render () {
const {
loading, prefixCls,
avatar, title, paragraph, active,
} = this.$props
if (loading || !hasProp(this, 'loading')) {
const hasAvatar = !!avatar || avatar === ''
const hasTitle = !!title
const hasParagraph = !!paragraph
// Avatar
let avatarNode
if (hasAvatar) {
const avatarProps = {
props: {
...getAvatarBasicProps(hasTitle, hasParagraph),
...getComponentProps(avatar),
},
}
avatarNode = (
<div class={`${prefixCls}-header`}>
<Avatar {...avatarProps} />
</div>
)
}
let contentNode
if (hasTitle || hasParagraph) {
// Title
let $title
if (hasTitle) {
const titleProps = {
props: {
...getTitleBasicProps(hasAvatar, hasParagraph),
...getComponentProps(title),
},
}
$title = (
<Title {...titleProps} />
)
}
// Paragraph
let paragraphNode
if (hasParagraph) {
const paragraphProps = {
props: {
...getParagraphBasicProps(hasAvatar, hasTitle),
...getComponentProps(paragraph),
},
}
paragraphNode = (
<Paragraph {...paragraphProps} />
)
}
contentNode = (
<div class={`${prefixCls}-content`}>
{$title}
{paragraphNode}
</div>
)
}
const cls = classNames(
prefixCls, {
[`${prefixCls}-with-avatar`]: hasAvatar,
[`${prefixCls}-active`]: active,
},
)
return (
<div class={cls}>
{avatarNode}
{contentNode}
</div>
)
}
return this.$slots.default && this.$slots.default[0]
},
}
/* istanbul ignore next */
Skeleton.install = function (Vue) {
Vue.component(Skeleton.name, Skeleton)
}
export default Skeleton

View File

@ -0,0 +1,31 @@
## API
### Skeleton
| 属性 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| active | 是否展示动画效果 | boolean | false |
| avatar | 是否显示头像占位图 | boolean \| [SkeletonAvatarProps](#SkeletonAvatarProps) | false |
| loading | 为 `true` 时,显示占位图。反之则直接展示子组件 | boolean | - |
| paragraph | 是否显示段落占位图 | boolean \| [SkeletonParagraphProps](#SkeletonParagraphProps) | true |
| title | 是否显示标题占位图 | boolean \| [SkeletonTitleProps](#SkeletonTitleProps) | true |
### SkeletonAvatarProps
| 属性 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| size | 设置头像占位图的大小 | Enum{ 'large', 'small', 'default' } | - |
| shape | 指定头像的形状 | Enum{ 'circle', 'square' } | - |
### SkeletonTitleProps
| 属性 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| width | 设置标题占位图的宽度 | number \| string | - |
### SkeletonParagraphProps
| 属性 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| rows | 设置段落占位图的行数 | number | - |
| width | 设置段落占位图的宽度,若为数组时则为对应的每行宽度,反之则是最后一行的宽度 | number \| string \| Array<number \| string> | - |

View File

@ -0,0 +1,2 @@
import '../../style/index.less';
import './index.less';

View File

@ -0,0 +1,123 @@
@import "../../style/themes/default";
@import "../../style/mixins/index";
@skeleton-prefix-cls: ~"@{ant-prefix}-skeleton";
@skeleton-avatar-prefix-cls: ~"@{skeleton-prefix-cls}-avatar";
@skeleton-title-prefix-cls: ~"@{skeleton-prefix-cls}-title";
@skeleton-paragraph-prefix-cls: ~"@{skeleton-prefix-cls}-paragraph";
@skeleton-to-color: shade(@skeleton-color, 5%);
.@{skeleton-prefix-cls} {
display: table;
width: 100%;
&-header {
display: table-cell;
vertical-align: top;
padding-right: 16px;
// Avatar
.@{skeleton-avatar-prefix-cls} {
display: inline-block;
vertical-align: top;
background: @skeleton-color;
.avatar-size(@avatar-size-base);
&-lg {
.avatar-size(@avatar-size-lg);
}
&-sm {
.avatar-size(@avatar-size-sm);
}
}
}
&-content {
display: table-cell;
vertical-align: top;
width: 100%;
// Title
.@{skeleton-title-prefix-cls} {
margin-top: 16px;
height: 16px;
width: 100%;
background: @skeleton-color;
+ .@{skeleton-paragraph-prefix-cls} {
margin-top: 24px;
}
}
// paragraph
.@{skeleton-paragraph-prefix-cls} {
> li {
height: 16px;
background: @skeleton-color;
list-style: none;
width: 100%;
&:last-child:not(:first-child):not(:nth-child(2)) {
width: 61%;
}
+ li {
margin-top: 16px;
}
}
}
}
&-with-avatar &-content {
// Title
.@{skeleton-title-prefix-cls} {
margin-top: 12px;
+ .@{skeleton-paragraph-prefix-cls} {
margin-top: 28px;
}
}
}
// With active animation
&.@{skeleton-prefix-cls}-active {
& .@{skeleton-prefix-cls}-content {
.@{skeleton-title-prefix-cls},
.@{skeleton-paragraph-prefix-cls} > li {
.skeleton-color();
}
}
.@{skeleton-avatar-prefix-cls} {
.skeleton-color();
}
}
}
.avatar-size(@size) {
width: @size;
height: @size;
line-height: @size;
&.@{skeleton-avatar-prefix-cls}-circle {
border-radius: 50%;
}
}
.skeleton-color() {
background: linear-gradient(90deg, @skeleton-color 25%, @skeleton-to-color 37%, @skeleton-color 63%);
animation: ~"@{skeleton-prefix-cls}-loading" 1.4s ease infinite;
background-size: 400% 100%;
}
@keyframes ~"@{skeleton-prefix-cls}-loading" {
0% {
background-position: 100% 50%;
}
100% {
background-position: 0 50%;
}
}

View File

@ -50,3 +50,4 @@ import './list/style'
import './carousel/style'
import './tree-select/style'
import './drawer/style'
import './skeleton/style'

View File

@ -354,6 +354,11 @@
@tag-default-color: @text-color;
@tag-font-size: @font-size-sm;
// Skeleton
// ---
@skeleton-color: #f2f2f2;
// TimePicker
// ---
@time-picker-panel-column-width: 56px;

View File

@ -180,4 +180,4 @@
"vue-ref": "^1.0.3",
"warning": "^3.0.0"
}
}
}

View File

@ -55,6 +55,7 @@ import {
Upload,
// version,
Drawer,
Skeleton,
} from 'ant-design-vue'
Vue.prototype.$message = message
@ -117,6 +118,7 @@ Vue.use(TimePicker)
Vue.use(Timeline)
Vue.use(Tooltip)
Vue.use(Upload)
Vue.use(Skeleton)
/* v1.1.2 registration methods */
// Vue.component(Affix.name, Affix) // a-affix