summaryrefslogtreecommitdiffstats
path: root/devtools/client/responsive/components/ViewportDimension.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/responsive/components/ViewportDimension.js')
-rw-r--r--devtools/client/responsive/components/ViewportDimension.js249
1 files changed, 249 insertions, 0 deletions
diff --git a/devtools/client/responsive/components/ViewportDimension.js b/devtools/client/responsive/components/ViewportDimension.js
new file mode 100644
index 0000000000..2eea572da8
--- /dev/null
+++ b/devtools/client/responsive/components/ViewportDimension.js
@@ -0,0 +1,249 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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 { isKeyIn } = require("devtools/client/responsive/utils/key");
+const {
+ MIN_VIEWPORT_DIMENSION,
+} = require("devtools/client/responsive/constants");
+const Types = require("devtools/client/responsive/types");
+
+class ViewportDimension extends PureComponent {
+ static get propTypes() {
+ return {
+ doResizeViewport: PropTypes.func.isRequired,
+ onRemoveDeviceAssociation: PropTypes.func.isRequired,
+ viewport: PropTypes.shape(Types.viewport).isRequired,
+ };
+ }
+
+ constructor(props) {
+ super(props);
+
+ const { width, height } = props.viewport;
+
+ this.state = {
+ width,
+ height,
+ isEditing: false,
+ isWidthValid: true,
+ isHeightValid: true,
+ };
+
+ this.isInputValid = this.isInputValid.bind(this);
+ this.onInputBlur = this.onInputBlur.bind(this);
+ this.onInputChange = this.onInputChange.bind(this);
+ this.onInputFocus = this.onInputFocus.bind(this);
+ this.onInputKeyDown = this.onInputKeyDown.bind(this);
+ this.onInputKeyUp = this.onInputKeyUp.bind(this);
+ this.onInputSubmit = this.onInputSubmit.bind(this);
+ }
+
+ componentWillReceiveProps(nextProps) {
+ const { width, height } = nextProps.viewport;
+
+ this.setState({
+ width,
+ height,
+ });
+ }
+
+ /**
+ * Return true if the given value is a number and greater than MIN_VIEWPORT_DIMENSION
+ * and false otherwise.
+ */
+ isInputValid(value) {
+ return (
+ /^\d{2,4}$/.test(value) && parseInt(value, 10) >= MIN_VIEWPORT_DIMENSION
+ );
+ }
+
+ onInputBlur() {
+ const { width, height } = this.props.viewport;
+
+ if (this.state.width != width || this.state.height != height) {
+ this.onInputSubmit();
+ }
+
+ this.setState({ isEditing: false });
+ }
+
+ onInputChange({ target }, callback) {
+ if (target.value.length > 4) {
+ return;
+ }
+
+ if (this.widthInput == target) {
+ this.setState(
+ {
+ width: target.value,
+ isWidthValid: this.isInputValid(target.value),
+ },
+ callback
+ );
+ }
+
+ if (this.heightInput == target) {
+ this.setState(
+ {
+ height: target.value,
+ isHeightValid: this.isInputValid(target.value),
+ },
+ callback
+ );
+ }
+ }
+
+ onInputFocus(e) {
+ this.setState({ isEditing: true });
+ e.target.select();
+ }
+
+ onInputKeyDown(event) {
+ const increment = getIncrement(event);
+ if (!increment) {
+ return;
+ }
+
+ const { target } = event;
+ target.value = parseInt(target.value, 10) + increment;
+ this.onInputChange(event, this.onInputSubmit);
+
+ // Keep this event from having default processing. Since the field is a
+ // number field, default processing would trigger additional manipulations
+ // of the value, and we've already applied the desired amount.
+ event.preventDefault();
+ }
+
+ onInputKeyUp({ target, keyCode }) {
+ // On Enter, submit the input
+ if (keyCode == 13) {
+ this.onInputSubmit();
+ }
+
+ // On Esc, blur the target
+ if (keyCode == 27) {
+ target.blur();
+ }
+ }
+
+ onInputSubmit() {
+ const {
+ viewport,
+ onRemoveDeviceAssociation,
+ doResizeViewport,
+ } = this.props;
+
+ if (!this.state.isWidthValid || !this.state.isHeightValid) {
+ const { width, height } = viewport;
+
+ this.setState({
+ width,
+ height,
+ isWidthValid: true,
+ isHeightValid: true,
+ });
+
+ return;
+ }
+
+ // Change the device selector back to an unselected device
+ // TODO: Bug 1332754: Logic like this probably belongs in the action creator.
+ if (viewport.device) {
+ onRemoveDeviceAssociation(viewport.id);
+ }
+
+ doResizeViewport(
+ viewport.id,
+ parseInt(this.state.width, 10),
+ parseInt(this.state.height, 10)
+ );
+ }
+
+ render() {
+ return dom.div(
+ {
+ className:
+ "viewport-dimension" +
+ (this.state.isEditing ? " editing" : "") +
+ (!this.state.isWidthValid || !this.state.isHeightValid
+ ? " invalid"
+ : ""),
+ },
+ dom.input({
+ ref: input => {
+ this.widthInput = input;
+ },
+ className:
+ "text-input viewport-dimension-input" +
+ (this.state.isWidthValid ? "" : " invalid"),
+ size: 4,
+ type: "number",
+ value: this.state.width,
+ onBlur: this.onInputBlur,
+ onChange: this.onInputChange,
+ onFocus: this.onInputFocus,
+ onKeyDown: this.onInputKeyDown,
+ onKeyUp: this.onInputKeyUp,
+ }),
+ dom.span(
+ {
+ className: "viewport-dimension-separator",
+ },
+ "×"
+ ),
+ dom.input({
+ ref: input => {
+ this.heightInput = input;
+ },
+ className:
+ "text-input viewport-dimension-input" +
+ (this.state.isHeightValid ? "" : " invalid"),
+ size: 4,
+ type: "number",
+ value: this.state.height,
+ onBlur: this.onInputBlur,
+ onChange: this.onInputChange,
+ onFocus: this.onInputFocus,
+ onKeyDown: this.onInputKeyDown,
+ onKeyUp: this.onInputKeyUp,
+ })
+ );
+ }
+}
+
+/**
+ * Get the increment/decrement step to use for the provided key event.
+ */
+function getIncrement(event) {
+ const defaultIncrement = 1;
+ const largeIncrement = 100;
+ const mediumIncrement = 10;
+
+ let increment = 0;
+ const key = event.keyCode;
+
+ if (isKeyIn(key, "UP", "PAGE_UP")) {
+ increment = 1 * defaultIncrement;
+ } else if (isKeyIn(key, "DOWN", "PAGE_DOWN")) {
+ increment = -1 * defaultIncrement;
+ }
+
+ if (event.shiftKey) {
+ if (isKeyIn(key, "PAGE_UP", "PAGE_DOWN")) {
+ increment *= largeIncrement;
+ } else {
+ increment *= mediumIncrement;
+ }
+ }
+
+ return increment;
+}
+
+module.exports = ViewportDimension;