refactor: skeleton

pull/4134/head
tanjinzhou 2021-05-28 13:16:59 +08:00
parent 5096ee4d70
commit c8898828d6
13 changed files with 631 additions and 259 deletions

View File

@ -2,7 +2,7 @@ import { App } from 'vue';
import Avatar from './Avatar';
import Group from './Group';
export { AvatarProps, AvatarSize } from './Avatar';
export { AvatarProps, AvatarSize, avatarProps } from './Avatar';
export { AvatarGroupProps } from './Group';
Avatar.Group = Group;

View File

@ -1,50 +1,40 @@
import { defineComponent, ExtractPropTypes } from 'vue';
import { computed, defineComponent } from 'vue';
import classNames from '../_util/classNames';
import PropTypes from '../_util/vue-types';
import { tuple } from '../_util/type';
import initDefaultProps from '../_util/props-util/initDefaultProps';
import useConfigInject from '../_util/hooks/useConfigInject';
import Element, { skeletonElementProps, SkeletonElementProps } from './Element';
const skeletonAvatarProps = {
prefixCls: PropTypes.string,
size: PropTypes.oneOfType([
PropTypes.oneOf(tuple('large', 'small', 'default')),
PropTypes.number,
]),
shape: PropTypes.oneOf(tuple('circle', 'square')),
};
export interface AvatarProps extends Omit<SkeletonElementProps, 'shape'> {
shape?: 'circle' | 'square';
}
export const SkeletonAvatarProps = PropTypes.shape(skeletonAvatarProps).loose;
export type ISkeletonAvatarProps = Partial<ExtractPropTypes<typeof skeletonAvatarProps>>;
const Avatar = defineComponent({
props: initDefaultProps(skeletonAvatarProps, {
export const avatarProps = initDefaultProps(
{ ...skeletonElementProps(), shape: PropTypes.oneOf(tuple('circle', 'square')) },
{
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',
});
const sizeStyle =
typeof size === 'number'
? {
width: `${size}px`,
height: `${size}px`,
lineHeight: `${size}px`,
}
: {};
return <span class={classNames(prefixCls, sizeCls, shapeCls)} style={sizeStyle} />;
const SkeletonAvatar = defineComponent({
name: 'ASkeletonAvatar',
props: avatarProps,
setup(props) {
const { prefixCls } = useConfigInject('skeleton', props);
const cls = computed(() =>
classNames(prefixCls.value, `${prefixCls.value}-element`, {
[`${prefixCls.value}-active`]: props.active,
}),
);
return () => {
return (
<div class={cls.value}>
<Element {...props} prefixCls={`${prefixCls.value}-avatar`} />
</div>
);
};
},
});
export default Avatar;
export default SkeletonAvatar;

View File

@ -0,0 +1,32 @@
import { computed, defineComponent } from 'vue';
import classNames from '../_util/classNames';
import PropTypes from '../_util/vue-types';
import { tuple } from '../_util/type';
import useConfigInject from '../_util/hooks/useConfigInject';
import Element, { skeletonElementProps, SkeletonElementProps } from './Element';
export interface SkeletonButtonProps extends Omit<SkeletonElementProps, 'size'> {
size?: 'large' | 'small' | 'default';
}
const SkeletonButton = defineComponent({
name: 'ASkeletonButton',
props: { ...skeletonElementProps(), size: PropTypes.oneOf(tuple('large', 'small', 'default')) },
setup(props) {
const { prefixCls } = useConfigInject('skeleton', props);
const cls = computed(() =>
classNames(prefixCls.value, `${prefixCls.value}-element`, {
[`${prefixCls.value}-active`]: props.active,
}),
);
return () => {
return (
<div class={cls.value}>
<Element {...props} prefixCls={`${prefixCls.value}-button`} />
</div>
);
};
},
});
export default SkeletonButton;

View File

@ -0,0 +1,47 @@
import { CSSProperties, ExtractPropTypes, FunctionalComponent } from '@vue/runtime-dom';
import classNames from '../_util/classNames';
import { tuple } from '../_util/type';
import PropTypes from '../_util/vue-types';
export const skeletonElementProps = () => ({
prefixCls: PropTypes.string,
size: PropTypes.oneOfType([
PropTypes.oneOf(tuple('large', 'small', 'default')),
PropTypes.number,
]),
shape: PropTypes.oneOf(tuple('circle', 'square', 'round')),
active: PropTypes.looseBool,
});
export type SkeletonElementProps = Partial<
ExtractPropTypes<ReturnType<typeof skeletonElementProps>>
>;
const Element: FunctionalComponent<SkeletonElementProps> = props => {
const { prefixCls, size, shape } = props;
const sizeCls = classNames({
[`${prefixCls}-lg`]: size === 'large',
[`${prefixCls}-sm`]: size === 'small',
});
const shapeCls = classNames({
[`${prefixCls}-circle`]: shape === 'circle',
[`${prefixCls}-square`]: shape === 'square',
[`${prefixCls}-round`]: shape === 'round',
});
const sizeStyle: CSSProperties =
typeof size === 'number'
? {
width: `${size}px`,
height: `${size}px`,
lineHeight: `${size}px`,
}
: {};
return <span class={classNames(prefixCls, sizeCls, shapeCls)} style={sizeStyle} />;
};
Element.displayName = 'SkeletonElement';
export default Element;

View File

@ -0,0 +1,36 @@
import { computed, defineComponent } from 'vue';
import classNames from '../_util/classNames';
import useConfigInject from '../_util/hooks/useConfigInject';
import { skeletonElementProps, SkeletonElementProps } from './Element';
export interface SkeletonImageProps
extends Omit<SkeletonElementProps, 'size' | 'shape' | 'active'> {}
const path =
'M365.714286 329.142857q0 45.714286-32.036571 77.677714t-77.677714 32.036571-77.677714-32.036571-32.036571-77.677714 32.036571-77.677714 77.677714-32.036571 77.677714 32.036571 32.036571 77.677714zM950.857143 548.571429l0 256-804.571429 0 0-109.714286 182.857143-182.857143 91.428571 91.428571 292.571429-292.571429zM1005.714286 146.285714l-914.285714 0q-7.460571 0-12.873143 5.412571t-5.412571 12.873143l0 694.857143q0 7.460571 5.412571 12.873143t12.873143 5.412571l914.285714 0q7.460571 0 12.873143-5.412571t5.412571-12.873143l0-694.857143q0-7.460571-5.412571-12.873143t-12.873143-5.412571zM1097.142857 164.571429l0 694.857143q0 37.741714-26.843429 64.585143t-64.585143 26.843429l-914.285714 0q-37.741714 0-64.585143-26.843429t-26.843429-64.585143l0-694.857143q0-37.741714 26.843429-64.585143t64.585143-26.843429l914.285714 0q37.741714 0 64.585143 26.843429t26.843429 64.585143z';
const SkeletonImage = defineComponent({
name: 'ASkeletonImage',
props: skeletonElementProps(),
setup(props) {
const { prefixCls } = useConfigInject('skeleton', props);
const cls = computed(() => classNames(prefixCls.value, `${prefixCls.value}-element`));
return () => {
return (
<div class={cls.value}>
<div class={`${prefixCls.value}-image`}>
<svg
viewBox="0 0 1098 1024"
xmlns="http://www.w3.org/2000/svg"
class={`${prefixCls.value}-image-svg`}
>
<path d={path} class={`${prefixCls.value}-image-path`} />
</svg>
</div>
</div>
);
};
},
});
export default SkeletonImage;

View File

@ -0,0 +1,36 @@
import { computed, defineComponent } from 'vue';
import classNames from '../_util/classNames';
import PropTypes from '../_util/vue-types';
import { tuple } from '../_util/type';
import useConfigInject from '../_util/hooks/useConfigInject';
import Element, { skeletonElementProps, SkeletonElementProps } from './Element';
import Omit from 'omit.js';
export interface SkeletonInputProps extends Omit<SkeletonElementProps, 'size' | 'shape'> {
size?: 'large' | 'small' | 'default';
}
const SkeletonInput = defineComponent({
name: 'ASkeletonInput',
props: {
...Omit(skeletonElementProps(), 'shape'),
size: PropTypes.oneOf(tuple('large', 'small', 'default')),
},
setup(props) {
const { prefixCls } = useConfigInject('skeleton', props);
const cls = computed(() =>
classNames(prefixCls.value, `${prefixCls.value}-element`, {
[`${prefixCls.value}-active`]: props.active,
}),
);
return () => {
return (
<div class={cls.value}>
<Element {...props} prefixCls={`${prefixCls.value}-input`} />
</div>
);
};
},
});
export default SkeletonInput;

View File

@ -3,21 +3,20 @@ import PropTypes from '../_util/vue-types';
const widthUnit = PropTypes.oneOfType([PropTypes.number, PropTypes.string]);
const skeletonParagraphProps = {
export const skeletonParagraphProps = {
prefixCls: PropTypes.string,
width: PropTypes.oneOfType([widthUnit, PropTypes.arrayOf(widthUnit)]),
rows: PropTypes.number,
};
export const SkeletonParagraphProps = PropTypes.shape(skeletonParagraphProps).loose;
export type SkeletonParagraphProps = Partial<ExtractPropTypes<typeof skeletonParagraphProps>>;
export type ISkeletonParagraphProps = Partial<ExtractPropTypes<typeof skeletonParagraphProps>>;
const Paragraph = defineComponent({
const SkeletonParagraph = defineComponent({
props: skeletonParagraphProps,
methods: {
getWidth(index: number) {
const { width, rows = 2 } = this;
name: 'SkeletonParagraph',
setup(props) {
const getWidth = (index: number) => {
const { width, rows = 2 } = props;
if (Array.isArray(width)) {
return width[index];
}
@ -26,16 +25,18 @@ const Paragraph = defineComponent({
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>;
};
return () => {
const { prefixCls, rows } = props;
const rowList = [...Array(rows)].map((_, index) => {
const width = getWidth(index);
return (
<li key={index} style={{ width: typeof width === 'number' ? `${width}px` : width }} />
);
});
return <ul class={prefixCls}>{rowList}</ul>;
};
},
});
export default Paragraph;
export default SkeletonParagraph;

View File

@ -0,0 +1,175 @@
import { defineComponent, ExtractPropTypes } from 'vue';
import classNames from '../_util/classNames';
import PropTypes, { withUndefined } from '../_util/vue-types';
import { initDefaultProps } from '../_util/props-util';
import { AvatarProps, avatarProps } from './Avatar';
import Title, { SkeletonTitleProps, skeletonTitleProps } from './Title';
import Paragraph, { SkeletonParagraphProps, skeletonParagraphProps } from './Paragraph';
import Omit from 'omit.js';
import useConfigInject from '../_util/hooks/useConfigInject';
import Element from './Element';
/* This only for skeleton internal. */
interface SkeletonAvatarProps extends Omit<AvatarProps, 'active'> {}
export const skeletonProps = {
active: PropTypes.looseBool,
loading: PropTypes.looseBool,
prefixCls: PropTypes.string,
avatar: withUndefined(
PropTypes.oneOfType([
PropTypes.string,
PropTypes.shape(Omit(avatarProps, ['active'])).loose,
PropTypes.looseBool,
]),
),
title: withUndefined(
PropTypes.oneOfType([
PropTypes.looseBool,
PropTypes.string,
PropTypes.shape(skeletonTitleProps).loose,
]),
),
paragraph: withUndefined(
PropTypes.oneOfType([
PropTypes.looseBool,
PropTypes.string,
PropTypes.shape(skeletonParagraphProps).loose,
]),
),
};
export type SkeletonProps = Partial<ExtractPropTypes<typeof skeletonProps>>;
function getComponentProps<T>(prop: T | boolean | undefined): T | {} {
if (prop && typeof prop === 'object') {
return prop;
}
return {};
}
function getAvatarBasicProps(hasTitle: boolean, hasParagraph: boolean): SkeletonAvatarProps {
if (hasTitle && !hasParagraph) {
// Square avatar
return { size: 'large', shape: 'square' };
}
return { size: 'large', shape: 'circle' };
}
function getTitleBasicProps(hasAvatar: boolean, hasParagraph: boolean): SkeletonTitleProps {
if (!hasAvatar && hasParagraph) {
return { width: '38%' };
}
if (hasAvatar && hasParagraph) {
return { width: '50%' };
}
return {};
}
function getParagraphBasicProps(hasAvatar: boolean, hasTitle: boolean): SkeletonParagraphProps {
const basicProps: SkeletonParagraphProps = {};
// Width
if (!hasAvatar || !hasTitle) {
basicProps.width = '61%';
}
// Rows
if (!hasAvatar && hasTitle) {
basicProps.rows = 3;
} else {
basicProps.rows = 2;
}
return basicProps;
}
const Skeleton = defineComponent({
name: 'ASkeleton',
props: initDefaultProps(skeletonProps, {
avatar: false,
title: true,
paragraph: true,
}),
setup(props, { slots }) {
const { prefixCls, direction } = useConfigInject('skeleton', props);
return () => {
const { loading, avatar, title, paragraph, active, round } = props;
const pre = prefixCls.value;
if (loading || props.loading === undefined) {
const hasAvatar = !!avatar || avatar === '';
const hasTitle = !!title || title === '';
const hasParagraph = !!paragraph || paragraph === '';
// Avatar
let avatarNode;
if (hasAvatar) {
const avatarProps = {
prefixCls: `${pre}-avatar`,
...getAvatarBasicProps(hasTitle, hasParagraph),
...getComponentProps(avatar),
};
avatarNode = (
<div class={`${pre}-header`}>
<Element {...avatarProps} />
</div>
);
}
let contentNode;
if (hasTitle || hasParagraph) {
// Title
let $title;
if (hasTitle) {
const titleProps = {
prefixCls: `${pre}-title`,
...getTitleBasicProps(hasAvatar, hasParagraph),
...getComponentProps(title),
};
$title = <Title {...titleProps} />;
}
// Paragraph
let paragraphNode;
if (hasParagraph) {
const paragraphProps = {
prefixCls: `${pre}-paragraph`,
...getParagraphBasicProps(hasAvatar, hasTitle),
...getComponentProps(paragraph),
};
paragraphNode = <Paragraph {...paragraphProps} />;
}
contentNode = (
<div class={`${pre}-content`}>
{$title}
{paragraphNode}
</div>
);
}
const cls = classNames(pre, {
[`${pre}-with-avatar`]: hasAvatar,
[`${pre}-active`]: active,
[`${pre}-rtl`]: direction.value === 'rtl',
[`${pre}-round`]: round,
});
return (
<div class={cls}>
{avatarNode}
{contentNode}
</div>
);
}
return slots.default?.();
};
},
});
export default Skeleton;

View File

@ -1,22 +1,23 @@
import { defineComponent, ExtractPropTypes } from 'vue';
import PropTypes from '../_util/vue-types';
const skeletonTitleProps = {
export const skeletonTitleProps = {
prefixCls: PropTypes.string,
width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
};
export const SkeletonTitleProps = PropTypes.shape(skeletonTitleProps).loose;
export type SkeletonTitleProps = Partial<ExtractPropTypes<typeof skeletonTitleProps>>;
export type ISkeletonTitleProps = Partial<ExtractPropTypes<typeof skeletonTitleProps>>;
const Title = defineComponent({
const SkeletonTitle = defineComponent({
props: skeletonTitleProps,
render() {
const { prefixCls, width } = this.$props;
const zWidth = typeof width === 'number' ? `${width}px` : width;
return <h3 class={prefixCls} style={{ width: zWidth }} />;
name: 'SkeletonTitle',
setup(props) {
return () => {
const { prefixCls, width } = props;
const zWidth = typeof width === 'number' ? `${width}px` : width;
return <h3 class={prefixCls} style={{ width: zWidth }} />;
};
},
});
export default Title;
export default SkeletonTitle;

View File

@ -1,167 +1,31 @@
import { defineComponent, inject } from 'vue';
import classNames from '../_util/classNames';
import PropTypes, { withUndefined } from '../_util/vue-types';
import { initDefaultProps, hasProp } from '../_util/props-util';
import { defaultConfigProvider } from '../config-provider';
import Avatar, { SkeletonAvatarProps, ISkeletonAvatarProps } from './Avatar';
import Title, { SkeletonTitleProps, ISkeletonTitleProps } from './Title';
import Paragraph, { SkeletonParagraphProps, ISkeletonParagraphProps } from './Paragraph';
import { withInstall } from '../_util/type';
import { App } from 'vue';
import Skeleton from './Skeleton';
import SkeletonButton from './Button';
import SkeletonInput from './Input';
import SkeletonImage from './Image';
import SkeletonAvatar from './Avatar';
export const SkeletonProps = {
active: PropTypes.looseBool,
loading: PropTypes.looseBool,
prefixCls: PropTypes.string,
children: PropTypes.any,
avatar: withUndefined(
PropTypes.oneOfType([PropTypes.string, SkeletonAvatarProps, PropTypes.looseBool]),
),
title: withUndefined(
PropTypes.oneOfType([PropTypes.looseBool, PropTypes.string, SkeletonTitleProps]),
),
paragraph: withUndefined(
PropTypes.oneOfType([PropTypes.looseBool, PropTypes.string, SkeletonParagraphProps]),
),
export { SkeletonProps, skeletonProps } from './Skeleton';
Skeleton.Button = SkeletonButton;
Skeleton.Avatar = SkeletonAvatar;
Skeleton.Input = SkeletonInput;
Skeleton.Image = SkeletonImage;
/* istanbul ignore next */
Skeleton.install = function(app: App) {
app.component(Skeleton.name, Skeleton);
app.component(Skeleton.Button.name, SkeletonButton);
app.component(Skeleton.Avatar.name, SkeletonAvatar);
app.component(Skeleton.Input.name, SkeletonInput);
app.component(Skeleton.Image.name, SkeletonImage);
return app;
};
function getComponentProps<T>(prop: T | boolean | undefined): T | {} {
if (prop && typeof prop === 'object') {
return prop;
}
return {};
}
function getAvatarBasicProps(hasTitle: boolean, hasParagraph: boolean): ISkeletonAvatarProps {
if (hasTitle && !hasParagraph) {
return { shape: 'square' };
}
return { shape: 'circle' };
}
function getTitleBasicProps(hasAvatar: boolean, hasParagraph: boolean): ISkeletonTitleProps {
if (!hasAvatar && hasParagraph) {
return { width: '38%' };
}
if (hasAvatar && hasParagraph) {
return { width: '50%' };
}
return {};
}
function getParagraphBasicProps(hasAvatar: boolean, hasTitle: boolean): ISkeletonParagraphProps {
const basicProps: ISkeletonParagraphProps = {};
// Width
if (!hasAvatar || !hasTitle) {
basicProps.width = '61%';
}
// Rows
if (!hasAvatar && hasTitle) {
basicProps.rows = 3;
} else {
basicProps.rows = 2;
}
return basicProps;
}
const Skeleton = defineComponent({
name: 'ASkeleton',
props: initDefaultProps(SkeletonProps, {
avatar: false,
title: true,
paragraph: true,
}),
setup() {
return {
configProvider: inject('configProvider', defaultConfigProvider),
};
},
render() {
const {
prefixCls: customizePrefixCls,
loading,
avatar,
title,
paragraph,
active,
} = this.$props;
const { getPrefixCls } = this.configProvider;
const prefixCls = getPrefixCls('skeleton', customizePrefixCls);
if (loading || !hasProp(this, 'loading')) {
const hasAvatar = !!avatar || avatar === '';
const hasTitle = !!title;
const hasParagraph = !!paragraph;
// Avatar
let avatarNode;
if (hasAvatar) {
const avatarProps = {
prefixCls: `${prefixCls}-avatar`,
...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 = {
prefixCls: `${prefixCls}-title`,
...getTitleBasicProps(hasAvatar, hasParagraph),
...getComponentProps(title),
};
$title = <Title {...titleProps} />;
}
// Paragraph
let paragraphNode;
if (hasParagraph) {
const paragraphProps = {
prefixCls: `${prefixCls}-paragraph`,
...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?.();
},
});
export default withInstall(Skeleton);
export default Skeleton as typeof Skeleton &
Plugin & {
readonly Button: typeof SkeletonButton;
readonly Avatar: typeof SkeletonAvatar;
readonly Input: typeof SkeletonInput;
readonly Image: typeof SkeletonImage;
};

View File

@ -5,8 +5,10 @@
@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-button-prefix-cls: ~'@{skeleton-prefix-cls}-button';
@skeleton-input-prefix-cls: ~'@{skeleton-prefix-cls}-input';
@skeleton-image-prefix-cls: ~'@{skeleton-prefix-cls}-image';
@skeleton-block-radius: 4px;
.@{skeleton-prefix-cls} {
display: table;
@ -14,24 +16,12 @@
&-header {
display: table-cell;
padding-right: 16px;
padding-right: @padding-md;
vertical-align: top;
// 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);
}
.skeleton-element-avatar();
}
}
@ -43,12 +33,13 @@
// Title
.@{skeleton-title-prefix-cls} {
width: 100%;
height: 16px;
margin-top: 16px;
height: @skeleton-title-height;
margin-top: @margin-md;
background: @skeleton-color;
border-radius: @skeleton-block-radius;
+ .@{skeleton-paragraph-prefix-cls} {
margin-top: 24px;
margin-top: @skeleton-title-paragraph-margin-top;
}
}
@ -58,16 +49,17 @@
> li {
width: 100%;
height: 16px;
height: @skeleton-paragraph-li-height;
list-style: none;
background: @skeleton-color;
border-radius: @skeleton-block-radius;
&:last-child:not(:first-child):not(:nth-child(2)) {
width: 61%;
}
+ li {
margin-top: 16px;
margin-top: @skeleton-paragraph-li-margin-top;
}
}
}
@ -76,14 +68,21 @@
&-with-avatar &-content {
// Title
.@{skeleton-title-prefix-cls} {
margin-top: 12px;
margin-top: @margin-sm;
+ .@{skeleton-paragraph-prefix-cls} {
margin-top: 28px;
margin-top: @skeleton-paragraph-margin-top;
}
}
}
&-round &-content {
.@{skeleton-title-prefix-cls},
.@{skeleton-paragraph-prefix-cls} > li {
border-radius: 100px;
}
}
// With active animation
&.@{skeleton-prefix-cls}-active {
& .@{skeleton-prefix-cls}-content {
@ -96,19 +95,156 @@
.@{skeleton-avatar-prefix-cls} {
.skeleton-color();
}
.@{skeleton-button-prefix-cls} {
.skeleton-color();
}
.@{skeleton-input-prefix-cls} {
.skeleton-color();
}
.@{skeleton-image-prefix-cls} {
.skeleton-color();
}
}
// Skeleton element
&-element {
display: inline-block;
width: auto;
.@{skeleton-button-prefix-cls} {
.skeleton-element-button();
}
.@{skeleton-avatar-prefix-cls} {
.skeleton-element-avatar();
}
.@{skeleton-input-prefix-cls} {
.skeleton-element-input();
}
.@{skeleton-image-prefix-cls} {
.skeleton-element-image();
}
}
}
// Button
.skeleton-element-button() {
display: inline-block;
vertical-align: top;
background: @skeleton-color;
border-radius: @border-radius-base;
.skeleton-element-button-size(@btn-height-base);
&-lg {
.skeleton-element-button-size(@btn-height-lg);
}
&-sm {
.skeleton-element-button-size(@btn-height-sm);
}
}
// Avatar
.skeleton-element-avatar() {
display: inline-block;
vertical-align: top;
background: @skeleton-color;
.skeleton-element-avatar-size(@avatar-size-base);
&-lg {
.skeleton-element-avatar-size(@avatar-size-lg);
}
&-sm {
.skeleton-element-avatar-size(@avatar-size-sm);
}
}
.avatar-size(@size) {
// Input
.skeleton-element-input() {
display: inline-block;
vertical-align: top;
background: @skeleton-color;
.skeleton-element-input-size(@input-height-base);
&-lg {
.skeleton-element-input-size(@input-height-lg);
}
&-sm {
.skeleton-element-input-size(@input-height-sm);
}
}
// Image
.skeleton-element-image() {
display: flex;
align-items: center;
justify-content: center;
vertical-align: top;
background: @skeleton-color;
.skeleton-element-image-size(@image-size-base*2);
&-path {
fill: #bfbfbf;
}
&-svg {
.skeleton-element-image-size(@image-size-base);
max-width: @image-size-base * 4;
max-height: @image-size-base * 4;
}
}
.skeleton-element-avatar-size(@size) {
width: @size;
height: @size;
line-height: @size;
.skeleton-element-common-size(@size);
&.@{skeleton-avatar-prefix-cls}-circle {
border-radius: 50%;
}
}
.skeleton-element-button-size(@size) {
width: @size * 2;
.skeleton-element-common-size(@size);
&.@{skeleton-button-prefix-cls}-circle {
width: @size;
border-radius: 50%;
}
&.@{skeleton-button-prefix-cls}-round {
border-radius: @size;
}
}
.skeleton-element-input-size(@size) {
width: 100%;
.skeleton-element-common-size(@size);
}
.skeleton-element-image-size(@size) {
width: @size;
.skeleton-element-common-size(@size);
&.@{skeleton-image-prefix-cls}-circle {
border-radius: 50%;
}
}
.skeleton-element-common-size(@size) {
height: @size;
line-height: @size;
}
.skeleton-color() {
background: linear-gradient(
90deg,
@ -128,3 +264,5 @@
background-position: 0 50%;
}
}
@import './rtl';

View File

@ -0,0 +1,47 @@
@import '../../style/themes/index';
@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-prefix-cls} {
&-rtl {
direction: rtl;
}
&-header {
.@{skeleton-prefix-cls}-rtl & {
padding-right: 0;
padding-left: 16px;
}
}
// With active animation
&.@{skeleton-prefix-cls}-active {
& .@{skeleton-prefix-cls}-content {
.@{skeleton-title-prefix-cls},
.@{skeleton-paragraph-prefix-cls} > li {
.@{skeleton-prefix-cls}-rtl& {
animation-name: ~'@{skeleton-prefix-cls}-loading-rtl';
}
}
}
.@{skeleton-avatar-prefix-cls} {
.@{skeleton-prefix-cls}-rtl& {
animation-name: ~'@{skeleton-prefix-cls}-loading-rtl';
}
}
}
}
@keyframes ~"@{skeleton-prefix-cls}-loading-rtl" {
0% {
background-position: 0% 50%;
}
100% {
background-position: 100% 50%;
}
}

View File

@ -755,8 +755,13 @@
// Skeleton
// ---
@skeleton-color: #f2f2f2;
@skeleton-color: rgba(190, 190, 190, 0.2);
@skeleton-to-color: shade(@skeleton-color, 5%);
@skeleton-paragraph-margin-top: 28px;
@skeleton-paragraph-li-margin-top: @margin-md;
@skeleton-paragraph-li-height: 16px;
@skeleton-title-height: 16px;
@skeleton-title-paragraph-margin-top: @margin-lg;
// Transfer
// ---