Skip to content

Instantly share code, notes, and snippets.

@OwenMelbz
Created May 1, 2018 16:39
Show Gist options
  • Save OwenMelbz/49fea7873e1c6eab66b43e40bee237dd to your computer and use it in GitHub Desktop.
Save OwenMelbz/49fea7873e1c6eab66b43e40bee237dd to your computer and use it in GitHub Desktop.
Simple Form with Floating Labels
.input {
max-width: 100%;
padding: 5px 10px;
transition: border-color .2s ease;
border-radius: 0;
border-color: $black-1;
outline: none;
font-size: 16px;
-webkit-appearance: none;
&:focus {
border-color: $grey-2;
}
.has-errors & {
border-color: $pink-3;
}
}
.label {
padding: 7px 10px;
transform: translate(0);
transform-origin: top left;
transition: transform .3s ease;
font-size: 16px;
pointer-events: none;
.filled & {
transform: scale(.5) translate(-10px, -40px);
}
.has-errors & {
color: $pink-1;
}
}
<template>
<div class="mt2 pb2">
<form>
<div v-if="errorToDisplay" class="message-to-read bg-pink p1 mb4" style="padding: 10px;">
{{ errorToDisplay }}
</div>
<div v-if="messageToDisplay" class="message-to-read bg-white mb4" style="padding: 10px;">
{{ messageToDisplay }}
</div>
<div class="grid clearfix">
<div class="width-12-12 width-6-12@m pr2@m" :class="{ filled: isFilled('name') || isFocused('name'), 'has-errors': hasErrors('name') }">
<label for="your-name" class="absolute block label">Your name</label>
<input @blur="blurField()" @focus="focusField('name')" class="input width-12-12 border" id="your-name" name="your-name" autocomplete="name" v-model="formData.name" />
</div>
<div class="width-12-12 width-6-12@m mt2 mt0@m" :class="{ filled: isFilled('email') || isFocused('email'), 'has-errors': hasErrors('email') }">
<label for="your-email" class="absolute block label">Your email</label>
<input @blur="blurField()" @focus="focusField('email')" class="input width-12-12 border" id="your-email" name="your-email" autocomplete="email" type="email" v-model="formData.email" />
</div>
<div class="mt2 width-12-12" :class="{ filled: isFilled('message') || isFocused('message'), 'has-errors': hasErrors('message') }">
<label class="absolute block label" for="your-message">Your message</label>
<textarea @blur="blurField()" @focus="focusField('message')" class="block input width-12-12 border" id="your-message" name="your-message" v-model="formData.message" style="min-height: 150px"></textarea>
</div>
</div>
<div class="width-12-12 mt2 right-align@m">
<button class="button border-none bg-none" type="submit" @click.prevent="submitForm">
<span class="underline fs-3 fs-4@m weight-600">
{{ buttonText() }}
</span>
</button>
</div>
</form>
</div>
</template>
<script>
const each = require('lodash.foreach');
export default {
props: {
endpoint: {
type: String,
required: true,
},
unknownErrorMessage: {
type: String,
default: 'Oh dear, something’s gone a bit wrong. Please email us on [email protected] instead, and we’ll get back to you very shortly.',
},
},
data() {
return {
busy: false,
formData: {
name: '',
email: '',
message: '',
_token: document.head.querySelector('meta[name="csrf-token"]').content,
},
formErrors: {},
focusedField: null,
errorToDisplay: null,
messageToDisplay: null,
}
},
computed: {
formIsValid() {
return this.formData.name && this.formData.email && this.formData.message;
},
},
mounted() {
window.addEventListener('online', () => {
this.$nextTick(() => this.$forceUpdate());
});
window.addEventListener('offline', () => {
this.$nextTick(() => this.$forceUpdate());
});
},
methods: {
buttonText() {
if (!navigator.onLine) {
return 'No internet detected'
}
if (this.busy) {
return 'Sending...';
}
return 'Send your message';
},
blurField() {
this.focusedField = null;
},
focusField(name) {
this.focusedField = name;
},
isFocused(name) {
return this.focusedField == name;
},
isFilled(field) {
return this.formData[field];
},
hasErrors(field) {
let found = false;
each(this.formErrors, (errorMessage, fieldName) => {
found = fieldName == field ? true : found;
});
return found;
},
submitForm() {
this.busy = true;
this.formErrors = {};
this.errorToDisplay = null;
this.messageToDisplay = null;
axios.post(this.endpoint, this.formData)
.then(resp => {
this.busy = false;
if (typeof resp.data !== 'object') {
this.errorToDisplay = this.unknownErrorMessage;
return this.$nextTick(() => scrollToErrors.scrollNow());
}
this.formData.message = '';
this.messageToDisplay = resp.data.message;
try {
dataLayer.push({
event: 'Short Enquiry Form',
eventCategory: 'Form Submit',
eventAction: 'Short Enquiry Form',
eventLabel: window.location.href,
eventValue: 1,
});
} catch (err) {
console.log(err);
}
return this.$nextTick(() => scrollToErrors.scrollNow());
})
.catch(err => {
this.busy = false;
if (!err.response || err.response.status !== 422 || typeof err.response.data !== 'object') {
this.errorToDisplay = this.unknownErrorMessage;
return this.$nextTick(() => scrollToErrors.scrollNow());
}
this.formErrors = err.response.data;
each(this.formErrors, errorMessage => {
if (!this.errorToDisplay) {
this.errorToDisplay = errorMessage;
}
});
try {
dataLayer.push({
event: 'Short Enquiry Form Failed',
eventCategory: 'Failed Form Submission',
eventAction: 'Short Enquiry Form Failed',
eventLabel: this.errorToDisplay,
eventValue: 1,
});
} catch (err) {
console.log(err);
}
this.$nextTick(() => scrollToErrors.scrollNow());
});
},
},
};
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment