tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ViewportDimension.js (7226B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 "use strict";
      6 
      7 const {
      8  PureComponent,
      9 } = require("resource://devtools/client/shared/vendor/react.mjs");
     10 const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
     11 const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.mjs");
     12 
     13 const {
     14  isKeyIn,
     15 } = require("resource://devtools/client/responsive/utils/key.js");
     16 const {
     17  MIN_VIEWPORT_DIMENSION,
     18 } = require("resource://devtools/client/responsive/constants.js");
     19 const Types = require("resource://devtools/client/responsive/types.js");
     20 
     21 loader.lazyRequireGetter(
     22  this,
     23  "KeyCodes",
     24  "resource://devtools/client/shared/keycodes.js",
     25  true
     26 );
     27 
     28 class ViewportDimension extends PureComponent {
     29  static get propTypes() {
     30    return {
     31      doResizeViewport: PropTypes.func.isRequired,
     32      onRemoveDeviceAssociation: PropTypes.func,
     33      viewport: PropTypes.shape(Types.viewport).isRequired,
     34    };
     35  }
     36 
     37  static getDerivedStateFromProps(props, state) {
     38    const { width, height } = props.viewport;
     39    if (state.prevWidth !== width || state.prevHeight !== height) {
     40      return {
     41        width,
     42        height,
     43        prevWidth: width,
     44        prevHeight: height,
     45      };
     46    }
     47    return null;
     48  }
     49 
     50  constructor(props) {
     51    super(props);
     52 
     53    const { width, height } = props.viewport;
     54 
     55    this.state = {
     56      width,
     57      height,
     58      prevWidth: width,
     59      prevHeight: height,
     60      isEditing: false,
     61      isWidthValid: true,
     62      isHeightValid: true,
     63    };
     64 
     65    this.isInputValid = this.isInputValid.bind(this);
     66    this.onInputBlur = this.onInputBlur.bind(this);
     67    this.onInputChange = this.onInputChange.bind(this);
     68    this.onInputFocus = this.onInputFocus.bind(this);
     69    this.onInputKeyDown = this.onInputKeyDown.bind(this);
     70    this.onInputKeyUp = this.onInputKeyUp.bind(this);
     71    this.onInputSubmit = this.onInputSubmit.bind(this);
     72  }
     73 
     74  /**
     75   * Return true if the given value is a number and greater than MIN_VIEWPORT_DIMENSION
     76   * and false otherwise.
     77   */
     78  isInputValid(value) {
     79    return (
     80      /^\d{2,4}$/.test(value) && parseInt(value, 10) >= MIN_VIEWPORT_DIMENSION
     81    );
     82  }
     83 
     84  onInputBlur() {
     85    const { width, height } = this.props.viewport;
     86 
     87    if (this.state.width != width || this.state.height != height) {
     88      this.onInputSubmit();
     89    }
     90 
     91    this.setState({ isEditing: false });
     92  }
     93 
     94  onInputChange({ target }, callback) {
     95    if (target.value.length > 4) {
     96      return;
     97    }
     98 
     99    if (this.widthInput == target) {
    100      this.setState(
    101        {
    102          width: target.value,
    103          isWidthValid: this.isInputValid(target.value),
    104        },
    105        callback
    106      );
    107    }
    108 
    109    if (this.heightInput == target) {
    110      this.setState(
    111        {
    112          height: target.value,
    113          isHeightValid: this.isInputValid(target.value),
    114        },
    115        callback
    116      );
    117    }
    118  }
    119 
    120  onInputFocus(e) {
    121    this.setState({ isEditing: true });
    122    e.target.select();
    123  }
    124 
    125  onInputKeyDown(event) {
    126    const increment = getIncrement(event);
    127    if (!increment) {
    128      return;
    129    }
    130 
    131    const { target } = event;
    132    target.value = parseInt(target.value, 10) + increment;
    133    this.onInputChange(event, this.onInputSubmit);
    134 
    135    // Keep this event from having default processing. Since the field is a
    136    // number field, default processing would trigger additional manipulations
    137    // of the value, and we've already applied the desired amount.
    138    event.preventDefault();
    139  }
    140 
    141  onInputKeyUp({ target, keyCode }) {
    142    // On Enter, submit the input
    143    if (keyCode == KeyCodes.DOM_VK_RETURN) {
    144      this.onInputSubmit();
    145    }
    146 
    147    // On Esc, revert the value and blur the target
    148    if (keyCode == KeyCodes.DOM_VK_ESCAPE) {
    149      if (this.widthInput == target) {
    150        const width = this.props.viewport.width;
    151        this.setState(
    152          {
    153            width,
    154            isWidthValid: this.isInputValid(width),
    155          },
    156          () => target.blur()
    157        );
    158      }
    159 
    160      if (this.heightInput == target) {
    161        const height = this.props.viewport.height;
    162        this.setState(
    163          {
    164            height,
    165            isHeightValid: this.isInputValid(height),
    166          },
    167          () => target.blur()
    168        );
    169      }
    170    }
    171  }
    172 
    173  onInputSubmit() {
    174    const { viewport, onRemoveDeviceAssociation, doResizeViewport } =
    175      this.props;
    176 
    177    if (!this.state.isWidthValid || !this.state.isHeightValid) {
    178      const { width, height } = viewport;
    179 
    180      this.setState({
    181        width,
    182        height,
    183        isWidthValid: true,
    184        isHeightValid: true,
    185      });
    186 
    187      return;
    188    }
    189 
    190    // Change the device selector back to an unselected device
    191    // TODO: Bug 1332754: Logic like this probably belongs in the action creator.
    192    if (viewport.device && typeof onRemoveDeviceAssociation === "function") {
    193      onRemoveDeviceAssociation(viewport.id, { resetProfile: false });
    194    }
    195 
    196    doResizeViewport(
    197      viewport.id,
    198      parseInt(this.state.width, 10),
    199      parseInt(this.state.height, 10)
    200    );
    201  }
    202 
    203  render() {
    204    return dom.div(
    205      {
    206        className:
    207          "viewport-dimension" +
    208          (this.state.isEditing ? " editing" : "") +
    209          (!this.state.isWidthValid || !this.state.isHeightValid
    210            ? " invalid"
    211            : ""),
    212      },
    213      dom.input({
    214        ref: input => {
    215          this.widthInput = input;
    216        },
    217        className:
    218          "text-input viewport-dimension-input" +
    219          (this.state.isWidthValid ? "" : " invalid"),
    220        size: 4,
    221        type: "number",
    222        value: this.state.width,
    223        onBlur: this.onInputBlur,
    224        onChange: this.onInputChange,
    225        onFocus: this.onInputFocus,
    226        onKeyDown: this.onInputKeyDown,
    227        onKeyUp: this.onInputKeyUp,
    228      }),
    229      dom.span(
    230        {
    231          className: "viewport-dimension-separator",
    232        },
    233        "×"
    234      ),
    235      dom.input({
    236        ref: input => {
    237          this.heightInput = input;
    238        },
    239        className:
    240          "text-input viewport-dimension-input" +
    241          (this.state.isHeightValid ? "" : " invalid"),
    242        size: 4,
    243        type: "number",
    244        value: this.state.height,
    245        onBlur: this.onInputBlur,
    246        onChange: this.onInputChange,
    247        onFocus: this.onInputFocus,
    248        onKeyDown: this.onInputKeyDown,
    249        onKeyUp: this.onInputKeyUp,
    250      })
    251    );
    252  }
    253 }
    254 
    255 /**
    256 * Get the increment/decrement step to use for the provided key event.
    257 */
    258 function getIncrement(event) {
    259  const defaultIncrement = 1;
    260  const largeIncrement = 100;
    261  const mediumIncrement = 10;
    262 
    263  let increment = 0;
    264  const key = event.keyCode;
    265 
    266  if (isKeyIn(key, "UP", "PAGE_UP")) {
    267    increment = 1 * defaultIncrement;
    268  } else if (isKeyIn(key, "DOWN", "PAGE_DOWN")) {
    269    increment = -1 * defaultIncrement;
    270  }
    271 
    272  if (event.shiftKey) {
    273    if (isKeyIn(key, "PAGE_UP", "PAGE_DOWN")) {
    274      increment *= largeIncrement;
    275    } else {
    276      increment *= mediumIncrement;
    277    }
    278  }
    279 
    280  return increment;
    281 }
    282 
    283 module.exports = ViewportDimension;