Browse Source

TimePicker: support keyboard control (#6050)

* TimePicker: support keyboard control

* fix time-range

* fix timeformat
Dreamacro 8 năm trước cách đây
mục cha
commit
de28477064

+ 32 - 1
packages/date-picker/src/basic/time-spinner.vue

@@ -129,7 +129,8 @@
         hoursPrivate: 0,
         minutesPrivate: 0,
         secondsPrivate: 0,
-        selectableRange: []
+        selectableRange: [],
+        currentScrollbar: null
       };
     },
 
@@ -162,6 +163,7 @@
         } else if (type === 'seconds') {
           this.$emit('select-range', 6, 8);
         }
+        this.currentScrollbar = type;
       },
 
       bindScrollEvent() {
@@ -188,6 +190,35 @@
 
       ajustElTop(type, value) {
         this[`${type}El`].scrollTop = Math.max(0, (value - 2.5) * 32 + 80);
+      },
+
+      scrollDown(step) {
+        if (!this.currentScrollbar) {
+          this.emitSelectRange('hours');
+        }
+
+        const label = this.currentScrollbar;
+        const hoursList = this.hoursList;
+        let now = this[label];
+
+        if (this.currentScrollbar === 'hours') {
+          let total = Math.abs(step);
+          step = step > 0 ? 1 : -1;
+          let length = hoursList.length;
+          while (length-- && total) {
+            now = (now + step + hoursList.length) % hoursList.length;
+            if (hoursList[now]) {
+              continue;
+            }
+            total--;
+          }
+          if (hoursList[now]) return;
+        } else {
+          now = (now + step + 60) % 60;
+        }
+
+        this.$emit('change', { [label]: now });
+        this.ajustElTop(label.slice(0, -1), now);
       }
     }
   };

+ 36 - 2
packages/date-picker/src/panel/time-range.vue

@@ -89,6 +89,14 @@
     computed: {
       showSeconds() {
         return (this.format || '').indexOf('ss') !== -1;
+      },
+
+      offset() {
+        return this.showSeconds ? 11 : 8;
+      },
+
+      spinner() {
+        return this.selectionRange[0] < this.offset ? this.$refs.minSpinner : this.$refs.maxSpinner;
       }
     },
 
@@ -110,7 +118,8 @@
         minSeconds: time.minTime.getSeconds(),
         format: 'HH:mm:ss',
         visible: false,
-        width: 0
+        width: 0,
+        selectionRange: [0, 2]
       };
     },
 
@@ -118,6 +127,12 @@
       value(newVal) {
         this.panelCreated();
         this.$nextTick(_ => this.ajustScrollTop());
+      },
+
+      visible(val) {
+        if (val) {
+          this.$nextTick(() => this.$refs.minSpinner.emitSelectRange('hours'));
+        }
       }
     },
 
@@ -194,10 +209,12 @@
 
       setMinSelectionRange(start, end) {
         this.$emit('select-range', start, end);
+        this.selectionRange = [start, end];
       },
 
       setMaxSelectionRange(start, end) {
-        this.$emit('select-range', start + 11, end + 11);
+        this.$emit('select-range', start + this.offset, end + this.offset);
+        this.selectionRange = [start + this.offset, end + this.offset];
       },
 
       handleConfirm(visible = false, first = false) {
@@ -214,6 +231,23 @@
       ajustScrollTop() {
         this.$refs.minSpinner.ajustScrollTop();
         this.$refs.maxSpinner.ajustScrollTop();
+      },
+
+      scrollDown(step) {
+        this.spinner.scrollDown(step);
+      },
+
+      changeSelectionRange(step) {
+        const list = this.showSeconds ? [0, 3, 6, 11, 14, 17] : [0, 3, 8, 11];
+        const mapping = ['hours', 'minutes'].concat(this.showSeconds ? ['seconds'] : []);
+        const index = list.indexOf(this.selectionRange[0]);
+        const next = (index + step + list.length) % list.length;
+        const half = list.length / 2;
+        if (next < half) {
+          this.$refs.minSpinner.emitSelectRange(mapping[next]);
+        } else {
+          this.$refs.maxSpinner.emitSelectRange(mapping[next - half]);
+        }
       }
     },
 

+ 18 - 1
packages/date-picker/src/panel/time.vue

@@ -54,6 +54,9 @@
     watch: {
       visible(val) {
         this.currentVisible = val;
+        if (val) {
+          this.$nextTick(() => this.$refs.spinner.emitSelectRange('hours'));
+        }
       },
 
       pickerWidth(val) {
@@ -92,7 +95,8 @@
         selectableRange: [],
         currentDate: this.$options.defaultValue || this.date || new Date(),
         currentVisible: this.visible || false,
-        width: this.pickerWidth || 0
+        width: this.pickerWidth || 0,
+        selectionRange: [0, 2]
       };
     },
 
@@ -130,6 +134,7 @@
 
       setSelectionRange(start, end) {
         this.$emit('select-range', start, end);
+        this.selectionRange = [start, end];
       },
 
       handleConfirm(visible = false, first) {
@@ -140,6 +145,18 @@
 
       ajustScrollTop() {
         return this.$refs.spinner.ajustScrollTop();
+      },
+
+      scrollDown(step) {
+        this.$refs.spinner.scrollDown(step);
+      },
+
+      changeSelectionRange(step) {
+        const list = [0, 3].concat(this.showSeconds ? [6] : []);
+        const mapping = ['hours', 'minutes'].concat(this.showSeconds ? ['seconds'] : []);
+        const index = list.indexOf(this.selectionRange[0]);
+        const next = (index + step + list.length) % list.length;
+        this.$refs.spinner.emitSelectRange(mapping[next]);
       }
     },
 

+ 38 - 0
packages/date-picker/src/picker/time-picker.js

@@ -34,5 +34,43 @@ export default {
   created() {
     this.type = this.isRange ? 'timerange' : 'time';
     this.panel = this.isRange ? TimeRangePanel : TimePanel;
+  },
+
+  methods: {
+    handleKeydown(event) {
+      const keyCode = event.keyCode;
+
+      // TAB or ESC
+      if (keyCode === 9 || keyCode === 27) {
+        this.pickerVisible = false;
+        event.stopPropagation();
+        return;
+      }
+
+      const mapping = { 38: -1, 40: 1, 37: -1, 39: 1 };
+
+      // Left or Right
+      if (keyCode === 37 || keyCode === 39) {
+        const step = mapping[keyCode];
+        this.picker.changeSelectionRange(step);
+        event.preventDefault();
+        return;
+      }
+
+      // Up or Down
+      if (keyCode === 38 || keyCode === 40) {
+        const step = mapping[keyCode];
+        this.picker.scrollDown(step);
+        event.preventDefault();
+        return;
+      }
+
+      if (keyCode === 13) {
+        this.picker.handleConfirm();
+        this.$refs.reference.$refs.input.blur();
+        event.preventDefault();
+        return;
+      }
+    }
   }
 };