admin-fe/src/components/MDinput/index.vue

408 lines
11 KiB
Vue

<template>
<div class="material-input__component" :class="computedClasses">
<input
v-if="type === 'email'"
type="email"
class="material-input"
:name="name"
:id="id"
:placeholder="placeholder"
v-model="valueCopy"
:readonly="readonly"
:disabled="disabled"
:autocomplete="autocomplete"
:required="required"
@focus="handleFocus(true)"
@blur="handleFocus(false)"
@input="handleModelInput"
>
<input
v-if="type === 'url'"
type="url"
class="material-input"
:name="name"
:id="id"
:placeholder="placeholder"
v-model="valueCopy"
:readonly="readonly"
:disabled="disabled"
:autocomplete="autocomplete"
:required="required"
@focus="handleFocus(true)"
@blur="handleFocus(false)"
@input="handleModelInput"
>
<input
v-if="type === 'number'"
type="number"
class="material-input"
:name="name"
:id="id"
:placeholder="placeholder"
v-model="valueCopy"
:readonly="readonly"
:disabled="disabled"
:autocomplete="autocomplete"
:max="max"
:min="min"
:minlength="minlength"
:maxlength="maxlength"
:required="required"
@focus="handleFocus(true)"
@blur="handleFocus(false)"
@input="handleModelInput"
>
<input
v-if="type === 'password'"
type="password"
class="material-input"
:name="name"
:id="id"
:placeholder="placeholder"
v-model="valueCopy"
:readonly="readonly"
:disabled="disabled"
:autocomplete="autocomplete"
:max="max"
:min="min"
:required="required"
@focus="handleFocus(true)"
@blur="handleFocus(false)"
@input="handleModelInput"
>
<input
v-if="type === 'tel'"
type="tel"
class="material-input"
:name="name"
:id="id"
:placeholder="placeholder"
v-model="valueCopy"
:readonly="readonly"
:disabled="disabled"
:autocomplete="autocomplete"
:required="required"
@focus="handleFocus(true)"
@blur="handleFocus(false)"
@input="handleModelInput"
>
<input
v-if="type === 'text'"
type="text"
class="material-input"
:name="name"
:id="id"
:placeholder="placeholder"
v-model="valueCopy"
:readonly="readonly"
:disabled="disabled"
:autocomplete="autocomplete"
:minlength="minlength"
:maxlength="maxlength"
:required="required"
@focus="handleFocus(true)"
@blur="handleFocus(false)"
@input="handleModelInput"
>
<span class="material-input-bar"></span>
<label class="material-label">
<slot></slot>
</label>
<div v-if="errorMessages" class="material-errors">
<div v-for="error in computedErrors" class="material-error">
{{ error }}
</div>
</div>
</div>
</template>
<script>
export default {
name: 'material-input',
computed: {
computedErrors() {
return typeof this.errorMessages === 'string'
? [this.errorMessages] : this.errorMessages
},
computedClasses() {
return {
'material--active': this.focus,
'material--disabled': this.disabled,
'material--has-errors': Boolean(
!this.valid ||
(this.errorMessages && this.errorMessages.length)),
'material--raised': Boolean(
this.focus ||
this.valueCopy || // has value
(this.placeholder && !this.valueCopy)) // has placeholder
}
}
},
data() {
return {
valueCopy: null,
focus: false,
valid: true
}
},
beforeMount() {
// Here we are following the Vue2 convention on custom v-model:
// https://github.com/vuejs/vue/issues/2873#issuecomment-223759341
this.copyValue(this.value)
},
methods: {
handleModelInput(event) {
this.$emit('input', event.target.value, event)
this.handleValidation()
},
handleFocus(focused) {
this.focus = focused
},
handleValidation() {
this.valid = this.$el ? this.$el.querySelector(
'.material-input').validity.valid : this.valid
},
copyValue(value) {
this.valueCopy = value
this.handleValidation()
}
},
watch: {
value(newValue) {
this.copyValue(newValue)
}
},
props: {
id: {
type: String,
default: null
},
name: {
type: String,
default: null
},
type: {
type: String,
default: 'text'
},
value: {
default: null
},
placeholder: {
type: String,
default: null
},
readonly: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
},
min: {
type: String,
default: null
},
max: {
type: String,
default: null
},
minlength: {
type: Number,
default: null
},
maxlength: {
type: Number,
default: null
},
required: {
type: Boolean,
default: true
},
autocomplete: {
type: String,
default: 'off'
},
errorMessages: {
type: [Array, String],
default: null
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
// Fonts:
$font-size-base: 16px;
$font-size-small: 18px;
$font-size-smallest: 12px;
$font-weight-normal: normal;
// Utils
$spacer: 12px;
$transition: 0.2s ease all;
// Base clases:
%base-bar-pseudo {
content: '';
height: 1px;
width: 0;
bottom: 0;
position: absolute;
transition: $transition;
}
// Mixins:
@mixin slided-top() {
top: -2 * $spacer;
font-size: $font-size-small;
}
// Component:
.material-input__component {
/*margin-top: 30px;*/
position: relative;
* {
box-sizing: border-box;
}
.material-input {
font-size: $font-size-base;
padding: $spacer $spacer $spacer $spacer / 2;
display: block;
width: 100%;
border: none;
border-radius: 0;
&:focus {
outline: none;
border: none;
border-bottom: 1px solid transparent; // fixes the height issue
}
}
.material-label {
font-size: $font-size-base;
font-weight: $font-weight-normal;
position: absolute;
pointer-events: none;
left: 0;
top: $spacer;
transition: $transition;
}
.material-input-bar {
position: relative;
display: block;
width: 100%;
&:before {
@extend %base-bar-pseudo;
left: 50%;
}
&:after {
@extend %base-bar-pseudo;
right: 50%;
}
}
// Disabled state:
&.material--disabled {
.material-input {
border-bottom-style: dashed;
}
}
// Raised state:
&.material--raised {
.material-label {
@include slided-top();
}
}
// Active state:
&.material--active {
.material-input-bar {
&:before,
&:after {
width: 50%;
}
}
}
// Errors:
.material-errors {
position: relative;
overflow: hidden;
.material-error {
font-size: $font-size-smallest;
line-height: $font-size-smallest + 2px;
overflow: hidden;
margin-top: 0;
padding-top: $spacer / 2;
padding-right: $spacer / 2;
padding-left: 0;
}
}
}
// Theme:
$color-white: white;
$color-grey: #9E9E9E;
$color-grey-light: #E0E0E0;
$color-blue: #2196F3;
$color-red: #F44336;
$color-black: black;
.material-input__component {
background: $color-white;
.material-input {
background: none;
color: $color-black;
text-indent: 30px;
border-bottom: 1px solid $color-grey-light;
}
.material-label {
color: $color-grey;
}
.material-input-bar {
&:before,
&:after {
background: $color-blue;
}
}
// Active state:
&.material--active {
.material-label {
color: $color-blue;
}
}
// Errors:
&.material--has-errors {
// These styles are required
// for custom validation:
&.material--active .material-label {
color: $color-red;
}
.material-input-bar {
&:before,
&:after {
background: $color-red;
}
}
.material-errors {
color: $color-red;
}
}
}
</style>