problems timeline: highlight regions

This commit is contained in:
Alexander Zobnin
2018-12-19 15:24:17 +03:00
parent 492c448273
commit 1ec8b01af2
2 changed files with 116 additions and 35 deletions

View File

@@ -1,4 +1,5 @@
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import _ from 'lodash';
import moment from 'moment'; import moment from 'moment';
import { GFTimeRange, ZBXEvent } from 'panel-triggers/types'; import { GFTimeRange, ZBXEvent } from 'panel-triggers/types';
@@ -21,11 +22,13 @@ export interface ProblemTimelineProps {
interface ProblemTimelineState { interface ProblemTimelineState {
width: number; width: number;
highlightedEvent?: ZBXEvent | null; highlightedEvent?: ZBXEvent | null;
highlightedRegion?: number | null;
showEventInfo?: boolean; showEventInfo?: boolean;
} }
export default class ProblemTimeline extends PureComponent<ProblemTimelineProps, ProblemTimelineState> { export default class ProblemTimeline extends PureComponent<ProblemTimelineProps, ProblemTimelineState> {
rootRef: any; rootRef: any;
sortedEvents: ZBXEvent[];
static defaultProps = { static defaultProps = {
okColor: DEFAULT_OK_COLOR, okColor: DEFAULT_OK_COLOR,
@@ -39,16 +42,38 @@ export default class ProblemTimeline extends PureComponent<ProblemTimelineProps,
this.state = { this.state = {
width: 0, width: 0,
highlightedEvent: null, highlightedEvent: null,
highlightedRegion: null,
showEventInfo: false, showEventInfo: false,
}; };
} }
componentDidUpdate(prevProps, prevState) {
if (this.rootRef && prevState.width !== this.rootRef.clientWidth) {
const width = this.rootRef.clientWidth;
this.setState({ width });
}
}
setRootRef = ref => { setRootRef = ref => {
this.rootRef = ref; this.rootRef = ref;
const width = ref && ref.clientWidth || 0; const width = ref && ref.clientWidth || 0;
this.setState({ width }); this.setState({ width });
} }
handlePointHighlight = (event: ZBXEvent, index?: number) => {
const regionToHighlight = this.getRegionToHighlight(index);
this.setState({
highlightedEvent: event,
showEventInfo: true,
highlightedRegion: regionToHighlight
});
// this.showEventInfo(event);
}
handlePointUnHighlight = () => {
this.setState({ showEventInfo: false, highlightedRegion: null });
}
showEventInfo = (event: ZBXEvent) => { showEventInfo = (event: ZBXEvent) => {
this.setState({ highlightedEvent: event, showEventInfo: true }); this.setState({ highlightedEvent: event, showEventInfo: true });
} }
@@ -57,11 +82,16 @@ export default class ProblemTimeline extends PureComponent<ProblemTimelineProps,
this.setState({ showEventInfo: false }); this.setState({ showEventInfo: false });
} }
componentDidUpdate(prevProps, prevState, snapshot) { getRegionToHighlight = (index: number): number => {
if (this.rootRef && prevState.width !== this.rootRef.clientWidth) { const event = this.sortedEvents[index];
const width = this.rootRef.clientWidth; const regionToHighlight = event.value === '1' ? index + 1 : index;
this.setState({ width }); return regionToHighlight;
} }
sortEvents() {
const events = _.sortBy(this.props.events, e => Number(e.clock));
this.sortedEvents = events;
return events;
} }
render() { render() {
@@ -69,7 +99,8 @@ export default class ProblemTimeline extends PureComponent<ProblemTimelineProps,
return <div className="event-timeline" ref={this.setRootRef} />; return <div className="event-timeline" ref={this.setRootRef} />;
} }
const { events, timeRange, eventPointSize, eventRegionHeight, problemColor, okColor } = this.props; const { timeRange, eventPointSize, eventRegionHeight, problemColor, okColor } = this.props;
const events = this.sortEvents();
const boxWidth = this.state.width; const boxWidth = this.state.width;
const boxHeight = eventPointSize * 2; const boxHeight = eventPointSize * 2;
const width = boxWidth - eventPointSize; const width = boxWidth - eventPointSize;
@@ -125,6 +156,7 @@ export default class ProblemTimeline extends PureComponent<ProblemTimelineProps,
height={eventRegionHeight} height={eventRegionHeight}
okColor={okColor} okColor={okColor}
problemColor={problemColor} problemColor={problemColor}
highlightedRegion={this.state.highlightedRegion}
/> />
</g> </g>
<g className="timeline-points" transform={`translate(0, ${pointsYpos})`}> <g className="timeline-points" transform={`translate(0, ${pointsYpos})`}>
@@ -135,8 +167,8 @@ export default class ProblemTimeline extends PureComponent<ProblemTimelineProps,
pointSize={eventPointSize} pointSize={eventPointSize}
okColor={okColor} okColor={okColor}
problemColor={problemColor} problemColor={problemColor}
onPointHighlight={this.showEventInfo} onPointHighlight={this.handlePointHighlight}
onPointUnHighlight={this.hideEventInfo} onPointUnHighlight={this.handlePointUnHighlight}
/> />
</g> </g>
</g> </g>
@@ -191,13 +223,19 @@ interface TimelineRegionsProps {
height: number; height: number;
okColor: string; okColor: string;
problemColor: string; problemColor: string;
highlightedRegion?: number | null;
} }
class TimelineRegions extends PureComponent<TimelineRegionsProps> { class TimelineRegions extends PureComponent<TimelineRegionsProps> {
static defaultProps = {
highlightedRegion: null,
};
render() { render() {
const { events, timeRange, width, height, okColor, problemColor } = this.props; const { events, timeRange, width, height, okColor, problemColor, highlightedRegion } = this.props;
const { timeFrom, timeTo } = timeRange; const { timeFrom, timeTo } = timeRange;
const range = timeTo - timeFrom; const range = timeTo - timeFrom;
console.log(highlightedRegion);
let firstItem: React.ReactNode; let firstItem: React.ReactNode;
if (events.length) { if (events.length) {
@@ -205,6 +243,8 @@ class TimelineRegions extends PureComponent<TimelineRegionsProps> {
const duration = (firstTs - timeFrom) / range; const duration = (firstTs - timeFrom) / range;
const regionWidth = Math.round(duration * width); const regionWidth = Math.round(duration * width);
const firstEventColor = events[0].value !== '1' ? problemColor : okColor; const firstEventColor = events[0].value !== '1' ? problemColor : okColor;
const highlighted = highlightedRegion === 0;
const className = `problem-event-region ${highlighted ? 'highlighted' : ''}`;
const firstEventAttributes = { const firstEventAttributes = {
x: 0, x: 0,
y: 0, y: 0,
@@ -213,7 +253,7 @@ class TimelineRegions extends PureComponent<TimelineRegionsProps> {
fill: firstEventColor, fill: firstEventColor,
}; };
firstItem = ( firstItem = (
<rect key='0' className="problem-event-region" {...firstEventAttributes}></rect> <rect key='0' className={className} {...firstEventAttributes}></rect>
); );
} }
@@ -224,6 +264,8 @@ class TimelineRegions extends PureComponent<TimelineRegionsProps> {
const regionWidth = Math.round(duration * width); const regionWidth = Math.round(duration * width);
const posLeft = Math.round((ts - timeFrom) / range * width); const posLeft = Math.round((ts - timeFrom) / range * width);
const eventColor = event.value === '1' ? problemColor : okColor; const eventColor = event.value === '1' ? problemColor : okColor;
const highlighted = highlightedRegion && highlightedRegion - 1 === index;
const className = `problem-event-region ${highlighted ? 'highlighted' : ''}`;
const attributes = { const attributes = {
x: posLeft, x: posLeft,
y: 0, y: 0,
@@ -233,7 +275,7 @@ class TimelineRegions extends PureComponent<TimelineRegionsProps> {
}; };
return ( return (
<rect key={event.eventid} className="problem-event-region" {...attributes} /> <rect key={event.eventid} className={className} {...attributes} />
); );
}); });
@@ -251,46 +293,81 @@ interface TimelinePointsProps {
pointSize: number; pointSize: number;
okColor: string; okColor: string;
problemColor: string; problemColor: string;
onPointHighlight?: (event: ZBXEvent) => void; highlightRegion?: boolean;
onPointHighlight?: (event: ZBXEvent, index?: number) => void;
onPointUnHighlight?: () => void; onPointUnHighlight?: () => void;
} }
interface TimelinePointsState { interface TimelinePointsState {
order: number[]; order: number[];
highlighted: number | null; highlighted: number[];
} }
class TimelinePoints extends PureComponent<TimelinePointsProps, TimelinePointsState> { class TimelinePoints extends PureComponent<TimelinePointsProps, TimelinePointsState> {
static defaultProps = {
highlightRegion: true,
};
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { order: [], highlighted: null }; this.state = { order: [], highlighted: [] };
} }
bringToFront = (index: number, highlight = false ) => { bringToFront = (indexes: number[], highlight = false) => {
const { events } = this.props; const { events } = this.props;
const length = events.length; let order = events.map((v, i) => i);
const order = events.map((v, i) => i); order = moveToEnd(order, indexes);
order.splice(index, 1); const highlighted = highlight ? indexes : null;
order.push(index);
const highlighted = highlight ? index : null;
this.setState({ order, highlighted }); this.setState({ order, highlighted });
} }
highlightPoint = index => () => { highlightPoint = (index: number) => () => {
let pointsToHighlight = [index];
if (this.props.onPointHighlight) { if (this.props.onPointHighlight) {
this.props.onPointHighlight(this.props.events[index]); if (this.props.highlightRegion) {
pointsToHighlight = this.getRegionEvents(index);
this.props.onPointHighlight(this.props.events[index], index);
} else {
this.props.onPointHighlight(this.props.events[index]);
}
} }
this.bringToFront(index, true); this.bringToFront(pointsToHighlight, true);
// this.setState({ highlighted: this.props.events.length - 1 });
} }
getRegionEvents(index: number) {
const events = this.props.events;
const event = events[index];
if (event.value === '1' && index < events.length ) {
// Problem event
for (let i = index; i < events.length; i++) {
if (events[i].value === '0') {
const okEventIndex = i;
return [index, okEventIndex];
}
}
} else if (event.value === '0' && index > 0) {
// OK event
let lastProblemIndex = null;
for (let i = index - 1; i >= 0; i--) {
if (events[i].value === '1') {
lastProblemIndex = i;
} else {
break;
}
}
if (lastProblemIndex !== null) {
return [index, lastProblemIndex];
}
}
return [index];
}
unHighlightPoint = index => () => { unHighlightPoint = index => () => {
if (this.props.onPointUnHighlight) { if (this.props.onPointUnHighlight) {
this.props.onPointUnHighlight(); this.props.onPointUnHighlight();
} }
const order = this.props.events.map((v, i) => i); const order = this.props.events.map((v, i) => i);
this.setState({ order, highlighted: null }); this.setState({ order, highlighted: [] });
} }
render() { render() {
@@ -298,15 +375,11 @@ class TimelinePoints extends PureComponent<TimelinePointsProps, TimelinePointsSt
const { timeFrom, timeTo } = timeRange; const { timeFrom, timeTo } = timeRange;
const range = timeTo - timeFrom; const range = timeTo - timeFrom;
const pointR = pointSize / 2; const pointR = pointSize / 2;
const eventsItems = events.map((event, index) => { const eventsItems = events.map((event, i) => {
const ts = Number(event.clock); const ts = Number(event.clock);
const posLeft = Math.round((ts - timeFrom) / range * width - pointR); const posLeft = Math.round((ts - timeFrom) / range * width - pointR);
const eventColor = event.value === '1' ? problemColor : okColor; const eventColor = event.value === '1' ? problemColor : okColor;
const highlighted = this.state.highlighted === index || ( const highlighted = this.state.highlighted.indexOf(i) !== -1;
this.state.highlighted !== null && index === this.state.highlighted + 1 && event.value !== '1'
) || (
this.state.highlighted !== null && index === this.state.highlighted - 1 && event.value === '1'
);
return ( return (
<TimelinePoint <TimelinePoint
@@ -316,8 +389,8 @@ class TimelinePoints extends PureComponent<TimelinePointsProps, TimelinePointsSt
r={pointR} r={pointR}
color={eventColor} color={eventColor}
highlighted={highlighted} highlighted={highlighted}
onPointHighlight={this.highlightPoint(index)} onPointHighlight={this.highlightPoint(i)}
onPointUnHighlight={this.unHighlightPoint(index)} onPointUnHighlight={this.unHighlightPoint(i)}
/> />
); );
}); });
@@ -385,3 +458,10 @@ class TimelinePoint extends PureComponent<TimelinePointProps, TimelinePointState
); );
} }
} }
function moveToEnd<T>(array: T[], itemsToMove: number[]): T[] {
const removed = _.pullAt(array, itemsToMove);
removed.reverse();
array.push(...removed);
return array;
}

View File

@@ -165,7 +165,7 @@
transition: all 0.3s ease-out; transition: all 0.3s ease-out;
&.show { &.show {
max-height: 16rem; max-height: 20rem;
opacity: 1; opacity: 1;
} }
@@ -370,7 +370,8 @@
// transition: all 0.2s ease-out; // transition: all 0.2s ease-out;
// opacity: 0.7; // opacity: 0.7;
&:hover { &:hover,
&.highlighted {
// opacity: 1; // opacity: 1;
stroke: $zbx-text-highlighted; stroke: $zbx-text-highlighted;
filter: url(#glowShadow); filter: url(#glowShadow);