Skip to content

Instantly share code, notes, and snippets.

@TheDutchCoder
Created April 29, 2016 14:37
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save TheDutchCoder/32f468b8357d237e2f6beaec3a89702a to your computer and use it in GitHub Desktop.
Save TheDutchCoder/32f468b8357d237e2f6beaec3a89702a to your computer and use it in GitHub Desktop.
Form Input element for Vue
<template>
<div class="input" :class="classList">
<label for="{{ id }}" class="input__label" @click="activate">{{ label }}<span class="required" v-if="required"> *</span></label>
<input id="{{ id }}" type="{{ type }}" class="input__input" required="{{ required }}" v-model="value | formatInput" @focus="activate" @blur="deactivate" :value="val"></input>
<div class="input__error" v-if="hasValue && !isValid" transition="input-error">{{ error }}</div>
</div>
</template>
<script>
// Helpers.
import { validate } from '../../helpers'
// Component export.
export default {
/**
* The initial component data.
*
* @return {Object} The component's data.
*/
data() {
return {
active: false,
value: ''
}
},
/**
* Passed down properties.
*
* @type {Object}
*/
props: {
id: {
type: String,
required: true
},
label: {
type: String,
required: true,
default: 'Label'
},
type: {
type: String,
default: 'text'
},
required: {
type: Boolean,
default: false
},
val: {
type: String,
default: ''
},
validation: {
type: String
},
error: {
type: String
}
},
/**
* Computed properties.
*
* @type {Object}
*/
computed: {
/**
* Generates a list of classes to use on the component.
*
* @return {Object} A map of classes to use.
*/
classList() {
return {
'is-active': this.active || this.hasValue,
'is-valid': this.hasValue && this.isValid,
'is-invalid': this.hasValue && !this.isValid
}
},
/**
* Determines if the form field has a value.
*
* @return {Boolean} If there's a value or not.
*/
hasValue() {
return this.value.length
},
/**
* Determines if the form field is validated.
*
* @return {Boolean} If the component is valid or not.
*/
isValid() {
let type = this.validation
let value = this.value
// Only validate when requested, otherwise assume validation is not
// required.
if (type) {
return validate(type, value)
} else {
return !!value
}
}
},
/**
* Initialize the component.
*/
ready() {
if (this.validation) {
switch (this.validation) {
case 'email':
this.error = 'Format: you@domain.com'
break
case 'password':
this.error = 'Minimum of 8 characters'
break
case 'name':
this.error = 'Minimum of 2 characters'
break
case 'address':
this.error = 'Address error'
break
case 'housenumber':
this.error = 'Housenumer error'
break
case 'tel':
this.error = 'Exactly 10 digits'
break
case 'postalcode':
this.error = 'Format: A1B 2C3'
break
case 'text':
this.error = 'Minimum of 2 characters'
break
default:
this.error = 'Not a valid value'
}
}
},
/**
* Component filters.
*
* @type {Object}
*/
filters: {
/**
* Formats input elements.
*
* @type {Object}
*/
formatInput: {
/**
* Reads the model and outputs the view.
*
* @param {String} val The input value
* @return {String} The formatted output.
*/
read: function(val) {
let area
let first
let second
switch (this.validation) {
case 'tel':
area = val.substr(0, 3)
first = val.substr(3, 3)
second = val.substr(6, 4)
if (area || first || second) {
val = '(' + area + ') ' + first + ' ' + second
}
break
case 'postalcode':
first = val.substr(0, 3)
second = val.substr(3, 3)
val = first.toUpperCase() + ' ' + second.toUpperCase()
break
default:
break
}
return val
},
/**
* Writes the model and reads the view.
*
* @param {String} val The formatted input.
* @return {String} The cleaned input.
*/
write: function(val) {
switch (this.validation) {
case 'tel':
val = val.replace(/\D/gmi, '')
break
case 'postalcode':
val = val.replace(/\s/gmi, '')
break
default:
break
}
return val
}
}
},
/**
* Component methods.
*
* @type {Object}
*/
methods: {
/**
* Activates the form field.
*/
activate() {
this.active = true
},
/**
* Deactivates the form field.
*/
deactivate() {
this.active = false
}
}
}
</script>
<style lang="scss">
@import '../../assets/sass/tools';
.input {
position: relative;
margin: $ui-padding 0 ($ui-padding * 2);
text-align: left;
font-size: $ui-font-size;
@include mq('desk') {
font-size: 14px;
}
}
.grid__item .input {
margin: 20px 0 30px;
}
.input__label {
display: block;
width: 100%;
position: absolute;
padding: $ui-padding 0;
color: lighten(color('black'), 50%);
text-align: left;
cursor: pointer;
transition: all .3s ease;
transform-origin: 0 50%;
}
.input__input {
display: block;
width: 100%;
border: 0 solid color('ui');
border-bottom-width: 1px;
padding: 10px 0;
font-size: $ui-font-size;
// background-color: color('ui-light');
@include transition('ui');
&:hover,
&:focus {
border-color: color('secondary', 'yellow');
outline: none;
}
}
.input__error {
position: absolute;
top: 105%;
width: 100%;
color: color('primary', 'red');
font-size: $ui-font-size-small;
text-align: right;
}
.is-active {
.input__label {
padding: 5px 0;
color: lighten(color('black'), 25%);
transform: translate(0, -65%) scale(.75);
}
}
.is-invalid {
.input__input {
border-color: color('primary', 'red');
}
}
// Transitions.
.input-error-transition {
@include transition('fast', 'opacity, transform');
}
.input-error-enter,
.input-error-leave {
opacity: 0;
transform: translate(0, 25%);
}
</style>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment