256 lines
6.4 KiB
JavaScript
256 lines
6.4 KiB
JavaScript
/* 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("resource://devtools/client/shared/vendor/react.mjs");
|
||
const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
|
||
const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.mjs");
|
||
|
||
const {
|
||
isKeyIn,
|
||
} = require("resource://devtools/client/responsive/utils/key.js");
|
||
const {
|
||
MIN_VIEWPORT_DIMENSION,
|
||
} = require("resource://devtools/client/responsive/constants.js");
|
||
const Types = require("resource://devtools/client/responsive/types.js");
|
||
|
||
class ViewportDimension extends PureComponent {
|
||
static get propTypes() {
|
||
return {
|
||
doResizeViewport: PropTypes.func.isRequired,
|
||
onRemoveDeviceAssociation: PropTypes.func,
|
||
viewport: PropTypes.shape(Types.viewport).isRequired,
|
||
};
|
||
}
|
||
|
||
static getDerivedStateFromProps(props, state) {
|
||
const { width, height } = props.viewport;
|
||
if (state.prevWidth !== width || state.prevHeight !== height) {
|
||
return {
|
||
width,
|
||
height,
|
||
prevWidth: width,
|
||
prevHeight: height,
|
||
};
|
||
}
|
||
return null;
|
||
}
|
||
|
||
constructor(props) {
|
||
super(props);
|
||
|
||
const { width, height } = props.viewport;
|
||
|
||
this.state = {
|
||
width,
|
||
height,
|
||
prevWidth: width,
|
||
prevHeight: 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);
|
||
}
|
||
|
||
/**
|
||
* 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 && typeof onRemoveDeviceAssociation === "function") {
|
||
onRemoveDeviceAssociation(viewport.id, { resetProfile: false });
|
||
}
|
||
|
||
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;
|