From e66f240e8148fd63e6739950bd773b4052d91501 Mon Sep 17 00:00:00 2001 From: Maximilian Hils Date: Thu, 18 Sep 2014 23:22:02 +0200 Subject: [PATCH] add splitter --- libmproxy/web/static/css/app.css | 88 +++++++----------------- libmproxy/web/static/js/app.js | 89 ++++++++++++++++++++++++- web/gulpfile.js | 1 + web/src/css/flowdetail.less | 1 + web/src/css/header.less | 30 --------- web/src/css/layout.less | 14 +++- web/src/js/components/flowdetail.jsx.js | 4 +- web/src/js/components/mainview.jsx.js | 1 + web/src/js/components/proxyapp.jsx.js | 2 + web/src/js/components/utils.jsx.js | 80 ++++++++++++++++++++++ 10 files changed, 210 insertions(+), 100 deletions(-) create mode 100644 web/src/js/components/utils.jsx.js diff --git a/libmproxy/web/static/css/app.css b/libmproxy/web/static/css/app.css index e7564ee83..4c8727119 100644 --- a/libmproxy/web/static/css/app.css +++ b/libmproxy/web/static/css/app.css @@ -1,12 +1,10 @@ html { - -moz-box-sizing: border-box; - box-sizing: border-box; + box-sizing: border-box; } *, *:before, *:after { - -moz-box-sizing: inherit; - box-sizing: inherit; + box-sizing: inherit; } .resource-icon { width: 32px; @@ -50,44 +48,36 @@ body, overflow: hidden; } #container { - display: -webkit-flex; - display: -ms-flexbox; display: flex; - -webkit-flex-direction: column; - -ms-flex-direction: column; - flex-direction: column; + flex-direction: column; } #container > header, #container > footer, #container > .eventlog { - -webkit-flex: 0 0 auto; - -ms-flex: 0 0 auto; - flex: 0 0 auto; + flex: 0 0 auto; } .main-view { - -webkit-flex: 1 1 auto; - -ms-flex: 1 1 auto; - flex: 1 1 auto; - display: -webkit-flex; - display: -ms-flexbox; + flex: 1 1 auto; display: flex; - -webkit-flex-direction: row; - -ms-flex-direction: row; - flex-direction: row; + flex-direction: row; } .main-view.vertical { - -webkit-flex-direction: column; - -ms-flex-direction: column; - flex-direction: column; + flex-direction: column; } .main-view .flow-detail, .main-view .flow-table { - -webkit-flex: 1 1 auto; - -ms-flex: 1 1 auto; - flex: 1 1 auto; - -webkit-flex-basis: 50%; - -ms-flex-preferred-size: 50%; - flex-basis: 50%; + flex: 1 1 auto; +} +.splitter { + flex: 0 0 4px; + background-color: #ccc; + content: "X"; +} +.splitter-x { + cursor: col-resize; +} +.splitter-y { + cursor: row-resize; } .nav-tabs { border-bottom: solid #a6a6a6 1px; @@ -120,37 +110,6 @@ body, } header { background-color: white; - /* - nav { - border-bottom: solid @separator-color 1px; - - a { - display: inline-block; - padding: 3px 14px; - margin: 0 2px -1px; - border: solid transparent 1px; - //text-transform: uppercase; - //font-family: Lato; - - &.active { - border-color: @separator-color; - border-bottom-color: white; - } - &.active, &:hover { - text-decoration: none; - } - &.special { - @special-color: #396cad; - color: white; - background-color: @special-color; - border-bottom-color: @special-color; - &:hover { - background-color: lighten(@special-color, 10%); - } - } - } - } -*/ } header .title-bar { line-height: 25px; @@ -215,15 +174,14 @@ header .menu { text-align: right; } .flow-detail { + width: 100%; overflow: auto; } .flow-detail nav { background-color: #F2F2F2; } .eventlog { - -webkit-flex: 0 0 auto; - -ms-flex: 0 0 auto; - flex: 0 0 auto; + flex: 0 0 auto; margin: 0; border-radius: 0; height: 200px; @@ -233,4 +191,6 @@ header .menu { footer { box-shadow: 0 -1px 3px #d3d3d3; padding: 0px 10px 3px; -} \ No newline at end of file +} + +/*# sourceMappingURL=../css/app.css.map */ \ No newline at end of file diff --git a/libmproxy/web/static/js/app.js b/libmproxy/web/static/js/app.js index 5e1760903..5e9654bde 100644 --- a/libmproxy/web/static/js/app.js +++ b/libmproxy/web/static/js/app.js @@ -381,6 +381,86 @@ var Connection = new _Connection(location.origin + "/updates"); /** @jsx React.DOM */ +//React utils. For other utilities, see ../utils.js + +var Splitter = React.createClass({displayName: 'Splitter', + getDefaultProps: function () { + return { + axis: "x" + } + }, + getInitialState: function(){ + return { + applied: false, + startX: false, + startY: false + }; + }, + onMouseDown: function(e){ + this.setState({ + startX: e.pageX, + startY: e.pageY + }); + window.addEventListener("mousemove",this.onMouseMove); + window.addEventListener("mouseup",this.onMouseUp); + }, + onMouseUp: function(e){ + window.removeEventListener("mouseup",this.onMouseUp); + window.removeEventListener("mousemove",this.onMouseMove); + + var node = this.getDOMNode(); + var prev = node.previousElementSibling; + var next = node.nextElementSibling; + this.getDOMNode().style.transform=""; + + var dX = e.pageX-this.state.startX; + var dY = e.pageY-this.state.startY; + var flexBasis; + if(this.props.axis === "x"){ + flexBasis = prev.offsetWidth + dX; + } else { + flexBasis = prev.offsetHeight + dY; + } + + prev.style.flex = "0 0 "+Math.max(0, flexBasis)+"px"; + next.style.flex = "1 1 auto"; + + this.setState({ + applied: true + }); + }, + onMouseMove: function(e){ + var dX = 0, dY = 0; + if(this.props.axis === "x"){ + dX = e.pageX-this.state.startX; + } else { + dY = e.pageY-this.state.startY; + } + this.getDOMNode().style.transform = "translate("+dX+"px,"+dY+"px)"; + }, + reset: function(){ + if(!this.state.applied){ + return; + } + var node = this.getDOMNode(); + var prev = node.previousElementSibling; + var next = node.nextElementSibling; + + prev.style.flex = ""; + next.style.flex = ""; + }, + render: function(){ + var className = "splitter"; + if(this.props.axis === "x"){ + className += " splitter-x"; + } else { + className += " splitter-y"; + } + return React.DOM.div({className: className, onMouseDown: this.onMouseDown}); + } +}); +/** @jsx React.DOM */ + var MainMenu = React.createClass({displayName: 'MainMenu', statics: { title: "Traffic", @@ -745,13 +825,13 @@ var FlowDetailConnectionInfo = React.createClass({displayName: 'FlowDetailConnec render: function(){ return React.DOM.div(null, "details"); } -}) +}); var tabs = { request: FlowDetailRequest, response: FlowDetailResponse, details: FlowDetailConnectionInfo -} +}; var FlowDetail = React.createClass({displayName: 'FlowDetail', mixins: [StickyHeadMixin], @@ -830,6 +910,7 @@ var MainView = React.createClass({displayName: 'MainView', flows: this.state.flows, selectFlow: this.selectFlow, selected: selected}), + Splitter(null), details ) ); @@ -918,6 +999,7 @@ var ProxyAppMain = React.createClass({displayName: 'ProxyAppMain', React.DOM.div({id: "container"}, Header({settings: this.state.settings}), this.props.activeRouteHandler({settings: this.state.settings}), + Splitter({axis: "y"}), this.state.settings.showEventLog ? EventLog(null) : null, Footer({settings: this.state.settings}) ) @@ -938,7 +1020,8 @@ var ProxyApp = ( Route({path: "/", handler: ProxyAppMain}, Route({name: "flows", path: "flows", handler: MainView}), Route({name: "flow", path: "flows/:flowId/:detailTab", handler: MainView}), - Route({name: "reports", handler: Reports}) + Route({name: "reports", handler: Reports}), + Redirect({path: "/", to: "flows"}) ) ) ); diff --git a/web/gulpfile.js b/web/gulpfile.js index fc78bd1f6..584cc7d38 100644 --- a/web/gulpfile.js +++ b/web/gulpfile.js @@ -41,6 +41,7 @@ var path = { 'js/stores/eventlogstore.js', 'js/stores/flowstore.js', 'js/connection.js', + 'js/components/utils.jsx.js', 'js/components/header.jsx.js', 'js/components/flowtable-columns.jsx.js', 'js/components/flowtable.jsx.js', diff --git a/web/src/css/flowdetail.less b/web/src/css/flowdetail.less index 19eeecc0b..883343914 100644 --- a/web/src/css/flowdetail.less +++ b/web/src/css/flowdetail.less @@ -1,4 +1,5 @@ .flow-detail { + width: 100%; overflow: auto; nav { diff --git a/web/src/css/header.less b/web/src/css/header.less index 66e8ac8ae..5f91beaf8 100644 --- a/web/src/css/header.less +++ b/web/src/css/header.less @@ -7,37 +7,7 @@ header { } @separator-color: lighten(grey, 15%); -/* - nav { - border-bottom: solid @separator-color 1px; - a { - display: inline-block; - padding: 3px 14px; - margin: 0 2px -1px; - border: solid transparent 1px; - //text-transform: uppercase; - //font-family: Lato; - - &.active { - border-color: @separator-color; - border-bottom-color: white; - } - &.active, &:hover { - text-decoration: none; - } - &.special { - @special-color: #396cad; - color: white; - background-color: @special-color; - border-bottom-color: @special-color; - &:hover { - background-color: lighten(@special-color, 10%); - } - } - } - } -*/ .menu { padding: 10px; border-bottom: solid @separator-color 1px; diff --git a/web/src/css/layout.less b/web/src/css/layout.less index 6e4abd24f..55d6f61e3 100644 --- a/web/src/css/layout.less +++ b/web/src/css/layout.less @@ -25,7 +25,19 @@ html, body, #container { .flow-detail, .flow-table { flex: 1 1 auto; - flex-basis: 50%; } +} + +.splitter { + flex: 0 0 4px; + background-color: #ccc; + content: "X"; + +} +.splitter-x { + cursor: col-resize; +} +.splitter-y { + cursor: row-resize; } \ No newline at end of file diff --git a/web/src/js/components/flowdetail.jsx.js b/web/src/js/components/flowdetail.jsx.js index 253084c23..acf5e6f4f 100644 --- a/web/src/js/components/flowdetail.jsx.js +++ b/web/src/js/components/flowdetail.jsx.js @@ -39,13 +39,13 @@ var FlowDetailConnectionInfo = React.createClass({ render: function(){ return
details
; } -}) +}); var tabs = { request: FlowDetailRequest, response: FlowDetailResponse, details: FlowDetailConnectionInfo -} +}; var FlowDetail = React.createClass({ mixins: [StickyHeadMixin], diff --git a/web/src/js/components/mainview.jsx.js b/web/src/js/components/mainview.jsx.js index 10ecfed0e..79eb58ea4 100644 --- a/web/src/js/components/mainview.jsx.js +++ b/web/src/js/components/mainview.jsx.js @@ -62,6 +62,7 @@ var MainView = React.createClass({ flows={this.state.flows} selectFlow={this.selectFlow} selected={selected} /> + {details} ); diff --git a/web/src/js/components/proxyapp.jsx.js b/web/src/js/components/proxyapp.jsx.js index 6895b852f..b45ec9114 100644 --- a/web/src/js/components/proxyapp.jsx.js +++ b/web/src/js/components/proxyapp.jsx.js @@ -27,6 +27,7 @@ var ProxyAppMain = React.createClass({
+ {this.state.settings.showEventLog ? : null}
@@ -48,6 +49,7 @@ var ProxyApp = ( + ); \ No newline at end of file diff --git a/web/src/js/components/utils.jsx.js b/web/src/js/components/utils.jsx.js new file mode 100644 index 000000000..d986e24a7 --- /dev/null +++ b/web/src/js/components/utils.jsx.js @@ -0,0 +1,80 @@ +/** @jsx React.DOM */ + +//React utils. For other utilities, see ../utils.js + +var Splitter = React.createClass({ + getDefaultProps: function () { + return { + axis: "x" + } + }, + getInitialState: function(){ + return { + applied: false, + startX: false, + startY: false + }; + }, + onMouseDown: function(e){ + this.setState({ + startX: e.pageX, + startY: e.pageY + }); + window.addEventListener("mousemove",this.onMouseMove); + window.addEventListener("mouseup",this.onMouseUp); + }, + onMouseUp: function(e){ + window.removeEventListener("mouseup",this.onMouseUp); + window.removeEventListener("mousemove",this.onMouseMove); + + var node = this.getDOMNode(); + var prev = node.previousElementSibling; + var next = node.nextElementSibling; + this.getDOMNode().style.transform=""; + + var dX = e.pageX-this.state.startX; + var dY = e.pageY-this.state.startY; + var flexBasis; + if(this.props.axis === "x"){ + flexBasis = prev.offsetWidth + dX; + } else { + flexBasis = prev.offsetHeight + dY; + } + + prev.style.flex = "0 0 "+Math.max(0, flexBasis)+"px"; + next.style.flex = "1 1 auto"; + + this.setState({ + applied: true + }); + }, + onMouseMove: function(e){ + var dX = 0, dY = 0; + if(this.props.axis === "x"){ + dX = e.pageX-this.state.startX; + } else { + dY = e.pageY-this.state.startY; + } + this.getDOMNode().style.transform = "translate("+dX+"px,"+dY+"px)"; + }, + reset: function(){ + if(!this.state.applied){ + return; + } + var node = this.getDOMNode(); + var prev = node.previousElementSibling; + var next = node.nextElementSibling; + + prev.style.flex = ""; + next.style.flex = ""; + }, + render: function(){ + var className = "splitter"; + if(this.props.axis === "x"){ + className += " splitter-x"; + } else { + className += " splitter-y"; + } + return
; + } +}); \ No newline at end of file