Add validation to RSVP field

Add fade in/out of bg image
Clean up layout
This commit is contained in:
2017-02-12 18:42:40 +00:00
parent bbf757116d
commit 7ddacfed94
5 changed files with 145 additions and 16 deletions

View File

@@ -21,7 +21,7 @@ class Html extends React.Component {
<meta name="description" content={description} />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="apple-touch-icon" href="apple-touch-icon.png" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Poppins:500|Yesteryear" />
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Palanquin+Dark|Yesteryear" />
{style && <style id="css" dangerouslySetInnerHTML={{ __html: style }} />}
</head>
<body>

View File

@@ -27,7 +27,7 @@
}
.rsvpGroup {
width: 60%;
width: 65%;
}
.rsvpButtonGroup {
@@ -64,6 +64,27 @@
box-shadow: 0 0 4px #fff;
}
.inputInvalid {
animation: shake 0.5s linear;
}
.invalidMsg {
display: none;
padding: 10px 16px;
border: 4px solid var(--colour-red);
border-radius: 14px;
background: transparent;
color: #fff;
text-decoration: none;
font-size: 18px;
line-height: 1.3333333;
transition: opacity ease-in-out 0.25s, box-shadow ease-in-out 0.25s;
}
.invalidMsgVisible {
display: inline-block;
}
.button {
display: block;
box-sizing: border-box;
@@ -89,3 +110,28 @@
opacity: 1;
box-shadow: 0 0 4px #fff;
}
@keyframes shake {
8%,
41% {
transform: translateX(-10px);
}
25%,
58% {
transform: translateX(10px);
}
75% {
transform: translateX(-5px);
}
92% {
transform: translateX(5px);
}
0%,
100% {
transform: translateX(0);
}
}

View File

@@ -1,8 +1,12 @@
import React from 'react';
import React, { PropTypes } from 'react';
import withStyles from 'isomorphic-style-loader/lib/withStyles';
import s from './RsvpButton.css';
import fetch from '../../core/fetch';
class RsvpButton extends React.Component {
static propTypes = {
focusCallback: PropTypes.func,
};
constructor(props) {
super(props);
@@ -13,7 +17,45 @@ class RsvpButton extends React.Component {
this.setState({ showRsvpForm: true });
setTimeout(() => {
this.rsvpInput.focus();
}, 250);
}, 500);
}
handleRsvpFocus = () => {
if (this.props.focusCallback) {
this.props.focusCallback(true);
}
this.setState({ inputError: false });
}
handleRsvpBlur = () => {
if (this.props.focusCallback) {
this.props.focusCallback(false);
}
}
handleFormSubmit = (event) => {
event.preventDefault();
fetch(`rsvp/${this.rsvpInput.value}`)
.then(data => {
if (data.status === 200) {
location.href = data.url;
} else {
this.setState({
inputInvalid: true,
inputInvalidMsg: 'Sorry, that RSVP code is invalid.',
});
setTimeout(() => {
this.setState({
inputInvalid: false,
});
}, 500);
}
}).catch(() => {
this.setState({
inputInvalid: true,
inputInvalidMsg: 'Sorry, there was a problem validating your RSVP code, please try again later.',
});
});
}
render() {
@@ -35,14 +77,19 @@ class RsvpButton extends React.Component {
this.state.showRsvpForm ? s.rsvpFormVisible : null,
].join(' ')}
>
<form method="post" action="/rsvp">
<form method="post" action="/rsvp" onSubmit={this.handleFormSubmit}>
<div className={[s.formGroup, s.rsvpGroup].join(' ')}>
<label className={s.label} htmlFor="code">
Enter your RSVP code
</label>
<input
className={s.input}
className={[
s.input,
this.state.inputInvalid ? s.inputInvalid : null,
].join(' ')}
ref={(input) => { this.rsvpInput = input; }}
onFocus={this.handleRsvpFocus}
onBlur={this.handleRsvpBlur}
id="code"
type="text"
name="code"
@@ -55,6 +102,15 @@ class RsvpButton extends React.Component {
</button>
</div>
</form>
<div
className={[
s.invalidMsg,
this.state.inputInvalidMsg ? s.invalidMsgVisible : null,
].join(' ')}
role="alert"
>
{this.state.inputInvalidMsg}
</div>
</div>
</div>
);

View File

@@ -9,11 +9,17 @@
position: fixed;
width: 100vw;
height: 100vh;
background-image: url('/img/church_2500.jpg');
background: url('/img/church_2500.jpg');
background-size: cover;
background-position-x: 40%;
background-position-x: 50%;
background-color: #c1c1c1;
filter: grayscale(1) brightness(0.4);
z-index: -1;
transition: filter ease-in-out 0.5s;
}
.churchImageFocus {
filter: grayscale(0.4) brightness(0.6);
}
.container {
@@ -25,19 +31,23 @@
max-width: 640px;
margin: 0 auto 40px;
text-align: center;
color: var(--colour-nearblack);
color: #fff;
font-size: 1.3em;
}
.headings > * {
margin: 0 0 20px;
font-size: 2.5em;
.headings h1 {
margin: 0 0 10px;
}
.headings h2 {
margin: 0 0 60px;
}
.headings h3 {
margin-top: 40px;
margin: 0 0 20px;
font-family: var(--font-family-cursive);
}
.rsvp {
padding-top: 15vh;
padding-top: 10vh;
}

View File

@@ -4,10 +4,25 @@ import s from './Home.css';
import RsvpButton from '../../components/RsvpButton';
class Home extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
focusChurchImage = (focus) => {
this.setState({ focusChurch: focus });
}
render() {
return (
<div className={s.root}>
<div className={s.churchImage} />
<div
className={[
s.churchImage,
this.state.focusChurch ? s.churchImageFocus : null,
].join(' ')}
/>
<div className={s.container}>
<div className={s.headings}>
<h1>Daniel and Hana</h1>
@@ -15,7 +30,9 @@ class Home extends React.Component {
<h3>- 27th July 2017 -</h3>
</div>
<div className={s.rsvp}>
<RsvpButton />
<RsvpButton
focusCallback={this.focusChurchImage}
/>
</div>
</div>
</div>