stepper.vue 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. <template>
  2. <div :class="{'nut-stepper': !simple, 'nut-stepper-simple': simple}">
  3. <span
  4. @click="reduce()"
  5. :class="{'nut-stepper-grey': isGray}"
  6. v-html="require('../../assets/svg/minus.svg')">
  7. </span>
  8. <input
  9. type="number"
  10. :min="minNum"
  11. :max="max"
  12. :readonly="readonly"
  13. :value="num | maxv(minNum, max)"
  14. :style="{visibility:showNum? 'visible': 'hidden'}"
  15. @input="numchange"
  16. @keyup="checknum"
  17. @focus="focus"
  18. @blur="blur" />
  19. <div
  20. :class="['nut-stepper-fake', showAddAnim? 'nut-stepper-transition': 'nut-stepper-none-transition']"
  21. :style="{
  22. visibility:showAddAnim? 'visible': 'hidden',
  23. 'transform': 'translate(0,'+animTranslate_add+'%)',
  24. '-ms-transform': 'translate(0,'+animTranslate_add+'%)',
  25. '-moz-transform': 'translate(0,'+animTranslate_add+'%)',
  26. '-webkit-transform': 'translate(0,'+animTranslate_add+'%)',
  27. '-o-transform': 'translate(0,'+animTranslate_add+'%)'
  28. }">
  29. <div>{{animNum[0]}}</div>
  30. <div>{{animNum[1]}}</div>
  31. </div>
  32. <div
  33. :class="['nut-stepper-fake-', showReduceAnim? 'nut-stepper-transition': 'nut-stepper-none-transition']"
  34. :style="{
  35. visibility:showReduceAnim? 'visible': 'hidden',
  36. 'transform': 'translate(0,'+animTranslate_+'%)',
  37. '-ms-transform': 'translate(0,'+animTranslate_+'%)',
  38. '-moz-transform': 'translate(0,'+animTranslate_+'%)',
  39. '-webkit-transform': 'translate(0,'+animTranslate_+'%)',
  40. '-o-transform': 'translate(0,'+animTranslate_+'%)'
  41. }">
  42. <div>{{animNum[0]}}</div>
  43. <div>{{animNum[1]}}</div>
  44. </div>
  45. <span
  46. @click="add()"
  47. :class="{'nut-stepper-grey': max&&(Number(num) > max - step)}"
  48. v-html="require('../../assets/svg/plus.svg')">
  49. </span>
  50. </div>
  51. </template>
  52. <script>
  53. export default {
  54. name: 'nut-stepper',
  55. props: {
  56. simple: {
  57. type: Boolean,
  58. default: true,
  59. },
  60. min: {
  61. type: [Number, String],
  62. default: 0,
  63. },
  64. max: {
  65. type: [Number, String],
  66. default: Infinity,
  67. },
  68. step: {
  69. type: [Number, String],
  70. default: 1
  71. },
  72. readonly: {
  73. type: Boolean,
  74. default: false
  75. },
  76. transition: {
  77. type: Boolean,
  78. default: true
  79. },
  80. value: {
  81. type: [String, Number],
  82. required: true
  83. }
  84. },
  85. data() {
  86. return {
  87. tempNum: '',
  88. focusing: false,
  89. num: this.value,
  90. minNum: this.min,
  91. showNum: true,
  92. showAddAnim: false,
  93. showReduceAnim: false,
  94. animNum: [this.value, this.value],
  95. animTranslate_add: 0,
  96. animTranslate_: -100
  97. }
  98. },
  99. filters: {
  100. maxv(v, min, max) {
  101. let val = v;
  102. if(val > max) val = max;
  103. if(val < min) val = min;
  104. return val;
  105. }
  106. },
  107. watch: {
  108. value(v, ov) {
  109. if(v > this.max) v = this.max;
  110. if(v < this.minNum) v = this.minNum;
  111. this.num = v;
  112. this.$emit('change', this.num);
  113. }
  114. },
  115. computed: {
  116. isGray() {
  117. return (this.focusing? this.tempNum: this.num) - this.step < this.min;
  118. }
  119. },
  120. methods: {
  121. focus(e) {
  122. if(this.readonly) return;
  123. // clear val temporary when focus, e...s
  124. const v = this.num;
  125. this.tempNum = v;
  126. this.minNum = '';
  127. // this.num = '';
  128. this.focusing = true;
  129. this.$emit('focus', e, this.num);
  130. },
  131. blur(e) {
  132. if(this.readonly) return this.$emit('blur', e, this.num);
  133. let v = e.target.value;
  134. this.minNum = this.min;
  135. this.focusing = false;
  136. if(v) {
  137. if(v > this.max) v = this.max;
  138. if(v < this.minNum) v = this.minNum;
  139. this.num = v;
  140. }else{
  141. this.num = this.tempNum;
  142. }
  143. this.$emit('blur', e, this.num);
  144. },
  145. checknum(e) {
  146. let v = e.target.value;
  147. // this.minNum = this.min;
  148. this.focusing = false;
  149. if(v > this.max) v = this.max;
  150. if(v < this.minNum) v = this.minNum;
  151. e.target.value = v;
  152. this.num = v;
  153. },
  154. numchange(e) {
  155. let v = e.target.value;
  156. if(v > this.max) v = this.max;
  157. if(v < this.minNum) v = this.minNum;
  158. e.target.value = v;
  159. this.num = v;
  160. this.$emit('update:value', this.num);
  161. this.$emit('change', this.num);
  162. },
  163. add() {
  164. this.num = Number(this.num);
  165. if(this.num <= this.max - this.step && this.max > this.minNum) {
  166. let [n1, n2] = this.num.toString().split('.');
  167. let fixedLen = n2? n2.length: 0;
  168. this.num = parseFloat((Number(n1) + Number(this.step)) + (n2? '.'+n2: '')).toFixed(fixedLen);
  169. if(this.transition) {
  170. this.showNum = false;
  171. this.showAddAnim = true;
  172. this.showReduceAnim = false;
  173. this.animNum = [parseFloat(this.num - this.step).toFixed(fixedLen), this.num];
  174. this.animTranslate_add = -100;
  175. var f = this.$el.querySelector('.nut-stepper-fake');
  176. f.addEventListener("webkitTransitionEnd", () => {
  177. this.showNum = true;
  178. this.showAddAnim = false;
  179. this.animTranslate_add = 0;
  180. });
  181. };
  182. }
  183. this.$emit('update:value', this.num);
  184. this.$emit('add', this.num);
  185. this.$emit('change', this.num);
  186. },
  187. animEnd() {
  188. // unbind
  189. this.showNum = true;
  190. },
  191. reduce(){
  192. if(this.num - this.step >= this.minNum) {
  193. let [n1, n2] = this.num.toString().split('.');
  194. let fixedLen = n2? n2.length: 0;
  195. this.num = parseFloat((Number(n1) - this.step) + (n2? '.'+n2: '')).toFixed(fixedLen);
  196. if(this.transition) {
  197. this.showNum = false;
  198. this.showAddAnim = false;
  199. this.showReduceAnim = true;
  200. this.animNum = [this.num, this.num];
  201. this.animTranslate_ = 0;
  202. var f = this.$el.querySelector('.nut-stepper-fake-');
  203. f.addEventListener("webkitTransitionEnd", () => {
  204. this.showNum = true;
  205. this.showReduceAnim = false;
  206. this.animTranslate_ = -100;
  207. });
  208. }
  209. }
  210. this.$emit('update:value', this.num);
  211. this.$emit('reduce', this.num);
  212. this.$emit('change', this.num);
  213. },
  214. }
  215. }
  216. </script>