You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

277 lines
7.6 KiB

5 years ago
4 years ago
5 years ago
  1. <template>
  2. <div class="material-input__component" :class="computedClasses">
  3. <div :class="{iconClass:icon}">
  4. <i class="el-input__icon material-input__icon" :class="['el-icon-' + icon]" v-if="icon"></i>
  5. <input v-if="type === 'email'" type="email" class="material-input" :name="name" :placeholder="fillPlaceHolder" v-model="currentValue"
  6. :readonly="readonly" :disabled="disabled" :autoComplete="autoComplete" :required="required" @focus="handleMdFocus"
  7. @blur="handleMdBlur" @input="handleModelInput">
  8. <input v-if="type === 'url'" type="url" class="material-input" :name="name" :placeholder="fillPlaceHolder" v-model="currentValue"
  9. :readonly="readonly" :disabled="disabled" :autoComplete="autoComplete" :required="required" @focus="handleMdFocus"
  10. @blur="handleMdBlur" @input="handleModelInput">
  11. <input v-if="type === 'number'" type="number" class="material-input" :name="name" :placeholder="fillPlaceHolder" v-model="currentValue"
  12. :step="step" :readonly="readonly" :disabled="disabled" :autoComplete="autoComplete" :max="max" :min="min" :minlength="minlength"
  13. :maxlength="maxlength" :required="required" @focus="handleMdFocus" @blur="handleMdBlur" @input="handleModelInput">
  14. <input v-if="type === 'password'" type="password" class="material-input" :name="name" :placeholder="fillPlaceHolder" v-model="currentValue"
  15. :readonly="readonly" :disabled="disabled" :autoComplete="autoComplete" :max="max" :min="min" :required="required" @focus="handleMdFocus"
  16. @blur="handleMdBlur" @input="handleModelInput">
  17. <input v-if="type === 'tel'" type="tel" class="material-input" :name="name" :placeholder="fillPlaceHolder" v-model="currentValue"
  18. :readonly="readonly" :disabled="disabled" :autoComplete="autoComplete" :required="required" @focus="handleMdFocus"
  19. @blur="handleMdBlur" @input="handleModelInput">
  20. <input v-if="type === 'text'" type="text" class="material-input" :name="name" :placeholder="fillPlaceHolder" v-model="currentValue"
  21. :readonly="readonly" :disabled="disabled" :autoComplete="autoComplete" :minlength="minlength" :maxlength="maxlength"
  22. :required="required" @focus="handleMdFocus" @blur="handleMdBlur" @input="handleModelInput">
  23. <span class="material-input-bar"></span>
  24. <label class="material-label">
  25. <slot></slot>
  26. </label>
  27. </div>
  28. </div>
  29. </template>
  30. <script>
  31. // source:https://github.com/wemake-services/vue-material-input/blob/master/src/components/MaterialInput.vue
  32. export default {
  33. name: 'md-input',
  34. props: {
  35. icon: String,
  36. name: String,
  37. type: {
  38. type: String,
  39. default: 'text'
  40. },
  41. value: [String, Number],
  42. placeholder: String,
  43. readonly: Boolean,
  44. disabled: Boolean,
  45. min: String,
  46. max: String,
  47. step: String,
  48. minlength: Number,
  49. maxlength: Number,
  50. required: {
  51. type: Boolean,
  52. default: true
  53. },
  54. autoComplete: {
  55. type: String,
  56. default: 'off'
  57. },
  58. validateEvent: {
  59. type: Boolean,
  60. default: true
  61. }
  62. },
  63. computed: {
  64. computedClasses() {
  65. return {
  66. 'material--active': this.focus,
  67. 'material--disabled': this.disabled,
  68. 'material--raised': Boolean(this.focus || this.currentValue) // has value
  69. }
  70. }
  71. },
  72. watch: {
  73. value(newValue) {
  74. this.currentValue = newValue
  75. }
  76. },
  77. data() {
  78. return {
  79. currentValue: this.value,
  80. focus: false,
  81. fillPlaceHolder: null
  82. }
  83. },
  84. methods: {
  85. handleModelInput(event) {
  86. const value = event.target.value
  87. this.$emit('input', value)
  88. if (this.$parent.$options.componentName === 'ElFormItem') {
  89. if (this.validateEvent) {
  90. this.$parent.$emit('el.form.change', [value])
  91. }
  92. }
  93. this.$emit('change', value)
  94. },
  95. handleMdFocus(event) {
  96. this.focus = true
  97. this.$emit('focus', event)
  98. if (this.placeholder && this.placeholder !== '') {
  99. this.fillPlaceHolder = this.placeholder
  100. }
  101. },
  102. handleMdBlur(event) {
  103. this.focus = false
  104. this.$emit('blur', event)
  105. this.fillPlaceHolder = null
  106. if (this.$parent.$options.componentName === 'ElFormItem') {
  107. if (this.validateEvent) {
  108. this.$parent.$emit('el.form.blur', [this.currentValue])
  109. }
  110. }
  111. }
  112. }
  113. }
  114. </script>
  115. <style rel="stylesheet/scss" lang="scss" scoped>
  116. // Fonts:
  117. $font-size-base: 16px;
  118. $font-size-small: 18px;
  119. $font-size-smallest: 12px;
  120. $font-weight-normal: normal;
  121. $font-weight-bold: bold;
  122. $apixel: 1px;
  123. // Utils
  124. $spacer: 12px;
  125. $transition: 0.2s ease all;
  126. $index: 0px;
  127. $index-has-icon: 30px;
  128. // Theme:
  129. $color-white: white;
  130. $color-grey: #9E9E9E;
  131. $color-grey-light: #E0E0E0;
  132. $color-blue: #2196F3;
  133. $color-red: #F44336;
  134. $color-black: black;
  135. // Base clases:
  136. %base-bar-pseudo {
  137. content: '';
  138. height: 1px;
  139. width: 0;
  140. bottom: 0;
  141. position: absolute;
  142. transition: $transition;
  143. }
  144. // Mixins:
  145. @mixin slided-top() {
  146. top: - ($font-size-base + $spacer);
  147. left: 0;
  148. font-size: $font-size-base;
  149. font-weight: $font-weight-bold;
  150. }
  151. // Component:
  152. .material-input__component {
  153. position: relative;
  154. * {
  155. box-sizing: border-box;
  156. }
  157. .iconClass {
  158. .material-input__icon {
  159. position: absolute;
  160. left: 0;
  161. line-height: $font-size-base;
  162. color: $color-blue;
  163. top: $spacer;
  164. width: $index-has-icon;
  165. height: $font-size-base;
  166. font-size: $font-size-base;
  167. font-weight: $font-weight-normal;
  168. pointer-events: none;
  169. }
  170. .material-label {
  171. left: $index-has-icon;
  172. }
  173. .material-input {
  174. text-indent: $index-has-icon;
  175. }
  176. }
  177. .material-input {
  178. font-size: $font-size-base;
  179. padding: $spacer $spacer $spacer - $apixel * 10 $spacer / 2;
  180. display: block;
  181. width: 100%;
  182. border: none;
  183. line-height: 1;
  184. border-radius: 0;
  185. &:focus {
  186. outline: none;
  187. border: none;
  188. border-bottom: 1px solid transparent; // fixes the height issue
  189. }
  190. }
  191. .material-label {
  192. font-weight: $font-weight-normal;
  193. position: absolute;
  194. pointer-events: none;
  195. left: $index;
  196. top: 0;
  197. transition: $transition;
  198. font-size: $font-size-small;
  199. }
  200. .material-input-bar {
  201. position: relative;
  202. display: block;
  203. width: 100%;
  204. &:before {
  205. @extend %base-bar-pseudo;
  206. left: 50%;
  207. }
  208. &:after {
  209. @extend %base-bar-pseudo;
  210. right: 50%;
  211. }
  212. }
  213. // Disabled state:
  214. &.material--disabled {
  215. .material-input {
  216. border-bottom-style: dashed;
  217. }
  218. }
  219. // Raised state:
  220. &.material--raised {
  221. .material-label {
  222. @include slided-top();
  223. }
  224. }
  225. // Active state:
  226. &.material--active {
  227. .material-input-bar {
  228. &:before,
  229. &:after {
  230. width: 50%;
  231. }
  232. }
  233. }
  234. }
  235. .material-input__component {
  236. background: $color-white;
  237. .material-input {
  238. background: none;
  239. color: $color-black;
  240. text-indent: $index;
  241. border-bottom: 1px solid $color-grey-light;
  242. }
  243. .material-label {
  244. color: $color-grey;
  245. }
  246. .material-input-bar {
  247. &:before,
  248. &:after {
  249. background: $color-blue;
  250. }
  251. }
  252. // Active state:
  253. &.material--active {
  254. .material-label {
  255. color: $color-blue;
  256. }
  257. }
  258. // Errors:
  259. &.material--has-errors {
  260. &.material--active .material-label {
  261. color: $color-red;
  262. }
  263. .material-input-bar {
  264. &:before,
  265. &:after {
  266. background: transparent;
  267. }
  268. }
  269. }
  270. }
  271. </style>