Commit bdb26ef7c80f44e30ebdc33de2ddd60a3cc19c1f
Committed by
GitHub
Merge pull request #3643 from SergioCrisostomo/datepicker-keyboard
Datepicker keyboard
Showing
17 changed files
with
494 additions
and
77 deletions
Show diff stats
examples/routers/date.vue
| @@ -249,14 +249,17 @@ | @@ -249,14 +249,17 @@ | ||
| 249 | 249 | ||
| 250 | <template> | 250 | <template> |
| 251 | <div style="width: 500px;margin: 100px;"> | 251 | <div style="width: 500px;margin: 100px;"> |
| 252 | - <Row> | ||
| 253 | - <Col span="12"> | ||
| 254 | - <DatePicker type="date" show-week-numbers placeholder="Select date" style="width: 200px"></DatePicker> | ||
| 255 | - </Col> | ||
| 256 | - <Col span="12"> | ||
| 257 | - <DatePicker type="daterange" show-week-numbers placement="bottom-end" placeholder="Select date" style="width: 200px"></DatePicker> | ||
| 258 | - </Col> | ||
| 259 | - </Row> | 252 | + <p><input type="text"></p> |
| 253 | + | ||
| 254 | + <DatePicker type="month" show-week-numbers placeholder="Select date" style="width: 200px"></DatePicker> | ||
| 255 | + <DatePicker type="year" show-week-numbers placeholder="Select date" style="width: 200px"></DatePicker> | ||
| 256 | + | ||
| 257 | + <DatePicker type="date" transfer show-week-numbers placeholder="Select date" style="width: 400px"></DatePicker> | ||
| 258 | + <DatePicker type="datetime" show-week-numbers confirm placeholder="Select date" style="width: 400px"></DatePicker> | ||
| 259 | + | ||
| 260 | + <DatePicker type="daterange" transfer show-week-numbers placeholder="Select date" style="width: 400px"></DatePicker> | ||
| 261 | + <DatePicker type="datetimerange" transfer show-week-numbers placeholder="Select date" style="width: 400px"></DatePicker> | ||
| 262 | + <Time-Picker :steps="[1, 1, 15]" :value="new Date()"></Time-Picker> | ||
| 260 | </div> | 263 | </div> |
| 261 | </template> | 264 | </template> |
| 262 | <script> | 265 | <script> |
src/components/date-picker/base/confirm.vue
| 1 | <template> | 1 | <template> |
| 2 | - <div :class="[prefixCls + '-confirm']"> | ||
| 3 | - <span :class="timeClasses" v-if="showTime" @click="handleToggleTime"> | ||
| 4 | - <template v-if="isTime">{{ t('i.datepicker.selectDate') }}</template> | ||
| 5 | - <template v-else>{{ t('i.datepicker.selectTime') }}</template> | ||
| 6 | - </span> | ||
| 7 | - <i-button size="small" type="text" @click.native="handleClear">{{ t('i.datepicker.clear') }}</i-button> | ||
| 8 | - <i-button size="small" type="primary" @click.native="handleSuccess">{{ t('i.datepicker.ok') }}</i-button> | 2 | + <div :class="[prefixCls + '-confirm']" @keydown.tab.capture="handleTab"> |
| 3 | + <i-button :class="timeClasses" size="small" type="text" :disabled="timeDisabled" v-if="showTime" @click="handleToggleTime"> | ||
| 4 | + {{labels.time}} | ||
| 5 | + </i-button> | ||
| 6 | + <i-button size="small" type="ghost" @click.native="handleClear" @keydown.enter.native="handleClear"> | ||
| 7 | + {{labels.clear}} | ||
| 8 | + </i-button> | ||
| 9 | + <i-button size="small" type="primary" @click.native="handleSuccess" @keydown.enter.native="handleSuccess"> | ||
| 10 | + {{labels.ok}} | ||
| 11 | + </i-button> | ||
| 9 | </div> | 12 | </div> |
| 10 | </template> | 13 | </template> |
| 11 | <script> | 14 | <script> |
| 12 | import iButton from '../../button/button.vue'; | 15 | import iButton from '../../button/button.vue'; |
| 13 | import Locale from '../../../mixins/locale'; | 16 | import Locale from '../../../mixins/locale'; |
| 17 | + import Emitter from '../../../mixins/emitter'; | ||
| 14 | 18 | ||
| 15 | const prefixCls = 'ivu-picker'; | 19 | const prefixCls = 'ivu-picker'; |
| 16 | 20 | ||
| 17 | export default { | 21 | export default { |
| 18 | - mixins: [ Locale ], | ||
| 19 | - components: { iButton }, | 22 | + mixins: [Locale, Emitter], |
| 23 | + components: {iButton}, | ||
| 20 | props: { | 24 | props: { |
| 21 | showTime: false, | 25 | showTime: false, |
| 22 | isTime: false, | 26 | isTime: false, |
| 23 | timeDisabled: false | 27 | timeDisabled: false |
| 24 | }, | 28 | }, |
| 25 | - data () { | 29 | + data() { |
| 26 | return { | 30 | return { |
| 27 | prefixCls: prefixCls | 31 | prefixCls: prefixCls |
| 28 | }; | 32 | }; |
| 29 | }, | 33 | }, |
| 30 | computed: { | 34 | computed: { |
| 31 | timeClasses () { | 35 | timeClasses () { |
| 32 | - return { | ||
| 33 | - [`${prefixCls}-confirm-time-disabled`]: this.timeDisabled | ||
| 34 | - }; | 36 | + return `${prefixCls}-confirm-time`; |
| 37 | + }, | ||
| 38 | + labels(){ | ||
| 39 | + const labels = ['time', 'clear', 'ok']; | ||
| 40 | + const values = [(this.isTime ? 'selectDate' : 'selectTime'), 'clear', 'ok']; | ||
| 41 | + return labels.reduce((obj, key, i) => { | ||
| 42 | + obj[key] = this.t('i.datepicker.' + values[i]); | ||
| 43 | + return obj; | ||
| 44 | + }, {}); | ||
| 35 | } | 45 | } |
| 36 | }, | 46 | }, |
| 37 | methods: { | 47 | methods: { |
| @@ -44,6 +54,17 @@ | @@ -44,6 +54,17 @@ | ||
| 44 | handleToggleTime () { | 54 | handleToggleTime () { |
| 45 | if (this.timeDisabled) return; | 55 | if (this.timeDisabled) return; |
| 46 | this.$emit('on-pick-toggle-time'); | 56 | this.$emit('on-pick-toggle-time'); |
| 57 | + this.dispatch('CalendarPicker', 'focus-input'); | ||
| 58 | + }, | ||
| 59 | + handleTab(e) { | ||
| 60 | + const tabbables = [...this.$el.children]; | ||
| 61 | + const expectedFocus = tabbables[e.shiftKey ? 'shift' : 'pop'](); | ||
| 62 | + | ||
| 63 | + if (document.activeElement === expectedFocus) { | ||
| 64 | + e.preventDefault(); | ||
| 65 | + e.stopPropagation(); | ||
| 66 | + this.dispatch('CalendarPicker', 'focus-input'); | ||
| 67 | + } | ||
| 47 | } | 68 | } |
| 48 | } | 69 | } |
| 49 | }; | 70 | }; |
src/components/date-picker/base/date-table.vue
| @@ -7,9 +7,9 @@ | @@ -7,9 +7,9 @@ | ||
| 7 | </div> | 7 | </div> |
| 8 | <span | 8 | <span |
| 9 | :class="getCellCls(cell)" | 9 | :class="getCellCls(cell)" |
| 10 | - v-for="(cell, i) in readCells" | 10 | + v-for="(cell, i) in cells" |
| 11 | :key="String(cell.date) + i" | 11 | :key="String(cell.date) + i" |
| 12 | - @click="handleClick(cell)" | 12 | + @click="handleClick(cell, $event)" |
| 13 | @mouseenter="handleMouseMove(cell)" | 13 | @mouseenter="handleMouseMove(cell)" |
| 14 | > | 14 | > |
| 15 | <em>{{ cell.desc }}</em> | 15 | <em>{{ cell.desc }}</em> |
| @@ -61,7 +61,7 @@ | @@ -61,7 +61,7 @@ | ||
| 61 | const weekDays = translatedDays.splice(weekStartDay, 7 - weekStartDay).concat(translatedDays.splice(0, weekStartDay)); | 61 | const weekDays = translatedDays.splice(weekStartDay, 7 - weekStartDay).concat(translatedDays.splice(0, weekStartDay)); |
| 62 | return this.showWeekNumbers ? [''].concat(weekDays) : weekDays; | 62 | return this.showWeekNumbers ? [''].concat(weekDays) : weekDays; |
| 63 | }, | 63 | }, |
| 64 | - readCells () { | 64 | + cells () { |
| 65 | const tableYear = this.tableDate.getFullYear(); | 65 | const tableYear = this.tableDate.getFullYear(); |
| 66 | const tableMonth = this.tableDate.getMonth(); | 66 | const tableMonth = this.tableDate.getMonth(); |
| 67 | const today = clearHours(new Date()); // timestamp of today | 67 | const today = clearHours(new Date()); // timestamp of today |
| @@ -99,7 +99,9 @@ | @@ -99,7 +99,9 @@ | ||
| 99 | [`${prefixCls}-cell-prev-month`]: cell.type === 'prevMonth', | 99 | [`${prefixCls}-cell-prev-month`]: cell.type === 'prevMonth', |
| 100 | [`${prefixCls}-cell-next-month`]: cell.type === 'nextMonth', | 100 | [`${prefixCls}-cell-next-month`]: cell.type === 'nextMonth', |
| 101 | [`${prefixCls}-cell-week-label`]: cell.type === 'weekLabel', | 101 | [`${prefixCls}-cell-week-label`]: cell.type === 'weekLabel', |
| 102 | - [`${prefixCls}-cell-range`]: cell.range && !cell.start && !cell.end | 102 | + [`${prefixCls}-cell-range`]: cell.range && !cell.start && !cell.end, |
| 103 | + [`${prefixCls}-focused`]: clearHours(cell.date) === clearHours(this.focusedDate) | ||
| 104 | + | ||
| 103 | } | 105 | } |
| 104 | ]; | 106 | ]; |
| 105 | }, | 107 | }, |
src/components/date-picker/base/mixin.js
| @@ -2,6 +2,7 @@ | @@ -2,6 +2,7 @@ | ||
| 2 | import {clearHours} from '../util'; | 2 | import {clearHours} from '../util'; |
| 3 | 3 | ||
| 4 | export default { | 4 | export default { |
| 5 | + name: 'PanelTable', | ||
| 5 | props: { | 6 | props: { |
| 6 | tableDate: { | 7 | tableDate: { |
| 7 | type: Date, | 8 | type: Date, |
| @@ -26,7 +27,10 @@ export default { | @@ -26,7 +27,10 @@ export default { | ||
| 26 | selecting: false | 27 | selecting: false |
| 27 | }) | 28 | }) |
| 28 | }, | 29 | }, |
| 29 | - | 30 | + focusedDate: { |
| 31 | + type: Date, | ||
| 32 | + required: true, | ||
| 33 | + } | ||
| 30 | }, | 34 | }, |
| 31 | computed: { | 35 | computed: { |
| 32 | dates(){ | 36 | dates(){ |
src/components/date-picker/base/month-table.vue
| @@ -38,14 +38,16 @@ | @@ -38,14 +38,16 @@ | ||
| 38 | 38 | ||
| 39 | const tableYear = this.tableDate.getFullYear(); | 39 | const tableYear = this.tableDate.getFullYear(); |
| 40 | const selectedDays = this.dates.filter(Boolean).map(date => clearHours(new Date(date.getFullYear(), date.getMonth(), 1))); | 40 | const selectedDays = this.dates.filter(Boolean).map(date => clearHours(new Date(date.getFullYear(), date.getMonth(), 1))); |
| 41 | + const focusedDate = clearHours(new Date(this.focusedDate.getFullYear(), this.focusedDate.getMonth(), 1)); | ||
| 41 | 42 | ||
| 42 | for (let i = 0; i < 12; i++) { | 43 | for (let i = 0; i < 12; i++) { |
| 43 | const cell = deepCopy(cell_tmpl); | 44 | const cell = deepCopy(cell_tmpl); |
| 44 | cell.date = new Date(tableYear, i, 1); | 45 | cell.date = new Date(tableYear, i, 1); |
| 45 | cell.text = this.tCell(i + 1); | 46 | cell.text = this.tCell(i + 1); |
| 46 | - const time = clearHours(cell.date); | 47 | + const day = clearHours(cell.date); |
| 47 | cell.disabled = typeof this.disabledDate === 'function' && this.disabledDate(cell.date) && this.selectionMode === 'month'; | 48 | cell.disabled = typeof this.disabledDate === 'function' && this.disabledDate(cell.date) && this.selectionMode === 'month'; |
| 48 | - cell.selected = selectedDays.includes(time); | 49 | + cell.selected = selectedDays.includes(day); |
| 50 | + cell.focused = day === focusedDate; | ||
| 49 | cells.push(cell); | 51 | cells.push(cell); |
| 50 | } | 52 | } |
| 51 | 53 | ||
| @@ -59,6 +61,7 @@ | @@ -59,6 +61,7 @@ | ||
| 59 | { | 61 | { |
| 60 | [`${prefixCls}-cell-selected`]: cell.selected, | 62 | [`${prefixCls}-cell-selected`]: cell.selected, |
| 61 | [`${prefixCls}-cell-disabled`]: cell.disabled, | 63 | [`${prefixCls}-cell-disabled`]: cell.disabled, |
| 64 | + [`${prefixCls}-cell-focused`]: cell.focused, | ||
| 62 | [`${prefixCls}-cell-range`]: cell.range && !cell.start && !cell.end | 65 | [`${prefixCls}-cell-range`]: cell.range && !cell.start && !cell.end |
| 63 | } | 66 | } |
| 64 | ]; | 67 | ]; |
src/components/date-picker/base/time-spinner.vue
| @@ -22,8 +22,10 @@ | @@ -22,8 +22,10 @@ | ||
| 22 | import { deepCopy, scrollTop, firstUpperCase } from '../../../utils/assist'; | 22 | import { deepCopy, scrollTop, firstUpperCase } from '../../../utils/assist'; |
| 23 | 23 | ||
| 24 | const prefixCls = 'ivu-time-picker-cells'; | 24 | const prefixCls = 'ivu-time-picker-cells'; |
| 25 | + const timeParts = ['hours', 'minutes', 'seconds']; | ||
| 25 | 26 | ||
| 26 | export default { | 27 | export default { |
| 28 | + name: 'TimeSpinner', | ||
| 27 | mixins: [Options], | 29 | mixins: [Options], |
| 28 | props: { | 30 | props: { |
| 29 | hours: { | 31 | hours: { |
| @@ -51,7 +53,9 @@ | @@ -51,7 +53,9 @@ | ||
| 51 | return { | 53 | return { |
| 52 | spinerSteps: [1, 1, 1].map((one, i) => Math.abs(this.steps[i]) || one), | 54 | spinerSteps: [1, 1, 1].map((one, i) => Math.abs(this.steps[i]) || one), |
| 53 | prefixCls: prefixCls, | 55 | prefixCls: prefixCls, |
| 54 | - compiled: false | 56 | + compiled: false, |
| 57 | + focusedColumn: -1, // which column inside the picker | ||
| 58 | + focusedTime: [0, 0, 0] // the values array into [hh, mm, ss] | ||
| 55 | }; | 59 | }; |
| 56 | }, | 60 | }, |
| 57 | computed: { | 61 | computed: { |
| @@ -66,6 +70,7 @@ | @@ -66,6 +70,7 @@ | ||
| 66 | hoursList () { | 70 | hoursList () { |
| 67 | let hours = []; | 71 | let hours = []; |
| 68 | const step = this.spinerSteps[0]; | 72 | const step = this.spinerSteps[0]; |
| 73 | + const focusedHour = this.focusedColumn === 0 && this.focusedTime[0]; | ||
| 69 | const hour_tmpl = { | 74 | const hour_tmpl = { |
| 70 | text: 0, | 75 | text: 0, |
| 71 | selected: false, | 76 | selected: false, |
| @@ -76,6 +81,7 @@ | @@ -76,6 +81,7 @@ | ||
| 76 | for (let i = 0; i < 24; i += step) { | 81 | for (let i = 0; i < 24; i += step) { |
| 77 | const hour = deepCopy(hour_tmpl); | 82 | const hour = deepCopy(hour_tmpl); |
| 78 | hour.text = i; | 83 | hour.text = i; |
| 84 | + hour.focused = i === focusedHour; | ||
| 79 | 85 | ||
| 80 | if (this.disabledHours.length && this.disabledHours.indexOf(i) > -1) { | 86 | if (this.disabledHours.length && this.disabledHours.indexOf(i) > -1) { |
| 81 | hour.disabled = true; | 87 | hour.disabled = true; |
| @@ -90,6 +96,7 @@ | @@ -90,6 +96,7 @@ | ||
| 90 | minutesList () { | 96 | minutesList () { |
| 91 | let minutes = []; | 97 | let minutes = []; |
| 92 | const step = this.spinerSteps[1]; | 98 | const step = this.spinerSteps[1]; |
| 99 | + const focusedMinute = this.focusedColumn === 1 && this.focusedTime[1]; | ||
| 93 | const minute_tmpl = { | 100 | const minute_tmpl = { |
| 94 | text: 0, | 101 | text: 0, |
| 95 | selected: false, | 102 | selected: false, |
| @@ -100,6 +107,7 @@ | @@ -100,6 +107,7 @@ | ||
| 100 | for (let i = 0; i < 60; i += step) { | 107 | for (let i = 0; i < 60; i += step) { |
| 101 | const minute = deepCopy(minute_tmpl); | 108 | const minute = deepCopy(minute_tmpl); |
| 102 | minute.text = i; | 109 | minute.text = i; |
| 110 | + minute.focused = i === focusedMinute; | ||
| 103 | 111 | ||
| 104 | if (this.disabledMinutes.length && this.disabledMinutes.indexOf(i) > -1) { | 112 | if (this.disabledMinutes.length && this.disabledMinutes.indexOf(i) > -1) { |
| 105 | minute.disabled = true; | 113 | minute.disabled = true; |
| @@ -113,6 +121,7 @@ | @@ -113,6 +121,7 @@ | ||
| 113 | secondsList () { | 121 | secondsList () { |
| 114 | let seconds = []; | 122 | let seconds = []; |
| 115 | const step = this.spinerSteps[2]; | 123 | const step = this.spinerSteps[2]; |
| 124 | + const focusedMinute = this.focusedColumn === 2 && this.focusedTime[2]; | ||
| 116 | const second_tmpl = { | 125 | const second_tmpl = { |
| 117 | text: 0, | 126 | text: 0, |
| 118 | selected: false, | 127 | selected: false, |
| @@ -123,6 +132,7 @@ | @@ -123,6 +132,7 @@ | ||
| 123 | for (let i = 0; i < 60; i += step) { | 132 | for (let i = 0; i < 60; i += step) { |
| 124 | const second = deepCopy(second_tmpl); | 133 | const second = deepCopy(second_tmpl); |
| 125 | second.text = i; | 134 | second.text = i; |
| 135 | + second.focused = i === focusedMinute; | ||
| 126 | 136 | ||
| 127 | if (this.disabledSeconds.length && this.disabledSeconds.indexOf(i) > -1) { | 137 | if (this.disabledSeconds.length && this.disabledSeconds.indexOf(i) > -1) { |
| 128 | second.disabled = true; | 138 | second.disabled = true; |
| @@ -141,15 +151,32 @@ | @@ -141,15 +151,32 @@ | ||
| 141 | `${prefixCls}-cell`, | 151 | `${prefixCls}-cell`, |
| 142 | { | 152 | { |
| 143 | [`${prefixCls}-cell-selected`]: cell.selected, | 153 | [`${prefixCls}-cell-selected`]: cell.selected, |
| 154 | + [`${prefixCls}-cell-focused`]: cell.focused, | ||
| 144 | [`${prefixCls}-cell-disabled`]: cell.disabled | 155 | [`${prefixCls}-cell-disabled`]: cell.disabled |
| 156 | + | ||
| 145 | } | 157 | } |
| 146 | ]; | 158 | ]; |
| 147 | }, | 159 | }, |
| 160 | + chooseValue(values){ | ||
| 161 | + const changes = timeParts.reduce((obj, part, i) => { | ||
| 162 | + const value = values[i]; | ||
| 163 | + if (this[part] === value) return obj; | ||
| 164 | + return { | ||
| 165 | + ...obj, | ||
| 166 | + [part]: value | ||
| 167 | + }; | ||
| 168 | + }, {}); | ||
| 169 | + if (Object.keys(changes).length > 0) { | ||
| 170 | + this.emitChange(changes); | ||
| 171 | + } | ||
| 172 | + }, | ||
| 148 | handleClick (type, cell) { | 173 | handleClick (type, cell) { |
| 149 | if (cell.disabled) return; | 174 | if (cell.disabled) return; |
| 150 | - const data = {}; | ||
| 151 | - data[type] = cell.text; | ||
| 152 | - this.$emit('on-change', data); | 175 | + const data = {[type]: cell.text}; |
| 176 | + this.emitChange(data); | ||
| 177 | + }, | ||
| 178 | + emitChange(changes){ | ||
| 179 | + this.$emit('on-change', changes); | ||
| 153 | this.$emit('on-pick-click'); | 180 | this.$emit('on-pick-click'); |
| 154 | }, | 181 | }, |
| 155 | scroll (type, index) { | 182 | scroll (type, index) { |
| @@ -168,15 +195,19 @@ | @@ -168,15 +195,19 @@ | ||
| 168 | return index; | 195 | return index; |
| 169 | }, | 196 | }, |
| 170 | updateScroll () { | 197 | updateScroll () { |
| 171 | - const times = ['hours', 'minutes', 'seconds']; | ||
| 172 | this.$nextTick(() => { | 198 | this.$nextTick(() => { |
| 173 | - times.forEach(type => { | 199 | + timeParts.forEach(type => { |
| 174 | this.$refs[type].scrollTop = 24 * this[`${type}List`].findIndex(obj => obj.text == this[type]); | 200 | this.$refs[type].scrollTop = 24 * this[`${type}List`].findIndex(obj => obj.text == this[type]); |
| 175 | }); | 201 | }); |
| 176 | }); | 202 | }); |
| 177 | }, | 203 | }, |
| 178 | formatTime (text) { | 204 | formatTime (text) { |
| 179 | return text < 10 ? '0' + text : text; | 205 | return text < 10 ? '0' + text : text; |
| 206 | + }, | ||
| 207 | + updateFocusedTime(col, time) { | ||
| 208 | + this.focusedColumn = col; | ||
| 209 | + this.focusedTime = time.slice(); | ||
| 210 | + | ||
| 180 | } | 211 | } |
| 181 | }, | 212 | }, |
| 182 | watch: { | 213 | watch: { |
| @@ -191,6 +222,13 @@ | @@ -191,6 +222,13 @@ | ||
| 191 | seconds (val) { | 222 | seconds (val) { |
| 192 | if (!this.compiled) return; | 223 | if (!this.compiled) return; |
| 193 | this.scroll('seconds', this.secondsList.findIndex(obj => obj.text == val)); | 224 | this.scroll('seconds', this.secondsList.findIndex(obj => obj.text == val)); |
| 225 | + }, | ||
| 226 | + focusedTime(updated, old){ | ||
| 227 | + timeParts.forEach((part, i) => { | ||
| 228 | + if (updated[i] === old[i] || typeof updated[i] === 'undefined') return; | ||
| 229 | + const valueIndex = this[`${part}List`].findIndex(obj => obj.text === updated[i]); | ||
| 230 | + this.scroll(part, valueIndex); | ||
| 231 | + }); | ||
| 194 | } | 232 | } |
| 195 | }, | 233 | }, |
| 196 | mounted () { | 234 | mounted () { |
src/components/date-picker/base/year-table.vue
| @@ -39,13 +39,15 @@ | @@ -39,13 +39,15 @@ | ||
| 39 | }; | 39 | }; |
| 40 | 40 | ||
| 41 | const selectedDays = this.dates.filter(Boolean).map(date => clearHours(new Date(date.getFullYear(), 0, 1))); | 41 | const selectedDays = this.dates.filter(Boolean).map(date => clearHours(new Date(date.getFullYear(), 0, 1))); |
| 42 | + const focusedDate = clearHours(new Date(this.focusedDate.getFullYear(), 0, 1)); | ||
| 42 | 43 | ||
| 43 | for (let i = 0; i < 10; i++) { | 44 | for (let i = 0; i < 10; i++) { |
| 44 | const cell = deepCopy(cell_tmpl); | 45 | const cell = deepCopy(cell_tmpl); |
| 45 | cell.date = new Date(this.startYear + i, 0, 1); | 46 | cell.date = new Date(this.startYear + i, 0, 1); |
| 46 | cell.disabled = typeof this.disabledDate === 'function' && this.disabledDate(cell.date) && this.selectionMode === 'year'; | 47 | cell.disabled = typeof this.disabledDate === 'function' && this.disabledDate(cell.date) && this.selectionMode === 'year'; |
| 47 | - const time = clearHours(cell.date); | ||
| 48 | - cell.selected = selectedDays.includes(time); | 48 | + const day = clearHours(cell.date); |
| 49 | + cell.selected = selectedDays.includes(day); | ||
| 50 | + cell.focused = day === focusedDate; | ||
| 49 | cells.push(cell); | 51 | cells.push(cell); |
| 50 | } | 52 | } |
| 51 | 53 | ||
| @@ -59,6 +61,7 @@ | @@ -59,6 +61,7 @@ | ||
| 59 | { | 61 | { |
| 60 | [`${prefixCls}-cell-selected`]: cell.selected, | 62 | [`${prefixCls}-cell-selected`]: cell.selected, |
| 61 | [`${prefixCls}-cell-disabled`]: cell.disabled, | 63 | [`${prefixCls}-cell-disabled`]: cell.disabled, |
| 64 | + [`${prefixCls}-cell-focused`]: cell.focused, | ||
| 62 | [`${prefixCls}-cell-range`]: cell.range && !cell.start && !cell.end | 65 | [`${prefixCls}-cell-range`]: cell.range && !cell.start && !cell.end |
| 63 | } | 66 | } |
| 64 | ]; | 67 | ]; |
src/components/date-picker/panel/Date/date-panel-mixin.js
src/components/date-picker/panel/Date/date-range.vue
| @@ -41,6 +41,8 @@ | @@ -41,6 +41,8 @@ | ||
| 41 | :range-state="rangeState" | 41 | :range-state="rangeState" |
| 42 | :show-week-numbers="showWeekNumbers" | 42 | :show-week-numbers="showWeekNumbers" |
| 43 | :value="preSelecting.left ? [dates[0]] : dates" | 43 | :value="preSelecting.left ? [dates[0]] : dates" |
| 44 | + :focused-date="focusedDate" | ||
| 45 | + | ||
| 44 | @on-change-range="handleChangeRange" | 46 | @on-change-range="handleChangeRange" |
| 45 | @on-pick="panelPickerHandlers.left" | 47 | @on-pick="panelPickerHandlers.left" |
| 46 | @on-pick-click="handlePickClick" | 48 | @on-pick-click="handlePickClick" |
| @@ -80,6 +82,8 @@ | @@ -80,6 +82,8 @@ | ||
| 80 | :disabled-date="disabledDate" | 82 | :disabled-date="disabledDate" |
| 81 | :show-week-numbers="showWeekNumbers" | 83 | :show-week-numbers="showWeekNumbers" |
| 82 | :value="preSelecting.right ? [dates[dates.length - 1]] : dates" | 84 | :value="preSelecting.right ? [dates[dates.length - 1]] : dates" |
| 85 | + :focused-date="focusedDate" | ||
| 86 | + | ||
| 83 | @on-change-range="handleChangeRange" | 87 | @on-change-range="handleChangeRange" |
| 84 | @on-pick="panelPickerHandlers.right" | 88 | @on-pick="panelPickerHandlers.right" |
| 85 | @on-pick-click="handlePickClick"></component> | 89 | @on-pick-click="handlePickClick"></component> |
| @@ -178,7 +182,7 @@ | @@ -178,7 +182,7 @@ | ||
| 178 | [prefixCls + '-body-time']: this.showTime, | 182 | [prefixCls + '-body-time']: this.showTime, |
| 179 | [prefixCls + '-body-date']: !this.showTime, | 183 | [prefixCls + '-body-date']: !this.showTime, |
| 180 | } | 184 | } |
| 181 | - ] | 185 | + ]; |
| 182 | }, | 186 | }, |
| 183 | leftDatePanelLabel(){ | 187 | leftDatePanelLabel(){ |
| 184 | return this.panelLabelConfig('left'); | 188 | return this.panelLabelConfig('left'); |
| @@ -224,10 +228,7 @@ | @@ -224,10 +228,7 @@ | ||
| 224 | 228 | ||
| 225 | 229 | ||
| 226 | // set panels positioning | 230 | // set panels positioning |
| 227 | - const leftPanelDate = this.startDate || this.dates[0] || new Date(); | ||
| 228 | - this.leftPanelDate = leftPanelDate; | ||
| 229 | - const rightPanelDate = new Date(leftPanelDate.getFullYear(), leftPanelDate.getMonth() + 1, leftPanelDate.getDate()); | ||
| 230 | - this.rightPanelDate = this.splitPanels ? new Date(Math.max(this.dates[1], rightPanelDate)) : rightPanelDate; | 231 | + this.setPanelDates(this.startDate || this.dates[0] || new Date()); |
| 231 | }, | 232 | }, |
| 232 | currentView(currentView){ | 233 | currentView(currentView){ |
| 233 | const leftMonth = this.leftPanelDate.getMonth(); | 234 | const leftMonth = this.leftPanelDate.getMonth(); |
| @@ -246,6 +247,9 @@ | @@ -246,6 +247,9 @@ | ||
| 246 | }, | 247 | }, |
| 247 | selectionMode(type){ | 248 | selectionMode(type){ |
| 248 | this.currentView = type || 'range'; | 249 | this.currentView = type || 'range'; |
| 250 | + }, | ||
| 251 | + focusedDate(date){ | ||
| 252 | + this.setPanelDates(date || new Date()); | ||
| 249 | } | 253 | } |
| 250 | }, | 254 | }, |
| 251 | methods: { | 255 | methods: { |
| @@ -254,6 +258,11 @@ | @@ -254,6 +258,11 @@ | ||
| 254 | this.leftPickerTable = `${this.currentView}-table`; | 258 | this.leftPickerTable = `${this.currentView}-table`; |
| 255 | this.rightPickerTable = `${this.currentView}-table`; | 259 | this.rightPickerTable = `${this.currentView}-table`; |
| 256 | }, | 260 | }, |
| 261 | + setPanelDates(leftPanelDate){ | ||
| 262 | + this.leftPanelDate = leftPanelDate; | ||
| 263 | + const rightPanelDate = new Date(leftPanelDate.getFullYear(), leftPanelDate.getMonth() + 1, leftPanelDate.getDate()); | ||
| 264 | + this.rightPanelDate = this.splitPanels ? new Date(Math.max(this.dates[1], rightPanelDate)) : rightPanelDate; | ||
| 265 | + }, | ||
| 257 | panelLabelConfig (direction) { | 266 | panelLabelConfig (direction) { |
| 258 | const locale = this.t('i.locale'); | 267 | const locale = this.t('i.locale'); |
| 259 | const datePanelLabel = this.t('i.datepicker.datePanelLabel'); | 268 | const datePanelLabel = this.t('i.datepicker.datePanelLabel'); |
src/components/date-picker/panel/Date/date.vue
| @@ -39,6 +39,8 @@ | @@ -39,6 +39,8 @@ | ||
| 39 | :value="dates" | 39 | :value="dates" |
| 40 | :selection-mode="selectionMode" | 40 | :selection-mode="selectionMode" |
| 41 | :disabled-date="disabledDate" | 41 | :disabled-date="disabledDate" |
| 42 | + :focused-date="focusedDate" | ||
| 43 | + | ||
| 42 | @on-pick="panelPickerHandlers" | 44 | @on-pick="panelPickerHandlers" |
| 43 | @on-pick-click="handlePickClick" | 45 | @on-pick-click="handlePickClick" |
| 44 | ></component> | 46 | ></component> |
| @@ -51,6 +53,8 @@ | @@ -51,6 +53,8 @@ | ||
| 51 | :format="format" | 53 | :format="format" |
| 52 | :time-disabled="timeDisabled" | 54 | :time-disabled="timeDisabled" |
| 53 | :disabled-date="disabledDate" | 55 | :disabled-date="disabledDate" |
| 56 | + :focused-date="focusedDate" | ||
| 57 | + | ||
| 54 | v-bind="timePickerOptions" | 58 | v-bind="timePickerOptions" |
| 55 | @on-pick="handlePick" | 59 | @on-pick="handlePick" |
| 56 | @on-pick-click="handlePickClick" | 60 | @on-pick-click="handlePickClick" |
| @@ -150,7 +154,6 @@ | @@ -150,7 +154,6 @@ | ||
| 150 | }, | 154 | }, |
| 151 | currentView (currentView) { | 155 | currentView (currentView) { |
| 152 | this.$emit('on-selection-mode-change', currentView); | 156 | this.$emit('on-selection-mode-change', currentView); |
| 153 | - this.pickertable = this.getTableType(currentView); | ||
| 154 | 157 | ||
| 155 | if (this.currentView === 'time') { | 158 | if (this.currentView === 'time') { |
| 156 | this.$nextTick(() => { | 159 | this.$nextTick(() => { |
| @@ -162,6 +165,13 @@ | @@ -162,6 +165,13 @@ | ||
| 162 | selectionMode(type){ | 165 | selectionMode(type){ |
| 163 | this.currentView = type; | 166 | this.currentView = type; |
| 164 | this.pickerTable = this.getTableType(type); | 167 | this.pickerTable = this.getTableType(type); |
| 168 | + }, | ||
| 169 | + focusedDate(date){ | ||
| 170 | + const isDifferentYear = date.getFullYear() !== this.panelDate.getFullYear(); | ||
| 171 | + const isDifferentMonth = isDifferentYear || date.getMonth() !== this.panelDate.getMonth(); | ||
| 172 | + if (isDifferentYear || isDifferentMonth){ | ||
| 173 | + this.panelDate = date; | ||
| 174 | + } | ||
| 165 | } | 175 | } |
| 166 | }, | 176 | }, |
| 167 | methods: { | 177 | methods: { |
src/components/date-picker/picker.vue
| 1 | <template> | 1 | <template> |
| 2 | - <div :class="[prefixCls]" v-clickoutside="handleClose"> | 2 | + <div |
| 3 | + :class="wrapperClasses" | ||
| 4 | + v-click-outside:mousedown.capture="handleClose" | ||
| 5 | + v-click-outside.capture="handleClose" | ||
| 6 | + > | ||
| 3 | <div ref="reference" :class="[prefixCls + '-rel']"> | 7 | <div ref="reference" :class="[prefixCls + '-rel']"> |
| 4 | <slot> | 8 | <slot> |
| 5 | <i-input | 9 | <i-input |
| @@ -12,10 +16,14 @@ | @@ -12,10 +16,14 @@ | ||
| 12 | :placeholder="placeholder" | 16 | :placeholder="placeholder" |
| 13 | :value="visualValue" | 17 | :value="visualValue" |
| 14 | :name="name" | 18 | :name="name" |
| 19 | + ref="input" | ||
| 20 | + | ||
| 15 | @on-input-change="handleInputChange" | 21 | @on-input-change="handleInputChange" |
| 16 | @on-focus="handleFocus" | 22 | @on-focus="handleFocus" |
| 17 | @on-blur="handleBlur" | 23 | @on-blur="handleBlur" |
| 18 | @on-click="handleIconClick" | 24 | @on-click="handleIconClick" |
| 25 | + @click.native="handleFocus" | ||
| 26 | + @keydown.native="handleKeydown" | ||
| 19 | @mouseenter.native="handleInputMouseenter" | 27 | @mouseenter.native="handleInputMouseenter" |
| 20 | @mouseleave.native="handleInputMouseleave" | 28 | @mouseleave.native="handleInputMouseleave" |
| 21 | 29 | ||
| @@ -48,6 +56,7 @@ | @@ -48,6 +56,7 @@ | ||
| 48 | :show-week-numbers="showWeekNumbers" | 56 | :show-week-numbers="showWeekNumbers" |
| 49 | :picker-type="type" | 57 | :picker-type="type" |
| 50 | :multiple="multiple" | 58 | :multiple="multiple" |
| 59 | + :focused-date="focusedDate" | ||
| 51 | 60 | ||
| 52 | :time-picker-options="timePickerOptions" | 61 | :time-picker-options="timePickerOptions" |
| 53 | 62 | ||
| @@ -69,21 +78,49 @@ | @@ -69,21 +78,49 @@ | ||
| 69 | 78 | ||
| 70 | import iInput from '../../components/input/input.vue'; | 79 | import iInput from '../../components/input/input.vue'; |
| 71 | import Drop from '../../components/select/dropdown.vue'; | 80 | import Drop from '../../components/select/dropdown.vue'; |
| 72 | - import clickoutside from '../../directives/clickoutside'; | 81 | + import vClickOutside from 'v-click-outside-x/index'; |
| 73 | import TransferDom from '../../directives/transfer-dom'; | 82 | import TransferDom from '../../directives/transfer-dom'; |
| 74 | import { oneOf } from '../../utils/assist'; | 83 | import { oneOf } from '../../utils/assist'; |
| 75 | - import { DEFAULT_FORMATS, RANGE_SEPARATOR, TYPE_VALUE_RESOLVER_MAP } from './util'; | 84 | + import { DEFAULT_FORMATS, RANGE_SEPARATOR, TYPE_VALUE_RESOLVER_MAP, getDayCountOfMonth } from './util'; |
| 85 | + import {findComponentsDownward} from '../../utils/assist'; | ||
| 76 | import Emitter from '../../mixins/emitter'; | 86 | import Emitter from '../../mixins/emitter'; |
| 77 | 87 | ||
| 78 | const prefixCls = 'ivu-date-picker'; | 88 | const prefixCls = 'ivu-date-picker'; |
| 89 | + const pickerPrefixCls = 'ivu-picker'; | ||
| 79 | 90 | ||
| 80 | const isEmptyArray = val => val.reduce((isEmpty, str) => isEmpty && !str || (typeof str === 'string' && str.trim() === ''), true); | 91 | const isEmptyArray = val => val.reduce((isEmpty, str) => isEmpty && !str || (typeof str === 'string' && str.trim() === ''), true); |
| 92 | + const keyValueMapper = { | ||
| 93 | + 40: 'up', | ||
| 94 | + 39: 'right', | ||
| 95 | + 38: 'down', | ||
| 96 | + 37: 'left', | ||
| 97 | + }; | ||
| 98 | + | ||
| 99 | + const mapPossibleValues = (key, horizontal, vertical) => { | ||
| 100 | + if (key === 'left') return horizontal * -1; | ||
| 101 | + if (key === 'right') return horizontal * 1; | ||
| 102 | + if (key === 'up') return vertical * 1; | ||
| 103 | + if (key === 'down') return vertical * -1; | ||
| 104 | + }; | ||
| 105 | + | ||
| 106 | + const pulseElement = (el) => { | ||
| 107 | + const pulseClass = 'ivu-date-picker-btn-pulse'; | ||
| 108 | + el.classList.add(pulseClass); | ||
| 109 | + setTimeout(() => el.classList.remove(pulseClass), 200); | ||
| 110 | + }; | ||
| 111 | + | ||
| 112 | + const extractTime = date => { | ||
| 113 | + if (!date) return [0, 0, 0]; | ||
| 114 | + return [ | ||
| 115 | + date.getHours(), date.getMinutes(), date.getSeconds() | ||
| 116 | + ]; | ||
| 117 | + }; | ||
| 118 | + | ||
| 81 | 119 | ||
| 82 | export default { | 120 | export default { |
| 83 | - name: 'CalendarPicker', | ||
| 84 | mixins: [ Emitter ], | 121 | mixins: [ Emitter ], |
| 85 | components: { iInput, Drop }, | 122 | components: { iInput, Drop }, |
| 86 | - directives: { clickoutside, TransferDom }, | 123 | + directives: { clickOutside: vClickOutside.directive, TransferDom }, |
| 87 | props: { | 124 | props: { |
| 88 | format: { | 125 | format: { |
| 89 | type: String | 126 | type: String |
| @@ -172,6 +209,7 @@ | @@ -172,6 +209,7 @@ | ||
| 172 | const isRange = this.type.includes('range'); | 209 | const isRange = this.type.includes('range'); |
| 173 | const emptyArray = isRange ? [null, null] : [null]; | 210 | const emptyArray = isRange ? [null, null] : [null]; |
| 174 | const initialValue = isEmptyArray((isRange ? this.value : [this.value]) || []) ? emptyArray : this.parseDate(this.value); | 211 | const initialValue = isEmptyArray((isRange ? this.value : [this.value]) || []) ? emptyArray : this.parseDate(this.value); |
| 212 | + const focusedTime = initialValue.map(extractTime); | ||
| 175 | 213 | ||
| 176 | return { | 214 | return { |
| 177 | prefixCls: prefixCls, | 215 | prefixCls: prefixCls, |
| @@ -181,10 +219,24 @@ | @@ -181,10 +219,24 @@ | ||
| 181 | disableClickOutSide: false, // fixed when click a date,trigger clickoutside to close picker | 219 | disableClickOutSide: false, // fixed when click a date,trigger clickoutside to close picker |
| 182 | disableCloseUnderTransfer: false, // transfer ๆจกๅผไธ๏ผ็นๅปDropไนไผ่งฆๅๅ ณ้ญ, | 220 | disableCloseUnderTransfer: false, // transfer ๆจกๅผไธ๏ผ็นๅปDropไนไผ่งฆๅๅ ณ้ญ, |
| 183 | selectionMode: this.onSelectionModeChange(this.type), | 221 | selectionMode: this.onSelectionModeChange(this.type), |
| 184 | - forceInputRerender: 1 | 222 | + forceInputRerender: 1, |
| 223 | + isFocused: false, | ||
| 224 | + focusedDate: initialValue[0] || this.startDate || new Date(), | ||
| 225 | + focusedTime: { | ||
| 226 | + column: 0, // which column inside the picker | ||
| 227 | + picker: 0, // which picker | ||
| 228 | + time: focusedTime, // the values array into [hh, mm, ss], | ||
| 229 | + active: false | ||
| 230 | + }, | ||
| 231 | + internalFocus: false, | ||
| 185 | }; | 232 | }; |
| 186 | }, | 233 | }, |
| 187 | computed: { | 234 | computed: { |
| 235 | + wrapperClasses(){ | ||
| 236 | + return [prefixCls, { | ||
| 237 | + [prefixCls + '-focused']: this.isFocused | ||
| 238 | + }]; | ||
| 239 | + }, | ||
| 188 | publicVModelValue(){ | 240 | publicVModelValue(){ |
| 189 | if (this.multiple){ | 241 | if (this.multiple){ |
| 190 | return this.internalValue.slice(); | 242 | return this.internalValue.slice(); |
| @@ -232,32 +284,254 @@ | @@ -232,32 +284,254 @@ | ||
| 232 | handleTransferClick () { | 284 | handleTransferClick () { |
| 233 | if (this.transfer) this.disableCloseUnderTransfer = true; | 285 | if (this.transfer) this.disableCloseUnderTransfer = true; |
| 234 | }, | 286 | }, |
| 235 | - handleClose () { | 287 | + handleClose (e) { |
| 236 | if (this.disableCloseUnderTransfer) { | 288 | if (this.disableCloseUnderTransfer) { |
| 237 | this.disableCloseUnderTransfer = false; | 289 | this.disableCloseUnderTransfer = false; |
| 238 | return false; | 290 | return false; |
| 239 | } | 291 | } |
| 240 | - if (this.open !== null) return; | ||
| 241 | 292 | ||
| 242 | - this.visible = false; | 293 | + if (e && e.type === 'mousedown' && this.visible) { |
| 294 | + e.preventDefault(); | ||
| 295 | + e.stopPropagation(); | ||
| 296 | + return; | ||
| 297 | + } | ||
| 298 | + | ||
| 299 | + if (this.visible) { | ||
| 300 | + const pickerPanel = this.$refs.pickerPanel && this.$refs.pickerPanel.$el; | ||
| 301 | + if (e && pickerPanel && pickerPanel.contains(e.target)) return; // its a click inside own component, lets ignore it. | ||
| 302 | + | ||
| 303 | + this.visible = false; | ||
| 304 | + e && e.preventDefault(); | ||
| 305 | + e && e.stopPropagation(); | ||
| 306 | + return; | ||
| 307 | + } | ||
| 308 | + | ||
| 309 | + this.isFocused = false; | ||
| 243 | this.disableClickOutSide = false; | 310 | this.disableClickOutSide = false; |
| 244 | }, | 311 | }, |
| 245 | - handleFocus () { | 312 | + handleFocus (e) { |
| 246 | if (this.readonly) return; | 313 | if (this.readonly) return; |
| 314 | + this.isFocused = true; | ||
| 315 | + if (e && e.type === 'focus') return; // just focus, don't open yet | ||
| 247 | this.visible = true; | 316 | this.visible = true; |
| 248 | - this.$refs.pickerPanel.onToggleVisibility(true); | ||
| 249 | }, | 317 | }, |
| 250 | - handleBlur () { | ||
| 251 | - this.visible = false; | 318 | + handleBlur (e) { |
| 319 | + if (this.internalFocus){ | ||
| 320 | + this.internalFocus = false; | ||
| 321 | + return; | ||
| 322 | + } | ||
| 323 | + if (this.visible) { | ||
| 324 | + e.preventDefault(); | ||
| 325 | + return; | ||
| 326 | + } | ||
| 327 | + | ||
| 328 | + this.isFocused = false; | ||
| 252 | this.onSelectionModeChange(this.type); | 329 | this.onSelectionModeChange(this.type); |
| 253 | this.internalValue = this.internalValue.slice(); // trigger panel watchers to reset views | 330 | this.internalValue = this.internalValue.slice(); // trigger panel watchers to reset views |
| 254 | this.reset(); | 331 | this.reset(); |
| 255 | this.$refs.pickerPanel.onToggleVisibility(false); | 332 | this.$refs.pickerPanel.onToggleVisibility(false); |
| 256 | 333 | ||
| 257 | }, | 334 | }, |
| 335 | + handleKeydown(e){ | ||
| 336 | + const keyCode = e.keyCode; | ||
| 337 | + | ||
| 338 | + // handle "tab" key | ||
| 339 | + if (keyCode === 9){ | ||
| 340 | + if (this.visible){ | ||
| 341 | + e.stopPropagation(); | ||
| 342 | + e.preventDefault(); | ||
| 343 | + | ||
| 344 | + if (this.isConfirm){ | ||
| 345 | + const selector = `.${pickerPrefixCls}-confirm > *`; | ||
| 346 | + const tabbable = this.$refs.drop.$el.querySelectorAll(selector); | ||
| 347 | + this.internalFocus = true; | ||
| 348 | + const element = [...tabbable][e.shiftKey ? 'pop' : 'shift'](); | ||
| 349 | + element.focus(); | ||
| 350 | + } else { | ||
| 351 | + this.handleClose(); | ||
| 352 | + } | ||
| 353 | + } else { | ||
| 354 | + this.focused = false; | ||
| 355 | + } | ||
| 356 | + } | ||
| 357 | + | ||
| 358 | + // open the panel | ||
| 359 | + const arrows = [37, 38, 39, 40]; | ||
| 360 | + if (!this.visible && arrows.includes(keyCode)){ | ||
| 361 | + this.visible = true; | ||
| 362 | + return; | ||
| 363 | + } | ||
| 364 | + | ||
| 365 | + // close on "esc" key | ||
| 366 | + if (keyCode === 27){ | ||
| 367 | + if (this.visible) { | ||
| 368 | + e.stopPropagation(); | ||
| 369 | + this.handleClose(); | ||
| 370 | + } | ||
| 371 | + } | ||
| 372 | + | ||
| 373 | + // select date, "Enter" key | ||
| 374 | + if (keyCode === 13){ | ||
| 375 | + const timePickers = findComponentsDownward(this, 'TimeSpinner'); | ||
| 376 | + if (timePickers.length > 0){ | ||
| 377 | + const columnsPerPicker = timePickers[0].showSeconds ? 3 : 2; | ||
| 378 | + const pickerIndex = Math.floor(this.focusedTime.column / columnsPerPicker); | ||
| 379 | + const value = this.focusedTime.time[pickerIndex]; | ||
| 380 | + | ||
| 381 | + timePickers[pickerIndex].chooseValue(value); | ||
| 382 | + return; | ||
| 383 | + } | ||
| 384 | + | ||
| 385 | + if (this.type.match(/range/)){ | ||
| 386 | + this.$refs.pickerPanel.handleRangePick(this.focusedDate, 'date'); | ||
| 387 | + } else { | ||
| 388 | + const panels = findComponentsDownward(this, 'PanelTable'); | ||
| 389 | + const compareDate = (d) => { | ||
| 390 | + const sliceIndex = ['year', 'month', 'date'].indexOf((this.type)) + 1; | ||
| 391 | + return [d.getFullYear(), d.getMonth(), d.getDate()].slice(0, sliceIndex).join('-'); | ||
| 392 | + }; | ||
| 393 | + const dateIsValid = panels.find(({cells}) => { | ||
| 394 | + return cells.find(({date, disabled}) => compareDate(date) === compareDate(this.focusedDate) && !disabled); | ||
| 395 | + }); | ||
| 396 | + if (dateIsValid) this.onPick(this.focusedDate, false, 'date'); | ||
| 397 | + } | ||
| 398 | + } | ||
| 399 | + | ||
| 400 | + if (!arrows.includes(keyCode)) return; // ignore rest of keys | ||
| 401 | + | ||
| 402 | + // navigate times and dates | ||
| 403 | + if (this.focusedTime.active) e.preventDefault(); // to prevent cursor from moving | ||
| 404 | + this.navigateDatePanel(keyValueMapper[keyCode], e.shiftKey); | ||
| 405 | + }, | ||
| 258 | reset(){ | 406 | reset(){ |
| 259 | this.$refs.pickerPanel.reset && this.$refs.pickerPanel.reset(); | 407 | this.$refs.pickerPanel.reset && this.$refs.pickerPanel.reset(); |
| 260 | }, | 408 | }, |
| 409 | + navigateTimePanel(direction){ | ||
| 410 | + | ||
| 411 | + this.focusedTime.active = true; | ||
| 412 | + const horizontal = direction.match(/left|right/); | ||
| 413 | + const vertical = direction.match(/up|down/); | ||
| 414 | + const timePickers = findComponentsDownward(this, 'TimeSpinner'); | ||
| 415 | + | ||
| 416 | + const maxNrOfColumns = (timePickers[0].showSeconds ? 3 : 2) * timePickers.length; | ||
| 417 | + const column = (currentColumn => { | ||
| 418 | + const incremented = currentColumn + (horizontal ? (direction === 'left' ? -1 : 1) : 0); | ||
| 419 | + return (incremented + maxNrOfColumns) % maxNrOfColumns; | ||
| 420 | + })(this.focusedTime.column); | ||
| 421 | + | ||
| 422 | + const columnsPerPicker = maxNrOfColumns / timePickers.length; | ||
| 423 | + const pickerIndex = Math.floor(column / columnsPerPicker); | ||
| 424 | + const col = column % columnsPerPicker; | ||
| 425 | + | ||
| 426 | + | ||
| 427 | + if (horizontal){ | ||
| 428 | + const time = this.internalValue.map(extractTime); | ||
| 429 | + | ||
| 430 | + this.focusedTime = { | ||
| 431 | + ...this.focusedTime, | ||
| 432 | + column: column, | ||
| 433 | + time: time | ||
| 434 | + }; | ||
| 435 | + timePickers.forEach((instance, i) => { | ||
| 436 | + if (i === pickerIndex) instance.updateFocusedTime(col, time[pickerIndex]); | ||
| 437 | + else instance.updateFocusedTime(-1, instance.focusedTime); | ||
| 438 | + }); | ||
| 439 | + } | ||
| 440 | + | ||
| 441 | + if (vertical){ | ||
| 442 | + const increment = direction === 'up' ? 1 : -1; | ||
| 443 | + const timeParts = ['hours', 'minutes', 'seconds']; | ||
| 444 | + | ||
| 445 | + | ||
| 446 | + const pickerPossibleValues = timePickers[pickerIndex][`${timeParts[col]}List`]; | ||
| 447 | + const nextIndex = pickerPossibleValues.findIndex(({text}) => this.focusedTime.time[pickerIndex][col] === text) + increment; | ||
| 448 | + const nextValue = pickerPossibleValues[nextIndex % pickerPossibleValues.length].text; | ||
| 449 | + const times = this.focusedTime.time.map((time, i) => { | ||
| 450 | + if (i !== pickerIndex) return time; | ||
| 451 | + time[col] = nextValue; | ||
| 452 | + return time; | ||
| 453 | + }); | ||
| 454 | + this.focusedTime = { | ||
| 455 | + ...this.focusedTime, | ||
| 456 | + time: times | ||
| 457 | + }; | ||
| 458 | + | ||
| 459 | + timePickers.forEach((instance, i) => { | ||
| 460 | + if (i === pickerIndex) instance.updateFocusedTime(col, times[i]); | ||
| 461 | + else instance.updateFocusedTime(-1, instance.focusedTime); | ||
| 462 | + }); | ||
| 463 | + } | ||
| 464 | + }, | ||
| 465 | + navigateDatePanel(direction, shift){ | ||
| 466 | + | ||
| 467 | + const timePickers = findComponentsDownward(this, 'TimeSpinner'); | ||
| 468 | + if (timePickers.length > 0) { | ||
| 469 | + // we are in TimePicker mode | ||
| 470 | + this.navigateTimePanel(direction, shift, timePickers); | ||
| 471 | + return; | ||
| 472 | + } | ||
| 473 | + | ||
| 474 | + if (shift){ | ||
| 475 | + if (this.type === 'year'){ | ||
| 476 | + this.focusedDate = new Date( | ||
| 477 | + this.focusedDate.getFullYear() + mapPossibleValues(direction, 0, 10), | ||
| 478 | + this.focusedDate.getMonth(), | ||
| 479 | + this.focusedDate.getDate() | ||
| 480 | + ); | ||
| 481 | + } else { | ||
| 482 | + this.focusedDate = new Date( | ||
| 483 | + this.focusedDate.getFullYear() + mapPossibleValues(direction, 0, 1), | ||
| 484 | + this.focusedDate.getMonth() + mapPossibleValues(direction, 1, 0), | ||
| 485 | + this.focusedDate.getDate() | ||
| 486 | + ); | ||
| 487 | + } | ||
| 488 | + | ||
| 489 | + const position = direction.match(/left|down/) ? 'prev' : 'next'; | ||
| 490 | + const double = direction.match(/up|down/) ? '-double' : ''; | ||
| 491 | + | ||
| 492 | + // pulse button | ||
| 493 | + const button = this.$refs.drop.$el.querySelector(`.ivu-date-picker-${position}-btn-arrow${double}`); | ||
| 494 | + if (button) pulseElement(button); | ||
| 495 | + return; | ||
| 496 | + } | ||
| 497 | + | ||
| 498 | + const initialDate = this.focusedDate || (this.internalValue && this.internalValue[0]) || new Date(); | ||
| 499 | + const focusedDate = new Date(initialDate); | ||
| 500 | + | ||
| 501 | + if (this.type.match(/^date/)){ | ||
| 502 | + const lastOfMonth = getDayCountOfMonth(initialDate.getFullYear(), initialDate.getMonth()); | ||
| 503 | + const startDay = initialDate.getDate(); | ||
| 504 | + const nextDay = focusedDate.getDate() + mapPossibleValues(direction, 1, 7); | ||
| 505 | + | ||
| 506 | + if (nextDay < 1) { | ||
| 507 | + if (direction.match(/left|right/)) { | ||
| 508 | + focusedDate.setMonth(focusedDate.getMonth() + 1); | ||
| 509 | + focusedDate.setDate(nextDay); | ||
| 510 | + } else { | ||
| 511 | + focusedDate.setDate(startDay + Math.floor((lastOfMonth - startDay) / 7) * 7); | ||
| 512 | + } | ||
| 513 | + } else if (nextDay > lastOfMonth){ | ||
| 514 | + if (direction.match(/left|right/)) { | ||
| 515 | + focusedDate.setMonth(focusedDate.getMonth() - 1); | ||
| 516 | + focusedDate.setDate(nextDay); | ||
| 517 | + } else { | ||
| 518 | + focusedDate.setDate(startDay % 7); | ||
| 519 | + } | ||
| 520 | + } else { | ||
| 521 | + focusedDate.setDate(nextDay); | ||
| 522 | + } | ||
| 523 | + } | ||
| 524 | + | ||
| 525 | + if (this.type.match(/^month/)) { | ||
| 526 | + focusedDate.setMonth(focusedDate.getMonth() + mapPossibleValues(direction, 1, 3)); | ||
| 527 | + } | ||
| 528 | + | ||
| 529 | + if (this.type.match(/^year/)) { | ||
| 530 | + focusedDate.setFullYear(focusedDate.getFullYear() + mapPossibleValues(direction, 1, 3)); | ||
| 531 | + } | ||
| 532 | + | ||
| 533 | + this.focusedDate = focusedDate; | ||
| 534 | + }, | ||
| 261 | handleInputChange (event) { | 535 | handleInputChange (event) { |
| 262 | const isArrayValue = this.type.includes('range') || this.multiple; | 536 | const isArrayValue = this.type.includes('range') || this.multiple; |
| 263 | const oldValue = this.visualValue; | 537 | const oldValue = this.visualValue; |
| @@ -377,6 +651,12 @@ | @@ -377,6 +651,12 @@ | ||
| 377 | this.internalValue = Array.isArray(dates) ? dates : [dates]; | 651 | this.internalValue = Array.isArray(dates) ? dates : [dates]; |
| 378 | } | 652 | } |
| 379 | 653 | ||
| 654 | + this.focusedDate = this.internalValue[0]; | ||
| 655 | + this.focusedTime = { | ||
| 656 | + ...this.focusedTime, | ||
| 657 | + time: this.internalValue.map(extractTime) | ||
| 658 | + }; | ||
| 659 | + | ||
| 380 | if (!this.isConfirm) this.onSelectionModeChange(this.type); // reset the selectionMode | 660 | if (!this.isConfirm) this.onSelectionModeChange(this.type); // reset the selectionMode |
| 381 | if (!this.isConfirm) this.visible = visible; | 661 | if (!this.isConfirm) this.visible = visible; |
| 382 | this.emitChange(type); | 662 | this.emitChange(type); |
| @@ -384,22 +664,23 @@ | @@ -384,22 +664,23 @@ | ||
| 384 | onPickSuccess(){ | 664 | onPickSuccess(){ |
| 385 | this.visible = false; | 665 | this.visible = false; |
| 386 | this.$emit('on-ok'); | 666 | this.$emit('on-ok'); |
| 667 | + this.focus(); | ||
| 387 | this.reset(); | 668 | this.reset(); |
| 388 | }, | 669 | }, |
| 670 | + focus() { | ||
| 671 | + this.$refs.input.focus(); | ||
| 672 | + } | ||
| 389 | }, | 673 | }, |
| 390 | watch: { | 674 | watch: { |
| 391 | visible (state) { | 675 | visible (state) { |
| 392 | if (state === false){ | 676 | if (state === false){ |
| 393 | this.$refs.drop.destroy(); | 677 | this.$refs.drop.destroy(); |
| 394 | - const input = this.$el.querySelector('input'); | ||
| 395 | - if (input) input.blur(); | ||
| 396 | } | 678 | } |
| 397 | this.$refs.drop.update(); | 679 | this.$refs.drop.update(); |
| 398 | this.$emit('on-open-change', state); | 680 | this.$emit('on-open-change', state); |
| 399 | }, | 681 | }, |
| 400 | value(val) { | 682 | value(val) { |
| 401 | this.internalValue = this.parseDate(val); | 683 | this.internalValue = this.parseDate(val); |
| 402 | - | ||
| 403 | }, | 684 | }, |
| 404 | open (val) { | 685 | open (val) { |
| 405 | this.visible = val === true; | 686 | this.visible = val === true; |
| @@ -421,6 +702,9 @@ | @@ -421,6 +702,9 @@ | ||
| 421 | this.$emit('input', this.publicVModelValue); // to update v-model | 702 | this.$emit('input', this.publicVModelValue); // to update v-model |
| 422 | } | 703 | } |
| 423 | if (this.open !== null) this.visible = this.open; | 704 | if (this.open !== null) this.visible = this.open; |
| 705 | + | ||
| 706 | + // to handle focus from confirm buttons | ||
| 707 | + this.$on('focus-input', () => this.focus()); | ||
| 424 | } | 708 | } |
| 425 | }; | 709 | }; |
| 426 | </script> | 710 | </script> |
src/components/date-picker/picker/date-picker.js
| @@ -5,6 +5,7 @@ import RangeDatePickerPanel from '../panel/Date/date-range.vue'; | @@ -5,6 +5,7 @@ import RangeDatePickerPanel from '../panel/Date/date-range.vue'; | ||
| 5 | import { oneOf } from '../../../utils/assist'; | 5 | import { oneOf } from '../../../utils/assist'; |
| 6 | 6 | ||
| 7 | export default { | 7 | export default { |
| 8 | + name: 'CalendarPicker', | ||
| 8 | mixins: [Picker], | 9 | mixins: [Picker], |
| 9 | props: { | 10 | props: { |
| 10 | type: { | 11 | type: { |
src/components/date-picker/picker/time-picker.js
| @@ -3,7 +3,7 @@ import TimePickerPanel from '../panel/Time/time.vue'; | @@ -3,7 +3,7 @@ import TimePickerPanel from '../panel/Time/time.vue'; | ||
| 3 | import RangeTimePickerPanel from '../panel/Time/time-range.vue'; | 3 | import RangeTimePickerPanel from '../panel/Time/time-range.vue'; |
| 4 | import Options from '../time-mixins'; | 4 | import Options from '../time-mixins'; |
| 5 | 5 | ||
| 6 | -import { oneOf } from '../../../utils/assist'; | 6 | +import { findComponentsDownward, oneOf } from '../../../utils/assist'; |
| 7 | 7 | ||
| 8 | export default { | 8 | export default { |
| 9 | mixins: [Picker, Options], | 9 | mixins: [Picker, Options], |
| @@ -30,4 +30,14 @@ export default { | @@ -30,4 +30,14 @@ export default { | ||
| 30 | }; | 30 | }; |
| 31 | } | 31 | } |
| 32 | }, | 32 | }, |
| 33 | + watch: { | ||
| 34 | + visible(visible){ | ||
| 35 | + if (visible) { | ||
| 36 | + this.$nextTick(() => { | ||
| 37 | + const spinners = findComponentsDownward(this, 'TimeSpinner'); | ||
| 38 | + spinners.forEach(instance => instance.updateScroll()); | ||
| 39 | + }); | ||
| 40 | + } | ||
| 41 | + } | ||
| 42 | + } | ||
| 33 | }; | 43 | }; |
src/styles/components/date-picker.less
| @@ -44,17 +44,23 @@ | @@ -44,17 +44,23 @@ | ||
| 44 | margin: 2px; | 44 | margin: 2px; |
| 45 | color: @btn-disable-color; | 45 | color: @btn-disable-color; |
| 46 | } | 46 | } |
| 47 | + &-cell:hover{ | ||
| 48 | + em{ | ||
| 49 | + background: @date-picker-cell-hover-bg; | ||
| 50 | + } | ||
| 51 | + } | ||
| 52 | + &-focused{ | ||
| 53 | + em{ | ||
| 54 | + box-shadow: 0 0 0 1px @primary-color inset; | ||
| 55 | + } | ||
| 56 | + } | ||
| 57 | + | ||
| 47 | &-cell{ | 58 | &-cell{ |
| 48 | span&{ | 59 | span&{ |
| 49 | width: 28px; | 60 | width: 28px; |
| 50 | height: 28px; | 61 | height: 28px; |
| 51 | cursor: pointer; | 62 | cursor: pointer; |
| 52 | } | 63 | } |
| 53 | - &:hover{ | ||
| 54 | - em{ | ||
| 55 | - background: @date-picker-cell-hover-bg; | ||
| 56 | - } | ||
| 57 | - } | ||
| 58 | &-prev-month,&-next-month{ | 64 | &-prev-month,&-next-month{ |
| 59 | em{ | 65 | em{ |
| 60 | color: @btn-disable-color; | 66 | color: @btn-disable-color; |
| @@ -154,6 +160,11 @@ | @@ -154,6 +160,11 @@ | ||
| 154 | margin: 0; | 160 | margin: 0; |
| 155 | } | 161 | } |
| 156 | } | 162 | } |
| 163 | + | ||
| 164 | + .@{date-picker-prefix-cls}-cells-cell-focused{ | ||
| 165 | + background-color: tint(@primary-color, 80%); | ||
| 166 | + } | ||
| 167 | + | ||
| 157 | } | 168 | } |
| 158 | 169 | ||
| 159 | &-header{ | 170 | &-header{ |
| @@ -169,6 +180,11 @@ | @@ -169,6 +180,11 @@ | ||
| 169 | } | 180 | } |
| 170 | } | 181 | } |
| 171 | } | 182 | } |
| 183 | + &-btn-pulse{ | ||
| 184 | + background-color: tint(@primary-color, 80%) !important; | ||
| 185 | + border-radius: @border-radius-small; | ||
| 186 | + transition: background-color @transition-time @ease-in-out; | ||
| 187 | + } | ||
| 172 | &-prev-btn{ | 188 | &-prev-btn{ |
| 173 | float: left; | 189 | float: left; |
| 174 | &-arrow-double{ | 190 | &-arrow-double{ |
| @@ -216,6 +232,10 @@ | @@ -216,6 +232,10 @@ | ||
| 216 | max-height: none; | 232 | max-height: none; |
| 217 | width: auto; | 233 | width: auto; |
| 218 | } | 234 | } |
| 235 | + | ||
| 236 | + &-focused input{ | ||
| 237 | + .active(); | ||
| 238 | + } | ||
| 219 | } | 239 | } |
| 220 | 240 | ||
| 221 | .@{picker-prefix-cls} { | 241 | .@{picker-prefix-cls} { |
| @@ -289,9 +309,9 @@ | @@ -289,9 +309,9 @@ | ||
| 289 | color: @link-active-color; | 309 | color: @link-active-color; |
| 290 | } | 310 | } |
| 291 | } | 311 | } |
| 292 | - & > span&-time-disabled{ | ||
| 293 | - color: @btn-disable-color; | ||
| 294 | - cursor: @cursor-disabled; | 312 | + |
| 313 | + &-time{ | ||
| 314 | + float: left; | ||
| 295 | } | 315 | } |
| 296 | } | 316 | } |
| 297 | } | 317 | } |
src/styles/components/time-picker.less
| @@ -70,6 +70,9 @@ | @@ -70,6 +70,9 @@ | ||
| 70 | color: @primary-color; | 70 | color: @primary-color; |
| 71 | background: @background-color-select-hover; | 71 | background: @background-color-select-hover; |
| 72 | } | 72 | } |
| 73 | + &-focused{ | ||
| 74 | + background-color: tint(@primary-color, 80%); | ||
| 75 | + } | ||
| 73 | } | 76 | } |
| 74 | } | 77 | } |
| 75 | 78 | ||
| @@ -165,4 +168,4 @@ | @@ -165,4 +168,4 @@ | ||
| 165 | } | 168 | } |
| 166 | } | 169 | } |
| 167 | } | 170 | } |
| 168 | -} | ||
| 169 | \ No newline at end of file | 171 | \ No newline at end of file |
| 172 | +} |
test/unit/specs/date-picker.spec.js
| @@ -116,7 +116,7 @@ describe('DatePicker.vue', () => { | @@ -116,7 +116,7 @@ describe('DatePicker.vue', () => { | ||
| 116 | `); | 116 | `); |
| 117 | 117 | ||
| 118 | const picker = vm.$children[0]; | 118 | const picker = vm.$children[0]; |
| 119 | - picker.handleIconClick(); | 119 | + picker.handleFocus({type: 'focus'}); |
| 120 | vm.$nextTick(() => { | 120 | vm.$nextTick(() => { |
| 121 | const displayField = vm.$el.querySelector('.ivu-input'); | 121 | const displayField = vm.$el.querySelector('.ivu-input'); |
| 122 | const clickableCells = vm.$el.querySelectorAll('.ivu-date-picker-cells-cell'); | 122 | const clickableCells = vm.$el.querySelectorAll('.ivu-date-picker-cells-cell'); |
| @@ -169,7 +169,7 @@ describe('DatePicker.vue', () => { | @@ -169,7 +169,7 @@ describe('DatePicker.vue', () => { | ||
| 169 | }); | 169 | }); |
| 170 | 170 | ||
| 171 | const picker = vm.$children[0]; | 171 | const picker = vm.$children[0]; |
| 172 | - picker.handleIconClick(); | 172 | + picker.handleFocus({type: 'focus'}); |
| 173 | vm.$nextTick(() => { | 173 | vm.$nextTick(() => { |
| 174 | const panel = vm.$el.querySelector('.ivu-picker-panel-content'); | 174 | const panel = vm.$el.querySelector('.ivu-picker-panel-content'); |
| 175 | const dayPanel = panel.querySelector('[class="ivu-date-picker-cells"]'); | 175 | const dayPanel = panel.querySelector('[class="ivu-date-picker-cells"]'); |
| @@ -243,7 +243,7 @@ describe('DatePicker.vue', () => { | @@ -243,7 +243,7 @@ describe('DatePicker.vue', () => { | ||
| 243 | `); | 243 | `); |
| 244 | 244 | ||
| 245 | const picker = vm.$children[0]; | 245 | const picker = vm.$children[0]; |
| 246 | - picker.handleIconClick(); | 246 | + picker.handleFocus({type: 'focus'}); |
| 247 | vm.$nextTick(() => { | 247 | vm.$nextTick(() => { |
| 248 | const displayField = vm.$el.querySelector('.ivu-input'); | 248 | const displayField = vm.$el.querySelector('.ivu-input'); |
| 249 | const clickableCells = vm.$el.querySelectorAll('.ivu-date-picker-cells-cell'); | 249 | const clickableCells = vm.$el.querySelectorAll('.ivu-date-picker-cells-cell'); |
| @@ -266,9 +266,11 @@ describe('DatePicker.vue', () => { | @@ -266,9 +266,11 @@ describe('DatePicker.vue', () => { | ||
| 266 | // it should be closed by now | 266 | // it should be closed by now |
| 267 | expect(picker.visible).to.equal(false); | 267 | expect(picker.visible).to.equal(false); |
| 268 | // open picker again | 268 | // open picker again |
| 269 | - picker.handleIconClick(); | 269 | + picker.handleFocus({type: 'focus'}); |
| 270 | + picker.visible = true; | ||
| 270 | 271 | ||
| 271 | - vm.$nextTick(() => { | 272 | + |
| 273 | + vm.$nextTick(() => { | ||
| 272 | expect(picker.visible).to.equal(true); | 274 | expect(picker.visible).to.equal(true); |
| 273 | expect(JSON.stringify(picker.internalValue)).to.equal('[null,null]'); | 275 | expect(JSON.stringify(picker.internalValue)).to.equal('[null,null]'); |
| 274 | expect(displayField.value).to.equal(''); | 276 | expect(displayField.value).to.equal(''); |
| @@ -355,7 +357,7 @@ describe('DatePicker.vue', () => { | @@ -355,7 +357,7 @@ describe('DatePicker.vue', () => { | ||
| 355 | `); | 357 | `); |
| 356 | 358 | ||
| 357 | const picker = vm.$children[0]; | 359 | const picker = vm.$children[0]; |
| 358 | - picker.handleIconClick(); | 360 | + picker.handleFocus({type: 'focus'}); |
| 359 | vm.$nextTick(() => { | 361 | vm.$nextTick(() => { |
| 360 | const now = new Date(); | 362 | const now = new Date(); |
| 361 | const labels = vm.$el.querySelectorAll('.ivu-picker-panel-body .ivu-date-picker-header-label'); | 363 | const labels = vm.$el.querySelectorAll('.ivu-picker-panel-body .ivu-date-picker-header-label'); |
test/unit/specs/time-spinner.spec.js
| @@ -11,7 +11,7 @@ describe('TimePicker.vue', () => { | @@ -11,7 +11,7 @@ describe('TimePicker.vue', () => { | ||
| 11 | <Time-Picker></Time-Picker> | 11 | <Time-Picker></Time-Picker> |
| 12 | `); | 12 | `); |
| 13 | const picker = vm.$children[0]; | 13 | const picker = vm.$children[0]; |
| 14 | - picker.handleIconClick(); // open the picker panels | 14 | + picker.handleFocus({type: 'focus'}); // open the picker panels |
| 15 | 15 | ||
| 16 | vm.$nextTick(() => { | 16 | vm.$nextTick(() => { |
| 17 | const spiners = picker.$el.querySelectorAll('.ivu-time-picker-cells-list'); | 17 | const spiners = picker.$el.querySelectorAll('.ivu-time-picker-cells-list'); |
| @@ -28,7 +28,7 @@ describe('TimePicker.vue', () => { | @@ -28,7 +28,7 @@ describe('TimePicker.vue', () => { | ||
| 28 | <Time-Picker format="HH:mm"></Time-Picker> | 28 | <Time-Picker format="HH:mm"></Time-Picker> |
| 29 | `); | 29 | `); |
| 30 | const picker = vm.$children[0]; | 30 | const picker = vm.$children[0]; |
| 31 | - picker.handleIconClick(); // open the picker panels | 31 | + picker.handleFocus({type: 'focus'}); // open the picker panels |
| 32 | 32 | ||
| 33 | vm.$nextTick(() => { | 33 | vm.$nextTick(() => { |
| 34 | const spiners = picker.$el.querySelectorAll('.ivu-time-picker-cells-list'); | 34 | const spiners = picker.$el.querySelectorAll('.ivu-time-picker-cells-list'); |
| @@ -44,7 +44,7 @@ describe('TimePicker.vue', () => { | @@ -44,7 +44,7 @@ describe('TimePicker.vue', () => { | ||
| 44 | <Time-Picker :steps="[1, 15]"></Time-Picker> | 44 | <Time-Picker :steps="[1, 15]"></Time-Picker> |
| 45 | `); | 45 | `); |
| 46 | const picker = vm.$children[0]; | 46 | const picker = vm.$children[0]; |
| 47 | - picker.handleIconClick(); // open the picker panels | 47 | + picker.handleFocus({type: 'focus'}); // open the picker panels |
| 48 | 48 | ||
| 49 | vm.$nextTick(() => { | 49 | vm.$nextTick(() => { |
| 50 | const spiners = picker.$el.querySelectorAll('.ivu-time-picker-cells-list'); | 50 | const spiners = picker.$el.querySelectorAll('.ivu-time-picker-cells-list'); |