diff --git a/app/package.json b/app/package.json index 1d2ca9b..c00ee49 100644 --- a/app/package.json +++ b/app/package.json @@ -35,6 +35,7 @@ "moment": "^2.24.0", "moving-average": "^1.0.0", "number-abbreviate": "^2.0.0", + "parse-duration": "^0.1.1", "prismjs": "^1.15.0", "react": "16.8", "react-ace": "^7.0.1", diff --git a/app/src/components/ChartPanel/Chart.tsx b/app/src/components/ChartPanel/Chart.tsx index ad3bcb0..908cc84 100644 --- a/app/src/components/ChartPanel/Chart.tsx +++ b/app/src/components/ChartPanel/Chart.tsx @@ -72,6 +72,7 @@ function Chart(props: Props) { void +} + +function TimeRangeSettings(props: Props) { + const dismissClick = useCallback((e: MouseEvent) => e.stopPropagation(), []) + const [value, setValue] = useState( + props.chart.timeRange ? props.chart.timeRange.until : undefined + ) + const ranges = ['all', '10s', '30s', '1m', '5m', '15m', '1h', '6h', '1d'] + + const manuallySetIntervalHandler = useCallback( + (e: React.KeyboardEvent) => setValue(e.currentTarget.value), + [] + ) + + useEffect(() => { + if (!value) { + return + } + + const canBeParsed = Boolean(parseDuration(value)) + if (canBeParsed) { + props.actions.chart.updateChart({ + ...props.chart, + timeRange: { + until: value, + }, + }) + } + }, [value]) + + return useMemo(() => { + const createRangeHandler = (range: string) => (e: React.MouseEvent) => setValue(range === 'all' ? undefined : range) + + return ( + + Discard values older then +
+ {ranges.map(r => { + return ( + + ) + })} +
+
+ +
+ ) + }, [value, props.open]) +} + +const mapDispatchToProps = (dispatch: any) => { + return { + actions: { + chart: bindActionCreators(chartActions, dispatch), + }, + } +} + +export default connect( + undefined, + mapDispatchToProps +)(TimeRangeSettings) diff --git a/app/src/components/ChartPanel/ChartSettings/index.tsx b/app/src/components/ChartPanel/ChartSettings/index.tsx index 845c1f4..5d8501a 100644 --- a/app/src/components/ChartPanel/ChartSettings/index.tsx +++ b/app/src/components/ChartPanel/ChartSettings/index.tsx @@ -1,11 +1,12 @@ import * as React from 'react' +import ColorSettings from './ColorSettings' import InterpolationSettings from './InterpolationSettings' -import { ChartParameters } from '../../../reducers/Charts' -import { Menu, MenuItem } from '@material-ui/core' +import MoveUp from './MoveUp' import RangeSettings from './RangeSettings' import Size from './Size' -import MoveUp from './MoveUp' -import ColorSettings from './ColorSettings' +import TimeRangeSettings from './TimeRangeSettings' +import { ChartParameters } from '../../../reducers/Charts' +import { Menu, MenuItem } from '@material-ui/core' function ChartSettings(props: { open: boolean @@ -14,6 +15,7 @@ function ChartSettings(props: { anchorEl: React.MutableRefObject }) { const [rangeVisible, setRangeVisible] = React.useState(false) + const [timeRangeVisible, setTimeRangeVisible] = React.useState(false) const [interpolationVisible, setInterpolationVisible] = React.useState(false) const [sizeVisible, setSizeVisible] = React.useState(false) const [colorVisible, setColorVisible] = React.useState(false) @@ -25,6 +27,13 @@ function ChartSettings(props: { setRangeVisible(!rangeVisible) }, [rangeVisible, open]) + const toggleTimeRange = React.useCallback(() => { + if (open) { + props.close() + } + setTimeRangeVisible(!timeRangeVisible) + }, [timeRangeVisible, open]) + const toggleInterpolation = React.useCallback(() => { if (open) { props.close() @@ -52,6 +61,9 @@ function ChartSettings(props: { Set range + + Time range + Curve interpolation @@ -64,6 +76,12 @@ function ChartSettings(props: { + { return React.useMemo(() => { const data = props.data const calculatedDomain = domainForData(data) - let yDomain: [number, number] = props.range + const yDomain: [number, number] = props.range ? [props.range[0] || calculatedDomain[0], props.range[1] || calculatedDomain[1]] : calculatedDomain + const xDomain = props.timeRangeStart ? [Date.now() - props.timeRangeStart, Date.now()] : undefined + let color: string = props.theme.palette.type === 'light' ? props.theme.palette.secondary.dark : props.theme.palette.primary.light if (props.color) { @@ -67,7 +70,7 @@ export default withTheme((props: Props) => { return (
- + abbreviate(num)} /> diff --git a/app/src/components/TopicPlot.tsx b/app/src/components/TopicPlot.tsx index bc0d9c6..e0f9433 100644 --- a/app/src/components/TopicPlot.tsx +++ b/app/src/components/TopicPlot.tsx @@ -5,18 +5,28 @@ import PlotHistory from './Sidebar/PlotHistory' import { Base64Message } from '../../../backend/src/Model/Base64Message' import { toPlottableValue } from './Sidebar/CodeDiff/util' import { PlotCurveTypes } from '../reducers/Charts' +const parseDuration = require('parse-duration') interface Props { history: q.MessageHistory dotPath?: string + timeInterval?: string interpolation?: PlotCurveTypes range?: [number?, number?] color?: string } -function nodeToHistory(history: q.MessageHistory) { - return history - .toArray() +function filterUsingTimeRange(startTime: number | undefined, data: Array) { + if (startTime) { + const threshold = new Date(Date.now() - startTime) + return data.filter(d => d.received >= threshold) + } + + return data +} + +function nodeToHistory(startTime: number | undefined, history: q.MessageHistory) { + return filterUsingTimeRange(startTime, history.toArray()) .map((message: q.Message) => { const value = message.value ? toPlottableValue(Base64Message.toUnicodeString(message.value)) : NaN return { x: message.received.getTime(), y: toPlottableValue(value) } @@ -24,9 +34,8 @@ function nodeToHistory(history: q.MessageHistory) { .filter(data => !isNaN(data.y as any)) as any } -function nodeDotPathToHistory(history: q.MessageHistory, dotPath: string) { - return history - .toArray() +function nodeDotPathToHistory(startTime: number | undefined, history: q.MessageHistory, dotPath: string) { + return filterUsingTimeRange(startTime, history.toArray()) .map((message: q.Message) => { let json = {} try { @@ -41,8 +50,20 @@ function nodeDotPathToHistory(history: q.MessageHistory, dotPath: string) { } function render(props: Props) { - const data = props.dotPath ? nodeDotPathToHistory(props.history, props.dotPath) : nodeToHistory(props.history) - return + const startOffset = props.timeInterval ? parseDuration(props.timeInterval) : undefined + const data = props.dotPath + ? nodeDotPathToHistory(startOffset, props.history, props.dotPath) + : nodeToHistory(startOffset, props.history) + + return ( + + ) } export default render diff --git a/app/src/reducers/Charts.ts b/app/src/reducers/Charts.ts index 5e68ffc..7d847da 100644 --- a/app/src/reducers/Charts.ts +++ b/app/src/reducers/Charts.ts @@ -13,6 +13,9 @@ export interface ChartParameters { from?: number to?: number } + timeRange?: { + until: string + } width?: 'big' | 'medium' | 'small' color?: string } diff --git a/app/yarn.lock b/app/yarn.lock index ea3a84a..77e0bf1 100644 --- a/app/yarn.lock +++ b/app/yarn.lock @@ -4252,6 +4252,11 @@ parse-asn1@^5.0.0: pbkdf2 "^3.0.3" safe-buffer "^5.1.1" +parse-duration@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/parse-duration/-/parse-duration-0.1.1.tgz#13114ddc9891c1ecd280036244554de43647a226" + integrity sha1-ExFN3JiRwezSgANiRFVN5DZHoiY= + parse-json@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0"