Redesign Signup Modal

This commit is contained in:
Justin Edmund 2022-02-28 14:16:04 -08:00
parent d656ba7eba
commit 829146f1bd
5 changed files with 126 additions and 121 deletions

View file

@ -132,5 +132,7 @@
.text { .text {
color: inherit; color: inherit;
display: block;
width: 100%;
} }
} }

View file

@ -83,14 +83,7 @@ const HeaderMenu = (props: Props) => {
/> />
) : null} ) : null}
<li className="MenuItem" onClick={openSignupModal}> <SignupModal />
<span>Sign up</span>
</li>
{signupOpen ? (
<SignupModal
close={closeSignupModal}
/>
) : null}
</div> </div>
</ul> </ul>

View file

@ -1,67 +1,46 @@
.SignupForm { .Signup.Dialog form {
padding: 16px;
}
.SignupForm form {
margin: 0;
}
.SignupForm #fields {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 4px; gap: $unit / 2;
margin-bottom: 8px; margin-bottom: $unit;
}
#ModalTop { .Button {
display: flex; font-size: $font-regular;
flex-direction: row; padding: ($unit * 1.5) ($unit * 2);
align-items: center; width: 100%;
margin-bottom: 16px;
margin-right: -8px;
}
#ModalTop h1 { &.btn-disabled {
margin: 0; background: $grey-90;
font-size: $font-xlarge; color: $grey-70;
text-align: left; }
flex-grow: 1;
}
#ModalTop i { &:not(.btn-disabled) {
padding: 8px; background: $grey-90;
} color: $grey-40;
#ModalTop i:hover { &:hover {
cursor: pointer; background: $grey-80;
} }
}
}
#ModalTop i:hover svg { .terms {
color: #444; color: $grey-40;
} font-size: $font-small;
line-height: 1.2;
margin-top: $unit;
text-align: center;
#ModalTop svg { a {
color: #888; color: $blue;
height: 18px;
width: 18px;
transform: rotate(45deg);
}
#ModalBottom { &:hover {
display: flex; color: darken($blue, 30);
flex-direction: row; }
justify-content: flex-end; }
} }
#ModalBottom a { input {
color: #666; background: $grey-90;
font-size: $font-regular; }
font-weight: 500;
flex-grow: 1;
display: flex;
align-items: center;
}
#ModalBottom .Button {
min-width: 88px;
} }

View file

