Blame view

src/components/date-picker/picker.vue 32.1 KB
17e1fcf1   梁灏   init DatePicker
1
  <template>
75cb2998   Sergio Crisostomo   Add keyboard navi...
2
3
4
      <div
          :class="wrapperClasses"
          v-click-outside:mousedown.capture="handleClose"
69c2de7a   Роман Ковжогин   iOS click-outside...
5
          v-click-outside:touchstart.capture="handleClose"
75cb2998   Sergio Crisostomo   Add keyboard navi...
6
7
          v-click-outside.capture="handleClose"
      >
531cd165   梁灏   support DatePicke...
8
          <div ref="reference" :class="[prefixCls + '-rel']">
e9dd4dab   梁灏   publish 0.9.11-rc-1
9
10
              <slot>
                  <i-input
4863a75d   Sergio Crisostomo   Correct logic whe...
11
                      :key="forceInputRerender"
acb79ba3   梁灏   fixed #433
12
                      :element-id="elementId"
e9dd4dab   梁灏   publish 0.9.11-rc-1
13
14
15
16
17
18
                      :class="[prefixCls + '-editor']"
                      :readonly="!editable || readonly"
                      :disabled="disabled"
                      :size="size"
                      :placeholder="placeholder"
                      :value="visualValue"
0460a1e8   梁灏   fixed #812
19
                      :name="name"
75cb2998   Sergio Crisostomo   Add keyboard navi...
20
21
                      ref="input"
  
531cd165   梁灏   support DatePicke...
22
                      @on-input-change="handleInputChange"
e9dd4dab   梁灏   publish 0.9.11-rc-1
23
                      @on-focus="handleFocus"
030a522d   Sergio Crisostomo   make picker close...
24
                      @on-blur="handleBlur"
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"
154bb822   梁灏   DatePicker&TimePi...
29
30
31
                  >
                      <Icon @click="handleIconClick" :type="arrowType" :custom="customArrowType" :size="arrowSize" slot="suffix" />
                  </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
              <Drop
                  @click.native="handleTransferClick"
                  v-show="opened"
                  :class="{ [prefixCls + '-transfer']: transfer }"
                  :placement="placement"
                  ref="drop"
                  :data-transfer="transfer"
7bafe9d9   梁灏   fixes #4453 #4480...
42
                  :transfer="transfer"
ecaf8d51   梁灏   Date add transfer...
43
                  v-transfer-dom>
95eae081   Sergio Crisostomo   refactor Datepicker
44
45
46
                  <div>
                      <component
                          :is="panel"
46726afd   Sergio Crisostomo   Fix panels reset ...
47
                          ref="pickerPanel"
95eae081   Sergio Crisostomo   refactor Datepicker
48
49
50
51
52
53
54
                          :visible="visible"
                          :showTime="type === 'datetime' || type === 'datetimerange'"
                          :confirm="isConfirm"
                          :selectionMode="selectionMode"
                          :steps="steps"
                          :format="format"
                          :value="internalValue"
63bd0f7d   Sergio Crisostomo   Add start-date pr...
55
                          :start-date="startDate"
435bf781   Sergio Crisostomo   add split panel p...
56
                          :split-panels="splitPanels"
e55ba7a2   Sergio Crisostomo   Add week numbers
57
                          :show-week-numbers="showWeekNumbers"
b52e02e4   Sergio Crisostomo   Fix month|year pr...
58
                          :picker-type="type"
02859de9   Sergio Crisostomo   Use the last pic...
59
                          :multiple="multiple"
75cb2998   Sergio Crisostomo   Add keyboard navi...
60
                          :focused-date="focusedDate"
95eae081   Sergio Crisostomo   refactor Datepicker
61
  
79ac2457   Sergio Crisostomo   Allow DatePicker ...
62
63
                          :time-picker-options="timePickerOptions"
  
95eae081   Sergio Crisostomo   refactor Datepicker
64
65
66
67
68
69
70
71
72
                          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...
73
74
              </Drop>
          </transition>
0f677893   梁灏   update DatePicker
75
      </div>
17e1fcf1   梁灏   init DatePicker
76
77
  </template>
  <script>
95eae081   Sergio Crisostomo   refactor Datepicker
78
79
  
  
0f677893   梁灏   update DatePicker
80
81
      import iInput from '../../components/input/input.vue';
      import Drop from '../../components/select/dropdown.vue';
