feat: Skeleton Common

This commit is contained in:
sonnguyenkieio 2021-09-13 15:46:08 +07:00
parent 42207a0756
commit e154724fef
12 changed files with 169 additions and 251 deletions

View File

@ -1,32 +0,0 @@
@import '../../../styles/utilities';
$base-color: #ddd;
$shine-color: #e8e8e8;
$animation-duration: 1.6s;
@mixin background-gradient {
background-image: linear-gradient(90deg, $base-color 0px, $shine-color 40px, $base-color 80px) ;
background-size: 600px;
}
.skeletonCommon:empty {
margin: auto;
height: 14rem;
width: 14rem;
animation: name duration timing-function delay iteration-count direction fill-mode;
@include background-gradient;
animation: shine-lines $animation-duration infinite linear;
}
@keyframes shine-lines {
0% {
background-position: -100px;
}
40% {
background-position: 140px;
}
100% {
background-position: 140px;
}
}

View File

@ -1,17 +0,0 @@
import React from "react";
import s from './SkeletonCommon.module.scss'
interface SkeletonCommonProps {
children? : React.ReactNode;
}
const SkeletonCommon = ({ children }: SkeletonCommonProps) => {
return (
<div className={s.skeletonCommon}>
{children}
</div>
)
}
export default SkeletonCommon

View File

