problems timeline: use SVG

This commit is contained in:
Alexander Zobnin
2018-12-15 22:58:49 +03:00
parent 566db376a2
commit c115be1dee
2 changed files with 117 additions and 31 deletions

View File

@@ -42,6 +42,7 @@ export default class ProblemTimeline extends PureComponent<ProblemTimelineProps,
if (!this.rootRef) { if (!this.rootRef) {
return <div className="event-timeline" ref={this.setRootRef} />; return <div className="event-timeline" ref={this.setRootRef} />;
} }
const { events, timeRange } = this.props; const { events, timeRange } = this.props;
const { timeFrom, timeTo } = timeRange; const { timeFrom, timeTo } = timeRange;
const range = timeTo - timeFrom; const range = timeTo - timeFrom;
@@ -52,13 +53,14 @@ export default class ProblemTimeline extends PureComponent<ProblemTimelineProps,
const firstTs = events.length ? Number(events[0].clock) : timeTo; const firstTs = events.length ? Number(events[0].clock) : timeTo;
const duration = (firstTs - timeFrom) / range; const duration = (firstTs - timeFrom) / range;
const firstEventColor = events[0].value !== '1' ? this.props.problemColor : this.props.okColor; const firstEventColor = events[0].value !== '1' ? this.props.problemColor : this.props.okColor;
const firstEventStyle: React.CSSProperties = { const firstEventAttributes = {
width: duration * width, width: duration * width,
left: 0, x: 0,
background: firstEventColor, y: 0,
fill: firstEventColor,
}; };
firstItem = ( firstItem = (
<div key='0' className="problem-event-interval" style={firstEventStyle}></div> <rect key='0' className="problem-event-interval" {...firstEventAttributes}></rect>
); );
} }
@@ -68,14 +70,15 @@ export default class ProblemTimeline extends PureComponent<ProblemTimelineProps,
const duration = (nextTs - ts) / range; const duration = (nextTs - ts) / range;
const posLeft = (ts - timeFrom) / range * width; const posLeft = (ts - timeFrom) / range * width;
const eventColor = event.value === '1' ? this.props.problemColor : this.props.okColor; const eventColor = event.value === '1' ? this.props.problemColor : this.props.okColor;
const styles: React.CSSProperties = { const attributes = {
width: duration * width, width: duration * width,
left: posLeft, x: posLeft,
background: eventColor, y: 0,
fill: eventColor,
}; };
return ( return (
<div key={event.eventid} className="problem-event-interval" style={styles}></div> <rect key={event.eventid} className="problem-event-interval" {...attributes} />
); );
}); });
@@ -83,23 +86,97 @@ export default class ProblemTimeline extends PureComponent<ProblemTimelineProps,
const ts = Number(event.clock); const ts = Number(event.clock);
const posLeft = (ts - timeFrom) / range * width - EVENT_ITEM_SIZE / 2; const posLeft = (ts - timeFrom) / range * width - EVENT_ITEM_SIZE / 2;
const eventColor = event.value === '1' ? this.props.problemColor : this.props.okColor; const eventColor = event.value === '1' ? this.props.problemColor : this.props.okColor;
const styles: React.CSSProperties = {
transform: `translate(${posLeft}px, -2px)`,
// background: eventColor,
borderColor: eventColor,
};
return ( return (
<div key={event.eventid} className="problem-event-item" style={styles}></div> <TimelinePoint
key={event.eventid}
className="problem-event-item"
x={posLeft}
r={10}
color={eventColor}
/>
); );
}); });
return ( return (
<div className="event-timeline" ref={this.setRootRef}> <div className="event-timeline" ref={this.setRootRef}>
{firstItem} <svg className="event-timeline-canvas" viewBox={`0 0 ${width} 40`}>
{eventsIntervalItems} <defs>
{eventsItems} <filter id="dropShadow" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur in="SourceAlpha" stdDeviation="2" />
<feOffset dx="1" dy="1" />
<feMerge>
<feMergeNode />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
<filter id="glowShadow" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur in="SourceGraphic" stdDeviation="2" />
<feMerge>
<feMergeNode />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
<filter id="timelinePointBlur" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur in="SourceGraphic" stdDeviation="2" result="blurOut" />
</filter>
</defs>
<g className="event-timeline-group">
<g className="event-timeline-regions">
{firstItem}
{eventsIntervalItems}
</g>
<g className="timeline-points" transform={`translate(0, 6)`}>
{eventsItems}
</g>
</g>
</svg>
</div> </div>
); );
} }
} }
function TimelineRegion(props) {
return (
<rect></rect>
);
}
interface TimelinePointProps {
x: number;
r: number;
color: string;
className?: string;
}
class TimelinePoint extends PureComponent<TimelinePointProps, any> {
constructor(props) {
super(props);
this.state = { r: this.props.r };
}
handleMouseOver = () => {
this.setState({ r: this.props.r * 1.2 });
}
handleMouseLeave = () => {
this.setState({ r: this.props.r });
}
render() {
const { x, color, className } = this.props;
const r = this.state.r;
const cx = x + this.props.r;
const rInner = Math.floor(r * 0.6);
return (
<g className={className}
transform={`translate(${cx}, 0)`}
filter="url(#dropShadow)"
onMouseOver={this.handleMouseOver}
onMouseLeave={this.handleMouseLeave}>
<circle cx={0} cy={0} r={r} fill={color} className="point-border" />
<circle cx={0} cy={0} r={rInner} fill="#000000" className="point-core" />
</g>
);
}
}

View File

@@ -343,32 +343,41 @@
margin: 1.6rem 0; margin: 1.6rem 0;
// margin-top: auto; // margin-top: auto;
svg.event-timeline-canvas {
height: 40px;
g.event-timeline-group {
height: 40px;
transform: translate(0px, 14px);
}
}
.problem-event-interval { .problem-event-interval {
height: 12px; height: 12px;
position: absolute; // transition: all 0.2s ease-out;
// opacity: 0.7; // opacity: 0.7;
&:hover { &:hover {
// opacity: 1; // opacity: 1;
border: 1px solid $blue; stroke: $zbx-text-highlighted;
box-shadow: 0px 0px 5px rgba($blue, 0.5); filter: url(#glowShadow);
} }
} }
.problem-event-item { .problem-event-item {
position: absolute; circle {
width: 16px; transition: all 0.2s ease-out;
height: 16px; }
// transform: translate(0px, -2px);
background: $problem-statusbar-background;
border: 4px solid $blue;
border-radius: 16px;
z-index: 10;
&:hover { &:hover {
box-shadow: 0px 0px 6px 1px rgba($orange, 1); circle.point-border {
background-color: $zbx-text-highlighted; stroke: $zbx-text-highlighted;
z-index: 11; stroke-width: 1;
filter: url(#glowShadow);
}
.point-core {
fill: $zbx-text-highlighted;
}
} }
} }
} }