mirror of
https://github.com/apache/superset.git
synced 2024-09-12 00:29:39 -04:00
[SIP-5] Repair and refactor Horizon Chart (#5690)
* Migrate horizon chart to react * remove unused code * rename files * update props * enable renderTrigger * fix canvas transform issue * Address Chris' comment
This commit is contained in:
parent
f5cc98351e
commit
d7f06cbc26
@ -383,14 +383,15 @@ export const controls = {
|
|||||||
|
|
||||||
horizon_color_scale: {
|
horizon_color_scale: {
|
||||||
type: 'SelectControl',
|
type: 'SelectControl',
|
||||||
label: t('Horizon Color Scale'),
|
renderTrigger: true,
|
||||||
|
label: t('Value Domain'),
|
||||||
choices: [
|
choices: [
|
||||||
['series', 'series'],
|
['series', 'series'],
|
||||||
['overall', 'overall'],
|
['overall', 'overall'],
|
||||||
['change', 'change'],
|
['change', 'change'],
|
||||||
],
|
],
|
||||||
default: 'series',
|
default: 'series',
|
||||||
description: t('Defines how the color are attributed.'),
|
description: t('series: Treat each series independently; overall: All series use the same scale; change: Show changes compared to the first data point in each series'),
|
||||||
},
|
},
|
||||||
|
|
||||||
canvas_image_rendering: {
|
canvas_image_rendering: {
|
||||||
@ -1205,6 +1206,7 @@ export const controls = {
|
|||||||
|
|
||||||
series_height: {
|
series_height: {
|
||||||
type: 'SelectControl',
|
type: 'SelectControl',
|
||||||
|
renderTrigger: true,
|
||||||
freeForm: true,
|
freeForm: true,
|
||||||
label: t('Series Height'),
|
label: t('Series Height'),
|
||||||
default: '25',
|
default: '25',
|
||||||
|
17
superset/assets/src/visualizations/HorizonChart.css
Normal file
17
superset/assets/src/visualizations/HorizonChart.css
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
.horizon-chart {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.horizon-chart .horizon-row {
|
||||||
|
border-bottom: solid 1px #ddd;
|
||||||
|
border-top: 0px;
|
||||||
|
padding: 0px;
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.horizon-row span {
|
||||||
|
position: absolute;
|
||||||
|
color: #333;
|
||||||
|
font-size: 0.8em;
|
||||||
|
text-shadow: 1px 1px rgba(255, 255, 255, 0.75);
|
||||||
|
}
|
103
superset/assets/src/visualizations/HorizonChart.jsx
Normal file
103
superset/assets/src/visualizations/HorizonChart.jsx
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import d3 from 'd3';
|
||||||
|
import HorizonRow, { DEFAULT_COLORS } from './HorizonRow';
|
||||||
|
import './HorizonChart.css';
|
||||||
|
|
||||||
|
const propTypes = {
|
||||||
|
className: PropTypes.string,
|
||||||
|
width: PropTypes.number,
|
||||||
|
seriesHeight: PropTypes.number,
|
||||||
|
data: PropTypes.arrayOf(PropTypes.shape({
|
||||||
|
key: PropTypes.arrayOf(PropTypes.string),
|
||||||
|
values: PropTypes.arrayOf(PropTypes.shape({
|
||||||
|
y: PropTypes.number,
|
||||||
|
})),
|
||||||
|
})).isRequired,
|
||||||
|
// number of bands in each direction (positive / negative)
|
||||||
|
bands: PropTypes.number,
|
||||||
|
colors: PropTypes.arrayOf(PropTypes.string),
|
||||||
|
colorScale: PropTypes.string,
|
||||||
|
mode: PropTypes.string,
|
||||||
|
offsetX: PropTypes.number,
|
||||||
|
};
|
||||||
|
const defaultProps = {
|
||||||
|
className: '',
|
||||||
|
width: 800,
|
||||||
|
seriesHeight: 20,
|
||||||
|
bands: Math.floor(DEFAULT_COLORS.length / 2),
|
||||||
|
colors: DEFAULT_COLORS,
|
||||||
|
colorScale: 'series',
|
||||||
|
mode: 'offset',
|
||||||
|
offsetX: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
class HorizonChart extends React.PureComponent {
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
className,
|
||||||
|
width,
|
||||||
|
data,
|
||||||
|
seriesHeight,
|
||||||
|
bands,
|
||||||
|
colors,
|
||||||
|
colorScale,
|
||||||
|
mode,
|
||||||
|
offsetX,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
let yDomain;
|
||||||
|
if (colorScale === 'overall') {
|
||||||
|
const allValues = data.reduce(
|
||||||
|
(acc, current) => acc.concat(current.values),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
yDomain = d3.extent(allValues, d => d.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`horizon-chart ${className}`}>
|
||||||
|
{data.map(row => (
|
||||||
|
<HorizonRow
|
||||||
|
key={row.key}
|
||||||
|
width={width}
|
||||||
|
height={seriesHeight}
|
||||||
|
title={row.key[0]}
|
||||||
|
data={row.values}
|
||||||
|
bands={bands}
|
||||||
|
colors={colors}
|
||||||
|
colorScale={colorScale}
|
||||||
|
mode={mode}
|
||||||
|
offsetX={offsetX}
|
||||||
|
yDomain={yDomain}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HorizonChart.propTypes = propTypes;
|
||||||
|
HorizonChart.defaultProps = defaultProps;
|
||||||
|
|
||||||
|
function adaptor(slice, payload) {
|
||||||
|
const { selector, formData } = slice;
|
||||||
|
const element = document.querySelector(selector);
|
||||||
|
const {
|
||||||
|
horizon_color_scale: colorScale,
|
||||||
|
series_height: seriesHeight,
|
||||||
|
} = formData;
|
||||||
|
|
||||||
|
ReactDOM.render(
|
||||||
|
<HorizonChart
|
||||||
|
data={payload.data}
|
||||||
|
width={slice.width()}
|
||||||
|
seriesHeight={parseInt(seriesHeight, 10)}
|
||||||
|
colorScale={colorScale}
|
||||||
|
/>,
|
||||||
|
element,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default adaptor;
|
182
superset/assets/src/visualizations/HorizonRow.jsx
Normal file
182
superset/assets/src/visualizations/HorizonRow.jsx
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import d3 from 'd3';
|
||||||
|
|
||||||
|
export const DEFAULT_COLORS = [
|
||||||
|
'#313695',
|
||||||
|
'#4575b4',
|
||||||
|
'#74add1',
|
||||||
|
'#abd9e9',
|
||||||
|
'#fee090',
|
||||||
|
'#fdae61',
|
||||||
|
'#f46d43',
|
||||||
|
'#d73027',
|
||||||
|
];
|
||||||
|
|
||||||
|
const propTypes = {
|
||||||
|
className: PropTypes.string,
|
||||||
|
width: PropTypes.number,
|
||||||
|
height: PropTypes.number,
|
||||||
|
data: PropTypes.arrayOf(PropTypes.shape({
|
||||||
|
y: PropTypes.number,
|
||||||
|
})).isRequired,
|
||||||
|
bands: PropTypes.number,
|
||||||
|
colors: PropTypes.arrayOf(PropTypes.string),
|
||||||
|
colorScale: PropTypes.string,
|
||||||
|
mode: PropTypes.string,
|
||||||
|
offsetX: PropTypes.number,
|
||||||
|
title: PropTypes.string,
|
||||||
|
yDomain: PropTypes.arrayOf(PropTypes.number),
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultProps = {
|
||||||
|
className: '',
|
||||||
|
width: 800,
|
||||||
|
height: 20,
|
||||||
|
bands: DEFAULT_COLORS.length >> 1,
|
||||||
|
colors: DEFAULT_COLORS,
|
||||||
|
colorScale: 'series',
|
||||||
|
mode: 'offset',
|
||||||
|
offsetX: 0,
|
||||||
|
title: '',
|
||||||
|
yDomain: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
class HorizonRow extends React.PureComponent {
|
||||||
|
componentDidMount() {
|
||||||
|
this.drawChart();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate() {
|
||||||
|
this.drawChart();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.canvas = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
drawChart() {
|
||||||
|
if (this.canvas) {
|
||||||
|
const {
|
||||||
|
data: rawData,
|
||||||
|
yDomain,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
bands,
|
||||||
|
colors,
|
||||||
|
colorScale,
|
||||||
|
offsetX,
|
||||||
|
mode,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const data = colorScale === 'change'
|
||||||
|
? rawData.map(d => ({ ...d, y: d.y - rawData[0].y }))
|
||||||
|
: rawData;
|
||||||
|
|
||||||
|
const context = this.canvas.getContext('2d');
|
||||||
|
context.imageSmoothingEnabled = false;
|
||||||
|
context.clearRect(0, 0, width, height);
|
||||||
|
// Reset transform
|
||||||
|
context.setTransform(1, 0, 0, 1, 0, 0);
|
||||||
|
context.translate(0.5, 0.5);
|
||||||
|
|
||||||
|
const step = width / data.length;
|
||||||
|
// the data frame currently being shown:
|
||||||
|
const startIndex = Math.floor(Math.max(0, -(offsetX / step)));
|
||||||
|
const endIndex = Math.floor(Math.min(data.length, startIndex + (width / step)));
|
||||||
|
|
||||||
|
// skip drawing if there's no data to be drawn
|
||||||
|
if (startIndex > data.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create y-scale
|
||||||
|
const [min, max] = yDomain || d3.extent(data, d => d.y);
|
||||||
|
const y = d3.scale.linear()
|
||||||
|
.domain([0, Math.max(-min, max)])
|
||||||
|
.range([0, height]);
|
||||||
|
|
||||||
|
// we are drawing positive & negative bands separately to avoid mutating canvas state
|
||||||
|
// http://www.html5rocks.com/en/tutorials/canvas/performance/
|
||||||
|
let hasNegative = false;
|
||||||
|
// draw positive bands
|
||||||
|
let value;
|
||||||
|
let bExtents;
|
||||||
|
for (let b = 0; b < bands; b += 1) {
|
||||||
|
context.fillStyle = colors[bands + b];
|
||||||
|
|
||||||
|
// Adjust the range based on the current band index.
|
||||||
|
bExtents = (b + 1 - bands) * height;
|
||||||
|
y.range([bands * height + bExtents, bExtents]);
|
||||||
|
|
||||||
|
// only the current data frame is being drawn i.e. what's visible:
|
||||||
|
for (let i = startIndex; i < endIndex; i++) {
|
||||||
|
value = data[i].y;
|
||||||
|
if (value <= 0) {
|
||||||
|
hasNegative = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (value !== undefined) {
|
||||||
|
context.fillRect(
|
||||||
|
offsetX + i * step,
|
||||||
|
y(value),
|
||||||
|
step + 1,
|
||||||
|
y(0) - y(value),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw negative bands
|
||||||
|
if (hasNegative) {
|
||||||
|
// mirror the negative bands, by flipping the canvas
|
||||||
|
if (mode === 'offset') {
|
||||||
|
context.translate(0, height);
|
||||||
|
context.scale(1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let b = 0; b < bands; b++) {
|
||||||
|
context.fillStyle = colors[bands - b - 1];
|
||||||
|
|
||||||
|
// Adjust the range based on the current band index.
|
||||||
|
bExtents = (b + 1 - bands) * height;
|
||||||
|
y.range([bands * height + bExtents, bExtents]);
|
||||||
|
|
||||||
|
// only the current data frame is being drawn i.e. what's visible:
|
||||||
|
for (let ii = startIndex; ii < endIndex; ii++) {
|
||||||
|
value = data[ii].y;
|
||||||
|
if (value >= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
context.fillRect(
|
||||||
|
offsetX + ii * step,
|
||||||
|
y(-value),
|
||||||
|
step + 1,
|
||||||
|
y(0) - y(-value),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { className, title, width, height } = this.props;
|
||||||
|
return (
|
||||||
|
<div className={`horizon-row ${className}`}>
|
||||||
|
<span className="title">{title}</span>
|
||||||
|
<canvas
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
ref={(c) => { this.canvas = c; }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HorizonRow.propTypes = propTypes;
|
||||||
|
HorizonRow.defaultProps = defaultProps;
|
||||||
|
|
||||||
|
export default HorizonRow;
|
@ -1,17 +0,0 @@
|
|||||||
.horizon .slice_container div.horizon {
|
|
||||||
border-bottom: solid 1px #444;
|
|
||||||
border-top: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
margin: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.horizon span {
|
|
||||||
left: 5;
|
|
||||||
position: absolute;
|
|
||||||
color: black;
|
|
||||||
text-shadow: 1px 1px rgba(255, 255, 255, 0.75);
|
|
||||||
}
|
|
||||||
|
|
||||||
.horizon .slice_container {
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
@ -1,227 +0,0 @@
|
|||||||
/* eslint-disable prefer-rest-params, no-param-reassign */
|
|
||||||
// Copied and modified from
|
|
||||||
// https://github.com/kmandov/d3-horizon-chart
|
|
||||||
import d3 from 'd3';
|
|
||||||
import './horizon.css';
|
|
||||||
|
|
||||||
const horizonChart = function () {
|
|
||||||
let colors = [
|
|
||||||
'#313695',
|
|
||||||
'#4575b4',
|
|
||||||
'#74add1',
|
|
||||||
'#abd9e9',
|
|
||||||
'#fee090',
|
|
||||||
'#fdae61',
|
|
||||||
'#f46d43',
|
|
||||||
'#d73027',
|
|
||||||
];
|
|
||||||
let height = 30;
|
|
||||||
const y = d3.scale.linear().range([0, height]);
|
|
||||||
let bands = colors.length >> 1; // number of bands in each direction (positive / negative)
|
|
||||||
let width = 1000;
|
|
||||||
let offsetX = 0;
|
|
||||||
let spacing = 0;
|
|
||||||
let mode = 'offset';
|
|
||||||
let axis;
|
|
||||||
let title;
|
|
||||||
let extent; // the extent is derived from the data, unless explicitly set via .extent([min, max])
|
|
||||||
let x;
|
|
||||||
let canvas;
|
|
||||||
|
|
||||||
function my(data) {
|
|
||||||
const horizon = d3.select(this);
|
|
||||||
const step = width / data.length;
|
|
||||||
|
|
||||||
horizon.append('span')
|
|
||||||
.attr('class', 'title')
|
|
||||||
.text(title);
|
|
||||||
|
|
||||||
horizon.append('span')
|
|
||||||
.attr('class', 'value');
|
|
||||||
|
|
||||||
canvas = horizon.append('canvas');
|
|
||||||
|
|
||||||
canvas
|
|
||||||
.attr('width', width)
|
|
||||||
.attr('height', height);
|
|
||||||
|
|
||||||
const context = canvas.node().getContext('2d');
|
|
||||||
context.imageSmoothingEnabled = false;
|
|
||||||
|
|
||||||
// update the y scale, based on the data extents
|
|
||||||
const ext = extent || d3.extent(data, d => d.y);
|
|
||||||
|
|
||||||
const max = Math.max(-ext[0], ext[1]);
|
|
||||||
y.domain([0, max]);
|
|
||||||
|
|
||||||
// x = d3.scaleTime().domain[];
|
|
||||||
axis = d3.svg.axis(x).ticks(5);
|
|
||||||
|
|
||||||
context.clearRect(0, 0, width, height);
|
|
||||||
// context.translate(0.5, 0.5);
|
|
||||||
|
|
||||||
// the data frame currently being shown:
|
|
||||||
const startIndex = Math.floor(Math.max(0, -(offsetX / step)));
|
|
||||||
const endIndex = Math.floor(Math.min(data.length, startIndex + (width / step)));
|
|
||||||
|
|
||||||
// skip drawing if there's no data to be drawn
|
|
||||||
if (startIndex > data.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we are drawing positive & negative bands separately to avoid mutating canvas state
|
|
||||||
// http://www.html5rocks.com/en/tutorials/canvas/performance/
|
|
||||||
let negative = false;
|
|
||||||
// draw positive bands
|
|
||||||
let value;
|
|
||||||
let bExtents;
|
|
||||||
for (let b = 0; b < bands; b += 1) {
|
|
||||||
context.fillStyle = colors[bands + b];
|
|
||||||
|
|
||||||
// Adjust the range based on the current band index.
|
|
||||||
bExtents = (b + 1 - bands) * height;
|
|
||||||
y.range([bands * height + bExtents, bExtents]);
|
|
||||||
|
|
||||||
// only the current data frame is being drawn i.e. what's visible:
|
|
||||||
for (let i = startIndex; i < endIndex; i++) {
|
|
||||||
value = data[i].y;
|
|
||||||
if (value <= 0) { negative = true; continue; }
|
|
||||||
if (value === undefined) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
context.fillRect(offsetX + i * step, y(value), step + 1, y(0) - y(value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// draw negative bands
|
|
||||||
if (negative) {
|
|
||||||
// mirror the negative bands, by flipping the canvas
|
|
||||||
if (mode === 'offset') {
|
|
||||||
context.translate(0, height);
|
|
||||||
context.scale(1, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let b = 0; b < bands; b++) {
|
|
||||||
context.fillStyle = colors[bands - b - 1];
|
|
||||||
|
|
||||||
// Adjust the range based on the current band index.
|
|
||||||
bExtents = (b + 1 - bands) * height;
|
|
||||||
y.range([bands * height + bExtents, bExtents]);
|
|
||||||
|
|
||||||
// only the current data frame is being drawn i.e. what's visible:
|
|
||||||
for (let ii = startIndex; ii < endIndex; ii++) {
|
|
||||||
value = data[ii].y;
|
|
||||||
if (value >= 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
context.fillRect(offsetX + ii * step, y(-value), step + 1, y(0) - y(-value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
my.axis = function (_) {
|
|
||||||
if (!arguments.length) { return axis; }
|
|
||||||
axis = _;
|
|
||||||
return my;
|
|
||||||
};
|
|
||||||
|
|
||||||
my.title = function (_) {
|
|
||||||
if (!arguments.length) { return title; }
|
|
||||||
title = _;
|
|
||||||
return my;
|
|
||||||
};
|
|
||||||
|
|
||||||
my.canvas = function (_) {
|
|
||||||
if (!arguments.length) { return canvas; }
|
|
||||||
canvas = _;
|
|
||||||
return my;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Array of colors representing the number of bands
|
|
||||||
my.colors = function (_) {
|
|
||||||
if (!arguments.length) {
|
|
||||||
return colors;
|
|
||||||
}
|
|
||||||
colors = _;
|
|
||||||
|
|
||||||
// update the number of bands
|
|
||||||
bands = colors.length >> 1;
|
|
||||||
return my;
|
|
||||||
};
|
|
||||||
|
|
||||||
my.height = function (_) {
|
|
||||||
if (!arguments.length) { return height; }
|
|
||||||
height = _;
|
|
||||||
return my;
|
|
||||||
};
|
|
||||||
|
|
||||||
my.width = function (_) {
|
|
||||||
if (!arguments.length) { return width; }
|
|
||||||
width = _;
|
|
||||||
return my;
|
|
||||||
};
|
|
||||||
|
|
||||||
my.spacing = function (_) {
|
|
||||||
if (!arguments.length) { return spacing; }
|
|
||||||
spacing = _;
|
|
||||||
return my;
|
|
||||||
};
|
|
||||||
|
|
||||||
// mirror or offset
|
|
||||||
my.mode = function (_) {
|
|
||||||
if (!arguments.length) { return mode; }
|
|
||||||
mode = _;
|
|
||||||
return my;
|
|
||||||
};
|
|
||||||
|
|
||||||
my.extent = function (_) {
|
|
||||||
if (!arguments.length) { return extent; }
|
|
||||||
extent = _;
|
|
||||||
return my;
|
|
||||||
};
|
|
||||||
|
|
||||||
my.offsetX = function (_) {
|
|
||||||
if (!arguments.length) { return offsetX; }
|
|
||||||
offsetX = _;
|
|
||||||
return my;
|
|
||||||
};
|
|
||||||
|
|
||||||
return my;
|
|
||||||
};
|
|
||||||
|
|
||||||
function horizonViz(slice, payload) {
|
|
||||||
const fd = slice.formData;
|
|
||||||
const div = d3.select(slice.selector);
|
|
||||||
div.selectAll('*').remove();
|
|
||||||
let extent;
|
|
||||||
if (fd.horizon_color_scale === 'overall') {
|
|
||||||
let allValues = [];
|
|
||||||
payload.data.forEach(function (d) {
|
|
||||||
allValues = allValues.concat(d.values);
|
|
||||||
});
|
|
||||||
extent = d3.extent(allValues, d => d.y);
|
|
||||||
} else if (fd.horizon_color_scale === 'change') {
|
|
||||||
payload.data.forEach(function (series) {
|
|
||||||
const t0y = series.values[0].y; // value at time 0
|
|
||||||
series.values = series.values.map(d =>
|
|
||||||
Object.assign({}, d, { y: d.y - t0y }),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
div.selectAll('.horizon')
|
|
||||||
.data(payload.data)
|
|
||||||
.enter()
|
|
||||||
.append('div')
|
|
||||||
.attr('class', 'horizon')
|
|
||||||
.each(function (d, i) {
|
|
||||||
horizonChart()
|
|
||||||
.height(fd.series_height)
|
|
||||||
.width(slice.width())
|
|
||||||
.extent(extent)
|
|
||||||
.title(d.key)
|
|
||||||
.call(this, d.values, i);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = horizonViz;
|
|
@ -83,7 +83,7 @@ const vizMap = {
|
|||||||
[VIZ_TYPES.heatmap]: () => loadVis(import(/* webpackChunkName: "heatmap" */ './heatmap.js')),
|
[VIZ_TYPES.heatmap]: () => loadVis(import(/* webpackChunkName: "heatmap" */ './heatmap.js')),
|
||||||
[VIZ_TYPES.histogram]: () =>
|
[VIZ_TYPES.histogram]: () =>
|
||||||
loadVis(import(/* webpackChunkName: "histogram" */ './histogram.js')),
|
loadVis(import(/* webpackChunkName: "histogram" */ './histogram.js')),
|
||||||
[VIZ_TYPES.horizon]: () => loadVis(import(/* webpackChunkName: "horizon" */ './horizon.js')),
|
[VIZ_TYPES.horizon]: () => loadVis(import(/* webpackChunkName: "horizon" */ './HorizonChart.jsx')),
|
||||||
[VIZ_TYPES.iframe]: () => loadVis(import(/* webpackChunkName: "iframe" */ './iframe.js')),
|
[VIZ_TYPES.iframe]: () => loadVis(import(/* webpackChunkName: "iframe" */ './iframe.js')),
|
||||||
[VIZ_TYPES.line]: loadNvd3,
|
[VIZ_TYPES.line]: loadNvd3,
|
||||||
[VIZ_TYPES.line_multi]: () =>
|
[VIZ_TYPES.line_multi]: () =>
|
||||||
|
Loading…
Reference in New Issue
Block a user