/* 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;