Skip to content

Commit 1c97dd4

Browse files
committed
Correctly center the spinner in buttons
1 parent 8c20158 commit 1c97dd4

File tree

4 files changed

+40
-51
lines changed

4 files changed

+40
-51
lines changed

resources/scripts/components/auth/LoginCheckpointContainer.tsx

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ import { httpErrorToHuman } from '@/api/http';
55
import LoginFormContainer from '@/components/auth/LoginFormContainer';
66
import { ActionCreator } from 'easy-peasy';
77
import { StaticContext } from 'react-router';
8-
import Spinner from '@/components/elements/Spinner';
98
import { useFormikContext, withFormik } from 'formik';
10-
import { object, string } from 'yup';
119
import useFlash from '@/plugins/useFlash';
1210
import { FlashStore } from '@/state/flashes';
1311
import Field from '@/components/elements/Field';
12+
import tw from 'twin.macro';
13+
import Button from '@/components/elements/Button';
1414

1515
interface Values {
1616
code: string;
@@ -29,13 +29,10 @@ const LoginCheckpointContainer = () => {
2929
const [ isMissingDevice, setIsMissingDevice ] = useState(false);
3030

3131
return (
32-
<LoginFormContainer
33-
title={'Device Checkpoint'}
34-
className={'w-full flex'}
35-
>
36-
<div className={'mt-6'}>
32+
<LoginFormContainer title={'Device Checkpoint'} css={tw`w-full flex`}>
33+
<div css={tw`mt-6`}>
3734
<Field
38-
light={true}
35+
light
3936
name={isMissingDevice ? 'recoveryCode' : 'code'}
4037
title={isMissingDevice ? 'Recovery Code' : 'Authentication Code'}
4138
description={
@@ -44,38 +41,35 @@ const LoginCheckpointContainer = () => {
4441
: 'Enter the two-factor token generated by your device.'
4542
}
4643
type={isMissingDevice ? 'text' : 'number'}
47-
autoFocus={true}
44+
autoFocus
4845
/>
4946
</div>
50-
<div className={'mt-6'}>
51-
<button
47+
<div css={tw`mt-6`}>
48+
<Button
49+
size={'xlarge'}
5250
type={'submit'}
53-
className={'btn btn-primary btn-jumbo'}
5451
disabled={isSubmitting}
52+
isLoading={isSubmitting}
5553
>
56-
{isSubmitting ?
57-
<Spinner size={'small'} className={'mx-auto'}/>
58-
:
59-
'Continue'
60-
}
61-
</button>
54+
Continue
55+
</Button>
6256
</div>
63-
<div className={'mt-6 text-center'}>
57+
<div css={tw`mt-6 text-center`}>
6458
<span
6559
onClick={() => {
6660
setFieldValue('code', '');
6761
setFieldValue('recoveryCode', '');
6862
setIsMissingDevice(s => !s);
6963
}}
70-
className={'cursor-pointer text-xs text-neutral-500 tracking-wide uppercase no-underline hover:text-neutral-700'}
64+
css={tw`cursor-pointer text-xs text-neutral-500 tracking-wide uppercase no-underline hover:text-neutral-700`}
7165
>
7266
{!isMissingDevice ? 'I\'ve Lost My Device' : 'I Have My Device'}
7367
</span>
7468
</div>
75-
<div className={'mt-6 text-center'}>
69+
<div css={tw`mt-6 text-center`}>
7670
<Link
7771
to={'/auth/login'}
78-
className={'text-xs text-neutral-500 tracking-wide uppercase no-underline hover:text-neutral-700'}
72+
css={tw`text-xs text-neutral-500 tracking-wide uppercase no-underline hover:text-neutral-700`}
7973
>
8074
Return to Login
8175
</Link>

resources/scripts/components/auth/LoginContainer.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,7 @@ const LoginContainer = ({ isSubmitting, setFieldValue, values, submitForm, handl
3636
return (
3737
<React.Fragment>
3838
{ref.current && ref.current.render()}
39-
<LoginFormContainer
40-
title={'Login to Continue'}
41-
css={tw`w-full flex`}
42-
onSubmit={submit}
43-
>
39+
<LoginFormContainer title={'Login to Continue'} css={tw`w-full flex`} onSubmit={submit}>
4440
<Field
4541
type={'text'}
4642
label={'Username or Email'}

resources/scripts/components/auth/ResetPasswordContainer.tsx

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,16 @@ import Spinner from '@/components/elements/Spinner';
1111
import { Formik, FormikHelpers } from 'formik';
1212
import { object, ref, string } from 'yup';
1313
import Field from '@/components/elements/Field';
14-
15-
type Props = Readonly<RouteComponentProps<{ token: string }> & {}>;
14+
import Input from '@/components/elements/Input';
15+
import tw from 'twin.macro';
16+
import Button from '@/components/elements/Button';
1617

1718
interface Values {
1819
password: string;
1920
passwordConfirmation: string;
2021
}
2122

22-
export default ({ match, history, location }: Props) => {
23+
export default ({ match, location }: RouteComponentProps<{ token: string }>) => {
2324
const [ email, setEmail ] = useState('');
2425

2526
const { clearFlashes, addFlash } = useStoreActions((actions: Actions<ApplicationStore>) => actions.flashes);
@@ -62,46 +63,43 @@ export default ({ match, history, location }: Props) => {
6263
{({ isSubmitting }) => (
6364
<LoginFormContainer
6465
title={'Reset Password'}
65-
className={'w-full flex'}
66+
css={tw`w-full flex`}
6667
>
6768
<div>
6869
<label>Email</label>
69-
<input className={'input'} value={email} disabled={true}/>
70+
<Input value={email} light disabled/>
7071
</div>
71-
<div className={'mt-6'}>
72+
<div css={tw`mt-6`}>
7273
<Field
73-
light={true}
74+
light
7475
label={'New Password'}
7576
name={'password'}
7677
type={'password'}
7778
description={'Passwords must be at least 8 characters in length.'}
7879
/>
7980
</div>
80-
<div className={'mt-6'}>
81+
<div css={tw`mt-6`}>
8182
<Field
82-
light={true}
83+
light
8384
label={'Confirm New Password'}
8485
name={'passwordConfirmation'}
8586
type={'password'}
8687
/>
8788
</div>
88-
<div className={'mt-6'}>
89-
<button
89+
<div css={tw`mt-6`}>
90+
<Button
91+
size={'xlarge'}
9092
type={'submit'}
91-
className={'btn btn-primary btn-jumbo'}
9293
disabled={isSubmitting}
94+
isLoading={isSubmitting}
9395
>
94-
{isSubmitting ?
95-
<Spinner size={'small'} className={'mx-auto'}/>
96-
:
97-
'Reset Password'
98-
}
99-
</button>
96+
Reset Password
97+
</Button>
10098
</div>
101-
<div className={'mt-6 text-center'}>
99+
<div css={tw`mt-6 text-center`}>
102100
<Link
103101
to={'/auth/login'}
104-
className={'text-xs text-neutral-500 tracking-wide no-underline uppercase hover:text-neutral-600'}
102+
css={tw`text-xs text-neutral-500 tracking-wide no-underline uppercase hover:text-neutral-600`}
105103
>
106104
Return to Login
107105
</Link>

resources/scripts/components/elements/Button.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React from 'react';
22
import styled, { css } from 'styled-components/macro';
33
import tw from 'twin.macro';
4+
import Spinner from '@/components/elements/Spinner';
45

56
interface Props {
67
isLoading?: boolean;
@@ -10,9 +11,9 @@ interface Props {
1011
}
1112

1213
const StyledButton = styled.button<Omit<Props, 'isLoading'>>`
13-
${tw`rounded p-2 uppercase tracking-wide text-sm transition-all duration-150`};
14+
${tw`relative inline-block rounded p-2 uppercase tracking-wide text-sm transition-all duration-150`};
1415
15-
${props => props.isSecondary && css<Props>`
16+
${props => props.isSecondary && css<Props>`
1617
${tw`border border-neutral-600 bg-transparent text-neutral-200`};
1718
1819
&:hover:not(:disabled) {
@@ -79,8 +80,8 @@ type ComponentProps = Omit<JSX.IntrinsicElements['button'], 'ref' | keyof Props>
7980
const Button: React.FC<ComponentProps> = ({ children, isLoading, ...props }) => (
8081
<StyledButton {...props}>
8182
{isLoading &&
82-
<div css={tw`w-full flex absolute justify-center`} style={{ marginLeft: '-0.75rem' }}>
83-
<div className={'spinner-circle spinner-white spinner-sm'}/>
83+
<div css={tw`flex absolute justify-center items-center w-full h-full left-0 top-0`}>
84+
<Spinner size={'small'}/>
8485
</div>
8586
}
8687
<span css={isLoading ? tw`text-transparent` : undefined}>

0 commit comments

Comments
 (0)