feat: add animated preloader (#2464)

* feat: add animated preloader

* feat: add animated preloader - add scss lines

* feat: add animated preloader - add scss lines

* feat: add animated preloader - ref scss lines

* feat: add animated preloader - add preloader property to loader component
This commit is contained in:
macqbat 2022-12-27 16:46:44 +01:00 committed by GitHub
parent 485c609791
commit 64aa39fe4b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 108 additions and 19 deletions

View File

@ -8,8 +8,7 @@ import {
Web3Provider as Web3ProviderInternal,
useWeb3ConnectStore,
} from '@vegaprotocol/web3';
import { AsyncRenderer, Splash } from '@vegaprotocol/ui-toolkit';
import { t } from '@vegaprotocol/react-helpers';
import { AsyncRenderer, Loader } from '@vegaprotocol/ui-toolkit';
interface AppLoaderProps {
children: ReactNode;
@ -21,10 +20,7 @@ interface AppLoaderProps {
*/
export function AppLoader({ children }: AppLoaderProps) {
return (
<NetworkLoader
skeleton={<Splash>{t('Loading...')}</Splash>}
cache={cacheConfig}
>
<NetworkLoader skeleton={<Loader />} cache={cacheConfig}>
{children}
</NetworkLoader>
);

View File

@ -0,0 +1,22 @@
import { Loader } from '@vegaprotocol/ui-toolkit';
export const Preloader = () => {
return (
<>
<style>
{`
body{
display: block;
width: 100%;
height: 100%;
}
`}
</style>
<div className="pre-loader">
<Loader forceTheme="light" preloader />
</div>
</>
);
};
export default Preloader;

View File

@ -1,4 +1,5 @@
import Head from 'next/head';
import dynamic from 'next/dynamic';
import type { AppProps } from 'next/app';
import { Navbar } from '../components/navbar';
import { t } from '@vegaprotocol/react-helpers';
@ -22,6 +23,7 @@ import {
} from '@vegaprotocol/environment';
import { AppLoader, Web3Provider } from '../components/app-loader';
import './styles.css';
import './gen-styles.scss';
import { usePageTitleStore } from '../stores';
import { Footer } from '../components/footer';
import { useEffect, useMemo, useState } from 'react';
@ -67,7 +69,7 @@ function AppBody({ Component }: AppProps) {
const { VEGA_ENV } = useEnvironment();
return (
<>
<div className="h-full dark:bg-black dark:text-white">
<Head>
{/* Cannot use meta tags in _document.page.tsx see https://nextjs.org/docs/messages/no-document-viewport-meta */}
<meta name="viewport" content="width=device-width, initial-scale=1" />
@ -76,7 +78,7 @@ function AppBody({ Component }: AppProps) {
<VegaWalletProvider>
<AppLoader>
<Web3Provider>
<div className="h-full relative dark:bg-black dark:text-white z-0 grid grid-rows-[min-content,1fr,min-content]">
<div className="h-full relative z-0 grid grid-rows-[min-content,1fr,min-content]">
<Navbar
navbarTheme={VEGA_ENV === Networks.TESTNET ? 'yellow' : 'dark'}
/>
@ -92,10 +94,17 @@ function AppBody({ Component }: AppProps) {
</Web3Provider>
</AppLoader>
</VegaWalletProvider>
</>
</div>
);
}
const DynamicLoader = dynamic(
() => import('../components/preloader/preloader'),
{
loading: () => <>Loading...</>,
}
);
function VegaTradingApp(props: AppProps) {
const [mounted, setMounted] = useState(false);
@ -106,7 +115,9 @@ function VegaTradingApp(props: AppProps) {
setMounted(true);
}, []);
if (!mounted) return null;
if (!mounted) {
return <DynamicLoader />;
}
return (
<HashRouter>

View File

@ -0,0 +1,50 @@
.pre-loader {
display: flex;
width: 100%;
min-height: 100vh;
justify-content: center;
align-items: center;
@for $i from 0 through 16 {
.loader-item:nth-child(#{$i}) {
@if $i % 2 == 0 {
animation-delay: #{$i * 50 * random(5)}ms;
animation-direction: reverse;
} @else {
animation-delay: #{$i * -50 * random(5)}ms;
animation-direction: alternate;
}
}
}
.pre-loader-center {
align-items: center;
display: flex;
flex-direction: column;
}
.pre-loader-wrapper {
width: 50px;
height: 50px;
display: flex;
flex-wrap: wrap;
}
.loader-item {
width: 10px;
height: 10px;
background: black;
animation: flickering 0.4s steps(2, jump-none) alternate infinite;
}
}
@keyframes flickering {
0% {
opacity: 1;
}
25% {
opacity: 1;
}
26% {
opacity: 0;
}
100% {
opacity: 0;
}
}

View File

@ -4,20 +4,27 @@ import { useEffect, useState } from 'react';
export interface LoaderProps {
size?: 'small' | 'large';
forceTheme?: 'dark' | 'light';
preloader?: boolean;
}
export const Loader = ({ size = 'large', forceTheme }: LoaderProps) => {
export const Loader = ({
size = 'large',
forceTheme,
preloader,
}: LoaderProps) => {
const [, forceRender] = useState(false);
useEffect(() => {
const interval = setInterval(() => {
const interval = preloader
? undefined
: setInterval(() => {
forceRender((x) => !x);
}, 100);
return () => clearInterval(interval);
}, []);
}, [preloader]);
const itemClasses = classNames({
const itemClasses = classNames('loader-item', {
'dark:bg-white bg-black': !forceTheme,
'bg-white': forceTheme === 'dark',
'bg-black': forceTheme === 'light',
@ -29,8 +36,11 @@ export const Loader = ({ size = 'large', forceTheme }: LoaderProps) => {
const items = size === 'small' ? 9 : 16;
return (
<div className="flex flex-col items-center" data-testid="loader">
<div className={`${wrapperClasses} flex flex-wrap`}>
<div
className="flex flex-col items-center pre-loader-center"
data-testid="loader"
>
<div className={`${wrapperClasses} flex flex-wrap pre-loader-wrapper`}>
{new Array(items).fill(null).map((_, i) => {
return (
<div