bi-eleme 2.2.1 → 2.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/lib/alert.js +4 -4
  2. package/lib/aside.js +4 -4
  3. package/lib/autocomplete.js +10 -10
  4. package/lib/avatar.js +4 -4
  5. package/lib/backtop.js +6 -6
  6. package/lib/badge.js +4 -4
  7. package/lib/breadcrumb-item.js +4 -4
  8. package/lib/breadcrumb.js +4 -4
  9. package/lib/button-group.js +4 -4
  10. package/lib/button.js +4 -4
  11. package/lib/calendar.js +15 -15
  12. package/lib/card.js +4 -4
  13. package/lib/carousel-item.js +4 -4
  14. package/lib/carousel.js +6 -6
  15. package/lib/cascader-panel.js +10 -10
  16. package/lib/cascader.js +12 -12
  17. package/lib/checkbox-button.js +4 -4
  18. package/lib/checkbox-group.js +4 -4
  19. package/lib/checkbox.js +4 -4
  20. package/lib/col.js +2 -2
  21. package/lib/collapse-item.js +6 -6
  22. package/lib/collapse.js +4 -4
  23. package/lib/color-picker.js +8 -8
  24. package/lib/container.js +4 -4
  25. package/lib/date-picker.js +65 -64
  26. package/lib/descriptions-item.js +2 -2
  27. package/lib/descriptions.js +2 -2
  28. package/lib/dialog.js +8 -8
  29. package/lib/divider.js +4 -4
  30. package/lib/drawer.js +6 -6
  31. package/lib/dropdown-item.js +4 -4
  32. package/lib/dropdown-menu.js +4 -4
  33. package/lib/dropdown.js +16 -16
  34. package/lib/element-ui.common.js +4772 -311
  35. package/lib/empty.js +4 -4
  36. package/lib/footer.js +4 -4
  37. package/lib/form-item.js +6 -6
  38. package/lib/form.js +11 -11
  39. package/lib/header.js +4 -4
  40. package/lib/icon.js +4 -4
  41. package/lib/image.js +8 -8
  42. package/lib/index.js +1 -1
  43. package/lib/infinite-scroll.js +2 -2
  44. package/lib/input-number.js +6 -6
  45. package/lib/input.js +6 -6
  46. package/lib/link.js +4 -4
  47. package/lib/loading.js +6 -6
  48. package/lib/main.js +4 -4
  49. package/lib/menu-item-group.js +4 -4
  50. package/lib/menu-item.js +6 -6
  51. package/lib/menu.js +6 -6
  52. package/lib/message-box.js +13 -13
  53. package/lib/message.js +8 -8
  54. package/lib/notification.js +8 -8
  55. package/lib/option-group.js +4 -4
  56. package/lib/option.js +4 -4
  57. package/lib/page-header.js +4 -4
  58. package/lib/pagination.js +4 -4
  59. package/lib/popconfirm.js +8 -8
  60. package/lib/popover.js +4 -4
  61. package/lib/progress.js +4 -4
  62. package/lib/radio-button.js +4 -4
  63. package/lib/radio-group.js +4 -4
  64. package/lib/radio.js +4 -4
  65. package/lib/rate.js +6 -6
  66. package/lib/result.js +4 -4
  67. package/lib/row.js +2 -2
  68. package/lib/scrollbar.js +2 -2
  69. package/lib/select.js +16 -16
  70. package/lib/skeleton-item.js +4 -4
  71. package/lib/skeleton.js +4 -4
  72. package/lib/slider.js +8 -8
  73. package/lib/spinner.js +4 -4
  74. package/lib/step.js +4 -4
  75. package/lib/steps.js +6 -6
  76. package/lib/submenu.js +6 -6
  77. package/lib/super-date.js +4832 -0
  78. package/lib/switch.js +6 -6
  79. package/lib/tab-pane.js +4 -4
  80. package/lib/table-column.js +2 -2
  81. package/lib/table.js +20 -18
  82. package/lib/tabs.js +4 -4
  83. package/lib/tag.js +4 -4
  84. package/lib/theme-chalk/index.css +1 -1
  85. package/lib/theme-chalk/super-date.css +1 -0
  86. package/lib/time-picker.js +59 -59
  87. package/lib/time-select.js +16 -16
  88. package/lib/timeline-item.js +4 -4
  89. package/lib/timeline.js +4 -4
  90. package/lib/tooltip.js +2 -2
  91. package/lib/transfer.js +8 -8
  92. package/lib/tree.js +6 -6
  93. package/lib/upload.js +15 -15
  94. package/lib/utils/date-util.js +5 -2
  95. package/lib/utils/date.js +67 -9
  96. package/package.json +1 -1
  97. package/packages/super-date/index.js +8 -0
  98. package/packages/super-date/src/basic/date-table.vue +448 -0
  99. package/packages/super-date/src/basic/month-table.vue +278 -0
  100. package/packages/super-date/src/basic/time-spinner.vue +340 -0
  101. package/packages/super-date/src/basic/year-table.vue +144 -0
  102. package/packages/super-date/src/panel/date-range.vue +1000 -0
  103. package/packages/super-date/src/panel/date.vue +649 -0
  104. package/packages/super-date/src/panel/month-range.vue +289 -0
  105. package/packages/super-date/src/panel/time-range.vue +250 -0
  106. package/packages/super-date/src/panel/time-select.vue +195 -0
  107. package/packages/super-date/src/panel/time.vue +211 -0
  108. package/packages/super-date/src/picker/date-picker.js +29 -0
  109. package/packages/super-date/src/picker/time-picker.js +39 -0
  110. package/packages/super-date/src/picker/time-select.js +21 -0
  111. package/packages/super-date/src/picker.vue +955 -0
  112. package/packages/theme-chalk/lib/index.css +1 -1
  113. package/packages/theme-chalk/lib/super-date.css +1 -0
  114. package/packages/theme-chalk/src/index.scss +1 -0
  115. package/packages/theme-chalk/src/super-date/date-picker.scss +106 -0
  116. package/packages/theme-chalk/src/super-date/date-range-picker.scss +138 -0
  117. package/packages/theme-chalk/src/super-date/date-table.scss +154 -0
  118. package/packages/theme-chalk/src/super-date/month-table.scss +96 -0
  119. package/packages/theme-chalk/src/super-date/picker-panel.scss +130 -0
  120. package/packages/theme-chalk/src/super-date/picker.scss +204 -0
  121. package/packages/theme-chalk/src/super-date/time-picker.scss +94 -0
  122. package/packages/theme-chalk/src/super-date/time-range-picker.scss +32 -0
  123. package/packages/theme-chalk/src/super-date/time-spinner.scss +111 -0
  124. package/packages/theme-chalk/src/super-date/year-table.scss +66 -0
  125. package/packages/theme-chalk/src/super-date.scss +12 -0
  126. package/src/index.js +4 -1
  127. package/src/utils/date-util.js +3 -0
  128. package/src/utils/date.js +291 -195
  129. package/types/element-ui.d.ts +9 -5
  130. package/types/super-date.d.ts +124 -0
