forked from p34709852/monkey
Updated the VennDiagram
This commit is contained in:
parent
bb1ee6ff14
commit
9538c3f0e6
|
@ -1,5 +1,6 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import {Popover, OverlayTrigger} from 'react-bootstrap';
|
||||||
import * as d3 from 'd3'
|
import * as d3 from 'd3'
|
||||||
|
|
||||||
class ArcNode extends React.Component {
|
class ArcNode extends React.Component {
|
||||||
|
@ -10,6 +11,10 @@ class ArcNode extends React.Component {
|
||||||
let id = prefix + 'Node_' + index;
|
let id = prefix + 'Node_' + index;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<OverlayTrigger key={prefix + 'ArcOverlayTrigger' + index} trigger={['hover', 'focus']} placement={data.popover}
|
||||||
|
overlay={<Popover key={prefix + 'ArcTooltip' + index} id={prefix + 'Popover'}
|
||||||
|
style={{backgroundColor: data.hex}}
|
||||||
|
title={data.node.pillar}>{data.tooltip}</Popover>}>
|
||||||
<g transform={'rotate(180)'} id={data.node.pillar}>
|
<g transform={'rotate(180)'} id={data.node.pillar}>
|
||||||
<path
|
<path
|
||||||
|
|
||||||
|
@ -27,6 +32,7 @@ class ArcNode extends React.Component {
|
||||||
</textPath>
|
</textPath>
|
||||||
</text>
|
</text>
|
||||||
</g>
|
</g>
|
||||||
|
</OverlayTrigger>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,19 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import PillarLabel from "../PillarLabel";
|
||||||
|
import {Popover, OverlayTrigger} from 'react-bootstrap';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
class CircularNode extends React.Component {
|
class CircularNode extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
let {prefix, index, data} = this.props;
|
let {prefix, index, data} = this.props;
|
||||||
|
|
||||||
let tspans = data.label.split("|").map((d_, i_) => {
|
|
||||||
let halfTextHeight = data.fontStyle.size * data.label.split("|").length / 2;
|
|
||||||
let key = 'vennDiagramCircularNode' + index + '_Tspan' + i_;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<tspan key={key} x={data.offset.x} y={data.offset.y - halfTextHeight + i_ * data.fontStyle.size}>{d_}</tspan>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
let translate = 'translate(' + data.cx + ',' + data.cy + ')';
|
let translate = 'translate(' + data.cx + ',' + data.cy + ')';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<OverlayTrigger key={prefix + 'CircularOverlayTrigger' + index} trigger={['hover', 'focus']}
|
||||||
|
placement={data.popover}
|
||||||
|
overlay={<Popover key={prefix + 'CircularTooltip' + index} id={prefix + 'Popover'}
|
||||||
|
style={{backgroundColor: data.hex}}
|
||||||
|
title={data.node.pillar}>{data.tooltip}</Popover>}>
|
||||||
<g transform={translate} id={data.node.pillar}>
|
<g transform={translate} id={data.node.pillar}>
|
||||||
<circle
|
<circle
|
||||||
id={prefix + 'Node_' + index}
|
id={prefix + 'Node_' + index}
|
||||||
|
@ -26,11 +23,14 @@ class CircularNode extends React.Component {
|
||||||
opacity={0.8}
|
opacity={0.8}
|
||||||
fill={data.hex}
|
fill={data.hex}
|
||||||
/>
|
/>
|
||||||
<text textAnchor='middle' fill={data.fontStyle.color} dominantBaseline={'middle'}
|
<foreignObject style={{fontSize: data.fontStyle.size, pointerEvents: 'none'}}
|
||||||
fontSize={data.fontStyle.size + 'px'} pointerEvents={'none'}>
|
key={prefix + 'PillarLabel' + index} x={data.offset.x - data.fontStyle.size * 6}
|
||||||
{tspans}
|
y={data.offset.y - data.fontStyle.size} width={data.fontStyle.size * 12}
|
||||||
</text>
|
height={data.fontStyle.size * 6}>
|
||||||
|
<PillarLabel pillar={data.node.pillar} status={data.status}/>
|
||||||
|
</foreignObject>
|
||||||
</g>
|
</g>
|
||||||
|
</OverlayTrigger>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
import React from 'react'
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
class Tooltip extends React.Component {
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {prefix, bcolor, top, left, display, html} = this.props;
|
|
||||||
|
|
||||||
const style = {
|
|
||||||
backgroundColor: bcolor,
|
|
||||||
border: '1px solid #FFFFFF',
|
|
||||||
borderRadius: '2px',
|
|
||||||
fontSize: 10,
|
|
||||||
padding: 8,
|
|
||||||
display,
|
|
||||||
opacity: 0.9,
|
|
||||||
position: 'fixed',
|
|
||||||
top,
|
|
||||||
left,
|
|
||||||
pointerEvents: 'none'
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
|
|
||||||
<div className='tooltip' style={style}>
|
|
||||||
{html.split('\n').map((i_, key_) => {
|
|
||||||
return <div key={prefix + 'Element' + key_}>{i_}</div>;
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Tooltip.propTypes = {
|
|
||||||
prefix: PropTypes.string,
|
|
||||||
bcolor: PropTypes.string,
|
|
||||||
top: PropTypes.number,
|
|
||||||
left: PropTypes.number,
|
|
||||||
display: PropTypes.string,
|
|
||||||
html: PropTypes.string
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Tooltip;
|
|
|
@ -1,5 +1,4 @@
|
||||||
export class TypographicUtilities {
|
export class TypographicUtilities {
|
||||||
|
|
||||||
static removeAmpersand(string_) {
|
static removeAmpersand(string_) {
|
||||||
return string_.replace(' & ', 'And');
|
return string_.replace(' & ', 'And');
|
||||||
}
|
}
|
||||||
|
@ -7,9 +6,4 @@ export class TypographicUtilities {
|
||||||
static removeBrokenBar(string_) {
|
static removeBrokenBar(string_) {
|
||||||
return string_.replace(/\|/g, ' ');
|
return string_.replace(/\|/g, ' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
static setTitle(string_) {
|
|
||||||
return string_.charAt(0).toUpperCase() + string_.substr(1).toLowerCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import Tooltip from './Tooltip'
|
|
||||||
import CircularNode from './CircularNode'
|
import CircularNode from './CircularNode'
|
||||||
import ArcNode from './ArcNode'
|
import ArcNode from './ArcNode'
|
||||||
import {TypographicUtilities} from './Utility.js'
|
import {TypographicUtilities} from './Utility.js'
|
||||||
|
@ -14,7 +13,6 @@ class VennDiagram extends React.Component {
|
||||||
|
|
||||||
this.width = this.height = 512;
|
this.width = this.height = 512;
|
||||||
|
|
||||||
this.colors = ['#777777', '#D9534F', '#F0AD4E', '#5CB85C'];
|
|
||||||
this.prefix = 'vennDiagram';
|
this.prefix = 'vennDiagram';
|
||||||
this.suffices = ['', '|tests are|conclusive', '|tests were|inconclusive', '|tests|performed'];
|
this.suffices = ['', '|tests are|conclusive', '|tests were|inconclusive', '|tests|performed'];
|
||||||
this.fontStyles = [{size: Math.max(9, this.width / 32), color: 'white'}, {
|
this.fontStyles = [{size: Math.max(9, this.width / 32), color: 'white'}, {
|
||||||
|
@ -28,19 +26,27 @@ class VennDiagram extends React.Component {
|
||||||
this.width2By7 = 2 * this.width / 7;
|
this.width2By7 = 2 * this.width / 7;
|
||||||
this.width1By11 = this.width / 11;
|
this.width1By11 = this.width / 11;
|
||||||
this.width1By28 = this.width / 28;
|
this.width1By28 = this.width / 28;
|
||||||
|
this.arcNodesGap = 4;
|
||||||
|
|
||||||
this.toggle = false;
|
this.toggle = false;
|
||||||
|
|
||||||
this.layout = {
|
this.layout = {
|
||||||
Data: {cx: 0, cy: 0, r: this.thirdWidth - this.offset * 2, offset: {x: 0, y: 0}},
|
Data: {cx: 0, cy: 0, r: this.sixthWidth, offset: {x: 0, y: 0}, popover: 'top'},
|
||||||
People: {cx: -this.width2By7, cy: 0, r: this.sixthWidth, offset: {x: this.width1By11, y: 0}},
|
People: {cx: -this.width2By7, cy: 0, r: this.sixthWidth, offset: {x: this.width1By11, y: 0}, popover: 'right'},
|
||||||
Networks: {cx: this.width2By7, cy: 0, r: this.sixthWidth, offset: {x: -this.width1By11, y: 0}},
|
Networks: {cx: this.width2By7, cy: 0, r: this.sixthWidth, offset: {x: -this.width1By11, y: 0}, popover: 'left'},
|
||||||
Devices: {cx: 0, cy: this.width2By7, r: this.sixthWidth, offset: {x: 0, y: -this.width1By11}},
|
Devices: {cx: 0, cy: this.width2By7, r: this.sixthWidth, offset: {x: 0, y: -this.width1By11}, popover: 'top'},
|
||||||
Workloads: {cx: 0, cy: -this.width2By7, r: this.sixthWidth, offset: {x: 0, y: this.width1By11}},
|
Workloads: {
|
||||||
VisibilityAndAnalytics: {inner: this.thirdWidth - this.width1By28, outer: this.thirdWidth},
|
cx: 0,
|
||||||
|
cy: -this.width2By7,
|
||||||
|
r: this.sixthWidth,
|
||||||
|
offset: {x: 0, y: this.width1By11},
|
||||||
|
popover: 'bottom'
|
||||||
|
},
|
||||||
|
VisibilityAndAnalytics: {inner: this.thirdWidth - this.width1By28, outer: this.thirdWidth, popover: 'left'},
|
||||||
AutomationAndOrchestration: {
|
AutomationAndOrchestration: {
|
||||||
inner: this.thirdWidth - this.width1By28 * 2,
|
inner: this.thirdWidth - this.width1By28 * 2 - this.arcNodesGap,
|
||||||
outer: this.thirdWidth - this.width1By28
|
outer: this.thirdWidth - this.width1By28 - this.arcNodesGap,
|
||||||
|
popover: 'right'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -65,37 +71,36 @@ class VennDiagram extends React.Component {
|
||||||
this.rules = [
|
this.rules = [
|
||||||
|
|
||||||
{
|
{
|
||||||
id: 'Rule #1', f: function (d_) {
|
id: 'Rule #1', status: 'Unexecuted', hex: '#777777', f: function (d_) {
|
||||||
return d_['Conclusive'] + d_['Inconclusive'] + d_['Positive'] === 0;
|
return d_['Conclusive'] + d_['Inconclusive'] + d_['Positive'] === 0;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'Rule #2', f: function (d_) {
|
id: 'Rule #2', status: 'Conclusive', hex: '#D9534F', f: function (d_) {
|
||||||
return d_['Conclusive'] > 0;
|
return d_['Conclusive'] > 0;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'Rule #3', f: function (d_) {
|
id: 'Rule #3', status: 'Inconclusive', hex: '#F0AD4E', f: function (d_) {
|
||||||
return d_['Conclusive'] === 0 && d_['Inconclusive'] > 0;
|
return d_['Conclusive'] === 0 && d_['Inconclusive'] > 0;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'Rule #4', f: function (d_) {
|
id: 'Rule #4', status: 'Positive', hex: '#5CB85C', f: function (d_) {
|
||||||
return d_['Positive'] + d_['Unexecuted'] >= 2 && d_['Positive'] * d_['Unexecuted'] > 0;
|
return d_['Positive'] + d_['Unexecuted'] >= 2 && d_['Positive'] * d_['Unexecuted'] > 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
this._onScroll = this._onScroll.bind(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.parseData();
|
this.parseData();
|
||||||
window.addEventListener('scroll', this._onScroll);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_onMouseMove(e) {
|
_onMouseMove(e) {
|
||||||
|
|
||||||
let self = this;
|
let self = this;
|
||||||
|
|
||||||
if (!this.toggle) {
|
if (!this.toggle) {
|
||||||
|
@ -108,16 +113,12 @@ class VennDiagram extends React.Component {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (e.target.id.includes('Node')) {
|
if (e.target.id.includes('Node')) {
|
||||||
html = e.target.dataset.tooltip;
|
|
||||||
this.divElement.style.cursor = 'pointer';
|
|
||||||
hidden = 'block';
|
|
||||||
e.target.setAttribute('opacity', 0.95);
|
e.target.setAttribute('opacity', 0.95);
|
||||||
bcolor = e.target.getAttribute('fill');
|
|
||||||
|
|
||||||
// Set highest z-index
|
// Set highest z-index
|
||||||
e.target.parentNode.parentNode.appendChild(e.target.parentNode);
|
e.target.parentNode.parentNode.appendChild(e.target.parentNode);
|
||||||
} else {
|
} else {
|
||||||
this.divElement.style.cursor = 'default';
|
|
||||||
|
|
||||||
// Return z indices to default
|
// Return z indices to default
|
||||||
Object.keys(this.layout).forEach(function (d_, i_) {
|
Object.keys(this.layout).forEach(function (d_, i_) {
|
||||||
|
@ -125,23 +126,7 @@ class VennDiagram extends React.Component {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({
|
|
||||||
target: e,
|
|
||||||
tooltip: {
|
|
||||||
target: e.target,
|
|
||||||
bcolor: bcolor,
|
|
||||||
top: e.clientY + 8,
|
|
||||||
left: e.clientX + 8,
|
|
||||||
display: hidden,
|
|
||||||
html: html
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_onScroll(e) {
|
|
||||||
this.divElement.style.cursor = 'default';
|
|
||||||
this.setState({target: null, tooltip: {target: null, bcolor: 'none', top: 0, left: 0, display: 'none', html: ''}});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_onClick(e) {
|
_onClick(e) {
|
||||||
|
@ -152,6 +137,7 @@ class VennDiagram extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
parseData() {
|
parseData() {
|
||||||
|
|
||||||
let self = this;
|
let self = this;
|
||||||
let data = [];
|
let data = [];
|
||||||
const omit = (prop, {[prop]: _, ...rest}) => rest;
|
const omit = (prop, {[prop]: _, ...rest}) => rest;
|
||||||
|
@ -159,9 +145,8 @@ class VennDiagram extends React.Component {
|
||||||
this.props.pillarsGrades.forEach((d_, i_) => {
|
this.props.pillarsGrades.forEach((d_, i_) => {
|
||||||
|
|
||||||
let params = omit('pillar', d_);
|
let params = omit('pillar', d_);
|
||||||
let sum = Object.keys(params).reduce((sum_, key_) => sum_ + parseFloat(params[key_] || 0), 0);
|
|
||||||
let key = TypographicUtilities.removeAmpersand(d_.pillar);
|
let key = TypographicUtilities.removeAmpersand(d_.pillar);
|
||||||
let html = self.buildTooltipHtmlContent(d_);
|
let html = self.buildTooltipHtmlContent(params);
|
||||||
let rule = null;
|
let rule = null;
|
||||||
|
|
||||||
for (let j = 0; j < self.rules.length; j++) {
|
for (let j = 0; j < self.rules.length; j++) {
|
||||||
|
@ -181,7 +166,13 @@ class VennDiagram extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTooltipHtmlContent(object_) {
|
buildTooltipHtmlContent(object_) {
|
||||||
return Object.keys(object_).reduce((out_, key_) => out_ + TypographicUtilities.setTitle(key_) + ': ' + object_[key_] + '\n', '');
|
|
||||||
|
var out = [];
|
||||||
|
Object.keys(object_).forEach(function (d_) {
|
||||||
|
out.push([d_ + ': ' + object_[d_], <br/>]);
|
||||||
|
});
|
||||||
|
return out;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setLayoutElement(rule_, key_, html_, d_) {
|
setLayoutElement(rule_, key_, html_, d_) {
|
||||||
|
@ -195,7 +186,8 @@ class VennDiagram extends React.Component {
|
||||||
this.layout[key_].fontStyle = this.fontStyles[1];
|
this.layout[key_].fontStyle = this.fontStyles[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
this.layout[key_].hex = this.colors[rule_];
|
this.layout[key_].hex = this.rules[rule_].hex;
|
||||||
|
this.layout[key_].status = this.rules[rule_].status;
|
||||||
this.layout[key_].label = d_.pillar + this.suffices[rule_];
|
this.layout[key_].label = d_.pillar + this.suffices[rule_];
|
||||||
this.layout[key_].node = d_;
|
this.layout[key_].node = d_;
|
||||||
this.layout[key_].tooltip = html_;
|
this.layout[key_].tooltip = html_;
|
||||||
|
@ -220,7 +212,6 @@ class VennDiagram extends React.Component {
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
d_.label = TypographicUtilities.removeBrokenBar(d_.label);
|
d_.label = TypographicUtilities.removeBrokenBar(d_.label);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ArcNode
|
<ArcNode
|
||||||
prefix={this.prefix}
|
prefix={this.prefix}
|
||||||
|
@ -239,7 +230,6 @@ class VennDiagram extends React.Component {
|
||||||
xmlns='http://www.w3.org/2000/svg' xmlnsXlink='http://www.w3.org/1999/xlink'>
|
xmlns='http://www.w3.org/2000/svg' xmlnsXlink='http://www.w3.org/1999/xlink'>
|
||||||
{nodes}
|
{nodes}
|
||||||
</svg>
|
</svg>
|
||||||
<Tooltip id={this.prefix + 'Tooltip'} prefix={this.prefix} {...this.state.tooltip} />
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue