Blame view

src/components/date-picker/picker.vue 28 KB
17e1fcf1   梁灏   init DatePicker
1
  <template>
75cb2998   Sergio Crisostomo   Add keyboard navi...
2
3
4
5
6
      <div
          :class="wrapperClasses"
          v-click-outside:mousedown.capture="handleClose"
          v-click-outside.capture="handleClose"
      >
531cd165   梁灏   support DatePicke...
7
          <div ref="reference" :class="[prefixCls + '-rel']">
e9dd4dab   梁灏   publish 0.9.11-rc-1
8
9
              <slot>
                  <i-input
4863a75d   Sergio Crisostomo   Correct logic whe...
10
                      :key="forceInputRerender"
acb79ba3   梁灏   fixed #433
11
                      :element-id="elementId"
e9dd4dab   梁灏   publish 0.9.11-rc-1
12
13
14
15
16
17
                      :class="[prefixCls + '-editor']"
                      :readonly="!editable || readonly"
                      :disabled="disabled"
                      :size="size"
                      :placeholder="placeholder"
                      :value="visualValue"
0460a1e8   梁灏   fixed #812
18
                      :name="name"
75cb2998   Sergio Crisostomo   Add keyboard navi...
19
20
                      ref="input"
  
531cd165   梁灏   support DatePicke...
21
                      @on-input-change="handleInputChange"
e9dd4dab   梁灏   publish 0.9.11-rc-1
22
                      @on-focus="handleFocus"
030a522d   Sergio Crisostomo   make picker close...
23
                      @on-blur="handleBlur"
e9dd4dab   梁灏   publish 0.9.11-rc-1
24
                      @on-click="handleIconClick"
75cb2998   Sergio Crisostomo   Add keyboard navi...
25
26
                      @click.native="handleFocus"
                      @keydown.native="handleKeydown"
531cd165   梁灏   support DatePicke...
27
28
                      @mouseenter.native="handleInputMouseenter"
                      @mouseleave.native="handleInputMouseleave"
e55ba7a2   Sergio Crisostomo   Add week numbers
29
30
31
  
                      :icon="iconType"
                  ></i-input>
e9dd4dab   梁灏   publish 0.9.11-rc-1
32
33
              </slot>
          </div>
e09b07b7   huanghong   解决drop弹出动画异常
34
          <transition name="transition-drop">
ecaf8d51   梁灏   Date add transfer...
35
36
37
38
39
40
41
42
              <Drop
                  @click.native="handleTransferClick"
                  v-show="opened"
                  :class="{ [prefixCls + '-transfer']: transfer }"
                  :placement="placement"
                  ref="drop"
                  :data-transfer="transfer"
                  v-transfer-dom>
95eae081   Sergio Crisostomo   refactor Datepicker
43
44
45
                  <div>
                      <component
                          :is="panel"
46726afd   Sergio Crisostomo   Fix panels reset ...
46
                          ref="pickerPanel"
95eae081   Sergio Crisostomo   refactor Datepicker
47
48
49
50
51
52
53
                          :visible="visible"
                          :showTime="type === 'datetime' || type === 'datetimerange'"
                          :confirm="isConfirm"
                          :selectionMode="selectionMode"
                          :steps="steps"
                          :format="format"
                          :value="internalValue"
63bd0f7d   Sergio Crisostomo   Add start-date pr...
54
                          :start-date="startDate"
435bf781   Sergio Crisostomo   add split panel p...
55
                          :split-panels="splitPanels"
e55ba7a2   Sergio Crisostomo   Add week numbers
56
                          :show-week-numbers="showWeekNumbers"
b52e02e4   Sergio Crisostomo   Fix month|year pr...
57
                          :picker-type="type"
02859de9   Sergio Crisostomo   Use the last pic...
58
                          :multiple="multiple"
75cb2998   Sergio Crisostomo   Add keyboard navi...
59
                          :focused-date="focusedDate"
95eae081   Sergio Crisostomo   refactor Datepicker
60
  
79ac2457   Sergio Crisostomo   Allow DatePicker ...
61
62
                          :time-picker-options="timePickerOptions"
  
95eae081   Sergio Crisostomo   refactor Datepicker
63
64
65
66
67
68
69
70
71
                          v-bind="ownPickerProps"
  
                          @on-pick="onPick"
                          @on-pick-clear="handleClear"
                          @on-pick-success="onPickSuccess"
                          @on-pick-click="disableClickOutSide = true"
                          @on-selection-mode-change="onSelectionModeChange"
                      ></component>
                  </div>
531cd165   梁灏   support DatePicke...
72
73
              </Drop>
          </transition>
0f677893   梁灏   update DatePicker
74
      </div>
17e1fcf1   梁灏   init DatePicker
75
76
  </template>
  <script>
95eae081   Sergio Crisostomo   refactor Datepicker
77
78
  
  
0f677893   梁灏   update DatePicker
79
80
      import iInput from '../../components/input/input.vue';
      import Drop from '../../components/select/dropdown.vue';
75cb2998   Sergio Crisostomo   Add keyboard navi...
81
      import vClickOutside from 'v-click-outside-x/index';
ecaf8d51   梁灏   Date add transfer...
82
      import TransferDom from '../../directives/transfer-dom';
0f677893   梁灏   update DatePicker
83
      import { oneOf } from '../../utils/assist';
75cb2998   Sergio Crisostomo   Add keyboard navi...
84
85
      import { DEFAULT_FORMATS, RANGE_SEPARATOR, TYPE_VALUE_RESOLVER_MAP, getDayCountOfMonth } from './util';
      import {findComponentsDownward} from '../../utils/assist';
cd78c9c4   梁灏   some comps suppor...
86
      import Emitter from '../../mixins/emitter';
0f677893   梁灏   update DatePicker
87
88
  
      const prefixCls = 'ivu-date-picker';
75cb2998   Sergio Crisostomo   Add keyboard navi...
89
      const pickerPrefixCls = 'ivu-picker';
0f677893   梁灏   update DatePicker
90
  
34867ff9   Sergio Crisostomo   normalise empty i...
91
      const isEmptyArray = val => val.reduce((isEmpty, str) => isEmpty && !str || (typeof str === 'string' && str.trim() === ''), true);
75cb2998   Sergio Crisostomo   Add keyboard navi...
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
      const keyValueMapper = {
          40: 'up',
          39: 'right',
          38: 'down',
          37: 'left',
      };
  
      const mapPossibleValues = (key, horizontal, vertical) => {
          if (key === 'left') return horizontal * -1;
          if (key === 'right') return horizontal * 1;
          if (key === 'up') return vertical * 1;
          if (key === 'down') return vertical * -1;
      };
  
      const pulseElement = (el) => {
          const pulseClass = 'ivu-date-picker-btn-pulse';
          el.classList.add(pulseClass);
          setTimeout(() => el.classList.remove(pulseClass), 200);
      };
  
      const extractTime = date => {
          if (!date) return [0, 0, 0];
          return [
              date.getHours(), date.getMinutes(), date.getSeconds()
          ];
      };
  
34867ff9   Sergio Crisostomo   normalise empty i...
119
  
