How to create a simple reusable Vue Input Text component
Crate your configurable Vue component for Input Text
Working on a project I needed an Input text field with:
- basic animation for a better visual on focus and blur input state
- label, hint and icon support
- color and other configuration available
Something to use like :
<input-text
v-model="firstname"
placeholder="please input your firstname"
label="Firstname"
icon="persone"
hint="input your firstname"
color="#2196F3"
/>
See a live demo here
Data, props and computed properties
In order to set the component working properly we set
show
as data property (true/false)
the following props:
value
to manage the user input (required)placeholder
to set the input field placeholderlabel
to set the field labelhint
to set a hint message to visualize when user focus on the input fieldicon
the icon namecolor
any css valid color for the label and the field effect
and the computed properties:
filled
set the classhas__content
when input field has some valuehas__icon
set the classinput__has__icon
if the icon prop is set to a valuefocus__border
to change the background of the focus__border element (used for the animation) getting the value of thecolor
prop
Template
<template>
<div class="input__container">
<div :class="'input__effect ' + filled">
<input
type="text"
:value="value"
:placeholder="placeholder" @input="$emit('input',$event.target.value)"
:class="'effect ' + has__icon"
@focus="show=!show"
@blur="show=!show"
/>
<label :style="{color:color}">{{label}}</label>
<span class="focus__border" :style="focus__border"></span>
</div>
<span class="input__hint" v-if="show">{{hint}}</span>
<i class="material-icons input__icon">{{icon}}</i>
</div>
</template>
Script
<script>
export default{
data:()=>({
show: false
}),
computed:{
filled(){
if ( !this.show && this.value ) {
return 'has__content'
}
return ''
},
has__icon(){
if ( this.icon ){
return 'input__has__icon'
}
return
},
focus__border(){
return {
"background-color" : this.color
}
}
},
props: {
value : { type: String , required: false, default: ''},
label: { type: String , required: false, default: ''},
hint: { type: String, required:false , default:'hint'},
icon: { type: String, required: false , default: '' },
placeholder: { type: String, required: false, default: ''},
color: { type: String , required: false , default: 'indigo' }
},
}
</script>
CSS Style (scoped)
<style scoped>
::placeholder {
opacity:.4;
}
input[type="text"]{
color: #555;
width: 100%;
box-sizing: border-box;
letter-spacing: 1px;
outline:none;
}
.input__container {
width:100%; padding:.5rem .5rem 0 0
}
.input__effect {
float: left;
width: 100%;
margin: 1.5rem 0rem 1.5rem 0;
position: relative;
} /* necessary to give position: relative to parent. */
.input__icon {
position: relative;
left: 0rem;
top: -3.5rem;
opacity:.3;
}
.input__hint {
float: left;
width: 100%;
margin:-1.2rem 0 0 0;
position: relative;
font-size:.8rem; opacity:.6;
}
.input__has__icon {
padding-left: 2rem!important;
}
.effect {
border: 0;
padding: 4px 0;
border-bottom: 1px solid #ccc;
background-color: transparent;
}
.effect ~ .focus__border {
position: absolute;
bottom: 0;
left: 0;
width: 0;
height: 2px;
background-color: indigo;
transition: 0.4s;
}
.effect:focus ~ .focus__border ,
.has-content.effect ~ .focus__border {
width: 100%;
transition: 0.4s;
}
.effect ~ label {
position: absolute;
left: 0;
width: 100%;
top: -1.3rem;
color: #aaa;
transition: 0.3s;
z-index: -1;
letter-spacing: 0.5px;
}
.effect:focus ~ label,
.has__content.effect ~ label {
top: -1rem;
font-size: 12px;
color: indigo;
transition: 0.3s;
}
</style>
MIT License