Bug 1475208 - (Part 5) Add keydown/keyup handlers for font size auto-increment; refine mousedown/mouseup handlers. r=gl
authorRazvan Caliman <rcaliman@mozilla.com>
Mon, 30 Jul 2018 23:46:48 +0200
changeset 430556 d473e53176769aa24fd35ae712a9da226fddac2b
parent 430555 33b3f88d4232b6799f170e4f9f660357ae92893e
child 430557 2ad148a42f6c5a73e385197665c63309867fb164
push id34409
push usertoros@mozilla.com
push dateThu, 09 Aug 2018 10:00:05 +0000
treeherdermozilla-central@eb9ff7de69ef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgl
bugs1475208
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1475208 - (Part 5) Add keydown/keyup handlers for font size auto-increment; refine mousedown/mouseup handlers. r=gl MozReview-Commit-ID: JUdXfhw4Ca0
devtools/client/inspector/fonts/components/FontPropertyValue.js
--- a/devtools/client/inspector/fonts/components/FontPropertyValue.js
+++ b/devtools/client/inspector/fonts/components/FontPropertyValue.js
@@ -2,16 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { PureComponent } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+const { KeyCodes } = require("devtools/client/shared/keycodes");
 
 // Milliseconds between auto-increment interval iterations.
 const AUTOINCREMENT_DELAY = 300;
 const UNITS = ["em", "rem", "%", "px", "vh", "vw"];
 
 class FontPropertyValue extends PureComponent {
   static get propTypes() {
     return {
@@ -35,20 +36,24 @@ class FontPropertyValue extends PureComp
     this.interval = null;
     this.state = {
       // Whether the user is dragging the slider thumb or pressing on the numeric stepper.
       interactive: false,
       value: null,
     };
 
     this.autoIncrement = this.autoIncrement.bind(this);
+    this.onBlur = this.onBlur.bind(this);
     this.onChange = this.onChange.bind(this);
+    this.onKeyDown = this.onKeyDown.bind(this);
+    this.onKeyUp = this.onKeyUp.bind(this);
     this.onMouseDown = this.onMouseDown.bind(this);
     this.onMouseUp = this.onMouseUp.bind(this);
     this.onUnitChange = this.onUnitChange.bind(this);
+    this.stopAutoIncrement = this.stopAutoIncrement.bind(this);
   }
 
   componentDidUpdate(prevProps, prevState) {
     // Clear the auto-increment interval if interactive state changed from true to false.
     if (prevState.interactive && !this.state.interactive) {
       this.stopAutoIncrement();
     }
   }
@@ -74,16 +79,20 @@ class FontPropertyValue extends PureComp
    * @param  {Number} value
    *         Numeric value.
    * @return {Boolean}
    */
   isAtUpperBound(value) {
     return value >= Math.floor(this.props.max);
   }
 
+  onBlur() {
+    this.toggleInteractiveState(false);
+  }
+
   /**
    * Handler for "change" events from the range and number input fields. Calls the change
    * handler provided with props and updates internal state with the current value.
    * Begins auto-incrementing if the value is already at the upper bound.
    *
    * @param {Event} e
    *        Change event.
    */
@@ -97,38 +106,89 @@ class FontPropertyValue extends PureComp
     }
 
     // Begin auto-incrementing when reaching the upper bound.
     if (this.isAtUpperBound(value) && this.state.interactive) {
       this.startAutoIncrement();
     }
   }
 