@ -2,6 +2,8 @@ import React, { useState } from 'react'
import { withCookies, Cookies } from 'react-cookie' import { withCookies, Cookies } from 'react-cookie'
import { createPortal } from 'react-dom' import { createPortal } from 'react-dom'
import * as Dialog from '@radix-ui/react-dialog'
import api from '~utils/api' import api from '~utils/api'
import { accountState } from '~utils/accountState' import { accountState } from '~utils/accountState'
@ -10,12 +12,10 @@ import Fieldset from '~components/Fieldset'
import Modal from '~components/Modal' import Modal from '~components/Modal'
import Overlay from '~components/Overlay' import Overlay from '~components/Overlay'
import CrossIcon from '~public/icons/Cross.svg'
import './index.scss' import './index.scss'
interface Props { interface Props {}
cookies: Cookies
close: () => void
}
interface ErrorMap { interface ErrorMap {
[index: string]: string [index: string]: string
@ -28,6 +28,7 @@ interface ErrorMap {
const emailRegex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ const emailRegex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
const SignupModal = (props: Props) => { const SignupModal = (props: Props) => {
// Set up error handling
const [formValid, setFormValid] = useState(false) const [formValid, setFormValid] = useState(false)
const [errors, setErrors] = useState<ErrorMap>({ const [errors, setErrors] = useState<ErrorMap>({
username: '', username: '',
@ -36,6 +37,7 @@ const SignupModal = (props: Props) => {
passwordConfirmation: '' passwordConfirmation: ''
}) })
// Set up form refs
const usernameInput = React.createRef<HTMLInputElement>() const usernameInput = React.createRef<HTMLInputElement>()
const emailInput = React.createRef<HTMLInputElement>() const emailInput = React.createRef<HTMLInputElement>()
const passwordInput = React.createRef<HTMLInputElement>() const passwordInput = React.createRef<HTMLInputElement>()
@ -153,58 +155,70 @@ const SignupModal = (props: Props) => {
} }
return ( return (
createPortal( <Dialog.Root>
<div> <Dialog.Trigger asChild>
<Modal <li className="MenuItem">
title="Sign up" <span>Sign up</span>
styleName="SignupForm" </li>
close={ () => {} } </Dialog.Trigger>
> <Dialog.Portal>
<Dialog.Content className="Signup Dialog" onOpenAutoFocus={ (event) => event.preventDefault() }>
<div className="DialogHeader">
<Dialog.Title className="DialogTitle">Sign up</Dialog.Title>
<Dialog.Close className="DialogClose" asChild>
<span>
<CrossIcon />
</span>
</Dialog.Close>
</div>
<form className="form" onSubmit={process}> <form className="form" onSubmit={process}>
<div id="fields"> <Fieldset
<Fieldset fieldName="username"
fieldName="username" placeholder="Username"
placeholder="Username" onBlur={check}
onBlur={check} onChange={handleChange}
onChange={handleChange} error={errors.username}
error={errors.username} ref={usernameInput}
ref={usernameInput} />
/>
<Fieldset <Fieldset
fieldName="email" fieldName="email"
placeholder="Email address" placeholder="Email address"
onBlur={check} onBlur={check}
onChange={handleChange} onChange={handleChange}
error={errors.email} error={errors.email}
ref={emailInput} ref={emailInput}
/> />
<Fieldset <Fieldset
fieldName="password" fieldName="password"
placeholder="Password" placeholder="Password"
onChange={handleChange} onChange={handleChange}
error={errors.password} error={errors.password}
ref={passwordInput} ref={passwordInput}
/> />
<Fieldset <Fieldset
fieldName="confirm_password" fieldName="confirm_password"
placeholder="Password (again)" placeholder="Password (again)"
onChange={handleChange} onChange={handleChange}
error={errors.passwordConfirmation} error={errors.passwordConfirmation}
ref={passwordConfirmationInput} ref={passwordConfirmationInput}
/> />
</div>
<div id="ModalBottom"> <Button disabled={!formValid}>Sign up</Button>
<Button disabled={!formValid}>Sign up</Button>
</div> <Dialog.Description className="terms">
By signing up, I agree to the<br /><a href="#">Terms and Conditions</a> and <a href="#">Usage Guidelines</a>.
</Dialog.Description>
</form> </form>
</Modal> </Dialog.Content>
<Overlay onClick={props.close} /> <Dialog.Overlay className="Overlay" />
</div>, </Dialog.Portal>
document.body </Dialog.Root>
)
) )
} }

View file

@ -90,16 +90,18 @@ select {
} }
.Dialog { .Dialog {
$multiplier: 4;
animation: 0.5s cubic-bezier(0.16, 1, 0.3, 1) 0s 1 normal none running openModal; animation: 0.5s cubic-bezier(0.16, 1, 0.3, 1) 0s 1 normal none running openModal;
background: white; background: white;
border-radius: $unit; border-radius: $unit;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: $unit * 3; gap: $unit * $multiplier;
height: auto; height: auto;
min-width: $unit * 48; min-width: $unit * 48;
min-height: $unit * 12; min-height: $unit * 12;
padding: $unit * 3; padding: $unit * $multiplier;
position: absolute; position: absolute;
top: 50%; top: 50%;
left: 50%; left: 50%;
@ -108,28 +110,43 @@ select {
.DialogHeader { .DialogHeader {
display: flex; display: flex;
gap: $unit;
.left {
display: flex;
flex-direction: column;
flex-grow: 1;
gap: $unit;
p {
color: $grey-40;
font-size: $font-small;
line-height: 1.25;
}
}
} }
.DialogClose { .DialogClose {
background: transparent; background: transparent;
height: 21px;
width: 21px;
&:hover { &:hover {
cursor: pointer; cursor: pointer;
svg { svg {
fill: $grey-00; fill: $error;
} }
} }
svg { svg {
fill: $grey-40; fill: $grey-40;
float: right;
height: 24px;
width: 24px;
} }
} }
.DialogTitle { .DialogTitle {
font-size: $font-large; font-size: $font-xlarge;
flex-grow: 1; flex-grow: 1;
} }