154bb822   梁灏   DatePicker&TimePi...
82
      import Icon from '../../components/icon/icon.vue';
26369639   Graham Fairweather   Update v-click-ou...
83
      import {directive as clickOutside} from 'v-click-outside-x';
ecaf8d51   梁灏   Date add transfer...
84
      import TransferDom from '../../directives/transfer-dom';
0f677893   梁灏   update DatePicker
85
      import { oneOf } from '../../utils/assist';
4c534a77   梁灏   fixed #5146 , clo...
86
      import { DEFAULT_FORMATS, TYPE_VALUE_RESOLVER_MAP, getDayCountOfMonth } from './util';
75cb2998   Sergio Crisostomo   Add keyboard navi...
87
      import {findComponentsDownward} from '../../utils/assist';
cd78c9c4   梁灏   some comps suppor...
88
      import Emitter from '../../mixins/emitter';
0f677893   梁灏   update DatePicker
89
90
  
      const prefixCls = 'ivu-date-picker';
75cb2998   Sergio Crisostomo   Add keyboard navi...
91
      const pickerPrefixCls = 'ivu-picker';
0f677893   梁灏   update DatePicker
92
  
34867ff9   Sergio Crisostomo   normalise empty i...
93
      const isEmptyArray = val => val.reduce((isEmpty, str) => isEmpty && !str || (typeof str === 'string' && str.trim() === ''), true);
75cb2998   Sergio Crisostomo   Add keyboard navi...
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
119
120
      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...
121
  
