aboutsummaryrefslogtreecommitdiffstats
path: root/web/src/js
diff options
context:
space:
mode:
authorJason <jason.daurus@gmail.com>2016-06-09 17:46:14 +0800
committerJason <jason.daurus@gmail.com>2016-06-09 17:52:30 +0800
commitf306cfa8b6445dd04c5f7188d1a5022bcb747a62 (patch)
tree8b229ee951b4e4756c3596b66cefee632bfb8870 /web/src/js
parent52754f40c29b95b8355627036660f7e182007e74 (diff)
downloadmitmproxy-f306cfa8b6445dd04c5f7188d1a5022bcb747a62.tar.gz
mitmproxy-f306cfa8b6445dd04c5f7188d1a5022bcb747a62.tar.bz2
mitmproxy-f306cfa8b6445dd04c5f7188d1a5022bcb747a62.zip
[web] separate flowtable to multiple files
Diffstat (limited to 'web/src/js')
-rw-r--r--web/src/js/app.jsx (renamed from web/src/js/app.js)0
-rw-r--r--web/src/js/components/FlowTable.jsx120
-rw-r--r--web/src/js/components/FlowTable/FlowColumns.jsx137
-rw-r--r--web/src/js/components/FlowTable/FlowRow.jsx28
-rw-r--r--web/src/js/components/FlowTable/FlowTableHead.jsx43
-rw-r--r--web/src/js/components/MainView.jsx (renamed from web/src/js/components/MainView.js)47
-rw-r--r--web/src/js/components/ProxyApp.jsx (renamed from web/src/js/components/ProxyApp.js)3
-rw-r--r--web/src/js/components/flowtable-columns.js131
-rw-r--r--web/src/js/components/flowtable.js201
-rw-r--r--web/src/js/ducks/flows.js6
10 files changed, 361 insertions, 355 deletions
diff --git a/web/src/js/app.js b/web/src/js/app.jsx
index 8fa52a00..8fa52a00 100644
--- a/web/src/js/app.js
+++ b/web/src/js/app.jsx
diff --git a/web/src/js/components/FlowTable.jsx b/web/src/js/components/FlowTable.jsx
new file mode 100644
index 00000000..eddeed62
--- /dev/null
+++ b/web/src/js/components/FlowTable.jsx
@@ -0,0 +1,120 @@
+import React, { PropTypes } from 'react'
+import ReactDOM from 'react-dom'
+import shallowEqual from 'shallowequal'
+import AutoScroll from './helpers/AutoScroll'
+import { calcVScroll } from './helpers/VirtualScroll'
+import FlowTableHead from './FlowTable/FlowTableHead'
+import FlowRow from './FlowTable/FlowRow'
+import Filt from "../filt/filt"
+
+class FlowTable extends React.Component {
+
+ static propTypes = {
+ onSelect: PropTypes.func.isRequired,
+ flows: PropTypes.array.isRequired,
+ rowHeight: PropTypes.number,
+ highlight: PropTypes.string,
+ selected: PropTypes.object,
+ }
+
+ static defaultProps = {
+ rowHeight: 32,
+ }
+
+ constructor(props, context) {
+ super(props, context)
+
+ this.state = { vScroll: calcVScroll() }
+ this.onViewportUpdate = this.onViewportUpdate.bind(this)
+ }
+
+ componentWillMount() {
+ window.addEventListener('resize', this.onViewportUpdate)
+ }
+
+ componentWillUnmount() {
+ window.removeEventListener('resize', this.onViewportUpdate)
+ }
+
+ componentDidUpdate() {
+ this.onViewportUpdate()
+
+ if (!this.shouldScrollIntoView) {
+ return
+ }
+
+ this.shouldScrollIntoView = false
+
+ const { rowHeight, flows, selected } = this.props
+ const viewport = ReactDOM.findDOMNode(this)
+ const head = ReactDOM.findDOMNode(this.refs.head)
+
+ const headHeight = head ? head.offsetHeight : 0
+
+ const rowTop = (flows.indexOf(selected) * rowHeight) + headHeight
+ const rowBottom = rowTop + rowHeight
+
+ const viewportTop = viewport.scrollTop
+ const viewportHeight = viewport.offsetHeight
+
+ // Account for pinned thead
+ if (rowTop - headHeight < viewportTop) {
+ viewport.scrollTop = rowTop - headHeight
+ } else if (rowBottom > viewportTop + viewportHeight) {
+ viewport.scrollTop = rowBottom - viewportHeight
+ }
+ }
+
+ componentWillReceiveProps(nextProps) {
+ if (nextProps.selected && nextProps.selected !== this.props.selected) {
+ this.shouldScrollIntoView = true
+ }
+ }
+
+ onViewportUpdate() {
+ const viewport = ReactDOM.findDOMNode(this)
+ const viewportTop = viewport.scrollTop
+
+ const vScroll = calcVScroll({
+ viewportTop,
+ viewportHeight: viewport.offsetHeight,
+ itemCount: this.props.flows.length,
+ rowHeight: this.props.rowHeight,
+ })
+
+ if (this.state.viewportTop !== viewportTop || !shallowEqual(this.state.vScroll, vScroll)) {
+ this.setState({ vScroll, viewportTop })
+ }
+ }
+
+ render() {
+ const { vScroll, viewportTop } = this.state
+ const { flows, selected, highlight } = this.props
+ const isHighlighted = highlight ? Filt.parse(highlight) : () => false
+
+ return (
+ <div className="flow-table" onScroll={this.onViewportUpdate}>
+ <table>
+ <thead ref="head" style={{ transform: `translateY(${viewportTop}px)` }}>
+ <FlowTableHead />
+ </thead>
+ <tbody>
+ <tr style={{ height: vScroll.paddingTop }}></tr>
+ {flows.slice(vScroll.start, vScroll.end).map(flow => (
+ <FlowRow
+ key={flow.id}
+ flow={flow}
+ selected={flow === selected}
+ highlighted={isHighlighted(flow)}
+ onSelect={this.props.onSelect}
+ />
+ ))}
+ <tr style={{ height: vScroll.paddingBottom }}></tr>
+ </tbody>
+ </table>
+ </div>
+ )
+ }
+}
+
+export default AutoScroll(FlowTable)
diff --git a/web/src/js/components/FlowTable/FlowColumns.jsx b/web/src/js/components/FlowTable/FlowColumns.jsx
new file mode 100644
index 00000000..11c0796c
--- /dev/null
+++ b/web/src/js/components/FlowTable/FlowColumns.jsx
@@ -0,0 +1,137 @@
+import React, { Component } from 'react'
+import classnames from 'classnames'
+import { RequestUtils, ResponseUtils } from '../../flow/utils.js'
+import { formatSize, formatTimeDelta } from '../../utils.js'
+
+export function TLSColumn({ flow }) {
+ return (
+ <td className={classnames('col-tls', flow.request.scheme === 'https' ? 'col-tls-https' : 'col-tls-http')}></td>
+ )
+}
+
+TLSColumn.sortKeyFun = flow => flow.request.scheme
+TLSColumn.headerClass = 'col-tls'
+TLSColumn.headerName = ''
+
+export function IconColumn({ flow }) {
+ return (
+ <td className="col-icon">
+ <div className={classnames('resource-icon', IconColumn.getIcon(flow))}></div>
+ </td>
+ )
+}
+
+IconColumn.headerClass = 'col-icon'
+IconColumn.headerName = ''
+
+IconColumn.getIcon = flow => {
+ if (!flow.response) {
+ return 'resource-icon-plain'
+ }
+
+ var contentType = ResponseUtils.getContentType(flow.response) || ''
+
+ // @todo We should assign a type to the flow somewhere else.
+ if (flow.response.status_code === 304) {
+ return 'resource-icon-not-modified'
+ }
+ if (300 <= flow.response.status_code && flow.response.status_code < 400) {
+ return 'resource-icon-redirect'
+ }
+ if (contentType.indexOf('image') >= 0) {
+ return 'resource-icon-image'
+ }
+ if (contentType.indexOf('javascript') >= 0) {
+ return 'resource-icon-js'
+ }
+ if (contentType.indexOf('css') >= 0) {
+ return 'resource-icon-css'
+ }
+ if (contentType.indexOf('html') >= 0) {
+ return 'resource-icon-document'
+ }
+
+ return 'resource-icon-plain'
+}
+
+export function PathColumn({ flow }) {
+ return (
+ <td className="col-path">
+ {flow.request.is_replay && (
+ <i className="fa fa-fw fa-repeat pull-right"></i>
+ )}
+ {flow.intercepted && (
+ <i className="fa fa-fw fa-pause pull-right"></i>
+ )}
+ {RequestUtils.pretty_url(flow.request)}
+ </td>
+ )
+}
+
+PathColumn.sortKeyFun = flow => RequestUtils.pretty_url(flow.request)
+PathColumn.headerClass = 'col-path'
+PathColumn.headerName = 'Path'
+
+export function MethodColumn({ flow }) {
+ return (
+ <td className="col-method">{flow.request.method}</td>
+ )
+}
+
+MethodColumn.sortKeyFun = flow => flow.request.method
+MethodColumn.headerClass = 'col-method'
+MethodColumn.headerName = 'Method'
+
+export function StatusColumn({ flow }) {
+ return (
+ <td className="col-status">{flow.response && flow.response.status_code}</td>
+ )
+}
+
+StatusColumn.sortKeyFun = flow => flow.response && flow.response.status_code
+StatusColumn.headerClass = 'col-status'
+StatusColumn.headerName = 'Status'
+
+export function SizeColumn({ flow }) {
+ return (
+ <td className="col-size">{formatSize(SizeColumn.getTotalSize(flow))}</td>
+ )
+}
+
+SizeColumn.sortKeyFun = flow => {
+ let total = flow.request.contentLength
+ if (flow.response) {
+ total += flow.response.contentLength || 0
+ }
+ return total
+}
+
+SizeColumn.getTotalSize = SizeColumn.sortKeyFun
+SizeColumn.headerClass = 'col-size'
+SizeColumn.headerName = 'Size'
+
+export function TimeColumn({ flow }) {
+ return (
+ <td className="col-time">
+ {flow.response ? (
+ formatTimeDelta(1000 * (flow.response.timestamp_end - flow.request.timestamp_start))
+ ) : (
+ '...'
+ )}
+ </td>
+ )
+}
+
+TimeColumn.sortKeyFun = flow => flow.response && flow.response.timestamp_end - flow.request.timestamp_start
+TimeColumn.headerClass = 'col-time'
+TimeColumn.headerName = 'Time'
+
+export default [
+ TLSColumn,
+ IconColumn,
+ PathColumn,
+ MethodColumn,
+ StatusColumn,
+ SizeColumn,
+ TimeColumn,
+]
diff --git a/web/src/js/components/FlowTable/FlowRow.jsx b/web/src/js/components/FlowTable/FlowRow.jsx
new file mode 100644
index 00000000..749bc0ce
--- /dev/null
+++ b/web/src/js/components/FlowTable/FlowRow.jsx
@@ -0,0 +1,28 @@
+import React, { PropTypes } from 'react'
+import classnames from 'classnames'
+import columns from './FlowColumns'
+
+FlowRow.propTypes = {
+ onSelect: PropTypes.func.isRequired,
+ flow: PropTypes.object.isRequired,
+ highlighted: PropTypes.bool,
+ selected: PropTypes.bool,
+}
+
+export default function FlowRow({ flow, selected, highlighted, onSelect }) {
+ const className = classnames({
+ 'selected': selected,
+ 'highlighted': highlighted,
+ 'intercepted': flow.intercepted,
+ 'has-request': flow.request,
+ 'has-response': flow.response,
+ })
+
+ return (
+ <tr className={className} onClick={() => onSelect(flow)}>
+ {columns.map(Column => (
+ <Column key={Column.name} flow={flow}/>
+ ))}
+ </tr>
+ )
+}
diff --git a/web/src/js/components/FlowTable/FlowTableHead.jsx b/web/src/js/components/FlowTable/FlowTableHead.jsx
new file mode 100644
index 00000000..a46219d1
--- /dev/null
+++ b/web/src/js/components/FlowTable/FlowTableHead.jsx
@@ -0,0 +1,43 @@
+import React, { PropTypes } from 'react'
+import { bindActionCreators } from 'redux'
+import { connect } from 'react-redux'
+import classnames from 'classnames'
+import columns from './FlowColumns'
+
+import { setSort } from "../../ducks/flows"
+
+FlowTableHead.propTypes = {
+ onSort: PropTypes.func.isRequired,
+ sortDesc: React.PropTypes.bool.isRequired,
+ sortColumn: React.PropTypes.string,
+}
+
+function FlowTableHead({ sortColumn, sortDesc, onSort }) {
+ const sortType = sortDesc ? 'sort-desc' : 'sort-asc'
+
+ return (
+ <tr>
+ {columns.map(Column => (
+ <th className={classnames(Column.headerClass, sortColumn === Column.name && sortType)}
+ key={Column.name}
+ onClick={() => onClick(Column)}>
+ {Column.headerName}
+ </th>
+ ))}
+ </tr>
+ )
+
+ function onClick(Column) {
+ onSort({ sortColumn: Column.name, sortDesc: Column.name !== sortColumn ? false : !sortDesc })
+ }
+}
+
+export default connect(
+ state => ({
+ sortDesc: state.flows.sort.sortDesc,
+ sortColumn: state.flows.sort.sortColumn,
+ }),
+ dispatch => bindActionCreators({
+ onSort: setSort,
+ }, dispatch)
+)(FlowTableHead)
diff --git a/web/src/js/components/MainView.js b/web/src/js/components/MainView.jsx
index 6172ce77..dbea76e5 100644
--- a/web/src/js/components/MainView.js
+++ b/web/src/js/components/MainView.jsx
@@ -1,16 +1,22 @@
-import React, { Component } from "react"
-
-import { FlowActions } from "../actions.js"
-import { Query } from "../actions.js"
-import { Key } from "../utils.js"
-import { Splitter } from "./common.js"
-import FlowTable from "./flowtable.js"
-import FlowView from "./flowview/index.js"
+import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux'
-import { selectFlow, setFilter, setHighlight } from "../ducks/flows"
+import { bindActionCreators } from 'redux'
+
+import { FlowActions } from '../actions.js'
+import { Query } from '../actions.js'
+import { Key } from '../utils.js'
+import { Splitter } from './common.js'
+import FlowTable from './FlowTable'
+import FlowView from './flowview/index.js'
+import { selectFlow, setFilter, setHighlight } from '../ducks/flows'
class MainView extends Component {
+ static propTypes = {
+ highlight: PropTypes.string,
+ sort: PropTypes.object,
+ }
+
/**
* @todo move to actions
* @todo replace with mapStateToProps
@@ -33,9 +39,9 @@ class MainView extends Component {
*/
selectFlow(flow) {
if (flow) {
- this.props.updateLocation(`/flows/${flow.id}/${this.props.routeParams.detailTab || "request"}`)
+ this.props.updateLocation(`/flows/${flow.id}/${this.props.routeParams.detailTab || 'request'}`)
} else {
- this.props.updateLocation("/flows")
+ this.props.updateLocation('/flows')
}
}
@@ -143,20 +149,22 @@ class MainView extends Component {
case Key.SHIFT:
break
default:
- console.debug("keydown", e.keyCode)
+ console.debug('keydown', e.keyCode)
return
}
e.preventDefault()
}
render() {
- const { selectedFlow } = this.props
+ const { flows, selectedFlow, highlight, sort } = this.props
return (
<div className="main-view">
<FlowTable
ref="flowTable"
- selectFlow={flow => this.selectFlow(flow)}
+ flows={flows}
selected={selectedFlow}
+ highlight={highlight}
+ onSelect={flow => this.selectFlow(flow)}
/>
{selectedFlow && [
<Splitter key="splitter"/>,
@@ -178,14 +186,15 @@ export default connect(
state => ({
flows: state.flows.view,
filter: state.flows.filter,
+ sort: state.flows.sort,
highlight: state.flows.highlight,
selectedFlow: state.flows.all.byId[state.flows.selected[0]]
}),
- dispatch => ({
- selectFlow: flowId => dispatch(selectFlow(flowId)),
- setFilter: filter => dispatch(setFilter(filter)),
- setHighlight: highlight => dispatch(setHighlight(highlight))
- }),
+ dispatch => bindActionCreators({
+ selectFlow,
+ setFilter,
+ setHighlight,
+ }, dispatch),
undefined,
{ withRef: true }
)(MainView)
diff --git a/web/src/js/components/ProxyApp.js b/web/src/js/components/ProxyApp.jsx
index 71a7bf9b..9f497a7d 100644
--- a/web/src/js/components/ProxyApp.js
+++ b/web/src/js/components/ProxyApp.jsx
@@ -31,6 +31,7 @@ class ProxyAppMain extends Component {
this.state = { settings: this.settingsStore.dict }
+ this.focus = this.focus.bind(this)
this.onKeyDown = this.onKeyDown.bind(this)
this.updateLocation = this.updateLocation.bind(this)
this.onSettingsChange = this.onSettingsChange.bind(this)
@@ -45,7 +46,7 @@ class ProxyAppMain extends Component {
}
const query = this.props.location.query
for (const key of Object.keys(queryUpdate || {})) {
- query[i] = queryUpdate[i] || undefined
+ query[key] = queryUpdate[key] || undefined
}
this.context.router.replace({ pathname, query })
}
diff --git a/web/src/js/components/flowtable-columns.js b/web/src/js/components/flowtable-columns.js
deleted file mode 100644
index 799b3f9f..00000000
--- a/web/src/js/components/flowtable-columns.js
+++ /dev/null
@@ -1,131 +0,0 @@
-import React from "react"
-import {RequestUtils, ResponseUtils} from "../flow/utils.js"
-import {formatSize, formatTimeDelta} from "../utils.js"
-
-
-export function TLSColumn({flow}) {
- let ssl = (flow.request.scheme === "https")
- let classes
- if (ssl) {
- classes = "col-tls col-tls-https"
- } else {
- classes = "col-tls col-tls-http"
- }
- return <td className={classes}></td>
-}
-TLSColumn.Title = ({className = "", ...props}) => <th {...props} className={"col-tls " + className }></th>
-TLSColumn.sortKeyFun = flow => flow.request.scheme
-
-
-export function IconColumn({flow}) {
- let icon
- if (flow.response) {
- var contentType = ResponseUtils.getContentType(flow.response)
-
- //TODO: We should assign a type to the flow somewhere else.
- if (flow.response.status_code === 304) {
- icon = "resource-icon-not-modified"
- } else if (300 <= flow.response.status_code && flow.response.status_code < 400) {
- icon = "resource-icon-redirect"
- } else if (contentType && contentType.indexOf("image") >= 0) {
- icon = "resource-icon-image"
- } else if (contentType && contentType.indexOf("javascript") >= 0) {
- icon = "resource-icon-js"
- } else if (contentType && contentType.indexOf("css") >= 0) {
- icon = "resource-icon-css"
- } else if (contentType && contentType.indexOf("html") >= 0) {
- icon = "resource-icon-document"
- }
- }
- if (!icon) {
- icon = "resource-icon-plain"
- }
-
- icon += " resource-icon"
- return <td className="col-icon">
- <div className={icon}></div>
- </td>
-}
-IconColumn.Title = ({className = "", ...props}) => <th {...props} className={"col-icon " + className }></th>
-
-
-export function PathColumn({flow}) {
- return <td className="col-path">
- {flow.request.is_replay ? <i className="fa fa-fw fa-repeat pull-right"></i> : null}
- {flow.intercepted ? <i className="fa fa-fw fa-pause pull-right"></i> : null}
- { RequestUtils.pretty_url(flow.request) }
- </td>
-}
-PathColumn.Title = ({className = "", ...props}) =>
- <th {...props} className={"col-path " + className }>Path</th>
-PathColumn.sortKeyFun = flow => RequestUtils.pretty_url(flow.request)
-
-
-export function MethodColumn({flow}) {
- return <td className="col-method">{flow.request.method}</td>
-}
-MethodColumn.Title = ({className = "", ...props}) =>
- <th {...props} className={"col-method " + className }>Method</th>
-MethodColumn.sortKeyFun = flow => flow.request.method
-
-
-export function StatusColumn({flow}) {
- let status
- if (flow.response) {
- status = flow.response.status_code
- } else {
- status = null
- }
- return <td className="col-status">{status}</td>
-
-}
-StatusColumn.Title = ({className = "", ...props}) =>
- <th {...props} className={"col-status " + className }>Status</th>
-StatusColumn.sortKeyFun = flow => flow.response ? flow.response.status_code : undefined
-
-
-export function SizeColumn({flow}) {
- let total = flow.request.contentLength
- if (flow.response) {
- total += flow.response.contentLength || 0
- }
- let size = formatSize(total)
- return <td className="col-size">{size}</td>
-
-}
-SizeColumn.Title = ({className = "", ...props}) =>
- <th {...props} className={"col-size " + className }>Size</th>
-SizeColumn.sortKeyFun = flow => {
- let total = flow.request.contentLength
- if (flow.response) {
- total += flow.response.contentLength || 0
- }
- return total
-}
-
-
-export function TimeColumn({flow}) {
- let time
- if (flow.response) {
- time = formatTimeDelta(1000 * (flow.response.timestamp_end - flow.request.timestamp_start))
- } else {
- time = "..."
- }
- return <td className="col-time">{time}</td>
-}
-TimeColumn.Title = ({className = "", ...props}) =>
- <th {...props} className={"col-time " + className }>Time</th>
-TimeColumn.sortKeyFun = flow => flow.response.timestamp_end - flow.request.timestamp_start
-
-
-var all_columns = [
- TLSColumn,
- IconColumn,
- PathColumn,
- MethodColumn,
- StatusColumn,
- SizeColumn,
- TimeColumn
-]
-
-export default all_columns
diff --git a/web/src/js/components/flowtable.js b/web/src/js/components/flowtable.js
deleted file mode 100644
index 5a0793ba..00000000
--- a/web/src/js/components/flowtable.js
+++ /dev/null
@@ -1,201 +0,0 @@
-import React from "react";
-import ReactDOM from "react-dom";
-import {connect} from 'react-redux'
-import classNames from "classnames";
-import _ from "lodash";
-import shallowEqual from "shallowequal";
-import AutoScroll from "./helpers/AutoScroll";
-import {calcVScroll} from "./helpers/VirtualScroll";
-import flowtable_columns from "./flowtable-columns.js";
-import Filt from "../filt/filt";
-import {setSort} from "../ducks/flows";
-
-
-FlowRow.propTypes = {
- selectFlow: React.PropTypes.func.isRequired,
- columns: React.PropTypes.array.isRequired,
- flow: React.PropTypes.object.isRequired,
- highlight: React.PropTypes.string,
- selected: React.PropTypes.bool,
-};
-
-function FlowRow({flow, selected, highlight, columns, selectFlow}) {
-
- const className = classNames({
- "selected": selected,
- "highlighted": highlight && parseFilter(highlight)(flow),
- "intercepted": flow.intercepted,
- "has-request": flow.request,
- "has-response": flow.response,
- });
-
- return (
- <tr className={className} onClick={() => selectFlow(flow)}>
- {columns.map(Column => (
- <Column key={Column.name} flow={flow}/>
- ))}
- </tr>
- );
-}
-
-const FlowRowContainer = connect(
- (state, ownProps) => ({
- flow: state.flows.all.byId[ownProps.flowId],
- highlight: state.flows.highlight,
- selected: state.flows.selected.indexOf(ownProps.flowId) >= 0
- })
-)(FlowRow)
-
-function FlowTableHead({setSort, columns, sort}) {
- const sortColumn = sort.sortColumn;
- const sortType = sort.sortDesc ? "sort-desc" : "sort-asc";
-
- return (
- <tr>
- {columns.map(Column => (
- <Column.Title
- key={Column.name}
- onClick={() => setSort({sortColumn: Column.name, sortDesc: Column.name != sort.sortColumn ? false : !sort.sortDesc})}
- className={sortColumn === Column.name ? sortType : undefined}
- />
- ))}
- </tr>
- );
-}
-
-FlowTableHead.propTypes = {
- setSort: React.PropTypes.func.isRequired,
- sort: React.PropTypes.object.isRequired,
- columns: React.PropTypes.array.isRequired
-};
-
-const FlowTableHeadContainer = connect(
- state => ({
- sort: state.flows.sort
- }),
- dispatch => ({
- setSort: (sort) => dispatch(setSort(sort)),
- })
-)(FlowTableHead)
-
-class FlowTable extends React.Component {
-
- static propTypes = {
- rowHeight: React.PropTypes.number,
- };
-
- static defaultProps = {
- rowHeight: 32,
- };
-
- constructor(props, context) {
- super(props, context);
-
- this.state = {vScroll: calcVScroll()};
-
- this.onViewportUpdate = this.onViewportUpdate.bind(this);
- }
-
- componentWillMount() {
- window.addEventListener("resize", this.onViewportUpdate);
- }
-
- componentWillUnmount() {
- window.removeEventListener("resize", this.onViewportUpdate);
- }
-
- componentWillReceiveProps(nextProps) {
- if (nextProps.selected && nextProps.selected !== this.props.selected) {
- window.setTimeout(() => this.scrollIntoView(nextProps.selected), 1)
- }
- }
-
- componentDidUpdate() {
- this.onViewportUpdate();
- }
-
- onViewportUpdate() {
- const viewport = ReactDOM.findDOMNode(this);
- const viewportTop = viewport.scrollTop;
-
- const vScroll = calcVScroll({
- viewportTop,
- viewportHeight: viewport.offsetHeight,
- itemCount: this.props.flows.length,
- rowHeight: this.props.rowHeight,
- });
-
- if (!shallowEqual(this.state.vScroll, vScroll) ||
- this.state.viewportTop !== viewportTop) {
- this.setState({vScroll, viewportTop});
- }
- }
-
- scrollIntoView(flow) {
- const viewport = ReactDOM.findDOMNode(this);
- const index = this.props.flows.indexOf(flow);
- const rowHeight = this.props.rowHeight;
- const head = ReactDOM.findDOMNode(this.refs.head);
-
- const headHeight = head ? head.offsetHeight : 0;
-
- const rowTop = (index * rowHeight) + headHeight;
- const rowBottom = rowTop + rowHeight;
-
- const viewportTop = viewport.scrollTop;
- const viewportHeight = viewport.offsetHeight;
-
- // Account for pinned thead
- if (rowTop - headHeight < viewportTop) {
- viewport.scrollTop = rowTop - headHeight;
- } else if (rowBottom > viewportTop + viewportHeight) {
- viewport.scrollTop = rowBottom - viewportHeight;
- }
- }
-
- render() {
- const vScroll = this.state.vScroll;
- const flows = this.props.flows.slice(vScroll.start, vScroll.end);
-
- const transform = `translate(0,${this.state.viewportTop}px)`;
-
- return (
- <div className="flow-table" onScroll={this.onViewportUpdate}>
- <table>
- <thead ref="head" style={{ transform }}>
- <FlowTableHeadContainer
- columns={flowtable_columns}
- setSortKeyFun={this.props.setSortKeyFun}
- setSort={this.props.setSort}
- />
- </thead>
- <tbody>
- <tr style={{ height: vScroll.paddingTop }}></tr>
- {flows.map(flow => (
- <FlowRowContainer
- key={flow.id}
- flowId={flow.id}
- columns={flowtable_columns}
- selectFlow={this.props.selectFlow}
- />
- ))}
- <tr style={{ height: vScroll.paddingBottom }}></tr>
- </tbody>
- </table>
- </div>
- );
- }
-}
-
-FlowTable = AutoScroll(FlowTable)
-
-
-const parseFilter = _.memoize(Filt.parse)
-
-const FlowTableContainer = connect(
- state => ({
- flows: state.flows.view
- })
-)(FlowTable)
-
-export default FlowTableContainer;
diff --git a/web/src/js/ducks/flows.js b/web/src/js/ducks/flows.js
index 6adde71c..f7a5538a 100644
--- a/web/src/js/ducks/flows.js
+++ b/web/src/js/ducks/flows.js
@@ -2,7 +2,7 @@ import makeList from "./utils/list"
import Filt from "../filt/filt"
import {updateViewFilter, updateViewList, updateViewSort} from "./utils/view"
import {reverseString} from "../utils.js";
-import * as flow_table_columns from "../components/flowtable-columns.js";
+import * as columns from "../components/FlowTable/FlowColumns";
export const UPDATE_FLOWS = "UPDATE_FLOWS"
export const SET_FILTER = "SET_FLOW_FILTER"
@@ -32,7 +32,7 @@ function makeFilterFn(filter) {
function makeSortFn(sort){
- let column = flow_table_columns[sort.sortColumn];
+ let column = columns[sort.sortColumn];
if (!column) return;
let sortKeyFun = column.sortKeyFun;
@@ -108,4 +108,4 @@ export function selectFlow(flowId) {
}
-export {updateList as updateFlows, fetchList as fetchFlows} \ No newline at end of file
+export {updateList as updateFlows, fetchList as fetchFlows}