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.

360 lines
8.3 KiB

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