17e1fcf1   梁灏   init DatePicker
122
      export default {
cd78c9c4   梁灏   some comps suppor...
123
          mixins: [ Emitter ],
154bb822   梁灏   DatePicker&TimePi...
124
          components: { iInput, Drop, Icon },
26369639   Graham Fairweather   Update v-click-ou...
125
          directives: { clickOutside, TransferDom },
0f677893   梁灏   update DatePicker
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
          props: {
              format: {
                  type: String
              },
              readonly: {
                  type: Boolean,
                  default: false
              },
              disabled: {
                  type: Boolean,
                  default: false
              },
              editable: {
                  type: Boolean,
                  default: true
              },
fe44201b   梁灏   DatePicker add cl...
142
143
144
145
              clearable: {
                  type: Boolean,
                  default: true
              },
b9041a0d   梁灏   DatePicker add co...
146
147
148
149
              confirm: {
                  type: Boolean,
                  default: false
              },
e9dd4dab   梁灏   publish 0.9.11-rc-1
150
151
152
153
              open: {
                  type: Boolean,
                  default: null
              },
95eae081   Sergio Crisostomo   refactor Datepicker
154
155
156
157
              multiple: {
                  type: Boolean,
                  default: false
              },
79ac2457   Sergio Crisostomo   Allow DatePicker ...
158
159
160
161
              timePickerOptions: {
                  default: () => ({}),
                  type: Object,
              },
435bf781   Sergio Crisostomo   add split panel p...
162
163
164
165
              splitPanels: {
                  type: Boolean,
                  default: false
              },
e55ba7a2   Sergio Crisostomo   Add week numbers
166
167
168
169
              showWeekNumbers: {
                  type: Boolean,
                  default: false
              },
63bd0f7d   Sergio Crisostomo   Add start-date pr...
170
171
172
              startDate: {
                  type: Date
              },
0f677893   梁灏   update DatePicker
173
174
              size: {
                  validator (value) {
f00a037c   梁灏   some Components's...
175
                      return oneOf(value, ['small', 'large', 'default']);
20cfa1b6   梁灏   DatePicker suppor...
176
177
                  },
                  default () {
fe5ffd7f   梁灏   fixed #4196 #4165
178
                      return !this.$IVIEW || this.$IVIEW.size === '' ? 'default' : this.$IVIEW.size;
0f677893   梁灏   update DatePicker
179
180
181
182
183
184
                  }
              },
              placeholder: {
                  type: String,
                  default: ''
              },
68e9b100   梁灏   update DatePicker
185
              placement: {
0f677893   梁灏   update DatePicker
186
                  validator (value) {
68e9b100   梁灏   update DatePicker
187
                      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
188
                  },
68e9b100   梁灏   update DatePicker
189
                  default: 'bottom-start'
0f677893   梁灏   update DatePicker
190
              },
ecaf8d51   梁灏   Date add transfer...
191
192
              transfer: {
                  type: Boolean,
20cfa1b6   梁灏   DatePicker suppor...
193
                  default () {
fe5ffd7f   梁灏   fixed #4196 #4165
194
                      return !this.$IVIEW || this.$IVIEW.transfer === '' ? false : this.$IVIEW.transfer;
20cfa1b6   梁灏   DatePicker suppor...
195
                  }
0460a1e8   梁灏   fixed #812
196
197
198
              },
              name: {
                  type: String
acb79ba3   梁灏   fixed #433
199
200
201
              },
              elementId: {
                  type: String
95eae081   Sergio Crisostomo   refactor Datepicker
202
203
204
205
206
207
              },
              steps: {
                  type: Array,
                  default: () => []
              },
              value: {
8878e4a3   Sergio Crisostomo   Remove validator ...
208
                  type: [Date, String, Array]
4863a75d   Sergio Crisostomo   Correct logic whe...
209
210
211
212
              },
              options: {
                  type: Object,
                  default: () => ({})
4c534a77   梁灏   fixed #5146 , clo...
213
214
215
216
              },
              separator: {
                  type: String,
                  default: ' - '
0f677893   梁灏   update DatePicker
217
218
              }
          },
95eae081   Sergio Crisostomo   refactor Datepicker
219
          data(){
34867ff9   Sergio Crisostomo   normalise empty i...
220
221
              const isRange = this.type.includes('range');
              const emptyArray = isRange ? [null, null] : [null];
4a1734b7   Sergio Crisostomo   fix time and time...
222
              const initialValue = isEmptyArray((isRange ? this.value : [this.value]) || []) ? emptyArray : this.parseDate(this.value);
75cb2998   Sergio Crisostomo   Add keyboard navi...
223
              const focusedTime = initialValue.map(extractTime);
4a1734b7   Sergio Crisostomo   fix time and time...
224
  
0f677893   梁灏   update DatePicker
225
226
227
              return {
                  prefixCls: prefixCls,
                  showClose: false,
0f677893   梁灏   update DatePicker
228
                  visible: false,
34867ff9   Sergio Crisostomo   normalise empty i...
229
                  internalValue: initialValue,
531cd165   梁灏   support DatePicke...
230
                  disableClickOutSide: false,    // fixed when click a date,trigger clickoutside to close picker
95eae081   Sergio Crisostomo   refactor Datepicker
231
                  disableCloseUnderTransfer: false,  // transfer 模式下,点击Drop也会触发关闭,
4863a75d   Sergio Crisostomo   Correct logic whe...
232
                  selectionMode: this.onSelectionModeChange(this.type),
75cb2998   Sergio Crisostomo   Add keyboard navi...
233
234
                  forceInputRerender: 1,
                  isFocused: false,
9a7a0e75   Sergio Crisostomo   take into account...
235
                  focusedDate: initialValue[0] || this.startDate || new Date(),
75cb2998   Sergio Crisostomo   Add keyboard navi...
236
237
238
239
240
241
242
                  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
243
              };
0f677893   梁灏   update DatePicker
244
245
          },
          computed: {
75cb2998   Sergio Crisostomo   Add keyboard navi...
246
247
248
249
250
              wrapperClasses(){
                  return [prefixCls, {
                      [prefixCls + '-focused']: this.isFocused
                  }];
              },
b42322fe   Sergio Crisostomo   Pass Strings to @...
251
              publicVModelValue(){
d07b4f33   Sergio Crisostomo   fix logic for mul...
252
                  if (this.multiple){
8f6aeda4   Sergio Crisostomo   Fix parser and fo...
253
                      return this.internalValue.slice();
d07b4f33   Sergio Crisostomo   fix logic for mul...
254
255
                  } else {
                      const isRange = this.type.includes('range');
57f0582b   Sergio Crisostomo   Pass correct argu...
256
257
258
                      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...
259
                      return (isRange || this.multiple) ? val : val[0];
d07b4f33   Sergio Crisostomo   fix logic for mul...
260
                  }
95eae081   Sergio Crisostomo   refactor Datepicker
261
              },
b42322fe   Sergio Crisostomo   Pass Strings to @...
262
263
264
              publicStringValue(){
                  const {formatDate, publicVModelValue, type} = this;
                  if (type.match(/^time/)) return publicVModelValue;
2332ac9b   Sergio Crisostomo   Fix error in form...
265
                  if (this.multiple) return formatDate(publicVModelValue);
b42322fe   Sergio Crisostomo   Pass Strings to @...
266
267
                  return Array.isArray(publicVModelValue) ? publicVModelValue.map(formatDate) : formatDate(publicVModelValue);
              },
e9dd4dab   梁灏   publish 0.9.11-rc-1
268
269
270
              opened () {
                  return this.open === null ? this.visible : this.open;
              },
d20fe0ee   梁灏   update DatePicker
271
              transition () {
95eae081   Sergio Crisostomo   refactor Datepicker
272
273
                  const bottomPlaced = this.placement.match(/^bottom/);
                  return bottomPlaced ? 'slide-up' : 'slide-down';
d20fe0ee   梁灏   update DatePicker
274
              },
95eae081   Sergio Crisostomo   refactor Datepicker
275
              visualValue() {
2fb29fae   Sergio Crisostomo   export utilities ...
276
                  return this.formatDate(this.internalValue);
95eae081   Sergio Crisostomo   refactor Datepicker
277
278
              },
              isConfirm(){
d07b4f33   Sergio Crisostomo   fix logic for mul...
279
                  return this.confirm || this.type === 'datetime' || this.type === 'datetimerange' || this.multiple;
154bb822   梁灏   DatePicker&TimePi...
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
              },
              // 3.4.0, global setting customArrow 有值时,arrow 赋值空
              arrowType () {
                  let type = '';
  
                  if (this.type === 'time' || this.type === 'timerange') {
                      type = 'ios-time-outline';
  
                      if (this.$IVIEW) {
                          if (this.$IVIEW.timePicker.customIcon) {
                              type = '';
                          } else if (this.$IVIEW.timePicker.icon) {
                              type = this.$IVIEW.timePicker.icon;
                          }
                      }
                  } else {
                      type = 'ios-calendar-outline';
  
                      if (this.$IVIEW) {
                          if (this.$IVIEW.datePicker.customIcon) {
                              type = '';
                          } else if (this.$IVIEW.datePicker.icon) {
                              type = this.$IVIEW.datePicker.icon;
                          }
                      }
                  }
  
                  if (this.showClose) type = 'ios-close-circle';
  
                  return type;
              },
              // 3.4.0, global setting
              customArrowType () {
                  let type = '';
  
                  if (!this.showClose) {
                      if (this.type === 'time' || this.type === 'timerange') {
                          if (this.$IVIEW) {
                              if (this.$IVIEW.timePicker.customIcon) {
                                  type = this.$IVIEW.timePicker.customIcon;
                              }
                          }
                      } else {
                          if (this.$IVIEW) {
                              if (this.$IVIEW.datePicker.customIcon) {
                                  type = this.$IVIEW.datePicker.customIcon;
                              }
                          }
                      }
                  }
  
                  return type;
              },
              // 3.4.0, global setting
              arrowSize () {
                  let size = '';
  
                  if (!this.showClose) {
                      if (this.type === 'time' || this.type === 'timerange') {
                          if (this.$IVIEW) {
                              if (this.$IVIEW.timePicker.iconSize) {
                                  size = this.$IVIEW.timePicker.iconSize;
                              }
                          }
                      } else {
                          if (this.$IVIEW) {
                              if (this.$IVIEW.datePicker.iconSize) {
                                  size = this.$IVIEW.datePicker.iconSize;
                              }
                          }
                      }
                  }
  
                  return size;
0f677893   梁灏   update DatePicker
354
355
356
              }
          },
          methods: {
95eae081   Sergio Crisostomo   refactor Datepicker
357
              onSelectionModeChange(type){
95eae081   Sergio Crisostomo   refactor Datepicker
358
                  if (type.match(/^date/)) type = 'date';
46726afd   Sergio Crisostomo   Fix panels reset ...
359
360
                  this.selectionMode = oneOf(type, ['year', 'month', 'date', 'time']) && type;
                  return this.selectionMode;
95eae081   Sergio Crisostomo   refactor Datepicker
361
              },
ecaf8d51   梁灏   Date add transfer...
362
363
364
365
              // 开启 transfer 时,点击 Drop 即会关闭,这里不让其关闭
              handleTransferClick () {
                  if (this.transfer) this.disableCloseUnderTransfer = true;
              },
75cb2998   Sergio Crisostomo   Add keyboard navi...
366
              handleClose (e) {
ecaf8d51   梁灏   Date add transfer...
367
368
369
370
                  if (this.disableCloseUnderTransfer) {
                      this.disableCloseUnderTransfer = false;
                      return false;
                  }
95eae081   Sergio Crisostomo   refactor Datepicker
371
  
75cb2998   Sergio Crisostomo   Add keyboard navi...
372
373
374
375
376
377
378
379
380
381
382
383
384
                  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();
af672a4f   梁灏   DatePicker add @o...
385
                      this.$emit('on-clickoutside', e);
75cb2998   Sergio Crisostomo   Add keyboard navi...
386
387
388
389
                      return;
                  }
  
                  this.isFocused = false;
68e9b100   梁灏   update DatePicker
390
                  this.disableClickOutSide = false;
0f677893   梁灏   update DatePicker
391
              },
75cb2998   Sergio Crisostomo   Add keyboard navi...
392
              handleFocus (e) {
e1874103   梁灏   update DatePicker
393
                  if (this.readonly) return;
75cb2998   Sergio Crisostomo   Add keyboard navi...
394
395
                  this.isFocused = true;
                  if (e && e.type === 'focus') return; // just focus, don't open yet
3881f820   oyv1cent   fix
396
397
398
                  if(!this.disabled){
                      this.visible = true;
                  }
0f677893   梁灏   update DatePicker
399
              },
75cb2998   Sergio Crisostomo   Add keyboard navi...
400
401
402
403
404
405
406
407
408
409
410
              handleBlur (e) {
                  if (this.internalFocus){
                      this.internalFocus = false;
                      return;
                  }
                  if (this.visible) {
                      e.preventDefault();
                      return;
                  }
  
                  this.isFocused = false;
46726afd   Sergio Crisostomo   Fix panels reset ...
411
                  this.onSelectionModeChange(this.type);
c2b7fed0   Sergio Crisostomo   Reset panel selec...
412
                  this.internalValue = this.internalValue.slice(); // trigger panel watchers to reset views
46726afd   Sergio Crisostomo   Fix panels reset ...
413
                  this.reset();
6017ed75   Sergio Crisostomo   update scroll whe...
414
415
                  this.$refs.pickerPanel.onToggleVisibility(false);
  
46726afd   Sergio Crisostomo   Fix panels reset ...
416
              },
75cb2998   Sergio Crisostomo   Add keyboard navi...
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
              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 {
939a162a   Sergio Crisostomo   Prevent selecting...
470
471
472
473
474
475
476
477
478
                          const panels = findComponentsDownward(this, 'PanelTable');
                          const compareDate = (d) => {
                              const sliceIndex = ['year', 'month', 'date'].indexOf((this.type)) + 1;
                              return [d.getFullYear(), d.getMonth(), d.getDate()].slice(0, sliceIndex).join('-');
                          };
                          const dateIsValid = panels.find(({cells}) => {
                              return cells.find(({date, disabled}) => compareDate(date) === compareDate(this.focusedDate) && !disabled);
                          });
                          if (dateIsValid) this.onPick(this.focusedDate, false, 'date');
75cb2998   Sergio Crisostomo   Add keyboard navi...
479
480
481
482
483
484
485
486
487
                      }
                  }
  
                  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 ...
488
489
              reset(){
                  this.$refs.pickerPanel.reset && this.$refs.pickerPanel.reset();
030a522d   Sergio Crisostomo   make picker close...
490
              },
75cb2998   Sergio Crisostomo   Add keyboard navi...
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
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
              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
617
              handleInputChange (event) {
4863a75d   Sergio Crisostomo   Correct logic whe...
618
                  const isArrayValue = this.type.includes('range') || this.multiple;
e1874103   梁灏   update DatePicker
619
                  const oldValue = this.visualValue;
95eae081   Sergio Crisostomo   refactor Datepicker
620
                  const newValue = event.target.value;
4863a75d   Sergio Crisostomo   Correct logic whe...
621
622
623
624
625
626
                  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 ...
627
                  const isDisabled = disabledDateFn && disabledDateFn(valueToTest);
72f225e9   Sergio Crisostomo   Fix date manual i...
628
                  const isValidDate = newDate.reduce((valid, date) => valid && date instanceof Date, true);
e1874103   梁灏   update DatePicker
629
  
72f225e9   Sergio Crisostomo   Fix date manual i...
630
                  if (newValue !== oldValue && !isDisabled && isValidDate) {
90ebd5a7   Sergio Crisostomo   Expose changed da...
631
                      this.emitChange(this.type);
4863a75d   Sergio Crisostomo   Correct logic whe...
632
633
634
                      this.internalValue = newDate;
                  } else {
                      this.forceInputRerender++;
7c5ccdab   梁灏   update DatePicker
635
                  }
c46f385a   梁灏   update DatePicker
636
637
              },
              handleInputMouseenter () {
0f677893   梁灏   update DatePicker
638
                  if (this.readonly || this.disabled) return;
fe44201b   梁灏   DatePicker add cl...
639
                  if (this.visualValue && this.clearable) {
0f677893   梁灏   update DatePicker
640
641
642
                      this.showClose = true;
                  }
              },
c46f385a   梁灏   update DatePicker
643
              handleInputMouseleave () {
0f677893   梁灏   update DatePicker
644
645
                  this.showClose = false;
              },
d9464035   梁灏   fixed unit test
646
              handleIconClick (e) {
7b7178f1   梁灏   fixed #528
647
                  if (this.showClose) {
d9464035   梁灏   fixed unit test
648
                      if (e) e.stopPropagation();
7b7178f1   梁灏   fixed #528
649
                      this.handleClear();
f1f0206c   Aresn   fixed Date bug
650
                  } else if (!this.disabled) {
7b7178f1   梁灏   fixed #528
651
652
                      this.handleFocus();
                  }
b9041a0d   梁灏   DatePicker add co...
653
654
              },
              handleClear () {
c46f385a   梁灏   update DatePicker
655
                  this.visible = false;
95eae081   Sergio Crisostomo   refactor Datepicker
656
                  this.internalValue = this.internalValue.map(() => null);
d20fe0ee   梁灏   update DatePicker
657
                  this.$emit('on-clear');
cd78c9c4   梁灏   some comps suppor...
658
                  this.dispatch('FormItem', 'on-form-change', '');
90ebd5a7   Sergio Crisostomo   Expose changed da...
659
                  this.emitChange(this.type);
46726afd   Sergio Crisostomo   Fix panels reset ...
660
                  this.reset();
9d844d53   梁灏   fixed Layout bug
661
  
95eae081   Sergio Crisostomo   refactor Datepicker
662
663
664
665
                  setTimeout(
                      () => this.onSelectionModeChange(this.type),
                      500 // delay to improve dropdown close visual effect
                  );
344131a7   梁灏   update DatePicker
666
              },
