mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-22 09:45:41 +00:00
Bug 1260552 - Implement Splitter React component. r=jlongster, r=pbro
--HG-- extra : rebase_source : d5a84a350811d78355f6e58274a5ae0c385dd394 extra : histedit_source : ad1505ba34acce7b12865c9c70b538a491cd93a6
This commit is contained in:
parent
e0ff590488
commit
da67e3a3cc
@ -6,6 +6,7 @@
|
||||
|
||||
DIRS += [
|
||||
'reps',
|
||||
'splitter',
|
||||
'tabs',
|
||||
'tree'
|
||||
]
|
||||
|
54
devtools/client/shared/components/splitter/draggable.js
Normal file
54
devtools/client/shared/components/splitter/draggable.js
Normal file
@ -0,0 +1,54 @@
|
||||
/* 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 React = require("devtools/client/shared/vendor/react");
|
||||
const ReactDOM = require("devtools/client/shared/vendor/react-dom");
|
||||
const { DOM: dom, PropTypes } = React;
|
||||
|
||||
const Draggable = React.createClass({
|
||||
displayName: "Draggable",
|
||||
|
||||
propTypes: {
|
||||
onMove: PropTypes.func.isRequired,
|
||||
onStart: PropTypes.func,
|
||||
onStop: PropTypes.func,
|
||||
style: PropTypes.object,
|
||||
className: PropTypes.string
|
||||
},
|
||||
|
||||
startDragging(ev) {
|
||||
ev.preventDefault();
|
||||
const doc = ReactDOM.findDOMNode(this).ownerDocument;
|
||||
doc.addEventListener("mousemove", this.onMove);
|
||||
doc.addEventListener("mouseup", this.onUp);
|
||||
this.props.onStart && this.props.onStart();
|
||||
},
|
||||
|
||||
onMove(ev) {
|
||||
ev.preventDefault();
|
||||
// Use screen coordinates so, moving mouse over iframes
|
||||
// doesn't mangle (relative) coordinates.
|
||||
this.props.onMove(ev.screenX, ev.screenY);
|
||||
},
|
||||
|
||||
onUp(ev) {
|
||||
ev.preventDefault();
|
||||
const doc = ReactDOM.findDOMNode(this).ownerDocument;
|
||||
doc.removeEventListener("mousemove", this.onMove);
|
||||
doc.removeEventListener("mouseup", this.onUp);
|
||||
this.props.onStop && this.props.onStop();
|
||||
},
|
||||
|
||||
render() {
|
||||
return dom.div({
|
||||
style: this.props.style,
|
||||
className: this.props.className,
|
||||
onMouseDown: this.startDragging
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = Draggable;
|
11
devtools/client/shared/components/splitter/moz.build
Normal file
11
devtools/client/shared/components/splitter/moz.build
Normal file
@ -0,0 +1,11 @@
|
||||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
DevToolsModules(
|
||||
'draggable.js',
|
||||
'split-box.css',
|
||||
'split-box.js',
|
||||
)
|
88
devtools/client/shared/components/splitter/split-box.css
Normal file
88
devtools/client/shared/components/splitter/split-box.css
Normal file
@ -0,0 +1,88 @@
|
||||
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||
/* 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/. */
|
||||
|
||||
.split-box {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.split-box.vert {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.split-box.horz {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.split-box > .uncontrolled {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.split-box > .controlled {
|
||||
display: flex;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.split-box > .splitter {
|
||||
background-image: none;
|
||||
border: 0;
|
||||
border-style: solid;
|
||||
border-color: transparent;
|
||||
background-color: var(--theme-splitter-color);
|
||||
background-clip: content-box;
|
||||
position: relative;
|
||||
|
||||
box-sizing: border-box;
|
||||
|
||||
/* Positive z-index positions the splitter on top of its siblings and makes
|
||||
it clickable on both sides. */
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.split-box.vert > .splitter {
|
||||
min-width: calc(var(--devtools-splitter-inline-start-width) +
|
||||
var(--devtools-splitter-inline-end-width) + 1px);
|
||||
|
||||
border-inline-start-width: var(--devtools-splitter-inline-start-width);
|
||||
border-inline-end-width: var(--devtools-splitter-inline-end-width);
|
||||
|
||||
margin-inline-start: calc(-1 * var(--devtools-splitter-inline-start-width) - 1px);
|
||||
margin-inline-end: calc(-1 * var(--devtools-splitter-inline-end-width));
|
||||
|
||||
cursor: ew-resize;
|
||||
}
|
||||
|
||||
.split-box.horz > .splitter {
|
||||
min-height: calc(var(--devtools-splitter-top-width) +
|
||||
var(--devtools-splitter-bottom-width) + 1px);
|
||||
|
||||
border-top-width: var(--devtools-splitter-top-width);
|
||||
border-bottom-width: var(--devtools-splitter-bottom-width);
|
||||
|
||||
margin-top: calc(-1 * var(--devtools-splitter-top-width) - 1px);
|
||||
margin-bottom: calc(-1 * var(--devtools-splitter-bottom-width));
|
||||
|
||||
cursor: ns-resize;
|
||||
}
|
||||
|
||||
.split-box.disabled {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure splitter panels are not processing any mouse
|
||||
* events. This is good for performance during splitter
|
||||
* bar dragging.
|
||||
*/
|
||||
.split-box.dragging > .controlled,
|
||||
.split-box.dragging > .uncontrolled {
|
||||
pointer-events: none;
|
||||
}
|
207
devtools/client/shared/components/splitter/split-box.js
Normal file
207
devtools/client/shared/components/splitter/split-box.js
Normal file
@ -0,0 +1,207 @@
|
||||
/* 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 React = require("devtools/client/shared/vendor/react");
|
||||
const ReactDOM = require("devtools/client/shared/vendor/react-dom");
|
||||
const Draggable = React.createFactory(require("devtools/client/shared/components/splitter/draggable"));
|
||||
const { DOM: dom, PropTypes } = React;
|
||||
|
||||
/**
|
||||
* This component represents a Splitter. The splitter supports vertical
|
||||
* as well as horizontal mode.
|
||||
*/
|
||||
const SplitBox = React.createClass({
|
||||
displayName: "SplitBox",
|
||||
|
||||
propTypes: {
|
||||
// Custom class name. You can use more names separated by a space.
|
||||
className: PropTypes.string,
|
||||
// Initial size of controlled panel.
|
||||
initialSize: PropTypes.number,
|
||||
// Left/top panel
|
||||
startPanel: PropTypes.any,
|
||||
// Min panel size.
|
||||
minSize: PropTypes.number,
|
||||
// Max panel size.
|
||||
maxSize: PropTypes.number,
|
||||
// Right/bottom panel
|
||||
endPanel: PropTypes.any,
|
||||
// True if the right/bottom panel should be controlled.
|
||||
endPanelControl: PropTypes.bool,
|
||||
// Size of the splitter handle bar.
|
||||
splitterSize: PropTypes.number,
|
||||
// True if the splitter bar is vertical (default is vertical).
|
||||
vert: PropTypes.bool
|
||||
},
|
||||
|
||||
getDefaultProps() {
|
||||
return {
|
||||
splitterSize: 5,
|
||||
vert: true,
|
||||
endPanelControl: false
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* The state stores the current orientation (vertical or horizontal)
|
||||
* and the current size (width/height). All these values can change
|
||||
* during the component's life time.
|
||||
*/
|
||||
getInitialState() {
|
||||
return {
|
||||
vert: this.props.vert,
|
||||
width: this.props.initialWidth || this.props.initialSize,
|
||||
height: this.props.initialHeight || this.props.initialSize
|
||||
};
|
||||
},
|
||||
|
||||
// Dragging Events
|
||||
|
||||
/**
|
||||
* Set 'resizing' cursor on entire document during splitter dragging.
|
||||
* This avoids cursor-flickering that happens when the mouse leaves
|
||||
* the splitter bar area (happens frequently).
|
||||
*/
|
||||
onStartMove() {
|
||||
const splitBox = ReactDOM.findDOMNode(this);
|
||||
const doc = splitBox.ownerDocument;
|
||||
let defaultCursor = doc.documentElement.style.cursor;
|
||||
doc.documentElement.style.cursor = (this.state.vert ? "ew-resize" : "ns-resize");
|
||||
|
||||
splitBox.classList.add("dragging");
|
||||
|
||||
this.setState({
|
||||
defaultCursor: defaultCursor
|
||||
});
|
||||
},
|
||||
|
||||
onStopMove() {
|
||||
const splitBox = ReactDOM.findDOMNode(this);
|
||||
const doc = splitBox.ownerDocument;
|
||||
doc.documentElement.style.cursor = this.state.defaultCursor;
|
||||
|
||||
splitBox.classList.remove("dragging");
|
||||
},
|
||||
|
||||
/**
|
||||
* Adjust size of the controlled panel. Depending on the current
|
||||
* orientation we either remember the width or height of
|
||||
* the splitter box.
|
||||
*/
|
||||
onMove(x, y) {
|
||||
const node = ReactDOM.findDOMNode(this);
|
||||
const doc = node.ownerDocument;
|
||||
const win = doc.defaultView;
|
||||
|
||||
let size;
|
||||
let { endPanelControl } = this.props;
|
||||
|
||||
if (this.state.vert) {
|
||||
// Switch the control flag in case of RTL. Note that RTL
|
||||
// has impact on vertical splitter only.
|
||||
let dir = win.getComputedStyle(doc.documentElement).direction;
|
||||
if (dir == "rtl") {
|
||||
endPanelControl = !endPanelControl;
|
||||
}
|
||||
|
||||
let innerOffset = x - win.mozInnerScreenX;
|
||||
size = endPanelControl ?
|
||||
(node.offsetLeft + node.offsetWidth) - innerOffset :
|
||||
innerOffset - node.offsetLeft;
|
||||
|
||||
this.setState({
|
||||
width: size
|
||||
});
|
||||
} else {
|
||||
let innerOffset = y - win.mozInnerScreenY;
|
||||
size = endPanelControl ?
|
||||
(node.offsetTop + node.offsetHeight) - innerOffset :
|
||||
innerOffset - node.offsetTop;
|
||||
|
||||
this.setState({
|
||||
height: size
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// Rendering
|
||||
|
||||
render() {
|
||||
const vert = this.state.vert;
|
||||
const { startPanel, endPanel, endPanelControl, minSize,
|
||||
maxSize, splitterSize } = this.props;
|
||||
|
||||
let style = Object.assign({}, this.props.style);
|
||||
|
||||
// Calculate class names list.
|
||||
let classNames = ["split-box"];
|
||||
classNames.push(vert ? "vert" : "horz");
|
||||
if (this.props.className) {
|
||||
classNames = classNames.concat(this.props.className.split(" "));
|
||||
}
|
||||
|
||||
let leftPanelStyle;
|
||||
let rightPanelStyle;
|
||||
|
||||
// Set proper size for panels depending on the current state.
|
||||
if (vert) {
|
||||
leftPanelStyle = {
|
||||
maxWidth: endPanelControl ? null : maxSize,
|
||||
minWidth: endPanelControl ? null : minSize,
|
||||
width: endPanelControl ? null : this.state.width
|
||||
};
|
||||
rightPanelStyle = {
|
||||
maxWidth: endPanelControl ? maxSize : null,
|
||||
minWidth: endPanelControl ? minSize : null,
|
||||
width: endPanelControl ? this.state.width : null
|
||||
};
|
||||
} else {
|
||||
leftPanelStyle = {
|
||||
maxHeight: endPanelControl ? null : maxSize,
|
||||
minHeight: endPanelControl ? null : minSize,
|
||||
height: endPanelControl ? null : this.state.height
|
||||
};
|
||||
rightPanelStyle = {
|
||||
maxHeight: endPanelControl ? maxSize : null,
|
||||
minHeight: endPanelControl ? minSize : null,
|
||||
height: endPanelControl ? this.state.height : null
|
||||
};
|
||||
}
|
||||
|
||||
// Calculate splitter size
|
||||
let splitterStyle = {
|
||||
flex: "0 0 " + splitterSize + "px"
|
||||
};
|
||||
|
||||
return (
|
||||
dom.div({
|
||||
className: classNames.join(" "),
|
||||
style: style },
|
||||
startPanel ?
|
||||
dom.div({
|
||||
className: endPanelControl ? "uncontrolled" : "controlled",
|
||||
style: leftPanelStyle},
|
||||
startPanel
|
||||
) : null,
|
||||
Draggable({
|
||||
className: "splitter",
|
||||
style: splitterStyle,
|
||||
onStart: this.onStartMove,
|
||||
onStop: this.onStopMove,
|
||||
onMove: this.onMove
|
||||
}),
|
||||
endPanel ?
|
||||
dom.div({
|
||||
className: endPanelControl ? "controlled" : "uncontrolled",
|
||||
style: rightPanelStyle},
|
||||
endPanel
|
||||
) : null
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = SplitBox;
|
Loading…
Reference in New Issue
Block a user