diff options
author | Jason <jason.daurus@gmail.com> | 2016-06-09 17:46:14 +0800 |
---|---|---|
committer | Jason <jason.daurus@gmail.com> | 2016-06-09 17:52:30 +0800 |
commit | f306cfa8b6445dd04c5f7188d1a5022bcb747a62 (patch) | |
tree | 8b229ee951b4e4756c3596b66cefee632bfb8870 /web | |
parent | 52754f40c29b95b8355627036660f7e182007e74 (diff) | |
download | mitmproxy-f306cfa8b6445dd04c5f7188d1a5022bcb747a62.tar.gz mitmproxy-f306cfa8b6445dd04c5f7188d1a5022bcb747a62.tar.bz2 mitmproxy-f306cfa8b6445dd04c5f7188d1a5022bcb747a62.zip |
[web] separate flowtable to multiple files
Diffstat (limited to 'web')
-rw-r--r-- | web/conf.js | 4 | ||||
-rw-r--r-- | web/gulpfile.js | 1 | ||||
-rw-r--r-- | web/src/js/app.jsx (renamed from web/src/js/app.js) | 0 | ||||
-rw-r--r-- | web/src/js/components/FlowTable.jsx | 120 | ||||
-rw-r--r-- | web/src/js/components/FlowTable/FlowColumns.jsx | 137 | ||||
-rw-r--r-- | web/src/js/components/FlowTable/FlowRow.jsx | 28 | ||||
-rw-r--r-- | web/src/js/components/FlowTable/FlowTableHead.jsx | 43 | ||||
-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.js | 131 | ||||
-rw-r--r-- | web/src/js/components/flowtable.js | 201 | ||||
-rw-r--r-- | web/src/js/ducks/flows.js | 6 |
12 files changed, 364 insertions, 357 deletions
diff --git a/web/conf.js b/web/conf.js index 7fe75da4..cc616a63 100644 --- a/web/conf.js +++ b/web/conf.js @@ -11,7 +11,7 @@ var conf = { // Package these as well as the dependencies vendor_includes: [ ], - app: 'src/js/app.js', + app: 'src/js/app', eslint: ["src/js/**/*.js", "!src/js/filt/filt.js"] }, css: { @@ -27,4 +27,4 @@ var conf = { peg: ["src/js/filt/filt.peg"] }; -module.exports = conf;
\ No newline at end of file +module.exports = conf; diff --git a/web/gulpfile.js b/web/gulpfile.js index ffa5387a..4a8c765f 100644 --- a/web/gulpfile.js +++ b/web/gulpfile.js @@ -142,6 +142,7 @@ function app_stream(dev) { var bundler = browserify({ entries: [conf.js.app], debug: true, + extensions: ['.jsx'], cache: {}, // required for watchify packageCache: {} // required for watchify }); 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} |