90ebd5a7   Sergio Crisostomo   Expose changed da...
667
              emitChange (type) {
fc0c4c78   梁灏   fixed #494
668
                  this.$nextTick(() => {
90ebd5a7   Sergio Crisostomo   Expose changed da...
669
                      this.$emit('on-change', this.publicStringValue, type);
b42322fe   Sergio Crisostomo   Pass Strings to @...
670
                      this.dispatch('FormItem', 'on-form-change', this.publicStringValue);
fc0c4c78   梁灏   fixed #494
671
                  });
21dad188   梁灏   prevent dispatch ...
672
              },
8f6aeda4   Sergio Crisostomo   Fix parser and fo...
673
              parseDate(val) {
95eae081   Sergio Crisostomo   refactor Datepicker
674
                  const isRange = this.type.includes('range');
456877a1   梁灏   update TimePicker
675
                  const type = this.type;
95eae081   Sergio Crisostomo   refactor Datepicker
676
                  const parser = (
456877a1   梁灏   update TimePicker
677
                      TYPE_VALUE_RESOLVER_MAP[type] ||
699a9dc8   梁灏   update DatePicker
678
                      TYPE_VALUE_RESOLVER_MAP['default']
95eae081   Sergio Crisostomo   refactor Datepicker
679
                  ).parser;
2fb29fae   Sergio Crisostomo   export utilities ...
680
681
                  const format = this.format || DEFAULT_FORMATS[type];
                  const multipleParser = TYPE_VALUE_RESOLVER_MAP['multiple'].parser;
699a9dc8   梁灏   update DatePicker
682
  
95eae081   Sergio Crisostomo   refactor Datepicker
683
                  if (val && type === 'time' && !(val instanceof Date)) {
4c534a77   梁灏   fixed #5146 , clo...
684
                      val = parser(val, format, this.separator);
2fb29fae   Sergio Crisostomo   export utilities ...
685
                  } else if (this.multiple && val) {
4c534a77   梁灏   fixed #5146 , clo...
686
                      val = multipleParser(val, format, this.separator);
4863a75d   Sergio Crisostomo   Correct logic whe...
687
                  } else if (isRange) {
95eae081   Sergio Crisostomo   refactor Datepicker
688
689
690
                      if (!val){
                          val = [null, null];
                      } else {
283b90aa   Sergio Crisostomo   Fix daterange man...
691
                          if (typeof val === 'string') {
4c534a77   梁灏   fixed #5146 , clo...
692
                              val = parser(val, format, this.separator);
4a1734b7   Sergio Crisostomo   fix time and time...
693
                          } else if (type === 'timerange') {
4c534a77   梁灏   fixed #5146 , clo...
694
                              val = parser(val, format, this.separator).map(v => v || '');
283b90aa   Sergio Crisostomo   Fix daterange man...
695
                          } else {
524dc2a5   Sergio Crisostomo   Fix date parsing
696
697
698
699
                              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'){
4c534a77   梁灏   fixed #5146 , clo...
700
                                  val = parser(val.join(this.separator), format, this.separator);
524dc2a5   Sergio Crisostomo   Fix date parsing
701
702
703
                              } else if (!start || !end){
                                  val = [null, null];
                              }
283b90aa   Sergio Crisostomo   Fix daterange man...
704
                          }
95eae081   Sergio Crisostomo   refactor Datepicker
705
                      }
8f6aeda4   Sergio Crisostomo   Fix parser and fo...
706
                  } else if (typeof val === 'string' && type.indexOf('time') !== 0){
72f225e9   Sergio Crisostomo   Fix date manual i...
707
                      val = parser(val, format) || null;
95eae081   Sergio Crisostomo   refactor Datepicker
708
                  }
