Add progress bar to top of page for nicer loading indicator styles

This commit is contained in:
Dane Everitt 2020-04-10 12:41:08 -07:00
parent 708c15eba8
commit d3a06e1ca8
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
12 changed files with 133 additions and 34 deletions

View file

@ -9,6 +9,7 @@ import AuthenticationRouter from '@/routers/AuthenticationRouter';
import { Provider } from 'react-redux';
import { SiteSettings } from '@/state/settings';
import { DefaultTheme, ThemeProvider } from 'styled-components';
import ProgressBar from '@/components/elements/ProgressBar';
interface ExtendedWindow extends Window {
SiteConfiguration?: SiteSettings;
@ -57,6 +58,7 @@ const App = () => {
<ThemeProvider theme={theme}>
<StoreProvider store={store}>
<Provider store={store}>
<ProgressBar/>
<div className={'mx-auto w-auto'}>
<BrowserRouter basename={'/'} key={'root-router'}>
<Switch>

View file

@ -1,19 +0,0 @@
import React from 'react';
import Spinner from '@/components/elements/Spinner';
import { CSSTransition } from 'react-transition-group';
interface Props {
visible: boolean;
children?: React.ReactChild;
}
const ListRefreshIndicator = ({ visible, children }: Props) => (
<CSSTransition timeout={250} in={visible} appear={true} unmountOnExit={true} classNames={'fade'}>
<div className={'flex items-center mb-2'}>
<Spinner size={'tiny'}/>
<p className={'ml-2 text-sm text-neutral-400'}>{children || 'Refreshing listing...'}</p>
</div>
</CSSTransition>
);
export default ListRefreshIndicator;

View file

@ -0,0 +1,73 @@
import React, { useEffect, useRef, useState } from 'react';
import styled from 'styled-components';
import { useStoreActions, useStoreState } from 'easy-peasy';
import { randomInt } from '@/helpers';
import { CSSTransition } from 'react-transition-group';
const BarFill = styled.div`
${tw`h-full bg-cyan-400`};
transition: 250ms ease-in-out;
box-shadow: 0 -2px 10px 2px hsl(178, 78%, 57%);
`;
export default () => {
const interval = useRef<number>(null);
const timeout = useRef<number>(null);
const [ visible, setVisible ] = useState(false);
const progress = useStoreState(state => state.progress.progress);
const continuous = useStoreState(state => state.progress.continuous);
const setProgress = useStoreActions(actions => actions.progress.setProgress);
useEffect(() => {
return () => {
timeout.current && clearTimeout(timeout.current);
interval.current && clearInterval(interval.current);
};
}, []);
useEffect(() => {
setVisible((progress || 0) > 0);
if (progress === 100) {
// @ts-ignore
timeout.current = setTimeout(() => setProgress(undefined), 500);
}
}, [ progress ]);
useEffect(() => {
if (!continuous) {
interval.current && clearInterval(interval.current);
return;
}
if (!progress || progress === 0) {
setProgress(randomInt(20, 30));
}
}, [ continuous ]);
useEffect(() => {
if (continuous) {
interval.current && clearInterval(interval.current);
if ((progress || 0) >= 90) {
setProgress(90);
} else {
// @ts-ignore
interval.current = setTimeout(() => setProgress(progress + randomInt(1, 5)), 500);
}
}
}, [ progress, continuous ]);
return (
<div className={'w-full fixed'} style={{ height: '2px' }}>
<CSSTransition
timeout={250}
appear={true}
in={visible}
unmountOnExit={true}
classNames={'fade'}
>
<BarFill style={{ width: progress === undefined ? '100%' : `${progress}%` }}/>
</CSSTransition>
</div>
);
};

View file

@ -1,6 +1,6 @@
import React, { useEffect, useState } from 'react';
import Spinner from '@/components/elements/Spinner';
import getServerBackups, { ServerBackup } from '@/api/server/backups/getServerBackups';
import getServerBackups from '@/api/server/backups/getServerBackups';
import useServer from '@/plugins/useServer';
import useFlash from '@/plugins/useFlash';
import { httpErrorToHuman } from '@/api/http';
@ -9,7 +9,6 @@ import CreateBackupButton from '@/components/server/backups/CreateBackupButton';
import FlashMessageRender from '@/components/FlashMessageRender';
import BackupRow from '@/components/server/backups/BackupRow';
import { ServerContext } from '@/state/server';
import ListRefreshIndicator from '@/components/elements/ListRefreshIndicator';
export default () => {
const { uuid } = useServer();
@ -36,7 +35,6 @@ export default () => {
return (
<div className={'mt-10 mb-6'}>
<ListRefreshIndicator visible={loading}/>
<FlashMessageRender byKey={'backups'} className={'mb-4'}/>
{!backups.length ?
<p className="text-center text-sm text-neutral-400">

View file

@ -10,7 +10,6 @@ import CreateDatabaseButton from '@/components/server/databases/CreateDatabaseBu
import Can from '@/components/elements/Can';
import useFlash from '@/plugins/useFlash';
import useServer from '@/plugins/useServer';
import ListRefreshIndicator from '@/components/elements/ListRefreshIndicator';
export default () => {
const { uuid, featureLimits } = useServer();
@ -41,7 +40,6 @@ export default () => {
:
<CSSTransition classNames={'fade'} timeout={250}>
<>
<ListRefreshIndicator visible={loading}/>
{databases.length > 0 ?
databases.map((database, index) => (
<DatabaseRow

View file

@ -1,18 +1,15 @@
import React, { useEffect, useMemo, useState } from 'react';
import getServerSchedules, { Schedule } from '@/api/server/schedules/getServerSchedules';
import React, { useEffect, useState } from 'react';
import getServerSchedules from '@/api/server/schedules/getServerSchedules';
import { ServerContext } from '@/state/server';
import Spinner from '@/components/elements/Spinner';
import { RouteComponentProps } from 'react-router-dom';
import FlashMessageRender from '@/components/FlashMessageRender';
import ScheduleRow from '@/components/server/schedules/ScheduleRow';
import { httpErrorToHuman } from '@/api/http';
import { Actions, useStoreActions } from 'easy-peasy';
import { ApplicationStore } from '@/state';
import EditScheduleModal from '@/components/server/schedules/EditScheduleModal';
import Can from '@/components/elements/Can';
import useServer from '@/plugins/useServer';
import useFlash from '@/plugins/useFlash';
import ListRefreshIndicator from '@/components/elements/ListRefreshIndicator';
export default ({ match, history }: RouteComponentProps) => {
const { uuid } = useServer();
@ -37,7 +34,6 @@ export default ({ match, history }: RouteComponentProps) => {
return (
<div className={'my-10 mb-6'}>
<FlashMessageRender byKey={'schedules'} className={'mb-4'}/>
<ListRefreshIndicator visible={loading}/>
{(!schedules.length && loading) ?
<Spinner size={'large'} centered={true}/>
:

View file

@ -9,7 +9,6 @@ import FlashMessageRender from '@/components/FlashMessageRender';
import getServerSubusers from '@/api/server/users/getServerSubusers';
import { httpErrorToHuman } from '@/api/http';
import Can from '@/components/elements/Can';
import ListRefreshIndicator from '@/components/elements/ListRefreshIndicator';
export default () => {
const [ loading, setLoading ] = useState(true);
@ -48,7 +47,6 @@ export default () => {
return (
<div className={'mt-10 mb-6'}>
<ListRefreshIndicator visible={loading}/>
<FlashMessageRender byKey={'users'} className={'mb-4'}/>
{!subusers.length ?
<p className={'text-center text-sm text-neutral-400'}>