From bf1ffcace994039990f0b8bb301f75e09893da56 Mon Sep 17 00:00:00 2001 From: Belen Curcio Date: Thu, 15 Oct 2020 11:10:07 -0300 Subject: [PATCH] Skeleton Component --- .../ui/LoadingDots/LoadingDots.module.css | 26 ++++---- components/ui/LoadingDots/LoadingDots.tsx | 2 +- components/ui/Skeleton/Skeleton.module.css | 59 +++++++++++++++++++ components/ui/Skeleton/Skeleton.tsx | 57 ++++++++++++++++++ components/ui/Skeleton/index.ts | 1 + components/ui/index.ts | 1 + lib/to-pixels.ts | 13 ++++ pages/search.tsx | 5 +- pages/ui.tsx | 14 +++++ 9 files changed, 163 insertions(+), 15 deletions(-) create mode 100644 components/ui/Skeleton/Skeleton.module.css create mode 100644 components/ui/Skeleton/Skeleton.tsx create mode 100644 components/ui/Skeleton/index.ts create mode 100644 lib/to-pixels.ts create mode 100644 pages/ui.tsx diff --git a/components/ui/LoadingDots/LoadingDots.module.css b/components/ui/LoadingDots/LoadingDots.module.css index abfa9af65..88ce77ec6 100644 --- a/components/ui/LoadingDots/LoadingDots.module.css +++ b/components/ui/LoadingDots/LoadingDots.module.css @@ -1,16 +1,4 @@ -@keyframes blink { - 0% { - opacity: 0.2; - } - 20% { - opacity: 1; - } - 100% { - opacity: 0.2; - } -} - -.loading { +.root { @apply inline-flex text-center items-center leading-7; & span { @@ -30,3 +18,15 @@ } } } + +@keyframes blink { + 0% { + opacity: 0.2; + } + 20% { + opacity: 1; + } + 100% { + opacity: 0.2; + } +} diff --git a/components/ui/LoadingDots/LoadingDots.tsx b/components/ui/LoadingDots/LoadingDots.tsx index eb26c7340..10e5bbae1 100644 --- a/components/ui/LoadingDots/LoadingDots.tsx +++ b/components/ui/LoadingDots/LoadingDots.tsx @@ -2,7 +2,7 @@ import s from './LoadingDots.module.css' const LoadingDots: React.FC = () => { return ( - + diff --git a/components/ui/Skeleton/Skeleton.module.css b/components/ui/Skeleton/Skeleton.module.css new file mode 100644 index 000000000..a33e84ba2 --- /dev/null +++ b/components/ui/Skeleton/Skeleton.module.css @@ -0,0 +1,59 @@ +.skeleton { + @apply block rounded-md; + + &.loaded { + width: unset !important; + } + + &:not(.wrapper):not(.show) { + display: none; + } + + &::not(.wrapper):not(.loaded) { + background-image: linear-gradient( + 270deg, + var(--accents-1), + var(--accents-2), + var(--accents-2), + var(--accents-1) + ); + background-size: 400% 100%; + animation: loading 8s ease-in-out infinite; + } +} + +.wrapper { + @apply block relative; + + &:not(.show)::before { + content: none; + } + + &::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 100; + background-image: linear-gradient( + 270deg, + var(--accents-1), + var(--accents-2), + var(--accents-2), + var(--accents-1) + ); + background-size: 400% 100%; + animation: loading 8s ease-in-out infinite; + } +} + +@keyframes loading { + 0% { + background-position: 200% 0; + } + 100% { + background-position: -200% 0; + } +} diff --git a/components/ui/Skeleton/Skeleton.tsx b/components/ui/Skeleton/Skeleton.tsx new file mode 100644 index 000000000..f7d1e293a --- /dev/null +++ b/components/ui/Skeleton/Skeleton.tsx @@ -0,0 +1,57 @@ +import React, { CSSProperties } from 'react' +import cn from 'classnames' +import px from '@lib/to-pixels' +import s from './skeleton.module.css' + +interface Props { + width?: string | number + height?: string | number + boxHeight?: string | number + style?: CSSProperties + show?: boolean + block?: boolean + className?: string +} + +const Skeleton: React.FC = ({ + style, + width, + height, + children, + className, + show = true, + boxHeight = height, +}) => { + // Automatically calculate the size if there are children + // and no fixed sizes are specified + const shouldAutoSize = !!children && !(width || height) + + // Defaults + width = width || 24 + height = height || 24 + boxHeight = boxHeight || height + + return ( + + {children} + + ) +} + +export default Skeleton diff --git a/components/ui/Skeleton/index.ts b/components/ui/Skeleton/index.ts new file mode 100644 index 000000000..3ec6c0031 --- /dev/null +++ b/components/ui/Skeleton/index.ts @@ -0,0 +1 @@ +export { default } from './Skeleton' diff --git a/components/ui/index.ts b/components/ui/index.ts index f2731829f..6d0da5544 100644 --- a/components/ui/index.ts +++ b/components/ui/index.ts @@ -6,3 +6,4 @@ export { default as Sidebar } from './Sidebar' export { default as Marquee } from './Marquee' export { default as Container } from './Container' export { default as LoadingDots } from './LoadingDots' +export { default as Skeleton } from './Skeleton' diff --git a/lib/to-pixels.ts b/lib/to-pixels.ts new file mode 100644 index 000000000..1701a85fa --- /dev/null +++ b/lib/to-pixels.ts @@ -0,0 +1,13 @@ +// Convert numbers or strings to pixel value +// Helpful for styled-jsx when using a prop +// height: ${toPixels(height)}; (supports height={20} and height="20px") + +const toPixels = (value: string | number) => { + if (typeof value === 'number') { + return `${value}px` + } + + return value +} + +export default toPixels diff --git a/pages/search.tsx b/pages/search.tsx index c84fa8d39..22e5667a8 100644 --- a/pages/search.tsx +++ b/pages/search.tsx @@ -128,7 +128,10 @@ export default function Search({ ) : ( // TODO: add a proper loading state -
Searching...
+
+ Searching... + +
)}
diff --git a/pages/ui.tsx b/pages/ui.tsx new file mode 100644 index 000000000..3dc92f4dc --- /dev/null +++ b/pages/ui.tsx @@ -0,0 +1,14 @@ +import { GetStaticPropsContext, InferGetStaticPropsType } from 'next' +import { useRouter } from 'next/router' +import Link from 'next/link' +import { Layout } from '@components/core' +import { Container, Grid, Skeleton } from '@components/ui' +export default function Search() { + return ( + + + + ) +} + +Search.Layout = Layout