forked from p34709852/monkey
Venn diagram - new version from Vladimir
This commit is contained in:
parent
641308c91b
commit
8136c31476
|
@ -1,9 +1,14 @@
|
||||||
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 { 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 {
|
||||||
|
|
||||||
|
handleClick(e_) { this.props.disableHover(this.refs.overlay); }
|
||||||
|
handleOver(e_) { if(this.props.hover) { this.refs.overlay.show(); } }
|
||||||
|
handleOut(e_) { if(this.props.hover){ this.refs.overlay.hide(); } }
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let {prefix, index, data} = this.props;
|
let {prefix, index, data} = this.props;
|
||||||
|
|
||||||
|
@ -11,29 +16,29 @@ class ArcNode extends React.Component {
|
||||||
let id = prefix + 'Node_' + index;
|
let id = prefix + 'Node_' + index;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<OverlayTrigger key={prefix + 'arcGroup' + index} trigger={['hover', 'focus']} placement={data.popover}
|
<g transform={'rotate(180)'} id={data.node.pillar} key={prefix + 'arcGroup' + index}>
|
||||||
overlay={<Popover key={prefix + 'ArcTooltip' + index} id={prefix + 'Popover' + index}
|
<OverlayTrigger ref={'overlay'} key={prefix + 'arcOverlayTrigger' + index} trigger={null} placement={data.popover} overlay={<Popover id={prefix + 'ArcPopover' + index} style={{backgroundColor: data.hex}} title={data.node.pillar}>{data.tooltip}</Popover>} rootClose>
|
||||||
style={{backgroundColor: data.hex}}
|
<path
|
||||||
title={data.node.pillar}>{data.tooltip}</Popover>}>
|
|
||||||
<g transform={'rotate(180)'} id={data.node.pillar} key={prefix + 'arcGroup' + index}>
|
|
||||||
<path
|
|
||||||
|
|
||||||
id={prefix + 'Node_' + index}
|
id={prefix + 'Node_' + index}
|
||||||
className={'arcNode'}
|
className={'arcNode'}
|
||||||
data-tooltip={data.tooltip}
|
data-tooltip={data.tooltip}
|
||||||
d={arc()}
|
d={arc()}
|
||||||
fill={data.hex}
|
fill={data.hex}
|
||||||
|
onClick={this.handleClick.bind(this)}
|
||||||
|
onMouseEnter={this.handleOver.bind(this)}
|
||||||
|
onMouseLeave={this.handleOut.bind(this)}
|
||||||
|
|
||||||
/>
|
/>
|
||||||
<text x={0} dy={data.fontStyle.size * 1.75} fontSize={data.fontStyle.size} fill={'white'} textAnchor='middle'
|
</OverlayTrigger>
|
||||||
pointerEvents={'none'}>
|
<text x={0} dy={data.fontStyle.size * 1.2} fontSize={data.fontStyle.size} fill={'white'} textAnchor='middle'
|
||||||
<textPath href={'#' + id} startOffset={'26.4%'}>
|
pointerEvents={'none'}>
|
||||||
<tspan fontFamily={'FontAwesome'}>{data.icon + '\u2000'}</tspan>
|
<textPath href={'#' + id} startOffset={'26.4%'}>
|
||||||
<tspan>{data.label}</tspan>
|
<tspan fontFamily={'FontAwesome'}>{data.icon + '\u2000'}</tspan>
|
||||||
</textPath>
|
<tspan>{data.label}</tspan>
|
||||||
</text>
|
</textPath>
|
||||||
</g>
|
</text>
|
||||||
</OverlayTrigger>
|
</g>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,35 +1,38 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import PillarLabel from "../PillarLabel";
|
import PillarLabel from "../PillarLabel";
|
||||||
import {Popover, OverlayTrigger} from 'react-bootstrap';
|
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 {
|
||||||
|
|
||||||
|
handleClick(e_) { this.props.disableHover(this.refs.overlay); }
|
||||||
|
handleOver(e_) { if(this.props.hover) { this.refs.overlay.show(); } }
|
||||||
|
handleOut(e_) { if(this.props.hover){ this.refs.overlay.hide(); } }
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let {prefix, index, data} = this.props;
|
let {prefix, index, data} = this.props;
|
||||||
|
|
||||||
let translate = 'translate(' + data.cx + ',' + data.cy + ')';
|
let translate = 'translate(' + data.cx + ',' + data.cy + ')';
|
||||||
return (
|
return (
|
||||||
<OverlayTrigger key={prefix + 'circularGroup' + index} trigger={['hover', 'focus']} placement={data.popover}
|
<g transform={translate} id={data.node.pillar} key={prefix + 'circularGroup' + index}>
|
||||||
overlay={<Popover key={prefix + 'CircularTooltip' + index} id={prefix + 'Popover' + index}
|
<OverlayTrigger ref={'overlay'} key={prefix + 'CircularOverlay' + index} trigger={null} placement={data.popover} overlay={<Popover id={prefix + 'CircularClickPopover' + index} style={{backgroundColor : data.hex}} title={data.node.pillar}>{data.tooltip}</Popover>} rootClose>
|
||||||
style={{backgroundColor: data.hex}}
|
<circle
|
||||||
title={data.node.pillar}>{data.tooltip}</Popover>}>
|
id={prefix + 'Node_' + index}
|
||||||
<g transform={translate} id={data.node.pillar} key={prefix + 'circularGroup' + index}>
|
className={'circularNode'}
|
||||||
<circle
|
data-tooltip={data.tooltip}
|
||||||
id={prefix + 'Node_' + index}
|
r={data.r}
|
||||||
className={'circularNode'}
|
opacity={0.8}
|
||||||
data-tooltip={data.tooltip}
|
fill={data.hex}
|
||||||
r={data.r}
|
onClick={this.handleClick.bind(this)}
|
||||||
opacity={0.8}
|
onMouseEnter={this.handleOver.bind(this)}
|
||||||
fill={data.hex}
|
onMouseLeave={this.handleOut.bind(this)}
|
||||||
/>
|
|
||||||
<foreignObject style={{fontSize: data.fontStyle.size, pointerEvents: 'none'}}
|
/>
|
||||||
key={prefix + 'PillarLabel' + index} x={data.offset.x - data.fontStyle.size * 6}
|
</OverlayTrigger>
|
||||||
y={data.offset.y - data.fontStyle.size} width={data.fontStyle.size * 12}
|
<foreignObject style={{fontSize: data.fontStyle.size, pointerEvents: 'none'}} key={prefix + 'PillarLabelOject' + index} x={data.offset.x - data.fontStyle.size * 6} y={data.offset.y - data.fontStyle.size} width={ data.fontStyle.size * 12} height={data.fontStyle.size * 6}>
|
||||||
height={data.fontStyle.size * 6}>
|
<PillarLabel key={prefix + 'PillarLabel' + index} pillar={data.node.pillar} status={data.status} />
|
||||||
<PillarLabel pillar={data.node.pillar} status={data.status}/>
|
</foreignObject>
|
||||||
</foreignObject>
|
</g>
|
||||||
</g>
|
|
||||||
</OverlayTrigger>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,44 +10,29 @@ class VennDiagram extends React.Component {
|
||||||
constructor(props_) {
|
constructor(props_) {
|
||||||
super(props_);
|
super(props_);
|
||||||
|
|
||||||
this.state = {tooltip: {top: 0, left: 0, display: 'none', html: ''}};
|
this.state = {hover: true, currentPopover: undefined};
|
||||||
|
this._disableHover = this._disableHover.bind(this);
|
||||||
|
|
||||||
this.width = this.height = 512;
|
this.width = this.height = 512;
|
||||||
|
|
||||||
this.prefix = 'vennDiagram';
|
this.prefix = 'vennDiagram';
|
||||||
this.fontStyles = [{size: Math.max(9, this.width / 32), color: 'white'}, {
|
this.fontStyles = [{size: Math.max(9, this.width / 28), color: 'white'}, { size: Math.max(6, this.width / 38), color: 'white'}, { size: Math.max(6, this.width / 48), color: 'white'} ];
|
||||||
size: Math.max(6, this.width / 80),
|
|
||||||
color: 'black'
|
|
||||||
}];
|
|
||||||
this.offset = this.width / 16;
|
this.offset = this.width / 16;
|
||||||
|
|
||||||
this.thirdWidth = this.width / 3;
|
this.thirdWidth = this.width / 3;
|
||||||
this.sixthWidth = this.width / 6;
|
this.width11By2 = this.width / 5.5;
|
||||||
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.arcNodesGap = 4;
|
||||||
|
|
||||||
this.toggle = false;
|
|
||||||
|
|
||||||
this.layout = {
|
this.layout = {
|
||||||
Data: {cx: 0, cy: 0, r: this.sixthWidth, offset: {x: 0, y: 0}, popover: 'top'},
|
Data: {cx: 0, cy: 0, r: this.width11By2, offset: {x: 0, y: 0}, popover: 'top'},
|
||||||
People: {cx: -this.width2By7, cy: 0, r: this.sixthWidth, offset: {x: this.width1By11, y: 0}, popover: 'right'},
|
People: {cx: -this.width2By7, cy: 0, r: this.width11By2, offset: {x: this.width1By11 + this.fontStyles[1].size / 5 * 3, y: 0}, popover: 'right'},
|
||||||
Networks: {cx: this.width2By7, cy: 0, r: this.sixthWidth, offset: {x: -this.width1By11, y: 0}, popover: 'left'},
|
Networks: {cx: this.width2By7, cy: 0, r: this.width11By2, offset: {x: -this.width1By11 - this.fontStyles[1].size / 5 * 3, y: 0}, popover: 'left'},
|
||||||
Devices: {cx: 0, cy: this.width2By7, r: this.sixthWidth, offset: {x: 0, y: -this.width1By11}, popover: 'top'},
|
Devices: {cx: 0, cy: this.width2By7, r: this.width11By2, offset: {x: 0, y: -this.width1By11 + this.fontStyles[1].size / 6 * 3}, popover: 'top'},
|
||||||
Workloads: {
|
Workloads: {cx: 0, cy: -this.width2By7, r: this.width11By2, offset: {x: 0, y: this.width1By11}, popover: 'bottom' },
|
||||||
cx: 0,
|
VisibilityAndAnalytics: {inner: this.thirdWidth - this.width1By28, outer: this.thirdWidth, icon: '\uf070', popover: 'left'},
|
||||||
cy: -this.width2By7,
|
|
||||||
r: this.sixthWidth,
|
|
||||||
offset: {x: 0, y: this.width1By11},
|
|
||||||
popover: 'bottom'
|
|
||||||
},
|
|
||||||
VisibilityAndAnalytics: {
|
|
||||||
inner: this.thirdWidth - this.width1By28,
|
|
||||||
outer: this.thirdWidth,
|
|
||||||
icon: '\uf070',
|
|
||||||
popover: 'right'
|
|
||||||
},
|
|
||||||
AutomationAndOrchestration: {
|
AutomationAndOrchestration: {
|
||||||
inner: this.thirdWidth - this.width1By28 * 2 - this.arcNodesGap,
|
inner: this.thirdWidth - this.width1By28 * 2 - this.arcNodesGap,
|
||||||
outer: this.thirdWidth - this.width1By28 - this.arcNodesGap,
|
outer: this.thirdWidth - this.width1By28 - this.arcNodesGap,
|
||||||
|
@ -68,6 +53,8 @@ class VennDiagram extends React.Component {
|
||||||
sum(C, I) > 0 and C * I = 0, while C has to be 0
|
sum(C, I) > 0 and C * I = 0, while C has to be 0
|
||||||
|
|
||||||
RULE #4: By process of elimination, passed.
|
RULE #4: By process of elimination, passed.
|
||||||
|
if the P is bigger by 2 then negative U, first conditional
|
||||||
|
would be true.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -98,42 +85,51 @@ class VennDiagram extends React.Component {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() { this.parseData(); if(this.state.currentPopover !== undefined) { this.state.currentPopover.show(); } }
|
||||||
this.parseData();
|
|
||||||
}
|
_disableHover(ref_) { this.setState({hover: false, currentPopover: ref_, data: this.state.data }); }
|
||||||
|
|
||||||
_onMouseMove(e) {
|
_onMouseMove(e) {
|
||||||
|
|
||||||
let self = this;
|
let self = this;
|
||||||
|
|
||||||
|
let hidden = 'none';
|
||||||
|
let html = '';
|
||||||
|
let bcolor = '#DEDEDE';
|
||||||
|
|
||||||
|
if(this.state.currentPopover !== undefined) { this.state.currentPopover.show(); }
|
||||||
|
|
||||||
if (!this.toggle) {
|
document.querySelectorAll('circle, path').forEach((d_, i_) => {
|
||||||
let hidden = 'none';
|
d_.setAttribute('opacity', "0.8");
|
||||||
let html = '';
|
});
|
||||||
let bcolor = '#DEDEDE';
|
|
||||||
|
|
||||||
document.querySelectorAll('circle, path').forEach((d_, i_) => {
|
if (e.target.id.includes('Node')) {
|
||||||
d_.setAttribute('opacity', "0.8");
|
|
||||||
});
|
|
||||||
|
|
||||||
if (e.target.id.includes('Node')) {
|
|
||||||
|
|
||||||
e.target.setAttribute('opacity', 0.95);
|
e.target.setAttribute('opacity', 0.95);
|
||||||
|
|
||||||
// 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 {
|
||||||
|
|
||||||
// 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_) {
|
||||||
document.querySelector('#' + self.prefix).appendChild(document.querySelector('#' + self.prefix + 'Node_' + i_).parentNode);
|
document.querySelector('#' + self.prefix).appendChild(document.querySelector('#' + self.prefix + 'Node_' + i_).parentNode); })
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_onClick(e) {
|
||||||
|
|
||||||
|
if (!e.target.id.includes('Node')) {
|
||||||
|
|
||||||
|
this.state.currentPopover.hide();
|
||||||
|
this.setState({hover: true, currentPopover: undefined, data: this.state.data });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
parseData() {
|
parseData() {
|
||||||
|
|
||||||
let self = this;
|
let self = this;
|
||||||
let data = [];
|
let data = [];
|
||||||
const omit = (prop, {[prop]: _, ...rest}) => rest;
|
const omit = (prop, {[prop]: _, ...rest}) => rest;
|
||||||
|
@ -158,30 +154,28 @@ class VennDiagram extends React.Component {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.setState({data: data});
|
this.setState({hover: true, activePopover: undefined, data: data});
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTooltipHtmlContent(object_) {
|
buildTooltipHtmlContent(object_) {
|
||||||
|
|
||||||
var out = [];
|
return Object.keys(object_).map((key_, i_) => { return ( <p key={this.prefix + key_ + i_}>{key_}: {object_[key_]}</p> ) })
|
||||||
Object.keys(object_).forEach(function (d_) {
|
|
||||||
out.push([d_ + ': ' + object_[d_], <br/>]);
|
|
||||||
});
|
|
||||||
return out;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setLayoutElement(rule_, key_, html_, d_) {
|
setLayoutElement(rule_, key_, html_, d_) {
|
||||||
if (rule_ == null) {
|
|
||||||
throw Error('The node scores are invalid');
|
if(rule_ === null) { console.log(Error('The node scores are invalid, please check the data or the rules set.')); }
|
||||||
}
|
|
||||||
|
|
||||||
if (key_ === 'Data') {
|
if (key_ === 'Data') {
|
||||||
this.layout[key_].fontStyle = this.fontStyles[0];
|
this.layout[key_].fontStyle = this.fontStyles[0];
|
||||||
} else {
|
}
|
||||||
|
else if(this.layout[key_].hasOwnProperty('cx')){
|
||||||
this.layout[key_].fontStyle = this.fontStyles[1];
|
this.layout[key_].fontStyle = this.fontStyles[1];
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
this.layout[key_].fontStyle = this.fontStyles[2];
|
||||||
|
}
|
||||||
|
|
||||||
this.layout[key_].hex = this.rules[rule_].hex;
|
this.layout[key_].hex = this.rules[rule_].hex;
|
||||||
this.layout[key_].status = this.rules[rule_].status;
|
this.layout[key_].status = this.rules[rule_].status;
|
||||||
|
@ -196,7 +190,6 @@ class VennDiagram extends React.Component {
|
||||||
} else {
|
} else {
|
||||||
// equivalent to center translate (width/2, height/2)
|
// equivalent to center translate (width/2, height/2)
|
||||||
let viewPortParameters = (-this.width / 2) + ' ' + (-this.height / 2) + ' ' + this.width + ' ' + this.height;
|
let viewPortParameters = (-this.width / 2) + ' ' + (-this.height / 2) + ' ' + this.width + ' ' + this.height;
|
||||||
|
|
||||||
let nodes = Object.values(this.layout).map((d_, i_) => {
|
let nodes = Object.values(this.layout).map((d_, i_) => {
|
||||||
if (d_.hasOwnProperty('cx')) {
|
if (d_.hasOwnProperty('cx')) {
|
||||||
return (
|
return (
|
||||||
|
@ -205,6 +198,8 @@ class VennDiagram extends React.Component {
|
||||||
key={this.prefix + 'CircularNode' + i_}
|
key={this.prefix + 'CircularNode' + i_}
|
||||||
index={i_}
|
index={i_}
|
||||||
data={d_}
|
data={d_}
|
||||||
|
hover={this.state.hover}
|
||||||
|
disableHover={this._disableHover}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
@ -215,17 +210,19 @@ class VennDiagram extends React.Component {
|
||||||
key={this.prefix + 'ArcNode' + i_}
|
key={this.prefix + 'ArcNode' + i_}
|
||||||
index={i_}
|
index={i_}
|
||||||
data={d_}
|
data={d_}
|
||||||
|
hover={this.state.hover}
|
||||||
|
disableHover={this._disableHover}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={(divElement) => this.divElement = divElement} onMouseMove={this._onMouseMove.bind(this)}>
|
<div ref={(divElement) => this.divElement = divElement} onMouseMove={this._onMouseMove.bind(this)} onClick={this._onClick.bind(this)}>
|
||||||
<svg id={this.prefix} viewBox={viewPortParameters} width={'100%'} height={'100%'}
|
<svg id={this.prefix} viewBox={viewPortParameters} width={'100%'} height={'100%'}
|
||||||
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>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue