diff --git a/js/components/interface/graph-visualization/Graph.js b/js/components/interface/graph-visualization/Graph.js index 8996e721b..985adc1ca 100644 --- a/js/components/interface/graph-visualization/Graph.js +++ b/js/components/interface/graph-visualization/Graph.js @@ -26,6 +26,10 @@ export default class GeppettoGraphVisualization extends Component { getNodeLabel = this.props.nodeLabel ? this.fnOrField(this.props.nodeLabel) : node => node.name getLinkLabel = this.props.linkLabel ? this.fnOrField(this.props.linkLabel) : link => link.name + getLinkWidth = this.props.linkWidth ? this.props.linkWidth instanceof Function ? this.props.linkWidth : () => this.props.linkWidth : () => 0.25 + getLinkColor = this.props.linkColor ? this.props.linkColor instanceof Function ? this.props.linkColor : () => this.props.linkColor : () => 'white' + getNodeColor = this.props.nodeColor ? this.props.nodeColor instanceof Function ? this.props.nodeColor : () => this.props.nodeColor : () => '#6520ff' + getNodeLabelColor = this.props.nodeLabelColor ? this.props.nodeLabelColor instanceof Function ? this.props.nodeLabelColor : () => this.props.nodeLabelColor : () => '#ffffff' componentDidMount (){ const { data, url } = this.props @@ -34,7 +38,8 @@ export default class GeppettoGraphVisualization extends Component { const forceLinkDistance = this.props.forceLinkDistance ? this.props.forceLinkDistance : 90 const forceLinkStrength = this.props.forceLinkStrength ? this.props.forceLinkStrength : 0.7 const forceChargeStrength = this.props.forceChargeStrength ? this.props.forceChargeStrength : -200 - this.ggv.current.d3Force('collide', d3.forceCollide(this.size)); + const collideSize = this.collideSize ? this.collideSize : this.size * 1.5 + this.ggv.current.d3Force('collide', d3.forceCollide(collideSize)); this.ggv.current.d3Force('link').distance(forceLinkDistance).strength(forceLinkStrength) this.ggv.current.d3Force('charge').strength(forceChargeStrength) this.ggv.current.d3Force('radial', d3.forceRadial(this.props.forceRadial ? this.props.forceRadial : 1)) @@ -50,7 +55,7 @@ export default class GeppettoGraphVisualization extends Component { } } - componentDidUpdate () { + componentDidUpdate (prevProps, prevState) { const dimensions = ReactDOM.findDOMNode(this).parentNode.getBoundingClientRect() if (this.props.d2) { this.ggv.current.centerAt(0, 0, this.timeToCenter2DCamera) @@ -220,6 +225,8 @@ export default class GeppettoGraphVisualization extends Component { // Draw this: ( n1 )--- link_Label --->( n2 ) linkCanvasObject (link, ctx, globalScale) { + const color = this.getLinkColor(link) + ctx.lineWidth = this.getLinkWidth(link) const xs = link.source.x const xt = link.target.x const ys = link.source.y @@ -237,13 +244,12 @@ export default class GeppettoGraphVisualization extends Component { // [-PI ; PI] const angle2 = Math.atan2(yt - ys, xt - xs) - const textLength = ctx.measureText(link.id).width - - const doNotPlotLinkLabel = !linkText || availableSpaceForLinkLabel < ctx.measureText('Abc...').width + ctx.fillStyle = color + ctx.strokeStyle = color if (doNotPlotLinkLabel) { - + linkText = '' ctx.beginPath(); ctx.moveTo(xs, ys); ctx.lineTo(xt, yt); @@ -251,7 +257,7 @@ export default class GeppettoGraphVisualization extends Component { } else { - if (linkText && textLength > availableSpaceForLinkLabel){ + if (linkText && ctx.measureText(linkText).width > availableSpaceForLinkLabel){ var i = linkText.length - 3 // for the ... at the end while (ctx.measureText(linkText.substring(0, i) + '...').width > availableSpaceForLinkLabel) { i-- @@ -259,6 +265,7 @@ export default class GeppettoGraphVisualization extends Component { linkText = linkText.substring(0, i) + '...' } + const textLength = ctx.measureText(linkText).width const subX = Math.cos(angle2) * textLength / 2 const subY = Math.sin(angle2) * textLength / 2 @@ -273,19 +280,19 @@ export default class GeppettoGraphVisualization extends Component { ctx.moveTo(cx + subX, cy + subY); ctx.lineTo(xt, yt); ctx.stroke() - // Draw text for link label - } + // Draw text for link label ctx.save(); ctx.translate(cx, cy); ctx.rotate(angle) if (linkText){ ctx.fillText(linkText, 0, 0); } + // Draw arrow to indicate link direction var dist = (linkLength / 2 - this.size) - arrowSize - ctx.fillStyle = 'rgba(0, 0, 0, 1)'; + ctx.fillStyle = color; ctx.beginPath(); if (angle2 >= Math.PI / 2 || angle2 <= -Math.PI / 2){ dist *= -1 @@ -310,7 +317,9 @@ export default class GeppettoGraphVisualization extends Component { * \_____/ */ nodeWithName (node, ctx, globalScale) { - const color = node.color || '#6520ff' + const color = this.getNodeColor(node) + const labelColor = this.getNodeLabelColor(node) + ctx.font = this.font var label = this.getNodeLabel(node); @@ -327,7 +336,7 @@ export default class GeppettoGraphVisualization extends Component { ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; - ctx.fillStyle = 'rgba(255, 255, 255, 1)'; + ctx.fillStyle = labelColor; const maxCharsPerLine = Math.floor(this.size * 1.75 / ctx.measureText("a").width) @@ -356,7 +365,7 @@ export default class GeppettoGraphVisualization extends Component { this.addFixedPositionToNodes(data) - const props = { + const commonProps = { ref: this.ggv, graphData: data, width: this.dimensions.width - xGap, @@ -364,16 +373,15 @@ export default class GeppettoGraphVisualization extends Component { onNodeDrag: node => this.onNodeDrag(node), ...others } - + if (d2) { return "replace"} linkCanvasObject={this.linkCanvasObject.bind(this)} nodeCanvasObject={this.nodeWithName.bind(this)} nodeRelSize={this.size} - {...props}/> - } else { - return - } + {...commonProps}/> + } + return } } \ No newline at end of file diff --git a/js/components/interface/graph-visualization/utils.js b/js/components/interface/graph-visualization/utils.js index 599da98b7..d3c57695b 100644 --- a/js/components/interface/graph-visualization/utils.js +++ b/js/components/interface/graph-visualization/utils.js @@ -46,8 +46,10 @@ export function getDarkerColor (col, amt = -30) { } else if (col.startsWith('rgba')) { col = col.replace('rgba', '').replace('(', '').replace(')', '').split(',') + col = col.map(c => +c) } else if (col.startsWith('rgb')) { col = col.replace('rgb', '').replace('(', '').replace(')', '').split(',').push[1] + col = col.map(c => +c) } else { throw "Color format error. Accepted formats: #dddddd, rgba(ddd,ddd,ddd,f), rgb(ddd,ddd,ddd). Check parameters on GeppettoGraphVisualization"; } diff --git a/js/components/interface/listViewer/PopupColorPicker.js b/js/components/interface/listViewer/PopupColorPicker.js index 565b92244..fe99a8e5a 100644 --- a/js/components/interface/listViewer/PopupColorPicker.js +++ b/js/components/interface/listViewer/PopupColorPicker.js @@ -1,55 +1,81 @@ import React from 'react'; +import Popover from '@material-ui/core/Popover'; import { SketchPicker, CompactPicker, SwatchesPicker } from 'react-color'; import BaseIconComponent from './BaseIconComponent'; +const styles = { + container: { position: 'relative' }, + popover: { + anchorOrigin:{ + vertical: 'bottom', + horizontal: 'center', + }, + transformOrigin:{ + vertical: 'top', + horizontal: 'center', + } + }, +}; + export default class PopupColorPicker extends React.Component { constructor (props){ super(props); this.state = { - displayColorPicker: false, + anchorEl: null, color: props.color ? props.color : '#FFFFFF', }; this.Picker = this.props.picker ? this.props.picker : CompactPicker; } - handleClick () { - return this.setState({ displayColorPicker: !this.state.displayColorPicker }); + handleClick (currentTarget) { + this.setState({ anchorEl: currentTarget }); } handleClose () { - this.setState({ displayColorPicker: false }); + this.setState({ anchorEl: null }); } handleChange (color) { - this.setState({ color: color.hex, displayColorPicker: false }); + this.setState({ color: color.hex, anchorEl: null }); this.props.action(color.hex); } + render () { - - const styles = { - container: { position: 'relative' }, - popover: { - position: 'absolute', - zIndex: '2', - right: 0 - }, - - }; - + const { color, anchorEl } = this.state + const open = Boolean(anchorEl); + var container + + if (open) { + const colorPicker = React.createElement(this.Picker, { + color, + onChange: color => this.handleChange(color) + }) + container = ( + this.handleClose()} + anchorOrigin={styles.popover.anchorOrigin} + transformOrigin={styles.popover.transformOrigin} + > + {colorPicker} + + ) + } else { + container = null + } + return (
- this.handleClick() } color={ this.state.color } icon={this.props.icon ? this.props.icon : "tint"} /> - - { this.state.displayColorPicker ?
-
this.handleClose() }/> - { - React.createElement(this.Picker, { color: this.state.color, onChange: color => this.handleChange(color) }) - } -
: null } - + this.handleClick(e.currentTarget)} + color={ color } + icon={this.props.icon ? this.props.icon : "tint"} + /> + { container }
) }