diff --git a/ui/src/alerts/components/AlertsTable.js b/ui/src/alerts/components/AlertsTable.js index c813889b9..753a2a859 100644 --- a/ui/src/alerts/components/AlertsTable.js +++ b/ui/src/alerts/components/AlertsTable.js @@ -5,7 +5,7 @@ import classnames from 'classnames' import {Link} from 'react-router' import uuid from 'node-uuid' -import FancyScrollbar from 'shared/components/FancyScrollbar' +import InfiniteScroll from 'shared/components/InfiniteScroll' import {ALERTS_TABLE} from 'src/alerts/constants/tableSizing' @@ -117,11 +117,10 @@ class AlertsTable extends Component { Value - - {alerts.map(({name, level, time, host, value}) => { + itemHeight={25} + items={alerts.map(({name, level, time, host, value}) => { return (
) })} - + />
: this.renderTableEmpty() } diff --git a/ui/src/kapacitor/components/LogsTable.js b/ui/src/kapacitor/components/LogsTable.js index 0e49badd3..ad7945c1b 100644 --- a/ui/src/kapacitor/components/LogsTable.js +++ b/ui/src/kapacitor/components/LogsTable.js @@ -1,6 +1,6 @@ import React, {PropTypes} from 'react' -import FancyScrollbar from 'shared/components/FancyScrollbar' +import InfiniteScroll from 'shared/components/InfiniteScroll' import LogsTableRow from 'src/kapacitor/components/LogsTableRow' const LogsTable = ({logs}) => @@ -8,18 +8,17 @@ const LogsTable = ({logs}) =>

Logs

- -
- {logs.length - ? logs.map((log, i) => +
+ {logs.length + ? - ) - :
} -
- + )} + /> + :
} +
const {arrayOf, shape, string} = PropTypes diff --git a/ui/src/kapacitor/containers/TickscriptPage.js b/ui/src/kapacitor/containers/TickscriptPage.js index 8abf937aa..a2e6bfaea 100644 --- a/ui/src/kapacitor/containers/TickscriptPage.js +++ b/ui/src/kapacitor/containers/TickscriptPage.js @@ -101,13 +101,13 @@ class TickscriptPage extends Component { } this.setState({ - logs: [...this.state.logs, ...logs], + logs: [...logs, ...this.state.logs], failStr, }) } catch (err) { console.warn(err, failStr) this.setState({ - logs: [...this.state.logs, ...logs], + logs: [...logs, ...this.state.logs], failStr, }) } diff --git a/ui/src/shared/components/InfiniteScroll.js b/ui/src/shared/components/InfiniteScroll.js new file mode 100644 index 000000000..ebc1d9b0a --- /dev/null +++ b/ui/src/shared/components/InfiniteScroll.js @@ -0,0 +1,104 @@ +import React, {Component, PropTypes} from 'react' + +const {arrayOf, number, shape, string} = PropTypes + +class InfiniteScroll extends Component { + scrollElement + + // Cache values that need to be independent of + // Should not be setState as need not trigger a re-render + scrollTop = 0 + containerHeight = 0 + + static propTypes = { + itemHeight: number.isRequired, + items: arrayOf(shape()).isRequired, + className: string, + } + + state = { + topIndex: 0, + bottomIndex: 0, + topPadding: 0, + bottomPadding: 0, + } + + windowing = props => { + const {itemHeight, items} = props + const {bottomIndex} = this.state + + const itemDistance = Math.round(this.scrollTop / itemHeight) + const itemCount = Math.round(this.containerHeight / itemHeight) + + console.log(this.containerHeight) + console.log(itemDistance, itemCount) + + // If state is the same, do not setState to the same value multiple times. + // Improves performance and prevents errors. + if (bottomIndex === itemDistance + itemCount) { + return + } + + this.setState({ + // Number of items from top + topIndex: itemDistance, + // Number of items that can fit inside the container div + bottomIndex: itemDistance + itemCount, + // Offset list from top + topPadding: itemDistance * itemHeight, + // Provide scrolling room at the bottom of the list + bottomPadding: (items.length - itemDistance - itemCount) * itemHeight, + }) + } + + handleScroll = evt => { + if (evt.target === this.scrollElement) { + this.scrollTop = evt.target.scrollTop + this.windowing(this.props) + } + } + + handleResize = () => { + this.containerHeight = this.scrollElement.clientHeight + } + + componentDidMount() { + this.containerHeight = this.scrollElement.clientHeight + this.windowing(this.props) + + window.addEventListener('scroll', this.handleScroll, true) + window.addEventListener('resize', this.handleResize, true) + } + + componentWillUnmount() { + window.removeEventListener('scroll', this.handleScroll, true) + window.removeEventListener('resize', this.handleResize, true) + } + + componentWillReceiveProps(nextProps) { + console.log(nextProps) + // Updates values if new items are added + this.windowing(nextProps) + } + + render() { + const {className, items} = this.props + const {topIndex, bottomIndex, topPadding, bottomPadding} = this.state + console.log(items.length) + return ( +
(this.scrollElement = r)} + style={{ + overflowY: 'scroll', + }} + > +
+ {items.filter((_item, i) => i >= topIndex && i <= bottomIndex)} +
+
+ ) + } +} + +export default InfiniteScroll diff --git a/ui/src/style/components/kapacitor-logs-table.scss b/ui/src/style/components/kapacitor-logs-table.scss index fed167ff9..525e23dc8 100644 --- a/ui/src/style/components/kapacitor-logs-table.scss +++ b/ui/src/style/components/kapacitor-logs-table.scss @@ -32,12 +32,6 @@ $logs-margin: 4px; height: calc(100% - #{$logs-table-header-height}) !important; } -.logs-table, -.logs-table--row { - display: flex; - align-items: stretch; - flex-direction: column; -} @keyframes LogsFadeIn { from { background-color: $g6-smoke; @@ -47,9 +41,10 @@ $logs-margin: 4px; } } .logs-table { - flex-direction: column-reverse; + height: 100%; } .logs-table--row { + height: 87px; // Fixed height, required for Infinite Scroll, allows for 2 tags / fields per line padding: 8px ($logs-table-padding - 16px) 8px ($logs-table-padding / 2); border-bottom: 2px solid $g3-castle; animation-name: LogsFadeIn; diff --git a/ui/src/style/components/tables.scss b/ui/src/style/components/tables.scss index 9425cce4f..b4bfd9e42 100644 --- a/ui/src/style/components/tables.scss +++ b/ui/src/style/components/tables.scss @@ -234,7 +234,7 @@ $table-tab-scrollbar-height: 6px; color: $g17-whisper; } .alert-history-table--tbody { - flex: 1 0 0%; + flex: 1 0 0; width: 100%; } .alert-history-table--tr {