From 80ae669ed15df3f3dbb0cae566b0bffe887c7393 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Fri, 15 Jul 2022 19:11:39 +0200 Subject: [PATCH] fix(ui) - dev tools - sync with timeline --- .../app/components/Session_/Autoscroll.js | 153 +++--- .../components/Session_/Console/Console.js | 7 +- .../Session_/Console/ConsoleContent.js | 185 +++---- .../app/components/Session_/Fetch/Fetch.js | 308 +++++------ .../components/Session_/Network/Network.js | 22 +- .../Session_/TimeTable/TimeTable.tsx | 512 ++++++++---------- .../components/Session_/autoscroll.module.css | 2 +- frontend/app/components/ui/Button/Button.tsx | 2 +- 8 files changed, 554 insertions(+), 637 deletions(-) diff --git a/frontend/app/components/Session_/Autoscroll.js b/frontend/app/components/Session_/Autoscroll.js index 0d24f3930..02af15417 100644 --- a/frontend/app/components/Session_/Autoscroll.js +++ b/frontend/app/components/Session_/Autoscroll.js @@ -4,85 +4,82 @@ import cn from 'classnames'; import stl from './autoscroll.module.css'; export default class Autoscroll extends React.PureComponent { - static defaultProps = { - bottomOffset: 10, - } - state = { - autoScroll: true, - } + static defaultProps = { + bottomOffset: 10, + }; + state = { + autoScroll: true, + }; - componentDidMount() { - if (!this.scrollableElement) return; // is necessary ? - this.scrollableElement.addEventListener('scroll', this.scrollHandler); - this.scrollableElement.scrollTop = this.scrollableElement.scrollHeight; - } - - componentDidUpdate() { - if (!this.scrollableElement) return; // is necessary ? - if (this.state.autoScroll) { - this.scrollableElement.scrollTop = this.scrollableElement.scrollHeight; + componentDidMount() { + if (!this.scrollableElement) return; // is necessary ? + this.scrollableElement.addEventListener('scroll', this.scrollHandler); + this.scrollableElement.scrollTop = this.scrollableElement.scrollHeight; } - } - scrollHandler = (e) => { - if (!this.scrollableElement) return; - this.setState({ - autoScroll: this.scrollableElement.scrollHeight - - this.scrollableElement.clientHeight - - this.scrollableElement.scrollTop < this.props.bottomOffset, - }); - } - - onPrevClick = () => { - if (!this.scrollableElement) return; - const scEl = this.scrollableElement; - let prevItem; - for (let i = scEl.children.length - 1; i >= 0; i--) { - const child = scEl.children[ i ]; - const isScrollable = child.getAttribute("data-scroll-item") === "true"; - if (isScrollable && child.offsetTop < scEl.scrollTop) { - prevItem = child; - break; - } - } - if (!prevItem) return; - scEl.scrollTop = prevItem.offsetTop; - } - - onNextClick = () => { - if (!this.scrollableElement) return; - const scEl = this.scrollableElement; - let nextItem; - for (let i = 0; i < scEl.children.length; i++) { - const child = scEl.children[ i ]; - const isScrollable = child.getAttribute("data-scroll-item") === "true"; - if (isScrollable && child.offsetTop > scEl.scrollTop + 20) { // ? - nextItem = child; - break; - } - } - if (!nextItem) return; - scEl.scrollTop = nextItem.offsetTop; - } - - render() { - const { className, navigation=false, children, ...props } = this.props; - return ( -
-
this.scrollableElement = ref } - > - { children } -
- { navigation && -
- - -
+ componentDidUpdate() { + if (!this.scrollableElement) return; // is necessary ? + if (this.state.autoScroll) { + this.scrollableElement.scrollTop = this.scrollableElement.scrollHeight; } -
- ); - } -} \ No newline at end of file + } + + scrollHandler = (e) => { + if (!this.scrollableElement) return; + this.setState({ + autoScroll: + this.scrollableElement.scrollHeight - this.scrollableElement.clientHeight - this.scrollableElement.scrollTop < + this.props.bottomOffset, + }); + }; + + onPrevClick = () => { + if (!this.scrollableElement) return; + const scEl = this.scrollableElement; + let prevItem; + for (let i = scEl.children.length - 1; i >= 0; i--) { + const child = scEl.children[i]; + const isScrollable = child.getAttribute('data-scroll-item') === 'true'; + if (isScrollable && child.offsetTop < scEl.scrollTop) { + prevItem = child; + break; + } + } + if (!prevItem) return; + scEl.scrollTop = prevItem.offsetTop; + }; + + onNextClick = () => { + if (!this.scrollableElement) return; + const scEl = this.scrollableElement; + let nextItem; + for (let i = 0; i < scEl.children.length; i++) { + const child = scEl.children[i]; + const isScrollable = child.getAttribute('data-scroll-item') === 'true'; + if (isScrollable && child.offsetTop > scEl.scrollTop + 20) { + // ? + nextItem = child; + break; + } + } + if (!nextItem) return; + scEl.scrollTop = nextItem.offsetTop; + }; + + render() { + const { className, navigation = false, children, ...props } = this.props; + return ( +
+
(this.scrollableElement = ref)}> + {children} +
+ {navigation && ( +
+ + +
+ )} +
+ ); + } +} diff --git a/frontend/app/components/Session_/Console/Console.js b/frontend/app/components/Session_/Console/Console.js index 6c0207aa5..5534439fb 100644 --- a/frontend/app/components/Session_/Console/Console.js +++ b/frontend/app/components/Session_/Console/Console.js @@ -4,14 +4,15 @@ import ConsoleContent from './ConsoleContent'; @connectPlayer(state => ({ logs: state.logList, - time: state.time, + // time: state.time, livePlay: state.livePlay, + listNow: state.logListNow, })) export default class Console extends React.PureComponent { render() { - const { logs, time } = this.props; + const { logs, time, listNow } = this.props; return ( - + ); } } diff --git a/frontend/app/components/Session_/Console/ConsoleContent.js b/frontend/app/components/Session_/Console/ConsoleContent.js index 0ffaa6032..a2c084abd 100644 --- a/frontend/app/components/Session_/Console/ConsoleContent.js +++ b/frontend/app/components/Session_/Console/ConsoleContent.js @@ -3,126 +3,113 @@ import cn from 'classnames'; import { getRE } from 'App/utils'; import { Icon, NoContent, Tabs, Input } from 'UI'; import { jump } from 'Player'; -import { LEVEL } from 'Types/session/log'; +import { LEVEL } from 'Types/session/log'; import Autoscroll from '../Autoscroll'; import BottomBlock from '../BottomBlock'; import stl from './console.module.css'; - const ALL = 'ALL'; const INFO = 'INFO'; const WARNINGS = 'WARNINGS'; const ERRORS = 'ERRORS'; const LEVEL_TAB = { - [ LEVEL.INFO ]: INFO, - [ LEVEL.LOG ]: INFO, - [ LEVEL.WARNING ]: WARNINGS, - [ LEVEL.ERROR ]: ERRORS, - [ LEVEL.EXCEPTION ]: ERRORS, + [LEVEL.INFO]: INFO, + [LEVEL.LOG]: INFO, + [LEVEL.WARNING]: WARNINGS, + [LEVEL.ERROR]: ERRORS, + [LEVEL.EXCEPTION]: ERRORS, }; -const TABS = [ ALL, ERRORS, WARNINGS, INFO, ].map(tab => ({ text: tab, key: tab })); +const TABS = [ALL, ERRORS, WARNINGS, INFO].map((tab) => ({ text: tab, key: tab })); // eslint-disable-next-line complexity const getIconProps = (level) => { - switch (level) { - case LEVEL.INFO: - case LEVEL.LOG: - return { - name: 'console/info', - color: 'blue2', - }; - case LEVEL.WARN: - case LEVEL.WARNING: - return { - name: 'console/warning', - color: 'red2', - }; - case LEVEL.ERROR: - return { - name: 'console/error', - color: 'red', - }; - - } - return null; + switch (level) { + case LEVEL.INFO: + case LEVEL.LOG: + return { + name: 'console/info', + color: 'blue2', + }; + case LEVEL.WARN: + case LEVEL.WARNING: + return { + name: 'console/warning', + color: 'red2', + }; + case LEVEL.ERROR: + return { + name: 'console/error', + color: 'red', + }; + } + return null; }; function renderWithNL(s = '') { - if (typeof s !== 'string') return ''; - return s.split('\n').map((line, i) =>
{ line }
) + if (typeof s !== 'string') return ''; + return s.split('\n').map((line, i) =>
{line}
); } export default class ConsoleContent extends React.PureComponent { - state = { - filter: '', - activeTab: ALL, - } - onTabClick = activeTab => this.setState({ activeTab }) - onFilterChange = ({ target: { value }}) => this.setState({ filter: value }) + state = { + filter: '', + activeTab: ALL, + }; + onTabClick = (activeTab) => this.setState({ activeTab }); + onFilterChange = ({ target: { value } }) => this.setState({ filter: value }); - render() { - const { logs, isResult, additionalHeight, time } = this.props; - const { filter, activeTab, currentError } = this.state; - const filterRE = getRE(filter, 'i'); - const filtered = logs.filter(({ level, value }) => activeTab === ALL - ? filterRE.test(value) - : (filterRE.test(value) && LEVEL_TAB[ level ] === activeTab) - ); + render() { + const { logs, isResult, additionalHeight, lastIndex } = this.props; + const { filter, activeTab } = this.state; + const filterRE = getRE(filter, 'i'); + const filtered = logs.filter(({ level, value }) => + activeTab === ALL ? filterRE.test(value) : filterRE.test(value) && LEVEL_TAB[level] === activeTab + ); - const lastIndex = filtered.filter(item => item.time <= time).length - 1; - - return ( - <> - - -
- Console - -
- -
- - - - { filtered.map((l, index) => ( -
!isResult && jump(l.time) } - > - -
{ renderWithNL(l.value) }
-
- ))} -
-
-
-
- - ); - } + return ( + <> + + +
+ Console + +
+ +
+ + + + {filtered.map((l, index) => ( +
!isResult && jump(l.time)} + > + +
{renderWithNL(l.value)}
+
+ ))} +
+
+
+
+ + ); + } } diff --git a/frontend/app/components/Session_/Fetch/Fetch.js b/frontend/app/components/Session_/Fetch/Fetch.js index 5b8e49911..2dff482c9 100644 --- a/frontend/app/components/Session_/Fetch/Fetch.js +++ b/frontend/app/components/Session_/Fetch/Fetch.js @@ -10,170 +10,154 @@ import { renderName, renderDuration } from '../Network'; import { connect } from 'react-redux'; import { setTimelinePointer } from 'Duck/sessions'; -@connectPlayer(state => ({ - list: state.fetchList, - livePlay: state.livePlay, +@connectPlayer((state) => ({ + list: state.fetchList, + listNow: state.fetchListNow, + livePlay: state.livePlay, })) -@connect(state => ({ - timelinePointer: state.getIn(['sessions', 'timelinePointer']), -}), { setTimelinePointer }) +@connect( + (state) => ({ + timelinePointer: state.getIn(['sessions', 'timelinePointer']), + }), + { setTimelinePointer } +) export default class Fetch extends React.PureComponent { - state = { - filter: "", - filteredList: this.props.list, - current: null, - currentIndex: 0, - showFetchDetails: false, - hasNextError: false, - hasPreviousError: false, - } - - onFilterChange = ({ target: { value } }) => { - const { list } = this.props; - const filterRE = getRE(value, 'i'); - const filtered = list - .filter((r) => filterRE.test(r.name) || filterRE.test(r.url) || filterRE.test(r.method) || filterRE.test(r.status)); - this.setState({ filter: value, filteredList: value ? filtered : list, currentIndex: 0 }); - } + state = { + filter: '', + filteredList: this.props.list, + current: null, + currentIndex: 0, + showFetchDetails: false, + hasNextError: false, + hasPreviousError: false, + }; - setCurrent = (item, index) => { - if (!this.props.livePlay) { - pause(); - jump(item.time) + onFilterChange = ({ target: { value } }) => { + const { list } = this.props; + const filterRE = getRE(value, 'i'); + const filtered = list.filter((r) => filterRE.test(r.name) || filterRE.test(r.url) || filterRE.test(r.method) || filterRE.test(r.status)); + this.setState({ filter: value, filteredList: value ? filtered : list, currentIndex: 0 }); + }; + + setCurrent = (item, index) => { + if (!this.props.livePlay) { + pause(); + jump(item.time); + } + this.setState({ current: item, currentIndex: index }); + }; + + onRowClick = (item, index) => { + if (!this.props.livePlay) { + pause(); + } + this.setState({ current: item, currentIndex: index, showFetchDetails: true }); + this.props.setTimelinePointer(null); + }; + + closeModal = () => this.setState({ current: null, showFetchDetails: false }); + + nextClickHander = () => { + // const { list } = this.props; + const { currentIndex, filteredList } = this.state; + + if (currentIndex === filteredList.length - 1) return; + const newIndex = currentIndex + 1; + this.setCurrent(filteredList[newIndex], newIndex); + this.setState({ showFetchDetails: true }); + }; + + prevClickHander = () => { + // const { list } = this.props; + const { currentIndex, filteredList } = this.state; + + if (currentIndex === 0) return; + const newIndex = currentIndex - 1; + this.setCurrent(filteredList[newIndex], newIndex); + this.setState({ showFetchDetails: true }); + }; + + render() { + const { listNow } = this.props; + const { current, currentIndex, showFetchDetails, filteredList } = this.state; + return ( + + +

Fetch Request

+
+
+ Status + +
+ +
+ + } + isDisplayed={current != null && showFetchDetails} + content={ + current && + showFetchDetails && ( + + ) + } + onClose={this.closeModal} + /> + + +

Fetch

+
+ +
+
+ + + + {[ + { + label: 'Status', + dataKey: 'status', + width: 70, + }, + { + label: 'Method', + dataKey: 'method', + width: 60, + }, + { + label: 'Name', + width: 240, + render: renderName, + }, + { + label: 'Time', + width: 80, + render: renderDuration, + }, + ]} + + + +
+
+ ); } - this.setState({ current: item, currentIndex: index }); - } - - onRowClick = (item, index) => { - if (!this.props.livePlay) { - pause(); - } - this.setState({ current: item, currentIndex: index, showFetchDetails: true }); - this.props.setTimelinePointer(null); - } - - closeModal = () => this.setState({ current: null, showFetchDetails: false }); - - nextClickHander = () => { - // const { list } = this.props; - const { currentIndex, filteredList } = this.state; - - if (currentIndex === filteredList.length - 1) return; - const newIndex = currentIndex + 1; - this.setCurrent(filteredList[newIndex], newIndex); - this.setState({ showFetchDetails: true }); - } - - prevClickHander = () => { - // const { list } = this.props; - const { currentIndex, filteredList } = this.state; - - if (currentIndex === 0) return; - const newIndex = currentIndex - 1; - this.setCurrent(filteredList[newIndex], newIndex); - this.setState({ showFetchDetails: true }); - } - - static getDerivedStateFromProps(nextProps, prevState) { - const { filteredList } = prevState; - if (nextProps.timelinePointer) { - let activeItem = filteredList.find((r) => r.time >= nextProps.timelinePointer.time); - activeItem = activeItem || filteredList[filteredList.length - 1]; - return { - current: activeItem, - currentIndex: filteredList.indexOf(activeItem), - }; - } - } - - render() { - // const { list } = this.props; - const { current, currentIndex, showFetchDetails, filteredList } = this.state; - return ( - - -

Fetch Request

-
-
- Status - -
- -
- - } - isDisplayed={ current != null && showFetchDetails } - content={ current && showFetchDetails && - - } - onClose={ this.closeModal } - /> - - -

Fetch

-
- -
-
- - - - {[ - { - label: "Status", - dataKey: 'status', - width: 70, - }, { - label: "Method", - dataKey: "method", - width: 60, - }, { - label: "Name", - width: 240, - render: renderName, - }, - { - label: "Time", - width: 80, - render: renderDuration, - } - ]} - - - -
-
- ); - } } diff --git a/frontend/app/components/Session_/Network/Network.js b/frontend/app/components/Session_/Network/Network.js index 26396e5bd..887cc1148 100644 --- a/frontend/app/components/Session_/Network/Network.js +++ b/frontend/app/components/Session_/Network/Network.js @@ -78,6 +78,7 @@ export function renderDuration(r) { playing: state.playing, domBuildingTime: state.domBuildingTime, fetchPresented: state.fetchList.length > 0, + listNow: state.resourceListNow, })) @connect( (state) => ({ @@ -110,31 +111,16 @@ export default class Network extends React.PureComponent { this.setState({ filter: value, filteredList: value ? filtered : resources, currentIndex: 0 }); }; - static getDerivedStateFromProps(nextProps, prevState) { - const { filteredList } = prevState; - if (nextProps.timelinePointer) { - const activeItem = filteredList.find((r) => r.time >= nextProps.timelinePointer.time); - return { - currentIndex: activeItem ? filteredList.indexOf(activeItem) : filteredList.length - 1, - }; - } - } - render() { const { location, - resources, domContentLoadedTime, loadTime, domBuildingTime, fetchPresented, - // time, - playing, + listNow, } = this.props; - const { filter, activeTab, currentIndex, filteredList } = this.state; - // const filterRE = getRE(filter, 'i'); - // let filtered = resources.filter(({ type, name }) => - // filterRE.test(name) && (activeTab === ALL || type === TAB_TO_TYPE_MAP[ activeTab ])); + const { filteredList } = this.state; const resourcesSize = filteredList.reduce((sum, { decodedBodySize }) => sum + (decodedBodySize || 0), 0); const transferredSize = filteredList.reduce((sum, { headerSize, encodedBodySize }) => sum + (headerSize || 0) + (encodedBodySize || 0), 0); @@ -151,7 +137,7 @@ export default class Network extends React.PureComponent { resourcesSize={resourcesSize} transferredSize={transferredSize} onRowClick={this.onRowClick} - currentIndex={currentIndex} + currentIndex={listNow.length - 0} /> ); diff --git a/frontend/app/components/Session_/TimeTable/TimeTable.tsx b/frontend/app/components/Session_/TimeTable/TimeTable.tsx index 413af3f61..4a6f1140e 100644 --- a/frontend/app/components/Session_/TimeTable/TimeTable.tsx +++ b/frontend/app/components/Session_/TimeTable/TimeTable.tsx @@ -1,7 +1,7 @@ import React from 'react'; -import { List, AutoSizer } from "react-virtualized"; +import { List, AutoSizer } from 'react-virtualized'; import cn from 'classnames'; -import { NoContent, IconButton } from 'UI'; +import { NoContent, IconButton, Button } from 'UI'; import { percentOf } from 'App/utils'; import { formatMs } from 'App/date'; @@ -10,58 +10,58 @@ import stl from './timeTable.module.css'; import autoscrollStl from '../autoscroll.module.css'; //aaa - type Timed = { - time: number, -} + time: number; +}; type Durationed = { - duration: number, -} + duration: number; +}; type CanBeRed = { - //+isRed: boolean, - isRed: () => boolean, -} + //+isRed: boolean, + isRed: () => boolean; +}; -type Row = Timed & Durationed & CanBeRed +type Row = Timed & Durationed & CanBeRed; type Line = { - color: string, // Maybe use typescript? - hint?: string, - onClick?: any, -} & Timed + color: string; // Maybe use typescript? + hint?: string; + onClick?: any; +} & Timed; type Column = { - label: string, - width: number, - referenceLines?: Array, - style?: Object, -} & RenderOrKey + label: string; + width: number; + referenceLines?: Array; + style?: Object; +} & RenderOrKey; // type RenderOrKey = { // Disjoint? -// render: Row => React.Node +// render: Row => React.Node // } | { // dataKey: string, // } -type RenderOrKey = { - render?: (row: Row) => React.ReactNode, - key?: string, -} | { - dataKey: string, -} - +type RenderOrKey = + | { + render?: (row: Row) => React.ReactNode; + key?: string; + } + | { + dataKey: string; + }; type Props = { - className?: string, - rows: Array, - children: Array -} + className?: string; + rows: Array; + children: Array; +}; type TimeLineInfo = { - timestart: number, - timewidth: number, -} + timestart: number; + timewidth: number; +}; type State = TimeLineInfo & typeof initialState; @@ -73,266 +73,228 @@ const ROW_HEIGHT = 32; const TIME_SECTIONS_COUNT = 8; const ZERO_TIMEWIDTH = 1000; function formatTime(ms) { - if(ms < 0) return ""; - return formatMs(ms); + if (ms < 0) return ''; + return formatMs(ms); } function computeTimeLine(rows: Array, firstVisibleRowIndex: number, visibleCount): TimeLineInfo { - const visibleRows = rows.slice(firstVisibleRowIndex, firstVisibleRowIndex + visibleCount + _additionalHeight); - let timestart = visibleRows.length > 0 - ? Math.min(...visibleRows.map(r => r.time)) - : 0; - const timeend = visibleRows.length > 0 - ? Math.max(...visibleRows.map(r => r.time + r.duration)) - : 0; - let timewidth = timeend - timestart; - const offset = timewidth / 70; - if (timestart >= offset) { - timestart -= offset; - } - timewidth *= 1.5; // += offset; - if (timewidth === 0) { - timewidth = ZERO_TIMEWIDTH; - } - return { - timestart, - timewidth, - }; + const visibleRows = rows.slice(firstVisibleRowIndex, firstVisibleRowIndex + visibleCount + _additionalHeight); + let timestart = visibleRows.length > 0 ? Math.min(...visibleRows.map((r) => r.time)) : 0; + const timeend = visibleRows.length > 0 ? Math.max(...visibleRows.map((r) => r.time + r.duration)) : 0; + let timewidth = timeend - timestart; + const offset = timewidth / 70; + if (timestart >= offset) { + timestart -= offset; + } + timewidth *= 1.5; // += offset; + if (timewidth === 0) { + timewidth = ZERO_TIMEWIDTH; + } + return { + timestart, + timewidth, + }; } const initialState = { - firstVisibleRowIndex: 0, -} + firstVisibleRowIndex: 0, +}; export default class TimeTable extends React.PureComponent { - state = { - ...computeTimeLine(this.props.rows, initialState.firstVisibleRowIndex, this.visibleCount), - ...initialState, - } + state = { + ...computeTimeLine(this.props.rows, initialState.firstVisibleRowIndex, this.visibleCount), + ...initialState, + }; - get tableHeight() { - return this.props.tableHeight || 195; - } - - get visibleCount() { - return Math.ceil(this.tableHeight/ROW_HEIGHT); - } - - scroller = React.createRef(); - autoScroll = true; - - // componentDidMount() { - // if (this.scroller.current != null) { - // this.scroller.current.scrollToRow(this.props.rows.length - 1); - // } - // } - - componentDidUpdate(prevProps: any, prevState: any) { - // if (prevProps.rows.length !== this.props.rows.length && - // this.autoScroll && - // this.scroller.current != null) { - // this.scroller.current.scrollToRow(this.props.rows.length); - // } - if (prevState.firstVisibleRowIndex !== this.state.firstVisibleRowIndex || - (this.props.rows.length <= (this.visibleCount + _additionalHeight) && prevProps.rows.length !== this.props.rows.length)) { - this.setState({ - ...computeTimeLine(this.props.rows, this.state.firstVisibleRowIndex, this.visibleCount), - }); - } - if (this.props.activeIndex >= 0 && prevProps.activeIndex !== this.props.activeIndex && this.scroller.current != null) { - this.scroller.current.scrollToRow(this.props.activeIndex); - } - } - - onScroll = ({ scrollTop, scrollHeight, clientHeight }: - { scrollTop: number, scrollHeight: number, clientHeight: number }):void => { - const firstVisibleRowIndex = Math.floor(scrollTop / ROW_HEIGHT + 0.33); - - if (this.state.firstVisibleRowIndex !== firstVisibleRowIndex) { - this.autoScroll = (scrollHeight - clientHeight - scrollTop) < ROW_HEIGHT / 2; - this.setState({ firstVisibleRowIndex }); - } - } - - renderRow = ({ index, key, style: rowStyle }: any) => { - const { activeIndex } = this.props; - const { - children: columns, - rows, - renderPopup, - hoverable, - onRowClick, - } = this.props; - const { - timestart, - timewidth, - } = this.state; - const row = rows[ index ]; - return ( -
onRowClick(row, index) : null } - id="table-row" - > - { columns.map(({ dataKey, render, width }) => ( -
- { render ? render(row) : (row[ dataKey ] || {"empty"}) } -
- ))} -
- -
-
- ); - } - - onPrevClick = () => { - let prevRedIndex = -1; - for (let i = this.state.firstVisibleRowIndex-1; i >= 0; i--) { - if (this.props.rows[ i ].isRed()) { - prevRedIndex = i; - break; - } - } - if (this.scroller.current != null) { - this.scroller.current.scrollToRow(prevRedIndex); - } - } - - onNextClick = () => { - let prevRedIndex = -1; - for (let i = this.state.firstVisibleRowIndex+1; i < this.props.rows.length; i++) { - if (this.props.rows[ i ].isRed()) { - prevRedIndex = i; - break; - } - } - if (this.scroller.current != null) { - this.scroller.current.scrollToRow(prevRedIndex); - } - } - - render() { - const { - className, - rows, - children: columns, - navigation=false, - referenceLines = [], - additionalHeight = 0, - activeIndex, - } = this.props; - const { - timewidth, - timestart, - } = this.state; - - _additionalHeight = additionalHeight; - - const sectionDuration = Math.round(timewidth / TIME_SECTIONS_COUNT); - const timeColumns = []; - if (timewidth > 0) { - for (let i = 0; i < TIME_SECTIONS_COUNT; i++) { - timeColumns.push(timestart + i * sectionDuration); - } + get tableHeight() { + return this.props.tableHeight || 195; } - const visibleRefLines = referenceLines.filter(({ time }) => time > timestart && time < timestart + timewidth); + get visibleCount() { + return Math.ceil(this.tableHeight / ROW_HEIGHT); + } - const columnsSumWidth = columns.reduce((sum, { width }) => sum + width, 0); + scroller = React.createRef(); + autoScroll = true; - return ( -
- { navigation && -
- = 0 && prevProps.activeIndex !== this.props.activeIndex) { + this.scroller.current.scrollToRow(this.props.activeIndex); + } + } + + onScroll = ({ scrollTop, scrollHeight, clientHeight }: { scrollTop: number; scrollHeight: number; clientHeight: number }): void => { + const firstVisibleRowIndex = Math.floor(scrollTop / ROW_HEIGHT + 0.33); + + if (this.state.firstVisibleRowIndex !== firstVisibleRowIndex) { + this.autoScroll = scrollHeight - clientHeight - scrollTop < ROW_HEIGHT / 2; + this.setState({ firstVisibleRowIndex }); + } + }; + + renderRow = ({ index, key, style: rowStyle }: any) => { + const { activeIndex } = this.props; + const { children: columns, rows, renderPopup, hoverable, onRowClick } = this.props; + const { timestart, timewidth } = this.state; + const row = rows[index]; + return ( +
onRowClick(row, index) : null} + id="table-row" + > + {columns.map(({ dataKey, render, width }) => ( +
+ {render ? render(row) : row[dataKey] || {'empty'}} +
+ ))} +
+ +
+
+ ); + }; + + onPrevClick = () => { + let prevRedIndex = -1; + for (let i = this.state.firstVisibleRowIndex - 1; i >= 0; i--) { + if (this.props.rows[i].isRed()) { + prevRedIndex = i; + break; + } + } + if (this.scroller.current != null) { + this.scroller.current.scrollToRow(prevRedIndex); + } + }; + + onNextClick = () => { + let prevRedIndex = -1; + for (let i = this.state.firstVisibleRowIndex + 1; i < this.props.rows.length; i++) { + if (this.props.rows[i].isRed()) { + prevRedIndex = i; + break; + } + } + if (this.scroller.current != null) { + this.scroller.current.scrollToRow(prevRedIndex); + } + }; + + render() { + const { className, rows, children: columns, navigation = false, referenceLines = [], additionalHeight = 0, activeIndex } = this.props; + const { timewidth, timestart } = this.state; + + _additionalHeight = additionalHeight; + + const sectionDuration = Math.round(timewidth / TIME_SECTIONS_COUNT); + const timeColumns = []; + if (timewidth > 0) { + for (let i = 0; i < TIME_SECTIONS_COUNT; i++) { + timeColumns.push(timestart + i * sectionDuration); + } + } + + const visibleRefLines = referenceLines.filter(({ time }) => time > timestart && time < timestart + timewidth); + + const columnsSumWidth = columns.reduce((sum, { width }) => sum + width, 0); + + return ( +
+ {navigation && ( +
+
- } -
-
- { columns.map(({ label, width }) => ( -
- { label } -
- )) } -
-
- { timeColumns.map((time, i) => ( -
- { formatTime(time) } + /> */} +
+ )} +
+
+ {columns.map(({ label, width }) => ( +
+ {label} +
+ ))} +
+
+ {timeColumns.map((time, i) => ( +
+ {formatTime(time)} +
+ ))} +
- )) - } -
-
- -
-
- { timeColumns.map((_, index) => ( -
- )) - } - { visibleRefLines.map(({ time, color, onClick }) => ( -
- ))} + +
+
+ {timeColumns.map((_, index) => ( +
+ ))} + {visibleRefLines.map(({ time, color, onClick }) => ( +
+ ))} +
+ + {({ width }) => ( + + )} + +
+
- - {({ width }) => ( - - )} - -
-
-
- ); - } + ); + } } - diff --git a/frontend/app/components/Session_/autoscroll.module.css b/frontend/app/components/Session_/autoscroll.module.css index 546f87049..42c5d980a 100644 --- a/frontend/app/components/Session_/autoscroll.module.css +++ b/frontend/app/components/Session_/autoscroll.module.css @@ -13,7 +13,7 @@ .navButtons { position: absolute; right: 260px; - top: -34px; + top: -39px; } diff --git a/frontend/app/components/ui/Button/Button.tsx b/frontend/app/components/ui/Button/Button.tsx index 043491ed2..075d95b33 100644 --- a/frontend/app/components/ui/Button/Button.tsx +++ b/frontend/app/components/ui/Button/Button.tsx @@ -4,7 +4,7 @@ import { CircularLoader, Icon } from 'UI'; interface Props { className?: string; - children: React.ReactNode; + children?: React.ReactNode; onClick?: () => void; disabled?: boolean; type?: 'button' | 'submit' | 'reset';