mirror of
https://github.com/apache/superset.git
synced 2024-09-12 08:39:45 -04:00
feat: add sankey chart with loops (#77)
* feat(chart): add basic working look sankey chart * fix(doc): update readme and remove dead code * fix(clean): clean up both style and code * fix(reorg): reogranize for clarity * fix(path): remove dev path to chart * fix(lint): remove lint * fix(pr): fixes for PR * fix(path): fix dev path * fix(lint): remove unused
This commit is contained in:
parent
e2300812d7
commit
c0920f6397
@ -0,0 +1,34 @@
|
||||
## @superset-ui/legacy-plugin-chart-sankey-loop
|
||||
|
||||
[![Version](https://img.shields.io/npm/v/@superset-ui/legacy-plugin-chart-sankey.svg?style=flat-square)](https://img.shields.io/npm/v/@superset-ui/legacy-plugin-chart-sankey.svg?style=flat-square)
|
||||
[![David (path)](https://img.shields.io/david/apache-superset/superset-ui-plugins.svg?path=packages%2Fsuperset-ui-legacy-plugin-chart-sankey&style=flat-square)](https://david-dm.org/apache-superset/superset-ui-plugins?path=packages/superset-ui-legacy-plugin-chart-sankey)
|
||||
|
||||
This plugin provides Sankey Diagram with loops for Superset.
|
||||
|
||||
### Usage
|
||||
|
||||
Configure `key`, which can be any `string`, and register the plugin. This `key` will be used to lookup this chart throughout the app.
|
||||
|
||||
```js
|
||||
import SankeyLoopChartPlugin from '@superset-ui/legacy-plugin-chart-sankey-loop';
|
||||
|
||||
new SankeyLoopChartPlugin()
|
||||
.configure({ key: 'sankey' })
|
||||
.register();
|
||||
```
|
||||
|
||||
Then use it via `SuperChart`. See [storybook](https://apache-superset.github.io/superset-ui-plugins/?selectedKind=plugin-chart-sankey-loop) for more details.
|
||||
|
||||
```js
|
||||
<SuperChart
|
||||
chartType="sankey-loop"
|
||||
chartProps={{
|
||||
width: 600,
|
||||
height: 600,
|
||||
formData: {...},
|
||||
payload: {
|
||||
data: {...},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
```
|
@ -0,0 +1,41 @@
|
||||
{
|
||||
"name": "@superset-ui/legacy-plugin-chart-sankey-loop",
|
||||
"version": "0.10.11",
|
||||
"description": "Superset Legacy Chart - Sankey Diagram with Loops",
|
||||
"sideEffects": [
|
||||
"*.css"
|
||||
],
|
||||
"main": "lib/index.js",
|
||||
"module": "esm/index.js",
|
||||
"files": [
|
||||
"esm",
|
||||
"lib"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/apache-superset/superset-ui-plugins.git"
|
||||
},
|
||||
"keywords": [
|
||||
"superset"
|
||||
],
|
||||
"author": "Superset",
|
||||
"license": "Apache-2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/apache-superset/superset-ui-plugins/issues"
|
||||
},
|
||||
"homepage": "https://github.com/apache-superset/superset-ui-plugins#readme",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"dependencies": {
|
||||
"d3-sankey-diagram": "^0.7.3",
|
||||
"d3-selection": "^1.4.0",
|
||||
"prop-types": "^15.6.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@superset-ui/chart": "^0.10.0 || ^0.11.0",
|
||||
"@superset-ui/color": "^0.10.0 || ^0.11.0",
|
||||
"@superset-ui/number-format": "^0.10.0 || ^0.11.0",
|
||||
"@superset-ui/translation": "^0.10.0 || ^0.11.0"
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { reactify } from '@superset-ui/chart';
|
||||
import Component from './SankeyLoop';
|
||||
|
||||
export default reactify(Component);
|
@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
.superset-legacy-chart-sankey-loop .node rect {
|
||||
cursor: move;
|
||||
fill-opacity: .9;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
.superset-legacy-chart-sankey-loop .node text {
|
||||
pointer-events: none;
|
||||
text-shadow: 0 1px 0 #fff;
|
||||
}
|
||||
|
||||
.superset-legacy-chart-sankey-loop .link {
|
||||
fill: none;
|
||||
stroke: #000;
|
||||
stroke-opacity: .2;
|
||||
}
|
||||
|
||||
.superset-legacy-chart-sankey-loop .link:hover {
|
||||
stroke-opacity: .5;
|
||||
}
|
||||
|
||||
.superset-legacy-chart-sankey-loop .link path {
|
||||
opacity: .2;
|
||||
stroke-opacity: 0;
|
||||
}
|
||||
|
||||
.superset-legacy-chart-sankey-loop .link:hover path {
|
||||
opacity: .5
|
||||
}
|
||||
|
||||
.superset-legacy-chart-sankey-loop .link text {
|
||||
fill: #666;
|
||||
font-size: 10px
|
||||
}
|
||||
|
||||
.superset-legacy-chart-sankey-loop .link:hover text {
|
||||
opacity: 1
|
||||
}
|
||||
|
@ -0,0 +1,121 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
/* eslint-disable no-param-reassign, no-magic-numbers, sort-keys, babel/no-invalid-this */
|
||||
import PropTypes from 'prop-types';
|
||||
import { select } from 'd3-selection';
|
||||
import { sankeyDiagram, sankey } from 'd3-sankey-diagram';
|
||||
import { CategoricalColorNamespace } from '@superset-ui/color';
|
||||
import { getNumberFormatter, NumberFormats } from '@superset-ui/number-format';
|
||||
|
||||
import './SankeyLoop.css';
|
||||
|
||||
// a problem with 'd3-sankey-diagram' is that the sankey().extent() paramters, which
|
||||
// informs the layout of the bounding box of the sankey columns, does not account
|
||||
// for labels and paths which happen to be layedout outside that rectangle.
|
||||
// for that reason i've selected relatively large default left/right margins, and have
|
||||
// made 'margin' a property. i have raised an issue in the chart repo:
|
||||
//
|
||||
// https://github.com/ricklupton/d3-sankey-diagram/issues/20
|
||||
|
||||
const defaultMargin = {
|
||||
top: 0,
|
||||
right: 80,
|
||||
bottom: 0,
|
||||
left: 80,
|
||||
};
|
||||
|
||||
const propTypes = {
|
||||
data: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
source: PropTypes.string,
|
||||
target: PropTypes.string,
|
||||
value: PropTypes.number,
|
||||
}),
|
||||
),
|
||||
width: PropTypes.number,
|
||||
height: PropTypes.number,
|
||||
colorScheme: PropTypes.string,
|
||||
margin: PropTypes.shape({
|
||||
top: PropTypes.number,
|
||||
right: PropTypes.number,
|
||||
bottom: PropTypes.number,
|
||||
left: PropTypes.number,
|
||||
}),
|
||||
};
|
||||
|
||||
const percentFormat = getNumberFormatter(NumberFormats.PERCENT_1_POINT);
|
||||
const countFormat = getNumberFormatter();
|
||||
|
||||
function computeGraph(links) {
|
||||
// this assumes source and target are string values
|
||||
const nodes = Array.from(
|
||||
links.reduce((set, { source, target }) => set.add(source).add(target), new Set()),
|
||||
).map(id => ({ id, name: id }));
|
||||
|
||||
return {
|
||||
nodes,
|
||||
|
||||
// links are shallow copied as the chart layout modifies them, and it is best to
|
||||
// leave the passed data un-altered
|
||||
links: links.map(d => ({ ...d })),
|
||||
};
|
||||
}
|
||||
|
||||
function SankeyLoop(element, props) {
|
||||
const { data, width, height, colorScheme } = props;
|
||||
const color = CategoricalColorNamespace.getScale(colorScheme);
|
||||
const margin = { ...defaultMargin, ...props.margin };
|
||||
const innerWidth = width - margin.left - margin.right;
|
||||
const innerHeight = height - margin.top - margin.bottom;
|
||||
|
||||
const layout = sankey()
|
||||
.nodeId(d => d.id)
|
||||
.extent([[margin.left, margin.top], [innerWidth, innerHeight]]);
|
||||
|
||||
const diagram = sankeyDiagram()
|
||||
.nodeTitle(d => d.name)
|
||||
.linkTitle(
|
||||
({ source: { name: sName, value: sValue }, target: { name: tName }, value }) =>
|
||||
`${sName} → ${tName}: ${countFormat(value)} (${percentFormat(value / sValue)})`,
|
||||
)
|
||||
.linkColor(d => color(d.source.name));
|
||||
|
||||
const svg = select(element)
|
||||
.append('svg')
|
||||
.classed('superset-legacy-chart-sankey-loop', true)
|
||||
.style('width', width)
|
||||
.style('height', height)
|
||||
.datum(layout(computeGraph(data)))
|
||||
.call(diagram);
|
||||
|
||||
svg
|
||||
.selectAll('g.link')
|
||||
.classed('link', true)
|
||||
.append('text')
|
||||
.attr('x', d => d.points[0].x)
|
||||
.attr('y', d => d.points[0].y)
|
||||
.attr('dy', 3)
|
||||
.attr('dx', 2)
|
||||
.text(d => `${countFormat(d.value)} (${percentFormat(d.value / d.source.value)})`);
|
||||
}
|
||||
|
||||
SankeyLoop.displayName = 'SankeyLoop';
|
||||
SankeyLoop.propTypes = propTypes;
|
||||
|
||||
export default SankeyLoop;
|
Binary file not shown.
After Width: | Height: | Size: 138 KiB |
Binary file not shown.
After Width: | Height: | Size: 138 KiB |
@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
import { t } from '@superset-ui/translation';
|
||||
import { ChartMetadata, ChartPlugin } from '@superset-ui/chart';
|
||||
import transformProps from './transformProps';
|
||||
import thumbnail from './images/thumbnail.png';
|
||||
|
||||
const metadata = new ChartMetadata({
|
||||
credits: ['https://github.com/ricklupton/d3-sankey-diagram'],
|
||||
description: '',
|
||||
name: t('Sankey Diagram with Loops'),
|
||||
thumbnail,
|
||||
useLegacyApi: true,
|
||||
});
|
||||
|
||||
export default class SankeyChartPlugin extends ChartPlugin {
|
||||
constructor() {
|
||||
super({
|
||||
loadChart: () => import('./ReactSankeyLoop.js'),
|
||||
metadata,
|
||||
transformProps,
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
/* eslint-disable sort-keys */
|
||||
export default function transformProps(chartProps) {
|
||||
const { width, height, formData, payload, margin } = chartProps;
|
||||
const { colorScheme } = formData;
|
||||
|
||||
return {
|
||||
width,
|
||||
height,
|
||||
data: payload.data,
|
||||
colorScheme,
|
||||
margin,
|
||||
};
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
/* eslint-disable no-magic-numbers */
|
||||
import React from 'react';
|
||||
import { SuperChart } from '@superset-ui/chart';
|
||||
import data from './data';
|
||||
|
||||
export default [
|
||||
{
|
||||
renderStory: () => (
|
||||
<SuperChart
|
||||
chartType="sankey-loop"
|
||||
chartProps={{
|
||||
formData: {
|
||||
colorScheme: 'd3Category10',
|
||||
},
|
||||
height: 400,
|
||||
payload: { data },
|
||||
width: 400,
|
||||
}}
|
||||
/>
|
||||
),
|
||||
storyName: 'Basic',
|
||||
storyPath: 'legacy-|plugin-chart-sankey-loop|SankeyLoopChartPlugin',
|
||||
},
|
||||
];
|
@ -0,0 +1,33 @@
|
||||
/* eslint-disable sort-keys, no-magic-numbers */
|
||||
export default [
|
||||
{
|
||||
source: 'Lisdoonvarna',
|
||||
target: 'Cliffs of Moher',
|
||||
value: 50,
|
||||
},
|
||||
{
|
||||
source: 'Cliffs of Moher',
|
||||
target: 'Lisdoonvarna',
|
||||
value: 35,
|
||||
},
|
||||
{
|
||||
source: 'Cliffs of Moher',
|
||||
target: 'Killarney',
|
||||
value: 25,
|
||||
},
|
||||
{
|
||||
source: 'Lisdoonvarna',
|
||||
target: 'Killarney',
|
||||
value: 25,
|
||||
},
|
||||
{
|
||||
source: 'Lisdoonvarna',
|
||||
target: 'Kinvarra',
|
||||
value: 25,
|
||||
},
|
||||
{
|
||||
source: 'Kinvarra',
|
||||
target: 'Lisdoonvarna',
|
||||
value: 25,
|
||||
},
|
||||
];
|
@ -0,0 +1,8 @@
|
||||
import SankeyLoopChartPlugin from '../../../../superset-ui-legacy-plugin-chart-sankey-loop';
|
||||
import Stories from './Stories';
|
||||
|
||||
new SankeyLoopChartPlugin().configure({ key: 'sankey-loop' }).register();
|
||||
|
||||
export default {
|
||||
examples: [...Stories],
|
||||
};
|
Loading…
Reference in New Issue
Block a user