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 ( selectFlow(flow)}> {columns.map(Column => ( ))} ); } 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 ( {columns.map(Column => ( setSort({sortColumn: Column.name, sortDesc: Column.name != sort.sortColumn ? false : !sort.sortDesc})} className={sortColumn === Column.name ? sortType : undefined} /> ))} ); } 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 (
{flows.map(flow => ( ))}
); } } FlowTable = AutoScroll(FlowTable) const parseFilter = _.memoize(Filt.parse) const FlowTableContainer = connect( state => ({ flows: state.flows.view }) )(FlowTable) export default FlowTableContainer;