2fb29fae   Sergio Crisostomo   export utilities ...
709
710
  
                  return (isRange || this.multiple) ? (val || []) : [val];
95eae081   Sergio Crisostomo   refactor Datepicker
711
              },
8f6aeda4   Sergio Crisostomo   Fix parser and fo...
712
              formatDate(value){
8f6aeda4   Sergio Crisostomo   Fix parser and fo...
713
                  const format = DEFAULT_FORMATS[this.type];
2fb29fae   Sergio Crisostomo   export utilities ...
714
715
716
  
                  if (this.multiple) {
                      const formatter = TYPE_VALUE_RESOLVER_MAP.multiple.formatter;
4c534a77   梁灏   fixed #5146 , clo...
717
                      return formatter(value, this.format || format, this.separator);
2fb29fae   Sergio Crisostomo   export utilities ...
718
719
720
721
722
                  } else {
                      const {formatter} = (
                          TYPE_VALUE_RESOLVER_MAP[this.type] ||
                          TYPE_VALUE_RESOLVER_MAP['default']
                      );
4c534a77   梁灏   fixed #5146 , clo...
723
                      return formatter(value, this.format || format, this.separator);
2fb29fae   Sergio Crisostomo   export utilities ...
724
                  }
8f6aeda4   Sergio Crisostomo   Fix parser and fo...
725
              },
90ebd5a7   Sergio Crisostomo   Expose changed da...
726
              onPick(dates, visible = false, type) {
d07b4f33   Sergio Crisostomo   fix logic for mul...
727
                  if (this.multiple){
88d24200   Sergio Crisostomo   Fix date toggle i...
728
729
                      const pickedTimeStamp = dates.getTime();
                      const indexOfPickedDate = this.internalValue.findIndex(date => date && date.getTime() === pickedTimeStamp);
d07b4f33   Sergio Crisostomo   fix logic for mul...
730
                      const allDates = [...this.internalValue, dates].filter(Boolean);
88d24200   Sergio Crisostomo   Fix date toggle i...
731
                      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...
732
                      this.internalValue = timeStamps.map(ts => new Date(ts));
95eae081   Sergio Crisostomo   refactor Datepicker
733
                  } else {
6f816027   SergioCrisostomo   Don't parse multi...
734
                      dates = this.parseDate(dates);
95eae081   Sergio Crisostomo   refactor Datepicker
735
                      this.internalValue = Array.isArray(dates) ? dates : [dates];
0fd13696   梁灏   fixed DatePicker ...
736
                  }
95eae081   Sergio Crisostomo   refactor Datepicker
737
  
75c98064   Sergio Crisostomo   Don't use undefined
738
                  if (this.internalValue[0]) this.focusedDate = this.internalValue[0];
75cb2998   Sergio Crisostomo   Add keyboard navi...
739
740
741
742
743
                  this.focusedTime = {
                      ...this.focusedTime,
                      time: this.internalValue.map(extractTime)
                  };
  
95eae081   Sergio Crisostomo   refactor Datepicker
744
745
                  if (!this.isConfirm) this.onSelectionModeChange(this.type); // reset the selectionMode
                  if (!this.isConfirm) this.visible = visible;
90ebd5a7   Sergio Crisostomo   Expose changed da...
746
                  this.emitChange(type);
95eae081   Sergio Crisostomo   refactor Datepicker
747
748
749
750
              },
              onPickSuccess(){
                  this.visible = false;
                  this.$emit('on-ok');
75cb2998   Sergio Crisostomo   Add keyboard navi...
751
                  this.focus();
46726afd   Sergio Crisostomo   Fix panels reset ...
752
                  this.reset();
e55ba7a2   Sergio Crisostomo   Add week numbers
753
              },
