Get spinners back in working order

This commit is contained in:
Dane Everitt 2020-07-04 12:39:55 -07:00
parent d260200947
commit 5473edc006
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
16 changed files with 74 additions and 50 deletions

View file

@ -2,13 +2,13 @@ import React from 'react';
import MessageBox from '@/components/MessageBox'; import MessageBox from '@/components/MessageBox';
import { State, useStoreState } from 'easy-peasy'; import { State, useStoreState } from 'easy-peasy';
import { ApplicationStore } from '@/state'; import { ApplicationStore } from '@/state';
import tw from 'twin.macro';
type Props = Readonly<{ type Props = Readonly<{
byKey?: string; byKey?: string;
spacerClass?: string;
}>; }>;
export default ({ spacerClass, byKey }: Props) => { export default ({ byKey }: Props) => {
const flashes = useStoreState((state: State<ApplicationStore>) => state.flashes.items); const flashes = useStoreState((state: State<ApplicationStore>) => state.flashes.items);
let filtered = flashes; let filtered = flashes;
@ -25,7 +25,7 @@ export default ({ spacerClass, byKey }: Props) => {
{ {
filtered.map((flash, index) => ( filtered.map((flash, index) => (
<React.Fragment key={flash.id || flash.type + flash.message}> <React.Fragment key={flash.id || flash.type + flash.message}>
{index > 0 && <div css={tw`${spacerClass || 'mt-2'}`}></div>} {index > 0 && <div css={tw`mt-2`}></div>}
<MessageBox type={flash.type} title={flash.title}> <MessageBox type={flash.type} title={flash.title}>
{flash.message} {flash.message}
</MessageBox> </MessageBox>

View file

@ -54,7 +54,7 @@ const LoginCheckpointContainer = () => {
disabled={isSubmitting} disabled={isSubmitting}
> >
{isSubmitting ? {isSubmitting ?
<Spinner size={'tiny'} className={'mx-auto'}/> <Spinner size={'small'} className={'mx-auto'}/>
: :
'Continue' 'Continue'
} }

View file

@ -61,7 +61,7 @@ const LoginContainer = ({ isSubmitting, setFieldValue, values, submitForm, handl
className={'btn btn-primary btn-jumbo'} className={'btn btn-primary btn-jumbo'}
> >
{isSubmitting ? {isSubmitting ?
<Spinner size={'tiny'} className={'mx-auto'}/> <Spinner size={'small'} className={'mx-auto'}/>
: :
'Login' 'Login'
} }

View file

@ -92,7 +92,7 @@ export default ({ match, history, location }: Props) => {
disabled={isSubmitting} disabled={isSubmitting}
> >
{isSubmitting ? {isSubmitting ?
<Spinner size={'tiny'} className={'mx-auto'}/> <Spinner size={'small'} className={'mx-auto'}/>
: :
'Reset Password' 'Reset Password'
} }

View file

@ -79,7 +79,7 @@ export default ({ server }: { server: Server }) => {
<div css={tw`w-1/3 flex items-baseline relative`}> <div css={tw`w-1/3 flex items-baseline relative`}>
{!stats ? {!stats ?
!statsError ? !statsError ?
<SpinnerOverlay size={'tiny'} visible={true} backgroundOpacity={0.25}/> <SpinnerOverlay size={'small'} visible={true} backgroundOpacity={0.25}/>
: :
server.isInstalling ? server.isInstalling ?
<div css={tw`flex-1 text-center`}> <div css={tw`flex-1 text-center`}>

View file

@ -8,6 +8,8 @@ import { Actions, useStoreActions } from 'easy-peasy';
import { ApplicationStore } from '@/state'; import { ApplicationStore } from '@/state';
import disableAccountTwoFactor from '@/api/account/disableAccountTwoFactor'; import disableAccountTwoFactor from '@/api/account/disableAccountTwoFactor';
import { httpErrorToHuman } from '@/api/http'; import { httpErrorToHuman } from '@/api/http';
import tw from 'twin.macro';
import Button from '@/components/elements/Button';
interface Values { interface Values {
password: string; password: string;
@ -45,19 +47,19 @@ export default ({ ...props }: RequiredModalProps) => {
{({ isSubmitting, isValid }) => ( {({ isSubmitting, isValid }) => (
<Modal {...props} dismissable={!isSubmitting} showSpinnerOverlay={isSubmitting}> <Modal {...props} dismissable={!isSubmitting} showSpinnerOverlay={isSubmitting}>
<Form className={'mb-0'}> <Form className={'mb-0'}>
<FlashMessageRender className={'mb-6'} byKey={'account:two-factor'}/> <FlashMessageRender css={tw`mb-6`} byKey={'account:two-factor'}/>
<Field <Field
id={'password'} id={'password'}
name={'password'} name={'password'}
type={'password'} type={'password'}
label={'Current Password'} label={'Current Password'}
description={'In order to disable two-factor authentication you will need to provide your account password.'} description={'In order to disable two-factor authentication you will need to provide your account password.'}
autoFocus={true} autoFocus
/> />
<div className={'mt-6 text-right'}> <div css={tw`mt-6 text-right`}>
<button className={'btn btn-red btn-sm'} disabled={!isValid}> <Button disabled={!isValid}>
Disable Two-Factor Disable Two-Factor
</button> </Button>
</div> </div>
</Form> </Form>
</Modal> </Modal>

View file

@ -7,10 +7,9 @@ interface Props {
size?: 'xsmall' | 'small' | 'large' | 'xlarge'; size?: 'xsmall' | 'small' | 'large' | 'xlarge';
color?: 'green' | 'red' | 'primary' | 'grey'; color?: 'green' | 'red' | 'primary' | 'grey';
isSecondary?: boolean; isSecondary?: boolean;
disabled?: boolean;
} }
const StyledButton = styled.button<Props>` const StyledButton = styled.button<Omit<Props, 'isLoading'>>`
${tw`rounded p-2 uppercase tracking-wide text-sm transition-all duration-150`}; ${tw`rounded p-2 uppercase tracking-wide text-sm transition-all duration-150`};
${props => props.isSecondary && css<Props>` ${props => props.isSecondary && css<Props>`
@ -73,9 +72,6 @@ const StyledButton = styled.button<Props>`
${props => props.size === 'xlarge' && tw`p-4 w-full`}; ${props => props.size === 'xlarge' && tw`p-4 w-full`};
&:disabled { opacity: 0.55; cursor: default } &:disabled { opacity: 0.55; cursor: default }
${props => props.disabled && css`opacity: 0.55; cursor: default`};
`; `;
type ComponentProps = Props & type ComponentProps = Props &
@ -88,7 +84,7 @@ const Button: React.FC<ComponentProps> = ({ children, isLoading, ...props }) =>
<div className={'spinner-circle spinner-white spinner-sm'}/> <div className={'spinner-circle spinner-white spinner-sm'}/>
</div> </div>
} }
<span css={isLoading && tw`text-transparent`}> <span css={isLoading ? tw`text-transparent` : undefined}>
{children} {children}
</span> </span>
</StyledButton> </StyledButton>

View file

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { Field as FormikField, FieldProps } from 'formik'; import { Field as FormikField, FieldProps } from 'formik';
import classNames from 'classnames';
import Input from '@/components/elements/Input'; import Input from '@/components/elements/Input';
import Label from '@/components/elements/Label';
interface OwnProps { interface OwnProps {
name: string; name: string;
@ -19,7 +19,7 @@ const Field = ({ id, name, light = false, label, description, validate, classNam
({ field, form: { errors, touched } }: FieldProps) => ( ({ field, form: { errors, touched } }: FieldProps) => (
<React.Fragment> <React.Fragment>
{label && {label &&
<label htmlFor={id} className={light ? undefined : 'input-dark-label'}>{label}</label> <Label htmlFor={id} isLight={light}>{label}</Label>
} }
<Input <Input
id={id} id={id}

View file

@ -12,7 +12,7 @@ const InputSpinner = ({ visible, children }: { visible: boolean, children: React
classNames={'fade'} classNames={'fade'}
> >
<div className={'absolute right-0 h-full flex items-center justify-end pr-3'}> <div className={'absolute right-0 h-full flex items-center justify-end pr-3'}>
<Spinner size={'tiny'}/> <Spinner size={'small'}/>
</div> </div>
</CSSTransition> </CSSTransition>
{children} {children}

View file

@ -0,0 +1,9 @@
import styled from 'styled-components/macro';
import tw from 'twin.macro';
const Label = styled.label<{ isLight?: boolean }>`
${tw`block text-xs uppercase text-neutral-200 mb-2`};
${props => props.isLight && tw`text-neutral-700`};
`;
export default Label;

View file

@ -1,31 +1,48 @@
import React from 'react'; import React from 'react';
import classNames from 'classnames'; import styled, { css, keyframes } from 'styled-components/macro';
import tw from 'twin.macro';
export type SpinnerSize = 'large' | 'normal' | 'tiny'; export type SpinnerSize = 'small' | 'base' | 'large';
interface Props { interface Props {
size?: SpinnerSize; size?: SpinnerSize;
centered?: boolean; centered?: boolean;
className?: string; isBlue?: boolean;
} }
const Spinner = ({ size, centered, className }: Props) => ( const spin = keyframes`
to { transform: rotate(360deg); }
`;
// noinspection CssOverwrittenProperties
const SpinnerComponent = styled.div<Props>`
${tw`w-8 h-8`};
border-width: 3px;
border-radius: 50%;
animation: ${spin} 1s cubic-bezier(0.55, 0.25, 0.25, 0.70) infinite;
${props => props.size === 'small' ? tw`w-4 h-4 border-2` : (props.size === 'large' ? css`
${tw`w-16 h-16`};
border-width: 6px;
` : null)};
border-color: ${props => !props.isBlue ? 'rgba(255, 255, 255, 0.2)' : 'hsla(212, 92%, 43%, 0.2)'};
border-top-color: ${props => !props.isBlue ? 'rgb(255, 255, 255)' : 'hsl(212, 92%, 43%)'};
`;
const Spinner = ({ centered, ...props }: Props) => (
centered ? centered ?
<div className={classNames(`flex justify-center ${className}`, { 'm-20': size === 'large', 'm-6': size !== 'large' })}>
<div <div
className={classNames('spinner-circle spinner-white', { css={[
'spinner-lg': size === 'large', tw`flex justify-center`,
'spinner-sm': size === 'tiny', props.size === 'large' ? tw`m-20` : tw`m-6`,
})} ]}
/> >
<SpinnerComponent {...props}/>
</div> </div>
: :
<div <SpinnerComponent {...props}/>
className={classNames(`spinner-circle spinner-white ${className}`, {
'spinner-lg': size === 'large',
'spinner-sm': size === 'tiny',
})}
/>
); );
Spinner.DisplayName = 'Spinner';
export default Spinner; export default Spinner;

View file

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import classNames from 'classnames';
import { CSSTransition } from 'react-transition-group';
import Spinner, { SpinnerSize } from '@/components/elements/Spinner'; import Spinner, { SpinnerSize } from '@/components/elements/Spinner';
import Fade from '@/components/elements/Fade';
import tw from 'twin.macro';
interface Props { interface Props {
visible: boolean; visible: boolean;
@ -11,17 +11,17 @@ interface Props {
} }
const SpinnerOverlay = ({ size, fixed, visible, backgroundOpacity }: Props) => ( const SpinnerOverlay = ({ size, fixed, visible, backgroundOpacity }: Props) => (
<CSSTransition timeout={150} classNames={'fade'} in={visible} unmountOnExit={true}> <Fade timeout={150} in={visible} unmountOnExit>
<div <div
className={classNames('top-0 left-0 flex items-center justify-center w-full h-full rounded', { css={[
absolute: !fixed, tw`top-0 left-0 flex items-center justify-center w-full h-full rounded`,
fixed: fixed, !fixed ? tw`absolute` : tw`fixed`,
})} ]}
style={{ zIndex: 9999, background: `rgba(0, 0, 0, ${backgroundOpacity || 0.45})` }} style={{ zIndex: 9999, background: `rgba(0, 0, 0, ${backgroundOpacity || 0.45})` }}
> >
<Spinner size={size}/> <Spinner size={size}/>
</div> </div>
</CSSTransition> </Fade>
); );
export default SpinnerOverlay; export default SpinnerOverlay;

View file

@ -5,7 +5,7 @@ const SuspenseSpinner = ({ children }: { children?: React.ReactNode }) => (
<Suspense <Suspense
fallback={ fallback={
<div className={'mx-4 w-3/4 mr-4 flex items-center justify-center'}> <div className={'mx-4 w-3/4 mr-4 flex items-center justify-center'}>
<Spinner centered={true} size={'normal'}/> <Spinner centered/>
</div> </div>
} }
> >

View file

@ -69,7 +69,7 @@ export default () => {
<CSSTransition timeout={250} in={true} appear={true} classNames={'fade'}> <CSSTransition timeout={250} in={true} appear={true} classNames={'fade'}>
<div className={'bg-red-500 py-2'}> <div className={'bg-red-500 py-2'}>
<ContentContainer className={'flex items-center justify-center'}> <ContentContainer className={'flex items-center justify-center'}>
<Spinner size={'tiny'}/> <Spinner size={'small'}/>
<p className={'ml-2 text-sm text-red-100'}> <p className={'ml-2 text-sm text-red-100'}>
We're having some trouble connecting to your server, please wait... We're having some trouble connecting to your server, please wait...
</p> </p>

View file

@ -39,7 +39,7 @@ export default ({ backup, className }: Props) => {
{backup.completedAt ? {backup.completedAt ?
<FontAwesomeIcon icon={faArchive} className={'text-neutral-300'}/> <FontAwesomeIcon icon={faArchive} className={'text-neutral-300'}/>
: :
<Spinner size={'tiny'}/> <Spinner size={'small'}/>
} }
</div> </div>
<div className={'flex-1'}> <div className={'flex-1'}>

View file

@ -19,7 +19,7 @@ const RenameServerBox = () => {
return ( return (
<TitledGreyBox title={'Change Server Name'} className={'relative'}> <TitledGreyBox title={'Change Server Name'} className={'relative'}>
<SpinnerOverlay size={'normal'} visible={isSubmitting}/> <SpinnerOverlay visible={isSubmitting}/>
<Form className={'mb-0'}> <Form className={'mb-0'}>
<Field <Field
id={'name'} id={'name'}