Commit 06b2a84839493c6d82610b8c9fcf5e88faed87d1
Committed by
GitHub
Merge pull request #1580 from SergioCrisostomo/2.0
Time-Picker new feature: allow steps in lists
Showing
6 changed files
with
114 additions
and
38 deletions
Show diff stats
src/components/date-picker/base/time-spinner.vue
| 1 | 1 | <template> |
| 2 | 2 | <div :class="classes"> |
| 3 | 3 | <div :class="[prefixCls+ '-list']" ref="hours"> |
| 4 | - <ul :class="[prefixCls + '-ul']" @click="handleClickHours"> | |
| 5 | - <li :class="getCellCls(item)" v-for="(item, index) in hoursList" v-show="!item.hide" :index="index">{{ formatTime(item.text) }}</li> | |
| 4 | + <ul :class="[prefixCls + '-ul']"> | |
| 5 | + <li :class="getCellCls(item)" v-for="item in hoursList" v-show="!item.hide" @click="handleClick('hours', item)">{{ formatTime(item.text) }}</li> | |
| 6 | 6 | </ul> |
| 7 | 7 | </div> |
| 8 | 8 | <div :class="[prefixCls+ '-list']" ref="minutes"> |
| 9 | - <ul :class="[prefixCls + '-ul']" @click="handleClickMinutes"> | |
| 10 | - <li :class="getCellCls(item)" v-for="(item, index) in minutesList" v-show="!item.hide" :index="index">{{ formatTime(item.text) }}</li> | |
| 9 | + <ul :class="[prefixCls + '-ul']"> | |
| 10 | + <li :class="getCellCls(item)" v-for="item in minutesList" v-show="!item.hide" @click="handleClick('minutes', item)">{{ formatTime(item.text) }}</li> | |
| 11 | 11 | </ul> |
| 12 | 12 | </div> |
| 13 | 13 | <div :class="[prefixCls+ '-list']" v-show="showSeconds" ref="seconds"> |
| 14 | - <ul :class="[prefixCls + '-ul']" @click="handleClickSeconds"> | |
| 15 | - <li :class="getCellCls(item)" v-for="(item, index) in secondsList" v-show="!item.hide" :index="index">{{ formatTime(item.text) }}</li> | |
| 14 | + <ul :class="[prefixCls + '-ul']"> | |
| 15 | + <li :class="getCellCls(item)" v-for="item in secondsList" v-show="!item.hide" @click="handleClick('seconds', item)">{{ formatTime(item.text) }}</li> | |
| 16 | 16 | </ul> |
| 17 | 17 | </div> |
| 18 | 18 | </div> |
| ... | ... | @@ -41,10 +41,15 @@ |
| 41 | 41 | showSeconds: { |
| 42 | 42 | type: Boolean, |
| 43 | 43 | default: true |
| 44 | + }, | |
| 45 | + steps: { | |
| 46 | + type: Array, | |
| 47 | + default: () => [] | |
| 44 | 48 | } |
| 45 | 49 | }, |
| 46 | 50 | data () { |
| 47 | 51 | return { |
| 52 | + spinerSteps: [1, 1, 1].map((one, i) => Math.abs(this.steps[i]) || one), | |
| 48 | 53 | prefixCls: prefixCls, |
| 49 | 54 | compiled: false |
| 50 | 55 | }; |
| ... | ... | @@ -60,6 +65,7 @@ |
| 60 | 65 | }, |
| 61 | 66 | hoursList () { |
| 62 | 67 | let hours = []; |
| 68 | + const step = this.spinerSteps[0]; | |
| 63 | 69 | const hour_tmpl = { |
| 64 | 70 | text: 0, |
| 65 | 71 | selected: false, |
| ... | ... | @@ -67,7 +73,7 @@ |
| 67 | 73 | hide: false |
| 68 | 74 | }; |
| 69 | 75 | |
| 70 | - for (let i = 0; i < 24; i++) { | |
| 76 | + for (let i = 0; i < 24; i += step) { | |
| 71 | 77 | const hour = deepCopy(hour_tmpl); |
| 72 | 78 | hour.text = i; |
| 73 | 79 | |
| ... | ... | @@ -83,6 +89,7 @@ |
| 83 | 89 | }, |
| 84 | 90 | minutesList () { |
| 85 | 91 | let minutes = []; |
| 92 | + const step = this.spinerSteps[1]; | |
| 86 | 93 | const minute_tmpl = { |
| 87 | 94 | text: 0, |
| 88 | 95 | selected: false, |
| ... | ... | @@ -90,7 +97,7 @@ |
| 90 | 97 | hide: false |
| 91 | 98 | }; |
| 92 | 99 | |
| 93 | - for (let i = 0; i < 60; i++) { | |
| 100 | + for (let i = 0; i < 60; i += step) { | |
| 94 | 101 | const minute = deepCopy(minute_tmpl); |
| 95 | 102 | minute.text = i; |
| 96 | 103 | |
| ... | ... | @@ -101,11 +108,11 @@ |
| 101 | 108 | if (this.minutes === i) minute.selected = true; |
| 102 | 109 | minutes.push(minute); |
| 103 | 110 | } |
| 104 | - | |
| 105 | 111 | return minutes; |
| 106 | 112 | }, |
| 107 | 113 | secondsList () { |
| 108 | 114 | let seconds = []; |
| 115 | + const step = this.spinerSteps[2]; | |
| 109 | 116 | const second_tmpl = { |
| 110 | 117 | text: 0, |
| 111 | 118 | selected: false, |
| ... | ... | @@ -113,7 +120,7 @@ |
| 113 | 120 | hide: false |
| 114 | 121 | }; |
| 115 | 122 | |
| 116 | - for (let i = 0; i < 60; i++) { | |
| 123 | + for (let i = 0; i < 60; i += step) { | |
| 117 | 124 | const second = deepCopy(second_tmpl); |
| 118 | 125 | second.text = i; |
| 119 | 126 | |
| ... | ... | @@ -138,24 +145,11 @@ |
| 138 | 145 | } |
| 139 | 146 | ]; |
| 140 | 147 | }, |
| 141 | - handleClickHours (event) { | |
| 142 | - this.handleClick('hours', event); | |
| 143 | - }, | |
| 144 | - handleClickMinutes (event) { | |
| 145 | - this.handleClick('minutes', event); | |
| 146 | - }, | |
| 147 | - handleClickSeconds (event) { | |
| 148 | - this.handleClick('seconds', event); | |
| 149 | - }, | |
| 150 | - handleClick (type, event) { | |
| 151 | - const target = event.target; | |
| 152 | - if (target.tagName === 'LI') { | |
| 153 | - const cell = this[`${type}List`][parseInt(event.target.getAttribute('index'))]; | |
| 154 | - if (cell.disabled) return; | |
| 155 | - const data = {}; | |
| 156 | - data[type] = cell.text; | |
| 157 | - this.$emit('on-change', data); | |
| 158 | - } | |
| 148 | + handleClick (type, cell) { | |
| 149 | + if (cell.disabled) return; | |
| 150 | + const data = {}; | |
| 151 | + data[type] = cell.text; | |
| 152 | + this.$emit('on-change', data); | |
| 159 | 153 | this.$emit('on-pick-click'); |
| 160 | 154 | }, |
| 161 | 155 | scroll (type, index) { |
| ... | ... | @@ -183,20 +177,24 @@ |
| 183 | 177 | }, |
| 184 | 178 | formatTime (text) { |
| 185 | 179 | return text < 10 ? '0' + text : text; |
| 180 | + }, | |
| 181 | + getItemIndex(type, val){ | |
| 182 | + const item = this[`${type}List`].find(obj => obj.text == val); | |
| 183 | + return this[`${type}List`].indexOf(item); | |
| 186 | 184 | } |
| 187 | 185 | }, |
| 188 | 186 | watch: { |
| 189 | 187 | hours (val) { |
| 190 | 188 | if (!this.compiled) return; |
| 191 | - this.scroll('hours', val); | |
| 189 | + this.scroll('hours', this.getItemIndex('hours', val)); | |
| 192 | 190 | }, |
| 193 | 191 | minutes (val) { |
| 194 | 192 | if (!this.compiled) return; |
| 195 | - this.scroll('minutes', val); | |
| 193 | + this.scroll('minutes', this.getItemIndex('minutes', val)); | |
| 196 | 194 | }, |
| 197 | 195 | seconds (val) { |
| 198 | 196 | if (!this.compiled) return; |
| 199 | - this.scroll('seconds', val); | |
| 197 | + this.scroll('seconds', this.getItemIndex('seconds', val)); | |
| 200 | 198 | } |
| 201 | 199 | }, |
| 202 | 200 | mounted () { |
| ... | ... | @@ -204,4 +202,4 @@ |
| 204 | 202 | this.$nextTick(() => this.compiled = true); |
| 205 | 203 | } |
| 206 | 204 | }; |
| 207 | -</script> | |
| 208 | 205 | \ No newline at end of file |
| 206 | +</script> | ... | ... |
src/components/date-picker/panel/time.vue
| ... | ... | @@ -6,6 +6,7 @@ |
| 6 | 6 | <time-spinner |
| 7 | 7 | ref="timeSpinner" |
| 8 | 8 | :show-seconds="showSeconds" |
| 9 | + :steps="steps" | |
| 9 | 10 | :hours="hours" |
| 10 | 11 | :minutes="minutes" |
| 11 | 12 | :seconds="seconds" |
| ... | ... | @@ -39,6 +40,12 @@ |
| 39 | 40 | name: 'TimePicker', |
| 40 | 41 | mixins: [ Mixin, Locale ], |
| 41 | 42 | components: { TimeSpinner, Confirm }, |
| 43 | + props: { | |
| 44 | + steps: { | |
| 45 | + type: Array, | |
| 46 | + default: () => [] | |
| 47 | + } | |
| 48 | + }, | |
| 42 | 49 | data () { |
| 43 | 50 | return { |
| 44 | 51 | prefixCls: prefixCls, |
| ... | ... | @@ -113,4 +120,4 @@ |
| 113 | 120 | if (this.$parent && this.$parent.$options.name === 'DatePicker') this.showDate = true; |
| 114 | 121 | } |
| 115 | 122 | }; |
| 116 | -</script> | |
| 117 | 123 | \ No newline at end of file |
| 124 | +</script> | ... | ... |
src/components/date-picker/picker.vue
| ... | ... | @@ -32,7 +32,6 @@ |
| 32 | 32 | </div> |
| 33 | 33 | </template> |
| 34 | 34 | <script> |
| 35 | - import Vue from 'vue'; | |
| 36 | 35 | import iInput from '../../components/input/input.vue'; |
| 37 | 36 | import Drop from '../../components/select/dropdown.vue'; |
| 38 | 37 | import clickoutside from '../../directives/clickoutside'; |
| ... | ... | @@ -397,7 +396,7 @@ |
| 397 | 396 | let isConfirm = this.confirm; |
| 398 | 397 | const type = this.type; |
| 399 | 398 | |
| 400 | - this.picker = new Vue(this.panel).$mount(this.$refs.picker); | |
| 399 | + this.picker = this.Panel.$mount(this.$refs.picker); | |
| 401 | 400 | if (type === 'datetime' || type === 'datetimerange') { |
| 402 | 401 | isConfirm = true; |
| 403 | 402 | this.picker.showTime = true; | ... | ... |
src/components/date-picker/picker/date-picker.js
| 1 | +import Vue from 'vue'; | |
| 1 | 2 | import Picker from '../picker.vue'; |
| 2 | 3 | import DatePanel from '../panel/date.vue'; |
| 3 | 4 | import DateRangePanel from '../panel/date-range.vue'; |
| ... | ... | @@ -31,6 +32,7 @@ export default { |
| 31 | 32 | } |
| 32 | 33 | } |
| 33 | 34 | |
| 34 | - this.panel = getPanel(this.type); | |
| 35 | + const panel = getPanel(this.type); | |
| 36 | + this.Panel = new Vue(panel); | |
| 35 | 37 | } |
| 36 | 38 | }; | ... | ... |
src/components/date-picker/picker/time-picker.js
| 1 | +import Vue from 'vue'; | |
| 1 | 2 | import Picker from '../picker.vue'; |
| 2 | 3 | import TimePanel from '../panel/time.vue'; |
| 3 | 4 | import TimeRangePanel from '../panel/time-range.vue'; |
| ... | ... | @@ -21,6 +22,10 @@ export default { |
| 21 | 22 | }, |
| 22 | 23 | default: 'time' |
| 23 | 24 | }, |
| 25 | + steps: { | |
| 26 | + type: Array, | |
| 27 | + default: () => [] | |
| 28 | + }, | |
| 24 | 29 | value: {} |
| 25 | 30 | }, |
| 26 | 31 | created () { |
| ... | ... | @@ -31,6 +36,11 @@ export default { |
| 31 | 36 | this.currentValue = ''; |
| 32 | 37 | } |
| 33 | 38 | } |
| 34 | - this.panel = getPanel(this.type); | |
| 39 | + const Panel = Vue.extend(getPanel(this.type)); | |
| 40 | + this.Panel = new Panel({ | |
| 41 | + propsData: { | |
| 42 | + steps: this.steps | |
| 43 | + } | |
| 44 | + }); | |
| 35 | 45 | } |
| 36 | -}; | |
| 37 | 46 | \ No newline at end of file |
| 47 | +}; | ... | ... |
| 1 | +import { createVue, destroyVM } from '../util'; | |
| 2 | + | |
| 3 | +describe('TimePicker.vue', () => { | |
| 4 | + let vm; | |
| 5 | + afterEach(() => { | |
| 6 | + destroyVM(vm); | |
| 7 | + }); | |
| 8 | + | |
| 9 | + it('should create a TimePicker component with hours, minutes and seconds', done => { | |
| 10 | + vm = createVue(` | |
| 11 | + <Time-Picker></Time-Picker> | |
| 12 | + `); | |
| 13 | + const picker = vm.$children[0]; | |
| 14 | + picker.handleIconClick(); // open the picker panels | |
| 15 | + | |
| 16 | + vm.$nextTick(() => { | |
| 17 | + const spiners = picker.$el.querySelectorAll('.ivu-time-picker-cells-list'); | |
| 18 | + expect(spiners.length).to.equal(3); // hh:mm:ss | |
| 19 | + expect(spiners[0].querySelectorAll('.ivu-time-picker-cells-cell').length).to.equal(24); | |
| 20 | + expect(spiners[1].querySelectorAll('.ivu-time-picker-cells-cell').length).to.equal(60); | |
| 21 | + expect(spiners[2].querySelectorAll('.ivu-time-picker-cells-cell').length).to.equal(60); | |
| 22 | + done(); | |
| 23 | + }); | |
| 24 | + }); | |
| 25 | + | |
| 26 | + it('should create a TimePicker component with only hours and minutes', done => { | |
| 27 | + vm = createVue(` | |
| 28 | + <Time-Picker format="HH:mm"></Time-Picker> | |
| 29 | + `); | |
| 30 | + const picker = vm.$children[0]; | |
| 31 | + picker.handleIconClick(); // open the picker panels | |
| 32 | + | |
| 33 | + vm.$nextTick(() => { | |
| 34 | + const spiners = picker.$el.querySelectorAll('.ivu-time-picker-cells-list'); | |
| 35 | + expect([...spiners].filter(el => el.style.display != 'none').length).to.equal(2); // hh:mm | |
| 36 | + expect(spiners[0].querySelectorAll('.ivu-time-picker-cells-cell').length).to.equal(24); | |
| 37 | + expect(spiners[1].querySelectorAll('.ivu-time-picker-cells-cell').length).to.equal(60); | |
| 38 | + done(); | |
| 39 | + }); | |
| 40 | + }); | |
| 41 | + | |
| 42 | + it('should create a TimePicker component with steps of 15 minutes', done => { | |
| 43 | + vm = createVue(` | |
| 44 | + <Time-Picker :steps="[1, 15]"></Time-Picker> | |
| 45 | + `); | |
| 46 | + const picker = vm.$children[0]; | |
| 47 | + picker.handleIconClick(); // open the picker panels | |
| 48 | + | |
| 49 | + vm.$nextTick(() => { | |
| 50 | + const spiners = picker.$el.querySelectorAll('.ivu-time-picker-cells-list'); | |
| 51 | + const minutesList = [...spiners[1].querySelectorAll('.ivu-time-picker-cells-cell')]; | |
| 52 | + | |
| 53 | + expect(spiners[0].querySelectorAll('.ivu-time-picker-cells-cell').length).to.equal(24); | |
| 54 | + expect(minutesList.map(el => el.textContent).join(',')).to.equal('00,15,30,45'); | |
| 55 | + expect(spiners[1].querySelectorAll('.ivu-time-picker-cells-cell').length).to.equal(4); | |
| 56 | + expect(spiners[2].querySelectorAll('.ivu-time-picker-cells-cell').length).to.equal(60); | |
| 57 | + done(); | |
| 58 | + }); | |
| 59 | + }); | |
| 60 | +}); | ... | ... |