75cb2998   Sergio Crisostomo   Add keyboard navi...
754
              focus() {
9e375e67   Sergio Crisostomo   Focus only if com...
755
                  this.$refs.input && this.$refs.input.focus();
4cc72ddb   梁灏   fix #5046
756
757
758
              },
              updatePopper () {
                  this.$refs.drop.update();
75cb2998   Sergio Crisostomo   Add keyboard navi...
759
              }
0f677893   梁灏   update DatePicker
760
761
          },
          watch: {
95eae081   Sergio Crisostomo   refactor Datepicker
762
763
              visible (state) {
                  if (state === false){
0f677893   梁灏   update DatePicker
764
                      this.$refs.drop.destroy();
c46f385a   梁灏   update DatePicker
765
                  }
65ce6ced   Sergio Crisostomo   keep `this.$refs....
766
                  this.$refs.drop.update();
95eae081   Sergio Crisostomo   refactor Datepicker
767
                  this.$emit('on-open-change', state);
c46f385a   梁灏   update DatePicker
768
              },
95eae081   Sergio Crisostomo   refactor Datepicker
769
              value(val) {
8f6aeda4   Sergio Crisostomo   Fix parser and fo...
770
                  this.internalValue = this.parseDate(val);
d20fe0ee   梁灏   update DatePicker
771
772
              },
              open (val) {
95eae081   Sergio Crisostomo   refactor Datepicker
773
774
775
776
                  this.visible = val === true;
              },
              type(type){
                  this.onSelectionModeChange(type);
8f6aeda4   Sergio Crisostomo   Fix parser and fo...
777
              },
b42322fe   Sergio Crisostomo   Pass Strings to @...
778
              publicVModelValue(now, before){
8f6aeda4   Sergio Crisostomo   Fix parser and fo...
779
780
                  const newValue = JSON.stringify(now);
                  const oldValue = JSON.stringify(before);
d9ff845f   Sergio Crisostomo   Emit input event ...
781
782
                  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...
783
              },
e9dd4dab   梁灏   publish 0.9.11-rc-1
784
          },
531cd165   梁灏   support DatePicke...
785
          mounted () {
d9ff845f   Sergio Crisostomo   Emit input event ...
786
              const initialValue = this.value;
b42322fe   Sergio Crisostomo   Pass Strings to @...
787
              const parsedValue = this.publicVModelValue;
d9ff845f   Sergio Crisostomo   Emit input event ...
788
              if (typeof initialValue !== typeof parsedValue || JSON.stringify(initialValue) !== JSON.stringify(parsedValue)){
b42322fe   Sergio Crisostomo   Pass Strings to @...
789
                  this.$emit('input', this.publicVModelValue); // to update v-model
d9ff845f   Sergio Crisostomo   Emit input event ...
790
              }
d20fe0ee   梁灏   update DatePicker
791
              if (this.open !== null) this.visible = this.open;
75cb2998   Sergio Crisostomo   Add keyboard navi...
792
793
794
  
              // to handle focus from confirm buttons
              this.$on('focus-input', () => this.focus());
4cc72ddb   梁灏   fix #5046
795
              this.$on('update-popper', () => this.updatePopper());
0f677893   梁灏   update DatePicker
796
          }
b0893113   jingsam   :art: add eslint
797
798
      };
  </script>