@ -0,0 +1,53 @@
@import '../../../../styles/utilities';
.skeletonImage:empty {
@apply relative;
background: #DDDBDD;
&.small {
width: 10rem;
height: 10rem;
}
&.default {
width: 15rem;
height: 15rem;
}
&.large {
width: 20rem;
height: 20rem;
}
&.left {
margin-left: 0;
}
&.center {
margin: auto;
}
&::after {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
transform: translateX(-100%);
background-image: linear-gradient(
90deg,
rgba(#fff, 0) 0,
rgba(#fff, 0.2) 20%,
rgba(#fff, 0.5) 60%,
rgba(#fff, 0)
);
animation: shimmer 2s infinite;
content: '';
}
}
@keyframes shimmer {
100% {
transform: translateX(100%);
}
}

View File

@ -0,0 +1,22 @@
import classNames from "classnames";
import React from "react";
import s from './SkeletonImage.module.scss'
interface SkeletonImageProps {
align?: "left" | "center"
size?: "small" | "default" | "large"
children?: React.ReactNode
}
const SkeletonImage = ({ align="center", size="default", children }: SkeletonImageProps) => {
return (
<div className={classNames(s.skeletonImage, {
[s[size]] : size,
[s[align]] : align
})}>
{children}
</div>
)
}
export default SkeletonImage

View File

@ -0,0 +1,63 @@
@import '../../../../styles/utilities';
.skeletonParagraph {
.row {
display: inline-block;
height: 2rem;
width: 100%;
position: relative;
overflow: hidden;
background-color: #DDDBDD;
&::after {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
transform: translateX(-100%);
background-image: linear-gradient(
90deg,
rgba(#fff, 0) 0,
rgba(#fff, 0.2) 20%,
rgba(#fff, 0.5) 60%,
rgba(#fff, 0)
);
animation: shimmer 2s infinite;
content: '';
}
}
.lastRow {
display: inline-block;
height: 2rem;
width: 80%;
position: relative;
overflow: hidden;
background-color: #DDDBDD;
&::after {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
transform: translateX(-100%);
background-image: linear-gradient(
90deg,
rgba(#fff, 0) 0,
rgba(#fff, 0.2) 20%,
rgba(#fff, 0.5) 60%,
rgba(#fff, 0)
);
animation: shimmer 2s infinite;
content: '';
}
}
}
@keyframes shimmer {
100% {
transform: translateX(100%);
}
}

View File

@ -0,0 +1,31 @@
import React, { useEffect, useState } from "react";
import s from './SkeletonParagraph.module.scss'
interface SkeletonParagraphProps {
rows?: number // number of rows in paragraph
children?: React.ReactNode
}
const SkeletonParagraph = ({ rows=2, children }: SkeletonParagraphProps) => {
const [isChildLoaded, setIsChildLoaded] = useState(false)
useEffect(() => {
setIsChildLoaded(true);
}, [])
return (
<div className={s.skeletonParagraph}>
{
isChildLoaded ? children : [...Array(rows)].map((e, i) => {
if (i === rows-1) {
return <div key={i} className={s.lastRow}></div>
}
return <div key={i} className={s.row}></div>
})
}
</div>
)
}
export default SkeletonParagraph

View File

@ -1,51 +0,0 @@
@import '../../../../../styles/utilities';
$base-color: #8F8F8F;
$shine-color: #ebebeb;
$animation-duration: 1.6s;
$avatar-offset: 52 + 16;
@mixin background-gradient {
background-image: linear-gradient(90deg, $base-color 0px, $shine-color 40px, $base-color 80px) ;
background-size: 600px;
}
.skeletonAvatar {
&.active {
@include background-gradient;
animation: shine-avatar $animation-duration infinite linear;
}
&.circle {
border-radius: 50%;
}
&.square {
border-radius: 0;
}
&.small {
width: 3.2rem;
height: 3.2rem;
}
&.default {
width: 4.8rem;
height: 4.8rem;
}
&.large {
width: 6.4rem;
height: 6.4rem;
}
}
@keyframes shine-avatar {
0% {
background-position: -100px + $avatar-offset
}
40%, 100% {
background-position: 140px + $avatar-offset
}
}

View File

@ -1,25 +0,0 @@
import classNames from "classnames";
import React from "react";
import s from './SkeletonAvatar.module.scss'
interface SkeletonAvatarProps {
active?: boolean,
shape?: "circle" | "square",
size?: "small" | "default" | "large",
children: React.ReactNode
}
const SkeletonAvatar = ({ active=true, shape="circle", size="default", children }: SkeletonAvatarProps) => {
return (
<div className={classNames(s.skeletonAvatar, {
[s.active] : active,
[s[shape]] : shape,
[s[size]] : size,
})}>
{children}
</div>
)
}
export default SkeletonAvatar

View File

@ -1,41 +0,0 @@
@import '../../../../../styles/utilities';
$base-color: #DDD;
$shine-color: #ebebeb;
$animation-duration: 2s;
@mixin background-gradient {
background-image: linear-gradient(90deg, $base-color 0px, $shine-color 40px, $base-color 80px) ;
background-size: 100%;
}
.skeletonParagraph {
@apply bg-white;
height: fit-content;
width: 100%;
.row {
height: 2rem;
margin: 1rem;
animation: shine-lines $animation-duration infinite linear;
@include background-gradient;
}
.lastRow {
height: 2rem;
margin: 1rem;
width: 85%;
animation: shine-lines $animation-duration infinite linear;
@include background-gradient;
}
}
@keyframes shine-lines {
0% {
background-position: -100px;
}
40%, 100% {
background-position: 100%;
}
}

View File

@ -1,30 +0,0 @@
import classNames from "classnames";
import React from "react";
import s from './SkeletonParagraph.module.scss'
interface SkeletonParagraphProps {
active?: boolean,
rows?: number // number of rows in paragraph
children: React.ReactNode
}
const SkeletonParagraph = ({ active=true, rows=2, children }: SkeletonParagraphProps) => {
return (
<div className={classNames(s.skeletonParagraph, {
[s.active] : active
})}>
{
[...Array(rows)].map((e, i) => {
if (i === rows-1) {
return <p key={i} className={s.lastRow}></p>
}
return <p key={i} className={s.row}></p>
})
}
{children}
</div>
)
}
export default SkeletonParagraph

View File

@ -1,27 +0,0 @@
@import '../../../../../styles/utilities';
$base-color: #8F8F8F;
$shine-color: #ebebeb;
$animation-duration: 1.6s;
@mixin background-gradient {
background-image: linear-gradient(90deg, $base-color 0px, $shine-color 40px, $base-color 80px) ;
background-size: 600px;
}
.skeletonTitle {
&.active {
@include background-gradient;
animation: shine-lines $animation-duration infinite linear;
}
}
@keyframes shine-lines {
0% {
background-position: -100px;
}
40%, 100% {
background-position: 50rem;
}
}

View File

@ -1,28 +0,0 @@
import classNames from "classnames";
import React from "react";
import s from './SkeletonTitle.module.scss'
interface SkeletonTitleProps {
active?: boolean,
width: string | number, // number px
height: string | number,
children: React.ReactNode
}
const SkeletonTitle = ({ active=true, width, height, children }: SkeletonTitleProps) => {
const styles = {
width: width,
height: height
}
return (
<div style={styles} className={classNames(s.skeletonTitle, {
[s.active] : active
})}>
{children}
</div>
)
}
export default SkeletonTitle