Source code

Revision control

Copy as Markdown

Other Tools

/* 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/. */
/* eslint-env browser */
"use strict";
// A box with a start and a end pane, separated by a dragable splitter that
// allows the user to resize the relative widths of the panes.
//
// +-----------------------+---------------------+
// | | |
// | | |
// | S |
// | Start Pane p End Pane |
// | l |
// | i |
// | t |
// | t |
// | e |
// | r |
// | | |
// | | |
// +-----------------------+---------------------+
const {
Component,
} = require("resource://devtools/client/shared/vendor/react.js");
const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
const { assert } = require("resource://devtools/shared/DevToolsUtils.js");
class HSplitBox extends Component {
static get propTypes() {
return {
// The contents of the start pane.
start: PropTypes.any.isRequired,
// The contents of the end pane.
end: PropTypes.any.isRequired,
// The relative width of the start pane, expressed as a number between 0 and
// 1. The relative width of the end pane is 1 - startWidth. For example,
// with startWidth = .5, both panes are of equal width; with startWidth =
// .25, the start panel will take up 1/4 width and the end panel will take
// up 3/4 width.
startWidth: PropTypes.number,
// A minimum css width value for the start and end panes.
minStartWidth: PropTypes.any,
minEndWidth: PropTypes.any,
// A callback fired when the user drags the splitter to resize the relative
// pane widths. The function is passed the startWidth value that would put
// the splitter underneath the users mouse.
onResize: PropTypes.func.isRequired,
};
}
static get defaultProps() {
return {
startWidth: 0.5,
minStartWidth: "20px",
minEndWidth: "20px",
};
}
constructor(props) {
super(props);
this.state = {
mouseDown: false,
};
this._onMouseDown = this._onMouseDown.bind(this);
this._onMouseUp = this._onMouseUp.bind(this);
this._onMouseMove = this._onMouseMove.bind(this);
}
componentDidMount() {
document.defaultView.top.addEventListener("mouseup", this._onMouseUp);
document.defaultView.top.addEventListener("mousemove", this._onMouseMove);
}
componentWillUnmount() {
document.defaultView.top.removeEventListener("mouseup", this._onMouseUp);
document.defaultView.top.removeEventListener(
"mousemove",
this._onMouseMove
);
}
_onMouseDown(event) {
if (event.button !== 0) {
return;
}
this.setState({ mouseDown: true });
event.preventDefault();
}
_onMouseUp(event) {
if (event.button !== 0 || !this.state.mouseDown) {
return;
}
this.setState({ mouseDown: false });
event.preventDefault();
}
_onMouseMove(event) {
if (!this.state.mouseDown) {
return;
}
const rect = this.refs.box.getBoundingClientRect();
const { left, right } = rect;
const width = right - left;
const direction = this.refs.box.ownerDocument.dir;
const relative =
direction == "rtl" ? right - event.clientX : event.clientX - left;
this.props.onResize(relative / width);
event.preventDefault();
}
render() {
/* eslint-disable no-shadow */
const { start, end, startWidth, minStartWidth, minEndWidth } = this.props;
assert(
startWidth >= 0 && startWidth <= 1,
"0 <= this.props.startWidth <= 1"
);
/* eslint-enable */
return dom.div(
{
className: "h-split-box",
ref: "box",
},
dom.div(
{
className: "h-split-box-pane",
style: { flex: startWidth, minWidth: minStartWidth },
},
start
),
dom.div({
className: "devtools-side-splitter",
onMouseDown: this._onMouseDown,
}),
dom.div(
{
className: "h-split-box-pane",
style: { flex: 1 - startWidth, minWidth: minEndWidth },
},
end
)
);
}
}
module.exports = HSplitBox;