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.

278 lines
7.6 KiB

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. margin-top: 36px;
  154. position: relative;
  155. * {
  156. box-sizing: border-box;
  157. }
  158. .iconClass {
  159. .material-input__icon {
  160. position: absolute;
  161. left: 0;
  162. line-height: $font-size-base;
  163. color: $color-blue;
  164. top: $spacer;
  165. width: $index-has-icon;
  166. height: $font-size-base;
  167. font-size: $font-size-base;
  168. font-weight: $font-weight-normal;
  169. pointer-events: none;
  170. }
  171. .material-label {
  172. left: $index-has-icon;
  173. }
  174. .material-input {
  175. text-indent: $index-has-icon;
  176. }
  177. }
  178. .material-input {
  179. font-size: $font-size-base;
  180. padding: $spacer $spacer $spacer - $apixel * 10 $spacer / 2;
  181. display: block;
  182. width: 100%;
  183. border: none;
  184. line-height: 1;
  185. border-radius: 0;
  186. &:focus {
  187. outline: none;
  188. border: none;
  189. border-bottom: 1px solid transparent; // fixes the height issue
  190. }
  191. }
  192. .material-label {
  193. font-weight: $font-weight-normal;
  194. position: absolute;
  195. pointer-events: none;
  196. left: $index;
  197. top: 0;
  198. transition: $transition;
  199. font-size: $font-size-small;
  200. }
  201. .material-input-bar {
  202. position: relative;
  203. display: block;
  204. width: 100%;
  205. &:before {
  206. @extend %base-bar-pseudo;
  207. left: 50%;
  208. }
  209. &:after {
  210. @extend %base-bar-pseudo;
  211. right: 50%;
  212. }
  213. }
  214. // Disabled state:
  215. &.material--disabled {
  216. .material-input {
  217. border-bottom-style: dashed;
  218. }
  219. }
  220. // Raised state:
  221. &.material--raised {
  222. .material-label {
  223. @include slided-top();
  224. }
  225. }
  226. // Active state:
  227. &.material--active {
  228. .material-input-bar {
  229. &:before,
  230. &:after {
  231. width: 50%;
  232. }
  233. }
  234. }
  235. }
  236. .material-input__component {
  237. background: $color-white;
  238. .material-input {
  239. background: none;
  240. color: $color-black;
  241. text-indent: $index;
  242. border-bottom: 1px solid $color-grey-light;
  243. }
  244. .material-label {
  245. color: $color-grey;
  246. }
  247. .material-input-bar {
  248. &:before,
  249. &:after {
  250. background: $color-blue;
  251. }
  252. }
  253. // Active state:
  254. &.material--active {
  255. .material-label {
  256. color: $color-blue;
  257. }
  258. }
  259. // Errors:
  260. &.material--has-errors {
  261. &.material--active .material-label {
  262. color: $color-red;
  263. }
  264. .material-input-bar {
  265. &:before,
  266. &:after {
  267. background: transparent;
  268. }
  269. }
  270. }
  271. }
  272. </style>