You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ma...@apache.org on 2018/08/22 03:49:00 UTC
[incubator-superset] branch master updated: Refactor force-directed
graph (#5691)
This is an automated email from the ASF dual-hosted git repository.
maximebeauchemin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git
The following commit(s) were added to refs/heads/master by this push:
new 3929f0f Refactor force-directed graph (#5691)
3929f0f is described below
commit 3929f0f79d6aec3358d0272dc3ac4f9c138bc49d
Author: Krist Wongsuphasawat <kr...@gmail.com>
AuthorDate: Tue Aug 21 20:48:58 2018 -0700
Refactor force-directed graph (#5691)
* Refactor and add props to Force Directed Graph
* update label and enable renderTrigger
---
superset/assets/src/explore/controls.jsx | 2 +
superset/assets/src/explore/visTypes.jsx | 2 +-
.../assets/src/visualizations/directed_force.js | 150 ++++++++++++---------
3 files changed, 92 insertions(+), 62 deletions(-)
diff --git a/superset/assets/src/explore/controls.jsx b/superset/assets/src/explore/controls.jsx
index a392c61..8fa36b2 100644
--- a/superset/assets/src/explore/controls.jsx
+++ b/superset/assets/src/explore/controls.jsx
@@ -838,6 +838,7 @@ export const controls = {
link_length: {
type: 'SelectControl',
+ renderTrigger: true,
freeForm: true,
label: t('Link Length'),
default: '200',
@@ -847,6 +848,7 @@ export const controls = {
charge: {
type: 'SelectControl',
+ renderTrigger: true,
freeForm: true,
label: t('Charge'),
default: '-500',
diff --git a/superset/assets/src/explore/visTypes.jsx b/superset/assets/src/explore/visTypes.jsx
index f070096..d499c7a 100644
--- a/superset/assets/src/explore/visTypes.jsx
+++ b/superset/assets/src/explore/visTypes.jsx
@@ -1398,7 +1398,7 @@ export const visTypes = {
},
directed_force: {
- label: t('Directed Force Layout'),
+ label: t('Force-directed Graph'),
controlPanelSections: [
{
label: t('Query'),
diff --git a/superset/assets/src/visualizations/directed_force.js b/superset/assets/src/visualizations/directed_force.js
index b95829f..b3bf0f3 100644
--- a/superset/assets/src/visualizations/directed_force.js
+++ b/superset/assets/src/visualizations/directed_force.js
@@ -1,18 +1,34 @@
/* eslint-disable no-param-reassign */
import d3 from 'd3';
-
-require('./directed_force.css');
+import PropTypes from 'prop-types';
+import './directed_force.css';
+
+const propTypes = {
+ data: PropTypes.arrayOf(PropTypes.shape({
+ source: PropTypes.string,
+ target: PropTypes.string,
+ value: PropTypes.number,
+ })),
+ width: PropTypes.number,
+ height: PropTypes.number,
+ linkLength: PropTypes.number,
+ charge: PropTypes.number,
+};
/* Modified from http://bl.ocks.org/d3noob/5141278 */
-const directedForceVis = function (slice, json) {
- const div = d3.select(slice.selector);
- const width = slice.width();
- const height = slice.height();
- const fd = slice.formData;
- const linkLength = fd.link_length || 200;
- const charge = fd.charge || -500;
-
- const links = json.data;
+function ForceDirectedGraph(element, props) {
+ PropTypes.checkPropTypes(propTypes, props, 'prop', 'ForceDirectedGraph');
+
+ const {
+ data,
+ width,
+ height,
+ linkLength = 200,
+ charge = -500,
+ } = props;
+ const div = d3.select(element);
+
+ const links = data;
const nodes = {};
// Compute the distinct nodes from the links.
links.forEach(function (link) {
@@ -73,73 +89,73 @@ const directedForceVis = function (slice, json) {
/* eslint-enable no-use-before-define */
const force = d3.layout.force()
- .nodes(d3.values(nodes))
- .links(links)
- .size([width, height])
- .linkDistance(linkLength)
- .charge(charge)
- .on('tick', tick)
- .start();
+ .nodes(d3.values(nodes))
+ .links(links)
+ .size([width, height])
+ .linkDistance(linkLength)
+ .charge(charge)
+ .on('tick', tick)
+ .start();
div.selectAll('*').remove();
const svg = div.append('svg')
- .attr('width', width)
- .attr('height', height);
+ .attr('width', width)
+ .attr('height', height);
// build the arrow.
svg.append('svg:defs').selectAll('marker')
- .data(['end']) // Different link/path types can be defined here
+ .data(['end']) // Different link/path types can be defined here
.enter()
.append('svg:marker') // This section adds in the arrows
- .attr('id', String)
- .attr('viewBox', '0 -5 10 10')
- .attr('refX', 15)
- .attr('refY', -1.5)
- .attr('markerWidth', 6)
- .attr('markerHeight', 6)
- .attr('orient', 'auto')
+ .attr('id', String)
+ .attr('viewBox', '0 -5 10 10')
+ .attr('refX', 15)
+ .attr('refY', -1.5)
+ .attr('markerWidth', 6)
+ .attr('markerHeight', 6)
+ .attr('orient', 'auto')
.append('svg:path')
- .attr('d', 'M0,-5L10,0L0,5');
+ .attr('d', 'M0,-5L10,0L0,5');
const edgeScale = d3.scale.linear()
.range([0.1, 0.5]);
// add the links and the arrows
const path = svg.append('svg:g').selectAll('path')
- .data(force.links())
+ .data(force.links())
.enter()
.append('svg:path')
- .attr('class', 'link')
- .style('opacity', function (d) {
- return edgeScale(d.value / d.target.max);
- })
- .attr('marker-end', 'url(#end)');
+ .attr('class', 'link')
+ .style('opacity', function (d) {
+ return edgeScale(d.value / d.target.max);
+ })
+ .attr('marker-end', 'url(#end)');
// define the nodes
const node = svg.selectAll('.node')
- .data(force.nodes())
+ .data(force.nodes())
.enter()
- .append('g')
- .attr('class', 'node')
+ .append('g')
+ .attr('class', 'node')
.on('mouseenter', function () {
d3.select(this)
- .select('circle')
- .transition()
- .style('stroke-width', 5);
+ .select('circle')
+ .transition()
+ .style('stroke-width', 5);
d3.select(this)
- .select('text')
- .transition()
- .style('font-size', 25);
+ .select('text')
+ .transition()
+ .style('font-size', 25);
})
.on('mouseleave', function () {
d3.select(this)
- .select('circle')
- .transition()
- .style('stroke-width', 1.5);
+ .select('circle')
+ .transition()
+ .style('stroke-width', 1.5);
d3.select(this)
- .select('text')
- .transition()
- .style('font-size', 12);
+ .select('text')
+ .transition()
+ .style('font-size', 12);
})
.call(force.drag);
@@ -148,21 +164,33 @@ const directedForceVis = function (slice, json) {
return Math.sqrt(d.total);
});
const circleScale = d3.scale.linear()
- .domain(ext)
- .range([3, 30]);
+ .domain(ext)
+ .range([3, 30]);
node.append('circle')
- .attr('r', function (d) {
- return circleScale(Math.sqrt(d.total));
- });
+ .attr('r', function (d) {
+ return circleScale(Math.sqrt(d.total));
+ });
// add the text
node.append('text')
- .attr('x', 6)
- .attr('dy', '.35em')
- .text(function (d) {
- return d.name;
+ .attr('x', 6)
+ .attr('dy', '.35em')
+ .text(d => d.name);
+}
+
+function adaptor(slice, payload) {
+ const { selector, formData } = slice;
+ const { link_length: linkLength, charge } = formData;
+ const element = document.querySelector(selector);
+
+ return ForceDirectedGraph(element, {
+ data: payload.data,
+ width: slice.width(),
+ height: slice.height(),
+ linkLength,
+ charge,
});
-};
+}
-module.exports = directedForceVis;
+export default adaptor;