@@ -0,0 +1,278 @@
1
+ <template>
2
+ <table
3
+ @click="handleMonthTableClick"
4
+ @mousemove="handleMouseMove"
5
+ class="el-month-table"
6
+ >
7
+ <tbody>
8
+ <tr
9
+ v-for="(row, key) in rows"
10
+ :key="key"
11
+ >
12
+ <td
13
+ :class="getCellStyle(cell)"
14
+ v-for="(cell, key) in row"
15
+ :key="key"
16
+ >
17
+ <div>
18
+ <a class="cell">{{ t('el.datepicker.months.' + months[cell.text]) }}</a>
19
+ </div>
20
+ </td>
21
+ </tr>
22
+ </tbody>
23
+ </table>
24
+ </template>
25
+
26
+ <script type="text/babel">
27
+ import Locale from 'bi-eleme/src/mixins/locale'
28
+ import { isDate, range, getDayCountOfMonth, nextDate, getTimestampInTimezone } from 'bi-eleme/src/utils/date-util'
29
+ import { hasClass } from 'bi-eleme/src/utils/dom'
30
+ import { arrayFindIndex, coerceTruthyValueToArray, arrayFind } from 'bi-eleme/src/utils/util'
31
+
32
+ const datesInMonth = (year, month) => {
33
+ const numOfDays = getDayCountOfMonth(year, month)
34
+ const firstDay = new Date(year, month, 1)
35
+ return range(numOfDays).map((n) => nextDate(firstDay, n))
36
+ }
37
+
38
+ const clearDate = (date) => {
39
+ return new Date(date.getFullYear(), date.getMonth())
40
+ }
41
+
42
+ const getMonthTimestamp = function (time, utc = 8) {
43
+ if (typeof time === 'number' || typeof time === 'string') {
44
+ return clearDate(getTimestampInTimezone(new Date(time), utc)).getTime()
45
+ } else if (time instanceof Date) {
46
+ return clearDate(getTimestampInTimezone(time, utc)).getTime()
47
+ } else {
48
+ return NaN
49
+ }
50
+ }
51
+
52
+ // remove the first element that satisfies `pred` from arr
53
+ // return a new array if modification occurs
54
+ // return the original array otherwise
55
+ const removeFromArray = function (arr, pred) {
56
+ const idx = typeof pred === 'function' ? arrayFindIndex(arr, pred) : arr.indexOf(pred)
57
+ return idx >= 0 ? [...arr.slice(0, idx), ...arr.slice(idx + 1)] : arr
58
+ }
59
+ export default {
60
+ props: {
61
+ disabledDate: {},
62
+ value: {},
63
+ selectionMode: {
64
+ default: 'month'
65
+ },
66
+ minDate: {},
67
+ utc: {
68
+ default: 8,
69
+ type: Number
70
+ },
71
+ maxDate: {},
72
+ defaultValue: {
73
+ validator(val) {
74
+ // null or valid Date Object
75
+ return val === null || isDate(val) || (Array.isArray(val) && val.every(isDate))
76
+ }
77
+ },
78
+ date: {},
79
+ rangeState: {
80
+ default() {
81
+ return {
82
+ endDate: null,
83
+ selecting: false
84
+ }
85
+ }
86
+ }
87
+ },
88
+
89
+ mixins: [Locale],
90
+
91
+ watch: {
92
+ 'rangeState.endDate'(newVal) {
93
+ this.markRange(this.minDate, newVal)
94
+ },
95
+
96
+ minDate(newVal, oldVal) {
97
+ if (getMonthTimestamp(newVal) !== getMonthTimestamp(oldVal)) {
98
+ this.markRange(this.minDate, this.maxDate)
99
+ }
100
+ },
101
+
102
+ maxDate(newVal, oldVal) {
103
+ if (getMonthTimestamp(newVal) !== getMonthTimestamp(oldVal)) {
104
+ this.markRange(this.minDate, this.maxDate)
105
+ }
106
+ }
107
+ },
108
+
109
+ data() {
110
+ return {
111
+ months: ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'],
112
+ tableRows: [[], [], []],
113
+ lastRow: null,
114
+ lastColumn: null
115
+ }
116
+ },
117
+
118
+ methods: {
119
+ cellMatchesDate(cell, date) {
120
+ const value = new Date(date)
121
+ return this.date.getFullYear() === value.getFullYear() && Number(cell.text) === value.getMonth()
122
+ },
123
+ getCellStyle(cell) {
124
+ const style = {}
125
+ const year = this.date.getFullYear()
126
+ const today = new Date()
127
+ const month = cell.text
128
+ const defaultValue = this.defaultValue ? (Array.isArray(this.defaultValue) ? this.defaultValue : [this.defaultValue]) : []
129
+ style.disabled = typeof this.disabledDate === 'function' ? datesInMonth(year, month).every(this.disabledDate) : false
130
+ style.current = arrayFindIndex(coerceTruthyValueToArray(this.value), (date) => date.getFullYear() === year && date.getMonth() === month) >= 0
131
+ style.today = today.getFullYear() === year && today.getMonth() === month
132
+ style.default = defaultValue.some((date) => this.cellMatchesDate(cell, date))
133
+
134
+ if (cell.inRange) {
135
+ style['in-range'] = true
136
+
137
+ if (cell.start) {
138
+ style['start-date'] = true
139
+ }
140
+
141
+ if (cell.end) {
142
+ style['end-date'] = true
143
+ }
144
+ }
145
+ return style
146
+ },
147
+ getMonthOfCell(month) {
148
+ const year = this.date.getFullYear()
149
+ return new Date(year, month, 1)
150
+ },
151
+ markRange(minDate, maxDate) {
152
+ minDate = getMonthTimestamp(minDate)
153
+ maxDate = getMonthTimestamp(maxDate) || minDate
154
+ ;[minDate, maxDate] = [Math.min(minDate, maxDate), Math.max(minDate, maxDate)]
155
+ const rows = this.rows
156
+ for (let i = 0, k = rows.length; i < k; i++) {
157
+ const row = rows[i]
158
+ for (let j = 0, l = row.length; j < l; j++) {
159
+ const cell = row[j]
160
+ const index = i * 4 + j
161
+ const time = new Date(this.date.getFullYear(), index).getTime()
162
+
163
+ cell.inRange = minDate && time >= minDate && time <= maxDate
164
+ cell.start = minDate && time === minDate
165
+ cell.end = maxDate && time === maxDate
166
+ }
167
+ }
168
+ },
169
+ handleMouseMove(event) {
170
+ if (!this.rangeState.selecting) return
171
+
172
+ let target = event.target
173
+ if (target.tagName === 'A') {
174
+ target = target.parentNode.parentNode
175
+ }
176
+ if (target.tagName === 'DIV') {
177
+ target = target.parentNode
178
+ }
179
+ if (target.tagName !== 'TD') return
180
+
181
+ const row = target.parentNode.rowIndex
182
+ const column = target.cellIndex
183
+ // can not select disabled date
184
+ if (this.rows[row][column].disabled) return
185
+
186
+ // only update rangeState when mouse moves to a new cell
187
+ // this avoids frequent Date object creation and improves performance
188
+ if (row !== this.lastRow || column !== this.lastColumn) {
189
+ this.lastRow = row
190
+ this.lastColumn = column
191
+ this.$emit('changerange', {
192
+ minDate: this.minDate,
193
+ maxDate: this.maxDate,
194
+ rangeState: {
195
+ selecting: true,
196
+ endDate: this.getMonthOfCell(row * 4 + column)
197
+ }
198
+ })
199
+ }
200
+ },
201
+ handleMonthTableClick(event) {
202
+ let target = event.target
203
+ if (target.tagName === 'A') {
204
+ target = target.parentNode.parentNode
205
+ }
206
+ if (target.tagName === 'DIV') {
207
+ target = target.parentNode
208
+ }
209
+ if (target.tagName !== 'TD') return
210
+ if (hasClass(target, 'disabled')) return
211
+ const column = target.cellIndex
212
+ const row = target.parentNode.rowIndex
213
+ const month = row * 4 + column
214
+ const newDate = this.getMonthOfCell(month)
215
+ if (this.selectionMode === 'range') {
216
+ if (!this.rangeState.selecting) {
217
+ this.$emit('pick', { minDate: newDate, maxDate: null })
218
+ this.rangeState.selecting = true
219
+ } else {
220
+ if (newDate >= this.minDate) {
221
+ this.$emit('pick', { minDate: this.minDate, maxDate: newDate })
222
+ } else {
223
+ this.$emit('pick', { minDate: newDate, maxDate: this.minDate })
224
+ }
225
+ this.rangeState.selecting = false
226
+ }
227
+ } else if (this.selectionMode === 'months') {
228
+ const value = this.value || []
229
+ const year = this.date.getFullYear()
230
+ const newValue = arrayFindIndex(value, (date) => date.getFullYear() === year && date.getMonth() === month) >= 0 ? removeFromArray(value, (date) => date.getTime() === newDate.getTime()) : [...value, newDate]
231
+ this.$emit('pick', newValue)
232
+ } else {
233
+ this.$emit('pick', month)
234
+ }
235
+ }
236
+ },
237
+
238
+ computed: {
239
+ rows() {
240
+ // TODO: refactory rows / getCellClasses
241
+ const rows = this.tableRows
242
+ const disabledDate = this.disabledDate
243
+ const selectedDate = []
244
+ const now = getMonthTimestamp(new Date(), this.utc)
245
+
246
+ for (let i = 0; i < 3; i++) {
247
+ const row = rows[i]
248
+ for (let j = 0; j < 4; j++) {
249
+ let cell = row[j]
250
+ if (!cell) {
251
+ cell = { row: i, column: j, type: 'normal', inRange: false, start: false, end: false }
252
+ }
253
+
254
+ cell.type = 'normal'
255
+
256
+ const index = i * 4 + j
257
+ const time = new Date(this.date.getFullYear(), index).getTime()
258
+ cell.inRange = time >= getMonthTimestamp(this.minDate) && time <= getMonthTimestamp(this.maxDate)
259
+ cell.start = this.minDate && time === getMonthTimestamp(this.minDate)
260
+ cell.end = this.maxDate && time === getMonthTimestamp(this.maxDate)
261
+ const isToday = time === now
262
+
263
+ if (isToday) {
264
+ cell.type = 'today'
265
+ }
266
+ cell.text = index
267
+ let cellDate = new Date(time)
268
+ cell.disabled = typeof disabledDate === 'function' && disabledDate(cellDate)
269
+ cell.selected = arrayFind(selectedDate, (date) => date.getTime() === cellDate.getTime())
270
+
271
+ this.$set(row, j, cell)
272
+ }
273
+ }
274
+ return rows
275
+ }
276
+ }
277
+ }
278
+ </script>
@@ -0,0 +1,340 @@
1
+ <template>
2
+ <div
3
+ class="el-time-spinner"
4
+ :class="{ 'has-seconds': showSeconds }"
5
+ >
6
+ <template v-if="!arrowControl">
7
+ <el-scrollbar
8
+ @mouseenter.native="emitSelectRange('hours')"
9
+ @mousemove.native="adjustCurrentSpinner('hours')"
10
+ class="el-time-spinner__wrapper"
11
+ wrap-style="max-height: inherit;"
12
+ view-class="el-time-spinner__list"
13
+ noresize
14
+ tag="ul"
15
+ ref="hours"
16
+ >
17
+ <li
18
+ @click="handleClick('hours', { value: hour, disabled: disabled })"
19
+ v-for="(disabled, hour) in hoursList"
20
+ class="el-time-spinner__item"
21
+ :key="hour"
22
+ :class="{ 'active': hour === hours, 'disabled': disabled }"
23
+ >{{ ('0' + (amPmMode ? (hour % 12 || 12) : hour )).slice(-2) }}{{ amPm(hour) }}</li>
24
+ </el-scrollbar>
25
+ <el-scrollbar
26
+ @mouseenter.native="emitSelectRange('minutes')"
27
+ @mousemove.native="adjustCurrentSpinner('minutes')"
28
+ class="el-time-spinner__wrapper"
29
+ wrap-style="max-height: inherit;"
30
+ view-class="el-time-spinner__list"
31
+ noresize
32
+ tag="ul"
33
+ ref="minutes"
34
+ >
35
+ <li
36
+ @click="handleClick('minutes', { value: key, disabled: false })"
37
+ v-for="(enabled, key) in minutesList"
38
+ :key="key"
39
+ class="el-time-spinner__item"
40
+ :class="{ 'active': key === minutes, disabled: !enabled }"
41
+ >{{ ('0' + key).slice(-2) }}</li>
42
+ </el-scrollbar>
43
+ <el-scrollbar
44
+ v-show="showSeconds"
45
+ @mouseenter.native="emitSelectRange('seconds')"
46
+ @mousemove.native="adjustCurrentSpinner('seconds')"
47
+ class="el-time-spinner__wrapper"
48
+ wrap-style="max-height: inherit;"
49
+ view-class="el-time-spinner__list"
50
+ noresize
51
+ tag="ul"
52
+ ref="seconds"
53
+ >
54
+ <li
55
+ @click="handleClick('seconds', { value: key, disabled: false })"
56
+ v-for="(second, key) in 60"
57
+ class="el-time-spinner__item"
58
+ :class="{ 'active': key === seconds }"
59
+ :key="key"
60
+ >{{ ('0' + key).slice(-2) }}</li>
61
+ </el-scrollbar>
62
+ </template>
63
+ <template v-if="arrowControl">
64
+ <div
65
+ @mouseenter="emitSelectRange('hours')"
66
+ class="el-time-spinner__wrapper is-arrow"
67
+ >
68
+ <i
69
+ v-repeat-click="decrease"
70
+ class="el-time-spinner__arrow el-icon-arrow-up"
71
+ ></i>
72
+ <i
73
+ v-repeat-click="increase"
74
+ class="el-time-spinner__arrow el-icon-arrow-down"
75
+ ></i>
76
+ <ul
77
+ class="el-time-spinner__list"
78
+ ref="hours"
79
+ >
80
+ <li
81
+ class="el-time-spinner__item"
82
+ :class="{ 'active': hour === hours, 'disabled': hoursList[hour] }"
83
+ v-for="(hour, key) in arrowHourList"
84
+ :key="key"
85
+ >{{ hour === undefined ? '' : ('0' + (amPmMode ? (hour % 12 || 12) : hour )).slice(-2) + amPm(hour) }}</li>
86
+ </ul>
87
+ </div>
88
+ <div
89
+ @mouseenter="emitSelectRange('minutes')"
90
+ class="el-time-spinner__wrapper is-arrow"
91
+ >
92
+ <i
93
+ v-repeat-click="decrease"
94
+ class="el-time-spinner__arrow el-icon-arrow-up"
95
+ ></i>
96
+ <i
97
+ v-repeat-click="increase"
98
+ class="el-time-spinner__arrow el-icon-arrow-down"
99
+ ></i>
100
+ <ul
101
+ class="el-time-spinner__list"
102
+ ref="minutes"
103
+ >
104
+ <li
105
+ class="el-time-spinner__item"
106
+ :class="{ 'active': minute === minutes }"
107
+ v-for="(minute, key) in arrowMinuteList"
108
+ :key="key"
109
+ >
110
+ {{ minute === undefined ? '' : ('0' + minute).slice(-2) }}
111
+ </li>
112
+ </ul>
113
+ </div>
114
+ <div
115
+ @mouseenter="emitSelectRange('seconds')"
116
+ class="el-time-spinner__wrapper is-arrow"
117
+ v-if="showSeconds"
118
+ >
119
+ <i
120
+ v-repeat-click="decrease"
121
+ class="el-time-spinner__arrow el-icon-arrow-up"
122
+ ></i>
123
+ <i
124
+ v-repeat-click="increase"
125
+ class="el-time-spinner__arrow el-icon-arrow-down"
126
+ ></i>
127
+ <ul
128
+ class="el-time-spinner__list"
129
+ ref="seconds"
130
+ >
131
+ <li
132
+ v-for="(second, key) in arrowSecondList"
133
+ class="el-time-spinner__item"
134
+ :class="{ 'active': second === seconds }"
135
+ :key="key"
136
+ >
137
+ {{ second === undefined ? '' : ('0' + second).slice(-2) }}
138
+ </li>
139
+ </ul>
140
+ </div>
141
+ </template>
142
+ </div>
143
+ </template>
144
+
145
+ <script type="text/babel">
146
+ import { getRangeHours, getRangeMinutes, modifyTime } from 'bi-eleme/src/utils/date-util'
147
+ import ElScrollbar from 'bi-eleme/packages/scrollbar'
148
+ import RepeatClick from 'bi-eleme/src/directives/repeat-click'
149
+
150
+ export default {
151
+ components: { ElScrollbar },
152
+
153
+ directives: {
154
+ repeatClick: RepeatClick
155
+ },
156
+
157
+ props: {
158
+ date: {},
159
+ defaultValue: {}, // reserved for future use
160
+ showSeconds: {
161
+ type: Boolean,
162
+ default: true
163
+ },
164
+ arrowControl: Boolean,
165
+ amPmMode: {
166
+ type: String,
167
+ default: '' // 'a': am/pm; 'A': AM/PM
168
+ }
169
+ },
170
+
171
+ computed: {
172
+ hours() {
173
+ return this.date.getHours()
174
+ },
175
+ minutes() {
176
+ return this.date.getMinutes()
177
+ },
178
+ seconds() {
179
+ return this.date.getSeconds()
180
+ },
181
+ hoursList() {
182
+ return getRangeHours(this.selectableRange)
183
+ },
184
+ minutesList() {
185
+ return getRangeMinutes(this.selectableRange, this.hours)
186
+ },
187
+ arrowHourList() {
188
+ const hours = this.hours
189
+ return [hours > 0 ? hours - 1 : undefined, hours, hours < 23 ? hours + 1 : undefined]
190
+ },
191
+ arrowMinuteList() {
192
+ const minutes = this.minutes
193
+ return [minutes > 0 ? minutes - 1 : undefined, minutes, minutes < 59 ? minutes + 1 : undefined]
194
+ },
195
+ arrowSecondList() {
196
+ const seconds = this.seconds
197
+ return [seconds > 0 ? seconds - 1 : undefined, seconds, seconds < 59 ? seconds + 1 : undefined]
198
+ }
199
+ },
200
+
201
+ data() {
202
+ return {
203
+ selectableRange: [],
204
+ currentScrollbar: null
205
+ }
206
+ },
207
+
208
+ mounted() {
209
+ this.$nextTick(() => {
210
+ !this.arrowControl && this.bindScrollEvent()
211
+ })
212
+ },
213
+
214
+ methods: {
215
+ increase() {
216
+ this.scrollDown(1)
217
+ },
218
+
219
+ decrease() {
220
+ this.scrollDown(-1)
221
+ },
222
+
223
+ modifyDateField(type, value) {
224
+ switch (type) {
225
+ case 'hours':
226
+ this.$emit('change', modifyTime(this.date, value, this.minutes, this.seconds))
227
+ break
228
+ case 'minutes':
229
+ this.$emit('change', modifyTime(this.date, this.hours, value, this.seconds))
230
+ break
231
+ case 'seconds':
232
+ this.$emit('change', modifyTime(this.date, this.hours, this.minutes, value))
233
+ break
234
+ }
235
+ },
236
+
237
+ handleClick(type, { value, disabled }) {
238
+ if (!disabled) {
239
+ this.modifyDateField(type, value)
240
+ this.emitSelectRange(type)
241
+ this.adjustSpinner(type, value)
242
+ }
243
+ },
244
+
245
+ emitSelectRange(type) {
246
+ if (type === 'hours') {
247
+ this.$emit('select-range', 0, 2)
248
+ } else if (type === 'minutes') {
249
+ this.$emit('select-range', 3, 5)
250
+ } else if (type === 'seconds') {
251
+ this.$emit('select-range', 6, 8)
252
+ }
253
+ this.currentScrollbar = type
254
+ },
255
+
256
+ bindScrollEvent() {
257
+ const bindFunction = (type) => {
258
+ this.$refs[type].wrap.onscroll = (e) => {
259
+ // TODO: scroll is emitted when set scrollTop programatically
260
+ // should find better solutions in the future!
261
+ this.handleScroll(type, e)
262
+ }
263
+ }
264
+ bindFunction('hours')
265
+ bindFunction('minutes')
266
+ bindFunction('seconds')
267
+ },
268
+
269
+ handleScroll(type) {
270
+ const value = Math.min(Math.round((this.$refs[type].wrap.scrollTop - (this.scrollBarHeight(type) * 0.5 - 10) / this.typeItemHeight(type) + 3) / this.typeItemHeight(type)), type === 'hours' ? 23 : 59)
271
+ this.modifyDateField(type, value)
272
+ },
273
+
274
+ // NOTE: used by datetime / date-range panel
275
+ // renamed from adjustScrollTop
276
+ // should try to refactory it
277
+ adjustSpinners() {
278
+ this.adjustSpinner('hours', this.hours)
279
+ this.adjustSpinner('minutes', this.minutes)
280
+ this.adjustSpinner('seconds', this.seconds)
281
+ },
282
+
283
+ adjustCurrentSpinner(type) {
284
+ this.adjustSpinner(type, this[type])
285
+ },
286
+
287
+ adjustSpinner(type, value) {
288
+ if (this.arrowControl) return
289
+ const el = this.$refs[type].wrap
290
+ if (el) {
291
+ el.scrollTop = Math.max(0, value * this.typeItemHeight(type))
292
+ }
293
+ },
294
+
295
+ scrollDown(step) {
296
+ if (!this.currentScrollbar) {
297
+ this.emitSelectRange('hours')
298
+ }
299
+
300
+ const label = this.currentScrollbar
301
+ const hoursList = this.hoursList
302
+ let now = this[label]
303
+
304
+ if (this.currentScrollbar === 'hours') {
305
+ let total = Math.abs(step)
306
+ step = step > 0 ? 1 : -1
307
+ let length = hoursList.length
308
+ while (length-- && total) {
309
+ now = (now + step + hoursList.length) % hoursList.length
310
+ if (hoursList[now]) {
311
+ continue
312
+ }
313
+ total--
314
+ }
315
+ if (hoursList[now]) return
316
+ } else {
317
+ now = (now + step + 60) % 60
318
+ }
319
+
320
+ this.modifyDateField(label, now)
321
+ this.adjustSpinner(label, now)
322
+ this.$nextTick(() => this.emitSelectRange(this.currentScrollbar))
323
+ },
324
+ amPm(hour) {
325
+ let shouldShowAmPm = this.amPmMode.toLowerCase() === 'a'
326
+ if (!shouldShowAmPm) return ''
327
+ let isCapital = this.amPmMode === 'A'
328
+ let content = hour < 12 ? ' am' : ' pm'
329
+ if (isCapital) content = content.toUpperCase()
330
+ return content
331
+ },
332
+ typeItemHeight(type) {
333
+ return this.$refs[type].$el.querySelector('li').offsetHeight
334
+ },
335
+ scrollBarHeight(type) {
336
+ return this.$refs[type].$el.offsetHeight
337
+ }
338
+ }
339
+ }
340
+ </script>