aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMaximilian Hils <git@maximilianhils.com>2014-11-28 20:03:04 +0100
committerMaximilian Hils <git@maximilianhils.com>2014-11-28 20:03:04 +0100
commitdd1a45140c8a6cb3f6c5d7247120c05fa37cdf24 (patch)
tree88b171c7be75fe408ab647d8f5d3d74d5ae833b5
parentc39b6e4277357c9da1dfd5e3e8c41b5b3427e0ce (diff)
downloadmitmproxy-dd1a45140c8a6cb3f6c5d7247120c05fa37cdf24.tar.gz
mitmproxy-dd1a45140c8a6cb3f6c5d7247120c05fa37cdf24.tar.bz2
mitmproxy-dd1a45140c8a6cb3f6c5d7247120c05fa37cdf24.zip
web: add virtualscroll mixin
-rw-r--r--libmproxy/web/static/js/app.js119
-rw-r--r--web/gulpfile.js1
-rw-r--r--web/src/js/components/flowtable.jsx.js65
-rw-r--r--web/src/js/components/virtualscroll.jsx.js54
4 files changed, 143 insertions, 96 deletions
diff --git a/libmproxy/web/static/js/app.js b/libmproxy/web/static/js/app.js
index 64d6ba44..09fb71e6 100644
--- a/libmproxy/web/static/js/app.js
+++ b/libmproxy/web/static/js/app.js
@@ -698,6 +698,60 @@ $.ajaxPrefilter(function(options){
}
}
});
+var VirtualScrollMixin = {
+ getInitialState: function () {
+ return {
+ start: 0,
+ stop: 0
+ }
+ },
+ getPlaceholderTop: function () {
+ var style = {
+ height: this.state.start * this.props.rowHeight
+ };
+ var spacer = React.createElement("tr", {key: "placeholder-top", style: style});
+
+ if (this.state.start % 2 === 1) {
+ // fix even/odd rows
+ return [spacer, React.createElement("tr", {key: "placeholder-top-2"})];
+ } else {
+ return spacer;
+ }
+ },
+ getPlaceholderBottom: function (total) {
+ var style = {
+ height: Math.max(0, total - this.state.stop) * this.props.rowHeight
+ };
+ return React.createElement("tr", {key: "placeholder-bottom", style: style});
+ },
+ onScroll: function () {
+ var viewport = this.getDOMNode();
+ var top = viewport.scrollTop;
+ var height = viewport.offsetHeight;
+ var start = Math.floor(top / this.props.rowHeight);
+ var stop = start + Math.ceil(height / this.props.rowHeight);
+ this.setState({
+ start: start,
+ stop: stop
+ });
+ },
+ scrollRowIntoView: function(index, head_height){
+
+ var row_top = (index * this.props.rowHeight) + head_height;
+ var row_bottom = row_top + this.props.rowHeight;
+
+ var viewport = this.getDOMNode();
+ var viewport_top = viewport.scrollTop;
+ var viewport_bottom = viewport_top + viewport.offsetHeight;
+
+ // Account for pinned thead
+ if (row_top - head_height < viewport_top) {
+ viewport.scrollTop = row_top - head_height;
+ } else if (row_bottom > viewport_bottom) {
+ viewport.scrollTop = row_bottom - viewport.offsetHeight;
+ }
+ },
+};
var MainMenu = React.createClass({displayName: 'MainMenu',
statics: {
title: "Traffic",
@@ -993,12 +1047,10 @@ var FlowTableHead = React.createClass({displayName: 'FlowTableHead',
var ROW_HEIGHT = 32;
var FlowTable = React.createClass({displayName: 'FlowTable',
- mixins: [StickyHeadMixin, AutoScrollMixin],
+ mixins: [StickyHeadMixin, AutoScrollMixin, VirtualScrollMixin],
getInitialState: function () {
return {
- columns: all_columns,
- start: 0,
- stop: 0
+ columns: all_columns
};
},
componentWillMount: function () {
@@ -1015,46 +1067,26 @@ var FlowTable = React.createClass({displayName: 'FlowTable',
}
},
componentDidMount: function () {
- this.onScroll();
+ this.onScroll2();
},
- onScroll: function () {
+ getDefaultProps: function () {
+ return {
+ rowHeight: ROW_HEIGHT
+ };
+ },
+ onScroll2: function () {
this.adjustHead();
-
- var viewport = this.getDOMNode();
- var top = viewport.scrollTop;
- var height = viewport.offsetHeight;
- var start = Math.floor(top / ROW_HEIGHT);
- var stop = start + Math.ceil(height / ROW_HEIGHT);
- this.setState({
- start: start,
- stop: stop
- });
+ this.onScroll();
},
onChange: function () {
console.log("onChange");
this.forceUpdate();
},
scrollIntoView: function (flow) {
- // Now comes the fun part: Scroll the flow into the view.
- var viewport = this.getDOMNode();
- var thead_height = this.refs.body.getDOMNode().offsetTop;
-
- var flow_top = (this.props.view.index(flow) * ROW_HEIGHT) + thead_height;
-
- var viewport_top = viewport.scrollTop;
- var viewport_bottom = viewport_top + viewport.offsetHeight;
- var flow_bottom = flow_top + ROW_HEIGHT;
-
- // Account for pinned thead
-
-
- console.log("scrollInto", flow_top, flow_bottom, viewport_top, viewport_bottom, thead_height);
-
- if (flow_top - thead_height < viewport_top) {
- viewport.scrollTop = flow_top - thead_height;
- } else if (flow_bottom > viewport_bottom) {
- viewport.scrollTop = flow_bottom - viewport.offsetHeight;
- }
+ this.scrollRowIntoView(
+ this.props.view.index(flow),
+ this.refs.body.getDOMNode().offsetTop
+ );
},
render: function () {
var space_top = 0, space_bottom = 0, fix_nth_row = null;
@@ -1062,7 +1094,6 @@ var FlowTable = React.createClass({displayName: 'FlowTable',
if (this.props.view) {
var flows = this.props.view.flows;
var max = Math.min(flows.length, this.state.stop);
- console.log("render", this.props.view.flows.length, this.state.start, max - this.state.start, flows.length - this.state.stop);
for (var i = this.state.start; i < max; i++) {
var flow = flows[i];
@@ -1077,25 +1108,17 @@ var FlowTable = React.createClass({displayName: 'FlowTable',
)
);
}
-
- space_top = this.state.start * ROW_HEIGHT;
- space_bottom = Math.max(0, flows.length - this.state.stop) * ROW_HEIGHT;
- if(this.state.start % 2 === 1){
- fix_nth_row = React.createElement("tr", null);
- }
}
-
return (
- React.createElement("div", {className: "flow-table", onScroll: this.onScroll},
+ React.createElement("div", {className: "flow-table", onScroll: this.onScroll2},
React.createElement("table", null,
React.createElement(FlowTableHead, {ref: "head",
columns: this.state.columns}),
React.createElement("tbody", {ref: "body"},
- React.createElement("tr", {style: {height: space_top}}),
- fix_nth_row,
+ this.getPlaceholderTop(),
rows,
- React.createElement("tr", {style: {height: space_bottom}})
+ this.getPlaceholderBottom(flows.length)
)
)
)
diff --git a/web/gulpfile.js b/web/gulpfile.js
index d106ecd1..db723eec 100644
--- a/web/gulpfile.js
+++ b/web/gulpfile.js
@@ -43,6 +43,7 @@ var path = {
'js/stores/flowstore.js',
'js/connection.js',
'js/components/utils.jsx.js',
+ 'js/components/virtualscroll.jsx.js',
'js/components/header.jsx.js',
'js/components/flowtable-columns.jsx.js',
'js/components/flowtable.jsx.js',
diff --git a/web/src/js/components/flowtable.jsx.js b/web/src/js/components/flowtable.jsx.js
index 6d0f5ee7..f608f41d 100644
--- a/web/src/js/components/flowtable.jsx.js
+++ b/web/src/js/components/flowtable.jsx.js
@@ -39,12 +39,10 @@ var FlowTableHead = React.createClass({
var ROW_HEIGHT = 32;
var FlowTable = React.createClass({
- mixins: [StickyHeadMixin, AutoScrollMixin],
+ mixins: [StickyHeadMixin, AutoScrollMixin, VirtualScrollMixin],
getInitialState: function () {
return {
- columns: all_columns,
- start: 0,
- stop: 0
+ columns: all_columns
};
},
componentWillMount: function () {
@@ -61,46 +59,26 @@ var FlowTable = React.createClass({
}
},
componentDidMount: function () {
- this.onScroll();
+ this.onScroll2();
},
- onScroll: function () {
+ getDefaultProps: function () {
+ return {
+ rowHeight: ROW_HEIGHT
+ };
+ },
+ onScroll2: function () {
this.adjustHead();
-
- var viewport = this.getDOMNode();
- var top = viewport.scrollTop;
- var height = viewport.offsetHeight;
- var start = Math.floor(top / ROW_HEIGHT);
- var stop = start + Math.ceil(height / ROW_HEIGHT);
- this.setState({
- start: start,
- stop: stop
- });
+ this.onScroll();
},
onChange: function () {
console.log("onChange");
this.forceUpdate();
},
scrollIntoView: function (flow) {
- // Now comes the fun part: Scroll the flow into the view.
- var viewport = this.getDOMNode();
- var thead_height = this.refs.body.getDOMNode().offsetTop;
-
- var flow_top = (this.props.view.index(flow) * ROW_HEIGHT) + thead_height;
-
- var viewport_top = viewport.scrollTop;
- var viewport_bottom = viewport_top + viewport.offsetHeight;
- var flow_bottom = flow_top + ROW_HEIGHT;
-
- // Account for pinned thead
-
-
- console.log("scrollInto", flow_top, flow_bottom, viewport_top, viewport_bottom, thead_height);
-
- if (flow_top - thead_height < viewport_top) {
- viewport.scrollTop = flow_top - thead_height;
- } else if (flow_bottom > viewport_bottom) {
- viewport.scrollTop = flow_bottom - viewport.offsetHeight;
- }
+ this.scrollRowIntoView(
+ this.props.view.index(flow),
+ this.refs.body.getDOMNode().offsetTop
+ );
},
render: function () {
var space_top = 0, space_bottom = 0, fix_nth_row = null;
@@ -108,7 +86,6 @@ var FlowTable = React.createClass({
if (this.props.view) {
var flows = this.props.view.flows;
var max = Math.min(flows.length, this.state.stop);
- console.log("render", this.props.view.flows.length, this.state.start, max - this.state.start, flows.length - this.state.stop);
for (var i = this.state.start; i < max; i++) {
var flow = flows[i];
@@ -123,25 +100,17 @@ var FlowTable = React.createClass({
/>
);
}
-
- space_top = this.state.start * ROW_HEIGHT;
- space_bottom = Math.max(0, flows.length - this.state.stop) * ROW_HEIGHT;
- if(this.state.start % 2 === 1){
- fix_nth_row = <tr></tr>;
- }
}
-
return (
- <div className="flow-table" onScroll={this.onScroll}>
+ <div className="flow-table" onScroll={this.onScroll2}>
<table>
<FlowTableHead ref="head"
columns={this.state.columns}/>
<tbody ref="body">
- <tr style={{height: space_top}}></tr>
- { fix_nth_row }
+ { this.getPlaceholderTop() }
{rows}
- <tr style={{height: space_bottom}}></tr>
+ { this.getPlaceholderBottom(flows.length) }
</tbody>
</table>
</div>
diff --git a/web/src/js/components/virtualscroll.jsx.js b/web/src/js/components/virtualscroll.jsx.js
new file mode 100644
index 00000000..ec3daa41
--- /dev/null
+++ b/web/src/js/components/virtualscroll.jsx.js
@@ -0,0 +1,54 @@
+var VirtualScrollMixin = {
+ getInitialState: function () {
+ return {
+ start: 0,
+ stop: 0
+ }
+ },
+ getPlaceholderTop: function () {
+ var style = {
+ height: this.state.start * this.props.rowHeight
+ };
+ var spacer = <tr key="placeholder-top" style={style}></tr>;
+
+ if (this.state.start % 2 === 1) {
+ // fix even/odd rows
+ return [spacer, <tr key="placeholder-top-2"></tr>];
+ } else {
+ return spacer;
+ }
+ },
+ getPlaceholderBottom: function (total) {
+ var style = {
+ height: Math.max(0, total - this.state.stop) * this.props.rowHeight
+ };
+ return <tr key="placeholder-bottom" style={style}></tr>;
+ },
+ onScroll: function () {
+ var viewport = this.getDOMNode();
+ var top = viewport.scrollTop;
+ var height = viewport.offsetHeight;
+ var start = Math.floor(top / this.props.rowHeight);
+ var stop = start + Math.ceil(height / this.props.rowHeight);
+ this.setState({
+ start: start,
+ stop: stop
+ });
+ },
+ scrollRowIntoView: function(index, head_height){
+
+ var row_top = (index * this.props.rowHeight) + head_height;
+ var row_bottom = row_top + this.props.rowHeight;
+
+ var viewport = this.getDOMNode();
+ var viewport_top = viewport.scrollTop;
+ var viewport_bottom = viewport_top + viewport.offsetHeight;
+
+ // Account for pinned thead
+ if (row_top - head_height < viewport_top) {
+ viewport.scrollTop = row_top - head_height;
+ } else if (row_bottom > viewport_bottom) {
+ viewport.scrollTop = row_bottom - viewport.offsetHeight;
+ }
+ },
+}; \ No newline at end of file