17e1fcf1   梁灏   init DatePicker
120
      export default {
cd78c9c4   梁灏   some comps suppor...
121
          mixins: [ Emitter ],
0f677893   梁灏   update DatePicker
122
          components: { iInput, Drop },
75cb2998   Sergio Crisostomo   Add keyboard navi...
123
          directives: { clickOutside: vClickOutside.directive, TransferDom },
0f677893   梁灏   update DatePicker
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
          props: {
              format: {
                  type: String
              },
              readonly: {
                  type: Boolean,
                  default: false
              },
              disabled: {
                  type: Boolean,
                  default: false
              },
              editable: {
                  type: Boolean,
                  default: true
              },
fe44201b   梁灏   DatePicker add cl...
140
141
142
143
              clearable: {
                  type: Boolean,
                  default: true
              },
b9041a0d   梁灏   DatePicker add co...
144
145
146
147
              confirm: {
                  type: Boolean,
                  default: false
              },
e9dd4dab   梁灏   publish 0.9.11-rc-1
148
149
150
151
              open: {
                  type: Boolean,
                  default: null
              },
95eae081   Sergio Crisostomo   refactor Datepicker
152
153
154
155
              multiple: {
                  type: Boolean,
                  default: false
              },
79ac2457   Sergio Crisostomo   Allow DatePicker ...
156
157
158
159
              timePickerOptions: {
                  default: () => ({}),
                  type: Object,
              },
435bf781   Sergio Crisostomo   add split panel p...
160
161
162
163
              splitPanels: {
                  type: Boolean,
                  default: false
              },
e55ba7a2   Sergio Crisostomo   Add week numbers
164
165
166
167
              showWeekNumbers: {
                  type: Boolean,
                  default: false
              },
63bd0f7d   Sergio Crisostomo   Add start-date pr...
168
169
170
              startDate: {
                  type: Date
              },
0f677893   梁灏   update DatePicker
171
172
              size: {
                  validator (value) {
f00a037c   梁灏   some Components's...
173
                      return oneOf(value, ['small', 'large', 'default']);
0f677893   梁灏   update DatePicker
174
175
176
177
178
179
                  }
              },
              placeholder: {
                  type: String,
                  default: ''
              },
68e9b100   梁灏   update DatePicker
180
              placement: {
0f677893   梁灏   update DatePicker
181
                  validator (value) {
68e9b100   梁灏   update DatePicker
182
                      return oneOf(value, ['top', 'top-start', 'top-end', 'bottom', 'bottom-start', 'bottom-end', 'left', 'left-start', 'left-end', 'right', 'right-start', 'right-end']);
0f677893   梁灏   update DatePicker
183
                  },
68e9b100   梁灏   update DatePicker
184
                  default: 'bottom-start'
0f677893   梁灏   update DatePicker
185
              },
ecaf8d51   梁灏   Date add transfer...
186
187
188
              transfer: {
                  type: Boolean,
                  default: false
0460a1e8   梁灏   fixed #812
189
190
191
              },
              name: {
                  type: String
acb79ba3   梁灏   fixed #433
192
193
194
              },
              elementId: {
                  type: String
95eae081   Sergio Crisostomo   refactor Datepicker
195
196
197
198
199
200
              },
              steps: {
                  type: Array,
                  default: () => []
              },
              value: {
8878e4a3   Sergio Crisostomo   Remove validator ...
201
                  type: [Date, String, Array]
4863a75d   Sergio Crisostomo   Correct logic whe...
202
203
204
205
              },
              options: {
                  type: Object,
                  default: () => ({})
0f677893   梁灏   update DatePicker
206
207
              }
          },
95eae081   Sergio Crisostomo   refactor Datepicker
208
          data(){
34867ff9   Sergio Crisostomo   normalise empty i...
209
210
              const isRange = this.type.includes('range');
              const emptyArray = isRange ? [null, null] : [null];
4a1734b7   Sergio Crisostomo   fix time and time...
211
              const initialValue = isEmptyArray((isRange ? this.value : [this.value]) || []) ? emptyArray : this.parseDate(this.value);
75cb2998   Sergio Crisostomo   Add keyboard navi...
212
              const focusedTime = initialValue.map(extractTime);
4a1734b7   Sergio Crisostomo   fix time and time...
213
  
0f677893   梁灏   update DatePicker
214
215
216
              return {
                  prefixCls: prefixCls,
                  showClose: false,
0f677893   梁灏   update DatePicker
217
                  visible: false,
34867ff9   Sergio Crisostomo   normalise empty i...
218
                  internalValue: initialValue,
531cd165   梁灏   support DatePicke...
219
                  disableClickOutSide: false,    // fixed when click a date,trigger clickoutside to close picker
95eae081   Sergio Crisostomo   refactor Datepicker
220
                  disableCloseUnderTransfer: false,  // transfer 模式下,点击Drop也会触发关闭,
4863a75d   Sergio Crisostomo   Correct logic whe...
221
                  selectionMode: this.onSelectionModeChange(this.type),
75cb2998   Sergio Crisostomo   Add keyboard navi...
222
223
224
225
226
227
228
229
230
231
                  forceInputRerender: 1,
                  isFocused: false,
                  focusedDate: initialValue[0] || new Date(),
                  focusedTime: {
                      column: 0, // which column inside the picker
                      picker: 0, // which picker
                      time: focusedTime, // the values array into [hh, mm, ss],
                      active: false
                  },
                  internalFocus: false,
b0893113   jingsam   :art: add eslint
232
              };
0f677893   梁灏   update DatePicker
233
234
          },
          computed: {
75cb2998   Sergio Crisostomo   Add keyboard navi...
235
236
237
238
239
              wrapperClasses(){
                  return [prefixCls, {
                      [prefixCls + '-focused']: this.isFocused
                  }];
              },
b42322fe   Sergio Crisostomo   Pass Strings to @...
240
              publicVModelValue(){
d07b4f33   Sergio Crisostomo   fix logic for mul...
241
                  if (this.multiple){
8f6aeda4   Sergio Crisostomo   Fix parser and fo...
242
                      return this.internalValue.slice();
d07b4f33   Sergio Crisostomo   fix logic for mul...
243
244
                  } else {
                      const isRange = this.type.includes('range');
57f0582b   Sergio Crisostomo   Pass correct argu...
245
246
247
                      let val = this.internalValue.map(date => date instanceof Date ? new Date(date) : (date || ''));
  
                      if (this.type.match(/^time/)) val = val.map(this.formatDate);
8f6aeda4   Sergio Crisostomo   Fix parser and fo...
248
                      return (isRange || this.multiple) ? val : val[0];
d07b4f33   Sergio Crisostomo   fix logic for mul...
249
                  }
95eae081   Sergio Crisostomo   refactor Datepicker
250
              },
b42322fe   Sergio Crisostomo   Pass Strings to @...
251
252
253
              publicStringValue(){
                  const {formatDate, publicVModelValue, type} = this;
                  if (type.match(/^time/)) return publicVModelValue;
2332ac9b   Sergio Crisostomo   Fix error in form...
254
                  if (this.multiple) return formatDate(publicVModelValue);
b42322fe   Sergio Crisostomo   Pass Strings to @...
255
256
                  return Array.isArray(publicVModelValue) ? publicVModelValue.map(formatDate) : formatDate(publicVModelValue);
              },
e9dd4dab   梁灏   publish 0.9.11-rc-1
257
258
259
              opened () {
                  return this.open === null ? this.visible : this.open;
              },
0f677893   梁灏   update DatePicker
260
              iconType () {
9d844d53   梁灏   fixed Layout bug
261
                  let icon = 'ios-calendar-outline';
456877a1   梁灏   update TimePicker
262
                  if (this.type === 'time' || this.type === 'timerange') icon = 'ios-clock-outline';
9d844d53   梁灏   fixed Layout bug
263
264
                  if (this.showClose) icon = 'ios-close';
                  return icon;
0f677893   梁灏   update DatePicker
265
              },
d20fe0ee   梁灏   update DatePicker
266
              transition () {
95eae081   Sergio Crisostomo   refactor Datepicker
267
268
                  const bottomPlaced = this.placement.match(/^bottom/);
                  return bottomPlaced ? 'slide-up' : 'slide-down';
d20fe0ee   梁灏   update DatePicker
269
              },
95eae081   Sergio Crisostomo   refactor Datepicker
270
              visualValue() {
2fb29fae   Sergio Crisostomo   export utilities ...
271
                  return this.formatDate(this.internalValue);
95eae081   Sergio Crisostomo   refactor Datepicker
272
273
              },
              isConfirm(){
d07b4f33   Sergio Crisostomo   fix logic for mul...
274
                  return this.confirm || this.type === 'datetime' || this.type === 'datetimerange' || this.multiple;
0f677893   梁灏   update DatePicker
275
276
277
              }
          },
          methods: {
95eae081   Sergio Crisostomo   refactor Datepicker
278
              onSelectionModeChange(type){
95eae081   Sergio Crisostomo   refactor Datepicker
279
                  if (type.match(/^date/)) type = 'date';
46726afd   Sergio Crisostomo   Fix panels reset ...
280
281
                  this.selectionMode = oneOf(type, ['year', 'month', 'date', 'time']) && type;
                  return this.selectionMode;
95eae081   Sergio Crisostomo   refactor Datepicker
282
              },
ecaf8d51   梁灏   Date add transfer...
283
284
285
286
              // 开启 transfer 时,点击 Drop 即会关闭,这里不让其关闭
              handleTransferClick () {
                  if (this.transfer) this.disableCloseUnderTransfer = true;
              },
75cb2998   Sergio Crisostomo   Add keyboard navi...
287
              handleClose (e) {
ecaf8d51   梁灏   Date add transfer...
288
289
290
291
                  if (this.disableCloseUnderTransfer) {
                      this.disableCloseUnderTransfer = false;
                      return false;
                  }
95eae081   Sergio Crisostomo   refactor Datepicker
292
  
75cb2998   Sergio Crisostomo   Add keyboard navi...
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
                  if (e && e.type === 'mousedown' && this.visible) {
                      e.preventDefault();
                      e.stopPropagation();
                      return;
                  }
  
                  if (this.visible) {
                      const pickerPanel = this.$refs.pickerPanel && this.$refs.pickerPanel.$el;
                      if (e && pickerPanel && pickerPanel.contains(e.target)) return; // its a click inside own component, lets ignore it.
  
                      this.visible = false;
                      e && e.preventDefault();
                      e && e.stopPropagation();
                      return;
                  }
  
                  this.isFocused = false;
68e9b100   梁灏   update DatePicker
310
                  this.disableClickOutSide = false;
0f677893   梁灏   update DatePicker
311
              },
75cb2998   Sergio Crisostomo   Add keyboard navi...
312
              handleFocus (e) {
e1874103   梁灏   update DatePicker
313
                  if (this.readonly) return;
75cb2998   Sergio Crisostomo   Add keyboard navi...
314
315
                  this.isFocused = true;
                  if (e && e.type === 'focus') return; // just focus, don't open yet
0f677893   梁灏   update DatePicker
316
                  this.visible = true;
0f677893   梁灏   update DatePicker
317
              },
75cb2998   Sergio Crisostomo   Add keyboard navi...
318
319
320
321
322
323
324
325
326
327
328
              handleBlur (e) {
                  if (this.internalFocus){
                      this.internalFocus = false;
                      return;
                  }
                  if (this.visible) {
                      e.preventDefault();
                      return;
                  }
  
                  this.isFocused = false;
46726afd   Sergio Crisostomo   Fix panels reset ...
329
                  this.onSelectionModeChange(this.type);
c2b7fed0   Sergio Crisostomo   Reset panel selec...
330
                  this.internalValue = this.internalValue.slice(); // trigger panel watchers to reset views
46726afd   Sergio Crisostomo   Fix panels reset ...
331
                  this.reset();
6017ed75   Sergio Crisostomo   update scroll whe...
332
333
                  this.$refs.pickerPanel.onToggleVisibility(false);
  
46726afd   Sergio Crisostomo   Fix panels reset ...
334
              },
75cb2998   Sergio Crisostomo   Add keyboard navi...
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
              handleKeydown(e){
                  const keyCode = e.keyCode;
  
                  // handle "tab" key
                  if (keyCode === 9){
                      if (this.visible){
                          e.stopPropagation();
                          e.preventDefault();
  
                          if (this.isConfirm){
                              const selector = `.${pickerPrefixCls}-confirm > *`;
                              const tabbable = this.$refs.drop.$el.querySelectorAll(selector);
                              this.internalFocus = true;
                              const element = [...tabbable][e.shiftKey ? 'pop' : 'shift']();
                              element.focus();
                          } else {
                              this.handleClose();
                          }
                      } else {
                          this.focused = false;
                      }
                  }
  
                  // open the panel
                  const arrows = [37, 38, 39, 40];
                  if (!this.visible && arrows.includes(keyCode)){
                      this.visible = true;
                      return;
                  }
  
                  // close on "esc" key
                  if (keyCode === 27){
                      if (this.visible) {
                          e.stopPropagation();
                          this.handleClose();
                      }
                  }
  
                  // select date, "Enter" key
                  if (keyCode === 13){
                      const timePickers = findComponentsDownward(this, 'TimeSpinner');
                      if (timePickers.length > 0){
                          const columnsPerPicker = timePickers[0].showSeconds ? 3 : 2;
                          const pickerIndex = Math.floor(this.focusedTime.column / columnsPerPicker);
                          const value = this.focusedTime.time[pickerIndex];
  
                          timePickers[pickerIndex].chooseValue(value);
                          return;
                      }
  
                      if (this.type.match(/range/)){
                          this.$refs.pickerPanel.handleRangePick(this.focusedDate, 'date');
                      } else {
                          this.onPick(this.focusedDate, false, 'date');
                      }
                  }
  
                  if (!arrows.includes(keyCode)) return; // ignore rest of keys
  
                  // navigate times and dates
                  if (this.focusedTime.active) e.preventDefault(); // to prevent cursor from moving
                  this.navigateDatePanel(keyValueMapper[keyCode], e.shiftKey);
              },
46726afd   Sergio Crisostomo   Fix panels reset ...
398
399
              reset(){
                  this.$refs.pickerPanel.reset && this.$refs.pickerPanel.reset();
030a522d   Sergio Crisostomo   make picker close...
400
              },
75cb2998   Sergio Crisostomo   Add keyboard navi...
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
              navigateTimePanel(direction){
  
                  this.focusedTime.active = true;
                  const horizontal = direction.match(/left|right/);
                  const vertical = direction.match(/up|down/);
                  const timePickers = findComponentsDownward(this, 'TimeSpinner');
  
                  const maxNrOfColumns = (timePickers[0].showSeconds ? 3 : 2) * timePickers.length;
                  const column = (currentColumn => {
                      const incremented = currentColumn + (horizontal ? (direction === 'left' ? -1 : 1) : 0);
                      return (incremented + maxNrOfColumns) % maxNrOfColumns;
                  })(this.focusedTime.column);
  
                  const columnsPerPicker = maxNrOfColumns / timePickers.length;
                  const pickerIndex = Math.floor(column / columnsPerPicker);
                  const col = column % columnsPerPicker;
  
  
                  if (horizontal){
                      const time = this.internalValue.map(extractTime);
  
                      this.focusedTime = {
                          ...this.focusedTime,
                          column: column,
                          time: time
                      };
                      timePickers.forEach((instance, i) => {
                          if (i === pickerIndex) instance.updateFocusedTime(col, time[pickerIndex]);
                          else instance.updateFocusedTime(-1, instance.focusedTime);
                      });
                  }
  
                  if (vertical){
                      const increment = direction === 'up' ? 1 : -1;
                      const timeParts = ['hours', 'minutes', 'seconds'];
  
  
                      const pickerPossibleValues = timePickers[pickerIndex][`${timeParts[col]}List`];
                      const nextIndex = pickerPossibleValues.findIndex(({text}) => this.focusedTime.time[pickerIndex][col] === text) + increment;
                      const nextValue = pickerPossibleValues[nextIndex % pickerPossibleValues.length].text;
                      const times = this.focusedTime.time.map((time, i) => {
                          if (i !== pickerIndex) return time;
                          time[col] = nextValue;
                          return time;
                      });
                      this.focusedTime = {
                          ...this.focusedTime,
                          time: times
                      };
  
                      timePickers.forEach((instance, i) => {
                          if (i === pickerIndex) instance.updateFocusedTime(col, times[i]);
                          else instance.updateFocusedTime(-1, instance.focusedTime);
                      });
                  }
              },
              navigateDatePanel(direction, shift){
  
                  const timePickers = findComponentsDownward(this, 'TimeSpinner');
                  if (timePickers.length > 0) {
                      // we are in TimePicker mode
                      this.navigateTimePanel(direction, shift, timePickers);
                      return;
                  }
  
                  if (shift){
                      if (this.type === 'year'){
                          this.focusedDate = new Date(
                              this.focusedDate.getFullYear() + mapPossibleValues(direction, 0, 10),
                              this.focusedDate.getMonth(),
                              this.focusedDate.getDate()
                          );
                      } else {
                          this.focusedDate = new Date(
                              this.focusedDate.getFullYear() + mapPossibleValues(direction, 0, 1),
                              this.focusedDate.getMonth() + mapPossibleValues(direction, 1, 0),
                              this.focusedDate.getDate()
                          );
                      }
  
                      const position = direction.match(/left|down/) ? 'prev' : 'next';
                      const double = direction.match(/up|down/) ? '-double' : '';
  
                      // pulse button
                      const button = this.$refs.drop.$el.querySelector(`.ivu-date-picker-${position}-btn-arrow${double}`);
                      if (button) pulseElement(button);
                      return;
                  }
  
                  const initialDate = this.focusedDate || (this.internalValue && this.internalValue[0]) || new Date();
                  const focusedDate = new Date(initialDate);
  
                  if (this.type.match(/^date/)){
                      const lastOfMonth = getDayCountOfMonth(initialDate.getFullYear(), initialDate.getMonth());
                      const startDay = initialDate.getDate();
                      const nextDay = focusedDate.getDate() +  mapPossibleValues(direction, 1, 7);
  
                      if (nextDay < 1) {
                          if (direction.match(/left|right/)) {
                              focusedDate.setMonth(focusedDate.getMonth() + 1);
                              focusedDate.setDate(nextDay);
                          } else {
                              focusedDate.setDate(startDay + Math.floor((lastOfMonth - startDay) / 7) * 7);
                          }
                      } else if (nextDay > lastOfMonth){
                          if (direction.match(/left|right/)) {
                              focusedDate.setMonth(focusedDate.getMonth() - 1);
                              focusedDate.setDate(nextDay);
                          } else {
                              focusedDate.setDate(startDay % 7);
                          }
                      } else {
                          focusedDate.setDate(nextDay);
                      }
                  }
  
                  if (this.type.match(/^month/)) {
                      focusedDate.setMonth(focusedDate.getMonth() + mapPossibleValues(direction, 1, 3));
                  }
  
                  if (this.type.match(/^year/)) {
                      focusedDate.setFullYear(focusedDate.getFullYear() + mapPossibleValues(direction, 1, 3));
                  }
  
                  this.focusedDate = focusedDate;
              },
e1874103   梁灏   update DatePicker
527
              handleInputChange (event) {
4863a75d   Sergio Crisostomo   Correct logic whe...
528
                  const isArrayValue = this.type.includes('range') || this.multiple;
e1874103   梁灏   update DatePicker
529
                  const oldValue = this.visualValue;
95eae081   Sergio Crisostomo   refactor Datepicker
530
                  const newValue = event.target.value;
4863a75d   Sergio Crisostomo   Correct logic whe...
531
532
533
534
535
536
                  const newDate = this.parseDate(newValue);
                  const disabledDateFn =
                      this.options &&
                      typeof this.options.disabledDate === 'function' &&
                      this.options.disabledDate;
                  const valueToTest = isArrayValue ? newDate : newDate[0];
d9ff845f   Sergio Crisostomo   Emit input event ...
537
                  const isDisabled = disabledDateFn && disabledDateFn(valueToTest);
72f225e9   Sergio Crisostomo   Fix date manual i...
538
                  const isValidDate = newDate.reduce((valid, date) => valid && date instanceof Date, true);
e1874103   梁灏   update DatePicker
539
  
72f225e9   Sergio Crisostomo   Fix date manual i...
540
                  if (newValue !== oldValue && !isDisabled && isValidDate) {
90ebd5a7   Sergio Crisostomo   Expose changed da...
541
                      this.emitChange(this.type);
4863a75d   Sergio Crisostomo   Correct logic whe...
542
543
544
                      this.internalValue = newDate;
                  } else {
                      this.forceInputRerender++;
7c5ccdab   梁灏   update DatePicker
545
                  }
c46f385a   梁灏   update DatePicker
546
547
              },
              handleInputMouseenter () {
0f677893   梁灏   update DatePicker
548
                  if (this.readonly || this.disabled) return;
fe44201b   梁灏   DatePicker add cl...
549
                  if (this.visualValue && this.clearable) {
0f677893   梁灏   update DatePicker
550
551
552
                      this.showClose = true;
                  }
              },
c46f385a   梁灏   update DatePicker
553
              handleInputMouseleave () {
0f677893   梁灏   update DatePicker
554
555
556
                  this.showClose = false;
              },
              handleIconClick () {
7b7178f1   梁灏   fixed #528
557
558
                  if (this.showClose) {
                      this.handleClear();
f1f0206c   Aresn   fixed Date bug
559
                  } else if (!this.disabled) {
7b7178f1   梁灏   fixed #528
560
561
                      this.handleFocus();
                  }
b9041a0d   梁灏   DatePicker add co...
562
563
              },
              handleClear () {
c46f385a   梁灏   update DatePicker
564
                  this.visible = false;
95eae081   Sergio Crisostomo   refactor Datepicker
565
                  this.internalValue = this.internalValue.map(() => null);
d20fe0ee   梁灏   update DatePicker
566
                  this.$emit('on-clear');
cd78c9c4   梁灏   some comps suppor...
567
                  this.dispatch('FormItem', 'on-form-change', '');
90ebd5a7   Sergio Crisostomo   Expose changed da...
568
                  this.emitChange(this.type);
46726afd   Sergio Crisostomo   Fix panels reset ...
569
                  this.reset();
9d844d53   梁灏   fixed Layout bug
570
  
95eae081   Sergio Crisostomo   refactor Datepicker
571
572
573
574
                  setTimeout(
                      () => this.onSelectionModeChange(this.type),
                      500 // delay to improve dropdown close visual effect
                  );
344131a7   梁灏   update DatePicker
575
              },
90ebd5a7   Sergio Crisostomo   Expose changed da...
576
              emitChange (type) {
fc0c4c78   梁灏   fixed #494
577
                  this.$nextTick(() => {
90ebd5a7   Sergio Crisostomo   Expose changed da...
578
                      this.$emit('on-change', this.publicStringValue, type);
b42322fe   Sergio Crisostomo   Pass Strings to @...
579
                      this.dispatch('FormItem', 'on-form-change', this.publicStringValue);
fc0c4c78   梁灏   fixed #494
580
                  });
21dad188   梁灏   prevent dispatch ...
581
              },
8f6aeda4   Sergio Crisostomo   Fix parser and fo...
582
              parseDate(val) {
95eae081   Sergio Crisostomo   refactor Datepicker
583
                  const isRange = this.type.includes('range');
456877a1   梁灏   update TimePicker
584
                  const type = this.type;
95eae081   Sergio Crisostomo   refactor Datepicker
585
                  const parser = (
456877a1   梁灏   update TimePicker
586
                      TYPE_VALUE_RESOLVER_MAP[type] ||
699a9dc8   梁灏   update DatePicker
587
                      TYPE_VALUE_RESOLVER_MAP['default']
95eae081   Sergio Crisostomo   refactor Datepicker
588
                  ).parser;
2fb29fae   Sergio Crisostomo   export utilities ...
589
590
                  const format = this.format || DEFAULT_FORMATS[type];
                  const multipleParser = TYPE_VALUE_RESOLVER_MAP['multiple'].parser;
699a9dc8   梁灏   update DatePicker
591
  
95eae081   Sergio Crisostomo   refactor Datepicker
592
                  if (val && type === 'time' && !(val instanceof Date)) {
2fb29fae   Sergio Crisostomo   export utilities ...
593
594
595
                      val = parser(val, format);
                  } else if (this.multiple && val) {
                      val = multipleParser(val, format);
4863a75d   Sergio Crisostomo   Correct logic whe...
596
                  } else if (isRange) {
95eae081   Sergio Crisostomo   refactor Datepicker
597
598
599
                      if (!val){
                          val = [null, null];
                      } else {
283b90aa   Sergio Crisostomo   Fix daterange man...
600
601
                          if (typeof val === 'string') {
                              val = parser(val, format);
4a1734b7   Sergio Crisostomo   fix time and time...
602
                          } else if (type === 'timerange') {
94790b84   Sergio Crisostomo   Fix type passed w...
603
                              val = parser(val, format).map(v => v || '');
283b90aa   Sergio Crisostomo   Fix daterange man...
604
                          } else {
524dc2a5   Sergio Crisostomo   Fix date parsing
605
606
607
608
609
610
611
612
                              const [start, end] = val;
                              if (start instanceof Date && end instanceof Date){
                                  val = val.map(date => new Date(date));
                              } else if (typeof start === 'string' && typeof end === 'string'){
                                  val = parser(val.join(RANGE_SEPARATOR), format);
                              } else if (!start || !end){
                                  val = [null, null];
                              }
283b90aa   Sergio Crisostomo   Fix daterange man...
613
                          }
95eae081   Sergio Crisostomo   refactor Datepicker
614
                      }
8f6aeda4   Sergio Crisostomo   Fix parser and fo...
615
                  } else if (typeof val === 'string' && type.indexOf('time') !== 0){
72f225e9   Sergio Crisostomo   Fix date manual i...
616
                      val = parser(val, format) || null;
95eae081   Sergio Crisostomo   refactor Datepicker
617
                  }
2fb29fae   Sergio Crisostomo   export utilities ...
618
619
  
                  return (isRange || this.multiple) ? (val || []) : [val];
95eae081   Sergio Crisostomo   refactor Datepicker
620
              },
8f6aeda4   Sergio Crisostomo   Fix parser and fo...
621
              formatDate(value){
8f6aeda4   Sergio Crisostomo   Fix parser and fo...
622
                  const format = DEFAULT_FORMATS[this.type];
2fb29fae   Sergio Crisostomo   export utilities ...
623
624
625
626
627
628
629
630
631
632
633
  
                  if (this.multiple) {
                      const formatter = TYPE_VALUE_RESOLVER_MAP.multiple.formatter;
                      return formatter(value, this.format || format);
                  } else {
                      const {formatter} = (
                          TYPE_VALUE_RESOLVER_MAP[this.type] ||
                          TYPE_VALUE_RESOLVER_MAP['default']
                      );
                      return formatter(value, this.format || format);
                  }
8f6aeda4   Sergio Crisostomo   Fix parser and fo...
634
              },
90ebd5a7   Sergio Crisostomo   Expose changed da...
635
              onPick(dates, visible = false, type) {
d07b4f33   Sergio Crisostomo   fix logic for mul...
636
                  if (this.multiple){
88d24200   Sergio Crisostomo   Fix date toggle i...
637
638
                      const pickedTimeStamp = dates.getTime();
                      const indexOfPickedDate = this.internalValue.findIndex(date => date && date.getTime() === pickedTimeStamp);
d07b4f33   Sergio Crisostomo   fix logic for mul...
639
                      const allDates = [...this.internalValue, dates].filter(Boolean);
88d24200   Sergio Crisostomo   Fix date toggle i...
640
                      const timeStamps = allDates.map(date => date.getTime()).filter((ts, i, arr) => arr.indexOf(ts) === i && i !== indexOfPickedDate); // filter away duplicates
d07b4f33   Sergio Crisostomo   fix logic for mul...
641
                      this.internalValue = timeStamps.map(ts => new Date(ts));
95eae081   Sergio Crisostomo   refactor Datepicker
642
643
                  } else {
                      this.internalValue = Array.isArray(dates) ? dates : [dates];
0fd13696   梁灏   fixed DatePicker ...
644
                  }
95eae081   Sergio Crisostomo   refactor Datepicker
645
  
75cb2998   Sergio Crisostomo   Add keyboard navi...
646
647
648
649
650
651
                  this.focusedDate = this.internalValue[0];
                  this.focusedTime = {
                      ...this.focusedTime,
                      time: this.internalValue.map(extractTime)
                  };
  
95eae081   Sergio Crisostomo   refactor Datepicker
652
653
                  if (!this.isConfirm) this.onSelectionModeChange(this.type); // reset the selectionMode
                  if (!this.isConfirm) this.visible = visible;
90ebd5a7   Sergio Crisostomo   Expose changed da...
654
                  this.emitChange(type);
95eae081   Sergio Crisostomo   refactor Datepicker
655
656
657
658
              },
              onPickSuccess(){
                  this.visible = false;
                  this.$emit('on-ok');
75cb2998   Sergio Crisostomo   Add keyboard navi...
659
                  this.focus();
46726afd   Sergio Crisostomo   Fix panels reset ...
660
                  this.reset();
e55ba7a2   Sergio Crisostomo   Add week numbers
661
              },
75cb2998   Sergio Crisostomo   Add keyboard navi...
662
663
664
              focus() {
                  this.$refs.input.focus();
              }
0f677893   梁灏   update DatePicker
665
666
          },
          watch: {
95eae081   Sergio Crisostomo   refactor Datepicker
667
668
              visible (state) {
                  if (state === false){
0f677893   梁灏   update DatePicker
669
                      this.$refs.drop.destroy();
c46f385a   梁灏   update DatePicker
670
                  }
65ce6ced   Sergio Crisostomo   keep `this.$refs....
671
                  this.$refs.drop.update();
95eae081   Sergio Crisostomo   refactor Datepicker
672
                  this.$emit('on-open-change', state);
c46f385a   梁灏   update DatePicker
673
              },
95eae081   Sergio Crisostomo   refactor Datepicker
674
              value(val) {
8f6aeda4   Sergio Crisostomo   Fix parser and fo...
675
                  this.internalValue = this.parseDate(val);
d20fe0ee   梁灏   update DatePicker
676
677
              },
              open (val) {
95eae081   Sergio Crisostomo   refactor Datepicker
678
679
680
681
                  this.visible = val === true;
              },
              type(type){
                  this.onSelectionModeChange(type);
8f6aeda4   Sergio Crisostomo   Fix parser and fo...
682
              },
b42322fe   Sergio Crisostomo   Pass Strings to @...
683
              publicVModelValue(now, before){
8f6aeda4   Sergio Crisostomo   Fix parser and fo...
684
685
                  const newValue = JSON.stringify(now);
                  const oldValue = JSON.stringify(before);
d9ff845f   Sergio Crisostomo   Emit input event ...
686
687
                  const shouldEmitInput = newValue !== oldValue || typeof now !== typeof before;
                  if (shouldEmitInput) this.$emit('input', now); // to update v-model
8f6aeda4   Sergio Crisostomo   Fix parser and fo...
688
              },
e9dd4dab   梁灏   publish 0.9.11-rc-1
689
          },
531cd165   梁灏   support DatePicke...
690
          mounted () {
d9ff845f   Sergio Crisostomo   Emit input event ...
691
              const initialValue = this.value;
b42322fe   Sergio Crisostomo   Pass Strings to @...
692
              const parsedValue = this.publicVModelValue;
d9ff845f   Sergio Crisostomo   Emit input event ...
693
              if (typeof initialValue !== typeof parsedValue || JSON.stringify(initialValue) !== JSON.stringify(parsedValue)){
b42322fe   Sergio Crisostomo   Pass Strings to @...
694
                  this.$emit('input', this.publicVModelValue); // to update v-model
d9ff845f   Sergio Crisostomo   Emit input event ...
695
              }
d20fe0ee   梁灏   update DatePicker
696
              if (this.open !== null) this.visible = this.open;
75cb2998   Sergio Crisostomo   Add keyboard navi...
697
698
699
  
              // to handle focus from confirm buttons
              this.$on('focus-input', () => this.focus());
0f677893   梁灏   update DatePicker
700
          }
b0893113   jingsam   :art: add eslint
701
702
      };
  </script>