+  /**
+   * Handler for "keydown" events from the range and number input fields.
+   * Toggles on the "interactive" state. @See toggleInteractiveState();
+   * Begins auto-incrementing if the value is already at the upper bound.
+   *
+   * @param {Event} e
+   *        KeyDown event.
+   */
+  onKeyDown(e) {
+    const inputType = e.target.type;
+
+    if ([
+      KeyCodes.DOM_VK_UP,
+      KeyCodes.DOM_VK_DOWN,
+      KeyCodes.DOM_VK_RIGHT,
+      KeyCodes.DOM_VK_LEFT
+    ].includes(e.keyCode)) {
+      this.toggleInteractiveState(true);
+    }
+
+    // Begin auto-incrementing if the value is already at the upper bound
+    // and the user gesture requests a higher value.
+    if (this.isAtUpperBound(this.props.value)) {
+      if ((inputType === "range" &&
+            e.keyCode === KeyCodes.DOM_VK_UP || e.keyCode === KeyCodes.DOM_VK_RIGHT) ||
+          (inputType === "number" &&
+            e.keyCode === KeyCodes.DOM_VK_UP)) {
+        this.startAutoIncrement();
+      }
+    }
+  }
+
+  onKeyUp(e) {
+    if ([
+      KeyCodes.DOM_VK_UP,
+      KeyCodes.DOM_VK_DOWN,
+      KeyCodes.DOM_VK_RIGHT,
+      KeyCodes.DOM_VK_LEFT
+    ].includes(e.keyCode)) {
+      this.toggleInteractiveState(false);
+    }
+  }
+
   onUnitChange(e) {
     this.props.onChange(this.props.name, this.props.value, this.props.unit,
        e.target.value);
     // Reset internal state value and wait for converted value from props.
     this.setState((prevState) => {
       return {
         ...prevState,
         value: null
       };
     });
   }
 
+  /**
+   * Handler for "keydown" events from the sider and input fields.
+   * Toggles on the "interactive" state. @See toggleInteractiveState();
+   * Begins auto-incrementing if the value is already at the upper bound.
+   *
+   * @param {Event} e
+   *        MouseDown event.
+   */
   onMouseDown(e) {
-    this.setState((prevState, props) => {
-      return { ...prevState, interactive: true, value: props.value };
-    });
+    // Begin auto-incrementing if the value is already at the upper bound.
+    if (this.isAtUpperBound(this.props.value) && e.target.type === "range") {
+      this.startAutoIncrement();
+    }
+    this.toggleInteractiveState(true);
   }
 
   onMouseUp(e) {
-    this.setState((prevState, props) => {
-      return { ...prevState, interactive: false, value: props.value };
-    });
+    this.toggleInteractiveState(false);
   }
 
   startAutoIncrement() {
     // Do not set auto-increment interval if not allowed to or if one is already set.
     if (!this.props.allowAutoIncrement || this.interval) {
       return;
     }
 
@@ -138,17 +198,17 @@ class FontPropertyValue extends PureComp
   stopAutoIncrement() {
     clearInterval(this.interval);
     this.interval = null;
   }
 
   /**
    * Toggle the "interactive" state which causes render() to use `value` fom internal
    * state instead of from props to prevent jittering during continous dragging of the
-   * slider thumb or incrementing from the number input.
+   * range input thumb or incrementing from the number input.
    *
    * @param {Boolean} isInteractive
    *        Whether to mark the interactive state on or off.
    */
   toggleInteractiveState(isInteractive) {
     this.setState((prevState) => {
       return {
         ...prevState,
@@ -179,30 +239,33 @@ class FontPropertyValue extends PureComp
     // Guard against bad axis data.
     if (this.props.min === this.props.max) {
       return null;
     }
 
     const defaults = {
       min: this.props.min,
       max: this.props.max,
+      onBlur: this.onBlur,
       onChange: this.onChange,
-      onMouseDown: this.onMouseDown,
-      onMouseUp: this.onMouseUp,
+      onKeyUp: this.onKeyUp,
+      onKeyDown: this.onKeyDown,
       step: this.props.step || 1,
       // While interacting with the slider or numeric stepper, prevent updating value from
       // outside props which may be debounced and could cause jitter when rendering.
       value: this.state.interactive
         ? this.state.value
         : this.props.value || this.props.defaultValue,
     };
 
     const range = dom.input(
       {
         ...defaults,
+        onMouseDown: this.onMouseDown,
+        onMouseUp: this.onMouseUp,
         className: "font-value-slider",
         name: this.props.name,
         title: this.props.label,
         type: "range",
       }
     );
 
     const input = dom.input(